Skip to content

Commit 9d579d6

Browse files
supermihimatt-lethargic
authored andcommitted
Generic feature (#85)
* Add generic Feature<TGeometry, TProps> class * add visual studio settings folder to .gitignore * add tests for generic Feature<,> and fix a bug found with them * move generic Feature tests to dedicated source file * add Nunit3TestAdapter package to GeoJSON.Net.Tests Allows to run unit tests in visual studio without installing the VSIX extension. This appears to be the recommended way of running tests within VS now. Also, in VS2017 it was the only way I managed to run the test (and only using the 3.8.0 alpha version of the package). * Add test for deserializing typed feature * update NUnit packages (stable version now supports VS 2017 and .NET core)
1 parent ffef6a3 commit 9d579d6

8 files changed

Lines changed: 221 additions & 66 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ lib/*
55

66
#OSX
77
.DS_Store
8+
/.vs

src/GeoJSON.Net.Tests/Feature/FeatureTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4+
using GeoJSON.Net.Feature;
45
using GeoJSON.Net.Geometry;
56
using Newtonsoft.Json;
67
using NUnit.Framework;
Lines changed: 102 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,102 @@
1-
using System.Linq;
2-
using GeoJSON.Net.Geometry;
3-
using Newtonsoft.Json;
4-
using NUnit.Framework;
5-
6-
namespace GeoJSON.Net.Tests.Feature
7-
{
8-
[TestFixture]
9-
internal class GenericFeatureTests : TestBase
10-
{
11-
[Test]
12-
public void Can_Deserialize_Point_Feature()
13-
{
14-
var json = GetExpectedJson();
15-
16-
var feature = JsonConvert.DeserializeObject<Net.Feature.Feature<Point>>(json);
17-
18-
Assert.IsNotNull(feature);
19-
Assert.IsNotNull(feature.Properties);
20-
Assert.IsTrue(feature.Properties.Any());
21-
22-
Assert.IsTrue(feature.Properties.ContainsKey("name"));
23-
Assert.AreEqual("Dinagat Islands", feature.Properties["name"]);
24-
25-
Assert.AreEqual("test-id", feature.Id);
26-
27-
Assert.AreEqual(GeoJSONObjectType.Point, feature.Geometry.Type);
28-
Assert.AreEqual(125.6, feature.Geometry.Coordinates.Longitude);
29-
Assert.AreEqual(10.1, feature.Geometry.Coordinates.Latitude);
30-
Assert.AreEqual(456, feature.Geometry.Coordinates.Altitude);
31-
}
32-
33-
[Test]
34-
public void Can_Deserialize_LineString_Feature()
35-
{
36-
var json = GetExpectedJson();
37-
38-
var feature = JsonConvert.DeserializeObject<Net.Feature.Feature<LineString>>(json);
39-
40-
Assert.IsNotNull(feature);
41-
Assert.IsNotNull(feature.Properties);
42-
Assert.IsTrue(feature.Properties.Any());
43-
44-
Assert.IsTrue(feature.Properties.ContainsKey("name"));
45-
Assert.AreEqual("Dinagat Islands", feature.Properties["name"]);
46-
47-
Assert.AreEqual("test-id", feature.Id);
48-
49-
Assert.AreEqual(GeoJSONObjectType.LineString, feature.Geometry.Type);
50-
51-
Assert.AreEqual(4, feature.Geometry.Coordinates.Count);
52-
53-
//Assert.AreEqual(125.6, feature.Geometry.Coordinates.Longitude);
54-
//Assert.AreEqual(10.1, feature.Geometry.Coordinates.Latitude);
55-
//Assert.AreEqual(456, feature.Geometry.Coordinates.Altitude);
56-
}
57-
}
58-
}
1+
using System.Linq;
2+
using GeoJSON.Net.Feature;
3+
using GeoJSON.Net.Geometry;
4+
using Newtonsoft.Json;
5+
using NUnit.Framework;
6+
7+
namespace GeoJSON.Net.Tests.Feature
8+
{
9+
[TestFixture]
10+
internal class GenericFeatureTests : TestBase
11+
{
12+
[Test]
13+
public void Can_Deserialize_Point_Feature()
14+
{
15+
var json = GetExpectedJson();
16+
17+
var feature = JsonConvert.DeserializeObject<Feature<Point>>(json);
18+
19+
Assert.IsNotNull(feature);
20+
Assert.IsNotNull(feature.Properties);
21+
Assert.IsTrue(feature.Properties.Any());
22+
23+
Assert.IsTrue(feature.Properties.ContainsKey("name"));
24+
Assert.AreEqual("Dinagat Islands", feature.Properties["name"]);
25+
26+
Assert.AreEqual("test-id", feature.Id);
27+
28+
Assert.AreEqual(GeoJSONObjectType.Point, feature.Geometry.Type);
29+
Assert.AreEqual(125.6, feature.Geometry.Coordinates.Longitude);
30+
Assert.AreEqual(10.1, feature.Geometry.Coordinates.Latitude);
31+
Assert.AreEqual(456, feature.Geometry.Coordinates.Altitude);
32+
}
33+
34+
[Test]
35+
public void Can_Deserialize_LineString_Feature()
36+
{
37+
var json = GetExpectedJson();
38+
39+
var feature = JsonConvert.DeserializeObject<Feature<LineString>>(json);
40+
41+
Assert.IsNotNull(feature);
42+
Assert.IsNotNull(feature.Properties);
43+
Assert.IsTrue(feature.Properties.Any());
44+
45+
Assert.IsTrue(feature.Properties.ContainsKey("name"));
46+
Assert.AreEqual("Dinagat Islands", feature.Properties["name"]);
47+
48+
Assert.AreEqual("test-id", feature.Id);
49+
50+
Assert.AreEqual(GeoJSONObjectType.LineString, feature.Geometry.Type);
51+
52+
Assert.AreEqual(4, feature.Geometry.Coordinates.Count);
53+
54+
//Assert.AreEqual(125.6, feature.Geometry.Coordinates.Longitude);
55+
//Assert.AreEqual(10.1, feature.Geometry.Coordinates.Latitude);
56+
//Assert.AreEqual(456, feature.Geometry.Coordinates.Altitude);
57+
}
58+
59+
private class TypedFeatureProps
60+
{
61+
[JsonProperty("name")]
62+
public string Name { get; set; }
63+
[JsonProperty("value")]
64+
public double Value { get; set; }
65+
}
66+
67+
[Test]
68+
public void Can_Deserialize_Typed_Point_Feature()
69+
{
70+
var json = GetExpectedJson();
71+
var feature = JsonConvert.DeserializeObject<Feature<Point, TypedFeatureProps>>(json);
72+
73+
Assert.IsNotNull(feature);
74+
75+
Assert.IsNotNull(feature.Properties);
76+
Assert.AreEqual(feature.Properties.Name, "Dinagat Islands");
77+
Assert.AreEqual(feature.Properties.Value, 4.2);
78+
79+
Assert.AreEqual(feature.Id, "test-id");
80+
81+
Assert.AreEqual(feature.Geometry.Type, GeoJSONObjectType.Point);
82+
}
83+
84+
85+
[Test]
86+
public void Can_Serialize_Typed_Point_Feature()
87+
{
88+
var geometry = new Point(new Position(1, 2));
89+
var props = new TypedFeatureProps
90+
{
91+
Name = "no name here",
92+
Value = 1.337
93+
};
94+
var feature = new Feature<Point, TypedFeatureProps>(geometry, props, "no id there");
95+
96+
var expectedJson = GetExpectedJson();
97+
var actualJson = JsonConvert.SerializeObject(feature);
98+
99+
JsonAssert.AreEqual(expectedJson, actualJson);
100+
}
101+
}
102+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"type": "Feature",
3+
"id": "test-id",
4+
"geometry": {
5+
"type": "Point",
6+
"coordinates": [ 125.6, 10.1 ]
7+
},
8+
"properties": {
9+
"name": "Dinagat Islands",
10+
"value": 4.2
11+
}
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"geometry": {
3+
"coordinates": [ 2.0, 1.0 ],
4+
"type": "Point"
5+
},
6+
"properties": {
7+
"name": "no name here",
8+
"value": 1.337
9+
},
10+
"id": "no id there",
11+
"type": "Feature"
12+
}

src/GeoJSON.Net.Tests/GeoJSON.Net.Tests.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
</PropertyGroup>
1010
<ItemGroup>
1111
<ProjectReference Include="..\GeoJSON.Net\GeoJSON.Net.csproj" />
12-
<PackageReference Include="NUnit" Version="3.7.0" />
13-
<PackageReference Include="NUnit3TestAdapter" Version="3.8.0-alpha1" />
12+
<PackageReference Include="NUnit" Version="3.7.1" />
13+
<PackageReference Include="NUnit3TestAdapter" Version="3.8.0" />
1414
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
1515
</ItemGroup>
1616
<ItemGroup>
@@ -23,5 +23,5 @@
2323
</ItemGroup>
2424
<ItemGroup>
2525
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
26-
</ItemGroup>
26+
</ItemGroup>
2727
</Project>
Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
<?xml version="1.0" encoding="utf-8"?>
2-
<packages>
3-
<package id="Newtonsoft.Json" version="10.0.2" targetFramework="net45" />
4-
<package id="NUnit" version="3.7.0" targetFramework="net461" />
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<packages>
3+
<package id="Newtonsoft.Json" version="10.0.2" targetFramework="net45" />
4+
<package id="NUnit" version="3.7.0" targetFramework="net461" />
5+
<package id="NUnit3TestAdapter" version="3.8.0-alpha1" targetFramework="net45" />
56
</packages>

src/GeoJSON.Net/Feature/Feature.cs

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,90 @@
1212

1313
namespace GeoJSON.Net.Feature
1414
{
15+
/// <summary>
16+
/// A GeoJSON Feature Object; generic version for strongly typed <see cref="Geometry"/>
17+
/// and <see cref="Properties"/>
18+
/// </summary>
19+
/// <remarks>
20+
/// See https://tools.ietf.org/html/rfc7946#section-3.2
21+
/// </remarks>
22+
public class Feature<TGeometry, TProps> : GeoJSONObject, IEquatable<Feature<TGeometry, TProps>>
23+
where TGeometry : IGeometryObject
24+
{
25+
[JsonConstructor]
26+
public Feature(TGeometry geometry, TProps properties, string id = null)
27+
{
28+
Geometry = geometry;
29+
Properties = properties;
30+
Id = id;
31+
32+
Type = GeoJSONObjectType.Feature;
33+
}
34+
35+
[JsonProperty(PropertyName = "id", NullValueHandling = NullValueHandling.Ignore)]
36+
public string Id { get; }
37+
38+
[JsonProperty(PropertyName = "geometry", Required = Required.AllowNull)]
39+
[JsonConverter(typeof(GeometryConverter))]
40+
public TGeometry Geometry { get; }
41+
42+
[JsonProperty(PropertyName = "properties", Required = Required.AllowNull)]
43+
public TProps Properties { get;}
44+
45+
/// <summary>
46+
/// Equality comparer.
47+
/// </summary>
48+
/// <remarks>
49+
/// In contrast to <see cref="Feature.Equals(Feature)"/>, this implementation returns true only
50+
/// if <see cref="Id"/> and <see cref="Properties"/> are also equal. See
51+
/// <a href="https://github.com/GeoJSON-Net/GeoJSON.Net/issues/80">#80</a> for discussion. The rationale
52+
/// here is that a user explicitly specifying the property type most probably cares about the properties
53+
/// equality.
54+
/// </remarks>
55+
/// <param name="other"></param>
56+
/// <returns></returns>
57+
public bool Equals(Feature<TGeometry, TProps> other)
58+
{
59+
if (ReferenceEquals(null, other)) return false;
60+
if (ReferenceEquals(this, other)) return true;
61+
return base.Equals(other)
62+
&& string.Equals(Id, other.Id)
63+
&& EqualityComparer<TGeometry>.Default.Equals(Geometry, other.Geometry)
64+
&& EqualityComparer<TProps>.Default.Equals(Properties, other.Properties);
65+
}
66+
67+
public override bool Equals(object obj)
68+
{
69+
if (ReferenceEquals(null, obj)) return false;
70+
if (ReferenceEquals(this, obj)) return true;
71+
if (obj.GetType() != GetType()) return false;
72+
return Equals((Feature<TGeometry, TProps>) obj);
73+
}
74+
75+
public override int GetHashCode()
76+
{
77+
unchecked
78+
{
79+
var hashCode = base.GetHashCode();
80+
hashCode = (hashCode * 397) ^ (Id != null ? Id.GetHashCode() : 0);
81+
hashCode = (hashCode * 397) ^ EqualityComparer<TGeometry>.Default.GetHashCode(Geometry);
82+
hashCode = (hashCode * 397) ^ EqualityComparer<TProps>.Default.GetHashCode(Properties);
83+
return hashCode;
84+
}
85+
}
86+
87+
public static bool operator ==(Feature<TGeometry, TProps> left, Feature<TGeometry, TProps> right)
88+
{
89+
return object.Equals(left, right);
90+
}
91+
92+
public static bool operator !=(Feature<TGeometry, TProps> left, Feature<TGeometry, TProps> right)
93+
{
94+
return !object.Equals(left, right);
95+
}
96+
}
97+
98+
1599
/// <summary>
16100
/// A GeoJSON Feature Object.
17101
/// </summary>
@@ -156,7 +240,7 @@ public bool Equals(Feature<TGeometry> left, Feature<TGeometry> right)
156240
{
157241
return true;
158242
}
159-
if (ReferenceEquals(null, right))
243+
if (ReferenceEquals(null, left))
160244
{
161245
return false;
162246
}

0 commit comments

Comments
 (0)