Skip to content

Commit

Permalink
Use PrecisionModel on in- and output (#27)
Browse files Browse the repository at this point in the history
* allow to specify number of dimensions to handle. 2 and 3 are allowed,
  default is 3
* move Wgs84Factory to GeoJsonSerializer

resolves #27
  • Loading branch information
FObermaier committed Apr 24, 2019
1 parent 3238a4a commit 3392aa6
Show file tree
Hide file tree
Showing 8 changed files with 230 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ public void TestWriteFeatureCollectionWithFirstFeatureGeometryNull()
// Setup
var geoJsonWriter = new GeoJsonWriter();

var featureJson =
string featureJson =
"{\"type\": \"Feature\",\"geometry\": {\"type\": \"LineString\",\"coordinates\": [[0,0],[2,2],[3,2]]},\"properties\": {\"key\": \"value\"}}";
var notNullGeometryFeature = new GeoJsonReader().Read<Feature>(featureJson);

Expand Down Expand Up @@ -259,5 +259,108 @@ public void TestCoordinatesWithNullOrdinates()
Assert.That(g is IPoint);
Assert.That(!g.IsEmpty);
}

[NtsIssueNumber(27)]
[Test]
public void TestOutputPrecision()
{
var coords = new[]
{
new Coordinate(0.001, 0.001),
new Coordinate(10.1, 0.002),
new Coordinate(10, 10.1),
new Coordinate(0.05, 9.999),
new Coordinate(0.001, 0.001)
};

// Create a factory with scale = 10
var factory = new GeometryFactory(new PrecisionModel(10), 4326);

// Creating the polygon with the above factory
var polygon = factory.CreatePolygon(coords);

var serializer = GeoJsonSerializer.Create(factory);
var writer = new StringWriter();
serializer.Serialize(writer, polygon);

string json = writer.ToString();
Assert.That(json, Is.EqualTo("{\"type\":\"Polygon\",\"coordinates\":[[[0.0,0.0],[10.1,0.0],[10.0,10.1],[0.1,10.0],[0.0,0.0]]]}"));

var gjWriter = new GeoJsonWriter();
string json2 = gjWriter.Write(polygon);
Assert.That(json2, Is.EqualTo("{\"type\":\"Polygon\",\"coordinates\":[[[0.0,0.0],[10.1,0.0],[10.0,10.1],[0.1,10.0],[0.0,0.0]]]}"));
}

[Test]
public void TestOutputDimension()
{
var coords = new[]
{
new Coordinate(0.001, 0.001, 3),
new Coordinate(10.1, 0.002, 3),
new Coordinate(10, 10.1, 3),
new Coordinate(0.05, 9.999, 3),
new Coordinate(0.001, 0.001, 3)
};

// Create a factory with scale = 10
var factory = new GeometryFactory(new PrecisionModel(10), 4326);

// Creating the polygon with the above factory
var polygon = factory.CreatePolygon(coords);

var serializer2 = GeoJsonSerializer.Create(factory, 2);
var serializer3 = GeoJsonSerializer.Create(factory, 3);
var writer = new StringWriter();
serializer2.Serialize(writer, polygon);

string json2 = writer.ToString();
Assert.That(json2, Is.EqualTo("{\"type\":\"Polygon\",\"coordinates\":[[[0.0,0.0],[10.1,0.0],[10.0,10.1],[0.1,10.0],[0.0,0.0]]]}"));

writer = new StringWriter();
serializer3.Serialize(writer, polygon);
string json3 = writer.ToString();
Assert.That(json3, Is.EqualTo("{\"type\":\"Polygon\",\"coordinates\":[[[0.0,0.0,3.0],[10.1,0.0,3.0],[10.0,10.1,3.0],[0.1,10.0,3.0],[0.0,0.0,3.0]]]}"));
}

[NtsIssueNumber(27)]
[Test]
public void TestInputPrecision()
{
// Create a factory with scale = 10
var factory = new GeometryFactory(new PrecisionModel(10), 4326);

var serializer = GeoJsonSerializer.Create(factory);
var reader = new JsonTextReader(new StringReader("{\"type\":\"Polygon\",\"coordinates\":[[[0.001,0.001],[10.1,0.002],[10.0,10.1],[0.05,9.999],[0.001,0.001]]]}"));
var geom = serializer.Deserialize<IGeometry>(reader);
Assert.That(geom.AsText(), Is.EqualTo("POLYGON ((0 0, 10.1 0, 10 10.1, 0.1 10, 0 0))"));
}

