From 101b8136287a7c0ddd2408209038c8017800a933 Mon Sep 17 00:00:00 2001 From: Markus Jarderot Date: Sun, 29 Oct 2023 02:10:57 +0200 Subject: [PATCH] Respect the `PrecisionModel` grid when serializing coordinates. (#135) --- .../Converters/GeoJsonConverterFactory.cs | 2 +- .../Converters/StjEnvelopeConverter.cs | 27 ++++++++------ .../StjGeometryConverter.Coordinates.cs | 9 +++-- .../Converters/Utility.cs | 19 ++++++++++ .../Issues/Issue135.cs | 37 +++++++++++++++++++ 5 files changed, 78 insertions(+), 16 deletions(-) create mode 100644 test/NetTopologySuite.IO.GeoJSON4STJ.Test/Issues/Issue135.cs diff --git a/src/NetTopologySuite.IO.GeoJSON4STJ/Converters/GeoJsonConverterFactory.cs b/src/NetTopologySuite.IO.GeoJSON4STJ/Converters/GeoJsonConverterFactory.cs index 7a3a94d..668ee6f 100644 --- a/src/NetTopologySuite.IO.GeoJSON4STJ/Converters/GeoJsonConverterFactory.cs +++ b/src/NetTopologySuite.IO.GeoJSON4STJ/Converters/GeoJsonConverterFactory.cs @@ -202,7 +202,7 @@ public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializer if (GeometryTypes.Contains(typeToConvert)) return new StjGeometryConverter(_factory, _writeGeometryBBox, _ringOrientationOption); if (typeToConvert == typeof(Envelope)) - return new StjEnvelopeConverter(); + return new StjEnvelopeConverter(_factory.PrecisionModel); if (typeToConvert == typeof(FeatureCollection)) return new StjFeatureCollectionConverter(_writeGeometryBBox); if (typeof(IFeature).IsAssignableFrom(typeToConvert)) diff --git a/src/NetTopologySuite.IO.GeoJSON4STJ/Converters/StjEnvelopeConverter.cs b/src/NetTopologySuite.IO.GeoJSON4STJ/Converters/StjEnvelopeConverter.cs index e3c866b..6ef07fa 100644 --- a/src/NetTopologySuite.IO.GeoJSON4STJ/Converters/StjEnvelopeConverter.cs +++ b/src/NetTopologySuite.IO.GeoJSON4STJ/Converters/StjEnvelopeConverter.cs @@ -1,7 +1,5 @@ using NetTopologySuite.Geometries; using System; -using System.Collections.Generic; -using System.Text; using System.Text.Json; using System.Text.Json.Serialization; @@ -9,6 +7,13 @@ namespace NetTopologySuite.IO.Converters { internal class StjEnvelopeConverter : JsonConverter { + private readonly PrecisionModel _precisionModel; + + public StjEnvelopeConverter(PrecisionModel precisionModel) + { + _precisionModel = precisionModel; + } + public override Envelope Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { Envelope res = null; @@ -22,19 +27,19 @@ public override Envelope Read(ref Utf8JsonReader reader, Type typeToConvert, Jso { reader.ReadToken(JsonTokenType.StartArray); - double minX = reader.GetDouble(); + double minX = reader.GetDouble(_precisionModel); reader.Read(); - double minY = reader.GetDouble(); + double minY = reader.GetDouble(_precisionModel); reader.Read(); - double maxX = reader.GetDouble(); + double maxX = reader.GetDouble(_precisionModel); reader.Read(); - double maxY = reader.GetDouble(); + double maxY = reader.GetDouble(_precisionModel); reader.Read(); if (reader.TokenType == JsonTokenType.Number) { maxX = maxY; - maxY = reader.GetDouble(); + maxY = reader.GetDouble(_precisionModel); reader.Read(); reader.Read(); } @@ -59,10 +64,10 @@ public override void Write(Utf8JsonWriter writer, Envelope value, JsonSerializer } writer.WriteStartArray(); - writer.WriteNumberValue(value.MinX); - writer.WriteNumberValue(value.MinY); - writer.WriteNumberValue(value.MaxX); - writer.WriteNumberValue(value.MaxY); + writer.WriteNumberValue(value.MinX, _precisionModel); + writer.WriteNumberValue(value.MinY, _precisionModel); + writer.WriteNumberValue(value.MaxX, _precisionModel); + writer.WriteNumberValue(value.MaxY, _precisionModel); writer.WriteEndArray(); } } diff --git a/src/NetTopologySuite.IO.GeoJSON4STJ/Converters/StjGeometryConverter.Coordinates.cs b/src/NetTopologySuite.IO.GeoJSON4STJ/Converters/StjGeometryConverter.Coordinates.cs index 16c581d..f457578 100644 --- a/src/NetTopologySuite.IO.GeoJSON4STJ/Converters/StjGeometryConverter.Coordinates.cs +++ b/src/NetTopologySuite.IO.GeoJSON4STJ/Converters/StjGeometryConverter.Coordinates.cs @@ -8,7 +8,6 @@ internal partial class StjGeometryConverter { private void WriteCoordinateSequence(Utf8JsonWriter writer, CoordinateSequence sequence, JsonSerializerOptions options, bool multiple = true, OrientationIndex orientation = OrientationIndex.None) { - //writer.WritePropertyName("coordinates"); if (sequence == null) { writer.WriteNullValue(); @@ -29,15 +28,17 @@ private void WriteCoordinateSequence(Utf8JsonWriter writer, CoordinateSequence s for (int i = 0; i < sequence.Count; i++) { writer.WriteStartArray(); - writer.WriteNumberValue(sequence.GetX(i)); - writer.WriteNumberValue(sequence.GetY(i)); + + writer.WriteNumberValue(sequence.GetX(i), _geometryFactory.PrecisionModel); + writer.WriteNumberValue(sequence.GetY(i), _geometryFactory.PrecisionModel); if (hasZ) { double z = sequence.GetZ(i); if (!double.IsNaN(z)) - writer.WriteNumberValue(sequence.GetZ(i)); + writer.WriteNumberValue(z, _geometryFactory.PrecisionModel); } + writer.WriteEndArray(); if (!multiple) break; diff --git a/src/NetTopologySuite.IO.GeoJSON4STJ/Converters/Utility.cs b/src/NetTopologySuite.IO.GeoJSON4STJ/Converters/Utility.cs index 97180ce..51d6aee 100644 --- a/src/NetTopologySuite.IO.GeoJSON4STJ/Converters/Utility.cs +++ b/src/NetTopologySuite.IO.GeoJSON4STJ/Converters/Utility.cs @@ -5,6 +5,7 @@ using System.Text.Json.Nodes; using System.Text.Json.Serialization; using NetTopologySuite.Features; +using NetTopologySuite.Geometries; using NetTopologySuite.IO.Properties; namespace NetTopologySuite.IO.Converters @@ -94,5 +95,23 @@ private static void ThrowForUnexpectedEndOfStream() [MethodImpl(MethodImplOptions.NoInlining)] private static void ThrowForUnexpectedToken(JsonTokenType requiredNextTokenType, ref Utf8JsonReader reader) => throw new JsonException(string.Format(Resources.EX_UnexpectedToken, requiredNextTokenType, reader.TokenType, reader.GetString())); + + /// + /// Parses the current JSON token value from the source as a . Rounds a value to the grid. + /// + /// The reader. + /// The precision model to round to. + /// The rounded value + internal static double GetDouble(this Utf8JsonReader reader, PrecisionModel precisionModel) + => precisionModel.MakePrecise(reader.GetDouble()); + + /// + /// Rounds a value to the grid. Writes the rounded value (as a JSON number) as an element of a JSON array. + /// + /// The writer. + /// The value to write. + /// The precision model to round to. + internal static void WriteNumberValue(this Utf8JsonWriter writer, double value, PrecisionModel precisionModel) + => writer.WriteNumberValue(precisionModel.MakePrecise(value)); } } diff --git a/test/NetTopologySuite.IO.GeoJSON4STJ.Test/Issues/Issue135.cs b/test/NetTopologySuite.IO.GeoJSON4STJ.Test/Issues/Issue135.cs new file mode 100644 index 0000000..d7f86e8 --- /dev/null +++ b/test/NetTopologySuite.IO.GeoJSON4STJ.Test/Issues/Issue135.cs @@ -0,0 +1,37 @@ +using NetTopologySuite.Geometries; +using NetTopologySuite.IO.Converters; +using NetTopologySuite.IO.GeoJSON4STJ.Test.Converters; +using NUnit.Framework; +using System.Text.Json; + +namespace NetTopologySuite.IO.GeoJSON4STJ.Test.Issues +{ + internal class Issue135 : SandDTest + { + [Test, GeoJsonIssueNumber(135)] + 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) + }; + var factory = new GeometryFactory(new PrecisionModel(10), 4326); + var polygon = factory.CreatePolygon(coords); + + string json = JsonSerializer.Serialize(polygon, new JsonSerializerOptions + { + ReadCommentHandling = JsonCommentHandling.Skip, + Converters = + { + new GeoJsonConverterFactory(factory) + } + }); + + Assert.That(json, Is.EqualTo("{\"type\":\"Polygon\",\"coordinates\":[[[0,0],[10.1,0],[10,10.1],[0.1,10],[0,0]]]}")); + } + } +}