locationtech / jts

The JTS Topology Suite is a Java library for creating and manipulating vector geometry.
Other
1.94k stars 442 forks source link

intersection with large precision scale factor throws a TopologyException #984

Closed pirwani closed 1 year ago

pirwani commented 1 year ago

Consider the following example:

  public void toy_test_failure_in_JTS() {
    GeometryFactory factory =
        new GeometryFactory(
            new PrecisionModel(
                PrecisionModel.maximumPreciseValue));

    Polygon polygon =
        factory.createPolygon(
            new Coordinate[] {
              new Coordinate(1310, 400),
              new Coordinate(1460, 400),
              new Coordinate(1460, 500),
              new Coordinate(1310, 500),
              new Coordinate(1310, 400)
            });
    MultiPolygon mp =
        factory.createMultiPolygon(new Polygon[] {polygon});
    LineString linestring =
        factory.createLineString(
            new Coordinate[] {
              new Coordinate(1360, 430),
              new Coordinate(2120, 430)
            });

    Geometry intersection = mp.intersection(linestring);

    if (intersection != null) {
      System.out.println("The intersection of the multipolygon and linestring is: " + intersection);
    } else {
      System.out.println("The multipolygon and linestring do not intersect.");
    }
  }

fails with the following exception:

xxx.jts.geom.TopologyException: found non-noded intersection between LINESTRING ( 1460 400, 1024 430 ) and LINESTRING ( 1310 500, 1310 400 ) [ (1310.0, 410.3211009174312, NaN) ]
    at xxx.jts.noding.FastNodingValidator.checkValid(FastNodingValidator.java:139)
    at xxx.jts.geomgraph.EdgeNodingValidator.checkValid(EdgeNodingValidator.java:80)
    at xxx.jts.geomgraph.EdgeNodingValidator.checkValid(EdgeNodingValidator.java:45)
    at xxx.jts.operation.overlay.OverlayOp.computeOverlay(OverlayOp.java:229)
    at xxx.jts.operation.overlay.OverlayOp.getResultGeometry(OverlayOp.java:181)
    at xxx.jts.operation.overlay.OverlayOp.overlayOp(OverlayOp.java:84)
    at xxx.jts.operation.overlay.snap.SnapIfNeededOverlayOp.getResultGeometry(SnapIfNeededOverlayOp.java:75)
    at xxx.jts.operation.overlay.snap.SnapIfNeededOverlayOp.overlayOp(SnapIfNeededOverlayOp.java:37)
    at xxx.jts.geom.GeometryOverlay.overlay(GeometryOverlay.java:76)
    at xxx.jts.geom.GeometryOverlay.intersection(GeometryOverlay.java:119)
    at xxx.jts.geom.Geometry.intersection(Geometry.java:1330)
    at yyy.MultiPolygonTest.toy_test_failure_in_JTS(MultiPolygonTest.java:zzz)

However, if I replace the first line in the test that constructs a GeometryFactory with the following:

  public void toy_test_failure_in_JTS() {
    GeometryFactory factory =
        new GeometryFactory();
    ...
  }

...then, the test works.

Any ideas on what might be going wrong? Seems counter intuitive that a higher precision computation model would fail while it'd pass for a default model.

Thank you!

dr-jts commented 1 year ago

It is incorrect to use PrecisionModel.maximumPreciseValue as the scale factor for a PrecisionModel. A scale factor should indicate the number of digits of precision required, and is typically a power of 10 with a fairly small exponent (e.g. between 1 and 10^15). The code logic is not designed to handle values outside this range.