Skip to content

Commit 44abb34

Browse files
authored
Merge pull request #32 from leojth/improve-position-reading
Handle null altitudes when reading positions
2 parents d490b51 + e51f9b5 commit 44abb34

7 files changed

+214
-10
lines changed

src/GeoJSON.Text.Test.Unit/GeoJSON.Text.Test.Unit.csproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
<None Remove="Feature\GenericFeatureTests_Can_Deserialize_Typed_Point_Feature.json" />
2323
<None Remove="Feature\GenericFeatureTests_Can_Serialize_Typed_Point_Feature.json" />
2424
<None Remove="Geometry\LineStringTests_Can_Deserialize.json" />
25+
<None Remove="Geometry\LineStringTests_Can_Deserialize_Strings.json" />
26+
<None Remove="Geometry\LineStringTests_Can_Deserialize_String_Literals.json" />
27+
<None Remove="Geometry\LineStringTests_Can_Deserialize_With_Altitude.json" />
2528
<None Remove="Geometry\LineStringTests_Can_Serialize.json" />
2629
<None Remove="Geometry\MultiLineStringTests_Can_Deserialize.json" />
2730
<None Remove="Geometry\MultiLineStringTests_Can_Serialize.json" />
@@ -48,6 +51,8 @@
4851
<EmbeddedResource Include="Feature\GenericFeatureTests_Can_Deserialize_Typed_Point_Feature.json" />
4952
<EmbeddedResource Include="Feature\GenericFeatureTests_Can_Serialize_Typed_Point_Feature.json" />
5053
<EmbeddedResource Include="Geometry\LineStringTests_Can_Deserialize.json" />
54+
<EmbeddedResource Include="Geometry\LineStringTests_Can_Deserialize_Strings.json" />
55+
<EmbeddedResource Include="Geometry\LineStringTests_Can_Deserialize_String_Literals.json" />
5156
<EmbeddedResource Include="Geometry\LineStringTests_Can_Serialize.json" />
5257
<EmbeddedResource Include="Geometry\MultiLineStringTests_Can_Deserialize.json" />
5358
<EmbeddedResource Include="Geometry\MultiLineStringTests_Can_Serialize.json" />
@@ -76,6 +81,9 @@
7681
<ItemGroup>
7782
<ProjectReference Include="..\GeoJSON.Text\GeoJSON.Text.csproj" />
7883
</ItemGroup>
84+
<ItemGroup>
85+
<EmbeddedResource Include="Geometry\LineStringTests_Can_Deserialize_With_Altitude.json" />
86+
</ItemGroup>
7987
<ItemGroup>
8088
<None Update="Feature\*.json">
8189
<CopyToOutputDirectory>Always</CopyToOutputDirectory>

src/GeoJSON.Text.Test.Unit/Geometry/LineStringTests.cs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,83 @@ public void Can_Deserialize()
8383
Assert.AreEqual(expectedLineString.Coordinates[0].Longitude, actualLineString.Coordinates[0].Longitude);
8484
}
8585

