use of org.locationtech.jts.geom.prep.PreparedGeometryFactory in project qupath by qupath.
the class GeometryTools method removeInteriorRings.
/**
* Fill all interior rings for the specified geometry that have an area < a specified threshold.
* <p>
* Note that this assumes that the geometry is valid, and does not contain self-intersections or overlapping pieces.
* No checks are made to confirm this (for performance reasons).
*
* @param geometry
* @param minRingArea
* @return
*/
public static Geometry removeInteriorRings(Geometry geometry, double minRingArea) {
if (minRingArea <= 0)
return geometry;
// Single polygons are easy... just remove the interior rings
if (geometry instanceof Polygon)
return removeInteriorRings((Polygon) geometry, minRingArea, null);
// // Quick check to see if we are filling all holes - this is also rather a lot easier
// if (!Double.isFinite(minRingArea))
// return fillHoles(geometry);
// Remove interior rings that are too small, logging their location in case we need it
// Also keep a list of small geometries, which might be inside rings that have been removed
var list = flatten(geometry, null);
var smallGeometries = new HashSet<Geometry>();
Quadtree tree = null;
var preparedFactory = new PreparedGeometryFactory();
var removedRingList = new ArrayList<LinearRing>();
for (int i = 0; i < list.size(); i++) {
var temp = list.get(i);
if (temp instanceof Polygon) {
var poly = removeInteriorRings((Polygon) temp, minRingArea, removedRingList);
if (poly != temp) {
// TODO: Add the holes rather than the full polygons
if (tree == null)
tree = new Quadtree();
for (var ring : removedRingList) {
var hole = preparedFactory.create(ring.getFactory().createPolygon(ring));
tree.insert(ring.getEnvelopeInternal(), hole);
}
list.set(i, poly);
removedRingList.clear();
}
// Check also if the polygon could also be swallowed by another filled hole
if (org.locationtech.jts.algorithm.Area.ofRing(poly.getExteriorRing().getCoordinateSequence()) < minRingArea)
smallGeometries.add(poly);
} else if (temp.getArea() < minRingArea)
smallGeometries.add(temp);
}
// If we don't have a tree, we didn't change anything
if (tree == null)
return geometry;
// Loop through and remove any small polygons nested inside rings that were removed
var iter = list.iterator();
while (iter.hasNext()) {
var small = iter.next();
if (smallGeometries.contains(small)) {
var query = (List<PreparedPolygon>) tree.query(small.getEnvelopeInternal());
for (PreparedPolygon hole : query) {
if (hole.covers(small)) {
iter.remove();
break;
}
// if (PointLocation.isInRing(small.getInteriorPoint().getCoordinate(), ring.getCoordinates())) {
// iter.remove();
// break;
// }
}
}
}
// Build a geometry from what is left
return geometry.getFactory().buildGeometry(list);
// var filtered = list.stream().map(g -> {
// if (g instanceof Polygon)
// return removeInteriorRings((Polygon)g, minRingArea);
// else
// return g;
// }).collect(Collectors.toList());
// TODO: Find out how to avoid the Union operation (which can be very slow)
// We need to use union because there may be polygons nested within holes that have been filled
// return GeometryTools.union(filtered);
}
Aggregations