4343 * measure of how "narrow" a polygon is. It is the
4444 * distance at which the negative buffer becomes empty.
4545 * <p>
46+ * The class supports testing whether a polygon is "narrower"
47+ * than a specified distance via
48+ * {@link #isRadiusWithin(Geometry, double)} or
49+ * {@link #isRadiusWithin(double)}.
50+ * Testing for the maximum radius is generally much faster
51+ * than computing the actual radius value, since short-circuiting
52+ * is used to limit the approximation iterations.
53+ * <p>
4654 * The class supports polygons with holes and multipolygons.
4755 * <p>
4856 * The implementation uses a successive-approximation technique
4957 * over a grid of square cells covering the area geometry.
5058 * The grid is refined using a branch-and-bound algorithm.
5159 * Point containment and distance are computed in a performant
5260 * way by using spatial indexes.
53- *
61+ *
5462 * <h3>Future Enhancements</h3>
5563 * <ul>
56- * <li>Support a polygonal constraint on placement of center
64+ * <li>Support a polygonal constraint on placement of center point,
65+ * for example to produce circle-packing constructions,
66+ * or support multiple labels.
5767 * </ul>
5868 *
5969 * @author Martin Davis
70+ *
6071 * @see LargestEmptyCircle
6172 * @see InteriorPoint
6273 * @see Centroid
@@ -83,13 +94,28 @@ public static Point getCenter(Geometry polygonal, double tolerance) {
8394 *
8495 * @param polygonal a polygonal geometry
8596 * @param tolerance the distance tolerance for computing the center point
86- * @return a line from the center to a point on the circle
97+ * @return a 2-point line from the center to a point on the circle
8798 */
8899 public static LineString getRadiusLine (Geometry polygonal , double tolerance ) {
89100 MaximumInscribedCircle mic = new MaximumInscribedCircle (polygonal , tolerance );
90101 return mic .getRadiusLine ();
91102 }
92103
104+ /**
105+ * Tests if the radius of the maximum inscribed circle
106+ * is no longer than the specified distance.
107+ * This method determines the distance tolerance automatically
108+ * as a fraction of the maxRadius value.
109+ *
110+ * @param polygonal a polygonal geometry
111+ * @param maxRadius the radius value to test
112+ * @return true if the max in-circle radius is no longer than the max radius
113+ */
114+ public static boolean isRadiusWithin (Geometry polygonal , double maxRadius ) {
115+ MaximumInscribedCircle mic = new MaximumInscribedCircle (polygonal , -1 );
116+ return mic .isRadiusWithin (maxRadius );
117+ }
118+
93119 /**
94120 * Computes the maximum number of iterations allowed.
95121 * Uses a heuristic based on the size of the input geometry
@@ -122,6 +148,7 @@ static long computeMaximumIterations(Geometry geom, double toleranceDist) {
122148 private Coordinate radiusPt ;
123149 private Point centerPoint ;
124150 private Point radiusPoint ;
151+ private double maximumRadius = -1 ;;
125152
126153 /**
127154 * Creates a new instance of a Maximum Inscribed Circle computation.
@@ -131,9 +158,6 @@ static long computeMaximumIterations(Geometry geom, double toleranceDist) {
131158 * @throws IllegalArgumentException if the tolerance is non-positive, or the input geometry is non-polygonal or empty.
132159 */
133160 public MaximumInscribedCircle (Geometry polygonal , double tolerance ) {
134- if (tolerance <= 0 ) {
135- throw new IllegalArgumentException ("Tolerance must be positive" );
136- }
137161 if (! (polygonal instanceof Polygon || polygonal instanceof MultiPolygon )) {
138162 throw new IllegalArgumentException ("Input geometry must be a Polygon or MultiPolygon" );
139163 }
@@ -146,6 +170,35 @@ public MaximumInscribedCircle(Geometry polygonal, double tolerance) {
146170 this .tolerance = tolerance ;
147171 }
148172
173+ private static final double MAX_RADIUS_FRACTION = 0.0001 ;
174+
175+ /**
176+ * Tests if the radius of the maximum inscribed circle
177+ * is no longer than the specified distance.
178+ * This method determines the distance tolerance automatically
179+ * as a fraction of the maxRadius value.
180+ * After this method is called the center and radius
181+ * points provide locations demonstrating where
182+ * the radius exceeds the specified maximum.
183+ *
184+ * @param maxRadius the (non-negative) radius value to test
185+ * @return true if the max in-circle radius is no longer than the max radius
186+ */
187+ public boolean isRadiusWithin (double maxRadius ) {
188+ if (maxRadius < 0 ) {
189+ throw new IllegalArgumentException ("Radius length must be non-negative" );
190+ }
191+ //-- handle 0 corner case, to provide maximum domain
192+ if (maxRadius == 0 ) {
193+ return false ;
194+ }
195+ maximumRadius = maxRadius ;
196+ tolerance = maxRadius * MAX_RADIUS_FRACTION ;
197+ compute ();
198+ double radius = centerPt .distance (radiusPt );
199+ return radius <= maximumRadius ;
200+ }
201+
149202 /**
150203 * Gets the center point of the maximum inscribed circle
151204 * (up to the tolerance distance).
@@ -228,6 +281,10 @@ private void compute() {
228281 return ;
229282 }
230283
284+ //-- only needed for approximation
285+ if (tolerance <= 0 ) {
286+ throw new IllegalArgumentException ("Tolerance must be positive" );
287+ }
231288 computeApproximation ();
232289 }
233290
@@ -266,14 +323,25 @@ private void computeApproximation() {
266323 //System.out.println(iter + "] Dist: " + cell.getDistance() + " Max D: " + cell.getMaxDistance() + " size: " + cell.getHSide());
267324 //TestBuilderProxy.showIndicator(inputGeom.getFactory().toGeometry(cell.getEnvelope()));
268325
269- //-- if cell must be closer than furthest , terminate since all remaining cells in queue are even closer.
326+ //-- if cell must be closer than farthest , terminate since all remaining cells in queue are even closer.
270327 if (cell .getMaxDistance () < farthestCell .getDistance ())
271328 break ;
272329
273330 // update the circle center cell if the candidate is further from the boundary
274331 if (cell .getDistance () > farthestCell .getDistance ()) {
275332 farthestCell = cell ;
276333 }
334+
335+ //-- search termination when checking max radius predicate
336+ if (maximumRadius >= 0 ) {
337+ //-- found a inside point further than max radius
338+ if (farthestCell .getDistance () > maximumRadius )
339+ break ;
340+ //-- no cells can have larger radius
341+ if (cell .getMaxDistance () < maximumRadius )
342+ break ;
343+ }
344+
277345 /**
278346 * Refine this cell if the potential distance improvement
279347 * is greater than the required tolerance.
0 commit comments