Skip to content

Commit 5b7fdbb

Browse files
committed
Add auto-tolerance to MIC and LEC
1 parent 681db79 commit 5b7fdbb

File tree

4 files changed

+211
-72
lines changed

4 files changed

+211
-72
lines changed

modules/core/src/main/java/org/locationtech/jts/algorithm/construct/LargestEmptyCircle.java

Lines changed: 64 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020 Martin Davis.
2+
* Copyright (c) 2025 Martin Davis.
33
*
44
* All rights reserved. This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -28,7 +28,8 @@
2828

2929
/**
3030
* Constructs the Largest Empty Circle for a set
31-
* of obstacle geometries, up to a given accuracy distance tolerance.
31+
* of obstacle geometries, up to a given accuracy distance tolerance
32+
* (which can be specified or determined automatically).
3233
* The obstacles may be any combination of point, linear and polygonal geometries.
3334
* <p>
3435
* The Largest Empty Circle (LEC) is the largest circle
@@ -60,6 +61,19 @@
6061
*/
6162
public class LargestEmptyCircle {
6263

64+
/**
65+
* Computes the center point of the Largest Empty Circle
66+
* interior-disjoint to a set of obstacles.
67+
* The obstacles may be any collection of points, lines and polygons.
68+
* The center of the LEC lies within the convex hull of the obstacles.
69+
*
70+
* @param obstacles a geometry representing the obstacles
71+
* @return the center point of the Largest Empty Circle
72+
*/
73+
public static Point getCenter(Geometry obstacles) {
74+
return getCenter(obstacles, null, 0.0);
75+
}
76+
6377
/**
6478
* Computes the center point of the Largest Empty Circle
6579
* interior-disjoint to a set of obstacles,
@@ -75,6 +89,20 @@ public static Point getCenter(Geometry obstacles, double tolerance) {
7589
return getCenter(obstacles, null, tolerance);
7690
}
7791

92+
/**
93+
* Computes the center point of the Largest Empty Circle
94+
* interior-disjoint to a set of obstacles and within a polygonal boundary.
95+
* The obstacles may be any collection of points, lines and polygons.
96+
* The center of the LEC lies within the given boundary.
97+
*
98+
* @param obstacles a geometry representing the obstacles
99+
* @param boundary a polygonal geometry to contain the LEC center
100+
* @return the center point of the Largest Empty Circle
101+
*/
102+
public static Point getCenter(Geometry obstacles, Geometry boundary) {
103+
return getCenter(obstacles, boundary, 0.0);
104+
}
105+
78106
/**
79107
* Computes the center point of the Largest Empty Circle
80108
* interior-disjoint to a set of obstacles and within a polygonal boundary,
@@ -152,7 +180,23 @@ public static LineString getRadiusLine(Geometry obstacles, Geometry boundary, do
152180
*
153181
* @param obstacles a non-empty geometry representing the obstacles
154182
* @param boundary a polygonal geometry (may be null or empty)
155-
* @param tolerance a distance tolerance for computing the circle center point (a positive value)
183+
*/
184+
public LargestEmptyCircle(Geometry obstacles, Geometry boundary) {
185+
this(obstacles, boundary, 0.0);
186+
}
187+
188+
/**
189+
* Creates a new instance of a Largest Empty Circle construction,
190+
* interior-disjoint to a set of obstacle geometries
191+
* and having its center within a polygonal boundary.
192+
* The obstacles may be any collection of points, lines and polygons.
193+
* If the boundary is null or empty the convex hull
194+
* of the obstacles is used as the boundary.
195+
* A zero tolerance aut0matically determines an approximation tolerance.
196+
*
197+
* @param obstacles a non-empty geometry representing the obstacles
198+
* @param boundary a polygonal geometry (may be null or empty)
199+
* @param tolerance a distance tolerance for computing the circle center point (a non-negative value)
156200
*/
157201
public LargestEmptyCircle(Geometry obstacles, Geometry boundary, double tolerance) {
158202
if (obstacles == null || obstacles.isEmpty()) {
@@ -161,8 +205,8 @@ public LargestEmptyCircle(Geometry obstacles, Geometry boundary, double toleranc
161205
if (boundary != null && ! (boundary instanceof Polygonal)) {
162206
throw new IllegalArgumentException("Boundary must be polygonal");
163207
}
164-
if (tolerance <= 0) {
165-
throw new IllegalArgumentException("Accuracy tolerance is non-positive: " + tolerance);
208+
if (tolerance < 0) {
209+
throw new IllegalArgumentException("Accuracy tolerance is negative: " + tolerance);
166210
}
167211
this.obstacles = obstacles;
168212
this.boundary = boundary;
@@ -320,6 +364,9 @@ private void compute() {
320364
radiusPoint = factory.createPoint(radiusPt);
321365
}
322366

367+
//-- empirically determined to balance accuracy and speed
368+
private static final double AUTO_TOLERANCE_FRACTION = 0.001;
369+
323370
/**
324371
* Tests whether a cell may contain the circle center,
325372
* and thus should be refined (split into subcells
@@ -336,14 +383,24 @@ private boolean mayContainCircleCenter(Cell cell) {
336383
if (cell.isFullyOutside())
337384
return false;
338385

386+
/**
387+
* The tolerance can be automatically determined
388+
* as a fraction of the current farthest distance.
389+
* For a very small actual MIC distance this may cause many iterations,
390+
* but the iter limit prevents an infinite loop
391+
*/
392+
double requiredTol = tolerance > 0
393+
? tolerance
394+
: farthestCell.getDistance() * AUTO_TOLERANCE_FRACTION;
395+
339396
/**
340397
* The cell is outside, but overlaps the boundary
341398
* so it may contain a point which should be checked.
342399
* This is only the case if the potential overlap distance
343400
* is larger than the tolerance.
344401
*/
345402
if (cell.isOutside()) {
346-
boolean isOverlapSignificant = cell.getMaxDistance() > tolerance;
403+
boolean isOverlapSignificant = cell.getMaxDistance() > requiredTol;
347404
return isOverlapSignificant;
348405
}
349406

@@ -353,7 +410,7 @@ private boolean mayContainCircleCenter(Cell cell) {
353410
* (up to tolerance).
354411
*/
355412
double potentialIncrease = cell.getMaxDistance() - farthestCell.getDistance();
356-
return potentialIncrease > tolerance;
413+
return potentialIncrease > requiredTol;
357414
}
358415

359416
/**

0 commit comments

Comments
 (0)