Search in sources :

Example 1 with PreparedGeometryFactory

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);
}
Also used : Quadtree(org.locationtech.jts.index.quadtree.Quadtree) ArrayList(java.util.ArrayList) List(java.util.List) ArrayList(java.util.ArrayList) CoordinateList(org.locationtech.jts.geom.CoordinateList) PreparedPolygon(org.locationtech.jts.geom.prep.PreparedPolygon) Polygon(org.locationtech.jts.geom.Polygon) PreparedPolygon(org.locationtech.jts.geom.prep.PreparedPolygon) Point(org.locationtech.jts.geom.Point) MultiPoint(org.locationtech.jts.geom.MultiPoint) HashSet(java.util.HashSet) PreparedGeometryFactory(org.locationtech.jts.geom.prep.PreparedGeometryFactory)

Aggregations

ArrayList (java.util.ArrayList)1 HashSet (java.util.HashSet)1 List (java.util.List)1 CoordinateList (org.locationtech.jts.geom.CoordinateList)1 MultiPoint (org.locationtech.jts.geom.MultiPoint)1 Point (org.locationtech.jts.geom.Point)1 Polygon (org.locationtech.jts.geom.Polygon)1 PreparedGeometryFactory (org.locationtech.jts.geom.prep.PreparedGeometryFactory)1 PreparedPolygon (org.locationtech.jts.geom.prep.PreparedPolygon)1 Quadtree (org.locationtech.jts.index.quadtree.Quadtree)1