Skip to content
Merged
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 @@ -52,7 +52,7 @@ public static double maximumDiameterLength(Geometry g) {
//--------------------------------------------

@Metadata(description="Constructs the Maximum Inscribed Circle of a polygonal geometry")
public static Geometry maximumInscribedCircle(Geometry g,
public static Geometry maxInscribedCircle(Geometry g,
@Metadata(title="Distance tolerance")
double tolerance) {
MaximumInscribedCircle mic = new MaximumInscribedCircle(g, tolerance);
Expand All @@ -63,22 +63,22 @@ public static Geometry maximumInscribedCircle(Geometry g,
}

@Metadata(description="Constructs the center point of the Maximum Inscribed Circle of a polygonal geometry")
public static Geometry maximumInscribedCircleCenter(Geometry g,
public static Geometry maxInscribedCircleCenter(Geometry g,
@Metadata(title="Distance tolerance")
double tolerance) {
return MaximumInscribedCircle.getCenter(g, tolerance);
}

@Metadata(description="Constructs a radius line of the Maximum Inscribed Circle of a polygonal geometry")
public static Geometry maximumInscribedCircleRadius(Geometry g,
public static Geometry maxInscribedCircleRadius(Geometry g,
@Metadata(title="Distance tolerance")
double tolerance) {
MaximumInscribedCircle mic = new MaximumInscribedCircle(g, tolerance);
return mic.getRadiusLine();
}

@Metadata(description="Computes the radius of the Maximum Inscribed Circle of a polygonal geometry")
public static double maximumInscribedCircleRadiusLen(Geometry g,
public static double maxInscribedCircleRadiusLen(Geometry g,
@Metadata(title="Distance tolerance")
double tolerance) {
MaximumInscribedCircle mic = new MaximumInscribedCircle(g, tolerance);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@
import java.util.ArrayList;
import java.util.List;

import org.locationtech.jts.algorithm.construct.MaximumInscribedCircle;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.prep.PreparedGeometry;
import org.locationtech.jts.geom.prep.PreparedGeometryFactory;
import org.locationtech.jts.operation.distance.IndexedFacetDistance;
import org.locationtech.jtstest.geomfunction.Metadata;

public class SelectionFunctions
{
Expand Down Expand Up @@ -227,6 +229,17 @@ public boolean isTrue(Geometry g) {
});
}

public static Geometry maxInCircleRadiusWithin(Geometry a,
@Metadata(title="Max Radius Length")
double maximumRadius)
{
return select(a, new GeometryPredicate() {
public boolean isTrue(Geometry g) {
return MaximumInscribedCircle.isRadiusWithin(g, maximumRadius);
}
});
}

public static Geometry select(Geometry geom, GeometryPredicate pred)
{
List selected = new ArrayList();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,31 @@
* measure of how "narrow" a polygon is. It is the
* distance at which the negative buffer becomes empty.
* <p>
* The class supports testing whether a polygon is "narrower"
* than a specified distance via
* {@link #isRadiusWithin(Geometry, double)} or
* {@link #isRadiusWithin(double)}.
* Testing for the maximum radius is generally much faster
* than computing the actual radius value, since short-circuiting
* is used to limit the approximation iterations.
* <p>
* The class supports polygons with holes and multipolygons.
* <p>
* The implementation uses a successive-approximation technique
* over a grid of square cells covering the area geometry.
* The grid is refined using a branch-and-bound algorithm.
* Point containment and distance are computed in a performant
* way by using spatial indexes.
*
*
* <h3>Future Enhancements</h3>
* <ul>
* <li>Support a polygonal constraint on placement of center
* <li>Support a polygonal constraint on placement of center point,
* for example to produce circle-packing constructions,
* or support multiple labels.
* </ul>
*
* @author Martin Davis
*
* @see LargestEmptyCircle
* @see InteriorPoint
* @see Centroid
Expand All @@ -83,13 +94,28 @@ public static Point getCenter(Geometry polygonal, double tolerance) {
*
* @param polygonal a polygonal geometry
* @param tolerance the distance tolerance for computing the center point
* @return a line from the center to a point on the circle
* @return a 2-point line from the center to a point on the circle
*/
public static LineString getRadiusLine(Geometry polygonal, double tolerance) {
MaximumInscribedCircle mic = new MaximumInscribedCircle(polygonal, tolerance);
return mic.getRadiusLine();
}

/**
* Tests if the radius of the maximum inscribed circle
* is no longer than the specified distance.
* This method determines the distance tolerance automatically
* as a fraction of the maxRadius value.
*
* @param polygonal a polygonal geometry
* @param maxRadius the radius value to test
* @return true if the max in-circle radius is no longer than the max radius
*/
public static boolean isRadiusWithin(Geometry polygonal, double maxRadius) {
MaximumInscribedCircle mic = new MaximumInscribedCircle(polygonal, -1);
return mic.isRadiusWithin(maxRadius);
}

/**
* Computes the maximum number of iterations allowed.
* Uses a heuristic based on the size of the input geometry
Expand Down Expand Up @@ -122,6 +148,7 @@ static long computeMaximumIterations(Geometry geom, double toleranceDist) {
private Coordinate radiusPt;
private Point centerPoint;
private Point radiusPoint;
private double maximumRadius = -1;;

/**
* Creates a new instance of a Maximum Inscribed Circle computation.
Expand All @@ -131,9 +158,6 @@ static long computeMaximumIterations(Geometry geom, double toleranceDist) {
* @throws IllegalArgumentException if the tolerance is non-positive, or the input geometry is non-polygonal or empty.
*/
public MaximumInscribedCircle(Geometry polygonal, double tolerance) {
if (tolerance <= 0) {
throw new IllegalArgumentException("Tolerance must be positive");
}
if (! (polygonal instanceof Polygon || polygonal instanceof MultiPolygon)) {
throw new IllegalArgumentException("Input geometry must be a Polygon or MultiPolygon");
}
Expand All @@ -146,6 +170,35 @@ public MaximumInscribedCircle(Geometry polygonal, double tolerance) {
this.tolerance = tolerance;
}

private static final double MAX_RADIUS_FRACTION = 0.0001;

/**
* Tests if the radius of the maximum inscribed circle
* is no longer than the specified distance.
* This method determines the distance tolerance automatically
* as a fraction of the maxRadius value.
* After this method is called the center and radius
* points provide locations demonstrating where
* the radius exceeds the specified maximum.
*
* @param maxRadius the (non-negative) radius value to test
* @return true if the max in-circle radius is no longer than the max radius
*/
public boolean isRadiusWithin(double maxRadius) {
if (maxRadius < 0) {
throw new IllegalArgumentException("Radius length must be non-negative");
}
//-- handle 0 corner case, to provide maximum domain
if (maxRadius == 0) {
return false;
}
maximumRadius = maxRadius;
tolerance = maxRadius * MAX_RADIUS_FRACTION;
compute();
double radius = centerPt.distance(radiusPt);
return radius <= maximumRadius;
}

/**
* Gets the center point of the maximum inscribed circle
* (up to the tolerance distance).
Expand Down Expand Up @@ -228,6 +281,10 @@ private void compute() {
return;
}

//-- only needed for approximation
if (tolerance <= 0) {
throw new IllegalArgumentException("Tolerance must be positive");
}
computeApproximation();
}

Expand Down Expand Up @@ -266,14 +323,25 @@ private void computeApproximation() {
//System.out.println(iter + "] Dist: " + cell.getDistance() + " Max D: " + cell.getMaxDistance() + " size: " + cell.getHSide());
//TestBuilderProxy.showIndicator(inputGeom.getFactory().toGeometry(cell.getEnvelope()));

//-- if cell must be closer than furthest, terminate since all remaining cells in queue are even closer.
//-- if cell must be closer than farthest, terminate since all remaining cells in queue are even closer.
if (cell.getMaxDistance() < farthestCell.getDistance())
break;

// update the circle center cell if the candidate is further from the boundary
if (cell.getDistance() > farthestCell.getDistance()) {
farthestCell = cell;
}

//-- search termination when checking max radius predicate
if (maximumRadius >= 0) {
//-- found a inside point further than max radius
if (farthestCell.getDistance() > maximumRadius)
break;
//-- no cells can have larger radius
if (cell.getMaxDistance() < maximumRadius)
break;
}

/**
* Refine this cell if the potential distance improvement
* is greater than the required tolerance.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.locationtech.jts.algorithm.construct;

import org.locationtech.jts.geom.Geometry;

import junit.textui.TestRunner;
import test.jts.GeometryTestCase;

public class MaxInscribedCircleRadiusWithinTest extends GeometryTestCase {
public static void main(String args[]) {
TestRunner.run(MaxInscribedCircleRadiusWithinTest.class);
}

public MaxInscribedCircleRadiusWithinTest(String name) { super(name); }

public void testDiamond() {
String wkt = "POLYGON ((150 250, 50 150, 150 50, 250 150, 150 250))";
checkRadiusWithin(wkt, 0, false);
checkRadiusWithin(wkt, 70, false);
checkRadiusWithin(wkt, 71, true);
checkRadiusWithin(wkt, 1000, true);
}

public void testIsland() {
String wkt = "POLYGON ((0.041 0.3017, 0.1251 0.3137, 0.3414 0.3297, 0.7217 0.3351, 0.8979 0.3537, 1.0351 0.3838, 1.2432 0.4438, 1.3694 0.4492, 1.5145 0.4325, 1.6167 0.4151, 1.8529 0.4178, 1.94 0.4078, 2.0601 0.3798, 2.2283 0.3784, 2.3084 0.3677, 2.3254 0.347, 2.3414 0.2856, 2.3294 0.2643, 2.2934 0.2483, 2.2633 0.2376, 2.2613 0.2216, 2.2854 0.2069, 2.3414 0.1949, 2.3474 0.1802, 2.3394 0.1615, 2.3034 0.1402, 2.3133 0.1268, 2.3654 0.1228, 2.3855 0.1095, 2.3835 0.0708, 2.2984 0.0541, 2.2844 0.0407, 2.2704 0.0301, 2.2563 0.0327, 2.2203 0.0501, 2.1622 0.0514, 2.1422 0.0461, 2.1382 0.0314, 2.0882 0.0301, 2.0221 0.0407, 1.9761 0.0301, 1.932 0.022, 1.863 0, 1.8409 0.0053, 1.8309 0.0214, 1.8189 0.0147, 1.7869 0.0013, 1.7528 0.004, 1.7108 0.024, 1.6848 0.0547, 1.6888 0.0721, 1.6688 0.0841, 1.6207 0.0948, 1.5907 0.1201, 1.5606 0.1508, 1.4766 0.1628, 1.3054 0.1595, 1.2233 0.1782, 1.0872 0.1742, 0.907 0.1728, 0.7618 0.1935, 0.6317 0.2095, 0.4715 0.2015, 0.3974 0.2055, 0.2282 0.2409, 0.08 0.2382, 0.018 0.2329, 0 0.2422, 0.01 0.2729, 0.022 0.297, 0.041 0.3017))";
checkRadiusWithin(wkt, 0.1, false);
checkRadiusWithin(wkt, 0.2, true);
}

private void checkRadiusWithin(String wkt, double maxRadius, boolean expected) {
Geometry geom = read(wkt);
boolean actual = MaximumInscribedCircle.isRadiusWithin(geom, maxRadius);
assertEquals(expected, actual);
}

}