You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@sedona.apache.org by GitBox <gi...@apache.org> on 2023/01/02 18:32:53 UTC

[GitHub] [incubator-sedona] Kimahriman commented on a diff in pull request #742: [SEDONA-223] Add ST_Split

Kimahriman commented on code in PR #742:
URL: https://github.com/apache/incubator-sedona/pull/742#discussion_r1060157902


##########
common/src/main/java/org/apache/sedona/common/utils/GeometrySplitter.java:
##########
@@ -0,0 +1,321 @@
+package org.apache.sedona.common.utils;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.List;
+
+import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.geom.GeometryCollection;
+import org.locationtech.jts.geom.GeometryFactory;
+import org.locationtech.jts.geom.Lineal;
+import org.locationtech.jts.geom.LineString;
+import org.locationtech.jts.geom.MultiLineString;
+import org.locationtech.jts.geom.MultiPoint;
+import org.locationtech.jts.geom.MultiPolygon;
+import org.locationtech.jts.geom.Point;
+import org.locationtech.jts.geom.Polygon;
+import org.locationtech.jts.geom.Polygonal;
+import org.locationtech.jts.geom.Puntal;
+import org.locationtech.jts.linearref.LinearGeometryBuilder;
+import org.locationtech.jts.operation.polygonize.Polygonizer;
+
+
+/**
+ * Class to split geometry by other geometry.
+ */
+public final class GeometrySplitter {
+    private final GeometryFactory geometryFactory;
+
+    public GeometrySplitter(GeometryFactory geometryFactory) {
+        this.geometryFactory = geometryFactory;
+    }
+
+    /**
+     * Split input geometry by the blade geometry.
+     * Input geometry can be lineal (LineString or MultiLineString)
+     * or polygonal (Polygon or MultiPolygon). A GeometryCollection
+     * can also be used as an input but it must be homogeneous.
+     * For lineal geometry refer to the
+     * {@link splitLines(Geometry, Geometry) splitLines} method for
+     * restrictions on the blade. Refer to
+     * {@link splitPolygons(Geometry, Geometry) splitPolygons} for
+     * restrictions on the blade for polygonal input geometry.
+     * <p>
+     * The result will be null if the input geometry and blade are either
+     * invalid in general or in relation to eachother. Otherwise, the result
+     * will always be a MultiLineString or MultiPolygon depending on the input,
+     * and even if the result is a single geometry.
+     *
+     * @param  input input geometry
+     * @param  blade geometry to use as a blade
+     * @return       multi-geometry resulting from the split or null if invalid
+     */
+    public GeometryCollection split(Geometry input, Geometry blade) {
+        GeometryCollection result = null;
+
+        if (GeomUtils.geometryIsLineal(input)) {
+            result = splitLines(input, blade);
+        } else if (GeomUtils.geometryIsPolygonal(input)) {
+            result = splitPolygons(input, blade);
+        }
+
+        return result;
+    }
+
+    /**
+     * Split linear input geometry by the blade geometry.
+     * Input geometry is assumed to be either a LineString,
+     * MultiLineString, or a homogeneous collection of lines in a
+     * GeometryCollection. The blade geometry can be any indivdual
+     * puntal, lineal, or polygonal geometry or homogeneous collection
+     * of those geometries. Blades that are polygonal will use their
+     * boundary for the split. Will always return a MultiLineString.
+     *
+     * @param  input input geometry to be split that must be lineal
+     * @param  blade blade geometry to use for split
+     *
+     * @return       input geometry split by blade
+     */
+    public MultiLineString splitLines(Geometry input, Geometry blade) {
+        MultiLineString result = null;
+
+        if (GeomUtils.geometryIsPolygonal(blade)) {
+            blade = blade.getBoundary();
+        }
+
+        if (GeomUtils.geometryIsPuntal(blade)) {
+            result = splitLinesByPoints(input, blade);
+        } else if (GeomUtils.geometryIsLineal(blade)) {
+            result = splitLinesByLines(input, blade);
+        }
+
+        return result;
+    }
+
+    /**
+     * Split polygonal input geometry by the blade geometry.
+     * Input geometry is assumed to be either a Polygon, MultiPolygon,
+     * or a GeometryCollection of only polygons. The blade geometry
+     * can be any individual lineal or polygonal geometry or homogeneous
+     * collection of those geometries. Blades that are polygonal
+     * will use their boundary for the split. Will always return a
+     * MultiPolygon.
+     *
+     * @param  input input polygonal geometry to split
+     * @param  blade geometry to split the input by
+     * @return       input geometry split by the blade
+     */
+    public MultiPolygon splitPolygons(Geometry input, Geometry blade) {
+        MultiPolygon result = null;
+
+        if (GeomUtils.geometryIsPolygonal(blade)) {
+            blade = blade.getBoundary();
+        }
+
+        if (GeomUtils.geometryIsLineal(blade)) {
+            result = splitPolygonsByLines(input, blade);
+        }
+
+        return result;
+    }
+
+    private MultiLineString splitLinesByPoints(Geometry lines, Geometry points) {
+        // coords must be ordered for the algorithm in splitLineStringAtCoordinates
+        // do it here so the sorting is only done once per split operation
+        // use a deque for easy forward and reverse iteration
+        Deque<Coordinate> pointCoords = getOrderedCoords(points);
+
+        LinearGeometryBuilder lineBuilder = new LinearGeometryBuilder(geometryFactory);
+        lineBuilder.setIgnoreInvalidLines(true);
+
+        for (int lineIndex = 0; lineIndex < lines.getNumGeometries(); lineIndex++) {
+            splitLineStringAtCoordinates((LineString)lines.getGeometryN(lineIndex), pointCoords, lineBuilder);
+        }
+
+        MultiLineString result = (MultiLineString)ensureMultiGeometryOfDimensionN(lineBuilder.getGeometry(), 1);
+
+        return result;
+    }
+
+    private MultiLineString splitLinesByLines(Geometry inputLines, Geometry blade) {
+        // compute the intersection of inputLines and blade
+        // and pass back to splitLines to handle as points
+        Geometry intersectionWithBlade = inputLines.intersection(blade);
+
+        if (intersectionWithBlade.isEmpty()) {
+            // blade and inputLines are disjoint so just return the input as a multilinestring
+            return (MultiLineString)ensureMultiGeometryOfDimensionN(inputLines, 1);
+        } else if (intersectionWithBlade.getDimension() != 0) {
+            // FIXME need way to access the logger
+            System.err.println("Colinear sections detected between source and blade geometry. Returned null.");

Review Comment:
   I was looking into standardizing the logging dependencies a little bit as a follow on to my last PR, can submit one for that soon. Right now log4j is available when spark is included, but not in common or flink



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: dev-unsubscribe@sedona.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org