diff --git a/.gitignore b/.gitignore index f9d6b392..abc076e5 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ lib/* #OSX .DS_Store +/.vs diff --git a/src/GeoJSON.Net.Tests/Feature/FeatureTests.cs b/src/GeoJSON.Net.Tests/Feature/FeatureTests.cs index e7cd6ba8..30d8ca0d 100644 --- a/src/GeoJSON.Net.Tests/Feature/FeatureTests.cs +++ b/src/GeoJSON.Net.Tests/Feature/FeatureTests.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using GeoJSON.Net.Feature; using GeoJSON.Net.Geometry; using Newtonsoft.Json; using NUnit.Framework; diff --git a/src/GeoJSON.Net.Tests/Feature/GenericFeatureTests.cs b/src/GeoJSON.Net.Tests/Feature/GenericFeatureTests.cs index b7a78db0..fffebbb2 100644 --- a/src/GeoJSON.Net.Tests/Feature/GenericFeatureTests.cs +++ b/src/GeoJSON.Net.Tests/Feature/GenericFeatureTests.cs @@ -1,58 +1,102 @@ -using System.Linq; -using GeoJSON.Net.Geometry; -using Newtonsoft.Json; -using NUnit.Framework; - -namespace GeoJSON.Net.Tests.Feature -{ - [TestFixture] - internal class GenericFeatureTests : TestBase - { - [Test] - public void Can_Deserialize_Point_Feature() - { - var json = GetExpectedJson(); - - var feature = JsonConvert.DeserializeObject>(json); - - Assert.IsNotNull(feature); - Assert.IsNotNull(feature.Properties); - Assert.IsTrue(feature.Properties.Any()); - - Assert.IsTrue(feature.Properties.ContainsKey("name")); - Assert.AreEqual("Dinagat Islands", feature.Properties["name"]); - - Assert.AreEqual("test-id", feature.Id); - - Assert.AreEqual(GeoJSONObjectType.Point, feature.Geometry.Type); - Assert.AreEqual(125.6, feature.Geometry.Coordinates.Longitude); - Assert.AreEqual(10.1, feature.Geometry.Coordinates.Latitude); - Assert.AreEqual(456, feature.Geometry.Coordinates.Altitude); - } - - [Test] - public void Can_Deserialize_LineString_Feature() - { - var json = GetExpectedJson(); - - var feature = JsonConvert.DeserializeObject>(json); - - Assert.IsNotNull(feature); - Assert.IsNotNull(feature.Properties); - Assert.IsTrue(feature.Properties.Any()); - - Assert.IsTrue(feature.Properties.ContainsKey("name")); - Assert.AreEqual("Dinagat Islands", feature.Properties["name"]); - - Assert.AreEqual("test-id", feature.Id); - - Assert.AreEqual(GeoJSONObjectType.LineString, feature.Geometry.Type); - - Assert.AreEqual(4, feature.Geometry.Coordinates.Count); - - //Assert.AreEqual(125.6, feature.Geometry.Coordinates.Longitude); - //Assert.AreEqual(10.1, feature.Geometry.Coordinates.Latitude); - //Assert.AreEqual(456, feature.Geometry.Coordinates.Altitude); - } - } -} +using System.Linq; +using GeoJSON.Net.Feature; +using GeoJSON.Net.Geometry; +using Newtonsoft.Json; +using NUnit.Framework; + +namespace GeoJSON.Net.Tests.Feature +{ + [TestFixture] + internal class GenericFeatureTests : TestBase + { + [Test] + public void Can_Deserialize_Point_Feature() + { + var json = GetExpectedJson(); + + var feature = JsonConvert.DeserializeObject>(json); + + Assert.IsNotNull(feature); + Assert.IsNotNull(feature.Properties); + Assert.IsTrue(feature.Properties.Any()); + + Assert.IsTrue(feature.Properties.ContainsKey("name")); + Assert.AreEqual("Dinagat Islands", feature.Properties["name"]); + + Assert.AreEqual("test-id", feature.Id); + + Assert.AreEqual(GeoJSONObjectType.Point, feature.Geometry.Type); + Assert.AreEqual(125.6, feature.Geometry.Coordinates.Longitude); + Assert.AreEqual(10.1, feature.Geometry.Coordinates.Latitude); + Assert.AreEqual(456, feature.Geometry.Coordinates.Altitude); + } + + [Test] + public void Can_Deserialize_LineString_Feature() + { + var json = GetExpectedJson(); + + var feature = JsonConvert.DeserializeObject>(json); + + Assert.IsNotNull(feature); + Assert.IsNotNull(feature.Properties); + Assert.IsTrue(feature.Properties.Any()); + + Assert.IsTrue(feature.Properties.ContainsKey("name")); + Assert.AreEqual("Dinagat Islands", feature.Properties["name"]); + + Assert.AreEqual("test-id", feature.Id); + + Assert.AreEqual(GeoJSONObjectType.LineString, feature.Geometry.Type); + + Assert.AreEqual(4, feature.Geometry.Coordinates.Count); + + //Assert.AreEqual(125.6, feature.Geometry.Coordinates.Longitude); + //Assert.AreEqual(10.1, feature.Geometry.Coordinates.Latitude); + //Assert.AreEqual(456, feature.Geometry.Coordinates.Altitude); + } + + private class TypedFeatureProps + { + [JsonProperty("name")] + public string Name { get; set; } + [JsonProperty("value")] + public double Value { get; set; } + } + + [Test] + public void Can_Deserialize_Typed_Point_Feature() + { + var json = GetExpectedJson(); + var feature = JsonConvert.DeserializeObject>(json); + + Assert.IsNotNull(feature); + + Assert.IsNotNull(feature.Properties); + Assert.AreEqual(feature.Properties.Name, "Dinagat Islands"); + Assert.AreEqual(feature.Properties.Value, 4.2); + + Assert.AreEqual(feature.Id, "test-id"); + + Assert.AreEqual(feature.Geometry.Type, GeoJSONObjectType.Point); + } + + + [Test] + public void Can_Serialize_Typed_Point_Feature() + { + var geometry = new Point(new Position(1, 2)); + var props = new TypedFeatureProps + { + Name = "no name here", + Value = 1.337 + }; + var feature = new Feature(geometry, props, "no id there"); + + var expectedJson = GetExpectedJson(); + var actualJson = JsonConvert.SerializeObject(feature); + + JsonAssert.AreEqual(expectedJson, actualJson); + } + } +} diff --git a/src/GeoJSON.Net.Tests/Feature/GenericFeatureTests_Can_Deserialize_Typed_Point_Feature.json b/src/GeoJSON.Net.Tests/Feature/GenericFeatureTests_Can_Deserialize_Typed_Point_Feature.json new file mode 100644 index 00000000..2807e725 --- /dev/null +++ b/src/GeoJSON.Net.Tests/Feature/GenericFeatureTests_Can_Deserialize_Typed_Point_Feature.json @@ -0,0 +1,12 @@ +{ + "type": "Feature", + "id": "test-id", + "geometry": { + "type": "Point", + "coordinates": [ 125.6, 10.1 ] + }, + "properties": { + "name": "Dinagat Islands", + "value": 4.2 + } +} \ No newline at end of file diff --git a/src/GeoJSON.Net.Tests/Feature/GenericFeatureTests_Can_Serialize_Typed_Point_Feature.json b/src/GeoJSON.Net.Tests/Feature/GenericFeatureTests_Can_Serialize_Typed_Point_Feature.json new file mode 100644 index 00000000..4e68affa --- /dev/null +++ b/src/GeoJSON.Net.Tests/Feature/GenericFeatureTests_Can_Serialize_Typed_Point_Feature.json @@ -0,0 +1,12 @@ +{ + "geometry": { + "coordinates": [ 2.0, 1.0 ], + "type": "Point" + }, + "properties": { + "name": "no name here", + "value": 1.337 + }, + "id": "no id there", + "type": "Feature" +} \ No newline at end of file diff --git a/src/GeoJSON.Net.Tests/GeoJSON.Net.Tests.csproj b/src/GeoJSON.Net.Tests/GeoJSON.Net.Tests.csproj index 56a1baf4..18ed9f41 100644 --- a/src/GeoJSON.Net.Tests/GeoJSON.Net.Tests.csproj +++ b/src/GeoJSON.Net.Tests/GeoJSON.Net.Tests.csproj @@ -9,8 +9,8 @@ - - + + @@ -23,5 +23,5 @@ - + \ No newline at end of file diff --git a/src/GeoJSON.Net.Tests/packages.config b/src/GeoJSON.Net.Tests/packages.config index 1478e5af..13e3ab72 100644 --- a/src/GeoJSON.Net.Tests/packages.config +++ b/src/GeoJSON.Net.Tests/packages.config @@ -1,5 +1,6 @@ - - - - + + + + + \ No newline at end of file diff --git a/src/GeoJSON.Net/Feature/Feature.cs b/src/GeoJSON.Net/Feature/Feature.cs index 80fd02d6..506cc5fc 100644 --- a/src/GeoJSON.Net/Feature/Feature.cs +++ b/src/GeoJSON.Net/Feature/Feature.cs @@ -12,6 +12,90 @@ namespace GeoJSON.Net.Feature { + /// + /// A GeoJSON Feature Object; generic version for strongly typed + /// and + /// + /// + /// See https://tools.ietf.org/html/rfc7946#section-3.2 + /// + public class Feature : GeoJSONObject, IEquatable> + where TGeometry : IGeometryObject + { + [JsonConstructor] + public Feature(TGeometry geometry, TProps properties, string id = null) + { + Geometry = geometry; + Properties = properties; + Id = id; + + Type = GeoJSONObjectType.Feature; + } + + [JsonProperty(PropertyName = "id", NullValueHandling = NullValueHandling.Ignore)] + public string Id { get; } + + [JsonProperty(PropertyName = "geometry", Required = Required.AllowNull)] + [JsonConverter(typeof(GeometryConverter))] + public TGeometry Geometry { get; } + + [JsonProperty(PropertyName = "properties", Required = Required.AllowNull)] + public TProps Properties { get;} + + /// + /// Equality comparer. + /// + /// + /// In contrast to , this implementation returns true only + /// if and are also equal. See + /// #80 for discussion. The rationale + /// here is that a user explicitly specifying the property type most probably cares about the properties + /// equality. + /// + /// + /// + public bool Equals(Feature other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return base.Equals(other) + && string.Equals(Id, other.Id) + && EqualityComparer.Default.Equals(Geometry, other.Geometry) + && EqualityComparer.Default.Equals(Properties, other.Properties); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + return Equals((Feature) obj); + } + + public override int GetHashCode() + { + unchecked + { + var hashCode = base.GetHashCode(); + hashCode = (hashCode * 397) ^ (Id != null ? Id.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ EqualityComparer.Default.GetHashCode(Geometry); + hashCode = (hashCode * 397) ^ EqualityComparer.Default.GetHashCode(Properties); + return hashCode; + } + } + + public static bool operator ==(Feature left, Feature right) + { + return object.Equals(left, right); + } + + public static bool operator !=(Feature left, Feature right) + { + return !object.Equals(left, right); + } + } + + /// /// A GeoJSON Feature Object. /// @@ -156,7 +240,7 @@ public bool Equals(Feature left, Feature right) { return true; } - if (ReferenceEquals(null, right)) + if (ReferenceEquals(null, left)) { return false; }