[NtsIssueNumber(27)]
[Test]
public void TestInputDimension()
{
var coords = new[]
{
new Coordinate(0.001, 0.001),
new Coordinate(10.1, 0.002),
new Coordinate(10, 10.1),
new Coordinate(0.05, 9.999),
new Coordinate(0.001, 0.001)
};

// Create a factory with scale = 10
var factory = new GeometryFactory(new PrecisionModel(10), 4326);

var serializer3 = GeoJsonSerializer.Create(factory, 3);
var reader = new JsonTextReader(new StringReader("{\"type\":\"Polygon\",\"coordinates\":[[[0.001,0.001,3.0],[10.1,0.002,3.0],[10.0,10.1,3.0],[0.05,9.999,3.0],[0.001,0.001,3.0]]]}"));
var geom = serializer3.Deserialize<IGeometry>(reader);
Assert.That(geom.AsText(), Is.EqualTo("POLYGON ((0 0 3, 10.1 0 3, 10 10.1 3, 0.1 10 3, 0 0 3))"));

var serializer2 = GeoJsonSerializer.Create(factory, 2);
reader = new JsonTextReader(new StringReader("{\"type\":\"Polygon\",\"coordinates\":[[[0.001,0.001,3.0],[10.1,0.002,3.0],[10.0,10.1,3.0],[0.05,9.999,3.0],[0.001,0.001,3.0]]]}"));
geom = serializer2.Deserialize<IGeometry>(reader);
Assert.That(geom.AsText(), Is.EqualTo("POLYGON ((0 0, 10.1 0, 10 10.1, 0.1 10, 0 0))"));
}
}
}
52 changes: 39 additions & 13 deletions NetTopologySuite.IO.GeoJSON/Converters/CoordinateConverters.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;

using System.Globalization;
using GeoAPI.Geometries;

using NetTopologySuite.Geometries;
using Newtonsoft.Json;

