Search in sources :

Example 1 with PreparedPolygon

use of org.locationtech.jts.geom.prep.PreparedPolygon in project graphhopper by graphhopper.

the class ContourBuilder method punchHoles.

@SuppressWarnings("unchecked")
private List<Polygon> punchHoles(List<LinearRing> rings) {
    List<PreparedPolygon> shells = new ArrayList<>(rings.size());
    List<LinearRing> holes = new ArrayList<>(rings.size() / 2);
    // 1. Split the polygon list in two: shells and holes (CCW and CW)
    for (LinearRing ring : rings) {
        if (CGAlgorithms.signedArea(ring.getCoordinateSequence()) > 0.0)
            holes.add(ring);
        else
            shells.add(new PreparedPolygon(geometryFactory.createPolygon(ring)));
    }
    // 2. Sort the shells based on number of points to optimize step 3.
    shells.sort((o1, o2) -> o2.getGeometry().getNumPoints() - o1.getGeometry().getNumPoints());
    for (PreparedPolygon shell : shells) {
        shell.getGeometry().setUserData(new ArrayList<LinearRing>());
    }
    // 3. For each hole, determine which shell it fits in.
    for (LinearRing hole : holes) {
        outer: {
            // Probably most of the time, the first shell will be the one
            for (PreparedPolygon shell : shells) {
                if (shell.contains(hole)) {
                    ((List<LinearRing>) shell.getGeometry().getUserData()).add(hole);
                    break outer;
                }
            }
            throw new RuntimeException("Found a hole without a shell.");
        }
    }
    // 4. Build the list of punched polygons
    List<Polygon> punched = new ArrayList<>(shells.size());
    for (PreparedPolygon shell : shells) {
        List<LinearRing> shellHoles = ((List<LinearRing>) shell.getGeometry().getUserData());
        punched.add(geometryFactory.createPolygon((LinearRing) (((Polygon) shell.getGeometry()).getExteriorRing()), shellHoles.toArray(new LinearRing[shellHoles.size()])));
    }
    return punched;
}
Also used : PreparedPolygon(org.locationtech.jts.geom.prep.PreparedPolygon) PreparedPolygon(org.locationtech.jts.geom.prep.PreparedPolygon)

Example 2 with PreparedPolygon

use of org.locationtech.jts.geom.prep.PreparedPolygon in project graphhopper by graphhopper.

the class Polygon method parsePoints.

public static Polygon parsePoints(String pointsStr) {
    String[] arr = pointsStr.split(",");
    if (arr.length % 2 == 1)
        throw new IllegalArgumentException("incorrect polygon specified: " + Arrays.asList(arr));
    Coordinate[] coordinates = new Coordinate[arr.length / 2 + 1];
    for (int i = 0; i < coordinates.length - 1; i++) {
        int arrIndex = i * 2;
        coordinates[i] = new Coordinate(Double.parseDouble(arr[arrIndex + 1]), Double.parseDouble(arr[arrIndex]));
    }
    coordinates[coordinates.length - 1] = coordinates[0];
    // store more efficient
    return new Polygon(new PreparedPolygon(new GeometryFactory().createPolygon(new PackedCoordinateSequence.Double(coordinates, 2))));
}
Also used : GeometryFactory(org.locationtech.jts.geom.GeometryFactory) Coordinate(org.locationtech.jts.geom.Coordinate) PreparedPolygon(org.locationtech.jts.geom.prep.PreparedPolygon) PreparedPolygon(org.locationtech.jts.geom.prep.PreparedPolygon) PackedCoordinateSequence(org.locationtech.jts.geom.impl.PackedCoordinateSequence)

Example 3 with PreparedPolygon

use of org.locationtech.jts.geom.prep.PreparedPolygon in project qupath by qupath.

the class GeometryTools method removeInteriorRings.

/**
 * Fill all interior rings for the specified geometry that have an area &lt; 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

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