using System; using System.Collections.Generic; using GeoAPI.Geometries; using NetTopologySuite.Geometries; using NetTopologySuite.IO; namespace Deltares.Maps { public class Feature : NetTopologySuite.Features.Feature, IFeature { private string geomWkt; private IGeometry geometry; private int? oldHashCode; private Guid id; private Feature(string geomWkt) { this.geomWkt = geomWkt; ParseGeometryString(); IntializeGeometry(); } private Feature(IGeometry geometry) { this.geometry = geometry; WriteWktString(); IntializeGeometry(); } private void IntializeGeometry() { id = Guid.NewGuid(); Attributes = new AttributesDictionary(); } public virtual string WktFormat { get { if (geometry == null) { if (string.IsNullOrEmpty(geomWkt)) { WriteWktString(); } } return geomWkt; } } /// /// Gets the geometry. It parses the geometry wkt string when needed /// new public IGeometry Geometry { get { if (geometry == null) { if (!string.IsNullOrEmpty(geomWkt)) { ParseGeometryString(); } else { throw new InvalidOperationException( "The internal state for this object is not valid. The internal geometry is empty."); } } return geometry; } } public virtual Guid Id { get { return id; } protected set { id = value; } } public static Feature Create(string geomWkt, IEnumerable> attributes) { Feature feature = Create(geomWkt); foreach (var attribute in attributes) { feature.AddAttribute(attribute.Key, attribute.Value); } return feature; } /// /// Creates a feature with a geometry parsed from the argument string /// /// The well known geometry string /// A feature public static Feature Create(string geomWkt) { if (geomWkt == null) { throw new ArgumentNullException("geomWkt"); } try { return new Feature(geomWkt); } catch (Exception exception) { if (exception.Message.Contains("','")) { throw new ArgumentException( "There was an error parsing the WKT geometry string, maybe due to an incorrect culture format conversion", "geomWkt", exception); } } throw new InvalidOperationException(); } /// /// Creates an empty feature with a point geometry /// /// /// /// private static Feature Create(double x, double y) { return new Feature(new Point(x, y)); } public static Feature Create(double x, double y, IEnumerable> attributes) { Feature feature = Create(x, y); foreach (var attribute in attributes) { feature.AddAttribute(attribute.Key, attribute.Value); } return feature; } public static Feature Create(Coordinate coordinate) { if (coordinate == null) { throw new ArgumentNullException("coordinate"); } return new Feature(new Point(coordinate)); } public static Feature Create(IGeometry geometry) { if (geometry == null) { throw new ArgumentNullException("geometry"); } return new Feature(geometry); } private void WriteWktString() { var writer = new WKTWriter(); geomWkt = CultureHelper.InvokeWithUSCulture(() => writer.Write(geometry)); } private void ParseGeometryString() { var reader = new WKTReader(GeometryFactory.Default); geometry = reader.Read(geomWkt); } public override bool Equals(object obj) { var other = obj as Feature; if (other == null) { return false; } // handle the case of comparing two NEW objects bool otherIsTransient = Equals(other.Id, Guid.Empty); bool thisIsTransient = Equals(Id, Guid.Empty); if (otherIsTransient && thisIsTransient) { return ReferenceEquals(other, this); } return other.Id.Equals(Id); } public override int GetHashCode() { // Once we have a hash code we'll never change it if (oldHashCode.HasValue) { return oldHashCode.Value; } bool thisIsTransient = Equals(Id, Guid.Empty); // When this instance is transient, we use the base GetHashCode() // and remember it, so an instance can NEVER change its hash code. if (thisIsTransient) { oldHashCode = base.GetHashCode(); return oldHashCode.Value; } return Id.GetHashCode(); } public static bool operator ==(Feature x, Feature y) { return Equals(x, y); } public static bool operator !=(Feature x, Feature y) { return !(x == y); } public void AddAttribute(string attributeName, object value) { try { Attributes.AddAttribute(attributeName, value); } catch (ArgumentException exception) { throw new ArgumentException(attributeName, exception); } } public object this[string attributeName] { get { return Attributes[attributeName]; } set { Attributes[attributeName] = value; } } public object Tag { get; set; } } }