Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for 4-dimensional coordinates to GeoJSON4STJ #139

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -191,15 +191,21 @@ public MultiPolygon ToMultiPolygon(GeometryFactory factory)

private static Point ParsePoint(ref Utf8JsonReader reader, GeometryFactory factory)
{
var (x, y, zOrNull) = ReadXYZ(ref reader, factory.PrecisionModel);
var (x, y, zOrNull, mOrNull) = ReadXYZM(ref reader, factory.PrecisionModel);

var seq = factory.CoordinateSequenceFactory.Create(1, zOrNull.HasValue ? 3 : 2, 0);
int dimension = mOrNull.HasValue ? 4 : zOrNull.HasValue ? 3 : 2;
int measures = mOrNull.HasValue ? 1 : 0;
var seq = factory.CoordinateSequenceFactory.Create(1, dimension, measures);
seq.SetX(0, x);
seq.SetY(0, y);
if (zOrNull is double z)
{
seq.SetZ(0, z);
}
if (mOrNull is double m)
{
seq.SetM(0, m);
}

return factory.CreatePoint(seq);
}
Expand All @@ -208,10 +214,11 @@ private static CoordinateSequence ParseCoordinateSequence(ref Utf8JsonReader rea
{
ords = ords ?? new List<double>();
bool sequenceHasZ = false;
bool sequenceHasM = false;

// read the first coordinate to kick things off...
{
var (x, y, zOrNull) = ReadXYZ(ref reader, factory.PrecisionModel);
var (x, y, zOrNull, mOrNull) = ReadXYZM(ref reader, factory.PrecisionModel);

ords.Add(x);
ords.Add(y);
Expand All @@ -220,8 +227,13 @@ private static CoordinateSequence ParseCoordinateSequence(ref Utf8JsonReader rea
ords.Add(z);
sequenceHasZ = true;
}
if (mOrNull is double m)
{
ords.Add(m);
sequenceHasM = true;
}

Debug.Assert(reader.TokenType == JsonTokenType.EndArray, "ReadXYZ was supposed to leave us at the EndArray token just past the last ordinate value");
Debug.Assert(reader.TokenType == JsonTokenType.EndArray, "ReadXYZM was supposed to leave us at the EndArray token just past the last ordinate value");
reader.ReadOrThrow();
}

Expand All @@ -230,32 +242,46 @@ private static CoordinateSequence ParseCoordinateSequence(ref Utf8JsonReader rea
reader.ReadOrThrow();

reader.AssertToken(JsonTokenType.Number);
var (x, y, zOrNull) = ReadXYZ(ref reader, factory.PrecisionModel);
var (x, y, zOrNull, mOrNull) = ReadXYZM(ref reader, factory.PrecisionModel);

if (!sequenceHasZ && zOrNull.HasValue)
{
// we've been reading XY up to this point, but we just saw an XYZ. take a short
// one-time detour to weave dummy Z values into what we've already read so far,
// then continue reading the rest of the values.
ords = ConvertXYToXYZ(ords);
ords = ConvertOrdsToNewDimension(ords, 3);
sequenceHasZ = true;
}

if (!sequenceHasM && mOrNull.HasValue)
{
// we've been reading XYZ up to this point, but we just saw an XYZM. take a short
// one-time detour to weave dummy M values into what we've already read so far,
// then continue reading the rest of the values.
ords = ConvertOrdsToNewDimension(ords, 4);
sequenceHasM = true;
}

ords.Add(x);
ords.Add(y);
if (sequenceHasZ)
{
ords.Add(zOrNull ?? Coordinate.NullOrdinate);
}
if (sequenceHasM)
{
ords.Add(mOrNull ?? Coordinate.NullOrdinate);
}

Debug.Assert(reader.TokenType == JsonTokenType.EndArray, "ReadXYZ was supposed to leave us at the EndArray token just past the last ordinate value");
Debug.Assert(reader.TokenType == JsonTokenType.EndArray, "ReadXYZM was supposed to leave us at the EndArray token just past the last ordinate value");
reader.ReadOrThrow();
}

reader.AssertToken(JsonTokenType.EndArray);

int dimension = sequenceHasZ ? 3 : 2;
var seq = factory.CoordinateSequenceFactory.Create(ords.Count / dimension, dimension, 0);
int dimension = sequenceHasM ? 4 : sequenceHasZ ? 3 : 2;
int measures = sequenceHasM ? 1 : 0;
var seq = factory.CoordinateSequenceFactory.Create(ords.Count / dimension, dimension, measures);
int ordIndex = 0;
for (int coordIndex = 0; coordIndex < seq.Count; coordIndex++)
{
Expand All @@ -265,6 +291,10 @@ private static CoordinateSequence ParseCoordinateSequence(ref Utf8JsonReader rea
{
seq.SetZ(coordIndex, ords[ordIndex++]);
}
if (sequenceHasM)
{
seq.SetM(coordIndex, ords[ordIndex++]);
}
}

return seq;
Expand Down Expand Up @@ -326,9 +356,9 @@ private static MultiPolygon ParseMultiPolygon(ref Utf8JsonReader reader, Geometr
return factory.CreateMultiPolygon(polygons.ToArray());
}

private static (double x, double y, double? zOrNull) ReadXYZ(ref Utf8JsonReader reader, PrecisionModel precisionModel)
private static (double x, double y, double? zOrNull, double? mOrNull) ReadXYZM(ref Utf8JsonReader reader, PrecisionModel precisionModel)
{
Debug.Assert(reader.TokenType == JsonTokenType.Number, "ReadXYZ was supposed to be called with a reader positioned on the first Number token of the array.");
Debug.Assert(reader.TokenType == JsonTokenType.Number, "ReadXYZM was supposed to be called with a reader positioned on the first Number token of the array.");

// x
double x = precisionModel.MakePrecise(reader.GetDouble());
Expand All @@ -338,21 +368,28 @@ private static (double x, double y, double? zOrNull) ReadXYZ(ref Utf8JsonReader
reader.AssertToken(JsonTokenType.Number);
double y = precisionModel.MakePrecise(reader.GetDouble());

double? z = null;
double? m = null;

// z?
reader.ReadOrThrow();
if (reader.TokenType == JsonTokenType.Number)
{
// yes z
double z = reader.GetDouble();
AdvanceReaderToEndOfCurrentNumberArray(ref reader);
return (x, y, z);
z = reader.GetDouble();
reader.ReadOrThrow();
}
else

// m?
if (reader.TokenType == JsonTokenType.Number)
{
// no z
reader.AssertToken(JsonTokenType.EndArray);
return (x, y, null);
// yes m
m = reader.GetDouble();
reader.ReadOrThrow();
}

AdvanceReaderToEndOfCurrentNumberArray(ref reader);
return (x, y, z, m);
}

private static void AdvanceReaderToEndOfCurrentNumberArray(ref Utf8JsonReader reader)
Expand All @@ -365,20 +402,18 @@ private static void AdvanceReaderToEndOfCurrentNumberArray(ref Utf8JsonReader re
reader.AssertToken(JsonTokenType.EndArray);
}

private static List<double> ConvertXYToXYZ(List<double> xys)
private static List<double> ConvertOrdsToNewDimension(List<double> ords, int dimension)
{
Debug.Assert(xys.Count % 2 == 0, "This was only supposed to be called with XY values.");
Debug.Assert(ords.Count % dimension - 1 == 0, $"This was only supposed to be called with {dimension - 1}-dimensional values.");

var xyzs = new List<double>(xys.Capacity);
int i = 0;
while (i < xys.Count)
var newOrds = new List<double>(ords.Capacity);
for (int i = 0; i < dimension - 1; i++)
{
xyzs.Add(xys[i++]);
xyzs.Add(xys[i++]);
xyzs.Add(Coordinate.NullOrdinate);
newOrds.Add( ords[i] );
}
newOrds.Add(Coordinate.NullOrdinate);

return xyzs;
return newOrds;
}

private static Polygon ToPolygon(IReadOnlyList<CoordinateSequence> ringSequences, GeometryFactory factory)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,18 @@ public void TestReadPoint3D()
Assert.That(geom.Coordinate, Is.InstanceOf(typeof(CoordinateZ)));
}

[Test]
public void TestReadPoint4D()
{
string geoJson = @"{ ""type"" : ""Point"", ""coordinates"": [102.0, 0.5, 6.2, 0.02] }";
var options = DefaultOptions;
var geom = Deserialize(geoJson, options);

Assert.That(geom != null);
Assert.That(geom, Is.InstanceOf(typeof(Point)));
Assert.That(geom.Coordinate, Is.InstanceOf(typeof(CoordinateZM)));
}

[Test]
public void TestReadLineString2D()
{
Expand All @@ -81,6 +93,18 @@ public void TestReadLineString3D()
Assert.That(geom.Coordinate, Is.InstanceOf(typeof(CoordinateZ)));
}

[Test]
public void TestReadLineString4D()
{
string geoJson = @"{ ""type"" : ""LineString"", ""coordinates"": [[102.0, 0.5, 2.45, 0.02],[112.7, 2.1, 2.34, 0.04]] }";
var options = DefaultOptions;
var geom = Deserialize(geoJson, options);

Assert.That(geom != null);
Assert.That(geom, Is.InstanceOf(typeof(LineString)));
Assert.That(geom.Coordinate, Is.InstanceOf(typeof(CoordinateZM)));
}

[Test]
public void TestReadPolygon2D()
{
Expand Down
Loading