Skip to content

Commit

Permalink
Respect the PrecisionModel grid when serializing coordinates. (#135)
Browse files Browse the repository at this point in the history
  • Loading branch information
MizardX committed Oct 29, 2023
1 parent 0393f41 commit 101b813
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
using NetTopologySuite.Geometries;
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace NetTopologySuite.IO.Converters
{
internal class StjEnvelopeConverter : JsonConverter<Envelope>
{
private readonly PrecisionModel _precisionModel;

public StjEnvelopeConverter(PrecisionModel precisionModel)
{
_precisionModel = precisionModel;
}

public override Envelope Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
Envelope res = null;
Expand All @@ -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();
}
Expand All @@ -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();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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;
Expand Down
19 changes: 19 additions & 0 deletions src/NetTopologySuite.IO.GeoJSON4STJ/Converters/Utility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()));

/// <summary>
/// Parses the current JSON token value from the source as a <see cref="double"/>. Rounds a value to the <see cref="PrecisionModel"/> grid.
/// </summary>
/// <param name="reader">The reader.</param>
/// <param name="precisionModel">The precision model to round to.</param>
/// <returns>The rounded value</returns>
internal static double GetDouble(this Utf8JsonReader reader, PrecisionModel precisionModel)
=> precisionModel.MakePrecise(reader.GetDouble());

/// <summary>
/// Rounds a <see cref="double"/> value to the <see cref="PrecisionModel"/> grid. Writes the rounded value (as a JSON number) as an element of a JSON array.
/// </summary>
/// <param name="writer">The writer.</param>
/// <param name="value">The value to write.</param>
/// <param name="precisionModel">The precision model to round to.</param>
internal static void WriteNumberValue(this Utf8JsonWriter writer, double value, PrecisionModel precisionModel)
=> writer.WriteNumberValue(precisionModel.MakePrecise(value));
}
}
37 changes: 37 additions & 0 deletions test/NetTopologySuite.IO.GeoJSON4STJ.Test/Issues/Issue135.cs
Original file line number Diff line number Diff line change
@@ -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<Geometry>
{
[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]]]}"));
}
}
}

0 comments on commit 101b813

Please sign in to comment.