86+
[Test]
87+
public void Can_Deserialize_Strings()
88+
{
89+
var coordinates = new List<IPosition>
90+
{
91+
new Position(52.370725881211314, 4.889259338378906),
92+
new Position(52.3711451105601, 4.895267486572266),
93+
new Position(52.36931095278263, 4.892091751098633),
94+
new Position(52.370725881211314, 4.889259338378906)
95+
};
96+
97+
var expectedLineString = new LineString(coordinates);
98+
99+
var json = GetExpectedJson();
100+
var options = new JsonSerializerOptions { NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString };
101+
var actualLineString = JsonSerializer.Deserialize<LineString>(json, options);
102+
103+
Assert.AreEqual(expectedLineString, actualLineString);
104+
105+
Assert.AreEqual(4, actualLineString.Coordinates.Count);
106+
Assert.AreEqual(expectedLineString.Coordinates[0].Latitude, actualLineString.Coordinates[0].Latitude);
107+
Assert.AreEqual(expectedLineString.Coordinates[0].Longitude, actualLineString.Coordinates[0].Longitude);
108+
}
109+
110+
[Test]
111+
public void Can_Deserialize_With_Altitude()
112+
{
113+
var coordinates = new List<IPosition>
114+
{
115+
new Position(52.370725881211314, 4.889259338378906, 10.0),
116+
new Position(52.3711451105601, 4.895267486572266, 10.5),
117+
new Position(52.36931095278263, 4.892091751098633, null),
118+
new Position(52.370725881211314, 4.889259338378906, 10.2)
119+
};
120+
121+
var expectedLineString = new LineString(coordinates);
122+
123+
var json = GetExpectedJson();
124+
var actualLineString = JsonSerializer.Deserialize<LineString>(json);
125+
126+
Assert.AreEqual(expectedLineString, actualLineString);
127+
128+
Assert.AreEqual(4, actualLineString.Coordinates.Count);
129+
Assert.AreEqual(expectedLineString.Coordinates[0].Latitude, actualLineString.Coordinates[0].Latitude);
130+
Assert.AreEqual(expectedLineString.Coordinates[0].Longitude, actualLineString.Coordinates[0].Longitude);
131+
Assert.AreEqual(expectedLineString.Coordinates[0].Altitude, actualLineString.Coordinates[0].Altitude);
132+
Assert.AreEqual(expectedLineString.Coordinates[2].Altitude, actualLineString.Coordinates[2].Altitude);
133+
}
134+
135+
[Test]
136+
public void Can_Deserialize_String_Literals()
137+
{
138+
var coordinates = new List<IPosition>
139+
{
140+
new Position(52.370725881211314, 4.889259338378906, double.NegativeInfinity),
141+
new Position(52.3711451105601, 4.895267486572266, double.PositiveInfinity),
142+
new Position(52.36931095278263, 4.892091751098633, double.NaN),
143+
new Position(52.370725881211314, 4.889259338378906, double.NegativeInfinity)
144+
};
145+
146+
var expectedLineString = new LineString(coordinates);
147+
148+
var json = GetExpectedJson();
149+
var options = new JsonSerializerOptions { NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.AllowNamedFloatingPointLiterals };
150+
var actualLineString = JsonSerializer.Deserialize<LineString>(json, options);
151+
152+
bool b = expectedLineString.Coordinates[0].Equals(actualLineString.Coordinates[0]);
153+
Assert.AreEqual(expectedLineString, actualLineString);
154+
155+
Assert.AreEqual(4, actualLineString.Coordinates.Count);
156+
Assert.AreEqual(expectedLineString.Coordinates[0].Latitude, actualLineString.Coordinates[0].Latitude);
157+
Assert.AreEqual(expectedLineString.Coordinates[0].Longitude, actualLineString.Coordinates[0].Longitude);
158+
Assert.AreEqual(expectedLineString.Coordinates[0].Altitude, actualLineString.Coordinates[0].Altitude);
159+
Assert.AreEqual(expectedLineString.Coordinates[1].Altitude, actualLineString.Coordinates[1].Altitude);
160+
Assert.AreEqual(expectedLineString.Coordinates[2].Altitude, actualLineString.Coordinates[2].Altitude);
161+
}
162+
86163
[Test]
87164
public void Constructor_No_Coordinates_Throws_Exception()
88165
{
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"coordinates": [
3+
[4.8892593383789062, 52.370725881211314, "-Infinity"],
4+
[4.8952674865722656, 52.3711451105601, "Infinity"],
5+
[4.8920917510986328, 52.369310952782627, "NaN"],
6+
[4.8892593383789062, 52.370725881211314, "-Infinity"]
7+
],
8+
"type": "LineString"
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"coordinates": [
3+
["4.8892593383789062", "52.370725881211314"],
4+
["4.8952674865722656", "52.3711451105601"],
5+
["4.8920917510986328", "52.369310952782627"],
6+
["4.8892593383789062", "52.370725881211314"]
7+
],
8+
"type": "LineString"
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"coordinates": [
3+
[4.8892593383789062, 52.370725881211314, 10.0],
4+
[4.8952674865722656, 52.3711451105601, 10.5],
5+
[4.8920917510986328, 52.369310952782627, null],
6+
[4.8892593383789062, 52.370725881211314, 10.2]
7+
],
8+
"type": "LineString"
9+
}

src/GeoJSON.Text/Converters/PositionConverter.cs

Lines changed: 98 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
using GeoJSON.Text.Geometry;
44
using System;
5+
using System.Collections.Generic;
56
using System.Text.Json;
67
using System.Text.Json.Serialization;
78

