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

Keep M coordinates when writing to WKB and reading from WKB #734

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

Conversation

kaiwinter
Copy link

@kaiwinter kaiwinter commented May 29, 2021

The changes I made (adding ordinateFlags) are mainly taken over from the WKTReader/WKTWriter.

M coordinate is read as Z coordinate after writing/reading.
Referring to WKTReader/WKTWriter I added the EnumSet<Ordinate>
ordinateFlags. The WKBWriter uses these ordinateFlags to set the correct
flags on the byte stream.

Signed-off-by: Kai Winter <kaiwinter@gmx.de>
@kaiwinter kaiwinter changed the title Keep M coordinates when writing to WKB Keep M coordinates when writing to WKB and reading from WKB Jun 10, 2021
@kaiwinter
Copy link
Author

Are there still open issues or may this PR be merged?

@oers
Copy link

oers commented Sep 9, 2022

Any status on this?

@oers
Copy link

oers commented Sep 9, 2022

I had to fix this in WKBWriter, to make it work with XYZM

    private void writeCoordinate(CoordinateSequence seq, int index, OutStream os)
            throws IOException
    {
        ByteOrderValues.putDouble(seq.getX(index), buf, byteOrder);
        os.write(buf, 8);
        ByteOrderValues.putDouble(seq.getY(index), buf, byteOrder);
        os.write(buf, 8);

        // only write 3rd dim if caller has requested it for this writer
        if (outputDimension >= 3) {
            // if 3rd dim is requested, only write it if the CoordinateSequence provides it
            double ordVal = Coordinate.NULL_ORDINATE;
            if (seq.getDimension() == 3)
                ordVal = seq.getOrdinate(index, 2);
            ByteOrderValues.putDouble(ordVal, buf, byteOrder);
            os.write(buf, 8);
        }
        // only write 4th dim if caller has requested it for this writer
        if (outputDimension == 4) {
            // if 4th dim is requested, only write it if the CoordinateSequence provides it
            double ordVal = Coordinate.NULL_ORDINATE;
            if (seq.getDimension() == 4)
                ordVal = seq.getOrdinate(index, 3);
            ByteOrderValues.putDouble(ordVal, buf, byteOrder);
            os.write(buf, 8);
        }
    }

Test for XYZM is missing here

@kaiwinter
Copy link
Author

@oers Does the WKTWriter handle XYZM coordination correctly? If not this should better be fixed together in a new pull request maybe. If it does I could add your fix to this pull request.

@oers
Copy link

oers commented Sep 9, 2022

@kaiwinter yes, the textwriter works correctly. I tested both with XYZM Coordinates (reading and writing)

@kaiwinter
Copy link
Author

@oers I don't understand the implementation tbh. I tried to adapt an existing unit test but couldn't even get the WKTReader to parse a XYZM coordinate. So I couldn't verify what your change does exactly. Maybe this should better be an own issue and pull request? What does @dr-jts think?

@oers
Copy link

oers commented Sep 12, 2022

The wrong method in WKB Writer never wrote the 4th dimension. My Method fixes this (the new one below. It still had a bug)

    private void writeCoordinate(CoordinateSequence seq, int index, OutStream os)
            throws IOException
    {
        ByteOrderValues.putDouble(seq.getX(index), buf, byteOrder);
        os.write(buf, 8);
        ByteOrderValues.putDouble(seq.getY(index), buf, byteOrder);
        os.write(buf, 8);

        // only write 3rd dim if caller has requested it for this writer
        if (outputDimension >= 3) {
            // if 3rd dim is requested, only write it if the CoordinateSequence provides it
            double ordVal = seq.getOrdinate(index, 2);
            ByteOrderValues.putDouble(ordVal, buf, byteOrder);
            os.write(buf, 8);
        }
        // only write 4th dim if caller has requested it for this writer
        if (outputDimension == 4) {
            // if 4th dim is requested, only write it if the CoordinateSequence provides it
            double ordVal = seq.getOrdinate(index, 3);
            ByteOrderValues.putDouble(ordVal, buf, byteOrder);
            os.write(buf, 8);
        }
    }

My Test is like this:

    @Test
    void wkbLineStringZM() throws ParseException {
        LineString lineZM = fac.createLineString(new Coordinate[]{new CoordinateXYZM(1,2,3,4), new CoordinateXYZM(5,6,7,8)});
        byte[] write = new WKBWriter(4).write(lineZM);


        LineString deserialisiert = (LineString) new PostgisWKBReader().read(write);

        assertEquals(lineZM, deserialisiert);

        for (int i = 0; i < lineZM.getNumPoints() ; i++) {
            Point geometryN = lineZM.getPointN(1);
            Point geometryN1 = deserialisiert.getPointN(1);

            assertFalse(Double.isNaN(geometryN.getCoordinate().getM()));
            assertFalse(Double.isNaN(geometryN.getCoordinate().getZ()));
            assertFalse(Double.isNaN(geometryN1.getCoordinate().getM()), "M is persisted");
            assertFalse(Double.isNaN(geometryN1.getCoordinate().getZ()), "Z is persisted");

            assertEquals(geometryN, geometryN1);
        }
    }

    @Test
    void wktLineStringZM() throws ParseException {
        LineString lineZM = fac.createLineString(new Coordinate[]{new CoordinateXYZM(1,2,3,4), new CoordinateXYZM(5,6,7,8)});
        String write = new WKTWriter(4).write(lineZM);

        LineString deserialisiert = (LineString) new PostgisWKTReader().read(write);

        assertEquals(lineZM, deserialisiert);

        for (int i = 0; i < lineZM.getNumPoints() ; i++) {
            Point geometryN = lineZM.getPointN(1);
            Point geometryN1 = deserialisiert.getPointN(1);

            assertFalse(Double.isNaN(geometryN.getCoordinate().getM()));
            assertFalse(Double.isNaN(geometryN.getCoordinate().getZ()));
            assertFalse(Double.isNaN(geometryN1.getCoordinate().getM()), "M is persisted");
            assertFalse(Double.isNaN(geometryN1.getCoordinate().getZ()), "Z is persisted");

            assertEquals(geometryN, geometryN1);
        }
    }

Signed-off-by: Kai Winter <kaiwinter@gmx.de>
@kaiwinter
Copy link
Author

@oers I added your changes to this pull request.

Copy link

@oers oers left a comment

Choose a reason for hiding this comment

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

I integrated the changes made here in a project that uses postgis 14. Itworks really well.

@jiayuasu
Copy link
Contributor

jiayuasu commented Jan 8, 2024

@kaiwinter @dr-jts Thanks for the great work. Is there a reason why this PR hasn't been merged after a year? Is there anything others could help?

WKB format is often used for geometry serialization. Silently missing the M value will lead to incorrect results. We at Sedona uses WKB to serialize XYZM data for our Snowflake support.

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.

None yet

4 participants