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

Fix writing XYM geometries as WKB #1092

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

Kontinuation
Copy link
Contributor

Fixes #733

Please note that we must specify outputDimension = 4 when writing geometries with M dimension, even when the geometry is XYM instead of XYZM. This behavior is the same as WKTWriter.

@@ -0,0 +1,79 @@
/*
* Copyright (c) 2016 Vivid Solutions.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is incorrect for a new file

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All source code in this file is taken from WKTWriter.java, which is an old file. Should we update the copyright header or preserve the copyright header of the file where it originates from?

@Kontinuation Kontinuation requested a review from mprins February 17, 2025 06:55
@Kontinuation
Copy link
Contributor Author

Kontinuation commented Feb 17, 2025

Issue #733 is closed but the problem still exists in the latest master.

@jiayuasu
Copy link
Contributor

@jodygarnett @dr-jts Would you please merge this PR? We are working on adding Geo support into Apache Iceberg and Parquet Java reader and writer. JTS is a fundamental lib in this process. WKB is the encoding mechanism used in all these PRs.

@jiayuasu
Copy link
Contributor

@jnh5y CC James too 🙏🏻

@Kontinuation
Copy link
Contributor Author

I'd like to provide more context for the problem we are solving. Given the following MULTILINESTRING M geometry:

String wkt = "MULTILINESTRING M((1 1 1, 2 2 2))";
WKTReader wktReader = new WKTReader();
Geometry geometryBefore = wktReader.read(wkt);

Writing it using WKBWriter and read it back using WKBReader will result in a MULTILINESTRING Z:

WKBWriter wkbWriter = new WKBWriter(3);
byte[] write = wkbWriter.write(geometryBefore);
WKBReader wkbReader = new WKBReader();
Geometry geometryAfter = wkbReader.read(write);
System.out.println(Arrays.asList(geometryAfter.getCoordinates())); // [(1.0, 1.0, 1.0), (2.0, 2.0, 2.0)]

If we use WKTWriter and WKTReader for serializing and deserializing the MULTILINESTRING M geometry, we don't see the "phantom" Z ordinate, but we still lose the M ordinate:

WKTWriter wktWriter = new WKTWriter(3);
String write = wktWriter.write(geometryBefore);
Geometry geometryAfter = wktReader.read(write);
System.out.println(Arrays.asList(geometryAfter.getCoordinates())); // [(1.0, 1.0, NaN), (2.0, 2.0, NaN)]

We found that JTS will only output M ordinate when outputDimension > 3, so M is always treated as the 4th dimension, regardless of whether the coordinates are XYM or XYZM. Setting outputDimension as 4 when using WKTWriter generates the MULTILINESTRING M(...) WKT string we wanted:

WKTWriter wktWriter = new WKTWriter(4);
String write = wktWriter.write(geometryBefore);
System.out.println(write);  // MULTILINESTRING M((1 1 1, 2 2 2))
Geometry geometryAfter = wktReader.read(write);
System.out.println(Arrays.asList(geometryAfter.getCoordinates())); // [(1.0, 1.0 m=1.0), (2.0, 2.0 m=2.0)]

I believe that WKBWriter should behave the same as WKTWriter. However, specifying outputDimensions as 4 results in the following exception:

WKBWriter wkbWriter = new WKBWriter(4);
byte[] write = wkbWriter.write(geometryBefore);  // <- exception raised here
// java.lang.IllegalArgumentException: Invalid ordinate index: 3
//
//	at org.locationtech.jts.geom.CoordinateXYM.getOrdinate(CoordinateXYM.java:140)
//	at org.locationtech.jts.geom.impl.CoordinateArraySequence.getOrdinate(CoordinateArraySequence.java:260)
//	at org.locationtech.jts.io.WKBWriter.writeCoordinate(WKBWriter.java:524)
//	at org.locationtech.jts.io.WKBWriter.writeCoordinateSequence(WKBWriter.java:502)
//	at org.locationtech.jts.io.WKBWriter.writeLineString(WKBWriter.java:425)
//	at org.locationtech.jts.io.WKBWriter.write(WKBWriter.java:388)
//	at org.locationtech.jts.io.WKBWriter.writeGeometryCollection(WKBWriter.java:454)
//	at org.locationtech.jts.io.WKBWriter.write(WKBWriter.java:395)
//	at org.locationtech.jts.io.WKBWriter.write(WKBWriter.java:367)

This PR resolves the above issue by making the behavior of WKBWriter consistent with WKTWriter. No Z or M ordinates will be written with outputDimension = 3:

WKBWriter wkbWriter = new WKBWriter(3);
byte[] write = wkbWriter.write(geometryBefore);
WKBReader wkbReader = new WKBReader();
Geometry geometryAfter = wkbReader.read(write);
System.out.println(Arrays.asList(geometryAfter.getCoordinates())); // [(1.0, 1.0), (2.0, 2.0)]

The M ordinate will be written as expected with outputDimension = 4:

WKBWriter wkbWriter = new WKBWriter(4);
byte[] write = wkbWriter.write(geometryBefore);
WKBReader wkbReader = new WKBReader();
Geometry geometryAfter = wkbReader.read(write);
System.out.println(Arrays.asList(geometryAfter.getCoordinates())); // [(1.0, 1.0 m=1.0), (2.0, 2.0 m=2.0)]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Writing and reading WKB loses M dimension
3 participants