spatial4j icon indicating copy to clipboard operation
spatial4j copied to clipboard

JtsShapeFactory MultipolygonBuilder fails to validate shape

Open PapaStan opened this issue 3 years ago • 3 comments
trafficstars

It is possible to create invalid shapes through the JtsShapeFactory.MultipolygonBuilder.

You can reproduce by running the following class:

import org.locationtech.spatial4j.context.jts.JtsSpatialContext;
import org.locationtech.spatial4j.context.jts.JtsSpatialContextFactory;
import org.locationtech.spatial4j.exception.InvalidShapeException;
import org.locationtech.spatial4j.shape.jts.JtsShapeFactory;

public class Main {

    public static void main(String[] args) throws Exception {
        JtsSpatialContextFactory ctxFactory = new JtsSpatialContextFactory();
        ctxFactory.geo = false;
        ctxFactory.useJtsLineString = true;
        ctxFactory.useJtsMulti = true;
        ctxFactory.useJtsPoint = true;
        JtsSpatialContext ctx = new JtsSpatialContext(ctxFactory);
        JtsShapeFactory shapeFactory = new JtsShapeFactory(ctx, ctxFactory);

        JtsShapeFactory.PolygonBuilder polygonBuilder = shapeFactory.polygon()
                .pointLatLon(1, 1)
                .pointLatLon(-1, -1)
                .pointLatLon(1, -1)
                .pointLatLon(-1, 1)
                .pointLatLon(1, 1);

        try {
            polygonBuilder.build();
            System.out.println("OK");
        } catch (InvalidShapeException e) {
            System.out.println("KO: " + e.getMessage());
        }
        try {
            shapeFactory.multiPolygon().add(polygonBuilder).build();
            System.out.println("OK");
        } catch (InvalidShapeException e) {
            System.out.println("KO: " + e.getMessage());
        }
    }

}

Output:

KO: Self-intersection at or near point (0.0, 0.0, NaN)
OK

The JtsShapeFactory.MultipolygonBuilder is used in the ShapeDeserializer and GeometryDeserializer classes and can lead jackson to create invalid shapes.

PapaStan avatar Mar 07 '22 13:03 PapaStan

Garbage in, Garbage out -- no? Maybe I'm probably missing your point. What do you propose that Spatial4j should do/not-do?

dsmiley avatar Mar 13 '22 13:03 dsmiley

If you are proposing that the shape be eagerly validated (on construction) then that would definitely be a configurable option. I want to avoid re-validation of already validated data. Consider a persistent store of shapes that are retrieved and matched against some sort of query that needs to be real-time. The shapes were already validated when they were stored.

dsmiley avatar Mar 13 '22 13:03 dsmiley

My exemple is probably misleading. If we concentrate on the maybe failing part:

import org.locationtech.spatial4j.context.jts.JtsSpatialContext;
import org.locationtech.spatial4j.context.jts.JtsSpatialContextFactory;
import org.locationtech.spatial4j.exception.InvalidShapeException;
import org.locationtech.spatial4j.shape.jts.JtsShapeFactory;

public class Main {

    public static void main(String[] args) throws Exception {
        JtsSpatialContextFactory ctxFactory = new JtsSpatialContextFactory();
        ctxFactory.geo = false;
        ctxFactory.useJtsLineString = true;
        ctxFactory.useJtsMulti = true;
        ctxFactory.useJtsPoint = true;
        JtsSpatialContext ctx = new JtsSpatialContext(ctxFactory);
        JtsShapeFactory shapeFactory = new JtsShapeFactory(ctx, ctxFactory);

        JtsShapeFactory.PolygonBuilder polygonBuilder = shapeFactory.polygon()
                .pointLatLon(1, 1)
                .pointLatLon(-1, -1)
                .pointLatLon(1, -1)
                .pointLatLon(-1, 1)
                .pointLatLon(1, 1);

        try {
            shapeFactory.multiPolygon().add(polygonBuilder).build();
            System.out.println("OK");
        } catch (InvalidShapeException e) {
            System.out.println("KO: " + e.getMessage());
        }
    }

}

We get OK as output, meaning that the invalid polygon has been used in the creation of the multipolygon without failing.

I expect the MultiPolygonBuilder to validate the polygon at some point.

Digging a bit made me notice that the JtsShapeFactory$JtsMultiPolygonBuilder does not build polygons the same way the JtsShapeFactory$JtsPolygonBuilder does and may be the reason why the invalid polygons are created wihtout validation.

Thank you for your time

PapaStan avatar Mar 31 '22 08:03 PapaStan