@@ -28,10 +29,9 @@ public override bool CanConvert(Type objectType)
2829
/// <summary>
2930
/// Reads the JSON representation of the object.
3031
/// </summary>
31-
/// <param name="reader">The <see cref="T:Newtonsoft.Json.JsonReader" /> to read from.</param>
32-
/// <param name="objectType">Type of the object.</param>
33-
/// <param name="existingValue">The existing value of object being read.</param>
34-
/// <param name="serializer">The calling serializer.</param>
32+
/// <param name="reader">The <see cref="T:System.Text.Json.Utf8JsonReader" /> to read from.</param>
33+
/// <param name="type">Type of the object.</param>
34+
/// <param name="options">Serializer options.</param>
3535
/// <returns>
3636
/// The object value.
3737
/// </returns>
@@ -40,17 +40,106 @@ public override IPosition Read(
4040
Type type,
4141
JsonSerializerOptions options)
4242
{
43-
double[] coordinates;
44-
4543
try
46-
{
47-
coordinates = JsonSerializer.Deserialize<double[]>(ref reader, options);
44+
{
45+
if (reader.TokenType != JsonTokenType.StartArray)
46+
{
47+
throw new ArgumentException("Expected start of array");
48+
}
49+
50+
double lng, lat;
51+
double? alt;
52+
53+
// Read longitude
54+
if (!reader.Read())
55+
{
56+
throw new ArgumentException("Expected number, but got end of data");
57+
}
58+
59+
if (reader.TokenType == JsonTokenType.EndArray)
60+
{
61+
throw new ArgumentException("Expected 2 or 3 coordinates but got 0");
62+
}
63+
64+
if (reader.TokenType == JsonTokenType.Number)
65+
{
66+
lng = reader.GetDouble();
67+
}
68+
else if (reader.TokenType == JsonTokenType.String)
69+
{
70+
lng = JsonSerializer.Deserialize<double>(ref reader, options);
71+
}
72+
else
73+
{
74+
throw new ArgumentException("Expected number but got other type");
75+
}
76+
77+
// Read latitude
78+
if (!reader.Read())
79+
{
80+
throw new ArgumentException("Expected number, but got end of data");
81+
}
82+
83+
if (reader.TokenType == JsonTokenType.EndArray)
84+
{
85+
throw new ArgumentException("Expected 2 or 3 coordinates but got 1");
86+
}
87+
88+
if (reader.TokenType == JsonTokenType.Number)
89+
{
90+
lat = reader.GetDouble();
91+
}
92+
else if (reader.TokenType == JsonTokenType.String)
93+
{
94+
lat = JsonSerializer.Deserialize<double>(ref reader, options);
95+
}
96+
else
97+
{
98+
throw new ArgumentException("Expected number but got other type");
99+
}
100+
101+
// Read altitude, or return if end of array is found
102+
if (!reader.Read())
103+
{
104+
throw new ArgumentException("Unexpected end of data");
105+
}
106+
if (reader.TokenType == JsonTokenType.EndArray)
107+
{
108+
return new Position(lat, lng);
109+
}
110+
else if (reader.TokenType == JsonTokenType.Null)
111+
{
112+
alt = null;
113+
}
114+
else if (reader.TokenType == JsonTokenType.Number)
115+
{
116+
alt = reader.GetDouble();
117+
}
118+
else if (reader.TokenType == JsonTokenType.String)
119+
{
120+
alt = JsonSerializer.Deserialize<double>(ref reader, options);
121+
}
122+
else
123+
{
124+
throw new ArgumentException("Expected number but got other type");
125+
}
126+
127+
// Check what comes next. Expects end of array.
128+
if (!reader.Read())
129+
{
130+
throw new ArgumentException("Expected end of array, but got end of data");
131+
}
132+
if (reader.TokenType != JsonTokenType.EndArray)
133+
{
134+
throw new ArgumentException("Expected 2 or 3 coordinates but got >= 4");
135+
}
136+
137+
return new Position(lat, lng, alt);
48138
}
49139
catch (Exception e)
50140
{
51141
throw new JsonException("Error parsing coordinates", e);
52142
}
53-
return coordinates?.ToPosition() ?? throw new JsonException("Coordinates cannot be null");
54143
}
55144

56145
/// <summary>

src/GeoJSON.Text/DoubleTenDecimalPlaceComparer.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ public class DoubleTenDecimalPlaceComparer : IEqualityComparer<double>
1515
{
1616
public bool Equals(double x, double y)
1717
{
18-
return Math.Abs(x - y) < 0.0000000001;
18+
return (double.IsNaN(x) && double.IsNaN(y)) ||
19+
(double.IsInfinity(x) && double.IsInfinity(y)) ||
20+
(double.IsNegativeInfinity(x) && double.IsNegativeInfinity(y)) ||
21+
Math.Abs(x - y) < 0.0000000001;
1922
}
2023

2124
public int GetHashCode(double obj)

0 commit comments

Comments
 (0)