namespace NetTopologySuite.IO.Converters
Expand All @@ -13,6 +13,27 @@ namespace NetTopologySuite.IO.Converters
/// </summary>
public class CoordinateConverter : JsonConverter
{
private readonly IPrecisionModel _precisionModel;
private readonly int _dimension;

/// <summary>
/// Creates an instance of this class using a floating precision model and <see cref="GeoJsonSerializer.DefaultDimension"/> output dimensions
/// </summary>
internal CoordinateConverter()
: this(GeometryFactory.Floating.PrecisionModel)
{ }

/// <summary>
/// Creates an instance of this class
/// </summary>
/// <param name="precisionModel">The precision model to use for writing</param>
/// <param name="dimension">The number of dimensions</param>
internal CoordinateConverter(IPrecisionModel precisionModel, int dimension = GeoJsonSerializer.DefaultDimension)
{
_precisionModel = precisionModel;
_dimension = dimension;
}

/// <summary>
/// Writes a coordinate, a coordinate sequence or an enumeration of coordinates to JSON
/// </summary>
Expand Down Expand Up @@ -64,9 +85,12 @@ protected void WriteJsonCoordinate(JsonWriter writer, Coordinate coordinate, Jso
{
writer.WriteStartArray();

writer.WriteValue(coordinate.X);
writer.WriteValue(coordinate.Y);
if (!double.IsNaN(coordinate.Z))
double value = _precisionModel.MakePrecise(coordinate.X);
writer.WriteValue(value);
value = _precisionModel.MakePrecise(coordinate.Y);
writer.WriteValue(value);

if (_dimension > 2 && !double.IsNaN(coordinate.Z))
writer.WriteValue(coordinate.Z);

writer.WriteEndArray();
Expand Down Expand Up @@ -126,33 +150,35 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist

}

private static Coordinate ReadJsonCoordinate(JsonReader reader)
private Coordinate ReadJsonCoordinate(JsonReader reader)
{
reader.Read();
if (reader.TokenType != JsonToken.StartArray)
return null;

Coordinate c = new Coordinate();
var c = new Coordinate();

reader.Read();
Debug.Assert(reader.TokenType == JsonToken.Float || reader.TokenType == JsonToken.Integer);
c.X = Convert.ToDouble(reader.Value);
c.X = _precisionModel.MakePrecise(Convert.ToDouble(reader.Value));

reader.Read();
Debug.Assert(reader.TokenType == JsonToken.Float || reader.TokenType == JsonToken.Integer);
c.Y = Convert.ToDouble(reader.Value);
c.Y = _precisionModel.MakePrecise(Convert.ToDouble(reader.Value));

reader.Read();
if (reader.TokenType == JsonToken.Float || reader.TokenType == JsonToken.Integer)
{
c.Z = Convert.ToDouble(reader.Value);
double value = Convert.ToDouble(reader.Value);
if (_dimension > 2)
c.Z = value;
reader.Read();
}
Debug.Assert(reader.TokenType == JsonToken.EndArray);
return c;
}

private static Coordinate[] ReadJsonCoordinates(JsonReader reader)
private Coordinate[] ReadJsonCoordinates(JsonReader reader)
{
reader.Read();
if (reader.TokenType != JsonToken.StartArray) return null;
Expand All @@ -168,7 +194,7 @@ private static Coordinate[] ReadJsonCoordinates(JsonReader reader)
return coordinates.ToArray();
}

private static List<Coordinate[]> ReadJsonCoordinatesEnumerable(JsonReader reader)
private List<Coordinate[]> ReadJsonCoordinatesEnumerable(JsonReader reader)
{
reader.Read();
if (reader.TokenType != JsonToken.StartArray) return null;
Expand All @@ -184,7 +210,7 @@ private static List<Coordinate[]> ReadJsonCoordinatesEnumerable(JsonReader reade
return coordinates;
}

private static List<List<Coordinate[]>> ReadJsonCoordinatesEnumerable2(JsonReader reader)
private List<List<Coordinate[]>> ReadJsonCoordinatesEnumerable2(JsonReader reader)
{
reader.Read();
if (reader.TokenType != JsonToken.StartArray) return null;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using GeoAPI.Geometries;
using NetTopologySuite.Geometries;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

Expand All @@ -15,9 +14,9 @@ public class GeometryArrayConverter : JsonConverter
private readonly IGeometryFactory _factory;

/// <summary>
/// Creates an instance of this class using <see cref="GeoJsonReader.Wgs84Factory"/>
/// Creates an instance of this class using <see cref="GeoJsonSerializer.Wgs84Factory"/>
/// </summary>
public GeometryArrayConverter() : this(GeoJsonReader.Wgs84Factory) { }
public GeometryArrayConverter() : this(GeoJsonSerializer.Wgs84Factory) { }

/// <summary>
/// Creates an instance of this class using the provided <see cref="IGeometryFactory"/>
Expand Down
21 changes: 15 additions & 6 deletions NetTopologySuite.IO.GeoJSON/Converters/GeometryConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,27 @@ namespace NetTopologySuite.IO.Converters
public class GeometryConverter : JsonConverter
{
private readonly IGeometryFactory _factory;
private readonly int _dimension;
/// <summary>
/// Creates an instance of this class using <see cref="GeoJsonSerializer.Wgs84Factory"/> to create geometries.
/// </summary>
public GeometryConverter() : this(GeoJsonSerializer.Wgs84Factory) { }

/// <summary>
/// Creates an instance of this class using <see cref="GeoJsonReader.Wgs84Factory"/> to create geometries.
/// Creates an instance of this class using the provided <see cref="IGeometryFactory"/> to create geometries.
/// </summary>
public GeometryConverter() : this(GeoJsonReader.Wgs84Factory) { }
/// <param name="geometryFactory">The geometry factory.</param>
public GeometryConverter(IGeometryFactory geometryFactory) : this(geometryFactory, GeoJsonSerializer.DefaultDimension) { }

/// <summary>
/// Creates an instance of this class using the provided <see cref="IGeometryFactory"/> to create geometries.
/// </summary>
/// <param name="geometryFactory">The geometry factory.</param>
public GeometryConverter(IGeometryFactory geometryFactory)
/// <param name="dimension">The number of dimension to handle. Valid input are 2 and 3</param>
public GeometryConverter(IGeometryFactory geometryFactory, int dimension)
{
_factory = geometryFactory;
_dimension = dimension;
}

/// <summary>
Expand Down Expand Up @@ -219,7 +227,8 @@ private Coordinate GetPointCoordinate(IList list)

c.X = _factory.PrecisionModel.MakePrecise(Convert.ToDouble(list[0]));
c.Y = _factory.PrecisionModel.MakePrecise(Convert.ToDouble(list[1]));
if (list.Count > 2)

if (list.Count > 2 && _dimension > 2)
c.Z = Convert.ToDouble(list[2]);
return c;
}
Expand All @@ -235,7 +244,7 @@ private Coordinate[] GetLineStringCoordinates(IEnumerable list)
return coordinates.ToArray();
}

private static List<Coordinate[]> GetPolygonCoordinates(IEnumerable list)
private List<Coordinate[]> GetPolygonCoordinates(IEnumerable list)
{
var coordinates = new List<Coordinate[]>();
foreach (List<object> coord in list)
Expand All @@ -245,7 +254,7 @@ private static List<Coordinate[]> GetPolygonCoordinates(IEnumerable list)
return coordinates;
}

private static IEnumerable<List<Coordinate[]>> GetMultiPolygonCoordinates(IEnumerable list)
private IEnumerable<List<Coordinate[]>> GetMultiPolygonCoordinates(IEnumerable list)
{
var coordinates = new List<List<Coordinate[]>>();
foreach (List<object> coord in list)
Expand Down
24 changes: 20 additions & 4 deletions NetTopologySuite.IO.GeoJSON/GeoJsonReader.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.IO;
using System;
using System.IO;
using GeoAPI.Geometries;
using NetTopologySuite.Geometries;
using Newtonsoft.Json;
Expand All @@ -14,16 +15,18 @@ public class GeoJsonReader
/// <summary>
/// Gets a default GeometryFactory
/// </summary>
public static IGeometryFactory Wgs84Factory { get; } = new GeometryFactory(new PrecisionModel(), 4326);
[Obsolete("Create your own, internally we use GeoJsonSerializer.Wgs84Factory")]
public static IGeometryFactory Wgs84Factory { get; } = GeoJsonSerializer.Wgs84Factory;

private readonly IGeometryFactory _factory;
private readonly JsonSerializerSettings _serializerSettings;
private readonly int _dimension;

/// <summary>
/// Creates an instance of this class
/// </summary>
public GeoJsonReader()
: this(Wgs84Factory, new JsonSerializerSettings())
: this(GeoJsonSerializer.Wgs84Factory, new JsonSerializerSettings())
{
}

Expand All @@ -34,9 +37,22 @@ public GeoJsonReader()
/// <param name="factory">The factory to use when creating geometries</param>
/// <param name="serializerSettings">The serializer setting</param>
public GeoJsonReader(IGeometryFactory factory, JsonSerializerSettings serializerSettings)
: this(factory, serializerSettings, GeoJsonSerializer.DefaultDimension)
{
}

/// <summary>
/// Creates an instance of this class using the provided <see cref="IGeometryFactory"/> and
/// <see cref="JsonSerializerSettings"/>.
/// </summary>
/// <param name="factory">The factory to use when creating geometries</param>
/// <param name="serializerSettings">The serializer setting</param>
/// <param name="dimension">The number of dimensions to handle</param>
public GeoJsonReader(IGeometryFactory factory, JsonSerializerSettings serializerSettings, int dimension)
{
_factory = factory;
_serializerSettings = serializerSettings;
_dimension = dimension;
}

/// <summary>
Expand All @@ -48,7 +64,7 @@ public GeoJsonReader(IGeometryFactory factory, JsonSerializerSettings serializer
public TObject Read<TObject>(string json)
where TObject : class
{
var g = GeoJsonSerializer.Create(_serializerSettings, _factory);
var g = GeoJsonSerializer.Create(_serializerSettings, _factory, _dimension);
using (StringReader sr = new StringReader(json))
{
return g.Deserialize<TObject>(new JsonTextReader(sr));
Expand Down
Loading

0 comments on commit 3392aa6

Please sign in to comment.