Skip to content

JtsShapeFactory MultipolygonBuilder fails to validate shape #219

Description

@PapaStan

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.

Activity

  1. dsmiley commented on Mar 13, 2022

    @dsmiley
    Contributor

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

  2. dsmiley commented on Mar 13, 2022

    @dsmiley
    Contributor

    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.

  3. PapaStan commented on Mar 31, 2022

    @PapaStan
    ContributorAuthor

    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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions