This is an automated email from the ASF dual-hosted git repository. jiayu pushed a commit to branch SEDONA-602 in repository https://gitbox.apache.org/repos/asf/sedona.git
commit 87c8532727cec08316cf0ea23b3828a459c12801 Author: Feng Zhang <[email protected]> AuthorDate: Mon Jun 3 09:45:28 2024 -0700 Merge pull request #208 from wherobots/refactor-st-locate-along [TASK-187] Refactor ST_LocateAlong implementation to remove instanceof operator --- .../java/org/apache/sedona/common/Functions.java | 4 +- .../org/apache/sedona/common/utils/GeomUtils.java | 96 ---------------- .../common/utils/GeometryLocateAlongProcessor.java | 122 +++++++++++++++++++++ 3 files changed, 125 insertions(+), 97 deletions(-) diff --git a/common/src/main/java/org/apache/sedona/common/Functions.java b/common/src/main/java/org/apache/sedona/common/Functions.java index 767e1b77b..e3124cc2e 100644 --- a/common/src/main/java/org/apache/sedona/common/Functions.java +++ b/common/src/main/java/org/apache/sedona/common/Functions.java @@ -20,7 +20,9 @@ import com.uber.h3core.util.LatLng; import org.apache.commons.lang3.tuple.Pair; import org.apache.sedona.common.geometryObjects.Circle; import org.apache.sedona.common.sphere.Spheroid; +import org.apache.sedona.common.subDivide.ExtentBasedGeometrySubDivider; import org.apache.sedona.common.subDivide.GeometrySubDivider; +import org.apache.sedona.common.subDivide.SubdivideOptions; import org.apache.sedona.common.utils.*; import org.locationtech.jts.algorithm.MinimumBoundingCircle; import org.locationtech.jts.algorithm.Orientation; @@ -975,7 +977,7 @@ public class Functions { } public static Geometry locateAlong(Geometry linear, double measure, double offset) { - return GeomUtils.locateAlong(linear, measure, offset); + return GeometryLocateAlongProcessor.processGeometry(linear, measure, offset); } public static Geometry locateAlong(Geometry linear, double measure) { diff --git a/common/src/main/java/org/apache/sedona/common/utils/GeomUtils.java b/common/src/main/java/org/apache/sedona/common/utils/GeomUtils.java index b946dd7fb..fc36e79e5 100644 --- a/common/src/main/java/org/apache/sedona/common/utils/GeomUtils.java +++ b/common/src/main/java/org/apache/sedona/common/utils/GeomUtils.java @@ -550,100 +550,4 @@ public class GeomUtils { return geom; } } - - public static Geometry locateAlong(Geometry geometry, double measure, double offset) { - if (!isMeasuredGeometry(geometry)) { - throw new IllegalArgumentException("Input geometry is doesn't have a measure dimension"); - } - - if (geometry instanceof Point) { - return locateAlongPoint(geometry, measure, offset); - } - if (geometry instanceof MultiPoint) { - // iterating through Points in MultiPoint object - Point[] points = new Point[geometry.getNumGeometries()]; - for (int i = 0; i < geometry.getNumGeometries(); i++) { - points[i] = locateAlongPoint(geometry.getGeometryN(i), measure, offset); - } - return geometry.getFactory().createMultiPoint(Arrays.stream(points).filter(Objects::nonNull).toArray(Point[]::new)); - } - if (geometry instanceof LineString) { - return locateAlongLinestring(geometry, measure, offset); - } - if (geometry instanceof MultiLineString) { - // iterating through LineStrings in MultiLineString object - List<Point> points = new ArrayList<>(); - for (int i = 0; i < geometry.getNumGeometries(); i++) { - MultiPoint mPoint = (MultiPoint) locateAlongLinestring(geometry.getGeometryN(i), measure, offset); - for (int j = 0; j < mPoint.getNumGeometries(); j++) { - points.add((Point) mPoint.getGeometryN(j)); - } - } - - return geometry.getFactory().createMultiPoint(points.toArray(new Point[0])); - } - - throw new IllegalArgumentException(String.format("%s geometry type not supported, supported types are: (Multi)Point and (Multi)LineString.", geometry.getGeometryType())); - } - - private static Geometry locateAlongLinestring(Geometry geometry, double measure, double offset) { - Coordinate[] coordinates = geometry.getCoordinates(); - CoordinateList coordinateList = new CoordinateList(); - - for (int i = 1; i < coordinates.length; i++) { - Coordinate coordinate1 = coordinates[i - 1]; - Coordinate coordinate2 = coordinates[i]; - CoordinateXYZM newCoordinate = new CoordinateXYZM(); - double position; - - double measure1 = coordinate1.getM(), - measure2 = coordinate2.getM(); - - if ((measure < Math.min(measure1, measure2)) || (measure > Math.max(measure1, measure2))) { - continue; - } - - if (measure1 == measure2) { - // If the measures are equal then there is no valid interpolation range - if (coordinate1.equals(coordinate2)) { - newCoordinate.setX(coordinate1.getX()); - newCoordinate.setY(coordinate1.getY()); - newCoordinate.setZ(coordinate1.getZ()); - newCoordinate.setM(coordinate1.getM()); - coordinateList.add(newCoordinate, false); - continue; - } - // the point will be in the midpoint of coordinate1 and coordinate2 as measure1 and measure2 are same - position = 0.5; - } else { - // calculate the interpolation factor / position - position = (measure - measure1) / (measure2 - measure1); - } - - // apply linear interpolation to find the point along the line - newCoordinate.setX(coordinate1.x + (coordinate2.x - coordinate1.x) * position); - newCoordinate.setY(coordinate1.y + (coordinate2.y - coordinate1.y) * position); - newCoordinate.setZ(coordinate1.z + (coordinate2.z - coordinate1.z) * position); - newCoordinate.setM(measure); - - if (offset != 0D) { - // calculate the angle of the line segment - double theta = Math.atan2(coordinate2.y - coordinate1.y, coordinate2.x - coordinate1.x); - // shift the coordinate left or right by the offset - // if the offset is positive then shift to left - // else the offset is negative then shift to right - newCoordinate.setX(newCoordinate.x - Math.sin(theta) * offset); - newCoordinate.setY(newCoordinate.y + Math.cos(theta) * offset); - } - coordinateList.add(newCoordinate, false); - } - return geometry.getFactory().createMultiPointFromCoords(Arrays.stream(coordinateList.toCoordinateArray()).filter(Objects::nonNull).toArray(Coordinate[]::new)); - } - - private static Point locateAlongPoint(Geometry geometry, double measure, double offset) { - if (measure == geometry.getCoordinate().getM()) { - return (Point) geometry; - } - return null; - } } diff --git a/common/src/main/java/org/apache/sedona/common/utils/GeometryLocateAlongProcessor.java b/common/src/main/java/org/apache/sedona/common/utils/GeometryLocateAlongProcessor.java new file mode 100644 index 000000000..49bbdc4b4 --- /dev/null +++ b/common/src/main/java/org/apache/sedona/common/utils/GeometryLocateAlongProcessor.java @@ -0,0 +1,122 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sedona.common.utils; + +import org.locationtech.jts.geom.*; + +import java.util.*; +import java.util.function.BiFunction; + +public class GeometryLocateAlongProcessor { + + private final Map<Class<?>, BiFunction<Geometry, double[], Geometry>> geometryFunctions = new HashMap<>(); + + public GeometryLocateAlongProcessor() { + geometryFunctions.put(Point.class, (geometry, params) -> locateAlongPoint((Point) geometry, params[0], params[1])); + geometryFunctions.put(MultiPoint.class, (geometry, params) -> locateAlongMultiPoint((MultiPoint) geometry, params[0], params[1])); + geometryFunctions.put(LineString.class, (geometry, params) -> locateAlongLineString((LineString) geometry, params[0], params[1])); + geometryFunctions.put(MultiLineString.class, (geometry, params) -> locateAlongMultiLineString((MultiLineString) geometry, params[0], params[1])); + } + + public static Geometry processGeometry(Geometry geometry, double measure, double offset) { + GeometryLocateAlongProcessor processor = new GeometryLocateAlongProcessor(); + BiFunction<Geometry, double[], Geometry> function = processor.geometryFunctions.get(geometry.getClass()); + if (function != null) { + return function.apply(geometry, new double[]{measure, offset}); + } + throw new IllegalArgumentException(String.format("%s geometry type not supported, supported types are: (Multi)Point and (Multi)LineString.", geometry.getGeometryType())); + } + + private Geometry locateAlongPoint(Point point, double measure, double offset) { + if (measure == point.getCoordinate().getM()) { + return point; + } + return null; + } + + private Geometry locateAlongMultiPoint(MultiPoint multiPoint, double measure, double offset) { + Point[] points = new Point[multiPoint.getNumGeometries()]; + for (int i = 0; i < multiPoint.getNumGeometries(); i++) { + points[i] = (Point) locateAlongPoint((Point) multiPoint.getGeometryN(i), measure, offset); + } + return multiPoint.getFactory().createMultiPoint(Arrays.stream(points).filter(Objects::nonNull).toArray(Point[]::new)); + } + + private Geometry locateAlongLineString(LineString lineString, double measure, double offset) { + Coordinate[] coordinates = lineString.getCoordinates(); + CoordinateList coordinateList = new CoordinateList(); + + for (int i = 1; i < coordinates.length; i++) { + Coordinate coordinate1 = coordinates[i - 1]; + Coordinate coordinate2 = coordinates[i]; + CoordinateXYZM newCoordinate = new CoordinateXYZM(); + double position; + + double measure1 = coordinate1.getM(), + measure2 = coordinate2.getM(); + + if ((measure < Math.min(measure1, measure2)) || (measure > Math.max(measure1, measure2))) { + continue; + } + + if (measure1 == measure2) { + // If the measures are equal then there is no valid interpolation range + if (coordinate1.equals(coordinate2)) { + newCoordinate.setX(coordinate1.getX()); + newCoordinate.setY(coordinate1.getY()); + newCoordinate.setZ(coordinate1.getZ()); + newCoordinate.setM(coordinate1.getM()); + coordinateList.add(newCoordinate, false); + continue; + } + // the point will be in the midpoint of coordinate1 and coordinate2 as measure1 and measure2 are same + position = 0.5; + } else { + // calculate the interpolation factor / position + position = (measure - measure1) / (measure2 - measure1); + } + + // apply linear interpolation to find the point along the line + newCoordinate.setX(coordinate1.x + (coordinate2.x - coordinate1.x) * position); + newCoordinate.setY(coordinate1.y + (coordinate2.y - coordinate1.y) * position); + newCoordinate.setZ(coordinate1.z + (coordinate2.z - coordinate1.z) * position); + newCoordinate.setM(measure); + + if (offset != 0D) { + // calculate the angle of the line segment + double theta = Math.atan2(coordinate2.y - coordinate1.y, coordinate2.x - coordinate1.x); + // shift the coordinate left or right by the offset + // if the offset is positive then shift to left + // else the offset is negative then shift to right + newCoordinate.setX(newCoordinate.x - Math.sin(theta) * offset); + newCoordinate.setY(newCoordinate.y + Math.cos(theta) * offset); + } + coordinateList.add(newCoordinate, false); + } + return lineString.getFactory().createMultiPointFromCoords(Arrays.stream(coordinateList.toCoordinateArray()).filter(Objects::nonNull).toArray(Coordinate[]::new)); + } + + private Geometry locateAlongMultiLineString(MultiLineString multiLineString, double measure, double offset) { + // iterating through LineStrings in MultiLineString object + List<Point> points = new ArrayList<>(); + for (int i = 0; i < multiLineString.getNumGeometries(); i++) { + MultiPoint mPoint = (MultiPoint) locateAlongLineString((LineString) multiLineString.getGeometryN(i), measure, offset); + for (int j = 0; j < mPoint.getNumGeometries(); j++) { + points.add((Point) mPoint.getGeometryN(j)); + } + } + + return multiLineString.getFactory().createMultiPoint(points.toArray(new Point[0])); + } +}
