Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ public static boolean isResultAreaConsistent(Geometry geom0, Geometry geom1, int
isConsistent = isDifferenceAreaConsistent(areaA, areaB, areaResult, AREA_HEURISTIC_TOLERANCE);
break;
case OverlayNG.SYMDIFFERENCE:
isConsistent = isLess(areaResult, areaA + areaB, AREA_HEURISTIC_TOLERANCE);
isConsistent = isSymDifferenceAreaConsistent(areaA, areaB, areaResult, AREA_HEURISTIC_TOLERANCE);
break;
case OverlayNG.UNION:
isConsistent = isLess(areaA, areaResult, AREA_HEURISTIC_TOLERANCE)
Expand Down Expand Up @@ -435,6 +435,30 @@ private static boolean isDifferenceAreaConsistent(double areaA, double areaB, do
return areaResult > areaDiffMin;
}

/**
* Tests if the area of a symmetric difference is within the possible range.
* It cannot be greater than the sum of the input areas,
* and it cannot be less than the difference of the input areas.
* The lower bound is relaxed by a fraction of the larger input area,
* so that nearly identical inputs (whose areas differ only by rounding)
* can still produce an empty result.
* This is a heuristic which will only detect gross overlay errors
* (such as an inverted result with negative area).
*
* @param areaA the area of A
* @param areaB the area of B
* @param areaResult the result area
* @param tolFrac the area tolerance fraction
*
* @return true if the symmetric difference area is consistent.
*/
private static boolean isSymDifferenceAreaConsistent(double areaA, double areaB, double areaResult, double tolFrac) {
if (! isLess(areaResult, areaA + areaB, tolFrac))
return false;
double areaSymDiffMin = Math.abs(areaA - areaB) - tolFrac * Math.max(areaA, areaB);
return areaResult >= areaSymDiffMin;
}

private static boolean isLess(double v1, double v2, double tol) {
return v1 <= v2 * (1 + tol);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,22 @@ public void testUnionFixed() {
OverlayNG.overlay(geom1, geom2, OverlayNG.UNION, pm);
}

/**
* The first snapping attempt for this input produces an inverted polygon
* (with negative area), which the area check must reject for a symmetric difference.
* The retry with a larger snap tolerance gives the correct result,
* which is the polygon, since the line has no area.
*
* See https://github.com/locationtech/jts/issues/1239
*/
public void testSymDifferencePolygonLineInvertedResult() {
Geometry a = read("POLYGON ((20.58 46.965, 27.52 53.905, 28.52 54.914, 20.58 46.965))");
Geometry b = read("LINESTRING (27.519999973297118 53.90499997329712, 52.192 78.577, 27.519 53.904, 28.519999999 54.913999999, 39.599 65.984)");
Geometry result = OverlayNGRobust.overlay(a, b, OverlayNG.SYMDIFFERENCE);
assertTrue("result is valid", result.isValid());
assertEquals(a.getArea(), result.getArea(), 1e-6);
}

// MD 2020-09-14 There is no known test case that requires Snap-Rounding to succeed.

public static void checkUnionSuccess(Geometry a, Geometry b) {
Expand Down