From 2af48a36180eea85d155775d3dcdbbe8765a4d2e Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Wed, 7 Feb 2024 23:22:24 -0500 Subject: [PATCH 01/54] Init: ST_Buffer Spheroidal variant --- .../org/apache/sedona/common/Functions.java | 48 +++++++++++++------ .../apache/sedona/common/utils/CRSUtils.java | 46 ++++++++++++++++++ .../apache/sedona/common/FunctionsTest.java | 18 +++---- 3 files changed, 90 insertions(+), 22 deletions(-) create mode 100644 common/src/main/java/org/apache/sedona/common/utils/CRSUtils.java 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 08293bb04a..0bbd536260 100644 --- a/common/src/main/java/org/apache/sedona/common/Functions.java +++ b/common/src/main/java/org/apache/sedona/common/Functions.java @@ -24,20 +24,10 @@ import org.apache.sedona.common.utils.GeometrySplitter; import org.apache.sedona.common.utils.H3Utils; import org.apache.sedona.common.utils.S2Utils; +import org.apache.sedona.common.utils.CRSUtils; import org.locationtech.jts.algorithm.MinimumBoundingCircle; import org.locationtech.jts.algorithm.hull.ConcaveHull; -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.LineString; -import org.locationtech.jts.geom.LinearRing; -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.PrecisionModel; +import org.locationtech.jts.geom.*; import org.locationtech.jts.geom.impl.CoordinateArraySequence; import org.locationtech.jts.geom.util.GeometryFixer; import org.locationtech.jts.io.gml2.GMLWriter; @@ -53,6 +43,8 @@ import org.locationtech.jts.operation.valid.TopologyValidationError; import org.locationtech.jts.precision.GeometryPrecisionReducer; import org.locationtech.jts.simplify.TopologyPreservingSimplifier; +import org.opengis.referencing.FactoryException; +import org.opengis.referencing.operation.TransformException; import org.wololo.jts2geojson.GeoJSONWriter; import java.util.ArrayList; import java.util.Arrays; @@ -96,8 +88,12 @@ public static Geometry boundary(Geometry geometry) { return boundary; } - public static Geometry buffer(Geometry geometry, double radius) { - return buffer(geometry, radius, ""); + public static Geometry buffer(Geometry geometry, double radius) throws FactoryException, TransformException { + return buffer(geometry, radius, "", false); + } + + public static Geometry buffer(Geometry geometry, double radius, boolean spheroidal) throws FactoryException, TransformException { + return buffer(geometry, radius, "", spheroidal); } public static Geometry buffer(Geometry geometry, double radius, String params) { @@ -118,6 +114,30 @@ public static Geometry buffer(Geometry geometry, double radius, String params) { return BufferOp.bufferOp(geometry, radius, bufferParameters); } + public static Geometry buffer(Geometry geometry, double radius, String params, boolean spheroidal) throws FactoryException, TransformException { + if (spheroidal) { + // Determine the best SRID for spheroidal calculations + + Integer bestCRS = CRSUtils.bestSRID(geometry); + Integer originalCRS = geometry.getSRID(); + Integer WGS84CRS = 4326; + + // Transform the geometry to the selected SRID + Geometry transformedGeometry = FunctionsGeoTools.transform(geometry, "EPSG:"+originalCRS, "EPSG:"+bestCRS); + geometry.setSRID(bestCRS); + System.out.println("Best SRID: " + bestCRS); + + // Apply the buffer operation in the selected SRID + Geometry bufferedGeometry = buffer(geometry, radius, params); // Planar buffer in the selected SRID + + // Transform to WGS 84 -- WGS84 - World Geodetic System 1984, + return FunctionsGeoTools.transform(bufferedGeometry, "EPSG:"+bestCRS, "EPSG:"+WGS84CRS); + } else { + // Existing planar buffer logic + return buffer(geometry, radius, params); + } + } + private static BufferParameters parseBufferParams(String params) { String[] listBufferParameters = {"quad_segs", "endcap", "join", "mitre_limit", "miter_limit", "side"}; diff --git a/common/src/main/java/org/apache/sedona/common/utils/CRSUtils.java b/common/src/main/java/org/apache/sedona/common/utils/CRSUtils.java new file mode 100644 index 0000000000..da660c5d5d --- /dev/null +++ b/common/src/main/java/org/apache/sedona/common/utils/CRSUtils.java @@ -0,0 +1,46 @@ +package org.apache.sedona.common.utils; + +import org.locationtech.jts.geom.Envelope; +import org.locationtech.jts.geom.Geometry; + +public class CRSUtils { + // Standard EPSG Codes + public static final int EPSG_WORLD_MERCATOR = 3395; // Replacing SRID_WORLD_MERCATOR + public static final int EPSG_NORTH_UTM_START = 32601; // Replacing SRID_NORTH_UTM_START + public static final int EPSG_NORTH_UTM_END = 32660; // Replacing SRID_NORTH_UTM_END + public static final int EPSG_NORTH_LAMBERT = 3574; // Replacing SRID_NORTH_LAMBERT with EPSG:3574 (North Pole LAEA Atlantic) + public static final int EPSG_NORTH_STEREO = 3995; // Replacing SRID_NORTH_STEREO with ESRI:102017 (WGS 1984 North Pole LAEA) + public static final int EPSG_SOUTH_UTM_START = 32701; // Replacing SRID_SOUTH_UTM_START + public static final int EPSG_SOUTH_UTM_END = 32760; // Replacing SRID_SOUTH_UTM_END + public static final int EPSG_SOUTH_LAMBERT = 3409; // Replacing SRID_SOUTH_LAMBERT with EPSG:3409 (South Pole LAEA) + public static final int EPSG_SOUTH_STEREO = 3031; // Replacing SRID_SOUTH_STEREO + + private CRSUtils() {} + + public static int bestSRID(Geometry geometry) { + Envelope envelope = geometry.getEnvelopeInternal(); + if (envelope.isNull()) return EPSG_WORLD_MERCATOR; // Fallback EPSG + + // Center of the bounding box + double centerX = (envelope.getMinX() + envelope.getMaxX()) / 2.0; + double centerY = (envelope.getMinY() + envelope.getMaxY()) / 2.0; + + // Width and height in degrees + double xwidth = envelope.getWidth(); + double ywidth = envelope.getHeight(); + + // Check for polar regions + if (centerY > 70.0 && ywidth < 45.0) return EPSG_NORTH_LAMBERT; + if (centerY < -70.0 && ywidth < 45.0) return EPSG_SOUTH_LAMBERT; + + // Check for UTM zones + if (xwidth < 6.0) { + int zone = (int)Math.floor((centerX + 180.0) / 6.0); + zone = Math.min(zone, 59); + return (centerY < 0.0) ? EPSG_SOUTH_UTM_START + zone : EPSG_NORTH_UTM_START + zone; + } + + // Default fallback + return EPSG_WORLD_MERCATOR; + } +} diff --git a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java index 64ce4aad33..158321c796 100644 --- a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java +++ b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java @@ -1149,26 +1149,28 @@ public void nRingsMultiPolygonMixed() throws Exception { } @Test - public void testBuffer() { + public void testBuffer() throws FactoryException, TransformException { Polygon polygon = GEOMETRY_FACTORY.createPolygon(coordArray(50, 50, 50, 150, 150, 150, 150, 50, 50, 50)); - String actual = Functions.asWKT(Functions.reducePrecision(Functions.buffer(polygon, 15), 4)); + String actual = Functions.asWKT(Functions.reducePrecision(Functions.buffer(polygon, 15, true), 10)); + System.out.println(actual); String expected = "POLYGON ((47.0736 35.2882, 44.2597 36.1418, 41.6664 37.528, 39.3934 39.3934, 37.528 41.6664, 36.1418 44.2597, 35.2882 47.0736, 35 50, 35 150, 35.2882 152.9264, 36.1418 155.7403, 37.528 158.3336, 39.3934 160.6066, 41.6664 162.472, 44.2597 163.8582, 47.0736 164.7118, 50 165, 150 165, 152.9264 164.7118, 155.7403 163.8582, 158.3336 162.472, 160.6066 160.6066, 162.472 158.3336, 163.8582 155.7403, 164.7118 152.9264, 165 150, 165 50, 164.7118 47.0736, 163.8582 44.2597, 162.472 41.6664, 160.6066 39.3934, 158.3336 37.528, 155.7403 36.1418, 152.9264 35.2882, 150 35, 50 35, 47.0736 35.2882))"; - assertEquals(expected, actual); + System.out.println(expected); +// assertEquals(expected, actual); LineString lineString = GEOMETRY_FACTORY.createLineString(coordArray(0, 0, 50, 70, 100, 100)); actual = Functions.asWKT(Functions.reducePrecision(Functions.buffer(lineString, 10, "side=left"), 4)); expected = "POLYGON ((50 70, 0 0, -8.1373 5.8124, 41.8627 75.8124, 43.2167 77.3476, 44.855 78.5749, 94.855 108.5749, 100 100, 50 70))"; - assertEquals(expected, actual); +// assertEquals(expected, actual); lineString = GEOMETRY_FACTORY.createLineString(coordArray(0, 0, 50, 70, 70, -3)); actual = Functions.asWKT(Functions.reducePrecision(Functions.buffer(lineString, 10, "endcap=square"), 4)); expected = "POLYGON ((43.2156 77.3465, 44.8523 78.5733, 46.7044 79.4413, 48.6944 79.9144, 50.739 79.9727, 52.7527 79.6137, 54.6512 78.8525, 56.3552 77.7209, 57.7932 76.2663, 58.9052 74.5495, 59.6446 72.6424, 79.6446 -0.3576, 82.2869 -10.0022, 62.9978 -15.2869, 45.9128 47.0733, 8.1373 -5.8124, 2.325 -13.9497, -13.9497 -2.325, 41.8627 75.8124, 43.2156 77.3465))"; - assertEquals(expected, actual); +// assertEquals(expected, actual); Point point = GEOMETRY_FACTORY.createPoint(new Coordinate(100, 90)); actual = Functions.asWKT(Functions.reducePrecision(Functions.buffer(point, 10, "quad_segs=2"), 4)); expected = "POLYGON ((107.0711 82.9289, 100 80, 92.9289 82.9289, 90 90, 92.9289 97.0711, 100 100, 107.0711 97.0711, 110 90, 107.0711 82.9289))"; - assertEquals(expected, actual); +// assertEquals(expected, actual); } @Test @@ -1662,7 +1664,7 @@ public void geometryTypeWithMeasuredCollection() { } @Test - public void closestPoint() { + public void closestPoint() throws FactoryException, TransformException { Point point1 = GEOMETRY_FACTORY.createPoint(new Coordinate(1, 1)); LineString lineString1 = GEOMETRY_FACTORY.createLineString(coordArray(1, 0, 1, 1, 2, 1, 2, 0, 1, 0)); String expected1 = "POINT (1 1)"; @@ -1843,7 +1845,7 @@ public void transform() } @Test - public void voronoiPolygons() { + public void voronoiPolygons() throws FactoryException, TransformException { MultiPoint multiPoint = GEOMETRY_FACTORY.createMultiPointFromCoords(coordArray(0, 0, 2, 2)); Geometry actual1 = FunctionsGeoTools.voronoiPolygons(multiPoint, 0, null); assertEquals("GEOMETRYCOLLECTION (POLYGON ((-2 -2, -2 4, 4 -2, -2 -2)), POLYGON ((-2 4, 4 4, 4 -2, -2 4)))", From cbfd51df6889bd0a43f17529a43ed9c66fa69d0f Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Fri, 9 Feb 2024 16:58:03 -0500 Subject: [PATCH 02/54] Add tests --- .../org/apache/sedona/common/Functions.java | 20 ++++---- .../apache/sedona/common/FunctionsTest.java | 51 +++++++++++++++++++ 2 files changed, 62 insertions(+), 9 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 0bbd536260..7611cda1c7 100644 --- a/common/src/main/java/org/apache/sedona/common/Functions.java +++ b/common/src/main/java/org/apache/sedona/common/Functions.java @@ -117,21 +117,23 @@ public static Geometry buffer(Geometry geometry, double radius, String params) { public static Geometry buffer(Geometry geometry, double radius, String params, boolean spheroidal) throws FactoryException, TransformException { if (spheroidal) { // Determine the best SRID for spheroidal calculations + int bestCRS = CRSUtils.bestSRID(geometry); + int originalCRS = geometry.getSRID(); + final int WGS84CRS = 4326; - Integer bestCRS = CRSUtils.bestSRID(geometry); - Integer originalCRS = geometry.getSRID(); - Integer WGS84CRS = 4326; + // If originalCRS is not set, use bestCRS as the originalCRS for transformation + String sourceCRSCode = (originalCRS == 0) ? "EPSG:" + bestCRS : "EPSG:" + originalCRS; + String targetCRSCode = "EPSG:" + bestCRS; // Transform the geometry to the selected SRID - Geometry transformedGeometry = FunctionsGeoTools.transform(geometry, "EPSG:"+originalCRS, "EPSG:"+bestCRS); - geometry.setSRID(bestCRS); - System.out.println("Best SRID: " + bestCRS); + Geometry transformedGeometry = FunctionsGeoTools.transform(geometry, sourceCRSCode, targetCRSCode); // Apply the buffer operation in the selected SRID - Geometry bufferedGeometry = buffer(geometry, radius, params); // Planar buffer in the selected SRID + Geometry bufferedGeometry = buffer(transformedGeometry, radius, params); - // Transform to WGS 84 -- WGS84 - World Geodetic System 1984, - return FunctionsGeoTools.transform(bufferedGeometry, "EPSG:"+bestCRS, "EPSG:"+WGS84CRS); + // Transform back to the original SRID or to WGS 84 if original SRID was not set + String backTransformCRSCode = (originalCRS == 0) ? "EPSG:" + WGS84CRS : "EPSG:" + originalCRS; + return FunctionsGeoTools.transform(bufferedGeometry, targetCRSCode, backTransformCRSCode); } else { // Existing planar buffer logic return buffer(geometry, radius, params); diff --git a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java index 158321c796..36a0d1cc9e 100644 --- a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java +++ b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java @@ -14,6 +14,7 @@ package org.apache.sedona.common; import com.google.common.geometry.S2CellId; +import org.apache.sedona.common.utils.CRSUtils; import org.apache.sedona.common.utils.H3Utils; import com.google.common.math.DoubleMath; import org.apache.sedona.common.sphere.Haversine; @@ -1173,6 +1174,56 @@ public void testBuffer() throws FactoryException, TransformException { // assertEquals(expected, actual); } + @Test + public void testBestSRID() { + // Replace PostGIS custom codes with standard EPSG codes + int[][] testCases = { + {0, -70, 32761}, // Replacing 999161 (South Pole LAEA) with EPSG:32761 (Antarctic Polar Stereographic) + {0, 70, 3575}, // Replacing 999061 (North Pole LAEA Alaska) with EPSG:3575 (North Pole LAEA Alaska) + // UTM zones for Northern Hemisphere + {-177, 60, 32601}, {-171, 60, 32602}, {-165, 60, 32603}, {-159, 60, 32604}, {-153, 60, 32605}, + {-147, 60, 32606}, {-141, 60, 32607}, {-135, 60, 32608}, {-129, 60, 32609}, {-123, 60, 32610}, + {-117, 60, 32611}, {-111, 60, 32612}, {-105, 60, 32613}, {-99, 60, 32614}, {-93, 60, 32615}, + {-87, 60, 32616}, {-81, 60, 32617}, {-75, 60, 32618}, {-69, 60, 32619}, {-63, 60, 32620}, + {-57, 60, 32621}, {-51, 60, 32622}, {-45, 60, 32623}, {-39, 60, 32624}, {-33, 60, 32625}, + {-27, 60, 32626}, {-21, 60, 32627}, {-15, 60, 32628}, {-9, 60, 32629}, {-3, 60, 32630}, + {3, 60, 32631}, {9, 60, 32632}, {15, 60, 32633}, {21, 60, 32634}, {27, 60, 32635}, + {33, 60, 32636}, {39, 60, 32637}, {45, 60, 32638}, {51, 60, 32639}, {57, 60, 32640}, + {63, 60, 32641}, {69, 60, 32642}, {75, 60, 32643}, {81, 60, 32644}, {87, 60, 32645}, + {93, 60, 32646}, {99, 60, 32647}, {105, 60, 32648}, {111, 60, 32649}, {117, 60, 32650}, + {123, 60, 32651}, {129, 60, 32652}, {135, 60, 32653}, {141, 60, 32654}, {147, 60, 32655}, + {153, 60, 32656}, {159, 60, 32657}, {165, 60, 32658}, {171, 60, 32659}, {177, 60, 32660}, + // UTM zones for Southern Hemisphere + {-177, -60, 32701}, {-171, -60, 32702}, {-165, -60, 32703}, {-159, -60, 32704}, {-153, -60, 32705}, + {-147, -60, 32706}, {-141, -60, 32707}, {-135, -60, 32708}, {-129, -60, 32709}, {-123, -60, 32710}, + {-117, -60, 32711}, {-111, -60, 32712}, {-105, -60, 32713}, {-99, -60, 32714}, {-93, -60, 32715}, + {-87, -60, 32716}, {-81, -60, 32717}, {-75, -60, 32718}, {-69, -60, 32719}, {-63, -60, 32720}, + {-57, -60, 32721}, {-51, -60, 32722}, {-45, -60, 32723}, {-39, -60, 32724}, {-33, -60, 32725}, + {-27, -60, 32726}, {-21, -60, 32727}, {-15, -60, 32728}, {-9, -60, 32729}, {-3, -60, 32730}, + {3, -60, 32731}, {9, -60, 32732}, {15, -60, 32733}, {21, -60, 32734}, {27, -60, 32735}, {33, -60, 32736}, + {39, -60, 32737}, {45, -60, 32738}, {51, -60, 32739}, {57, -60, 32740}, {63, -60, 32741}, {69, -60, 32742}, + {75, -60, 32743}, {81, -60, 32744}, {87, -60, 32745}, {93, -60, 32746}, {99, -60, 32747}, {105, -60, 32748}, + {111, -60, 32749}, {117, -60, 32750}, {123, -60, 32751}, {129, -60, 32752}, {135, -60, 32753}, {141, -60, 32754}, + {147, -60, 32755}, {153, -60, 32756}, {159, -60, 32757}, {165, -60, 32758}, {171, -60, 32759}, {177, -60, 32760}, + // Special cases + {-180, 60, 32660}, {180, 60, 32660}, {-180, -60, 32760}, {180, -60, 32760}, + // World Mercator +// {"world", 3395} +}; + + + for (int[] testCase : testCases) { + int x = testCase[0]; + int y = testCase[1]; + int expectedEPSG = testCase[2]; + + Geometry geom = GEOMETRY_FACTORY.createPoint(new Coordinate(x, y)); + int actualEPSG = CRSUtils.bestSRID(geom); + + assertEquals("Failed at coordinates (" + x + ", " + y + ")", expectedEPSG, actualEPSG); + } + } + @Test public void nRingsUnsupported() { LineString lineString = GEOMETRY_FACTORY.createLineString(coordArray3d(0, 1, 1, 1, 2, 1, 1, 2, 2)); From d01819ee7151c11a38bf873a967684101b7e0c9f Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Tue, 20 Feb 2024 10:05:40 -0500 Subject: [PATCH 03/54] make BestSRID ST function; Add tests --- .../org/apache/sedona/common/Functions.java | 55 +++++- .../apache/sedona/common/sphere/Spheroid.java | 42 +++++ .../apache/sedona/common/utils/CRSUtils.java | 46 ----- .../apache/sedona/common/FunctionsTest.java | 173 +++++++++++++----- .../sedona/flink/expressions/Functions.java | 4 +- 5 files changed, 223 insertions(+), 97 deletions(-) delete mode 100644 common/src/main/java/org/apache/sedona/common/utils/CRSUtils.java 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 7611cda1c7..d73ca39064 100644 --- a/common/src/main/java/org/apache/sedona/common/Functions.java +++ b/common/src/main/java/org/apache/sedona/common/Functions.java @@ -18,13 +18,13 @@ 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.GeometrySubDivider; import org.apache.sedona.common.utils.GeomUtils; import org.apache.sedona.common.utils.GeometryGeoHashEncoder; import org.apache.sedona.common.utils.GeometrySplitter; import org.apache.sedona.common.utils.H3Utils; import org.apache.sedona.common.utils.S2Utils; -import org.apache.sedona.common.utils.CRSUtils; import org.locationtech.jts.algorithm.MinimumBoundingCircle; import org.locationtech.jts.algorithm.hull.ConcaveHull; import org.locationtech.jts.geom.*; @@ -114,15 +114,16 @@ public static Geometry buffer(Geometry geometry, double radius, String params) { return BufferOp.bufferOp(geometry, radius, bufferParameters); } - public static Geometry buffer(Geometry geometry, double radius, String params, boolean spheroidal) throws FactoryException, TransformException { - if (spheroidal) { + public static Geometry buffer(Geometry geometry, double radius, String params, boolean useSpheroidal) throws FactoryException, TransformException { + if (useSpheroidal) { // Determine the best SRID for spheroidal calculations - int bestCRS = CRSUtils.bestSRID(geometry); + int bestCRS = bestSRID(geometry); + System.out.println("bestCRS: "+bestCRS); int originalCRS = geometry.getSRID(); final int WGS84CRS = 4326; - // If originalCRS is not set, use bestCRS as the originalCRS for transformation - String sourceCRSCode = (originalCRS == 0) ? "EPSG:" + bestCRS : "EPSG:" + originalCRS; + // If originalCRS is not set, use WGS84 as the originalCRS for transformation + String sourceCRSCode = (originalCRS == 0) ? "EPSG:" + WGS84CRS : "EPSG:" + originalCRS; String targetCRSCode = "EPSG:" + bestCRS; // Transform the geometry to the selected SRID @@ -132,8 +133,10 @@ public static Geometry buffer(Geometry geometry, double radius, String params, b Geometry bufferedGeometry = buffer(transformedGeometry, radius, params); // Transform back to the original SRID or to WGS 84 if original SRID was not set - String backTransformCRSCode = (originalCRS == 0) ? "EPSG:" + WGS84CRS : "EPSG:" + originalCRS; - return FunctionsGeoTools.transform(bufferedGeometry, targetCRSCode, backTransformCRSCode); + int backTransformCRSCode = (originalCRS == 0) ? WGS84CRS : originalCRS; + Geometry bufferedResult = FunctionsGeoTools.transform(bufferedGeometry, targetCRSCode, "EPSG:" + backTransformCRSCode); + bufferedResult.setSRID(backTransformCRSCode); + return bufferedResult; } else { // Existing planar buffer logic return buffer(geometry, radius, params); @@ -218,6 +221,42 @@ else if (singleParam[0].equalsIgnoreCase(listBufferParameters[5])) { return bufferParameters; } + public static int bestSRID(Geometry geometry) { + Envelope envelope = geometry.getEnvelopeInternal(); +// System.out.println("Envelope: " + envelope); // Debug + if (envelope.isNull()) return Spheroid.EPSG_WORLD_MERCATOR; // Fallback EPSG + + // Calculate the center of the envelope +// Coordinate centroid = gboxCentroid(envelope); + double centerX = (envelope.getMinX() + envelope.getMaxX()) / 2.0; // centroid.getX(); + double centerY = (envelope.getMinY() + envelope.getMaxY()) / 2.0; // centroid.getY(); + + // Calculate angular width and height + double xwidth = Spheroid.angularWidth(envelope); // envelope.getWidth(); // Spheroid.distance(west, east); + double ywidth = Spheroid.angularHeight(envelope); // envelope.getHeight(); // Spheroid.distance(south, north); +// System.out.println("Center: (" + centerX + ", " + centerY + ")"); // Debug +// System.out.println("xwidth: " + xwidth + ", ywidth: " + ywidth); // Debug + + // Prioritize polar regions for Lambert Azimuthal Equal Area projection + if (centerY >= 70.0 && ywidth < 45.0) return Spheroid.EPSG_NORTH_LAMBERT; + if (centerY <= -70.0 && ywidth < 45.0) return Spheroid.EPSG_SOUTH_LAMBERT; + + // Check for UTM zones + if (xwidth < 6.0) { + int zone; + if (centerX == -180.0 || centerX == 180.0) { + zone = 59; // UTM zone 60 + } else { + zone = (int)Math.floor((centerX + 180.0) / 6.0); + zone = Math.min(zone, 59); + } + return (centerY < 0.0) ? Spheroid.EPSG_SOUTH_UTM_START + zone : Spheroid.EPSG_NORTH_UTM_START + zone; + } + + // Default fallback to Mercator projection + return Spheroid.EPSG_WORLD_MERCATOR; + } + public static Geometry envelope(Geometry geometry) { return geometry.getEnvelope(); } diff --git a/common/src/main/java/org/apache/sedona/common/sphere/Spheroid.java b/common/src/main/java/org/apache/sedona/common/sphere/Spheroid.java index ef64df55da..7b06f00d73 100644 --- a/common/src/main/java/org/apache/sedona/common/sphere/Spheroid.java +++ b/common/src/main/java/org/apache/sedona/common/sphere/Spheroid.java @@ -23,12 +23,24 @@ import net.sf.geographiclib.PolygonArea; import net.sf.geographiclib.PolygonResult; import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.Geometry; import static java.lang.Math.abs; public class Spheroid { + // Standard EPSG Codes + public static final int EPSG_WORLD_MERCATOR = 3395; + public static final int EPSG_NORTH_UTM_START = 32601; + public static final int EPSG_NORTH_UTM_END = 32660; + public static final int EPSG_NORTH_LAMBERT = 3574; + public static final int EPSG_NORTH_STEREO = 3995; + public static final int EPSG_SOUTH_UTM_START = 32701; + public static final int EPSG_SOUTH_UTM_END = 32760; + public static final int EPSG_SOUTH_LAMBERT = 3409; + public static final int EPSG_SOUTH_STEREO = 3031; + /** * Calculate the distance between two points on the earth using the Spheroid formula. * This algorithm does not use the radius of the earth, but instead uses the WGS84 ellipsoid. @@ -118,4 +130,34 @@ else if (geomType.equals("MultiPolygon") || geomType.equals("GeometryCollection" return 0.0; } } + + public static double angularWidth(Envelope envelope) { + double lon1 = envelope.getMinX(); + double lon2 = envelope.getMaxX(); + double lat = (envelope.getMinY() + envelope.getMaxY()) / 2; // Mid-latitude for width calculation + + // Compute geodesic distance + GeodesicData g = Geodesic.WGS84.Inverse(lat, lon1, lat, lon2); + double distance = g.s12; // Distance in meters + + // Convert distance to angular width in degrees + double angularWidth = Math.toDegrees(distance / (Geodesic.WGS84.EquatorialRadius() * Math.PI / 180)); + + return angularWidth; + } + + public static double angularHeight(Envelope envelope) { + double lat1 = envelope.getMinY(); + double lat2 = envelope.getMaxY(); + double lon = (envelope.getMinX() + envelope.getMaxX()) / 2; // Mid-longitude for height calculation + + // Compute geodesic distance + GeodesicData g = Geodesic.WGS84.Inverse(lat1, lon, lat2, lon); + double distance = g.s12; // Distance in meters + + // Convert distance to angular height in degrees + double angularHeight = Math.toDegrees(distance / (Geodesic.WGS84.EquatorialRadius() * Math.PI / 180)); + + return angularHeight; + } } diff --git a/common/src/main/java/org/apache/sedona/common/utils/CRSUtils.java b/common/src/main/java/org/apache/sedona/common/utils/CRSUtils.java deleted file mode 100644 index da660c5d5d..0000000000 --- a/common/src/main/java/org/apache/sedona/common/utils/CRSUtils.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.apache.sedona.common.utils; - -import org.locationtech.jts.geom.Envelope; -import org.locationtech.jts.geom.Geometry; - -public class CRSUtils { - // Standard EPSG Codes - public static final int EPSG_WORLD_MERCATOR = 3395; // Replacing SRID_WORLD_MERCATOR - public static final int EPSG_NORTH_UTM_START = 32601; // Replacing SRID_NORTH_UTM_START - public static final int EPSG_NORTH_UTM_END = 32660; // Replacing SRID_NORTH_UTM_END - public static final int EPSG_NORTH_LAMBERT = 3574; // Replacing SRID_NORTH_LAMBERT with EPSG:3574 (North Pole LAEA Atlantic) - public static final int EPSG_NORTH_STEREO = 3995; // Replacing SRID_NORTH_STEREO with ESRI:102017 (WGS 1984 North Pole LAEA) - public static final int EPSG_SOUTH_UTM_START = 32701; // Replacing SRID_SOUTH_UTM_START - public static final int EPSG_SOUTH_UTM_END = 32760; // Replacing SRID_SOUTH_UTM_END - public static final int EPSG_SOUTH_LAMBERT = 3409; // Replacing SRID_SOUTH_LAMBERT with EPSG:3409 (South Pole LAEA) - public static final int EPSG_SOUTH_STEREO = 3031; // Replacing SRID_SOUTH_STEREO - - private CRSUtils() {} - - public static int bestSRID(Geometry geometry) { - Envelope envelope = geometry.getEnvelopeInternal(); - if (envelope.isNull()) return EPSG_WORLD_MERCATOR; // Fallback EPSG - - // Center of the bounding box - double centerX = (envelope.getMinX() + envelope.getMaxX()) / 2.0; - double centerY = (envelope.getMinY() + envelope.getMaxY()) / 2.0; - - // Width and height in degrees - double xwidth = envelope.getWidth(); - double ywidth = envelope.getHeight(); - - // Check for polar regions - if (centerY > 70.0 && ywidth < 45.0) return EPSG_NORTH_LAMBERT; - if (centerY < -70.0 && ywidth < 45.0) return EPSG_SOUTH_LAMBERT; - - // Check for UTM zones - if (xwidth < 6.0) { - int zone = (int)Math.floor((centerX + 180.0) / 6.0); - zone = Math.min(zone, 59); - return (centerY < 0.0) ? EPSG_SOUTH_UTM_START + zone : EPSG_NORTH_UTM_START + zone; - } - - // Default fallback - return EPSG_WORLD_MERCATOR; - } -} diff --git a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java index 36a0d1cc9e..5ff7dd66d9 100644 --- a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java +++ b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java @@ -14,7 +14,6 @@ package org.apache.sedona.common; import com.google.common.geometry.S2CellId; -import org.apache.sedona.common.utils.CRSUtils; import org.apache.sedona.common.utils.H3Utils; import com.google.common.math.DoubleMath; import org.apache.sedona.common.sphere.Haversine; @@ -25,6 +24,7 @@ import org.geotools.referencing.operation.projection.ProjectionException; import org.junit.Test; import org.locationtech.jts.geom.*; +import org.locationtech.jts.io.ParseException; import org.locationtech.jts.io.WKTReader; import org.locationtech.jts.io.WKTWriter; import org.opengis.referencing.FactoryException; @@ -39,6 +39,7 @@ public class FunctionsTest extends TestBase { public static final GeometryFactory GEOMETRY_FACTORY = new GeometryFactory(); protected static final double FP_TOLERANCE = 1e-12; + protected static final double FP_TOLERANCE2 = 1e-4; protected static final CoordinateSequenceComparator COORDINATE_SEQUENCE_COMPARATOR = new CoordinateSequenceComparator(2){ @Override protected int compareCoordinate(CoordinateSequence s1, CoordinateSequence s2, int i, int dimension) { @@ -1152,64 +1153,139 @@ public void nRingsMultiPolygonMixed() throws Exception { @Test public void testBuffer() throws FactoryException, TransformException { Polygon polygon = GEOMETRY_FACTORY.createPolygon(coordArray(50, 50, 50, 150, 150, 150, 150, 50, 50, 50)); - String actual = Functions.asWKT(Functions.reducePrecision(Functions.buffer(polygon, 15, true), 10)); - System.out.println(actual); + String actual = Functions.asWKT(Functions.reducePrecision(Functions.buffer(polygon, 15), 4)); String expected = "POLYGON ((47.0736 35.2882, 44.2597 36.1418, 41.6664 37.528, 39.3934 39.3934, 37.528 41.6664, 36.1418 44.2597, 35.2882 47.0736, 35 50, 35 150, 35.2882 152.9264, 36.1418 155.7403, 37.528 158.3336, 39.3934 160.6066, 41.6664 162.472, 44.2597 163.8582, 47.0736 164.7118, 50 165, 150 165, 152.9264 164.7118, 155.7403 163.8582, 158.3336 162.472, 160.6066 160.6066, 162.472 158.3336, 163.8582 155.7403, 164.7118 152.9264, 165 150, 165 50, 164.7118 47.0736, 163.8582 44.2597, 162.472 41.6664, 160.6066 39.3934, 158.3336 37.528, 155.7403 36.1418, 152.9264 35.2882, 150 35, 50 35, 47.0736 35.2882))"; - System.out.println(expected); -// assertEquals(expected, actual); + assertEquals(expected, actual); LineString lineString = GEOMETRY_FACTORY.createLineString(coordArray(0, 0, 50, 70, 100, 100)); actual = Functions.asWKT(Functions.reducePrecision(Functions.buffer(lineString, 10, "side=left"), 4)); expected = "POLYGON ((50 70, 0 0, -8.1373 5.8124, 41.8627 75.8124, 43.2167 77.3476, 44.855 78.5749, 94.855 108.5749, 100 100, 50 70))"; -// assertEquals(expected, actual); + assertEquals(expected, actual); lineString = GEOMETRY_FACTORY.createLineString(coordArray(0, 0, 50, 70, 70, -3)); actual = Functions.asWKT(Functions.reducePrecision(Functions.buffer(lineString, 10, "endcap=square"), 4)); expected = "POLYGON ((43.2156 77.3465, 44.8523 78.5733, 46.7044 79.4413, 48.6944 79.9144, 50.739 79.9727, 52.7527 79.6137, 54.6512 78.8525, 56.3552 77.7209, 57.7932 76.2663, 58.9052 74.5495, 59.6446 72.6424, 79.6446 -0.3576, 82.2869 -10.0022, 62.9978 -15.2869, 45.9128 47.0733, 8.1373 -5.8124, 2.325 -13.9497, -13.9497 -2.325, 41.8627 75.8124, 43.2156 77.3465))"; -// assertEquals(expected, actual); + assertEquals(expected, actual); Point point = GEOMETRY_FACTORY.createPoint(new Coordinate(100, 90)); actual = Functions.asWKT(Functions.reducePrecision(Functions.buffer(point, 10, "quad_segs=2"), 4)); expected = "POLYGON ((107.0711 82.9289, 100 80, 92.9289 82.9289, 90 90, 92.9289 97.0711, 100 100, 107.0711 97.0711, 110 90, 107.0711 82.9289))"; -// assertEquals(expected, actual); + assertEquals(expected, actual); + } + + @Test + public void testBufferSpheroidal() throws FactoryException, TransformException, ParseException { + Geometry polygon1 = GEOMETRY_FACTORY.createPolygon(coordArray(16.2500, 48.2500, 16.3500, 48.2500, 16.3500, 48.2000, 16.2500, 48.2000, 16.2500, 48.2500)); + Geometry polygon2 = Constructors.geomFromWKT("POLYGON((-120 30, -80 30, -80 50, -120 50, -120 30))", 4269); + Geometry point1 = Constructors.geomFromWKT("POINT(-180 60)", 4269); + Geometry linestring1 = Constructors.geomFromEWKT("LINESTRING(-91.185 30.4505, -91.187 30.452, -91.189 30.4535)"); + Geometry polygon3 = Constructors.geomFromWKT("POLYGON((-120 30, -80 30, -80 50, -120 50, -120 30))", 4269); + + + Geometry result1 = Functions.buffer(polygon1, 100, true); + Geometry result2 = Functions.buffer(polygon2, 1000, true); + Geometry result3 = Functions.buffer(point1, 100, true); + Geometry result4 = Functions.buffer(linestring1, 10, true); + Geometry result5 = Functions.buffer(polygon3, 10000, true); + + + Geometry expected1 = Constructors.geomFromEWKT("POLYGON ((16.248653057837092 48.249999997812616, 16.24867891451385 48.25017542568974, 16.24875549793627 48.25034411793438, 16.24887986793391 48.25049959710695, 16.249047249310912 48.25063589306857, 16.249251215157546 48.250747772240985, 16.249483933612066 48.25083093858835, 16.24973646860016 48.250882198599385, 16.249999123001867 48.25089958393145, 16.35000087730807 48.25089956809054, 16.35026352923699 48.25088218269226, 16.350516061632444 48.250830922668115, 16.350748777319566 48.25074775640202, 16.350952740192536 48.250635877460866, 16.351120118386593 48.25049958194538, 16.35124448502055 48.25034410350242, 16.35132106495956 48.25017541233756, 16.351346918125614 48.24999998594946, 16.351345606585333 48.199999998284284, 16.351319726848164 48.19982442741667, 16.351243088946557 48.199655609589996, 16.351118640921797 48.19950003769327, 16.350951169518318 48.19936369511042, 16.35074711603489 48.19925182561273, 16.350514328568075 48.199168731701455, 16.350261760179283 48.19911760915295, 16.34999912459177 48.19910042412577, 16.250000875738642 48.19910040825404, 16.24973823767017 48.1991175932147, 16.249485666681004 48.19916871575088, 16.249252876439694 48.19925180974528, 16.24904881997628 48.19936367947716, 16.248881345384483 48.19950002251011, 16.248756893991796 48.19965559514148, 16.24868025260387 48.19982441405396, 16.2486543693546 48.199999986417545, 16.248653057837092 48.249999997812616))"); + Geometry expected2 = Constructors.geomFromEWKT("POLYGON ((-120.00898315284118 29.999999999999467, -120.00898315284118 49.999999999988724, -120.00881054407824 50.001129625613444, -120.0082993510474 50.002215815187945, -120.00746921861013 50.003216831381316, -120.00635204829044 50.00409421217581, -120.0049907723172 50.004814247955025, -120.00343770376273 50.00534927578318, -120.00175252618048 50.00567874131385, -119.99999999999999 50.005789987696524, -80 50.005789987696524, -79.9982474738195 50.00567874131385, -79.99656229623727 50.00534927578318, -79.99500922768277 50.004814247955025, -79.99364795170956 50.00409421217581, -79.99253078138989 50.003216831381316, -79.99170064895262 50.002215815187945, -79.99118945592176 50.001129625613444, -79.99101684715882 49.999999999988724, -79.99101684715882 29.999999999999467, -79.99118945592176 29.998474584401656, -79.99170064895262 29.997007767335376, -79.99253078138989 29.995655921596196, -79.99364795170956 29.994471003601635, -79.99500922768277 29.99349855586065, -79.99656229623727 29.99277595576926, -79.9982474738195 29.99233097818683, -80 29.992180727189663, -119.99999999999999 29.992180727189663, -120.00175252618048 29.99233097818683, -120.00343770376273 29.99277595576926, -120.0049907723172 29.99349855586065, -120.00635204829044 29.994471003601635, -120.00746921861013 29.995655921596196, -120.0082993510474 29.997007767335376, -120.00881054407824 29.998474584401656, -120.00898315284118 29.999999999999467))"); + Geometry expected3 = Constructors.geomFromEWKT("POLYGON ((-179.9982096288439 59.99995928994206, -179.9982598922349 59.99978513613418, -179.99837702566958 59.99961923978765, -179.99855652694714 59.99946797603053, -179.998791497433 59.99933715760413, -179.99907290724568 59.999231811518854, -179.9993899422849 59.99915598591007, -179.99973041976276 59.99911259450936, 179.9999187437241 59.999103304702594, 179.99957102955173 59.99912847347155, 179.99923979915093 59.999187133678575, 179.99893778069153 59.999277031220146, 179.99867658007233 59.99939471162435, 179.99846623498902 59.99953565276752, 179.9983148292063 59.99969443861621, 179.99822818185396 59.999864967323326, 179.9982096236965 60.00004068568767, 179.99825986898801 60.00021484097138, 179.99837698785996 60.00038074040053, 179.99855648032874 60.00053200837772, 179.99879144910062 60.00066283151926, 179.99907286455522 60.00076818209686, 179.9993899117333 60.00084401129125, 179.9997304059989 60.000887404824965, -179.99991873860708 60.00089669498742, -179.99957100633523 60.000871524742664, -179.9992397613717 60.000812861453035, -179.9989377341035 60.000722959691345, -179.99867653177037 60.00060527457202, -179.99846619232903 60.00046432893652, -179.99831479868513 60.000305539502236, -179.99822816812048 60.00013500866213, -179.9982096288439 59.99995928994206))"); + Geometry expected4 = Constructors.geomFromEWKT("POLYGON ((-91.18706814560424 30.451931798138848, -91.18906814731041 30.453431798551634, -91.18908219577726 30.453444627150773, -91.1890930855116 30.4534595836974, -91.18910039802604 30.453476093420573, -91.18910385230312 30.453493521861454, -91.18910331559482 30.453511199255043, -91.1890988085245 30.4535284462689, -91.18909050429437 30.453544600109474, -91.18907872203003 30.453559039992996, -91.18906391451668 30.453571211001968, -91.18904665079899 30.453580645410415, -91.18902759431288 30.453586980658557, -91.18900747739012 30.453589973285787, -91.18898707311482 30.453589508286903, -91.18896716561396 30.453585603531685, -91.18894851992349 30.453578409078172, -91.18893185258821 30.453568201405897, -91.18693185327697 30.45206820105564, -91.18493185405761 30.450568200774196, -91.18491780618592 30.450555372061554, -91.18490691698157 30.450540415431583, -91.18489960490953 30.450523905659963, -91.18489615096698 30.450506477208492, -91.18489668788492 30.450488799843015, -91.18490119502782 30.450471552894637, -91.18490949918679 30.450455399153434, -91.18492128123634 30.450440959397913, -91.18493608839827 30.450428788538883, -91.18495335164187 30.45041935429472, -91.1849724075514 30.45041301921735, -91.18499252382057 30.450410026759716, -91.18501292739447 30.450410491920046, -91.18503283417732 30.45041439682263, -91.18505147916453 30.450421591404723, -91.18506814584102 30.450431799183303, -91.18706814560424 30.451931798138848))"); + Geometry expected5 = Constructors.geomFromEWKT("POLYGON ((-120.08983152841193 29.999999999999467, -120.08983152841193 49.999999999988724, -120.08810544078257 50.011295055219406, -120.082993510474 50.02215353087995, -120.07469218610126 50.03215857448533, -120.06352048290445 50.040926345154375, -120.04990772317231 50.048120665845154, -120.03437703762728 50.05346582623596, -120.01752526180509 50.05675706193107, -119.99999999999999 50.057868324959486, -80 50.057868324959486, -79.98247473819491 50.05675706193107, -79.96562296237272 50.05346582623596, -79.95009227682765 50.048120665845154, -79.93647951709555 50.040926345154375, -79.92530781389875 50.03215857448533, -79.91700648952599 50.02215353087995, -79.91189455921744 50.011295055219406, -79.91016847158805 49.999999999988724, -79.91016847158805 29.999999999999467, -79.91189455921744 29.984744778413628, -79.91700648952599 29.970073573592575, -79.92530781389875 29.956550575939833, -79.93647951709555 29.944696041120597, -79.95009227682765 29.934966209460505, -79.96562296237272 29.927735669848445, -79.98247473819491 29.92328286155486, -80 29.92177928675603, -119.99999999999999 29.92177928675603, -120.01752526180509 29.92328286155486, -120.03437703762728 29.927735669848445, -120.04990772317231 29.934966209460505, -120.06352048290445 29.944696041120597, -120.07469218610126 29.956550575939833, -120.082993510474 29.970073573592575, -120.08810544078257 29.984744778413628, -120.08983152841193 29.999999999999467))"); + + Geometry postgis_result1 = Constructors.geomFromEWKT("POLYGON((16.24865305783681 48.249999997812985,16.248678914513565 48.250175425690124,16.248755497935985 48.250344117934766,16.248879867933624 48.25049959710733,16.249047249310628 48.25063589306893,16.249251215157262 48.25074777224136,16.24948393361178 48.25083093858872,16.24973646859988 48.25088219859975,16.249999123001583 48.25089958393185,16.35000087730765 48.25089956809112,16.35026352923657 48.25088218269285,16.350516061632025 48.2508309226687,16.35074877731915 48.2507477564026,16.35095274019212 48.25063587746145,16.351120118386177 48.250499581945974,16.35124448502013 48.250344103503025,16.351321064959144 48.25017541233815,16.3513469181252 48.24999998595004,16.351345606584914 48.19999999828486,16.35131972684775 48.19982442741726,16.351243088946138 48.19965560959058,16.351118640921378 48.19950003769385,16.3509511695179 48.199363695110996,16.350747116034473 48.19925182561332,16.350514328567655 48.19916873170203,16.35026176017886 48.19911760915353,16.34999912459135 48.19910042412636,16.250000875738355 48.19910040825441,16.249738237669884 48.19911759321507,16.249485666680723 48.19916871575125,16.24925287643941 48.199251809745654,16.249048819975993 48.19936367947753,16.2488813453842 48.199500022510485,16.248756893991516 48.19965559514185,16.248680252603585 48.199824414054326,16.248654369354316 48.199999986417914,16.24865305783681 48.249999997812985))"); + Geometry postgis_result2 = Constructors.geomFromEWKT("POLYGON((-120.00898315284118 29.999999999999993,-120.00898315284118 49.99999999999999,-120.00881054407824 50.00112962562472,-120.0082993510474 50.00221581519921,-120.00746921861011 50.003216831392564,-120.00635204829044 50.004094212187034,-120.00499077231723 50.00481424796622,-120.00343770376273 50.00534927579437,-120.00175252618051 50.00567874132504,-119.99999999999999 50.00578998770771,-80 50.00578998770771,-79.99824747381949 50.00567874132504,-79.99656229623727 50.00534927579437,-79.99500922768276 50.00481424796622,-79.99364795170956 50.004094212187034,-79.99253078138987 50.003216831392564,-79.9917006489526 50.00221581519921,-79.99118945592174 50.00112962562472,-79.9910168471588 49.99999999999999,-79.9910168471588 29.999999999999993,-79.99118945592174 29.99847458440221,-79.9917006489526 29.997007767335962,-79.99253078138987 29.99565592159683,-79.99364795170956 29.99447100360229,-79.99500922768276 29.993498555861333,-79.99656229623727 29.992775955769954,-79.99824747381949 29.992330978187542,-80 29.992180727190387,-119.99999999999999 29.992180727190387,-120.00175252618051 29.992330978187542,-120.00343770376273 29.992775955769954,-120.00499077231723 29.993498555861333,-120.00635204829044 29.99447100360229,-120.00746921861011 29.99565592159683,-120.0082993510474 29.997007767335962,-120.00881054407824 29.99847458440221,-120.00898315284118 29.999999999999993))"); + Geometry postgis_result3 = Constructors.geomFromEWKT("POLYGON((-179.99820962883618 59.99995929000264,-179.99825989222717 59.999785136194745,-179.9983770256619 59.99961923984817,-179.99855652693947 59.99946797609105,-179.99879149742534 59.99933715766465,-179.999072907238 59.99923181157932,-179.99938994227725 59.9991559859705,-179.99973041975517 59.999112594569745,179.9999187437317 59.999103304762926,179.99957102955932 59.999128473531854,179.9992397991585 59.99918713373882,179.99893778069907 59.999277031280386,179.99867658007986 59.99939471168456,179.99846623499656 59.99953565282769,179.99831482921377 59.99969443867636,179.99822818186144 59.999864967383445,179.998209623704 60.00004068574784,179.9982598689955 60.00021484103153,179.9983769878675 60.000380740460706,179.99855648033625 60.00053200843791,179.99879144910815 60.000662831579476,179.99907286456278 60.00076818215714,179.99938991174088 60.00084401135155,179.99973040600648 60.00088740488531,-179.9999187385995 60.0008966950478,-179.99957100632759 60.00087152480308,-179.9992397613641 60.0008128615135,-179.99893773409582 60.000722959751855,-179.99867653176275 60.00060527463255,-179.99846619232136 60.00046432899707,-179.99831479867737 60.00030553956281,-179.99822816811277 60.000135008722694,-179.99820962883618 59.99995929000264))"); + Geometry postgis_result4 = Constructors.geomFromEWKT("POLYGON((-91.18706814560713 30.45193179814003,-91.1890681473133 30.453431798552806,-91.18908219578012 30.453444627151963,-91.18909308551447 30.453459583698564,-91.18910039802891 30.453476093421752,-91.18910385230598 30.453493521862622,-91.1891033155977 30.453511199256216,-91.18909880852735 30.453528446270074,-91.18909050429724 30.453544600110646,-91.18907872203292 30.453559039994175,-91.18906391451956 30.453571211003144,-91.18904665080186 30.453580645411602,-91.18902759431575 30.453586980659722,-91.189007477393 30.45358997328696,-91.1889870731177 30.453589508288072,-91.18896716561683 30.45358560353286,-91.18894851992636 30.453578409079352,-91.18893185259108 30.453568201407066,-91.18693185325601 30.45206820103894,-91.1849318540605 30.450568200775386,-91.18491780618884 30.450555372062745,-91.18490691698447 30.450540415432766,-91.18489960491243 30.45052390566114,-91.18489615096986 30.450506477209665,-91.18489668788781 30.450488799844198,-91.1849011950307 30.45047155289582,-91.18490949918969 30.45045539915463,-91.18492128123923 30.450440959399103,-91.18493608840117 30.45042878854006,-91.18495335164478 30.45041935429591,-91.18497240755428 30.450413019218534,-91.18499252382345 30.450410026760903,-91.18501292739734 30.450410491921236,-91.1850328341802 30.450414396823803,-91.18505147916741 30.450421591405906,-91.1850681458439 30.450431799184482,-91.18706814560713 30.45193179814003))"); + Geometry postgis_result5 = Constructors.geomFromEWKT("POLYGON((-120.08983152841193 29.999999999999993,-120.08983152841193 49.99999999999999,-120.08810544078257 50.011295055230505,-120.082993510474 50.02215353089088,-120.07469218610122 50.032158574496115,-120.06352048290445 50.040926345165026,-120.04990772317234 50.04812066585569,-120.03437703762728 50.0534658262464,-120.01752526180509 50.05675706194147,-119.99999999999999 50.05786832496988,-80 50.05786832496988,-79.98247473819491 50.05675706194147,-79.9656229623727 50.0534658262464,-79.95009227682766 50.04812066585569,-79.93647951709556 50.040926345165026,-79.92530781389875 50.032158574496115,-79.91700648952599 50.02215353089088,-79.91189455921743 50.011295055230505,-79.91016847158805 49.99999999999999,-79.91016847158805 29.999999999999993,-79.91189455921743 29.984744778414523,-79.91700648952599 29.970073573593833,-79.92530781389875 29.956550575941442,-79.93647951709556 29.944696041122494,-79.95009227682766 29.934966209462644,-79.9656229623727 29.927735669850765,-79.98247473819491 29.923282861557286,-80 29.921779286758486,-119.99999999999999 29.921779286758486,-120.01752526180509 29.923282861557286,-120.03437703762728 29.927735669850765,-120.04990772317234 29.934966209462644,-120.06352048290445 29.944696041122494,-120.07469218610122 29.956550575941442,-120.082993510474 29.970073573593833,-120.08810544078257 29.984744778414523,-120.08983152841193 29.999999999999993))"); + + System.out.println(result1); + System.out.println(Functions.bestSRID(polygon1)); + System.out.println(result2); + System.out.println(result3); + System.out.println(result4); + System.out.println(result5); + + assertEquals(expected1, result1); + assertEquals(4326, Functions.getSRID(result1)); + assertEquals(7.424558176442617E-8, comparePolygons(postgis_result1, result1), FP_TOLERANCE2); + assertEquals(Spheroid.area(postgis_result1), Spheroid.area(result1), FP_TOLERANCE2); + + assertEquals(expected2, result2); + assertEquals(4269, Functions.getSRID(result2)); + assertEquals(Spheroid.area(postgis_result2), Spheroid.area(result2), 10); + assertEquals(7.424558176442617E-8, comparePolygons(postgis_result2, result2), FP_TOLERANCE2); + + assertEquals(expected3, result3); + assertEquals(4269, Functions.getSRID(result3)); + assertEquals(Spheroid.area(postgis_result3), Spheroid.area(result3), FP_TOLERANCE2); + assertEquals(7.424558176442617E-8, comparePolygons(postgis_result3, result3), FP_TOLERANCE2); + + assertEquals(expected4, result4); + assertEquals(4326, Functions.getSRID(result4)); + assertEquals(Spheroid.area(postgis_result4), Spheroid.area(result4), FP_TOLERANCE2); + assertEquals(7.424558176442617E-8, comparePolygons(postgis_result4, result4), FP_TOLERANCE2); + + assertEquals(expected5, result5); + assertEquals(4269, Functions.getSRID(result5)); + assertEquals(Spheroid.area(postgis_result5), Spheroid.area(result5), 10); + assertEquals(7.424558176442617E-8, comparePolygons(postgis_result5, result5), FP_TOLERANCE2); + + double difference = comparePolygons(result1, postgis_result1); +// System.out.println("Maximum vertex distance between polygons: " + difference); +// System.out.println("Sedona Area: "+Spheroid.area(result1)); +// System.out.println("PostGIS Area: "+Spheroid.area(postgis_result1)); + } + + private static double comparePolygons(Geometry p1, Geometry p2) { + Coordinate[] coords1 = p1.getCoordinates(); + Coordinate[] coords2 = p2.getCoordinates(); + + double maxDistance = 0.0; + for (int i = 0; i < Math.min(coords1.length, coords2.length); i++) { + Geometry point1 = GEOMETRY_FACTORY.createPoint(coords1[i]); + Geometry point2 = GEOMETRY_FACTORY.createPoint(coords2[i]); + double distance = Spheroid.distance(point1,point2); + maxDistance = Math.max(maxDistance, distance); + } + return maxDistance; } @Test public void testBestSRID() { - // Replace PostGIS custom codes with standard EPSG codes int[][] testCases = { - {0, -70, 32761}, // Replacing 999161 (South Pole LAEA) with EPSG:32761 (Antarctic Polar Stereographic) - {0, 70, 3575}, // Replacing 999061 (North Pole LAEA Alaska) with EPSG:3575 (North Pole LAEA Alaska) - // UTM zones for Northern Hemisphere - {-177, 60, 32601}, {-171, 60, 32602}, {-165, 60, 32603}, {-159, 60, 32604}, {-153, 60, 32605}, - {-147, 60, 32606}, {-141, 60, 32607}, {-135, 60, 32608}, {-129, 60, 32609}, {-123, 60, 32610}, - {-117, 60, 32611}, {-111, 60, 32612}, {-105, 60, 32613}, {-99, 60, 32614}, {-93, 60, 32615}, - {-87, 60, 32616}, {-81, 60, 32617}, {-75, 60, 32618}, {-69, 60, 32619}, {-63, 60, 32620}, - {-57, 60, 32621}, {-51, 60, 32622}, {-45, 60, 32623}, {-39, 60, 32624}, {-33, 60, 32625}, - {-27, 60, 32626}, {-21, 60, 32627}, {-15, 60, 32628}, {-9, 60, 32629}, {-3, 60, 32630}, - {3, 60, 32631}, {9, 60, 32632}, {15, 60, 32633}, {21, 60, 32634}, {27, 60, 32635}, - {33, 60, 32636}, {39, 60, 32637}, {45, 60, 32638}, {51, 60, 32639}, {57, 60, 32640}, - {63, 60, 32641}, {69, 60, 32642}, {75, 60, 32643}, {81, 60, 32644}, {87, 60, 32645}, - {93, 60, 32646}, {99, 60, 32647}, {105, 60, 32648}, {111, 60, 32649}, {117, 60, 32650}, - {123, 60, 32651}, {129, 60, 32652}, {135, 60, 32653}, {141, 60, 32654}, {147, 60, 32655}, - {153, 60, 32656}, {159, 60, 32657}, {165, 60, 32658}, {171, 60, 32659}, {177, 60, 32660}, - // UTM zones for Southern Hemisphere - {-177, -60, 32701}, {-171, -60, 32702}, {-165, -60, 32703}, {-159, -60, 32704}, {-153, -60, 32705}, - {-147, -60, 32706}, {-141, -60, 32707}, {-135, -60, 32708}, {-129, -60, 32709}, {-123, -60, 32710}, - {-117, -60, 32711}, {-111, -60, 32712}, {-105, -60, 32713}, {-99, -60, 32714}, {-93, -60, 32715}, - {-87, -60, 32716}, {-81, -60, 32717}, {-75, -60, 32718}, {-69, -60, 32719}, {-63, -60, 32720}, - {-57, -60, 32721}, {-51, -60, 32722}, {-45, -60, 32723}, {-39, -60, 32724}, {-33, -60, 32725}, - {-27, -60, 32726}, {-21, -60, 32727}, {-15, -60, 32728}, {-9, -60, 32729}, {-3, -60, 32730}, - {3, -60, 32731}, {9, -60, 32732}, {15, -60, 32733}, {21, -60, 32734}, {27, -60, 32735}, {33, -60, 32736}, - {39, -60, 32737}, {45, -60, 32738}, {51, -60, 32739}, {57, -60, 32740}, {63, -60, 32741}, {69, -60, 32742}, - {75, -60, 32743}, {81, -60, 32744}, {87, -60, 32745}, {93, -60, 32746}, {99, -60, 32747}, {105, -60, 32748}, - {111, -60, 32749}, {117, -60, 32750}, {123, -60, 32751}, {129, -60, 32752}, {135, -60, 32753}, {141, -60, 32754}, - {147, -60, 32755}, {153, -60, 32756}, {159, -60, 32757}, {165, -60, 32758}, {171, -60, 32759}, {177, -60, 32760}, - // Special cases - {-180, 60, 32660}, {180, 60, 32660}, {-180, -60, 32760}, {180, -60, 32760}, - // World Mercator -// {"world", 3395} -}; + {0, -70, 3409}, // EPSG:3409 (Antarctic Polar Stereographic) + {0, 70, 3574}, // EPSG:3575 (North Pole LAEA Alaska) + // UTM zones for Northern Hemisphere + {-177, 60, 32601}, {-171, 60, 32602}, {-165, 60, 32603}, {-159, 60, 32604}, {-153, 60, 32605}, + {-147, 60, 32606}, {-141, 60, 32607}, {-135, 60, 32608}, {-129, 60, 32609}, {-123, 60, 32610}, + {-117, 60, 32611}, {-111, 60, 32612}, {-105, 60, 32613}, {-99, 60, 32614}, {-93, 60, 32615}, + {-87, 60, 32616}, {-81, 60, 32617}, {-75, 60, 32618}, {-69, 60, 32619}, {-63, 60, 32620}, + {-57, 60, 32621}, {-51, 60, 32622}, {-45, 60, 32623}, {-39, 60, 32624}, {-33, 60, 32625}, + {-27, 60, 32626}, {-21, 60, 32627}, {-15, 60, 32628}, {-9, 60, 32629}, {-3, 60, 32630}, + {3, 60, 32631}, {9, 60, 32632}, {15, 60, 32633}, {21, 60, 32634}, {27, 60, 32635}, + {33, 60, 32636}, {39, 60, 32637}, {45, 60, 32638}, {51, 60, 32639}, {57, 60, 32640}, + {63, 60, 32641}, {69, 60, 32642}, {75, 60, 32643}, {81, 60, 32644}, {87, 60, 32645}, + {93, 60, 32646}, {99, 60, 32647}, {105, 60, 32648}, {111, 60, 32649}, {117, 60, 32650}, + {123, 60, 32651}, {129, 60, 32652}, {135, 60, 32653}, {141, 60, 32654}, {147, 60, 32655}, + {153, 60, 32656}, {159, 60, 32657}, {165, 60, 32658}, {171, 60, 32659}, {177, 60, 32660}, + // UTM zones for Southern Hemisphere + {-177, -60, 32701}, {-171, -60, 32702}, {-165, -60, 32703}, {-159, -60, 32704}, {-153, -60, 32705}, + {-147, -60, 32706}, {-141, -60, 32707}, {-135, -60, 32708}, {-129, -60, 32709}, {-123, -60, 32710}, + {-117, -60, 32711}, {-111, -60, 32712}, {-105, -60, 32713}, {-99, -60, 32714}, {-93, -60, 32715}, + {-87, -60, 32716}, {-81, -60, 32717}, {-75, -60, 32718}, {-69, -60, 32719}, {-63, -60, 32720}, + {-57, -60, 32721}, {-51, -60, 32722}, {-45, -60, 32723}, {-39, -60, 32724}, {-33, -60, 32725}, + {-27, -60, 32726}, {-21, -60, 32727}, {-15, -60, 32728}, {-9, -60, 32729}, {-3, -60, 32730}, + {3, -60, 32731}, {9, -60, 32732}, {15, -60, 32733}, {21, -60, 32734}, {27, -60, 32735}, {33, -60, 32736}, + {39, -60, 32737}, {45, -60, 32738}, {51, -60, 32739}, {57, -60, 32740}, {63, -60, 32741}, {69, -60, 32742}, + {75, -60, 32743}, {81, -60, 32744}, {87, -60, 32745}, {93, -60, 32746}, {99, -60, 32747}, {105, -60, 32748}, + {111, -60, 32749}, {117, -60, 32750}, {123, -60, 32751}, {129, -60, 32752}, {135, -60, 32753}, {141, -60, 32754}, + {147, -60, 32755}, {153, -60, 32756}, {159, -60, 32757}, {165, -60, 32758}, {171, -60, 32759}, {177, -60, 32760}, + // Special cases + {-180, 60, 32660}, {180, 60, 32660}, {-180, -60, 32760}, {180, -60, 32760}, + }; for (int[] testCase : testCases) { @@ -1218,10 +1294,23 @@ public void testBestSRID() { int expectedEPSG = testCase[2]; Geometry geom = GEOMETRY_FACTORY.createPoint(new Coordinate(x, y)); - int actualEPSG = CRSUtils.bestSRID(geom); + int actualEPSG = Functions.bestSRID(geom); assertEquals("Failed at coordinates (" + x + ", " + y + ")", expectedEPSG, actualEPSG); } + + // Large geometry that does not fit into UTM or polar categories + Geometry geom = GEOMETRY_FACTORY.createPolygon(new Coordinate[] { + new Coordinate(-160, -40), + new Coordinate(-160, 40), + new Coordinate(160, 40), + new Coordinate(160, -40), + new Coordinate(-160, -40) + }); + int expectedEPSG = 3395; // EPSG code for World Mercator + int actualEPSG = Functions.bestSRID(geom); + + assertEquals("Expected World Mercator projection for wide range geometry", expectedEPSG, actualEPSG); } @Test diff --git a/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java b/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java index 597dc6bb15..8d172ebebe 100644 --- a/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java +++ b/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java @@ -20,6 +20,8 @@ import org.apache.sedona.common.FunctionsGeoTools; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.operation.buffer.BufferParameters; +import org.opengis.referencing.FactoryException; +import org.opengis.referencing.operation.TransformException; import java.util.Arrays; @@ -69,7 +71,7 @@ public Geometry eval(@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.j public static class ST_Buffer extends ScalarFunction { @DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) public Geometry eval(@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) - Object o, @DataTypeHint("Double") Double radius) { + Object o, @DataTypeHint("Double") Double radius) throws FactoryException, TransformException { Geometry geom = (Geometry) o; return org.apache.sedona.common.Functions.buffer(geom, radius); } From 27f1f6b8e2a833fb6b1885cc2df476f8931fee26 Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Tue, 20 Feb 2024 15:52:44 -0500 Subject: [PATCH 04/54] Implement ST_BestSRID --- .../org/apache/sedona/common/Functions.java | 46 ++++++++++---- .../apache/sedona/common/sphere/Spheroid.java | 42 +++++++++++++ .../apache/sedona/common/FunctionsTest.java | 60 +++++++++++++++++++ .../java/org/apache/sedona/flink/Catalog.java | 1 + .../sedona/flink/expressions/Functions.java | 8 +++ .../org/apache/sedona/flink/FunctionTest.java | 24 ++++++-- python/sedona/sql/st_functions.py | 6 ++ python/tests/sql/test_dataframe_api.py | 2 + python/tests/sql/test_function.py | 30 ++++++++++ .../spark/test_constructor_functions.py | 6 +- .../snowflake/snowsql/TestFunctions.java | 9 +++ .../apache/sedona/snowflake/snowsql/UDFs.java | 7 +++ .../sedona/snowflake/snowsql/UDFsV2.java | 7 +++ .../org/apache/sedona/sql/UDF/Catalog.scala | 1 + .../sedona_sql/expressions/Functions.scala | 7 +++ .../sedona_sql/expressions/st_functions.scala | 3 + .../sedona/sql/dataFrameAPITestScala.scala | 21 +++++++ .../apache/sedona/sql/functionTestScala.scala | 9 +++ 18 files changed, 272 insertions(+), 17 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 08293bb04a..4faba7d4a6 100644 --- a/common/src/main/java/org/apache/sedona/common/Functions.java +++ b/common/src/main/java/org/apache/sedona/common/Functions.java @@ -18,6 +18,7 @@ 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.GeometrySubDivider; import org.apache.sedona.common.utils.GeomUtils; import org.apache.sedona.common.utils.GeometryGeoHashEncoder; @@ -26,18 +27,7 @@ import org.apache.sedona.common.utils.S2Utils; import org.locationtech.jts.algorithm.MinimumBoundingCircle; import org.locationtech.jts.algorithm.hull.ConcaveHull; -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.LineString; -import org.locationtech.jts.geom.LinearRing; -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.PrecisionModel; +import org.locationtech.jts.geom.*; import org.locationtech.jts.geom.impl.CoordinateArraySequence; import org.locationtech.jts.geom.util.GeometryFixer; import org.locationtech.jts.io.gml2.GMLWriter; @@ -196,6 +186,38 @@ else if (singleParam[0].equalsIgnoreCase(listBufferParameters[5])) { return bufferParameters; } + public static int bestSRID(Geometry geometry) { + Envelope envelope = geometry.getEnvelopeInternal(); + if (envelope.isNull()) return Spheroid.EPSG_WORLD_MERCATOR; // Fallback EPSG + + // Calculate the center of the envelope + double centerX = (envelope.getMinX() + envelope.getMaxX()) / 2.0; // centroid.getX(); + double centerY = (envelope.getMinY() + envelope.getMaxY()) / 2.0; // centroid.getY(); + + // Calculate angular width and height + double xwidth = Spheroid.angularWidth(envelope); + double ywidth = Spheroid.angularHeight(envelope); + + // Prioritize polar regions for Lambert Azimuthal Equal Area projection + if (centerY >= 70.0 && ywidth < 45.0) return Spheroid.EPSG_NORTH_LAMBERT; + if (centerY <= -70.0 && ywidth < 45.0) return Spheroid.EPSG_SOUTH_LAMBERT; + + // Check for UTM zones + if (xwidth < 6.0) { + int zone; + if (centerX == -180.0 || centerX == 180.0) { + zone = 59; // UTM zone 60 + } else { + zone = (int)Math.floor((centerX + 180.0) / 6.0); + zone = Math.min(zone, 59); + } + return (centerY < 0.0) ? Spheroid.EPSG_SOUTH_UTM_START + zone : Spheroid.EPSG_NORTH_UTM_START + zone; + } + + // Default fallback to Mercator projection + return Spheroid.EPSG_WORLD_MERCATOR; + } + public static Geometry envelope(Geometry geometry) { return geometry.getEnvelope(); } diff --git a/common/src/main/java/org/apache/sedona/common/sphere/Spheroid.java b/common/src/main/java/org/apache/sedona/common/sphere/Spheroid.java index ef64df55da..7b06f00d73 100644 --- a/common/src/main/java/org/apache/sedona/common/sphere/Spheroid.java +++ b/common/src/main/java/org/apache/sedona/common/sphere/Spheroid.java @@ -23,12 +23,24 @@ import net.sf.geographiclib.PolygonArea; import net.sf.geographiclib.PolygonResult; import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.Geometry; import static java.lang.Math.abs; public class Spheroid { + // Standard EPSG Codes + public static final int EPSG_WORLD_MERCATOR = 3395; + public static final int EPSG_NORTH_UTM_START = 32601; + public static final int EPSG_NORTH_UTM_END = 32660; + public static final int EPSG_NORTH_LAMBERT = 3574; + public static final int EPSG_NORTH_STEREO = 3995; + public static final int EPSG_SOUTH_UTM_START = 32701; + public static final int EPSG_SOUTH_UTM_END = 32760; + public static final int EPSG_SOUTH_LAMBERT = 3409; + public static final int EPSG_SOUTH_STEREO = 3031; + /** * Calculate the distance between two points on the earth using the Spheroid formula. * This algorithm does not use the radius of the earth, but instead uses the WGS84 ellipsoid. @@ -118,4 +130,34 @@ else if (geomType.equals("MultiPolygon") || geomType.equals("GeometryCollection" return 0.0; } } + + public static double angularWidth(Envelope envelope) { + double lon1 = envelope.getMinX(); + double lon2 = envelope.getMaxX(); + double lat = (envelope.getMinY() + envelope.getMaxY()) / 2; // Mid-latitude for width calculation + + // Compute geodesic distance + GeodesicData g = Geodesic.WGS84.Inverse(lat, lon1, lat, lon2); + double distance = g.s12; // Distance in meters + + // Convert distance to angular width in degrees + double angularWidth = Math.toDegrees(distance / (Geodesic.WGS84.EquatorialRadius() * Math.PI / 180)); + + return angularWidth; + } + + public static double angularHeight(Envelope envelope) { + double lat1 = envelope.getMinY(); + double lat2 = envelope.getMaxY(); + double lon = (envelope.getMinX() + envelope.getMaxX()) / 2; // Mid-longitude for height calculation + + // Compute geodesic distance + GeodesicData g = Geodesic.WGS84.Inverse(lat1, lon, lat2, lon); + double distance = g.s12; // Distance in meters + + // Convert distance to angular height in degrees + double angularHeight = Math.toDegrees(distance / (Geodesic.WGS84.EquatorialRadius() * Math.PI / 180)); + + return angularHeight; + } } diff --git a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java index 64ce4aad33..919e390b1e 100644 --- a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java +++ b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java @@ -1171,6 +1171,66 @@ public void testBuffer() { assertEquals(expected, actual); } + @Test + public void testBestSRID() { + int[][] testCases = { + {0, -70, 3409}, // EPSG:3409 (Antarctic Polar Stereographic) + {0, 70, 3574}, // EPSG:3575 (North Pole LAEA Alaska) + // UTM zones for Northern Hemisphere + {-177, 60, 32601}, {-171, 60, 32602}, {-165, 60, 32603}, {-159, 60, 32604}, {-153, 60, 32605}, + {-147, 60, 32606}, {-141, 60, 32607}, {-135, 60, 32608}, {-129, 60, 32609}, {-123, 60, 32610}, + {-117, 60, 32611}, {-111, 60, 32612}, {-105, 60, 32613}, {-99, 60, 32614}, {-93, 60, 32615}, + {-87, 60, 32616}, {-81, 60, 32617}, {-75, 60, 32618}, {-69, 60, 32619}, {-63, 60, 32620}, + {-57, 60, 32621}, {-51, 60, 32622}, {-45, 60, 32623}, {-39, 60, 32624}, {-33, 60, 32625}, + {-27, 60, 32626}, {-21, 60, 32627}, {-15, 60, 32628}, {-9, 60, 32629}, {-3, 60, 32630}, + {3, 60, 32631}, {9, 60, 32632}, {15, 60, 32633}, {21, 60, 32634}, {27, 60, 32635}, + {33, 60, 32636}, {39, 60, 32637}, {45, 60, 32638}, {51, 60, 32639}, {57, 60, 32640}, + {63, 60, 32641}, {69, 60, 32642}, {75, 60, 32643}, {81, 60, 32644}, {87, 60, 32645}, + {93, 60, 32646}, {99, 60, 32647}, {105, 60, 32648}, {111, 60, 32649}, {117, 60, 32650}, + {123, 60, 32651}, {129, 60, 32652}, {135, 60, 32653}, {141, 60, 32654}, {147, 60, 32655}, + {153, 60, 32656}, {159, 60, 32657}, {165, 60, 32658}, {171, 60, 32659}, {177, 60, 32660}, + // UTM zones for Southern Hemisphere + {-177, -60, 32701}, {-171, -60, 32702}, {-165, -60, 32703}, {-159, -60, 32704}, {-153, -60, 32705}, + {-147, -60, 32706}, {-141, -60, 32707}, {-135, -60, 32708}, {-129, -60, 32709}, {-123, -60, 32710}, + {-117, -60, 32711}, {-111, -60, 32712}, {-105, -60, 32713}, {-99, -60, 32714}, {-93, -60, 32715}, + {-87, -60, 32716}, {-81, -60, 32717}, {-75, -60, 32718}, {-69, -60, 32719}, {-63, -60, 32720}, + {-57, -60, 32721}, {-51, -60, 32722}, {-45, -60, 32723}, {-39, -60, 32724}, {-33, -60, 32725}, + {-27, -60, 32726}, {-21, -60, 32727}, {-15, -60, 32728}, {-9, -60, 32729}, {-3, -60, 32730}, + {3, -60, 32731}, {9, -60, 32732}, {15, -60, 32733}, {21, -60, 32734}, {27, -60, 32735}, {33, -60, 32736}, + {39, -60, 32737}, {45, -60, 32738}, {51, -60, 32739}, {57, -60, 32740}, {63, -60, 32741}, {69, -60, 32742}, + {75, -60, 32743}, {81, -60, 32744}, {87, -60, 32745}, {93, -60, 32746}, {99, -60, 32747}, {105, -60, 32748}, + {111, -60, 32749}, {117, -60, 32750}, {123, -60, 32751}, {129, -60, 32752}, {135, -60, 32753}, {141, -60, 32754}, + {147, -60, 32755}, {153, -60, 32756}, {159, -60, 32757}, {165, -60, 32758}, {171, -60, 32759}, {177, -60, 32760}, + // Special cases + {-180, 60, 32660}, {180, 60, 32660}, {-180, -60, 32760}, {180, -60, 32760}, + }; + + + for (int[] testCase : testCases) { + int x = testCase[0]; + int y = testCase[1]; + int expectedEPSG = testCase[2]; + + Geometry geom = GEOMETRY_FACTORY.createPoint(new Coordinate(x, y)); + int actualEPSG = Functions.bestSRID(geom); + + assertEquals("Failed at coordinates (" + x + ", " + y + ")", expectedEPSG, actualEPSG); + } + + // Large geometry that does not fit into UTM or polar categories + Geometry geom = GEOMETRY_FACTORY.createPolygon(new Coordinate[] { + new Coordinate(-160, -40), + new Coordinate(-160, 40), + new Coordinate(160, 40), + new Coordinate(160, -40), + new Coordinate(-160, -40) + }); + int expectedEPSG = 3395; // EPSG code for World Mercator + int actualEPSG = Functions.bestSRID(geom); + + assertEquals("Expected World Mercator projection for wide range geometry", expectedEPSG, actualEPSG); + } + @Test public void nRingsUnsupported() { LineString lineString = GEOMETRY_FACTORY.createLineString(coordArray3d(0, 1, 1, 1, 2, 1, 1, 2, 2)); diff --git a/flink/src/main/java/org/apache/sedona/flink/Catalog.java b/flink/src/main/java/org/apache/sedona/flink/Catalog.java index 8cb644bb8c..0a2b4390f8 100644 --- a/flink/src/main/java/org/apache/sedona/flink/Catalog.java +++ b/flink/src/main/java/org/apache/sedona/flink/Catalog.java @@ -46,6 +46,7 @@ public static UserDefinedFunction[] getFuncs() { new Functions.ST_Azimuth(), new Functions.ST_Boundary(), new Functions.ST_Buffer(), + new Functions.ST_BestSRID(), new Functions.ST_ClosestPoint(), new Functions.ST_Centroid(), new Functions.ST_Collect(), diff --git a/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java b/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java index 597dc6bb15..dc707a825d 100644 --- a/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java +++ b/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java @@ -82,6 +82,14 @@ public Geometry eval(@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.j } } + public static class ST_BestSRID extends ScalarFunction { + @DataTypeHint("Integer") + public int eval(@DataTypeHint(value = "RAW", bridgedTo = Geometry.class) Object o) { + Geometry geom = (Geometry) o; + return org.apache.sedona.common.Functions.bestSRID(geom); + } + } + public static class ST_ClosestPoint extends ScalarFunction { @DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) public Geometry eval(@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) Object g1, diff --git a/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java b/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java index 5486bd6229..3b342840a4 100644 --- a/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java +++ b/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java @@ -16,16 +16,14 @@ import org.apache.commons.codec.binary.Hex; import org.apache.commons.lang3.tuple.Pair; import org.apache.flink.table.api.Table; +import org.apache.flink.table.planner.expressions.In; import org.apache.sedona.flink.expressions.Functions; import org.apache.sedona.flink.expressions.FunctionsGeoTools; import org.geotools.referencing.CRS; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; -import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.geom.LineString; -import org.locationtech.jts.geom.Point; -import org.locationtech.jts.geom.Polygon; +import org.locationtech.jts.geom.*; import org.locationtech.jts.io.ParseException; import org.locationtech.jts.operation.buffer.BufferParameters; import org.opengis.referencing.FactoryException; @@ -99,6 +97,24 @@ public void testBuffer() { assertEquals(expected, actual); } + @Test + public void testBestSRID() { + Table table1 = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('POINT (160 40)') AS geom"); + table1 = table1.select(call(Functions.ST_BestSRID.class.getSimpleName(), $("geom"))); + int result = (int) first(table1).getField(0); + assertEquals(32657, result); + + Table table2 = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('LINESTRING(-91.185 30.4505, -91.187 30.452, -91.189 30.4535)') AS geom"); + table2 = table2.select(call(Functions.ST_BestSRID.class.getSimpleName(), $("geom"))); + result = (int) first(table2).getField(0); + assertEquals(32615, result); + + Table table3 = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('POLYGON((-120 30, -80 30, -80 50, -120 50, -120 30))') AS geom"); + table3 = table3.select(call(Functions.ST_BestSRID.class.getSimpleName(), $("geom"))); + result = (int) first(table3).getField(0); + assertEquals(3395, result); + } + @Test public void testClosestPoint() { Table table = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('POINT (160 40)') AS g1, ST_GeomFromWKT('POINT (10 10)') as g2"); diff --git a/python/sedona/sql/st_functions.py b/python/sedona/sql/st_functions.py index aad15ea879..cf215100ec 100644 --- a/python/sedona/sql/st_functions.py +++ b/python/sedona/sql/st_functions.py @@ -37,6 +37,7 @@ "ST_AsKML", "ST_AsText", "ST_Azimuth", + "ST_BestSRID", "ST_Boundary", "ST_Buffer", "ST_BuildArea", @@ -304,6 +305,11 @@ def ST_Azimuth(point_a: ColumnOrName, point_b: ColumnOrName) -> Column: return _call_st_function("ST_Azimuth", (point_a, point_b)) +@validate_argument_types +def ST_BestSRID(geometry: ColumnOrName) -> Column: + return _call_st_function("ST_BestSRID", geometry) + + @validate_argument_types def ST_Boundary(geometry: ColumnOrName) -> Column: """Calculate the closure of the combinatorial boundary of a geometry column. diff --git a/python/tests/sql/test_dataframe_api.py b/python/tests/sql/test_dataframe_api.py index bd3ebb57ba..2b2c8490bb 100644 --- a/python/tests/sql/test_dataframe_api.py +++ b/python/tests/sql/test_dataframe_api.py @@ -71,6 +71,7 @@ (stf.ST_AsKML, ("point",), "point_geom", "", "\n 0.0,1.0\n\n"), (stf.ST_AsText, ("point",), "point_geom", "", "POINT (0 1)"), (stf.ST_Azimuth, ("a", "b"), "two_points", "geom * 180.0 / pi()", 90.0), + (stf.ST_BestSRID, ("geom",), "triangle_geom", "", 1234), (stf.ST_Boundary, ("geom",), "triangle_geom", "", "LINESTRING (0 0, 1 0, 1 1, 0 0)"), (stf.ST_Buffer, ("point", 1.0), "point_geom", "ST_ReducePrecision(geom, 2)", "POLYGON ((0.98 0.8, 0.92 0.62, 0.83 0.44, 0.71 0.29, 0.56 0.17, 0.38 0.08, 0.2 0.02, 0 0, -0.2 0.02, -0.38 0.08, -0.56 0.17, -0.71 0.29, -0.83 0.44, -0.92 0.62, -0.98 0.8, -1 1, -0.98 1.2, -0.92 1.38, -0.83 1.56, -0.71 1.71, -0.56 1.83, -0.38 1.92, -0.2 1.98, 0 2, 0.2 1.98, 0.38 1.92, 0.56 1.83, 0.71 1.71, 0.83 1.56, 0.92 1.38, 0.98 1.2, 1 1, 0.98 0.8))"), (stf.ST_BuildArea, ("geom",), "multiline_geom", "ST_Normalize(geom)", "POLYGON ((0 0, 1 1, 1 0, 0 0))"), @@ -229,6 +230,7 @@ (stf.ST_AsText, (None,)), (stf.ST_Azimuth, (None, "")), (stf.ST_Azimuth, ("", None)), + (stf.ST_BestSRID, (None,)), (stf.ST_Boundary, (None,)), (stf.ST_Buffer, (None, 1.0)), (stf.ST_Buffer, ("", None)), diff --git a/python/tests/sql/test_function.py b/python/tests/sql/test_function.py index cb141e4546..34722f8823 100644 --- a/python/tests/sql/test_function.py +++ b/python/tests/sql/test_function.py @@ -101,6 +101,36 @@ def test_st_buffer(self): actual = function_df.take(1)[0][0].wkt assert actual == "POLYGON ((-107.02 42.06, -107.02 42.07, -107.02 42.09, -107.02 42.11, -107.02 42.32, -107.02 42.33, -107.01 42.42, -107.01 42.43, -106.77 44.33, -106.16 46.15, -105.22 47.82, -103.98 49.27, -102.48 50.47, -100.78 51.36, -98.94 51.9, -97.04 52.09, -97.03 52.09, -97.01 52.09, -96.95 52.09, -96.9 52.09, -96.81 52.09, -96.7 52.09, -96.68 52.09, -96.65 52.09, -96.55 52.09, -96.54 52.09, -96.49 52.09, -96.48 52.09, -94.58 51.89, -92.74 51.33, -91.04 50.43, -89.55 49.23, -88.32 47.76, -87.39 46.08, -86.79 44.25, -86.56 42.35, -86.56 42.18, -86.56 42.17, -86.56 42.1, -86.55 41.99, -86.56 41.9, -86.56 41.78, -86.56 41.77, -86.56 41.75, -86.56 41.73, -86.56 41.7, -86.75 39.76, -87.33 37.89, -88.25 36.17, -89.49 34.66, -91 33.43, -92.72 32.51, -94.59 31.94, -96.53 31.74, -96.55 31.74, -96.56 31.74, -96.57 31.74, -96.58 31.74, -96.6 31.74, -96.69 31.74, -96.72 31.74, -96.75 31.74, -96.94 31.74, -97.02 31.74, -97.04 31.74, -97.06 31.74, -98.99 31.94, -100.85 32.5, -102.56 33.42, -104.07 34.65, -105.31 36.14, -106.23 37.85, -106.81 39.71, -107.02 41.64, -107.02 41.75, -107.02 41.94, -107.02 41.96, -107.02 41.99, -107.02 42.01, -107.02 42.02, -107.02 42.03, -107.02 42.06))" + def test_st_bestsrid(self): + polygon_from_wkt = self.spark.read.format("csv"). \ + option("delimiter", "\t"). \ + option("header", "false"). \ + load(mixed_wkt_geometry_input_location) + + polygon_from_wkt.createOrReplaceTempView("polgontable") + polygon_from_wkt.show() + + polygon_df = self.spark.sql("select ST_GeomFromWKT(polygontable._c0) as countyshape from polygontable") + polygon_df.createOrReplaceTempView("polygondf") + polygon_df.show() + function_df = self.spark.sql("select ST_BestSRID(polygondf.countyshape) from polygondf") + function_df.show() + actual = function_df.take(1)[0][0] + assert actual == 1234 + + polygon_wkt_df = self.spark.read.format("csv"). \ + option("delimiter", "\t"). \ + option("header", "false"). \ + load(mixed_wkt_geometry_input_location) + + polygon_wkt_df.createOrReplaceTempView("polygontable") + polygon_wkt_df.show() + polygon_df = self.spark.sql("select ST_GeomFromWKT(polygontable._c0) as countyshape from polygontable") + polygon_df.createOrReplaceTempView("polygondf") + polygon_df.show() + function_df = self.spark.sql("select ST_Area(polygondf.countyshape) from polygondf") + function_df.show() + def test_st_envelope(self): polygon_from_wkt = self.spark.read.format("csv"). \ option("delimiter", "\t"). \ diff --git a/python/tests/streaming/spark/test_constructor_functions.py b/python/tests/streaming/spark/test_constructor_functions.py index e197cd2cdb..9e066063a5 100644 --- a/python/tests/streaming/spark/test_constructor_functions.py +++ b/python/tests/streaming/spark/test_constructor_functions.py @@ -297,7 +297,11 @@ "ST_GeomFromText('POINT(21.427834 52.042576573)')", "ST_GeomFromText('POINT(45.342524 56.342354355)')"]) .with_expected_result(0.0) - .with_transform("ST_LENGTH")) + .with_transform("ST_LENGTH")), + (SuiteContainer.empty() + .with_function_name("ST_BestSRID") + .with_arguments(["ST_GeomFromText('POINT (-177 60)')"]) + .with_expected_result(32601)) ] diff --git a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java index 37e71dff9e..a40050e9ca 100644 --- a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java +++ b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java @@ -191,6 +191,15 @@ public void test_ST_Buffer() { "POLYGON ((1 1, 0.9807852804032304 0.8049096779838718, 0.9238795325112867 0.6173165676349102, 0.8314696123025452 0.4444297669803978, 0.7071067811865476 0.2928932188134525, 0.5555702330196023 0.1685303876974548, 0.3826834323650898 0.0761204674887133, 0.1950903220161283 0.0192147195967696, 0.0000000000000001 0, -0.1950903220161282 0.0192147195967696, -0.3826834323650897 0.0761204674887133, -0.555570233019602 0.1685303876974547, -0.7071067811865475 0.2928932188134524, -0.8314696123025453 0.4444297669803978, -0.9238795325112867 0.6173165676349102, -0.9807852804032304 0.8049096779838714, -1 0.9999999999999999, -0.9807852804032304 1.1950903220161284, -0.9238795325112868 1.3826834323650896, -0.8314696123025455 1.555570233019602, -0.7071067811865477 1.7071067811865475, -0.5555702330196022 1.8314696123025453, -0.3826834323650903 1.9238795325112865, -0.1950903220161287 1.9807852804032304, -0.0000000000000002 2, 0.1950903220161283 1.9807852804032304, 0.38268343236509 1.9238795325112865, 0.5555702330196018 1.8314696123025453, 0.7071067811865474 1.7071067811865477, 0.8314696123025452 1.5555702330196022, 0.9238795325112865 1.3826834323650905, 0.9807852804032303 1.1950903220161286, 1 1))" ); } + + @Test + public void test_ST_BestSRID() { + registerUDF("ST_BestSRID", byte[].class); + verifySQLSingleRes( + "select sedona.ST_AsText(sedona.ST_BestSRID(sedona.ST_GeomFromText('POINT (-180 60)')))", + "32660" + ) + } @Test public void test_ST_BuildArea() { registerUDF("ST_BuildArea", byte[].class); diff --git a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFs.java b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFs.java index d9763d9fc2..6bd07a035f 100644 --- a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFs.java +++ b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFs.java @@ -168,6 +168,13 @@ public static byte[] ST_Buffer(byte[] geometry, double radius) { ); } + @UDFAnnotations.ParamMeta(argNames = {"geometry"}) + public static int ST_BestSRID(byte[] geometry) { + return Functions.bestSRID( + GeometrySerde.deserialize(geometry) + ); + } + @UDFAnnotations.ParamMeta(argNames = {"geometry"}) public static byte[] ST_BuildArea(byte[] geometry) { return GeometrySerde.serialize( diff --git a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java index a94a7df722..8ae5700a02 100644 --- a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java +++ b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java @@ -171,6 +171,13 @@ public static String ST_BoundingDiagonal(String geometry) { ); } + @UDFAnnotations.ParamMeta(argNames = {"geometry", "radius"}, argTypes = {"Geometry"}) + public static Integer ST_BestSRID(String geometry) { + return Functions.bestSRID( + GeometrySerde.deserGeoJson(geometry) + ); + } + @UDFAnnotations.ParamMeta(argNames = {"geometry", "radius"}, argTypes = {"Geometry", "double"}, returnTypes = "Geometry") public static String ST_Buffer(String geometry, double radius) { return GeometrySerde.serGeoJson( diff --git a/spark/common/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala b/spark/common/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala index 00a04b0786..60d43b52de 100644 --- a/spark/common/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala +++ b/spark/common/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala @@ -69,6 +69,7 @@ object Catalog { function[ST_NPoints](), function[ST_NDims](), function[ST_Buffer](), + function[ST_BestSRID](), function[ST_Envelope](), function[ST_Length](), function[ST_Area](), diff --git a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala index 3ebf42b910..9d933c18af 100644 --- a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala +++ b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala @@ -160,6 +160,13 @@ case class ST_Buffer(inputExpressions: Seq[Expression]) } } +case class ST_BestSRID(inputExpressions: Seq[Expression]) + extends InferredExpression(Functions.bestSRID _) { + + protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = { + copy(inputExpressions = newChildren) + } +} /** * Return the bounding rectangle for a Geometry diff --git a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala index 957c2483ee..6744ad1dfb 100644 --- a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala +++ b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala @@ -69,6 +69,9 @@ object st_functions extends DataFrameAPI { def ST_Buffer(geometry: Column, buffer: Column, parameters: Column): Column = wrapExpression[ST_Buffer](geometry, buffer, parameters) def ST_Buffer(geometry: String, buffer: Double, parameters: String): Column = wrapExpression[ST_Buffer](geometry, buffer, parameters) + def ST_BestSRID(geometry: Column): Column = wrapExpression[ST_BestSRID](geometry) + def ST_BestSRID(geometry: String): Column = wrapExpression[ST_BestSRID](geometry) + def ST_BuildArea(geometry: Column): Column = wrapExpression[ST_BuildArea](geometry) def ST_BuildArea(geometry: String): Column = wrapExpression[ST_BuildArea](geometry) diff --git a/spark/common/src/test/scala/org/apache/sedona/sql/dataFrameAPITestScala.scala b/spark/common/src/test/scala/org/apache/sedona/sql/dataFrameAPITestScala.scala index 0b163a1c34..747cd53f11 100644 --- a/spark/common/src/test/scala/org/apache/sedona/sql/dataFrameAPITestScala.scala +++ b/spark/common/src/test/scala/org/apache/sedona/sql/dataFrameAPITestScala.scala @@ -216,6 +216,27 @@ class dataFrameAPITestScala extends TestBaseScala { assertEquals(expected, actual) } + it("Passed ST_BestSRID") { + val pointDf = sparkSession.sql("SELECT ST_Point(-177, -60) AS geom") + val df = pointDf.select(ST_BestSRID("geom").as("geom")) + var actual = df.take(1)(0).get(0).asInstanceOf[Int] + var expected = 32701 + assertEquals(expected, actual) + + var linestringDf = sparkSession.sql("SELECT ST_GeomFromWKT('LINESTRING(-91.185 30.4505, -91.187 30.452, -91.189 30.4535)') AS geom") + var dfLine = linestringDf.select(ST_BestSRID("geom").as("geom")) + actual = dfLine.take(1)(0).get(0).asInstanceOf[Int] + expected = 32615 + assertEquals(expected, actual) + + val polygonDf = sparkSession.sql("SELECT ST_GeomFromWKT('POLYGON((-120 30, -80 30, -80 50, -120 50, -120 30))') AS geom") + val dfPolygon = polygonDf.select(ST_BestSRID("geom").as("geom")) + actual = dfPolygon.take(1)(0).get(0).asInstanceOf[Int] + println(actual) + expected = 3395 + assertEquals(expected, actual) + } + it("Passed ST_Envelope") { val polygonDf = sparkSession.sql("SELECT ST_GeomFromWKT('POLYGON ((0 0, 1 0, 1 1, 0 0))') AS geom") val df = polygonDf.select(ST_Envelope("geom")) diff --git a/spark/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala b/spark/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala index e8eb96de8b..f31d698193 100644 --- a/spark/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala +++ b/spark/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala @@ -73,6 +73,15 @@ class functionTestScala extends TestBaseScala with Matchers with GeometrySample assert(functionDf.count() > 0); } + it("Passed ST_BestSRID") { + val polygonWktDf = sparkSession.read.format("csv").option("delimiter", "\t").option("header", "false").load(mixedWktGeometryInputLocation) + polygonWktDf.createOrReplaceTempView("polygontable") + val polygonDf = sparkSession.sql("select ST_GeomFromWKT(polygontable._c0) as countyshape from polygontable") + polygonDf.createOrReplaceTempView("polygondf") + val functionDf = sparkSession.sql("select ST_BestSRID(polygondf.countyshape) from polygondf") + assert(functionDf.count() > 0); + } + it("Passed ST_Envelope") { var polygonWktDf = sparkSession.read.format("csv").option("delimiter", "\t").option("header", "false").load(mixedWktGeometryInputLocation) polygonWktDf.createOrReplaceTempView("polygontable") From 0f89438f0cdf5008d27a997ddbb0364713ad0039 Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Tue, 20 Feb 2024 16:02:00 -0500 Subject: [PATCH 05/54] fix typos --- python/tests/sql/test_function.py | 13 ------------- .../org/apache/sedona/snowflake/snowsql/UDFsV2.java | 2 +- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/python/tests/sql/test_function.py b/python/tests/sql/test_function.py index 34722f8823..c278654560 100644 --- a/python/tests/sql/test_function.py +++ b/python/tests/sql/test_function.py @@ -118,19 +118,6 @@ def test_st_bestsrid(self): actual = function_df.take(1)[0][0] assert actual == 1234 - polygon_wkt_df = self.spark.read.format("csv"). \ - option("delimiter", "\t"). \ - option("header", "false"). \ - load(mixed_wkt_geometry_input_location) - - polygon_wkt_df.createOrReplaceTempView("polygontable") - polygon_wkt_df.show() - polygon_df = self.spark.sql("select ST_GeomFromWKT(polygontable._c0) as countyshape from polygontable") - polygon_df.createOrReplaceTempView("polygondf") - polygon_df.show() - function_df = self.spark.sql("select ST_Area(polygondf.countyshape) from polygondf") - function_df.show() - def test_st_envelope(self): polygon_from_wkt = self.spark.read.format("csv"). \ option("delimiter", "\t"). \ diff --git a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java index 8ae5700a02..e8444784cc 100644 --- a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java +++ b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java @@ -171,7 +171,7 @@ public static String ST_BoundingDiagonal(String geometry) { ); } - @UDFAnnotations.ParamMeta(argNames = {"geometry", "radius"}, argTypes = {"Geometry"}) + @UDFAnnotations.ParamMeta(argNames = {"geometry"}, argTypes = {"Geometry"}) public static Integer ST_BestSRID(String geometry) { return Functions.bestSRID( GeometrySerde.deserGeoJson(geometry) From 6881f068e54a5d06fd88eddd12134b84cc0394b0 Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Tue, 20 Feb 2024 16:39:14 -0500 Subject: [PATCH 06/54] fix test case; Add comments --- python/sedona/sql/st_functions.py | 7 +++++++ python/tests/sql/test_dataframe_api.py | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/python/sedona/sql/st_functions.py b/python/sedona/sql/st_functions.py index cf215100ec..3465b9c162 100644 --- a/python/sedona/sql/st_functions.py +++ b/python/sedona/sql/st_functions.py @@ -307,6 +307,13 @@ def ST_Azimuth(point_a: ColumnOrName, point_b: ColumnOrName) -> Column: @validate_argument_types def ST_BestSRID(geometry: ColumnOrName) -> Column: + """Estimates the best SRID (EPSG code) of the geometry. + + :param geometry: Geometry column to calculate the boundary for. + :type geometry: ColumnOrName + :return: SRID as an Integer + :rtype: Column + """ return _call_st_function("ST_BestSRID", geometry) diff --git a/python/tests/sql/test_dataframe_api.py b/python/tests/sql/test_dataframe_api.py index 2b2c8490bb..be8278c727 100644 --- a/python/tests/sql/test_dataframe_api.py +++ b/python/tests/sql/test_dataframe_api.py @@ -71,7 +71,7 @@ (stf.ST_AsKML, ("point",), "point_geom", "", "\n 0.0,1.0\n\n"), (stf.ST_AsText, ("point",), "point_geom", "", "POINT (0 1)"), (stf.ST_Azimuth, ("a", "b"), "two_points", "geom * 180.0 / pi()", 90.0), - (stf.ST_BestSRID, ("geom",), "triangle_geom", "", 1234), + (stf.ST_BestSRID, ("geom",), "triangle_geom", "", 3395), (stf.ST_Boundary, ("geom",), "triangle_geom", "", "LINESTRING (0 0, 1 0, 1 1, 0 0)"), (stf.ST_Buffer, ("point", 1.0), "point_geom", "ST_ReducePrecision(geom, 2)", "POLYGON ((0.98 0.8, 0.92 0.62, 0.83 0.44, 0.71 0.29, 0.56 0.17, 0.38 0.08, 0.2 0.02, 0 0, -0.2 0.02, -0.38 0.08, -0.56 0.17, -0.71 0.29, -0.83 0.44, -0.92 0.62, -0.98 0.8, -1 1, -0.98 1.2, -0.92 1.38, -0.83 1.56, -0.71 1.71, -0.56 1.83, -0.38 1.92, -0.2 1.98, 0 2, 0.2 1.98, 0.38 1.92, 0.56 1.83, 0.71 1.71, 0.83 1.56, 0.92 1.38, 0.98 1.2, 1 1, 0.98 0.8))"), (stf.ST_BuildArea, ("geom",), "multiline_geom", "ST_Normalize(geom)", "POLYGON ((0 0, 1 1, 1 0, 0 0))"), From 412a8d4e71b37ae9350835fbb07b82f401a61138 Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Tue, 20 Feb 2024 18:32:10 -0500 Subject: [PATCH 07/54] Add docs; Fix test case --- .../apache/sedona/common/FunctionsTest.java | 71 +++++++++++-------- docs/api/flink/Function.md | 27 +++++++ docs/api/sql/Function.md | 27 +++++++ python/tests/sql/test_function.py | 2 +- .../sedona/sql/dataFrameAPITestScala.scala | 11 ++- 5 files changed, 103 insertions(+), 35 deletions(-) diff --git a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java index 919e390b1e..0e75192d29 100644 --- a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java +++ b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java @@ -1173,48 +1173,57 @@ public void testBuffer() { @Test public void testBestSRID() { - int[][] testCases = { + int[][] testCases_special = { {0, -70, 3409}, // EPSG:3409 (Antarctic Polar Stereographic) {0, 70, 3574}, // EPSG:3575 (North Pole LAEA Alaska) - // UTM zones for Northern Hemisphere - {-177, 60, 32601}, {-171, 60, 32602}, {-165, 60, 32603}, {-159, 60, 32604}, {-153, 60, 32605}, - {-147, 60, 32606}, {-141, 60, 32607}, {-135, 60, 32608}, {-129, 60, 32609}, {-123, 60, 32610}, - {-117, 60, 32611}, {-111, 60, 32612}, {-105, 60, 32613}, {-99, 60, 32614}, {-93, 60, 32615}, - {-87, 60, 32616}, {-81, 60, 32617}, {-75, 60, 32618}, {-69, 60, 32619}, {-63, 60, 32620}, - {-57, 60, 32621}, {-51, 60, 32622}, {-45, 60, 32623}, {-39, 60, 32624}, {-33, 60, 32625}, - {-27, 60, 32626}, {-21, 60, 32627}, {-15, 60, 32628}, {-9, 60, 32629}, {-3, 60, 32630}, - {3, 60, 32631}, {9, 60, 32632}, {15, 60, 32633}, {21, 60, 32634}, {27, 60, 32635}, - {33, 60, 32636}, {39, 60, 32637}, {45, 60, 32638}, {51, 60, 32639}, {57, 60, 32640}, - {63, 60, 32641}, {69, 60, 32642}, {75, 60, 32643}, {81, 60, 32644}, {87, 60, 32645}, - {93, 60, 32646}, {99, 60, 32647}, {105, 60, 32648}, {111, 60, 32649}, {117, 60, 32650}, - {123, 60, 32651}, {129, 60, 32652}, {135, 60, 32653}, {141, 60, 32654}, {147, 60, 32655}, - {153, 60, 32656}, {159, 60, 32657}, {165, 60, 32658}, {171, 60, 32659}, {177, 60, 32660}, - // UTM zones for Southern Hemisphere - {-177, -60, 32701}, {-171, -60, 32702}, {-165, -60, 32703}, {-159, -60, 32704}, {-153, -60, 32705}, - {-147, -60, 32706}, {-141, -60, 32707}, {-135, -60, 32708}, {-129, -60, 32709}, {-123, -60, 32710}, - {-117, -60, 32711}, {-111, -60, 32712}, {-105, -60, 32713}, {-99, -60, 32714}, {-93, -60, 32715}, - {-87, -60, 32716}, {-81, -60, 32717}, {-75, -60, 32718}, {-69, -60, 32719}, {-63, -60, 32720}, - {-57, -60, 32721}, {-51, -60, 32722}, {-45, -60, 32723}, {-39, -60, 32724}, {-33, -60, 32725}, - {-27, -60, 32726}, {-21, -60, 32727}, {-15, -60, 32728}, {-9, -60, 32729}, {-3, -60, 32730}, - {3, -60, 32731}, {9, -60, 32732}, {15, -60, 32733}, {21, -60, 32734}, {27, -60, 32735}, {33, -60, 32736}, - {39, -60, 32737}, {45, -60, 32738}, {51, -60, 32739}, {57, -60, 32740}, {63, -60, 32741}, {69, -60, 32742}, - {75, -60, 32743}, {81, -60, 32744}, {87, -60, 32745}, {93, -60, 32746}, {99, -60, 32747}, {105, -60, 32748}, - {111, -60, 32749}, {117, -60, 32750}, {123, -60, 32751}, {129, -60, 32752}, {135, -60, 32753}, {141, -60, 32754}, - {147, -60, 32755}, {153, -60, 32756}, {159, -60, 32757}, {165, -60, 32758}, {171, -60, 32759}, {177, -60, 32760}, // Special cases {-180, 60, 32660}, {180, 60, 32660}, {-180, -60, 32760}, {180, -60, 32760}, }; + // Number of UTM zones + int numZones = (177 - (-177)) / 6 + 1; + int numZonesSouth = (177 - (-177)) / 6 + 1; - for (int[] testCase : testCases) { - int x = testCase[0]; - int y = testCase[1]; + int[][] testCases_UTMNorth = new int[numZones][3]; + int[][] testCases_UTMSouth = new int[numZonesSouth][3]; + + int indexNorth = 0; + int northernLat = 60; // Latitude for Northern Hemisphere UTM zones + for (int lon = -177, epsg = 32601; lon <= 177; lon += 6, epsg++) { + testCases_UTMNorth[indexNorth][0] = lon; // Longitude + testCases_UTMNorth[indexNorth][1] = northernLat; // Latitude + testCases_UTMNorth[indexNorth][2] = epsg; // EPSG code + indexNorth++; + } + + int indexSouth = 0; + int southernLat = -60; // Latitude for Southern Hemisphere UTM zones + for (int lon = -177, epsg = 32701; lon <= 177; lon += 6, epsg++) { + testCases_UTMSouth[indexSouth][0] = lon; // Longitude + testCases_UTMSouth[indexSouth][1] = southernLat; // Latitude + testCases_UTMSouth[indexSouth][2] = epsg; // EPSG code + indexSouth++; + } + + for (int[] testCase : testCases_special) { + Geometry geom = GEOMETRY_FACTORY.createPoint(new Coordinate(testCase[0], testCase[1])); + int actualEPSG = Functions.bestSRID(geom); int expectedEPSG = testCase[2]; + assertEquals("Failed at coordinates (" + testCase[0] + ", " + testCase[1] + ")", expectedEPSG, actualEPSG); + } - Geometry geom = GEOMETRY_FACTORY.createPoint(new Coordinate(x, y)); + for (int[] testCase : testCases_UTMNorth) { + Geometry geom = GEOMETRY_FACTORY.createPoint(new Coordinate(testCase[0], testCase[1])); int actualEPSG = Functions.bestSRID(geom); + int expectedEPSG = testCase[2]; + assertEquals("Failed at coordinates (" + testCase[0] + ", " + testCase[1] + ")", expectedEPSG, actualEPSG); + } - assertEquals("Failed at coordinates (" + x + ", " + y + ")", expectedEPSG, actualEPSG); + for (int[] testCase : testCases_UTMSouth) { + Geometry geom = GEOMETRY_FACTORY.createPoint(new Coordinate(testCase[0], testCase[1])); + int actualEPSG = Functions.bestSRID(geom); + int expectedEPSG = testCase[2]; + assertEquals("Failed at coordinates (" + testCase[0] + ", " + testCase[1] + ")", expectedEPSG, actualEPSG); } // Large geometry that does not fit into UTM or polar categories diff --git a/docs/api/flink/Function.md b/docs/api/flink/Function.md index 73916552a0..433d4e8ae5 100644 --- a/docs/api/flink/Function.md +++ b/docs/api/flink/Function.md @@ -476,6 +476,33 @@ Output: 3.141592653589793 ``` +## ST_BestSRID + +Introduction: Returns the estimated most appropriate Spatial Reference Identifier (SRID) for a given geometry, based on its spatial extent and location. It evaluates the geometry's bounding envelope and selects an SRID that optimally represents the geometry on the Earth's surface. The function prioritizes Universal Transverse Mercator (UTM), Lambert Azimuthal Equal Area (LAEA), or falls back to the Mercator projection. + +- For geometries in the Arctic or Antarctic regions, the Lambert Azimuthal Equal Area projection is used. +- For geometries that fit within a single UTM zone and do not cross the International Date Line, a corresponding UTM SRID is chosen. +- In cases where none of the above conditions are met, or for geometries that cross the International Date Line, the function defaults to the Mercator projection. + +!!!Warning +`ST_BestSRID` is designed to estimate a suitable SRID from a set of approximately 65 EPSG codes and works best for geometries that fit within the UTM zones. It should not be solely relied upon to determine the most accurate SRID, especially for specialized or high-precision spatial requirements. + +Format: `ST_BestSRID(geom: Geometry)` + +Since: `v1.6.0` + +Spark SQL Example: + +```sql +SELECT ST_BestSRID(ST_GeomFromWKT('POLYGON((-73.9980 40.7265, -73.9970 40.7265, -73.9970 40.7255, -73.9980 40.7255, -73.9980 40.7265))')) +``` + +Output: + +``` +32618 +``` + ## ST_Boundary Introduction: Returns the closure of the combinatorial boundary of this Geometry. diff --git a/docs/api/sql/Function.md b/docs/api/sql/Function.md index e055dc97b2..bdca29b425 100644 --- a/docs/api/sql/Function.md +++ b/docs/api/sql/Function.md @@ -474,6 +474,33 @@ Output: 3.141592653589793 ``` +## ST_BestSRID + +Introduction: Returns the estimated most appropriate Spatial Reference Identifier (SRID) for a given geometry, based on its spatial extent and location. It evaluates the geometry's bounding envelope and selects an SRID that optimally represents the geometry on the Earth's surface. The function prioritizes Universal Transverse Mercator (UTM), Lambert Azimuthal Equal Area (LAEA), or falls back to the Mercator projection. + +- For geometries in the Arctic or Antarctic regions, the Lambert Azimuthal Equal Area projection is used. +- For geometries that fit within a single UTM zone and do not cross the International Date Line, a corresponding UTM SRID is chosen. +- In cases where none of the above conditions are met, or for geometries that cross the International Date Line, the function defaults to the Mercator projection. + +!!!Warning + `ST_BestSRID` is designed to estimate a suitable SRID from a set of approximately 65 EPSG codes and works best for geometries that fit within the UTM zones. It should not be solely relied upon to determine the most accurate SRID, especially for specialized or high-precision spatial requirements. + +Format: `ST_BestSRID(geom: Geometry)` + +Since: `v1.6.0` + +Spark SQL Example: + +```sql +SELECT ST_BestSRID(ST_GeomFromWKT('POLYGON((-73.9980 40.7265, -73.9970 40.7265, -73.9970 40.7255, -73.9980 40.7255, -73.9980 40.7265))')) +``` + +Output: + +``` +32618 +``` + ## ST_Boundary Introduction: Returns the closure of the combinatorial boundary of this Geometry. diff --git a/python/tests/sql/test_function.py b/python/tests/sql/test_function.py index c278654560..c7e9af4ffb 100644 --- a/python/tests/sql/test_function.py +++ b/python/tests/sql/test_function.py @@ -116,7 +116,7 @@ def test_st_bestsrid(self): function_df = self.spark.sql("select ST_BestSRID(polygondf.countyshape) from polygondf") function_df.show() actual = function_df.take(1)[0][0] - assert actual == 1234 + assert actual == 3395 def test_st_envelope(self): polygon_from_wkt = self.spark.read.format("csv"). \ diff --git a/spark/common/src/test/scala/org/apache/sedona/sql/dataFrameAPITestScala.scala b/spark/common/src/test/scala/org/apache/sedona/sql/dataFrameAPITestScala.scala index 747cd53f11..56685f65be 100644 --- a/spark/common/src/test/scala/org/apache/sedona/sql/dataFrameAPITestScala.scala +++ b/spark/common/src/test/scala/org/apache/sedona/sql/dataFrameAPITestScala.scala @@ -223,8 +223,8 @@ class dataFrameAPITestScala extends TestBaseScala { var expected = 32701 assertEquals(expected, actual) - var linestringDf = sparkSession.sql("SELECT ST_GeomFromWKT('LINESTRING(-91.185 30.4505, -91.187 30.452, -91.189 30.4535)') AS geom") - var dfLine = linestringDf.select(ST_BestSRID("geom").as("geom")) + val linestringDf = sparkSession.sql("SELECT ST_GeomFromWKT('LINESTRING(-91.185 30.4505, -91.187 30.452, -91.189 30.4535)') AS geom") + val dfLine = linestringDf.select(ST_BestSRID("geom").as("geom")) actual = dfLine.take(1)(0).get(0).asInstanceOf[Int] expected = 32615 assertEquals(expected, actual) @@ -232,9 +232,14 @@ class dataFrameAPITestScala extends TestBaseScala { val polygonDf = sparkSession.sql("SELECT ST_GeomFromWKT('POLYGON((-120 30, -80 30, -80 50, -120 50, -120 30))') AS geom") val dfPolygon = polygonDf.select(ST_BestSRID("geom").as("geom")) actual = dfPolygon.take(1)(0).get(0).asInstanceOf[Int] - println(actual) expected = 3395 assertEquals(expected, actual) + + val polygonDf2 = sparkSession.sql("SELECT ST_GeomFromWKT('POLYGON((-73.9980 40.7265, -73.9970 40.7265, -73.9970 40.7255, -73.9980 40.7255, -73.9980 40.7265))') AS geom") + val dfPolygon2 = polygonDf2.select(ST_BestSRID("geom").as("geom")) + actual = dfPolygon2.take(1)(0).get(0).asInstanceOf[Int] + expected = 32618 + assertEquals(expected, actual) } it("Passed ST_Envelope") { From 244a3488a8a94ac97095a6999ef973c983027de8 Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Tue, 20 Feb 2024 18:54:23 -0500 Subject: [PATCH 08/54] Docs: fix typo --- docs/api/flink/Function.md | 2 +- docs/api/sql/Function.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api/flink/Function.md b/docs/api/flink/Function.md index 433d4e8ae5..c8aedf462d 100644 --- a/docs/api/flink/Function.md +++ b/docs/api/flink/Function.md @@ -485,7 +485,7 @@ Introduction: Returns the estimated most appropriate Spatial Reference Identifie - In cases where none of the above conditions are met, or for geometries that cross the International Date Line, the function defaults to the Mercator projection. !!!Warning -`ST_BestSRID` is designed to estimate a suitable SRID from a set of approximately 65 EPSG codes and works best for geometries that fit within the UTM zones. It should not be solely relied upon to determine the most accurate SRID, especially for specialized or high-precision spatial requirements. +`ST_BestSRID` is designed to estimate a suitable SRID from a set of approximately 125 EPSG codes and works best for geometries that fit within the UTM zones. It should not be solely relied upon to determine the most accurate SRID, especially for specialized or high-precision spatial requirements. Format: `ST_BestSRID(geom: Geometry)` diff --git a/docs/api/sql/Function.md b/docs/api/sql/Function.md index bdca29b425..66d8bc64fa 100644 --- a/docs/api/sql/Function.md +++ b/docs/api/sql/Function.md @@ -483,7 +483,7 @@ Introduction: Returns the estimated most appropriate Spatial Reference Identifie - In cases where none of the above conditions are met, or for geometries that cross the International Date Line, the function defaults to the Mercator projection. !!!Warning - `ST_BestSRID` is designed to estimate a suitable SRID from a set of approximately 65 EPSG codes and works best for geometries that fit within the UTM zones. It should not be solely relied upon to determine the most accurate SRID, especially for specialized or high-precision spatial requirements. + `ST_BestSRID` is designed to estimate a suitable SRID from a set of approximately 125 EPSG codes and works best for geometries that fit within the UTM zones. It should not be solely relied upon to determine the most accurate SRID, especially for specialized or high-precision spatial requirements. Format: `ST_BestSRID(geom: Geometry)` From b66dc2a10219ea495413f86065fa108cc06e3f87 Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Tue, 20 Feb 2024 19:26:04 -0500 Subject: [PATCH 09/54] Docs: add Snowflake doc --- docs/api/snowflake/vector-data/Function.md | 27 ++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/docs/api/snowflake/vector-data/Function.md b/docs/api/snowflake/vector-data/Function.md index 5f1a53ebec..1e9fac46cf 100644 --- a/docs/api/snowflake/vector-data/Function.md +++ b/docs/api/snowflake/vector-data/Function.md @@ -320,6 +320,33 @@ SELECT ST_Azimuth(ST_POINT(0.0, 25.0), ST_POINT(0.0, 0.0)) Output: `3.141592653589793` +## ST_BestSRID + +Introduction: Returns the estimated most appropriate Spatial Reference Identifier (SRID) for a given geometry, based on its spatial extent and location. It evaluates the geometry's bounding envelope and selects an SRID that optimally represents the geometry on the Earth's surface. The function prioritizes Universal Transverse Mercator (UTM), Lambert Azimuthal Equal Area (LAEA), or falls back to the Mercator projection. + +- For geometries in the Arctic or Antarctic regions, the Lambert Azimuthal Equal Area projection is used. +- For geometries that fit within a single UTM zone and do not cross the International Date Line, a corresponding UTM SRID is chosen. +- In cases where none of the above conditions are met, or for geometries that cross the International Date Line, the function defaults to the Mercator projection. + +!!!Warning +`ST_BestSRID` is designed to estimate a suitable SRID from a set of approximately 125 EPSG codes and works best for geometries that fit within the UTM zones. It should not be solely relied upon to determine the most accurate SRID, especially for specialized or high-precision spatial requirements. + +Format: `ST_BestSRID(geom: Geometry)` + +Since: `v1.6.0` + +Spark SQL Example: + +```sql +SELECT ST_BestSRID(ST_GeomFromWKT('POLYGON((-73.9980 40.7265, -73.9970 40.7265, -73.9970 40.7255, -73.9980 40.7255, -73.9980 40.7265))')) +``` + +Output: + +``` +32618 +``` + ## ST_Boundary Introduction: Returns the closure of the combinatorial boundary of this Geometry. From 12c1af6867107d8a8c744614fd555e142807a454 Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Tue, 20 Feb 2024 23:11:03 -0500 Subject: [PATCH 10/54] add flink and scala tests --- .../org/apache/sedona/common/Functions.java | 73 +++++++++++++------ .../apache/sedona/common/FunctionsTest.java | 10 +-- .../sedona/flink/expressions/Functions.java | 10 ++- .../org/apache/sedona/flink/FunctionTest.java | 8 ++ python/sedona/sql/st_functions.py | 8 +- python/tests/sql/test_function.py | 9 +++ .../sedona_sql/expressions/Functions.scala | 2 +- .../sedona_sql/expressions/st_functions.scala | 2 + 8 files changed, 90 insertions(+), 32 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 d73ca39064..a4c3b1e6b5 100644 --- a/common/src/main/java/org/apache/sedona/common/Functions.java +++ b/common/src/main/java/org/apache/sedona/common/Functions.java @@ -92,33 +92,32 @@ public static Geometry buffer(Geometry geometry, double radius) throws FactoryEx return buffer(geometry, radius, "", false); } - public static Geometry buffer(Geometry geometry, double radius, boolean spheroidal) throws FactoryException, TransformException { - return buffer(geometry, radius, "", spheroidal); + public static Geometry buffer(Geometry geometry, double radius, String params) throws FactoryException, TransformException { + return buffer(geometry, radius, params, false); } - public static Geometry buffer(Geometry geometry, double radius, String params) { - if (params.isEmpty()) { - return BufferOp.bufferOp(geometry, radius); - } + public static Geometry buffer(Geometry geometry, double radius, String params, boolean useSpheroidal) throws FactoryException, TransformException { + BufferParameters bufferParameters = new BufferParameters(); - BufferParameters bufferParameters = parseBufferParams(params); + // Processing parameters + if (!params.isEmpty()) { + bufferParameters = parseBufferParams(params); - // convert the sign to the appropriate direction - // left - radius should be positive - // right - radius should be negative - if (bufferParameters.isSingleSided() && - (params.toLowerCase().contains("left") && radius < 0 || params.toLowerCase().contains("right") && radius > 0)) { + // convert the sign to the appropriate direction + // left - radius should be positive + // right - radius should be negative + if (bufferParameters.isSingleSided() && + (params.toLowerCase().contains("left") && radius < 0 || params.toLowerCase().contains("right") && radius > 0)) { radius = -radius; + } } - return BufferOp.bufferOp(geometry, radius, bufferParameters); - } - - public static Geometry buffer(Geometry geometry, double radius, String params, boolean useSpheroidal) throws FactoryException, TransformException { if (useSpheroidal) { + // Spheroidal buffering logic + // Determine the best SRID for spheroidal calculations int bestCRS = bestSRID(geometry); - System.out.println("bestCRS: "+bestCRS); + System.out.println("bestCRS: " + bestCRS); int originalCRS = geometry.getSRID(); final int WGS84CRS = 4326; @@ -130,7 +129,7 @@ public static Geometry buffer(Geometry geometry, double radius, String params, b Geometry transformedGeometry = FunctionsGeoTools.transform(geometry, sourceCRSCode, targetCRSCode); // Apply the buffer operation in the selected SRID - Geometry bufferedGeometry = buffer(transformedGeometry, radius, params); + Geometry bufferedGeometry = BufferOp.bufferOp(transformedGeometry, radius, bufferParameters); // Transform back to the original SRID or to WGS 84 if original SRID was not set int backTransformCRSCode = (originalCRS == 0) ? WGS84CRS : originalCRS; @@ -138,10 +137,40 @@ public static Geometry buffer(Geometry geometry, double radius, String params, b bufferedResult.setSRID(backTransformCRSCode); return bufferedResult; } else { - // Existing planar buffer logic - return buffer(geometry, radius, params); - } - } + // Existing planar buffer logic with params handling + return BufferOp.bufferOp(geometry, radius, bufferParameters); + } + } + + +// public static Geometry buffer(Geometry geometry, double radius, String params, boolean useSpheroidal) throws FactoryException, TransformException { +// if (useSpheroidal) { +// // Determine the best SRID for spheroidal calculations +// int bestCRS = bestSRID(geometry); +// System.out.println("bestCRS: "+bestCRS); +// int originalCRS = geometry.getSRID(); +// final int WGS84CRS = 4326; +// +// // If originalCRS is not set, use WGS84 as the originalCRS for transformation +// String sourceCRSCode = (originalCRS == 0) ? "EPSG:" + WGS84CRS : "EPSG:" + originalCRS; +// String targetCRSCode = "EPSG:" + bestCRS; +// +// // Transform the geometry to the selected SRID +// Geometry transformedGeometry = FunctionsGeoTools.transform(geometry, sourceCRSCode, targetCRSCode); +// +// // Apply the buffer operation in the selected SRID +// Geometry bufferedGeometry = buffer(transformedGeometry, radius, params); +// +// // Transform back to the original SRID or to WGS 84 if original SRID was not set +// int backTransformCRSCode = (originalCRS == 0) ? WGS84CRS : originalCRS; +// Geometry bufferedResult = FunctionsGeoTools.transform(bufferedGeometry, targetCRSCode, "EPSG:" + backTransformCRSCode); +// bufferedResult.setSRID(backTransformCRSCode); +// return bufferedResult; +// } else { +// // Existing planar buffer logic +// return buffer(geometry, radius, params); +// } +// } private static BufferParameters parseBufferParams(String params) { diff --git a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java index 5ff7dd66d9..62c11d42d4 100644 --- a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java +++ b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java @@ -1182,11 +1182,11 @@ public void testBufferSpheroidal() throws FactoryException, TransformException, Geometry polygon3 = Constructors.geomFromWKT("POLYGON((-120 30, -80 30, -80 50, -120 50, -120 30))", 4269); - Geometry result1 = Functions.buffer(polygon1, 100, true); - Geometry result2 = Functions.buffer(polygon2, 1000, true); - Geometry result3 = Functions.buffer(point1, 100, true); - Geometry result4 = Functions.buffer(linestring1, 10, true); - Geometry result5 = Functions.buffer(polygon3, 10000, true); + Geometry result1 = Functions.buffer(polygon1, 100, "", true); + Geometry result2 = Functions.buffer(polygon2, 1000, "", true); + Geometry result3 = Functions.buffer(point1, 100, "", true); + Geometry result4 = Functions.buffer(linestring1, 10, "", true); + Geometry result5 = Functions.buffer(polygon3, 10000, "", true); Geometry expected1 = Constructors.geomFromEWKT("POLYGON ((16.248653057837092 48.249999997812616, 16.24867891451385 48.25017542568974, 16.24875549793627 48.25034411793438, 16.24887986793391 48.25049959710695, 16.249047249310912 48.25063589306857, 16.249251215157546 48.250747772240985, 16.249483933612066 48.25083093858835, 16.24973646860016 48.250882198599385, 16.249999123001867 48.25089958393145, 16.35000087730807 48.25089956809054, 16.35026352923699 48.25088218269226, 16.350516061632444 48.250830922668115, 16.350748777319566 48.25074775640202, 16.350952740192536 48.250635877460866, 16.351120118386593 48.25049958194538, 16.35124448502055 48.25034410350242, 16.35132106495956 48.25017541233756, 16.351346918125614 48.24999998594946, 16.351345606585333 48.199999998284284, 16.351319726848164 48.19982442741667, 16.351243088946557 48.199655609589996, 16.351118640921797 48.19950003769327, 16.350951169518318 48.19936369511042, 16.35074711603489 48.19925182561273, 16.350514328568075 48.199168731701455, 16.350261760179283 48.19911760915295, 16.34999912459177 48.19910042412577, 16.250000875738642 48.19910040825404, 16.24973823767017 48.1991175932147, 16.249485666681004 48.19916871575088, 16.249252876439694 48.19925180974528, 16.24904881997628 48.19936367947716, 16.248881345384483 48.19950002251011, 16.248756893991796 48.19965559514148, 16.24868025260387 48.19982441405396, 16.2486543693546 48.199999986417545, 16.248653057837092 48.249999997812616))"); diff --git a/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java b/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java index 8d172ebebe..6506df3bbc 100644 --- a/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java +++ b/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java @@ -13,6 +13,7 @@ */ package org.apache.sedona.flink.expressions; +import org.apache.calcite.runtime.Geometries; import org.apache.commons.lang3.tuple.Pair; import org.apache.flink.table.annotation.DataTypeHint; import org.apache.flink.table.annotation.InputGroup; @@ -78,10 +79,17 @@ public Geometry eval(@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.j @DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) public Geometry eval(@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) - Object o, @DataTypeHint("Double") Double radius, @DataTypeHint("String") String params) { + Object o, @DataTypeHint("Double") Double radius, @DataTypeHint("String") String params) throws FactoryException, TransformException { Geometry geom = (Geometry) o; return org.apache.sedona.common.Functions.buffer(geom, radius, params); } + + @DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) + public Geometry eval(@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) + Object o, @DataTypeHint("Double") Double radius, @DataTypeHint("String") String params, @DataTypeHint("Boolean") Boolean useSpheroidal) throws FactoryException, TransformException { + Geometry geom = (Geometry) o; + return org.apache.sedona.common.Functions.buffer(geom, radius, params, useSpheroidal); + } } public static class ST_ClosestPoint extends ScalarFunction { diff --git a/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java b/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java index 5486bd6229..45c9cb7dfb 100644 --- a/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java +++ b/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java @@ -97,6 +97,14 @@ public void testBuffer() { actual = (String) first(tableEnv.sqlQuery("SELECT ST_AsText(ST_ReducePrecision(ST_Buffer(ST_Point(100, 90), 200, 'quad_segs=4'), 4))")).getField(0); expected = "POLYGON ((284.7759 13.4633, 241.4214 -51.4214, 176.5367 -94.7759, 100 -110, 23.4633 -94.7759, -41.4214 -51.4214, -84.7759 13.4633, -100 90, -84.7759 166.5367, -41.4214 231.4214, 23.4633 274.7759, 100 290, 176.5367 274.7759, 241.4214 231.4214, 284.7759 166.5367, 300 90, 284.7759 13.4633))"; assertEquals(expected, actual); + + actual = (String) first(tableEnv.sqlQuery("SELECT ST_AsText(ST_ReducePrecision(ST_Buffer(ST_GeomFromWKT('LINESTRING(-91.185 30.4505, -91.187 30.452, -91.189 30.4535)'), 10, 'side=left', true), 4))")).getField(0); + expected = "POLYGON ((-91.187 30.452, -91.185 30.4505, -91.1851 30.4504, -91.1871 30.4519, -91.1891 30.4534, -91.189 30.4535, -91.187 30.452))"; + assertEquals(expected, actual); + + actual = (String) first(tableEnv.sqlQuery("SELECT ST_AsText(ST_ReducePrecision(ST_Buffer(ST_GeomFromWKT('POLYGON((-120 30, -80 30, -80 50, -120 50, -120 30))'), 200, 'quad_segs=4', true), 4))")).getField(0); + expected = "POLYGON ((-120.0018 50, -120.0017 50.0004, -120.0013 50.0008, -120.0007 50.0011, -120 50.0012, -80 50.0012, -79.9993 50.0011, -79.9987 50.0008, -79.9983 50.0004, -79.9982 50, -79.9982 30, -79.9983 29.9994, -79.9987 29.9989, -79.9993 29.9986, -80 29.9984, -120 29.9984, -120.0007 29.9986, -120.0013 29.9989, -120.0017 29.9994, -120.0018 30, -120.0018 50))"; + assertEquals(expected, actual); } @Test diff --git a/python/sedona/sql/st_functions.py b/python/sedona/sql/st_functions.py index aad15ea879..91d6843c64 100644 --- a/python/sedona/sql/st_functions.py +++ b/python/sedona/sql/st_functions.py @@ -317,7 +317,7 @@ def ST_Boundary(geometry: ColumnOrName) -> Column: @validate_argument_types -def ST_Buffer(geometry: ColumnOrName, buffer: ColumnOrNameOrNumber, parameters: Optional[Union[ColumnOrName, str]] = None) -> Column: +def ST_Buffer(geometry: ColumnOrName, buffer: ColumnOrNameOrNumber, parameters: Optional[Union[ColumnOrName, str]] = None, useSpheroidal: Optional[Union[ColumnOrName, bool]] = None) -> Column: """Calculate a geometry that represents all points whose distance from the input geometry column is equal to or less than a given amount. @@ -328,10 +328,12 @@ def ST_Buffer(geometry: ColumnOrName, buffer: ColumnOrNameOrNumber, parameters: :return: Buffered geometry as a geometry column. :rtype: Column """ - if parameters is None: + if parameters is None and useSpheroidal is None: args = (geometry, buffer) - else: + elif useSpheroidal is None: args = (geometry, buffer, parameters) + else: + args = (geometry, buffer, parameters, useSpheroidal) return _call_st_function("ST_Buffer", args) diff --git a/python/tests/sql/test_function.py b/python/tests/sql/test_function.py index cb141e4546..547bfcdde2 100644 --- a/python/tests/sql/test_function.py +++ b/python/tests/sql/test_function.py @@ -93,14 +93,23 @@ def test_st_buffer(self): polygon_df = self.spark.sql("select ST_GeomFromWKT(polygontable._c0) as countyshape from polygontable") polygon_df.createOrReplaceTempView("polygondf") polygon_df.show() + function_df = self.spark.sql("select ST_ReducePrecision(ST_Buffer(polygondf.countyshape, 1), 2) from polygondf") actual = function_df.take(1)[0][0].wkt assert actual == "POLYGON ((-98.02 41.77, -98.02 41.78, -98.02 41.8, -98.02 41.81, -98.02 41.82, -98.02 41.83, -98.02 41.84, -98.02 41.85, -98.02 41.86, -98.02 41.87, -98.02 41.89, -98.02 41.9, -98.02 41.91, -98.02 41.92, -98.02 41.93, -98.02 41.95, -98.02 41.98, -98.02 42, -98.02 42.01, -98.02 42.02, -98.02 42.04, -98.02 42.05, -98.02 42.07, -98.02 42.09, -98.02 42.11, -98.02 42.12, -97.99 42.31, -97.93 42.5, -97.84 42.66, -97.72 42.81, -97.57 42.93, -97.4 43.02, -97.21 43.07, -97.02 43.09, -97 43.09, -96.98 43.09, -96.97 43.09, -96.96 43.09, -96.95 43.09, -96.94 43.09, -96.92 43.09, -96.91 43.09, -96.89 43.09, -96.88 43.09, -96.86 43.09, -96.84 43.09, -96.83 43.09, -96.82 43.09, -96.81 43.09, -96.8 43.09, -96.79 43.09, -96.78 43.09, -96.76 43.09, -96.74 43.09, -96.73 43.09, -96.71 43.09, -96.7 43.09, -96.69 43.09, -96.68 43.09, -96.66 43.09, -96.65 43.09, -96.64 43.09, -96.63 43.09, -96.62 43.09, -96.61 43.09, -96.6 43.09, -96.59 43.09, -96.58 43.09, -96.57 43.09, -96.56 43.09, -96.55 43.09, -96.36 43.07, -96.17 43.01, -96 42.92, -95.86 42.8, -95.73 42.66, -95.64 42.49, -95.58 42.31, -95.56 42.12, -95.56 42.1, -95.56 42.09, -95.56 42.08, -95.56 42.07, -95.56 42.06, -95.56 42.04, -95.56 42, -95.56 41.99, -95.56 41.98, -95.56 41.97, -95.56 41.96, -95.56 41.95, -95.56 41.94, -95.56 41.93, -95.56 41.92, -95.56 41.91, -95.56 41.9, -95.56 41.89, -95.56 41.88, -95.56 41.87, -95.56 41.86, -95.56 41.85, -95.56 41.83, -95.56 41.82, -95.56 41.81, -95.56 41.8, -95.56 41.79, -95.56 41.78, -95.56 41.77, -95.56 41.76, -95.56 41.75, -95.56 41.74, -95.58 41.54, -95.63 41.36, -95.72 41.19, -95.85 41.03, -96 40.91, -96.17 40.82, -96.36 40.76, -96.55 40.74, -96.56 40.74, -96.57 40.74, -96.58 40.74, -96.59 40.74, -96.6 40.74, -96.62 40.74, -96.63 40.74, -96.64 40.74, -96.65 40.74, -96.67 40.74, -96.68 40.74, -96.69 40.74, -96.7 40.74, -96.71 40.74, -96.72 40.74, -96.73 40.74, -96.74 40.74, -96.75 40.74, -96.76 40.74, -96.77 40.74, -96.78 40.74, -96.79 40.74, -96.8 40.74, -96.81 40.74, -96.82 40.74, -96.83 40.74, -96.85 40.74, -96.86 40.74, -96.88 40.74, -96.9 40.74, -96.91 40.74, -96.92 40.74, -96.93 40.74, -96.94 40.74, -96.95 40.74, -96.97 40.74, -96.98 40.74, -96.99 40.74, -97.01 40.74, -97.02 40.74, -97.22 40.76, -97.4 40.82, -97.57 40.91, -97.72 41.03, -97.85 41.18, -97.94 41.35, -98 41.54, -98.02 41.73, -98.02 41.75, -98.02 41.76, -98.02 41.77))" + function_df = self.spark.sql("select ST_ReducePrecision(ST_Buffer(polygondf.countyshape, 1, "", true), 2) from polygondf") + actual = function_df.take(1)[0][0].wkt + assert actual == "POLYGON ((-98.02 41.77, -98.02 41.78, -98.02 41.8, -98.02 41.81, -98.02 41.82, -98.02 41.83, -98.02 41.84, -98.02 41.85, -98.02 41.86, -98.02 41.87, -98.02 41.89, -98.02 41.9, -98.02 41.91, -98.02 41.92, -98.02 41.93, -98.02 41.95, -98.02 41.98, -98.02 42, -98.02 42.01, -98.02 42.02, -98.02 42.04, -98.02 42.05, -98.02 42.07, -98.02 42.09, -98.02 42.11, -98.02 42.12, -97.99 42.31, -97.93 42.5, -97.84 42.66, -97.72 42.81, -97.57 42.93, -97.4 43.02, -97.21 43.07, -97.02 43.09, -97 43.09, -96.98 43.09, -96.97 43.09, -96.96 43.09, -96.95 43.09, -96.94 43.09, -96.92 43.09, -96.91 43.09, -96.89 43.09, -96.88 43.09, -96.86 43.09, -96.84 43.09, -96.83 43.09, -96.82 43.09, -96.81 43.09, -96.8 43.09, -96.79 43.09, -96.78 43.09, -96.76 43.09, -96.74 43.09, -96.73 43.09, -96.71 43.09, -96.7 43.09, -96.69 43.09, -96.68 43.09, -96.66 43.09, -96.65 43.09, -96.64 43.09, -96.63 43.09, -96.62 43.09, -96.61 43.09, -96.6 43.09, -96.59 43.09, -96.58 43.09, -96.57 43.09, -96.56 43.09, -96.55 43.09, -96.36 43.07, -96.17 43.01, -96 42.92, -95.86 42.8, -95.73 42.66, -95.64 42.49, -95.58 42.31, -95.56 42.12, -95.56 42.1, -95.56 42.09, -95.56 42.08, -95.56 42.07, -95.56 42.06, -95.56 42.04, -95.56 42, -95.56 41.99, -95.56 41.98, -95.56 41.97, -95.56 41.96, -95.56 41.95, -95.56 41.94, -95.56 41.93, -95.56 41.92, -95.56 41.91, -95.56 41.9, -95.56 41.89, -95.56 41.88, -95.56 41.87, -95.56 41.86, -95.56 41.85, -95.56 41.83, -95.56 41.82, -95.56 41.81, -95.56 41.8, -95.56 41.79, -95.56 41.78, -95.56 41.77, -95.56 41.76, -95.56 41.75, -95.56 41.74, -95.58 41.54, -95.63 41.36, -95.72 41.19, -95.85 41.03, -96 40.91, -96.17 40.82, -96.36 40.76, -96.55 40.74, -96.56 40.74, -96.57 40.74, -96.58 40.74, -96.59 40.74, -96.6 40.74, -96.62 40.74, -96.63 40.74, -96.64 40.74, -96.65 40.74, -96.67 40.74, -96.68 40.74, -96.69 40.74, -96.7 40.74, -96.71 40.74, -96.72 40.74, -96.73 40.74, -96.74 40.74, -96.75 40.74, -96.76 40.74, -96.77 40.74, -96.78 40.74, -96.79 40.74, -96.8 40.74, -96.81 40.74, -96.82 40.74, -96.83 40.74, -96.85 40.74, -96.86 40.74, -96.88 40.74, -96.9 40.74, -96.91 40.74, -96.92 40.74, -96.93 40.74, -96.94 40.74, -96.95 40.74, -96.97 40.74, -96.98 40.74, -96.99 40.74, -97.01 40.74, -97.02 40.74, -97.22 40.76, -97.4 40.82, -97.57 40.91, -97.72 41.03, -97.85 41.18, -97.94 41.35, -98 41.54, -98.02 41.73, -98.02 41.75, -98.02 41.76, -98.02 41.77))" + function_df = self.spark.sql("select ST_ReducePrecision(ST_Buffer(polygondf.countyshape, 10, 'endcap=square'), 2) from polygondf") actual = function_df.take(1)[0][0].wkt assert actual == "POLYGON ((-107.02 42.06, -107.02 42.07, -107.02 42.09, -107.02 42.11, -107.02 42.32, -107.02 42.33, -107.01 42.42, -107.01 42.43, -106.77 44.33, -106.16 46.15, -105.22 47.82, -103.98 49.27, -102.48 50.47, -100.78 51.36, -98.94 51.9, -97.04 52.09, -97.03 52.09, -97.01 52.09, -96.95 52.09, -96.9 52.09, -96.81 52.09, -96.7 52.09, -96.68 52.09, -96.65 52.09, -96.55 52.09, -96.54 52.09, -96.49 52.09, -96.48 52.09, -94.58 51.89, -92.74 51.33, -91.04 50.43, -89.55 49.23, -88.32 47.76, -87.39 46.08, -86.79 44.25, -86.56 42.35, -86.56 42.18, -86.56 42.17, -86.56 42.1, -86.55 41.99, -86.56 41.9, -86.56 41.78, -86.56 41.77, -86.56 41.75, -86.56 41.73, -86.56 41.7, -86.75 39.76, -87.33 37.89, -88.25 36.17, -89.49 34.66, -91 33.43, -92.72 32.51, -94.59 31.94, -96.53 31.74, -96.55 31.74, -96.56 31.74, -96.57 31.74, -96.58 31.74, -96.6 31.74, -96.69 31.74, -96.72 31.74, -96.75 31.74, -96.94 31.74, -97.02 31.74, -97.04 31.74, -97.06 31.74, -98.99 31.94, -100.85 32.5, -102.56 33.42, -104.07 34.65, -105.31 36.14, -106.23 37.85, -106.81 39.71, -107.02 41.64, -107.02 41.75, -107.02 41.94, -107.02 41.96, -107.02 41.99, -107.02 42.01, -107.02 42.02, -107.02 42.03, -107.02 42.06))" + function_df = self.spark.sql("select ST_ReducePrecision(ST_Buffer(polygondf.countyshape, 10, 'endcap=square', true), 2) from polygondf") + actual = function_df.take(1)[0][0].wkt + assert actual == "POLYGON ((-107.02 42.06, -107.02 42.07, -107.02 42.09, -107.02 42.11, -107.02 42.32, -107.02 42.33, -107.01 42.42, -107.01 42.43, -106.77 44.33, -106.16 46.15, -105.22 47.82, -103.98 49.27, -102.48 50.47, -100.78 51.36, -98.94 51.9, -97.04 52.09, -97.03 52.09, -97.01 52.09, -96.95 52.09, -96.9 52.09, -96.81 52.09, -96.7 52.09, -96.68 52.09, -96.65 52.09, -96.55 52.09, -96.54 52.09, -96.49 52.09, -96.48 52.09, -94.58 51.89, -92.74 51.33, -91.04 50.43, -89.55 49.23, -88.32 47.76, -87.39 46.08, -86.79 44.25, -86.56 42.35, -86.56 42.18, -86.56 42.17, -86.56 42.1, -86.55 41.99, -86.56 41.9, -86.56 41.78, -86.56 41.77, -86.56 41.75, -86.56 41.73, -86.56 41.7, -86.75 39.76, -87.33 37.89, -88.25 36.17, -89.49 34.66, -91 33.43, -92.72 32.51, -94.59 31.94, -96.53 31.74, -96.55 31.74, -96.56 31.74, -96.57 31.74, -96.58 31.74, -96.6 31.74, -96.69 31.74, -96.72 31.74, -96.75 31.74, -96.94 31.74, -97.02 31.74, -97.04 31.74, -97.06 31.74, -98.99 31.94, -100.85 32.5, -102.56 33.42, -104.07 34.65, -105.31 36.14, -106.23 37.85, -106.81 39.71, -107.02 41.64, -107.02 41.75, -107.02 41.94, -107.02 41.96, -107.02 41.99, -107.02 42.01, -107.02 42.02, -107.02 42.03, -107.02 42.06))" + def test_st_envelope(self): polygon_from_wkt = self.spark.read.format("csv"). \ option("delimiter", "\t"). \ diff --git a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala index 3ebf42b910..9271527e3c 100644 --- a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala +++ b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala @@ -153,7 +153,7 @@ case class ST_NDims(inputExpressions: Seq[Expression]) * @param inputExpressions */ case class ST_Buffer(inputExpressions: Seq[Expression]) - extends InferredExpression(inferrableFunction2(Functions.buffer), inferrableFunction3(Functions.buffer)) { + extends InferredExpression(inferrableFunction2(Functions.buffer), inferrableFunction3(Functions.buffer), inferrableFunction4(Functions.buffer)) { protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = { copy(inputExpressions = newChildren) diff --git a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala index 957c2483ee..86fecbc133 100644 --- a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala +++ b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala @@ -68,6 +68,8 @@ object st_functions extends DataFrameAPI { def ST_Buffer(geometry: String, buffer: Double): Column = wrapExpression[ST_Buffer](geometry, buffer) def ST_Buffer(geometry: Column, buffer: Column, parameters: Column): Column = wrapExpression[ST_Buffer](geometry, buffer, parameters) def ST_Buffer(geometry: String, buffer: Double, parameters: String): Column = wrapExpression[ST_Buffer](geometry, buffer, parameters) + def ST_Buffer(geometry: Column, buffer: Column, parameters: Column, useSpheroidal: Column): Column = wrapExpression[ST_Buffer](geometry, buffer, parameters, useSpheroidal) + def ST_Buffer(geometry: String, buffer: Double, parameters: String, useSpheroidal: Boolean): Column = wrapExpression[ST_Buffer](geometry, buffer, parameters, useSpheroidal) def ST_BuildArea(geometry: Column): Column = wrapExpression[ST_BuildArea](geometry) def ST_BuildArea(geometry: String): Column = wrapExpression[ST_BuildArea](geometry) From 600ebe1e45ea6ff7584044fca076309a85857b28 Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Tue, 20 Feb 2024 23:13:59 -0500 Subject: [PATCH 11/54] Docs: Fix indentation --- docs/api/flink/Function.md | 2 +- docs/api/snowflake/vector-data/Function.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api/flink/Function.md b/docs/api/flink/Function.md index c8aedf462d..cda88f66af 100644 --- a/docs/api/flink/Function.md +++ b/docs/api/flink/Function.md @@ -485,7 +485,7 @@ Introduction: Returns the estimated most appropriate Spatial Reference Identifie - In cases where none of the above conditions are met, or for geometries that cross the International Date Line, the function defaults to the Mercator projection. !!!Warning -`ST_BestSRID` is designed to estimate a suitable SRID from a set of approximately 125 EPSG codes and works best for geometries that fit within the UTM zones. It should not be solely relied upon to determine the most accurate SRID, especially for specialized or high-precision spatial requirements. + `ST_BestSRID` is designed to estimate a suitable SRID from a set of approximately 125 EPSG codes and works best for geometries that fit within the UTM zones. It should not be solely relied upon to determine the most accurate SRID, especially for specialized or high-precision spatial requirements. Format: `ST_BestSRID(geom: Geometry)` diff --git a/docs/api/snowflake/vector-data/Function.md b/docs/api/snowflake/vector-data/Function.md index 1e9fac46cf..cd6c40e9c8 100644 --- a/docs/api/snowflake/vector-data/Function.md +++ b/docs/api/snowflake/vector-data/Function.md @@ -329,7 +329,7 @@ Introduction: Returns the estimated most appropriate Spatial Reference Identifie - In cases where none of the above conditions are met, or for geometries that cross the International Date Line, the function defaults to the Mercator projection. !!!Warning -`ST_BestSRID` is designed to estimate a suitable SRID from a set of approximately 125 EPSG codes and works best for geometries that fit within the UTM zones. It should not be solely relied upon to determine the most accurate SRID, especially for specialized or high-precision spatial requirements. + `ST_BestSRID` is designed to estimate a suitable SRID from a set of approximately 125 EPSG codes and works best for geometries that fit within the UTM zones. It should not be solely relied upon to determine the most accurate SRID, especially for specialized or high-precision spatial requirements. Format: `ST_BestSRID(geom: Geometry)` From c59b8b9f3a166c6568e920c6e80cf80e3979d25b Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Wed, 21 Feb 2024 00:57:09 -0500 Subject: [PATCH 12/54] Fix Snowflake test case --- .../java/org/apache/sedona/snowflake/snowsql/TestFunctions.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java index a40050e9ca..c188ea70b7 100644 --- a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java +++ b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java @@ -198,7 +198,7 @@ public void test_ST_BestSRID() { verifySQLSingleRes( "select sedona.ST_AsText(sedona.ST_BestSRID(sedona.ST_GeomFromText('POINT (-180 60)')))", "32660" - ) + ); } @Test public void test_ST_BuildArea() { From 1ece852a8c550b8e496265885755803ec9944480 Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Wed, 21 Feb 2024 03:48:01 -0500 Subject: [PATCH 13/54] Fix Snowflake compile issue; Update doc --- docs/api/flink/Function.md | 5 +++-- docs/api/snowflake/vector-data/Function.md | 5 +++-- docs/api/sql/Function.md | 5 +++-- .../apache/sedona/snowflake/snowsql/TestFunctions.java | 7 ++++--- .../apache/sedona/snowflake/snowsql/TestFunctionsV2.java | 8 ++++++++ .../java/org/apache/sedona/snowflake/snowsql/UDFsV2.java | 2 +- 6 files changed, 22 insertions(+), 10 deletions(-) diff --git a/docs/api/flink/Function.md b/docs/api/flink/Function.md index cda88f66af..8a26fd8a84 100644 --- a/docs/api/flink/Function.md +++ b/docs/api/flink/Function.md @@ -481,8 +481,9 @@ Output: Introduction: Returns the estimated most appropriate Spatial Reference Identifier (SRID) for a given geometry, based on its spatial extent and location. It evaluates the geometry's bounding envelope and selects an SRID that optimally represents the geometry on the Earth's surface. The function prioritizes Universal Transverse Mercator (UTM), Lambert Azimuthal Equal Area (LAEA), or falls back to the Mercator projection. - For geometries in the Arctic or Antarctic regions, the Lambert Azimuthal Equal Area projection is used. -- For geometries that fit within a single UTM zone and do not cross the International Date Line, a corresponding UTM SRID is chosen. -- In cases where none of the above conditions are met, or for geometries that cross the International Date Line, the function defaults to the Mercator projection. +- For geometries that fit within a single UTM zone and do not cross the International Date Line (IDL), a corresponding UTM SRID is chosen. +- In cases where none of the above conditions are met, the function defaults to the Mercator projection. +- For Geometries that cross the IDL, `ST_BestSRID` defaults the SRID to Mercator. Currently, `ST_BestSRID` does not handle geometries crossing the IDL. !!!Warning `ST_BestSRID` is designed to estimate a suitable SRID from a set of approximately 125 EPSG codes and works best for geometries that fit within the UTM zones. It should not be solely relied upon to determine the most accurate SRID, especially for specialized or high-precision spatial requirements. diff --git a/docs/api/snowflake/vector-data/Function.md b/docs/api/snowflake/vector-data/Function.md index cd6c40e9c8..35b28496f2 100644 --- a/docs/api/snowflake/vector-data/Function.md +++ b/docs/api/snowflake/vector-data/Function.md @@ -325,8 +325,9 @@ Output: `3.141592653589793` Introduction: Returns the estimated most appropriate Spatial Reference Identifier (SRID) for a given geometry, based on its spatial extent and location. It evaluates the geometry's bounding envelope and selects an SRID that optimally represents the geometry on the Earth's surface. The function prioritizes Universal Transverse Mercator (UTM), Lambert Azimuthal Equal Area (LAEA), or falls back to the Mercator projection. - For geometries in the Arctic or Antarctic regions, the Lambert Azimuthal Equal Area projection is used. -- For geometries that fit within a single UTM zone and do not cross the International Date Line, a corresponding UTM SRID is chosen. -- In cases where none of the above conditions are met, or for geometries that cross the International Date Line, the function defaults to the Mercator projection. +- For geometries that fit within a single UTM zone and do not cross the International Date Line (IDL), a corresponding UTM SRID is chosen. +- In cases where none of the above conditions are met, the function defaults to the Mercator projection. +- For Geometries that cross the IDL, `ST_BestSRID` defaults the SRID to Mercator. Currently, `ST_BestSRID` does not handle geometries crossing the IDL. !!!Warning `ST_BestSRID` is designed to estimate a suitable SRID from a set of approximately 125 EPSG codes and works best for geometries that fit within the UTM zones. It should not be solely relied upon to determine the most accurate SRID, especially for specialized or high-precision spatial requirements. diff --git a/docs/api/sql/Function.md b/docs/api/sql/Function.md index 66d8bc64fa..4043bc872b 100644 --- a/docs/api/sql/Function.md +++ b/docs/api/sql/Function.md @@ -479,8 +479,9 @@ Output: Introduction: Returns the estimated most appropriate Spatial Reference Identifier (SRID) for a given geometry, based on its spatial extent and location. It evaluates the geometry's bounding envelope and selects an SRID that optimally represents the geometry on the Earth's surface. The function prioritizes Universal Transverse Mercator (UTM), Lambert Azimuthal Equal Area (LAEA), or falls back to the Mercator projection. - For geometries in the Arctic or Antarctic regions, the Lambert Azimuthal Equal Area projection is used. -- For geometries that fit within a single UTM zone and do not cross the International Date Line, a corresponding UTM SRID is chosen. -- In cases where none of the above conditions are met, or for geometries that cross the International Date Line, the function defaults to the Mercator projection. +- For geometries that fit within a single UTM zone and do not cross the International Date Line (IDL), a corresponding UTM SRID is chosen. +- In cases where none of the above conditions are met, the function defaults to the Mercator projection. +- For Geometries that cross the IDL, `ST_BestSRID` defaults the SRID to Mercator. Currently, `ST_BestSRID` does not handle geometries crossing the IDL. !!!Warning `ST_BestSRID` is designed to estimate a suitable SRID from a set of approximately 125 EPSG codes and works best for geometries that fit within the UTM zones. It should not be solely relied upon to determine the most accurate SRID, especially for specialized or high-precision spatial requirements. diff --git a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java index c188ea70b7..25b298cf4f 100644 --- a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java +++ b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java @@ -195,11 +195,12 @@ public void test_ST_Buffer() { @Test public void test_ST_BestSRID() { registerUDF("ST_BestSRID", byte[].class); - verifySQLSingleRes( - "select sedona.ST_AsText(sedona.ST_BestSRID(sedona.ST_GeomFromText('POINT (-180 60)')))", - "32660" + verifySqlSingleRes( + "select sedona.ST_BestSRID(sedona.ST_GeomFromText('POINT (-180 60)'))", + 32660 ); } + @Test public void test_ST_BuildArea() { registerUDF("ST_BuildArea", byte[].class); diff --git a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java index a671d76693..d33790e04e 100644 --- a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java +++ b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java @@ -170,6 +170,14 @@ public void test_ST_Azimuth() { ); } @Test + public void test_ST_BestSRID() { + registerUDFV2("ST_BestSRID", String.class); + verifySqlSingleRes( + "select sedona.ST_BestSRID(sedona.ST_GeomFromText('POINT (-180 60)'))", + 32660 + ); + } + @Test public void test_ST_Boundary() { registerUDFV2("ST_Boundary", String.class); verifySqlSingleRes( diff --git a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java index e8444784cc..ab0abdc0dc 100644 --- a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java +++ b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java @@ -172,7 +172,7 @@ public static String ST_BoundingDiagonal(String geometry) { } @UDFAnnotations.ParamMeta(argNames = {"geometry"}, argTypes = {"Geometry"}) - public static Integer ST_BestSRID(String geometry) { + public static int ST_BestSRID(String geometry) { return Functions.bestSRID( GeometrySerde.deserGeoJson(geometry) ); From 6e8b513df643e82fdf10e43530f25813c5139992 Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Wed, 21 Feb 2024 14:37:16 -0500 Subject: [PATCH 14/54] refactor snowflake test --- .../org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java index d33790e04e..7bb16fc222 100644 --- a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java +++ b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java @@ -169,14 +169,16 @@ public void test_ST_Azimuth() { 240.0133139011053 * Math.PI / 180 ); } + @Test public void test_ST_BestSRID() { registerUDFV2("ST_BestSRID", String.class); verifySqlSingleRes( - "select sedona.ST_BestSRID(sedona.ST_GeomFromText('POINT (-180 60)'))", + "select sedona.ST_BestSRID(ST_GeometryFromWKT('POINT (-180 60)'))", 32660 ); } + @Test public void test_ST_Boundary() { registerUDFV2("ST_Boundary", String.class); From c1d8b676828bf10d1dc472768f856b870881b63f Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Wed, 21 Feb 2024 15:16:47 -0500 Subject: [PATCH 15/54] Docs: update --- docs/api/flink/Function.md | 4 ++-- docs/api/snowflake/vector-data/Function.md | 4 ++-- docs/api/sql/Function.md | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/api/flink/Function.md b/docs/api/flink/Function.md index 8a26fd8a84..940f3a722c 100644 --- a/docs/api/flink/Function.md +++ b/docs/api/flink/Function.md @@ -478,7 +478,7 @@ Output: ## ST_BestSRID -Introduction: Returns the estimated most appropriate Spatial Reference Identifier (SRID) for a given geometry, based on its spatial extent and location. It evaluates the geometry's bounding envelope and selects an SRID that optimally represents the geometry on the Earth's surface. The function prioritizes Universal Transverse Mercator (UTM), Lambert Azimuthal Equal Area (LAEA), or falls back to the Mercator projection. +Introduction: Returns the estimated most appropriate Spatial Reference Identifier (SRID) for a given geometry, based on its spatial extent and location. It evaluates the geometry's bounding envelope and selects an SRID that optimally represents the geometry on the Earth's surface. The function prioritizes Universal Transverse Mercator (UTM), Lambert Azimuthal Equal Area (LAEA), or falls back to the Mercator projection. The function takes a WGS84 geometry and must be in ==lon/lat== order. - For geometries in the Arctic or Antarctic regions, the Lambert Azimuthal Equal Area projection is used. - For geometries that fit within a single UTM zone and do not cross the International Date Line (IDL), a corresponding UTM SRID is chosen. @@ -492,7 +492,7 @@ Format: `ST_BestSRID(geom: Geometry)` Since: `v1.6.0` -Spark SQL Example: +SQL Example: ```sql SELECT ST_BestSRID(ST_GeomFromWKT('POLYGON((-73.9980 40.7265, -73.9970 40.7265, -73.9970 40.7255, -73.9980 40.7255, -73.9980 40.7265))')) diff --git a/docs/api/snowflake/vector-data/Function.md b/docs/api/snowflake/vector-data/Function.md index 35b28496f2..acbc52f8be 100644 --- a/docs/api/snowflake/vector-data/Function.md +++ b/docs/api/snowflake/vector-data/Function.md @@ -322,7 +322,7 @@ Output: `3.141592653589793` ## ST_BestSRID -Introduction: Returns the estimated most appropriate Spatial Reference Identifier (SRID) for a given geometry, based on its spatial extent and location. It evaluates the geometry's bounding envelope and selects an SRID that optimally represents the geometry on the Earth's surface. The function prioritizes Universal Transverse Mercator (UTM), Lambert Azimuthal Equal Area (LAEA), or falls back to the Mercator projection. +Introduction: Returns the estimated most appropriate Spatial Reference Identifier (SRID) for a given geometry, based on its spatial extent and location. It evaluates the geometry's bounding envelope and selects an SRID that optimally represents the geometry on the Earth's surface. The function prioritizes Universal Transverse Mercator (UTM), Lambert Azimuthal Equal Area (LAEA), or falls back to the Mercator projection. The function takes a WGS84 geometry and must be in ==lon/lat== order. - For geometries in the Arctic or Antarctic regions, the Lambert Azimuthal Equal Area projection is used. - For geometries that fit within a single UTM zone and do not cross the International Date Line (IDL), a corresponding UTM SRID is chosen. @@ -336,7 +336,7 @@ Format: `ST_BestSRID(geom: Geometry)` Since: `v1.6.0` -Spark SQL Example: +SQL Example: ```sql SELECT ST_BestSRID(ST_GeomFromWKT('POLYGON((-73.9980 40.7265, -73.9970 40.7265, -73.9970 40.7255, -73.9980 40.7255, -73.9980 40.7265))')) diff --git a/docs/api/sql/Function.md b/docs/api/sql/Function.md index 4043bc872b..a3d6f080be 100644 --- a/docs/api/sql/Function.md +++ b/docs/api/sql/Function.md @@ -476,7 +476,7 @@ Output: ## ST_BestSRID -Introduction: Returns the estimated most appropriate Spatial Reference Identifier (SRID) for a given geometry, based on its spatial extent and location. It evaluates the geometry's bounding envelope and selects an SRID that optimally represents the geometry on the Earth's surface. The function prioritizes Universal Transverse Mercator (UTM), Lambert Azimuthal Equal Area (LAEA), or falls back to the Mercator projection. +Introduction: Returns the estimated most appropriate Spatial Reference Identifier (SRID) for a given geometry, based on its spatial extent and location. It evaluates the geometry's bounding envelope and selects an SRID that optimally represents the geometry on the Earth's surface. The function prioritizes Universal Transverse Mercator (UTM), Lambert Azimuthal Equal Area (LAEA), or falls back to the Mercator projection. The function takes a WGS84 geometry and must be in ==lon/lat== order. - For geometries in the Arctic or Antarctic regions, the Lambert Azimuthal Equal Area projection is used. - For geometries that fit within a single UTM zone and do not cross the International Date Line (IDL), a corresponding UTM SRID is chosen. @@ -490,7 +490,7 @@ Format: `ST_BestSRID(geom: Geometry)` Since: `v1.6.0` -Spark SQL Example: +SQL Example: ```sql SELECT ST_BestSRID(ST_GeomFromWKT('POLYGON((-73.9980 40.7265, -73.9970 40.7265, -73.9970 40.7255, -73.9980 40.7255, -73.9980 40.7265))')) From fe685fda50bcb31820aaf2b5214d4ff2951fae13 Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Wed, 21 Feb 2024 19:12:32 -0500 Subject: [PATCH 16/54] Update docs; Add python tests --- .../apache/sedona/common/FunctionsTest.java | 60 ------------------- docs/api/flink/Function.md | 21 ++++++- docs/api/sql/Function.md | 15 +++++ python/tests/sql/test_dataframe_api.py | 1 + .../apache/sedona/snowflake/snowsql/UDFs.java | 4 +- .../sedona/snowflake/snowsql/UDFsV2.java | 4 +- .../apache/sedona/sql/functionTestScala.scala | 9 +++ 7 files changed, 49 insertions(+), 65 deletions(-) diff --git a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java index 282021ef44..5365d55d3a 100644 --- a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java +++ b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java @@ -1253,66 +1253,6 @@ private static double comparePolygons(Geometry p1, Geometry p2) { return maxDistance; } - @Test - public void testBestSRID() { - int[][] testCases = { - {0, -70, 3409}, // EPSG:3409 (Antarctic Polar Stereographic) - {0, 70, 3574}, // EPSG:3575 (North Pole LAEA Alaska) - // UTM zones for Northern Hemisphere - {-177, 60, 32601}, {-171, 60, 32602}, {-165, 60, 32603}, {-159, 60, 32604}, {-153, 60, 32605}, - {-147, 60, 32606}, {-141, 60, 32607}, {-135, 60, 32608}, {-129, 60, 32609}, {-123, 60, 32610}, - {-117, 60, 32611}, {-111, 60, 32612}, {-105, 60, 32613}, {-99, 60, 32614}, {-93, 60, 32615}, - {-87, 60, 32616}, {-81, 60, 32617}, {-75, 60, 32618}, {-69, 60, 32619}, {-63, 60, 32620}, - {-57, 60, 32621}, {-51, 60, 32622}, {-45, 60, 32623}, {-39, 60, 32624}, {-33, 60, 32625}, - {-27, 60, 32626}, {-21, 60, 32627}, {-15, 60, 32628}, {-9, 60, 32629}, {-3, 60, 32630}, - {3, 60, 32631}, {9, 60, 32632}, {15, 60, 32633}, {21, 60, 32634}, {27, 60, 32635}, - {33, 60, 32636}, {39, 60, 32637}, {45, 60, 32638}, {51, 60, 32639}, {57, 60, 32640}, - {63, 60, 32641}, {69, 60, 32642}, {75, 60, 32643}, {81, 60, 32644}, {87, 60, 32645}, - {93, 60, 32646}, {99, 60, 32647}, {105, 60, 32648}, {111, 60, 32649}, {117, 60, 32650}, - {123, 60, 32651}, {129, 60, 32652}, {135, 60, 32653}, {141, 60, 32654}, {147, 60, 32655}, - {153, 60, 32656}, {159, 60, 32657}, {165, 60, 32658}, {171, 60, 32659}, {177, 60, 32660}, - // UTM zones for Southern Hemisphere - {-177, -60, 32701}, {-171, -60, 32702}, {-165, -60, 32703}, {-159, -60, 32704}, {-153, -60, 32705}, - {-147, -60, 32706}, {-141, -60, 32707}, {-135, -60, 32708}, {-129, -60, 32709}, {-123, -60, 32710}, - {-117, -60, 32711}, {-111, -60, 32712}, {-105, -60, 32713}, {-99, -60, 32714}, {-93, -60, 32715}, - {-87, -60, 32716}, {-81, -60, 32717}, {-75, -60, 32718}, {-69, -60, 32719}, {-63, -60, 32720}, - {-57, -60, 32721}, {-51, -60, 32722}, {-45, -60, 32723}, {-39, -60, 32724}, {-33, -60, 32725}, - {-27, -60, 32726}, {-21, -60, 32727}, {-15, -60, 32728}, {-9, -60, 32729}, {-3, -60, 32730}, - {3, -60, 32731}, {9, -60, 32732}, {15, -60, 32733}, {21, -60, 32734}, {27, -60, 32735}, {33, -60, 32736}, - {39, -60, 32737}, {45, -60, 32738}, {51, -60, 32739}, {57, -60, 32740}, {63, -60, 32741}, {69, -60, 32742}, - {75, -60, 32743}, {81, -60, 32744}, {87, -60, 32745}, {93, -60, 32746}, {99, -60, 32747}, {105, -60, 32748}, - {111, -60, 32749}, {117, -60, 32750}, {123, -60, 32751}, {129, -60, 32752}, {135, -60, 32753}, {141, -60, 32754}, - {147, -60, 32755}, {153, -60, 32756}, {159, -60, 32757}, {165, -60, 32758}, {171, -60, 32759}, {177, -60, 32760}, - // Special cases - {-180, 60, 32660}, {180, 60, 32660}, {-180, -60, 32760}, {180, -60, 32760}, - }; - - - for (int[] testCase : testCases) { - int x = testCase[0]; - int y = testCase[1]; - int expectedEPSG = testCase[2]; - - Geometry geom = GEOMETRY_FACTORY.createPoint(new Coordinate(x, y)); - int actualEPSG = Functions.bestSRID(geom); - - assertEquals("Failed at coordinates (" + x + ", " + y + ")", expectedEPSG, actualEPSG); - } - - // Large geometry that does not fit into UTM or polar categories - Geometry geom = GEOMETRY_FACTORY.createPolygon(new Coordinate[] { - new Coordinate(-160, -40), - new Coordinate(-160, 40), - new Coordinate(160, 40), - new Coordinate(160, -40), - new Coordinate(-160, -40) - }); - int expectedEPSG = 3395; // EPSG code for World Mercator - int actualEPSG = Functions.bestSRID(geom); - - assertEquals("Expected World Mercator projection for wide range geometry", expectedEPSG, actualEPSG); - } - @Test public void testBestSRID() { int[][] testCases_special = { diff --git a/docs/api/flink/Function.md b/docs/api/flink/Function.md index 940f3a722c..3b305f6783 100644 --- a/docs/api/flink/Function.md +++ b/docs/api/flink/Function.md @@ -555,6 +555,8 @@ Output: `LINESTRING Z(-1 -1 0, 10 5 5)` Introduction: Returns a geometry/geography that represents all points whose distance from this Geometry/geography is less than or equal to distance. +Buffer Style Parameters: + The optional third parameter controls the buffer accuracy and style. Buffer accuracy is specified by the number of line segments approximating a quarter circle, with a default of 8 segments. Buffer style can be set by providing blank-separated key=value pairs in a list format. - `quad_segs=#` : Number of line segments utilized to approximate a quarter circle (default is 8). @@ -563,18 +565,31 @@ The optional third parameter controls the buffer accuracy and style. Buffer accu - `mitre_limit=#.#` : mitre ratio limit and it only affects mitred join style. `miter_limit` is an accepted synonym for `mitre_limit`. - `side=both|left|right` : The option `left` or `right` enables a single-sided buffer operation on the geometry, with the buffered side aligned according to the direction of the line. This functionality is specific to LINESTRING geometry and has no impact on POINT or POLYGON geometries. By default, square end caps are applied. +Mode of buffer calculation: + +- Planar Buffering (default): When `useSpheroid` is false, ST_Buffer performs standard planar buffering based on the provided parameters. +- Spheroidal Buffering: + - When `useSpheroid` is set to true, the function performs spheroidal buffering for more accurate representation over the Earth's spheroid. + - ST_Buffer first determines the best SRID for spheroidal calculations by using ST_BestSRID, which chooses an SRID that optimally represents the geometry on the Earth's surface. + - If the original SRID of the input geometry is not set, `ST_Buffer` defaults to using WGS 84 (SRID 4326) as the baseline for transformations. + - The geometry is then transformed to the selected SRID, and the buffer operation is applied in this coordinate system. + - Finally, the buffered geometry is transformed back to its original SRID, or to WGS 84 if the original SRID was not set. + !!!note - `ST_Buffer` throws an `IllegalArgumentException` if the correct format, parameters, or options are not provided. +`ST_Buffer` throws an `IllegalArgumentException` if the correct format, parameters, or options are not provided. Format: ``` ST_Buffer (A: Geometry, buffer: Double, bufferStyleParameters: String [Optional]) ``` +``` +ST_Buffer (A: Geometry, buffer: Double, bufferStyleParameters: String [Optional], useSperoidal: Boolean) +``` Since: `v1.5.1` -Example: +Spark SQL Example: ```sql SELECT ST_Buffer(ST_GeomFromWKT('POINT(0 0)'), 10) @@ -588,7 +603,7 @@ Output: 8 Segments   2 Segments -Example: +Spark SQL Example: ```sql SELECT ST_Buffer(ST_GeomFromWKT('LINESTRING(0 0, 50 70, 100 100)'), 10, 'side=left') diff --git a/docs/api/sql/Function.md b/docs/api/sql/Function.md index a3d6f080be..9edad687d2 100644 --- a/docs/api/sql/Function.md +++ b/docs/api/sql/Function.md @@ -553,6 +553,8 @@ Output: `LINESTRING Z(-1 -1 0, 10 5 5)` Introduction: Returns a geometry/geography that represents all points whose distance from this Geometry/geography is less than or equal to distance. +Buffer Style Parameters: + The optional third parameter controls the buffer accuracy and style. Buffer accuracy is specified by the number of line segments approximating a quarter circle, with a default of 8 segments. Buffer style can be set by providing blank-separated key=value pairs in a list format. - `quad_segs=#` : Number of line segments utilized to approximate a quarter circle (default is 8). @@ -561,6 +563,16 @@ The optional third parameter controls the buffer accuracy and style. Buffer accu - `mitre_limit=#.#` : mitre ratio limit and it only affects mitred join style. `miter_limit` is an accepted synonym for `mitre_limit`. - `side=both|left|right` : The option `left` or `right` enables a single-sided buffer operation on the geometry, with the buffered side aligned according to the direction of the line. This functionality is specific to LINESTRING geometry and has no impact on POINT or POLYGON geometries. By default, square end caps are applied. +Mode of buffer calculation: + +- Planar Buffering (default): When `useSpheroid` is false, ST_Buffer performs standard planar buffering based on the provided parameters. +- Spheroidal Buffering: + - When `useSpheroid` is set to true, the function performs spheroidal buffering for more accurate representation over the Earth's spheroid. + - ST_Buffer first determines the best SRID for spheroidal calculations by using ST_BestSRID, which chooses an SRID that optimally represents the geometry on the Earth's surface. + - If the original SRID of the input geometry is not set, `ST_Buffer` defaults to using WGS 84 (SRID 4326) as the baseline for transformations. + - The geometry is then transformed to the selected SRID, and the buffer operation is applied in this coordinate system. + - Finally, the buffered geometry is transformed back to its original SRID, or to WGS 84 if the original SRID was not set. + !!!note `ST_Buffer` throws an `IllegalArgumentException` if the correct format, parameters, or options are not provided. @@ -569,6 +581,9 @@ Format: ``` ST_Buffer (A: Geometry, buffer: Double, bufferStyleParameters: String [Optional]) ``` +``` +ST_Buffer (A: Geometry, buffer: Double, bufferStyleParameters: String [Optional], useSperoidal: Boolean) +``` Since: `v1.5.1` diff --git a/python/tests/sql/test_dataframe_api.py b/python/tests/sql/test_dataframe_api.py index be8278c727..0917bb8887 100644 --- a/python/tests/sql/test_dataframe_api.py +++ b/python/tests/sql/test_dataframe_api.py @@ -74,6 +74,7 @@ (stf.ST_BestSRID, ("geom",), "triangle_geom", "", 3395), (stf.ST_Boundary, ("geom",), "triangle_geom", "", "LINESTRING (0 0, 1 0, 1 1, 0 0)"), (stf.ST_Buffer, ("point", 1.0), "point_geom", "ST_ReducePrecision(geom, 2)", "POLYGON ((0.98 0.8, 0.92 0.62, 0.83 0.44, 0.71 0.29, 0.56 0.17, 0.38 0.08, 0.2 0.02, 0 0, -0.2 0.02, -0.38 0.08, -0.56 0.17, -0.71 0.29, -0.83 0.44, -0.92 0.62, -0.98 0.8, -1 1, -0.98 1.2, -0.92 1.38, -0.83 1.56, -0.71 1.71, -0.56 1.83, -0.38 1.92, -0.2 1.98, 0 2, 0.2 1.98, 0.38 1.92, 0.56 1.83, 0.71 1.71, 0.83 1.56, 0.92 1.38, 0.98 1.2, 1 1, 0.98 0.8))"), + (stf.ST_Buffer, ("point", 1.0, True), "point_geom", "ST_ReducePrecision(geom, 2)", "POLYGON ((0.98 0.8, 0.92 0.62, 0.83 0.44, 0.71 0.29, 0.56 0.17, 0.38 0.08, 0.2 0.02, 0 0, -0.2 0.02, -0.38 0.08, -0.56 0.17, -0.71 0.29, -0.83 0.44, -0.92 0.62, -0.98 0.8, -1 1, -0.98 1.2, -0.92 1.38, -0.83 1.56, -0.71 1.71, -0.56 1.83, -0.38 1.92, -0.2 1.98, 0 2, 0.2 1.98, 0.38 1.92, 0.56 1.83, 0.71 1.71, 0.83 1.56, 0.92 1.38, 0.98 1.2, 1 1, 0.98 0.8))"), (stf.ST_BuildArea, ("geom",), "multiline_geom", "ST_Normalize(geom)", "POLYGON ((0 0, 1 1, 1 0, 0 0))"), (stf.ST_BoundingDiagonal, ("geom",), "square_geom", "ST_BoundingDiagonal(geom)", "LINESTRING (1 0, 2 1)"), (stf.ST_Centroid, ("geom",), "triangle_geom", "ST_ReducePrecision(geom, 2)", "POINT (0.67 0.33)"), diff --git a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFs.java b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFs.java index 6bd07a035f..62fc14c069 100644 --- a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFs.java +++ b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFs.java @@ -25,6 +25,8 @@ import org.locationtech.jts.geom.Point; import org.locationtech.jts.io.ParseException; import org.locationtech.jts.io.WKBWriter; +import org.opengis.referencing.FactoryException; +import org.opengis.referencing.operation.TransformException; import org.xml.sax.SAXException; import javax.xml.parsers.ParserConfigurationException; @@ -159,7 +161,7 @@ public static byte[] ST_BoundingDiagonal(byte[] geometry) { } @UDFAnnotations.ParamMeta(argNames = {"geometry", "radius"}) - public static byte[] ST_Buffer(byte[] geometry, double radius) { + public static byte[] ST_Buffer(byte[] geometry, double radius) throws FactoryException, TransformException { return GeometrySerde.serialize( Functions.buffer( GeometrySerde.deserialize(geometry), diff --git a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java index ab0abdc0dc..96537c5821 100644 --- a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java +++ b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java @@ -21,6 +21,8 @@ import org.apache.sedona.snowflake.snowsql.annotations.UDFAnnotations; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.Point; +import org.opengis.referencing.FactoryException; +import org.opengis.referencing.operation.TransformException; import java.io.IOException; @@ -179,7 +181,7 @@ public static int ST_BestSRID(String geometry) { } @UDFAnnotations.ParamMeta(argNames = {"geometry", "radius"}, argTypes = {"Geometry", "double"}, returnTypes = "Geometry") - public static String ST_Buffer(String geometry, double radius) { + public static String ST_Buffer(String geometry, double radius) throws FactoryException, TransformException { return GeometrySerde.serGeoJson( Functions.buffer( GeometrySerde.deserGeoJson(geometry), diff --git a/spark/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala b/spark/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala index f31d698193..391e87d8f1 100644 --- a/spark/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala +++ b/spark/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala @@ -73,6 +73,15 @@ class functionTestScala extends TestBaseScala with Matchers with GeometrySample assert(functionDf.count() > 0); } + it("Passed ST_Buffer Spheroid") { + val polygonWktDf = sparkSession.read.format("csv").option("delimiter", "\t").option("header", "false").load(mixedWktGeometryInputLocation) + polygonWktDf.createOrReplaceTempView("polygontable") + val polygonDf = sparkSession.sql("select ST_GeomFromWKT(polygontable._c0) as countyshape from polygontable") + polygonDf.createOrReplaceTempView("polygondf") + val functionDf = sparkSession.sql("select ST_Buffer(polygondf.countyshape, 1, true) from polygondf") + assert(functionDf.count() > 0); + } + it("Passed ST_BestSRID") { val polygonWktDf = sparkSession.read.format("csv").option("delimiter", "\t").option("header", "false").load(mixedWktGeometryInputLocation) polygonWktDf.createOrReplaceTempView("polygontable") From 982be17f8ed88e60f384b7f5aed1cc937efe3a67 Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Wed, 21 Feb 2024 23:07:22 -0500 Subject: [PATCH 17/54] nit: update docs, change useSpheroidal to useSpheroid --- .../org/apache/sedona/common/Functions.java | 34 ++----------------- docs/api/flink/Function.md | 10 +++--- docs/api/sql/Function.md | 12 +++---- .../sedona/flink/expressions/Functions.java | 4 +-- python/sedona/sql/st_functions.py | 8 ++--- .../sedona_sql/expressions/st_functions.scala | 4 +-- 6 files changed, 21 insertions(+), 51 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 2f324a935f..ca175332c5 100644 --- a/common/src/main/java/org/apache/sedona/common/Functions.java +++ b/common/src/main/java/org/apache/sedona/common/Functions.java @@ -96,7 +96,7 @@ public static Geometry buffer(Geometry geometry, double radius, String params) t return buffer(geometry, radius, params, false); } - public static Geometry buffer(Geometry geometry, double radius, String params, boolean useSpheroidal) throws FactoryException, TransformException { + public static Geometry buffer(Geometry geometry, double radius, String params, boolean useSpheroid) throws FactoryException, TransformException { BufferParameters bufferParameters = new BufferParameters(); // Processing parameters @@ -112,7 +112,7 @@ public static Geometry buffer(Geometry geometry, double radius, String params, b } } - if (useSpheroidal) { + if (useSpheroid) { // Spheroidal buffering logic // Determine the best SRID for spheroidal calculations @@ -142,36 +142,6 @@ public static Geometry buffer(Geometry geometry, double radius, String params, b } } - -// public static Geometry buffer(Geometry geometry, double radius, String params, boolean useSpheroidal) throws FactoryException, TransformException { -// if (useSpheroidal) { -// // Determine the best SRID for spheroidal calculations -// int bestCRS = bestSRID(geometry); -// System.out.println("bestCRS: "+bestCRS); -// int originalCRS = geometry.getSRID(); -// final int WGS84CRS = 4326; -// -// // If originalCRS is not set, use WGS84 as the originalCRS for transformation -// String sourceCRSCode = (originalCRS == 0) ? "EPSG:" + WGS84CRS : "EPSG:" + originalCRS; -// String targetCRSCode = "EPSG:" + bestCRS; -// -// // Transform the geometry to the selected SRID -// Geometry transformedGeometry = FunctionsGeoTools.transform(geometry, sourceCRSCode, targetCRSCode); -// -// // Apply the buffer operation in the selected SRID -// Geometry bufferedGeometry = buffer(transformedGeometry, radius, params); -// -// // Transform back to the original SRID or to WGS 84 if original SRID was not set -// int backTransformCRSCode = (originalCRS == 0) ? WGS84CRS : originalCRS; -// Geometry bufferedResult = FunctionsGeoTools.transform(bufferedGeometry, targetCRSCode, "EPSG:" + backTransformCRSCode); -// bufferedResult.setSRID(backTransformCRSCode); -// return bufferedResult; -// } else { -// // Existing planar buffer logic -// return buffer(geometry, radius, params); -// } -// } - private static BufferParameters parseBufferParams(String params) { String[] listBufferParameters = {"quad_segs", "endcap", "join", "mitre_limit", "miter_limit", "side"}; diff --git a/docs/api/flink/Function.md b/docs/api/flink/Function.md index 3b305f6783..d8642e0e66 100644 --- a/docs/api/flink/Function.md +++ b/docs/api/flink/Function.md @@ -567,12 +567,12 @@ The optional third parameter controls the buffer accuracy and style. Buffer accu Mode of buffer calculation: -- Planar Buffering (default): When `useSpheroid` is false, ST_Buffer performs standard planar buffering based on the provided parameters. +- Planar Buffering (default): When `useSpheroid` is false, `ST_Buffer` performs standard planar buffering based on the provided parameters. - Spheroidal Buffering: - - When `useSpheroid` is set to true, the function performs spheroidal buffering for more accurate representation over the Earth's spheroid. - - ST_Buffer first determines the best SRID for spheroidal calculations by using ST_BestSRID, which chooses an SRID that optimally represents the geometry on the Earth's surface. - - If the original SRID of the input geometry is not set, `ST_Buffer` defaults to using WGS 84 (SRID 4326) as the baseline for transformations. - - The geometry is then transformed to the selected SRID, and the buffer operation is applied in this coordinate system. + - When `useSpheroid` is set to true, the function returns the spheroidal buffer polygon for more accurate representation over the Earth. + - ST_Buffer first determines the most appropriate Spatial Reference Identifier (SRID) for a given geometry, based on its spatial extent and location, using `ST_BestSRID`. + - The geometry is then transformed from its original SRID to the selected SRID. If the input geometry does not have a set SRID, `ST_Buffer` defaults to using WGS 84 (SRID 4326) as its original SRID. + - The standard planar buffer operation is then applied in this coordinate system. - Finally, the buffered geometry is transformed back to its original SRID, or to WGS 84 if the original SRID was not set. !!!note diff --git a/docs/api/sql/Function.md b/docs/api/sql/Function.md index 9edad687d2..6b519522bf 100644 --- a/docs/api/sql/Function.md +++ b/docs/api/sql/Function.md @@ -565,12 +565,12 @@ The optional third parameter controls the buffer accuracy and style. Buffer accu Mode of buffer calculation: -- Planar Buffering (default): When `useSpheroid` is false, ST_Buffer performs standard planar buffering based on the provided parameters. +- Planar Buffering (default): When `useSpheroid` is false, `ST_Buffer` performs standard planar buffering based on the provided parameters. - Spheroidal Buffering: - - When `useSpheroid` is set to true, the function performs spheroidal buffering for more accurate representation over the Earth's spheroid. - - ST_Buffer first determines the best SRID for spheroidal calculations by using ST_BestSRID, which chooses an SRID that optimally represents the geometry on the Earth's surface. - - If the original SRID of the input geometry is not set, `ST_Buffer` defaults to using WGS 84 (SRID 4326) as the baseline for transformations. - - The geometry is then transformed to the selected SRID, and the buffer operation is applied in this coordinate system. + - When `useSpheroid` is set to true, the function returns the spheroidal buffer polygon for more accurate representation over the Earth. + - ST_Buffer first determines the most appropriate Spatial Reference Identifier (SRID) for a given geometry, based on its spatial extent and location, using `ST_BestSRID`. + - The geometry is then transformed from its original SRID to the selected SRID. If the input geometry does not have a set SRID, `ST_Buffer` defaults to using WGS 84 (SRID 4326) as its original SRID. + - The standard planar buffer operation is then applied in this coordinate system. - Finally, the buffered geometry is transformed back to its original SRID, or to WGS 84 if the original SRID was not set. !!!note @@ -582,7 +582,7 @@ Format: ST_Buffer (A: Geometry, buffer: Double, bufferStyleParameters: String [Optional]) ``` ``` -ST_Buffer (A: Geometry, buffer: Double, bufferStyleParameters: String [Optional], useSperoidal: Boolean) +ST_Buffer (A: Geometry, buffer: Double, bufferStyleParameters: String [Optional], useSpheroid: Boolean) ``` Since: `v1.5.1` diff --git a/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java b/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java index 0cfc24d818..98e49d7152 100644 --- a/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java +++ b/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java @@ -86,9 +86,9 @@ public Geometry eval(@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.j @DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) public Geometry eval(@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) - Object o, @DataTypeHint("Double") Double radius, @DataTypeHint("String") String params, @DataTypeHint("Boolean") Boolean useSpheroidal) throws FactoryException, TransformException { + Object o, @DataTypeHint("Double") Double radius, @DataTypeHint("String") String params, @DataTypeHint("Boolean") Boolean useSpheroid) throws FactoryException, TransformException { Geometry geom = (Geometry) o; - return org.apache.sedona.common.Functions.buffer(geom, radius, params, useSpheroidal); + return org.apache.sedona.common.Functions.buffer(geom, radius, params, useSpheroid); } } diff --git a/python/sedona/sql/st_functions.py b/python/sedona/sql/st_functions.py index c668836d46..4888903966 100644 --- a/python/sedona/sql/st_functions.py +++ b/python/sedona/sql/st_functions.py @@ -330,7 +330,7 @@ def ST_Boundary(geometry: ColumnOrName) -> Column: @validate_argument_types -def ST_Buffer(geometry: ColumnOrName, buffer: ColumnOrNameOrNumber, parameters: Optional[Union[ColumnOrName, str]] = None, useSpheroidal: Optional[Union[ColumnOrName, bool]] = None) -> Column: +def ST_Buffer(geometry: ColumnOrName, buffer: ColumnOrNameOrNumber, parameters: Optional[Union[ColumnOrName, str]] = None, useSpheroid: Optional[Union[ColumnOrName, bool]] = None) -> Column: """Calculate a geometry that represents all points whose distance from the input geometry column is equal to or less than a given amount. @@ -341,12 +341,12 @@ def ST_Buffer(geometry: ColumnOrName, buffer: ColumnOrNameOrNumber, parameters: :return: Buffered geometry as a geometry column. :rtype: Column """ - if parameters is None and useSpheroidal is None: + if parameters is None and useSpheroid is None: args = (geometry, buffer) - elif useSpheroidal is None: + elif useSpheroid is None: args = (geometry, buffer, parameters) else: - args = (geometry, buffer, parameters, useSpheroidal) + args = (geometry, buffer, parameters, useSpheroid) return _call_st_function("ST_Buffer", args) diff --git a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala index cc9f0f7edd..ced4e7b6ec 100644 --- a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala +++ b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala @@ -68,8 +68,8 @@ object st_functions extends DataFrameAPI { def ST_Buffer(geometry: String, buffer: Double): Column = wrapExpression[ST_Buffer](geometry, buffer) def ST_Buffer(geometry: Column, buffer: Column, parameters: Column): Column = wrapExpression[ST_Buffer](geometry, buffer, parameters) def ST_Buffer(geometry: String, buffer: Double, parameters: String): Column = wrapExpression[ST_Buffer](geometry, buffer, parameters) - def ST_Buffer(geometry: Column, buffer: Column, parameters: Column, useSpheroidal: Column): Column = wrapExpression[ST_Buffer](geometry, buffer, parameters, useSpheroidal) - def ST_Buffer(geometry: String, buffer: Double, parameters: String, useSpheroidal: Boolean): Column = wrapExpression[ST_Buffer](geometry, buffer, parameters, useSpheroidal) + def ST_Buffer(geometry: Column, buffer: Column, parameters: Column, useSpheroid: Column): Column = wrapExpression[ST_Buffer](geometry, buffer, parameters, useSpheroid) + def ST_Buffer(geometry: String, buffer: Double, parameters: String, useSpheroid: Boolean): Column = wrapExpression[ST_Buffer](geometry, buffer, parameters, useSpheroid) def ST_BestSRID(geometry: Column): Column = wrapExpression[ST_BestSRID](geometry) def ST_BestSRID(geometry: String): Column = wrapExpression[ST_BestSRID](geometry) From 79dc271d1c465b80e5b433be68736274f4a4752f Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Wed, 21 Feb 2024 23:21:38 -0500 Subject: [PATCH 18/54] docs: update --- docs/api/flink/Function.md | 4 ++-- docs/api/sql/Function.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/api/flink/Function.md b/docs/api/flink/Function.md index d8642e0e66..e2eb133df4 100644 --- a/docs/api/flink/Function.md +++ b/docs/api/flink/Function.md @@ -553,7 +553,7 @@ Output: `LINESTRING Z(-1 -1 0, 10 5 5)` ## ST_Buffer -Introduction: Returns a geometry/geography that represents all points whose distance from this Geometry/geography is less than or equal to distance. +Introduction: Returns a geometry/geography that represents all points whose distance from this Geometry/geography is less than or equal to distance. The function supports both Planar/Euclidean and Spheroidal/Geodesic buffering (Since v1.6.0). Buffer Style Parameters: @@ -565,7 +565,7 @@ The optional third parameter controls the buffer accuracy and style. Buffer accu - `mitre_limit=#.#` : mitre ratio limit and it only affects mitred join style. `miter_limit` is an accepted synonym for `mitre_limit`. - `side=both|left|right` : The option `left` or `right` enables a single-sided buffer operation on the geometry, with the buffered side aligned according to the direction of the line. This functionality is specific to LINESTRING geometry and has no impact on POINT or POLYGON geometries. By default, square end caps are applied. -Mode of buffer calculation: +Mode of buffer calculation (Since: `v1.6.0`): - Planar Buffering (default): When `useSpheroid` is false, `ST_Buffer` performs standard planar buffering based on the provided parameters. - Spheroidal Buffering: diff --git a/docs/api/sql/Function.md b/docs/api/sql/Function.md index 6b519522bf..3c09732c16 100644 --- a/docs/api/sql/Function.md +++ b/docs/api/sql/Function.md @@ -551,7 +551,7 @@ Output: `LINESTRING Z(-1 -1 0, 10 5 5)` ## ST_Buffer -Introduction: Returns a geometry/geography that represents all points whose distance from this Geometry/geography is less than or equal to distance. +Introduction: Returns a geometry/geography that represents all points whose distance from this Geometry/geography is less than or equal to distance. The function supports both Planar/Euclidean and Spheroidal/Geodesic buffering (Since v1.6.0). Buffer Style Parameters: @@ -563,7 +563,7 @@ The optional third parameter controls the buffer accuracy and style. Buffer accu - `mitre_limit=#.#` : mitre ratio limit and it only affects mitred join style. `miter_limit` is an accepted synonym for `mitre_limit`. - `side=both|left|right` : The option `left` or `right` enables a single-sided buffer operation on the geometry, with the buffered side aligned according to the direction of the line. This functionality is specific to LINESTRING geometry and has no impact on POINT or POLYGON geometries. By default, square end caps are applied. -Mode of buffer calculation: +Mode of buffer calculation (Since: `v1.6.0`): - Planar Buffering (default): When `useSpheroid` is false, `ST_Buffer` performs standard planar buffering based on the provided parameters. - Spheroidal Buffering: From 69a2352ee009eb10c1e1b224da8575d212894c52 Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Wed, 21 Feb 2024 23:46:10 -0500 Subject: [PATCH 19/54] Fix duplicates --- .../org/apache/sedona/common/Functions.java | 1 - .../apache/sedona/common/FunctionsTest.java | 83 ------------------- .../sedona/flink/expressions/Functions.java | 8 -- .../org/apache/sedona/flink/FunctionTest.java | 20 +---- .../sedona_sql/expressions/st_functions.scala | 3 - 5 files changed, 2 insertions(+), 113 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 ca175332c5..359a3cc54b 100644 --- a/common/src/main/java/org/apache/sedona/common/Functions.java +++ b/common/src/main/java/org/apache/sedona/common/Functions.java @@ -117,7 +117,6 @@ public static Geometry buffer(Geometry geometry, double radius, String params, b // Determine the best SRID for spheroidal calculations int bestCRS = bestSRID(geometry); - System.out.println("bestCRS: " + bestCRS); int originalCRS = geometry.getSRID(); final int WGS84CRS = 4326; diff --git a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java index 09893c7841..25b6bec9eb 100644 --- a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java +++ b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java @@ -1181,14 +1181,12 @@ public void testBufferSpheroidal() throws FactoryException, TransformException, Geometry linestring1 = Constructors.geomFromEWKT("LINESTRING(-91.185 30.4505, -91.187 30.452, -91.189 30.4535)"); Geometry polygon3 = Constructors.geomFromWKT("POLYGON((-120 30, -80 30, -80 50, -120 50, -120 30))", 4269); - Geometry result1 = Functions.buffer(polygon1, 100, "", true); Geometry result2 = Functions.buffer(polygon2, 1000, "", true); Geometry result3 = Functions.buffer(point1, 100, "", true); Geometry result4 = Functions.buffer(linestring1, 10, "", true); Geometry result5 = Functions.buffer(polygon3, 10000, "", true); - Geometry expected1 = Constructors.geomFromEWKT("POLYGON ((16.248653057837092 48.249999997812616, 16.24867891451385 48.25017542568974, 16.24875549793627 48.25034411793438, 16.24887986793391 48.25049959710695, 16.249047249310912 48.25063589306857, 16.249251215157546 48.250747772240985, 16.249483933612066 48.25083093858835, 16.24973646860016 48.250882198599385, 16.249999123001867 48.25089958393145, 16.35000087730807 48.25089956809054, 16.35026352923699 48.25088218269226, 16.350516061632444 48.250830922668115, 16.350748777319566 48.25074775640202, 16.350952740192536 48.250635877460866, 16.351120118386593 48.25049958194538, 16.35124448502055 48.25034410350242, 16.35132106495956 48.25017541233756, 16.351346918125614 48.24999998594946, 16.351345606585333 48.199999998284284, 16.351319726848164 48.19982442741667, 16.351243088946557 48.199655609589996, 16.351118640921797 48.19950003769327, 16.350951169518318 48.19936369511042, 16.35074711603489 48.19925182561273, 16.350514328568075 48.199168731701455, 16.350261760179283 48.19911760915295, 16.34999912459177 48.19910042412577, 16.250000875738642 48.19910040825404, 16.24973823767017 48.1991175932147, 16.249485666681004 48.19916871575088, 16.249252876439694 48.19925180974528, 16.24904881997628 48.19936367947716, 16.248881345384483 48.19950002251011, 16.248756893991796 48.19965559514148, 16.24868025260387 48.19982441405396, 16.2486543693546 48.199999986417545, 16.248653057837092 48.249999997812616))"); Geometry expected2 = Constructors.geomFromEWKT("POLYGON ((-120.00898315284118 29.999999999999467, -120.00898315284118 49.999999999988724, -120.00881054407824 50.001129625613444, -120.0082993510474 50.002215815187945, -120.00746921861013 50.003216831381316, -120.00635204829044 50.00409421217581, -120.0049907723172 50.004814247955025, -120.00343770376273 50.00534927578318, -120.00175252618048 50.00567874131385, -119.99999999999999 50.005789987696524, -80 50.005789987696524, -79.9982474738195 50.00567874131385, -79.99656229623727 50.00534927578318, -79.99500922768277 50.004814247955025, -79.99364795170956 50.00409421217581, -79.99253078138989 50.003216831381316, -79.99170064895262 50.002215815187945, -79.99118945592176 50.001129625613444, -79.99101684715882 49.999999999988724, -79.99101684715882 29.999999999999467, -79.99118945592176 29.998474584401656, -79.99170064895262 29.997007767335376, -79.99253078138989 29.995655921596196, -79.99364795170956 29.994471003601635, -79.99500922768277 29.99349855586065, -79.99656229623727 29.99277595576926, -79.9982474738195 29.99233097818683, -80 29.992180727189663, -119.99999999999999 29.992180727189663, -120.00175252618048 29.99233097818683, -120.00343770376273 29.99277595576926, -120.0049907723172 29.99349855586065, -120.00635204829044 29.994471003601635, -120.00746921861013 29.995655921596196, -120.0082993510474 29.997007767335376, -120.00881054407824 29.998474584401656, -120.00898315284118 29.999999999999467))"); Geometry expected3 = Constructors.geomFromEWKT("POLYGON ((-179.9982096288439 59.99995928994206, -179.9982598922349 59.99978513613418, -179.99837702566958 59.99961923978765, -179.99855652694714 59.99946797603053, -179.998791497433 59.99933715760413, -179.99907290724568 59.999231811518854, -179.9993899422849 59.99915598591007, -179.99973041976276 59.99911259450936, 179.9999187437241 59.999103304702594, 179.99957102955173 59.99912847347155, 179.99923979915093 59.999187133678575, 179.99893778069153 59.999277031220146, 179.99867658007233 59.99939471162435, 179.99846623498902 59.99953565276752, 179.9983148292063 59.99969443861621, 179.99822818185396 59.999864967323326, 179.9982096236965 60.00004068568767, 179.99825986898801 60.00021484097138, 179.99837698785996 60.00038074040053, 179.99855648032874 60.00053200837772, 179.99879144910062 60.00066283151926, 179.99907286455522 60.00076818209686, 179.9993899117333 60.00084401129125, 179.9997304059989 60.000887404824965, -179.99991873860708 60.00089669498742, -179.99957100633523 60.000871524742664, -179.9992397613717 60.000812861453035, -179.9989377341035 60.000722959691345, -179.99867653177037 60.00060527457202, -179.99846619232903 60.00046432893652, -179.99831479868513 60.000305539502236, -179.99822816812048 60.00013500866213, -179.9982096288439 59.99995928994206))"); @@ -1201,13 +1199,6 @@ public void testBufferSpheroidal() throws FactoryException, TransformException, Geometry postgis_result4 = Constructors.geomFromEWKT("POLYGON((-91.18706814560713 30.45193179814003,-91.1890681473133 30.453431798552806,-91.18908219578012 30.453444627151963,-91.18909308551447 30.453459583698564,-91.18910039802891 30.453476093421752,-91.18910385230598 30.453493521862622,-91.1891033155977 30.453511199256216,-91.18909880852735 30.453528446270074,-91.18909050429724 30.453544600110646,-91.18907872203292 30.453559039994175,-91.18906391451956 30.453571211003144,-91.18904665080186 30.453580645411602,-91.18902759431575 30.453586980659722,-91.189007477393 30.45358997328696,-91.1889870731177 30.453589508288072,-91.18896716561683 30.45358560353286,-91.18894851992636 30.453578409079352,-91.18893185259108 30.453568201407066,-91.18693185325601 30.45206820103894,-91.1849318540605 30.450568200775386,-91.18491780618884 30.450555372062745,-91.18490691698447 30.450540415432766,-91.18489960491243 30.45052390566114,-91.18489615096986 30.450506477209665,-91.18489668788781 30.450488799844198,-91.1849011950307 30.45047155289582,-91.18490949918969 30.45045539915463,-91.18492128123923 30.450440959399103,-91.18493608840117 30.45042878854006,-91.18495335164478 30.45041935429591,-91.18497240755428 30.450413019218534,-91.18499252382345 30.450410026760903,-91.18501292739734 30.450410491921236,-91.1850328341802 30.450414396823803,-91.18505147916741 30.450421591405906,-91.1850681458439 30.450431799184482,-91.18706814560713 30.45193179814003))"); Geometry postgis_result5 = Constructors.geomFromEWKT("POLYGON((-120.08983152841193 29.999999999999993,-120.08983152841193 49.99999999999999,-120.08810544078257 50.011295055230505,-120.082993510474 50.02215353089088,-120.07469218610122 50.032158574496115,-120.06352048290445 50.040926345165026,-120.04990772317234 50.04812066585569,-120.03437703762728 50.0534658262464,-120.01752526180509 50.05675706194147,-119.99999999999999 50.05786832496988,-80 50.05786832496988,-79.98247473819491 50.05675706194147,-79.9656229623727 50.0534658262464,-79.95009227682766 50.04812066585569,-79.93647951709556 50.040926345165026,-79.92530781389875 50.032158574496115,-79.91700648952599 50.02215353089088,-79.91189455921743 50.011295055230505,-79.91016847158805 49.99999999999999,-79.91016847158805 29.999999999999993,-79.91189455921743 29.984744778414523,-79.91700648952599 29.970073573593833,-79.92530781389875 29.956550575941442,-79.93647951709556 29.944696041122494,-79.95009227682766 29.934966209462644,-79.9656229623727 29.927735669850765,-79.98247473819491 29.923282861557286,-80 29.921779286758486,-119.99999999999999 29.921779286758486,-120.01752526180509 29.923282861557286,-120.03437703762728 29.927735669850765,-120.04990772317234 29.934966209462644,-120.06352048290445 29.944696041122494,-120.07469218610122 29.956550575941442,-120.082993510474 29.970073573593833,-120.08810544078257 29.984744778414523,-120.08983152841193 29.999999999999993))"); - System.out.println(result1); - System.out.println(Functions.bestSRID(polygon1)); - System.out.println(result2); - System.out.println(result3); - System.out.println(result4); - System.out.println(result5); - assertEquals(expected1, result1); assertEquals(4326, Functions.getSRID(result1)); assertEquals(7.424558176442617E-8, comparePolygons(postgis_result1, result1), FP_TOLERANCE2); @@ -1232,11 +1223,6 @@ public void testBufferSpheroidal() throws FactoryException, TransformException, assertEquals(4269, Functions.getSRID(result5)); assertEquals(Spheroid.area(postgis_result5), Spheroid.area(result5), 10); assertEquals(7.424558176442617E-8, comparePolygons(postgis_result5, result5), FP_TOLERANCE2); - - double difference = comparePolygons(result1, postgis_result1); -// System.out.println("Maximum vertex distance between polygons: " + difference); -// System.out.println("Sedona Area: "+Spheroid.area(result1)); -// System.out.println("PostGIS Area: "+Spheroid.area(postgis_result1)); } private static double comparePolygons(Geometry p1, Geometry p2) { @@ -1322,75 +1308,6 @@ public void testBestSRID() { assertEquals("Expected World Mercator projection for wide range geometry", expectedEPSG, actualEPSG); } - @Test - public void testBestSRID() { - int[][] testCases_special = { - {0, -70, 3409}, // EPSG:3409 (Antarctic Polar Stereographic) - {0, 70, 3574}, // EPSG:3575 (North Pole LAEA Alaska) - // Special cases - {-180, 60, 32660}, {180, 60, 32660}, {-180, -60, 32760}, {180, -60, 32760}, - }; - - // Number of UTM zones - int numZones = (177 - (-177)) / 6 + 1; - int numZonesSouth = (177 - (-177)) / 6 + 1; - - int[][] testCases_UTMNorth = new int[numZones][3]; - int[][] testCases_UTMSouth = new int[numZonesSouth][3]; - - int indexNorth = 0; - int northernLat = 60; // Latitude for Northern Hemisphere UTM zones - for (int lon = -177, epsg = 32601; lon <= 177; lon += 6, epsg++) { - testCases_UTMNorth[indexNorth][0] = lon; // Longitude - testCases_UTMNorth[indexNorth][1] = northernLat; // Latitude - testCases_UTMNorth[indexNorth][2] = epsg; // EPSG code - indexNorth++; - } - - int indexSouth = 0; - int southernLat = -60; // Latitude for Southern Hemisphere UTM zones - for (int lon = -177, epsg = 32701; lon <= 177; lon += 6, epsg++) { - testCases_UTMSouth[indexSouth][0] = lon; // Longitude - testCases_UTMSouth[indexSouth][1] = southernLat; // Latitude - testCases_UTMSouth[indexSouth][2] = epsg; // EPSG code - indexSouth++; - } - - for (int[] testCase : testCases_special) { - Geometry geom = GEOMETRY_FACTORY.createPoint(new Coordinate(testCase[0], testCase[1])); - int actualEPSG = Functions.bestSRID(geom); - int expectedEPSG = testCase[2]; - assertEquals("Failed at coordinates (" + testCase[0] + ", " + testCase[1] + ")", expectedEPSG, actualEPSG); - } - - for (int[] testCase : testCases_UTMNorth) { - Geometry geom = GEOMETRY_FACTORY.createPoint(new Coordinate(testCase[0], testCase[1])); - int actualEPSG = Functions.bestSRID(geom); - int expectedEPSG = testCase[2]; - assertEquals("Failed at coordinates (" + testCase[0] + ", " + testCase[1] + ")", expectedEPSG, actualEPSG); - } - - for (int[] testCase : testCases_UTMSouth) { - Geometry geom = GEOMETRY_FACTORY.createPoint(new Coordinate(testCase[0], testCase[1])); - int actualEPSG = Functions.bestSRID(geom); - int expectedEPSG = testCase[2]; - assertEquals("Failed at coordinates (" + testCase[0] + ", " + testCase[1] + ")", expectedEPSG, actualEPSG); - } - - // Large geometry that does not fit into UTM or polar categories - Geometry geom = GEOMETRY_FACTORY.createPolygon(new Coordinate[] { - new Coordinate(-160, -40), - new Coordinate(-160, 40), - new Coordinate(160, 40), - new Coordinate(160, -40), - new Coordinate(-160, -40) - }); - int expectedEPSG = 3395; // EPSG code for World Mercator - int actualEPSG = Functions.bestSRID(geom); - - assertEquals("Expected World Mercator projection for wide range geometry", expectedEPSG, actualEPSG); - } - @Test public void nRingsUnsupported() { LineString lineString = GEOMETRY_FACTORY.createLineString(coordArray3d(0, 1, 1, 1, 2, 1, 1, 2, 2)); diff --git a/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java b/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java index fd5bb9e7ab..98e49d7152 100644 --- a/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java +++ b/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java @@ -100,14 +100,6 @@ public int eval(@DataTypeHint(value = "RAW", bridgedTo = Geometry.class) Object } } - public static class ST_BestSRID extends ScalarFunction { - @DataTypeHint("Integer") - public int eval(@DataTypeHint(value = "RAW", bridgedTo = Geometry.class) Object o) { - Geometry geom = (Geometry) o; - return org.apache.sedona.common.Functions.bestSRID(geom); - } - } - public static class ST_ClosestPoint extends ScalarFunction { @DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) public Geometry eval(@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) Object g1, diff --git a/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java b/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java index a1df6ea0bb..42d0cc3b76 100644 --- a/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java +++ b/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java @@ -123,24 +123,6 @@ public void testBestSRID() { assertEquals(3395, result); } - @Test - public void testBestSRID() { - Table table1 = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('POINT (160 40)') AS geom"); - table1 = table1.select(call(Functions.ST_BestSRID.class.getSimpleName(), $("geom"))); - int result = (int) first(table1).getField(0); - assertEquals(32657, result); - - Table table2 = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('LINESTRING(-91.185 30.4505, -91.187 30.452, -91.189 30.4535)') AS geom"); - table2 = table2.select(call(Functions.ST_BestSRID.class.getSimpleName(), $("geom"))); - result = (int) first(table2).getField(0); - assertEquals(32615, result); - - Table table3 = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('POLYGON((-120 30, -80 30, -80 50, -120 50, -120 30))') AS geom"); - table3 = table3.select(call(Functions.ST_BestSRID.class.getSimpleName(), $("geom"))); - result = (int) first(table3).getField(0); - assertEquals(3395, result); - } - @Test public void testClosestPoint() { Table table = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('POINT (160 40)') AS g1, ST_GeomFromWKT('POINT (10 10)') as g2"); @@ -148,6 +130,8 @@ public void testClosestPoint() { Geometry result = (Geometry) first(table).getField(0); assertEquals("POINT (160 40)", result.toString()); } + + @Test public void testCentroid() { Table polygonTable = tableEnv.sqlQuery("SELECT ST_GeomFromText('POLYGON ((2 2, 0 0, 2 0, 0 2, 2 2))') as geom"); Table resultTable = polygonTable.select(call(Functions.ST_Centroid.class.getSimpleName(), $("geom"))); diff --git a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala index fe39c1dd82..ced4e7b6ec 100644 --- a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala +++ b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala @@ -74,9 +74,6 @@ object st_functions extends DataFrameAPI { def ST_BestSRID(geometry: Column): Column = wrapExpression[ST_BestSRID](geometry) def ST_BestSRID(geometry: String): Column = wrapExpression[ST_BestSRID](geometry) - def ST_BestSRID(geometry: Column): Column = wrapExpression[ST_BestSRID](geometry) - def ST_BestSRID(geometry: String): Column = wrapExpression[ST_BestSRID](geometry) - def ST_BuildArea(geometry: Column): Column = wrapExpression[ST_BuildArea](geometry) def ST_BuildArea(geometry: String): Column = wrapExpression[ST_BuildArea](geometry) From d71f9c53223816f520f6a54b3f652135b3108c7e Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Thu, 22 Feb 2024 12:40:38 -0500 Subject: [PATCH 20/54] Fix lint; fix scala/java test --- .../java/org/apache/sedona/common/FunctionsTest.java | 12 ++++++------ docs/api/sql/Function.md | 3 +-- python/tests/sql/test_dataframe_api.py | 2 +- .../streaming/spark/test_constructor_functions.py | 5 +++++ 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java index 25b6bec9eb..ebbbc2b4ea 100644 --- a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java +++ b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java @@ -1187,7 +1187,7 @@ public void testBufferSpheroidal() throws FactoryException, TransformException, Geometry result4 = Functions.buffer(linestring1, 10, "", true); Geometry result5 = Functions.buffer(polygon3, 10000, "", true); - Geometry expected1 = Constructors.geomFromEWKT("POLYGON ((16.248653057837092 48.249999997812616, 16.24867891451385 48.25017542568974, 16.24875549793627 48.25034411793438, 16.24887986793391 48.25049959710695, 16.249047249310912 48.25063589306857, 16.249251215157546 48.250747772240985, 16.249483933612066 48.25083093858835, 16.24973646860016 48.250882198599385, 16.249999123001867 48.25089958393145, 16.35000087730807 48.25089956809054, 16.35026352923699 48.25088218269226, 16.350516061632444 48.250830922668115, 16.350748777319566 48.25074775640202, 16.350952740192536 48.250635877460866, 16.351120118386593 48.25049958194538, 16.35124448502055 48.25034410350242, 16.35132106495956 48.25017541233756, 16.351346918125614 48.24999998594946, 16.351345606585333 48.199999998284284, 16.351319726848164 48.19982442741667, 16.351243088946557 48.199655609589996, 16.351118640921797 48.19950003769327, 16.350951169518318 48.19936369511042, 16.35074711603489 48.19925182561273, 16.350514328568075 48.199168731701455, 16.350261760179283 48.19911760915295, 16.34999912459177 48.19910042412577, 16.250000875738642 48.19910040825404, 16.24973823767017 48.1991175932147, 16.249485666681004 48.19916871575088, 16.249252876439694 48.19925180974528, 16.24904881997628 48.19936367947716, 16.248881345384483 48.19950002251011, 16.248756893991796 48.19965559514148, 16.24868025260387 48.19982441405396, 16.2486543693546 48.199999986417545, 16.248653057837092 48.249999997812616))"); + Geometry expected1 = Constructors.geomFromEWKT("POLYGON ((16.248653057837092 48.24999999781262, 16.24867891451385 48.25017542568975, 16.24875549793627 48.25034411793438, 16.24887986793391 48.25049959710696, 16.249047249310912 48.25063589306856, 16.249251215157546 48.250747772240985, 16.249483933612066 48.25083093858835, 16.249736468600165 48.250882198599385, 16.249999123001867 48.25089958393145, 16.35000087730807 48.25089956809054, 16.35026352923699 48.250882182692266, 16.350516061632444 48.250830922668115, 16.35074877731957 48.25074775640202, 16.350952740192536 48.250635877460866, 16.351120118386593 48.25049958194538, 16.351244485020548 48.25034410350243, 16.35132106495956 48.25017541233756, 16.351346918125614 48.24999998594946, 16.351345606585333 48.199999998284284, 16.351319726848168 48.19982442741669, 16.351243088946557 48.19965560959, 16.351118640921797 48.19950003769327, 16.350951169518318 48.19936369511042, 16.350747116034892 48.19925182561274, 16.350514328568078 48.19916873170147, 16.350261760179283 48.199117609152964, 16.34999912459177 48.19910042412578, 16.25000087573864 48.19910040825405, 16.24973823767017 48.199117593214716, 16.249485666681007 48.199168715750886, 16.249252876439694 48.19925180974529, 16.24904881997628 48.199363679477166, 16.248881345384483 48.199500022510115, 16.248756893991796 48.199655595141486, 16.24868025260387 48.199824414053964, 16.2486543693546 48.19999998641755, 16.248653057837092 48.24999999781262))"); Geometry expected2 = Constructors.geomFromEWKT("POLYGON ((-120.00898315284118 29.999999999999467, -120.00898315284118 49.999999999988724, -120.00881054407824 50.001129625613444, -120.0082993510474 50.002215815187945, -120.00746921861013 50.003216831381316, -120.00635204829044 50.00409421217581, -120.0049907723172 50.004814247955025, -120.00343770376273 50.00534927578318, -120.00175252618048 50.00567874131385, -119.99999999999999 50.005789987696524, -80 50.005789987696524, -79.9982474738195 50.00567874131385, -79.99656229623727 50.00534927578318, -79.99500922768277 50.004814247955025, -79.99364795170956 50.00409421217581, -79.99253078138989 50.003216831381316, -79.99170064895262 50.002215815187945, -79.99118945592176 50.001129625613444, -79.99101684715882 49.999999999988724, -79.99101684715882 29.999999999999467, -79.99118945592176 29.998474584401656, -79.99170064895262 29.997007767335376, -79.99253078138989 29.995655921596196, -79.99364795170956 29.994471003601635, -79.99500922768277 29.99349855586065, -79.99656229623727 29.99277595576926, -79.9982474738195 29.99233097818683, -80 29.992180727189663, -119.99999999999999 29.992180727189663, -120.00175252618048 29.99233097818683, -120.00343770376273 29.99277595576926, -120.0049907723172 29.99349855586065, -120.00635204829044 29.994471003601635, -120.00746921861013 29.995655921596196, -120.0082993510474 29.997007767335376, -120.00881054407824 29.998474584401656, -120.00898315284118 29.999999999999467))"); Geometry expected3 = Constructors.geomFromEWKT("POLYGON ((-179.9982096288439 59.99995928994206, -179.9982598922349 59.99978513613418, -179.99837702566958 59.99961923978765, -179.99855652694714 59.99946797603053, -179.998791497433 59.99933715760413, -179.99907290724568 59.999231811518854, -179.9993899422849 59.99915598591007, -179.99973041976276 59.99911259450936, 179.9999187437241 59.999103304702594, 179.99957102955173 59.99912847347155, 179.99923979915093 59.999187133678575, 179.99893778069153 59.999277031220146, 179.99867658007233 59.99939471162435, 179.99846623498902 59.99953565276752, 179.9983148292063 59.99969443861621, 179.99822818185396 59.999864967323326, 179.9982096236965 60.00004068568767, 179.99825986898801 60.00021484097138, 179.99837698785996 60.00038074040053, 179.99855648032874 60.00053200837772, 179.99879144910062 60.00066283151926, 179.99907286455522 60.00076818209686, 179.9993899117333 60.00084401129125, 179.9997304059989 60.000887404824965, -179.99991873860708 60.00089669498742, -179.99957100633523 60.000871524742664, -179.9992397613717 60.000812861453035, -179.9989377341035 60.000722959691345, -179.99867653177037 60.00060527457202, -179.99846619232903 60.00046432893652, -179.99831479868513 60.000305539502236, -179.99822816812048 60.00013500866213, -179.9982096288439 59.99995928994206))"); Geometry expected4 = Constructors.geomFromEWKT("POLYGON ((-91.18706814560424 30.451931798138848, -91.18906814731041 30.453431798551634, -91.18908219577726 30.453444627150773, -91.1890930855116 30.4534595836974, -91.18910039802604 30.453476093420573, -91.18910385230312 30.453493521861454, -91.18910331559482 30.453511199255043, -91.1890988085245 30.4535284462689, -91.18909050429437 30.453544600109474, -91.18907872203003 30.453559039992996, -91.18906391451668 30.453571211001968, -91.18904665079899 30.453580645410415, -91.18902759431288 30.453586980658557, -91.18900747739012 30.453589973285787, -91.18898707311482 30.453589508286903, -91.18896716561396 30.453585603531685, -91.18894851992349 30.453578409078172, -91.18893185258821 30.453568201405897, -91.18693185327697 30.45206820105564, -91.18493185405761 30.450568200774196, -91.18491780618592 30.450555372061554, -91.18490691698157 30.450540415431583, -91.18489960490953 30.450523905659963, -91.18489615096698 30.450506477208492, -91.18489668788492 30.450488799843015, -91.18490119502782 30.450471552894637, -91.18490949918679 30.450455399153434, -91.18492128123634 30.450440959397913, -91.18493608839827 30.450428788538883, -91.18495335164187 30.45041935429472, -91.1849724075514 30.45041301921735, -91.18499252382057 30.450410026759716, -91.18501292739447 30.450410491920046, -91.18503283417732 30.45041439682263, -91.18505147916453 30.450421591404723, -91.18506814584102 30.450431799183303, -91.18706814560424 30.451931798138848))"); @@ -1199,27 +1199,27 @@ public void testBufferSpheroidal() throws FactoryException, TransformException, Geometry postgis_result4 = Constructors.geomFromEWKT("POLYGON((-91.18706814560713 30.45193179814003,-91.1890681473133 30.453431798552806,-91.18908219578012 30.453444627151963,-91.18909308551447 30.453459583698564,-91.18910039802891 30.453476093421752,-91.18910385230598 30.453493521862622,-91.1891033155977 30.453511199256216,-91.18909880852735 30.453528446270074,-91.18909050429724 30.453544600110646,-91.18907872203292 30.453559039994175,-91.18906391451956 30.453571211003144,-91.18904665080186 30.453580645411602,-91.18902759431575 30.453586980659722,-91.189007477393 30.45358997328696,-91.1889870731177 30.453589508288072,-91.18896716561683 30.45358560353286,-91.18894851992636 30.453578409079352,-91.18893185259108 30.453568201407066,-91.18693185325601 30.45206820103894,-91.1849318540605 30.450568200775386,-91.18491780618884 30.450555372062745,-91.18490691698447 30.450540415432766,-91.18489960491243 30.45052390566114,-91.18489615096986 30.450506477209665,-91.18489668788781 30.450488799844198,-91.1849011950307 30.45047155289582,-91.18490949918969 30.45045539915463,-91.18492128123923 30.450440959399103,-91.18493608840117 30.45042878854006,-91.18495335164478 30.45041935429591,-91.18497240755428 30.450413019218534,-91.18499252382345 30.450410026760903,-91.18501292739734 30.450410491921236,-91.1850328341802 30.450414396823803,-91.18505147916741 30.450421591405906,-91.1850681458439 30.450431799184482,-91.18706814560713 30.45193179814003))"); Geometry postgis_result5 = Constructors.geomFromEWKT("POLYGON((-120.08983152841193 29.999999999999993,-120.08983152841193 49.99999999999999,-120.08810544078257 50.011295055230505,-120.082993510474 50.02215353089088,-120.07469218610122 50.032158574496115,-120.06352048290445 50.040926345165026,-120.04990772317234 50.04812066585569,-120.03437703762728 50.0534658262464,-120.01752526180509 50.05675706194147,-119.99999999999999 50.05786832496988,-80 50.05786832496988,-79.98247473819491 50.05675706194147,-79.9656229623727 50.0534658262464,-79.95009227682766 50.04812066585569,-79.93647951709556 50.040926345165026,-79.92530781389875 50.032158574496115,-79.91700648952599 50.02215353089088,-79.91189455921743 50.011295055230505,-79.91016847158805 49.99999999999999,-79.91016847158805 29.999999999999993,-79.91189455921743 29.984744778414523,-79.91700648952599 29.970073573593833,-79.92530781389875 29.956550575941442,-79.93647951709556 29.944696041122494,-79.95009227682766 29.934966209462644,-79.9656229623727 29.927735669850765,-79.98247473819491 29.923282861557286,-80 29.921779286758486,-119.99999999999999 29.921779286758486,-120.01752526180509 29.923282861557286,-120.03437703762728 29.927735669850765,-120.04990772317234 29.934966209462644,-120.06352048290445 29.944696041122494,-120.07469218610122 29.956550575941442,-120.082993510474 29.970073573593833,-120.08810544078257 29.984744778414523,-120.08983152841193 29.999999999999993))"); - assertEquals(expected1, result1); + assertEquals(Functions.reducePrecision(expected1, 8), Functions.reducePrecision(result1, 8)); assertEquals(4326, Functions.getSRID(result1)); assertEquals(7.424558176442617E-8, comparePolygons(postgis_result1, result1), FP_TOLERANCE2); assertEquals(Spheroid.area(postgis_result1), Spheroid.area(result1), FP_TOLERANCE2); - assertEquals(expected2, result2); + assertEquals(Functions.reducePrecision(expected2, 8), Functions.reducePrecision(result2, 8)); assertEquals(4269, Functions.getSRID(result2)); assertEquals(Spheroid.area(postgis_result2), Spheroid.area(result2), 10); assertEquals(7.424558176442617E-8, comparePolygons(postgis_result2, result2), FP_TOLERANCE2); - assertEquals(expected3, result3); + assertEquals(Functions.reducePrecision(expected3, 8), Functions.reducePrecision(result3, 8)); assertEquals(4269, Functions.getSRID(result3)); assertEquals(Spheroid.area(postgis_result3), Spheroid.area(result3), FP_TOLERANCE2); assertEquals(7.424558176442617E-8, comparePolygons(postgis_result3, result3), FP_TOLERANCE2); - assertEquals(expected4, result4); + assertEquals(Functions.reducePrecision(expected4, 8), Functions.reducePrecision(result4, 8)); assertEquals(4326, Functions.getSRID(result4)); assertEquals(Spheroid.area(postgis_result4), Spheroid.area(result4), FP_TOLERANCE2); assertEquals(7.424558176442617E-8, comparePolygons(postgis_result4, result4), FP_TOLERANCE2); - assertEquals(expected5, result5); + assertEquals(Functions.reducePrecision(expected5, 8), Functions.reducePrecision(result5, 8)); assertEquals(4269, Functions.getSRID(result5)); assertEquals(Spheroid.area(postgis_result5), Spheroid.area(result5), 10); assertEquals(7.424558176442617E-8, comparePolygons(postgis_result5, result5), FP_TOLERANCE2); diff --git a/docs/api/sql/Function.md b/docs/api/sql/Function.md index 3c09732c16..5bf27edb29 100644 --- a/docs/api/sql/Function.md +++ b/docs/api/sql/Function.md @@ -552,8 +552,7 @@ Output: `LINESTRING Z(-1 -1 0, 10 5 5)` ## ST_Buffer Introduction: Returns a geometry/geography that represents all points whose distance from this Geometry/geography is less than or equal to distance. The function supports both Planar/Euclidean and Spheroidal/Geodesic buffering (Since v1.6.0). - -Buffer Style Parameters: +Buffer Style Parameters: The optional third parameter controls the buffer accuracy and style. Buffer accuracy is specified by the number of line segments approximating a quarter circle, with a default of 8 segments. Buffer style can be set by providing blank-separated key=value pairs in a list format. diff --git a/python/tests/sql/test_dataframe_api.py b/python/tests/sql/test_dataframe_api.py index 0917bb8887..c73f9e3bf0 100644 --- a/python/tests/sql/test_dataframe_api.py +++ b/python/tests/sql/test_dataframe_api.py @@ -74,7 +74,7 @@ (stf.ST_BestSRID, ("geom",), "triangle_geom", "", 3395), (stf.ST_Boundary, ("geom",), "triangle_geom", "", "LINESTRING (0 0, 1 0, 1 1, 0 0)"), (stf.ST_Buffer, ("point", 1.0), "point_geom", "ST_ReducePrecision(geom, 2)", "POLYGON ((0.98 0.8, 0.92 0.62, 0.83 0.44, 0.71 0.29, 0.56 0.17, 0.38 0.08, 0.2 0.02, 0 0, -0.2 0.02, -0.38 0.08, -0.56 0.17, -0.71 0.29, -0.83 0.44, -0.92 0.62, -0.98 0.8, -1 1, -0.98 1.2, -0.92 1.38, -0.83 1.56, -0.71 1.71, -0.56 1.83, -0.38 1.92, -0.2 1.98, 0 2, 0.2 1.98, 0.38 1.92, 0.56 1.83, 0.71 1.71, 0.83 1.56, 0.92 1.38, 0.98 1.2, 1 1, 0.98 0.8))"), - (stf.ST_Buffer, ("point", 1.0, True), "point_geom", "ST_ReducePrecision(geom, 2)", "POLYGON ((0.98 0.8, 0.92 0.62, 0.83 0.44, 0.71 0.29, 0.56 0.17, 0.38 0.08, 0.2 0.02, 0 0, -0.2 0.02, -0.38 0.08, -0.56 0.17, -0.71 0.29, -0.83 0.44, -0.92 0.62, -0.98 0.8, -1 1, -0.98 1.2, -0.92 1.38, -0.83 1.56, -0.71 1.71, -0.56 1.83, -0.38 1.92, -0.2 1.98, 0 2, 0.2 1.98, 0.38 1.92, 0.56 1.83, 0.71 1.71, 0.83 1.56, 0.92 1.38, 0.98 1.2, 1 1, 0.98 0.8))"), + (stf.ST_Buffer, ("point", 1.0, "", True), "point_geom", "ST_ReducePrecision(geom, 2)", "POLYGON ((0.98 0.8, 0.92 0.62, 0.83 0.44, 0.71 0.29, 0.56 0.17, 0.38 0.08, 0.2 0.02, 0 0, -0.2 0.02, -0.38 0.08, -0.56 0.17, -0.71 0.29, -0.83 0.44, -0.92 0.62, -0.98 0.8, -1 1, -0.98 1.2, -0.92 1.38, -0.83 1.56, -0.71 1.71, -0.56 1.83, -0.38 1.92, -0.2 1.98, 0 2, 0.2 1.98, 0.38 1.92, 0.56 1.83, 0.71 1.71, 0.83 1.56, 0.92 1.38, 0.98 1.2, 1 1, 0.98 0.8))"), (stf.ST_BuildArea, ("geom",), "multiline_geom", "ST_Normalize(geom)", "POLYGON ((0 0, 1 1, 1 0, 0 0))"), (stf.ST_BoundingDiagonal, ("geom",), "square_geom", "ST_BoundingDiagonal(geom)", "LINESTRING (1 0, 2 1)"), (stf.ST_Centroid, ("geom",), "triangle_geom", "ST_ReducePrecision(geom, 2)", "POINT (0.67 0.33)"), diff --git a/python/tests/streaming/spark/test_constructor_functions.py b/python/tests/streaming/spark/test_constructor_functions.py index 9e066063a5..cae6ec7c94 100644 --- a/python/tests/streaming/spark/test_constructor_functions.py +++ b/python/tests/streaming/spark/test_constructor_functions.py @@ -46,6 +46,11 @@ .with_arguments(["ST_GeomFromText('POINT (21 52)')", "1.0"]) .with_expected_result(3.1214451522580533) .with_transform("ST_AREA")), + (SuiteContainer.empty() + .with_function_name("ST_Buffer") + .with_arguments(["ST_GeomFromText('POINT (21 52)')", "1.0", "", True]) + .with_expected_result(3.1214451522580533) + .with_transform("ST_AREA")), (SuiteContainer.empty() .with_function_name("ST_Distance") .with_arguments(["ST_GeomFromText('POINT (21 52)')", "ST_GeomFromText('POINT (21 53)')"]) From 2c2d97e1939a96d028deab2135ed09e822c4c245 Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Fri, 23 Feb 2024 16:33:40 -0500 Subject: [PATCH 21/54] Change function signature; fix tests --- .../org/apache/sedona/common/Functions.java | 10 +-- .../apache/sedona/common/FunctionsTest.java | 16 ++--- .../sedona/flink/expressions/Functions.java | 8 +-- .../org/apache/sedona/flink/FunctionTest.java | 61 +++++++++++++------ python/sedona/sql/st_functions.py | 6 +- python/tests/sql/test_dataframe_api.py | 2 +- python/tests/sql/test_function.py | 6 +- .../spark/test_constructor_functions.py | 2 +- .../sedona_sql/expressions/st_functions.scala | 8 +-- .../sedona/sql/dataFrameAPITestScala.scala | 20 ++++-- .../apache/sedona/sql/functionTestScala.scala | 9 --- 11 files changed, 87 insertions(+), 61 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 359a3cc54b..9a93965872 100644 --- a/common/src/main/java/org/apache/sedona/common/Functions.java +++ b/common/src/main/java/org/apache/sedona/common/Functions.java @@ -89,14 +89,14 @@ public static Geometry boundary(Geometry geometry) { } public static Geometry buffer(Geometry geometry, double radius) throws FactoryException, TransformException { - return buffer(geometry, radius, "", false); + return buffer(geometry, radius, false, ""); } - public static Geometry buffer(Geometry geometry, double radius, String params) throws FactoryException, TransformException { - return buffer(geometry, radius, params, false); + public static Geometry buffer(Geometry geometry, double radius, boolean useSpheroid) throws FactoryException, TransformException { + return buffer(geometry, radius, useSpheroid, ""); } - public static Geometry buffer(Geometry geometry, double radius, String params, boolean useSpheroid) throws FactoryException, TransformException { + public static Geometry buffer(Geometry geometry, double radius, boolean useSpheroid, String params) throws FactoryException, TransformException { BufferParameters bufferParameters = new BufferParameters(); // Processing parameters @@ -226,6 +226,8 @@ public static int bestSRID(Geometry geometry) { // Calculate the center of the envelope double centerX = (envelope.getMinX() + envelope.getMaxX()) / 2.0; // centroid.getX(); double centerY = (envelope.getMinY() + envelope.getMaxY()) / 2.0; // centroid.getY(); + System.out.println("\ncenterX: "+ centerX); + System.out.println("centerY: "+ centerY); // Calculate angular width and height double xwidth = Spheroid.angularWidth(envelope); diff --git a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java index ebbbc2b4ea..92e0c74156 100644 --- a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java +++ b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java @@ -1158,17 +1158,17 @@ public void testBuffer() throws FactoryException, TransformException { assertEquals(expected, actual); LineString lineString = GEOMETRY_FACTORY.createLineString(coordArray(0, 0, 50, 70, 100, 100)); - actual = Functions.asWKT(Functions.reducePrecision(Functions.buffer(lineString, 10, "side=left"), 4)); + actual = Functions.asWKT(Functions.reducePrecision(Functions.buffer(lineString, 10, false, "side=left"), 4)); expected = "POLYGON ((50 70, 0 0, -8.1373 5.8124, 41.8627 75.8124, 43.2167 77.3476, 44.855 78.5749, 94.855 108.5749, 100 100, 50 70))"; assertEquals(expected, actual); lineString = GEOMETRY_FACTORY.createLineString(coordArray(0, 0, 50, 70, 70, -3)); - actual = Functions.asWKT(Functions.reducePrecision(Functions.buffer(lineString, 10, "endcap=square"), 4)); + actual = Functions.asWKT(Functions.reducePrecision(Functions.buffer(lineString, 10, false, "endcap=square"), 4)); expected = "POLYGON ((43.2156 77.3465, 44.8523 78.5733, 46.7044 79.4413, 48.6944 79.9144, 50.739 79.9727, 52.7527 79.6137, 54.6512 78.8525, 56.3552 77.7209, 57.7932 76.2663, 58.9052 74.5495, 59.6446 72.6424, 79.6446 -0.3576, 82.2869 -10.0022, 62.9978 -15.2869, 45.9128 47.0733, 8.1373 -5.8124, 2.325 -13.9497, -13.9497 -2.325, 41.8627 75.8124, 43.2156 77.3465))"; assertEquals(expected, actual); Point point = GEOMETRY_FACTORY.createPoint(new Coordinate(100, 90)); - actual = Functions.asWKT(Functions.reducePrecision(Functions.buffer(point, 10, "quad_segs=2"), 4)); + actual = Functions.asWKT(Functions.reducePrecision(Functions.buffer(point, 10, false, "quad_segs=2"), 4)); expected = "POLYGON ((107.0711 82.9289, 100 80, 92.9289 82.9289, 90 90, 92.9289 97.0711, 100 100, 107.0711 97.0711, 110 90, 107.0711 82.9289))"; assertEquals(expected, actual); } @@ -1181,11 +1181,11 @@ public void testBufferSpheroidal() throws FactoryException, TransformException, Geometry linestring1 = Constructors.geomFromEWKT("LINESTRING(-91.185 30.4505, -91.187 30.452, -91.189 30.4535)"); Geometry polygon3 = Constructors.geomFromWKT("POLYGON((-120 30, -80 30, -80 50, -120 50, -120 30))", 4269); - Geometry result1 = Functions.buffer(polygon1, 100, "", true); - Geometry result2 = Functions.buffer(polygon2, 1000, "", true); - Geometry result3 = Functions.buffer(point1, 100, "", true); - Geometry result4 = Functions.buffer(linestring1, 10, "", true); - Geometry result5 = Functions.buffer(polygon3, 10000, "", true); + Geometry result1 = Functions.buffer(polygon1, 100, true); + Geometry result2 = Functions.buffer(polygon2, 1000, true); + Geometry result3 = Functions.buffer(point1, 100, true); + Geometry result4 = Functions.buffer(linestring1, 10, true); + Geometry result5 = Functions.buffer(polygon3, 10000, true); Geometry expected1 = Constructors.geomFromEWKT("POLYGON ((16.248653057837092 48.24999999781262, 16.24867891451385 48.25017542568975, 16.24875549793627 48.25034411793438, 16.24887986793391 48.25049959710696, 16.249047249310912 48.25063589306856, 16.249251215157546 48.250747772240985, 16.249483933612066 48.25083093858835, 16.249736468600165 48.250882198599385, 16.249999123001867 48.25089958393145, 16.35000087730807 48.25089956809054, 16.35026352923699 48.250882182692266, 16.350516061632444 48.250830922668115, 16.35074877731957 48.25074775640202, 16.350952740192536 48.250635877460866, 16.351120118386593 48.25049958194538, 16.351244485020548 48.25034410350243, 16.35132106495956 48.25017541233756, 16.351346918125614 48.24999998594946, 16.351345606585333 48.199999998284284, 16.351319726848168 48.19982442741669, 16.351243088946557 48.19965560959, 16.351118640921797 48.19950003769327, 16.350951169518318 48.19936369511042, 16.350747116034892 48.19925182561274, 16.350514328568078 48.19916873170147, 16.350261760179283 48.199117609152964, 16.34999912459177 48.19910042412578, 16.25000087573864 48.19910040825405, 16.24973823767017 48.199117593214716, 16.249485666681007 48.199168715750886, 16.249252876439694 48.19925180974529, 16.24904881997628 48.199363679477166, 16.248881345384483 48.199500022510115, 16.248756893991796 48.199655595141486, 16.24868025260387 48.199824414053964, 16.2486543693546 48.19999998641755, 16.248653057837092 48.24999999781262))"); Geometry expected2 = Constructors.geomFromEWKT("POLYGON ((-120.00898315284118 29.999999999999467, -120.00898315284118 49.999999999988724, -120.00881054407824 50.001129625613444, -120.0082993510474 50.002215815187945, -120.00746921861013 50.003216831381316, -120.00635204829044 50.00409421217581, -120.0049907723172 50.004814247955025, -120.00343770376273 50.00534927578318, -120.00175252618048 50.00567874131385, -119.99999999999999 50.005789987696524, -80 50.005789987696524, -79.9982474738195 50.00567874131385, -79.99656229623727 50.00534927578318, -79.99500922768277 50.004814247955025, -79.99364795170956 50.00409421217581, -79.99253078138989 50.003216831381316, -79.99170064895262 50.002215815187945, -79.99118945592176 50.001129625613444, -79.99101684715882 49.999999999988724, -79.99101684715882 29.999999999999467, -79.99118945592176 29.998474584401656, -79.99170064895262 29.997007767335376, -79.99253078138989 29.995655921596196, -79.99364795170956 29.994471003601635, -79.99500922768277 29.99349855586065, -79.99656229623727 29.99277595576926, -79.9982474738195 29.99233097818683, -80 29.992180727189663, -119.99999999999999 29.992180727189663, -120.00175252618048 29.99233097818683, -120.00343770376273 29.99277595576926, -120.0049907723172 29.99349855586065, -120.00635204829044 29.994471003601635, -120.00746921861013 29.995655921596196, -120.0082993510474 29.997007767335376, -120.00881054407824 29.998474584401656, -120.00898315284118 29.999999999999467))"); diff --git a/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java b/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java index 98e49d7152..328591742f 100644 --- a/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java +++ b/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java @@ -79,16 +79,16 @@ public Geometry eval(@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.j @DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) public Geometry eval(@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) - Object o, @DataTypeHint("Double") Double radius, @DataTypeHint("String") String params) throws FactoryException, TransformException { + Object o, @DataTypeHint("Double") Double radius, @DataTypeHint("Boolean") Boolean useSpheroid) throws FactoryException, TransformException { Geometry geom = (Geometry) o; - return org.apache.sedona.common.Functions.buffer(geom, radius, params); + return org.apache.sedona.common.Functions.buffer(geom, radius, useSpheroid); } @DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) public Geometry eval(@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) - Object o, @DataTypeHint("Double") Double radius, @DataTypeHint("String") String params, @DataTypeHint("Boolean") Boolean useSpheroid) throws FactoryException, TransformException { + Object o, @DataTypeHint("Double") Double radius, @DataTypeHint("Boolean") Boolean useSpheroid, @DataTypeHint("String") String params) throws FactoryException, TransformException { Geometry geom = (Geometry) o; - return org.apache.sedona.common.Functions.buffer(geom, radius, params, useSpheroid); + return org.apache.sedona.common.Functions.buffer(geom, radius, useSpheroid, params); } } diff --git a/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java b/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java index 42d0cc3b76..d13dfc1f35 100644 --- a/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java +++ b/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java @@ -84,43 +84,64 @@ public void testBuffer() { Geometry result = (Geometry) first(bufferTable).getField(0); assert(result instanceof Polygon); - String actual = (String) first(tableEnv.sqlQuery("SELECT ST_AsText(ST_ReducePrecision(ST_Buffer(ST_GeomFromWKT('LINESTRING(0 0, 50 70, 100 100)'), 10, 'side=left'), 4))")).getField(0); + String actual = (String) first(tableEnv.sqlQuery("SELECT ST_AsText(ST_ReducePrecision(ST_Buffer(ST_GeomFromWKT('LINESTRING(0 0, 50 70, 100 100)'), 10, false, 'side=left'), 4))")).getField(0); String expected = "POLYGON ((50 70, 0 0, -8.1373 5.8124, 41.8627 75.8124, 43.2167 77.3476, 44.855 78.5749, 94.855 108.5749, 100 100, 50 70))"; assertEquals(expected, actual); - actual = (String) first(tableEnv.sqlQuery("SELECT ST_AsText(ST_ReducePrecision(ST_Buffer(ST_GeomFromWKT('LINESTRING(0 0, 50 70, 70 -3)'), 10, 'endcap=square'), 4))")).getField(0); + actual = (String) first(tableEnv.sqlQuery("SELECT ST_AsText(ST_ReducePrecision(ST_Buffer(ST_GeomFromWKT('LINESTRING(0 0, 50 70, 70 -3)'), 10, false, 'endcap=square'), 4))")).getField(0); expected = "POLYGON ((43.2156 77.3465, 44.8523 78.5733, 46.7044 79.4413, 48.6944 79.9144, 50.739 79.9727, 52.7527 79.6137, 54.6512 78.8525, 56.3552 77.7209, 57.7932 76.2663, 58.9052 74.5495, 59.6446 72.6424, 79.6446 -0.3576, 82.2869 -10.0022, 62.9978 -15.2869, 45.9128 47.0733, 8.1373 -5.8124, 2.325 -13.9497, -13.9497 -2.325, 41.8627 75.8124, 43.2156 77.3465))"; assertEquals(expected, actual); - actual = (String) first(tableEnv.sqlQuery("SELECT ST_AsText(ST_ReducePrecision(ST_Buffer(ST_Point(100, 90), 200, 'quad_segs=4'), 4))")).getField(0); +// actual = (String) first(tableEnv.sqlQuery("SELECT ST_AsText(ST_ReducePrecision(ST_Buffer(ST_GeomFromWKT('LINESTRING(179.95 -10, -179.95 -10)'), 10), 4))")).getField(0); +// expected = "POLYGON ((43.2156 77.3465, 44.8523 78.5733, 46.7044 79.4413, 48.6944 79.9144, 50.739 79.9727, 52.7527 79.6137, 54.6512 78.8525, 56.3552 77.7209, 57.7932 76.2663, 58.9052 74.5495, 59.6446 72.6424, 79.6446 -0.3576, 82.2869 -10.0022, 62.9978 -15.2869, 45.9128 47.0733, 8.1373 -5.8124, 2.325 -13.9497, -13.9497 -2.325, 41.8627 75.8124, 43.2156 77.3465))"; +// System.out.println("actual: "+actual); +// String area = (String) first(tableEnv.sqlQuery("ST_AsText(ST_Area(ST_GeomFromWKT('POLYGON ((181.05 0, 181.05 -20, 170.95 -20, 160.95 -20, 160.95 0, 171.05 0, 181.05 0))')))")).getField(0); +// System.out.println("area: "+area); +// assertEquals(expected, actual); + +// actual = (String) first(tableEnv.sqlQuery("SELECT ST_AsText(ST_ReducePrecision(ST_Buffer(ST_GeomFromWKT('LINESTRING(179.95 -10, -179.95 -10)'), 10, true, 'endcap=square'), 4))")).getField(0); +// expected = "POLYGON ((43.2156 77.3465, 44.8523 78.5733, 46.7044 79.4413, 48.6944 79.9144, 50.739 79.9727, 52.7527 79.6137, 54.6512 78.8525, 56.3552 77.7209, 57.7932 76.2663, 58.9052 74.5495, 59.6446 72.6424, 79.6446 -0.3576, 82.2869 -10.0022, 62.9978 -15.2869, 45.9128 47.0733, 8.1373 -5.8124, 2.325 -13.9497, -13.9497 -2.325, 41.8627 75.8124, 43.2156 77.3465))"; +// System.out.println("actual: "+actual); +// area = (String) first(tableEnv.sqlQuery("SELECT ST_AsText(ST_Area(ST_ReducePrecision(ST_Buffer(ST_GeomFromWKT('LINESTRING(179.95 -10, -179.95 -10)'), 10, 'endcap=square', true), 4)))")).getField(0); +// System.out.println("area: "+area); +// assertEquals(expected, actual); + + actual = (String) first(tableEnv.sqlQuery("SELECT ST_AsText(ST_ReducePrecision(ST_Buffer(ST_Point(100, 90), 200, false, 'quad_segs=4'), 4))")).getField(0); expected = "POLYGON ((284.7759 13.4633, 241.4214 -51.4214, 176.5367 -94.7759, 100 -110, 23.4633 -94.7759, -41.4214 -51.4214, -84.7759 13.4633, -100 90, -84.7759 166.5367, -41.4214 231.4214, 23.4633 274.7759, 100 290, 176.5367 274.7759, 241.4214 231.4214, 284.7759 166.5367, 300 90, 284.7759 13.4633))"; assertEquals(expected, actual); - actual = (String) first(tableEnv.sqlQuery("SELECT ST_AsText(ST_ReducePrecision(ST_Buffer(ST_GeomFromWKT('LINESTRING(-91.185 30.4505, -91.187 30.452, -91.189 30.4535)'), 10, 'side=left', true), 4))")).getField(0); + actual = (String) first(tableEnv.sqlQuery("SELECT ST_AsText(ST_ReducePrecision(ST_Buffer(ST_GeomFromWKT('LINESTRING(0 0, 50 70, 70 -3)'), 10, true, 'endcap=square'), 4))")).getField(0); expected = "POLYGON ((-91.187 30.452, -91.185 30.4505, -91.1851 30.4504, -91.1871 30.4519, -91.1891 30.4534, -91.189 30.4535, -91.187 30.452))"; - assertEquals(expected, actual); + System.out.println(actual); +// assertEquals(expected, actual); - actual = (String) first(tableEnv.sqlQuery("SELECT ST_AsText(ST_ReducePrecision(ST_Buffer(ST_GeomFromWKT('POLYGON((-120 30, -80 30, -80 50, -120 50, -120 30))'), 200, 'quad_segs=4', true), 4))")).getField(0); + actual = (String) first(tableEnv.sqlQuery("SELECT ST_AsText(ST_ReducePrecision(ST_Buffer(ST_GeomFromWKT('POLYGON((-120 30, -80 30, -80 50, -120 50, -120 30))'), 200, true, 'quad_segs=4'), 4))")).getField(0); expected = "POLYGON ((-120.0018 50, -120.0017 50.0004, -120.0013 50.0008, -120.0007 50.0011, -120 50.0012, -80 50.0012, -79.9993 50.0011, -79.9987 50.0008, -79.9983 50.0004, -79.9982 50, -79.9982 30, -79.9983 29.9994, -79.9987 29.9989, -79.9993 29.9986, -80 29.9984, -120 29.9984, -120.0007 29.9986, -120.0013 29.9989, -120.0017 29.9994, -120.0018 30, -120.0018 50))"; assertEquals(expected, actual); } @Test public void testBestSRID() { - Table table1 = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('POINT (160 40)') AS geom"); - table1 = table1.select(call(Functions.ST_BestSRID.class.getSimpleName(), $("geom"))); - int result = (int) first(table1).getField(0); - assertEquals(32657, result); - - Table table2 = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('LINESTRING(-91.185 30.4505, -91.187 30.452, -91.189 30.4535)') AS geom"); - table2 = table2.select(call(Functions.ST_BestSRID.class.getSimpleName(), $("geom"))); - result = (int) first(table2).getField(0); - assertEquals(32615, result); - - Table table3 = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('POLYGON((-120 30, -80 30, -80 50, -120 50, -120 30))') AS geom"); - table3 = table3.select(call(Functions.ST_BestSRID.class.getSimpleName(), $("geom"))); - result = (int) first(table3).getField(0); - assertEquals(3395, result); +// Table table1 = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('POINT (160 40)') AS geom"); +// table1 = table1.select(call(Functions.ST_BestSRID.class.getSimpleName(), $("geom"))); +// int result = (int) first(table1).getField(0); +// assertEquals(32657, result); +// +// Table table2 = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('LINESTRING(-91.185 30.4505, -91.187 30.452, -91.189 30.4535)') AS geom"); +// table2 = table2.select(call(Functions.ST_BestSRID.class.getSimpleName(), $("geom"))); +// result = (int) first(table2).getField(0); +// assertEquals(32615, result); + + Table table4 = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('LINESTRING(179.95 -80, -179.95 -80)') AS geom"); + table4 = table4.select(call(Functions.ST_BestSRID.class.getSimpleName(), $("geom"))); + int result = (int) first(table4).getField(0); + System.out.println(result); +// assertEquals(32615, result); + +// Table table3 = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('POLYGON((-120 30, -80 30, -80 50, -120 50, -120 30))') AS geom"); +// table3 = table3.select(call(Functions.ST_BestSRID.class.getSimpleName(), $("geom"))); +// result = (int) first(table3).getField(0); +// assertEquals(3395, result); } @Test diff --git a/python/sedona/sql/st_functions.py b/python/sedona/sql/st_functions.py index 4888903966..dd3e107617 100644 --- a/python/sedona/sql/st_functions.py +++ b/python/sedona/sql/st_functions.py @@ -343,10 +343,10 @@ def ST_Buffer(geometry: ColumnOrName, buffer: ColumnOrNameOrNumber, parameters: """ if parameters is None and useSpheroid is None: args = (geometry, buffer) - elif useSpheroid is None: - args = (geometry, buffer, parameters) + elif parameters is None: + args = (geometry, buffer, useSpheroid) else: - args = (geometry, buffer, parameters, useSpheroid) + args = (geometry, buffer, useSpheroid, parameters) return _call_st_function("ST_Buffer", args) diff --git a/python/tests/sql/test_dataframe_api.py b/python/tests/sql/test_dataframe_api.py index c73f9e3bf0..0917bb8887 100644 --- a/python/tests/sql/test_dataframe_api.py +++ b/python/tests/sql/test_dataframe_api.py @@ -74,7 +74,7 @@ (stf.ST_BestSRID, ("geom",), "triangle_geom", "", 3395), (stf.ST_Boundary, ("geom",), "triangle_geom", "", "LINESTRING (0 0, 1 0, 1 1, 0 0)"), (stf.ST_Buffer, ("point", 1.0), "point_geom", "ST_ReducePrecision(geom, 2)", "POLYGON ((0.98 0.8, 0.92 0.62, 0.83 0.44, 0.71 0.29, 0.56 0.17, 0.38 0.08, 0.2 0.02, 0 0, -0.2 0.02, -0.38 0.08, -0.56 0.17, -0.71 0.29, -0.83 0.44, -0.92 0.62, -0.98 0.8, -1 1, -0.98 1.2, -0.92 1.38, -0.83 1.56, -0.71 1.71, -0.56 1.83, -0.38 1.92, -0.2 1.98, 0 2, 0.2 1.98, 0.38 1.92, 0.56 1.83, 0.71 1.71, 0.83 1.56, 0.92 1.38, 0.98 1.2, 1 1, 0.98 0.8))"), - (stf.ST_Buffer, ("point", 1.0, "", True), "point_geom", "ST_ReducePrecision(geom, 2)", "POLYGON ((0.98 0.8, 0.92 0.62, 0.83 0.44, 0.71 0.29, 0.56 0.17, 0.38 0.08, 0.2 0.02, 0 0, -0.2 0.02, -0.38 0.08, -0.56 0.17, -0.71 0.29, -0.83 0.44, -0.92 0.62, -0.98 0.8, -1 1, -0.98 1.2, -0.92 1.38, -0.83 1.56, -0.71 1.71, -0.56 1.83, -0.38 1.92, -0.2 1.98, 0 2, 0.2 1.98, 0.38 1.92, 0.56 1.83, 0.71 1.71, 0.83 1.56, 0.92 1.38, 0.98 1.2, 1 1, 0.98 0.8))"), + (stf.ST_Buffer, ("point", 1.0, True), "point_geom", "ST_ReducePrecision(geom, 2)", "POLYGON ((0.98 0.8, 0.92 0.62, 0.83 0.44, 0.71 0.29, 0.56 0.17, 0.38 0.08, 0.2 0.02, 0 0, -0.2 0.02, -0.38 0.08, -0.56 0.17, -0.71 0.29, -0.83 0.44, -0.92 0.62, -0.98 0.8, -1 1, -0.98 1.2, -0.92 1.38, -0.83 1.56, -0.71 1.71, -0.56 1.83, -0.38 1.92, -0.2 1.98, 0 2, 0.2 1.98, 0.38 1.92, 0.56 1.83, 0.71 1.71, 0.83 1.56, 0.92 1.38, 0.98 1.2, 1 1, 0.98 0.8))"), (stf.ST_BuildArea, ("geom",), "multiline_geom", "ST_Normalize(geom)", "POLYGON ((0 0, 1 1, 1 0, 0 0))"), (stf.ST_BoundingDiagonal, ("geom",), "square_geom", "ST_BoundingDiagonal(geom)", "LINESTRING (1 0, 2 1)"), (stf.ST_Centroid, ("geom",), "triangle_geom", "ST_ReducePrecision(geom, 2)", "POINT (0.67 0.33)"), diff --git a/python/tests/sql/test_function.py b/python/tests/sql/test_function.py index 12b4ec70cf..066e56fd8f 100644 --- a/python/tests/sql/test_function.py +++ b/python/tests/sql/test_function.py @@ -98,15 +98,15 @@ def test_st_buffer(self): actual = function_df.take(1)[0][0].wkt assert actual == "POLYGON ((-98.02 41.77, -98.02 41.78, -98.02 41.8, -98.02 41.81, -98.02 41.82, -98.02 41.83, -98.02 41.84, -98.02 41.85, -98.02 41.86, -98.02 41.87, -98.02 41.89, -98.02 41.9, -98.02 41.91, -98.02 41.92, -98.02 41.93, -98.02 41.95, -98.02 41.98, -98.02 42, -98.02 42.01, -98.02 42.02, -98.02 42.04, -98.02 42.05, -98.02 42.07, -98.02 42.09, -98.02 42.11, -98.02 42.12, -97.99 42.31, -97.93 42.5, -97.84 42.66, -97.72 42.81, -97.57 42.93, -97.4 43.02, -97.21 43.07, -97.02 43.09, -97 43.09, -96.98 43.09, -96.97 43.09, -96.96 43.09, -96.95 43.09, -96.94 43.09, -96.92 43.09, -96.91 43.09, -96.89 43.09, -96.88 43.09, -96.86 43.09, -96.84 43.09, -96.83 43.09, -96.82 43.09, -96.81 43.09, -96.8 43.09, -96.79 43.09, -96.78 43.09, -96.76 43.09, -96.74 43.09, -96.73 43.09, -96.71 43.09, -96.7 43.09, -96.69 43.09, -96.68 43.09, -96.66 43.09, -96.65 43.09, -96.64 43.09, -96.63 43.09, -96.62 43.09, -96.61 43.09, -96.6 43.09, -96.59 43.09, -96.58 43.09, -96.57 43.09, -96.56 43.09, -96.55 43.09, -96.36 43.07, -96.17 43.01, -96 42.92, -95.86 42.8, -95.73 42.66, -95.64 42.49, -95.58 42.31, -95.56 42.12, -95.56 42.1, -95.56 42.09, -95.56 42.08, -95.56 42.07, -95.56 42.06, -95.56 42.04, -95.56 42, -95.56 41.99, -95.56 41.98, -95.56 41.97, -95.56 41.96, -95.56 41.95, -95.56 41.94, -95.56 41.93, -95.56 41.92, -95.56 41.91, -95.56 41.9, -95.56 41.89, -95.56 41.88, -95.56 41.87, -95.56 41.86, -95.56 41.85, -95.56 41.83, -95.56 41.82, -95.56 41.81, -95.56 41.8, -95.56 41.79, -95.56 41.78, -95.56 41.77, -95.56 41.76, -95.56 41.75, -95.56 41.74, -95.58 41.54, -95.63 41.36, -95.72 41.19, -95.85 41.03, -96 40.91, -96.17 40.82, -96.36 40.76, -96.55 40.74, -96.56 40.74, -96.57 40.74, -96.58 40.74, -96.59 40.74, -96.6 40.74, -96.62 40.74, -96.63 40.74, -96.64 40.74, -96.65 40.74, -96.67 40.74, -96.68 40.74, -96.69 40.74, -96.7 40.74, -96.71 40.74, -96.72 40.74, -96.73 40.74, -96.74 40.74, -96.75 40.74, -96.76 40.74, -96.77 40.74, -96.78 40.74, -96.79 40.74, -96.8 40.74, -96.81 40.74, -96.82 40.74, -96.83 40.74, -96.85 40.74, -96.86 40.74, -96.88 40.74, -96.9 40.74, -96.91 40.74, -96.92 40.74, -96.93 40.74, -96.94 40.74, -96.95 40.74, -96.97 40.74, -96.98 40.74, -96.99 40.74, -97.01 40.74, -97.02 40.74, -97.22 40.76, -97.4 40.82, -97.57 40.91, -97.72 41.03, -97.85 41.18, -97.94 41.35, -98 41.54, -98.02 41.73, -98.02 41.75, -98.02 41.76, -98.02 41.77))" - function_df = self.spark.sql("select ST_ReducePrecision(ST_Buffer(polygondf.countyshape, 1, "", true), 2) from polygondf") + function_df = self.spark.sql("select ST_ReducePrecision(ST_Buffer(polygondf.countyshape, 1, true), 2) from polygondf") actual = function_df.take(1)[0][0].wkt assert actual == "POLYGON ((-98.02 41.77, -98.02 41.78, -98.02 41.8, -98.02 41.81, -98.02 41.82, -98.02 41.83, -98.02 41.84, -98.02 41.85, -98.02 41.86, -98.02 41.87, -98.02 41.89, -98.02 41.9, -98.02 41.91, -98.02 41.92, -98.02 41.93, -98.02 41.95, -98.02 41.98, -98.02 42, -98.02 42.01, -98.02 42.02, -98.02 42.04, -98.02 42.05, -98.02 42.07, -98.02 42.09, -98.02 42.11, -98.02 42.12, -97.99 42.31, -97.93 42.5, -97.84 42.66, -97.72 42.81, -97.57 42.93, -97.4 43.02, -97.21 43.07, -97.02 43.09, -97 43.09, -96.98 43.09, -96.97 43.09, -96.96 43.09, -96.95 43.09, -96.94 43.09, -96.92 43.09, -96.91 43.09, -96.89 43.09, -96.88 43.09, -96.86 43.09, -96.84 43.09, -96.83 43.09, -96.82 43.09, -96.81 43.09, -96.8 43.09, -96.79 43.09, -96.78 43.09, -96.76 43.09, -96.74 43.09, -96.73 43.09, -96.71 43.09, -96.7 43.09, -96.69 43.09, -96.68 43.09, -96.66 43.09, -96.65 43.09, -96.64 43.09, -96.63 43.09, -96.62 43.09, -96.61 43.09, -96.6 43.09, -96.59 43.09, -96.58 43.09, -96.57 43.09, -96.56 43.09, -96.55 43.09, -96.36 43.07, -96.17 43.01, -96 42.92, -95.86 42.8, -95.73 42.66, -95.64 42.49, -95.58 42.31, -95.56 42.12, -95.56 42.1, -95.56 42.09, -95.56 42.08, -95.56 42.07, -95.56 42.06, -95.56 42.04, -95.56 42, -95.56 41.99, -95.56 41.98, -95.56 41.97, -95.56 41.96, -95.56 41.95, -95.56 41.94, -95.56 41.93, -95.56 41.92, -95.56 41.91, -95.56 41.9, -95.56 41.89, -95.56 41.88, -95.56 41.87, -95.56 41.86, -95.56 41.85, -95.56 41.83, -95.56 41.82, -95.56 41.81, -95.56 41.8, -95.56 41.79, -95.56 41.78, -95.56 41.77, -95.56 41.76, -95.56 41.75, -95.56 41.74, -95.58 41.54, -95.63 41.36, -95.72 41.19, -95.85 41.03, -96 40.91, -96.17 40.82, -96.36 40.76, -96.55 40.74, -96.56 40.74, -96.57 40.74, -96.58 40.74, -96.59 40.74, -96.6 40.74, -96.62 40.74, -96.63 40.74, -96.64 40.74, -96.65 40.74, -96.67 40.74, -96.68 40.74, -96.69 40.74, -96.7 40.74, -96.71 40.74, -96.72 40.74, -96.73 40.74, -96.74 40.74, -96.75 40.74, -96.76 40.74, -96.77 40.74, -96.78 40.74, -96.79 40.74, -96.8 40.74, -96.81 40.74, -96.82 40.74, -96.83 40.74, -96.85 40.74, -96.86 40.74, -96.88 40.74, -96.9 40.74, -96.91 40.74, -96.92 40.74, -96.93 40.74, -96.94 40.74, -96.95 40.74, -96.97 40.74, -96.98 40.74, -96.99 40.74, -97.01 40.74, -97.02 40.74, -97.22 40.76, -97.4 40.82, -97.57 40.91, -97.72 41.03, -97.85 41.18, -97.94 41.35, -98 41.54, -98.02 41.73, -98.02 41.75, -98.02 41.76, -98.02 41.77))" - function_df = self.spark.sql("select ST_ReducePrecision(ST_Buffer(polygondf.countyshape, 10, 'endcap=square'), 2) from polygondf") + function_df = self.spark.sql("select ST_ReducePrecision(ST_Buffer(polygondf.countyshape, 10, false, 'endcap=square'), 2) from polygondf") actual = function_df.take(1)[0][0].wkt assert actual == "POLYGON ((-107.02 42.06, -107.02 42.07, -107.02 42.09, -107.02 42.11, -107.02 42.32, -107.02 42.33, -107.01 42.42, -107.01 42.43, -106.77 44.33, -106.16 46.15, -105.22 47.82, -103.98 49.27, -102.48 50.47, -100.78 51.36, -98.94 51.9, -97.04 52.09, -97.03 52.09, -97.01 52.09, -96.95 52.09, -96.9 52.09, -96.81 52.09, -96.7 52.09, -96.68 52.09, -96.65 52.09, -96.55 52.09, -96.54 52.09, -96.49 52.09, -96.48 52.09, -94.58 51.89, -92.74 51.33, -91.04 50.43, -89.55 49.23, -88.32 47.76, -87.39 46.08, -86.79 44.25, -86.56 42.35, -86.56 42.18, -86.56 42.17, -86.56 42.1, -86.55 41.99, -86.56 41.9, -86.56 41.78, -86.56 41.77, -86.56 41.75, -86.56 41.73, -86.56 41.7, -86.75 39.76, -87.33 37.89, -88.25 36.17, -89.49 34.66, -91 33.43, -92.72 32.51, -94.59 31.94, -96.53 31.74, -96.55 31.74, -96.56 31.74, -96.57 31.74, -96.58 31.74, -96.6 31.74, -96.69 31.74, -96.72 31.74, -96.75 31.74, -96.94 31.74, -97.02 31.74, -97.04 31.74, -97.06 31.74, -98.99 31.94, -100.85 32.5, -102.56 33.42, -104.07 34.65, -105.31 36.14, -106.23 37.85, -106.81 39.71, -107.02 41.64, -107.02 41.75, -107.02 41.94, -107.02 41.96, -107.02 41.99, -107.02 42.01, -107.02 42.02, -107.02 42.03, -107.02 42.06))" - function_df = self.spark.sql("select ST_ReducePrecision(ST_Buffer(polygondf.countyshape, 10, 'endcap=square', true), 2) from polygondf") + function_df = self.spark.sql("select ST_ReducePrecision(ST_Buffer(polygondf.countyshape, 10, true, 'endcap=square'), 2) from polygondf") actual = function_df.take(1)[0][0].wkt assert actual == "POLYGON ((-107.02 42.06, -107.02 42.07, -107.02 42.09, -107.02 42.11, -107.02 42.32, -107.02 42.33, -107.01 42.42, -107.01 42.43, -106.77 44.33, -106.16 46.15, -105.22 47.82, -103.98 49.27, -102.48 50.47, -100.78 51.36, -98.94 51.9, -97.04 52.09, -97.03 52.09, -97.01 52.09, -96.95 52.09, -96.9 52.09, -96.81 52.09, -96.7 52.09, -96.68 52.09, -96.65 52.09, -96.55 52.09, -96.54 52.09, -96.49 52.09, -96.48 52.09, -94.58 51.89, -92.74 51.33, -91.04 50.43, -89.55 49.23, -88.32 47.76, -87.39 46.08, -86.79 44.25, -86.56 42.35, -86.56 42.18, -86.56 42.17, -86.56 42.1, -86.55 41.99, -86.56 41.9, -86.56 41.78, -86.56 41.77, -86.56 41.75, -86.56 41.73, -86.56 41.7, -86.75 39.76, -87.33 37.89, -88.25 36.17, -89.49 34.66, -91 33.43, -92.72 32.51, -94.59 31.94, -96.53 31.74, -96.55 31.74, -96.56 31.74, -96.57 31.74, -96.58 31.74, -96.6 31.74, -96.69 31.74, -96.72 31.74, -96.75 31.74, -96.94 31.74, -97.02 31.74, -97.04 31.74, -97.06 31.74, -98.99 31.94, -100.85 32.5, -102.56 33.42, -104.07 34.65, -105.31 36.14, -106.23 37.85, -106.81 39.71, -107.02 41.64, -107.02 41.75, -107.02 41.94, -107.02 41.96, -107.02 41.99, -107.02 42.01, -107.02 42.02, -107.02 42.03, -107.02 42.06))" diff --git a/python/tests/streaming/spark/test_constructor_functions.py b/python/tests/streaming/spark/test_constructor_functions.py index cae6ec7c94..2d4ffc9ad0 100644 --- a/python/tests/streaming/spark/test_constructor_functions.py +++ b/python/tests/streaming/spark/test_constructor_functions.py @@ -48,7 +48,7 @@ .with_transform("ST_AREA")), (SuiteContainer.empty() .with_function_name("ST_Buffer") - .with_arguments(["ST_GeomFromText('POINT (21 52)')", "1.0", "", True]) + .with_arguments(["ST_GeomFromText('POINT (21 52)')", "1.0", True]) .with_expected_result(3.1214451522580533) .with_transform("ST_AREA")), (SuiteContainer.empty() diff --git a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala index ced4e7b6ec..731dbc9d26 100644 --- a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala +++ b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala @@ -66,10 +66,10 @@ object st_functions extends DataFrameAPI { def ST_Buffer(geometry: Column, buffer: Column): Column = wrapExpression[ST_Buffer](geometry, buffer) def ST_Buffer(geometry: String, buffer: Double): Column = wrapExpression[ST_Buffer](geometry, buffer) - def ST_Buffer(geometry: Column, buffer: Column, parameters: Column): Column = wrapExpression[ST_Buffer](geometry, buffer, parameters) - def ST_Buffer(geometry: String, buffer: Double, parameters: String): Column = wrapExpression[ST_Buffer](geometry, buffer, parameters) - def ST_Buffer(geometry: Column, buffer: Column, parameters: Column, useSpheroid: Column): Column = wrapExpression[ST_Buffer](geometry, buffer, parameters, useSpheroid) - def ST_Buffer(geometry: String, buffer: Double, parameters: String, useSpheroid: Boolean): Column = wrapExpression[ST_Buffer](geometry, buffer, parameters, useSpheroid) + def ST_Buffer(geometry: Column, buffer: Column, useSpheroid: Column): Column = wrapExpression[ST_Buffer](geometry, buffer, useSpheroid) + def ST_Buffer(geometry: String, buffer: Double, useSpheroid: Boolean): Column = wrapExpression[ST_Buffer](geometry, buffer, useSpheroid) + def ST_Buffer(geometry: Column, buffer: Column, useSpheroid: Column, parameters: Column): Column = wrapExpression[ST_Buffer](geometry, buffer, useSpheroid, parameters) + def ST_Buffer(geometry: String, buffer: Double, useSpheroid: Boolean, parameters: String): Column = wrapExpression[ST_Buffer](geometry, buffer, useSpheroid, parameters) def ST_BestSRID(geometry: Column): Column = wrapExpression[ST_BestSRID](geometry) def ST_BestSRID(geometry: String): Column = wrapExpression[ST_BestSRID](geometry) diff --git a/spark/common/src/test/scala/org/apache/sedona/sql/dataFrameAPITestScala.scala b/spark/common/src/test/scala/org/apache/sedona/sql/dataFrameAPITestScala.scala index 56685f65be..492290d0fc 100644 --- a/spark/common/src/test/scala/org/apache/sedona/sql/dataFrameAPITestScala.scala +++ b/spark/common/src/test/scala/org/apache/sedona/sql/dataFrameAPITestScala.scala @@ -198,22 +198,34 @@ class dataFrameAPITestScala extends TestBaseScala { assertEquals(expected, actual) var linestringDf = sparkSession.sql("SELECT ST_GeomFromWKT('LINESTRING(0 0, 50 70, 100 100)') AS geom, 'side=left' as params") - var dfLine = linestringDf.select(ST_Buffer("geom", 10, "params").as("geom")).selectExpr("ST_ReducePrecision(geom, 2)") + var dfLine = linestringDf.select(ST_Buffer("geom", 10, false, "params").as("geom")).selectExpr("ST_ReducePrecision(geom, 2)") actual = dfLine.take(1)(0).get(0).asInstanceOf[Geometry].toText() expected = "POLYGON ((50 70, 0 0, -8.14 5.81, 41.86 75.81, 43.22 77.35, 44.86 78.57, 94.86 108.57, 100 100, 50 70))" assertEquals(expected, actual) linestringDf = sparkSession.sql("SELECT ST_GeomFromWKT('LINESTRING(0 0, 50 70, 70 -3)') AS geom, 'endcap=square' AS params") - dfLine = linestringDf.select(ST_Buffer("geom", 10, "params").as("geom")).selectExpr("ST_ReducePrecision(geom, 2)") + dfLine = linestringDf.select(ST_Buffer("geom", 10, false, "params").as("geom")).selectExpr("ST_ReducePrecision(geom, 2)") actual = dfLine.take(1)(0).get(0).asInstanceOf[Geometry].toText() expected = "POLYGON ((43.22 77.35, 44.85 78.57, 46.7 79.44, 48.69 79.91, 50.74 79.97, 52.75 79.61, 54.65 78.85, 56.36 77.72, 57.79 76.27, 58.91 74.55, 59.64 72.64, 79.64 -0.36, 82.29 -10, 63 -15.29, 45.91 47.07, 8.14 -5.81, 2.32 -13.95, -13.95 -2.32, 41.86 75.81, 43.22 77.35))" assertEquals(expected, actual) - val pointDf = sparkSession.sql("SELECT ST_Point(100, 90) AS geom, 'quad_segs=4' as params") - val dfPoint = pointDf.select(ST_Buffer("geom", 200, "params").as("geom")).selectExpr("ST_ReducePrecision(geom, 2)") + linestringDf = sparkSession.sql("SELECT ST_GeomFromWKT('LINESTRING(0 0, 50 70, 70 -3)') AS geom, 'endcap=square' AS params") + dfLine = linestringDf.select(ST_Buffer("geom", 10, true, "params").as("geom")).selectExpr("ST_ReducePrecision(geom, 4)") + actual = dfLine.take(1)(0).get(0).asInstanceOf[Geometry].toText() + expected = "POLYGON ((50 70, 50.0001 70, 70.0001 -3, 70.0001 -3.0001, 69.9999 -3.0001, 50 69.9999, 0.0001 0, 0 -0.0001, -0.0001 0, 49.9999 70, 50 70))" + assertEquals(expected, actual) + + var pointDf = sparkSession.sql("SELECT ST_Point(100, 90) AS geom, 'quad_segs=4' as params") + var dfPoint = pointDf.select(ST_Buffer("geom", 200, false, "params").as("geom")).selectExpr("ST_ReducePrecision(geom, 2)") actual = dfPoint.take(1)(0).get(0).asInstanceOf[Geometry].toText() expected = "POLYGON ((284.78 13.46, 241.42 -51.42, 176.54 -94.78, 100 -110, 23.46 -94.78, -41.42 -51.42, -84.78 13.46, -100 90, -84.78 166.54, -41.42 231.42, 23.46 274.78, 100 290, 176.54 274.78, 241.42 231.42, 284.78 166.54, 300 90, 284.78 13.46))" assertEquals(expected, actual) + + pointDf = sparkSession.sql("SELECT ST_Point(-180, 60) AS geom, 'quad_segs=4' as params") + dfPoint = pointDf.select(ST_Buffer("geom", 200, true, "params").as("geom")).selectExpr("ST_ReducePrecision(geom, 6)") + actual = dfPoint.take(1)(0).get(0).asInstanceOf[Geometry].toText() + expected = "POLYGON ((-179.99663 60.000611, -179.997353 60.001211, -179.998479 60.001626, -179.999837 60.001793, 179.99878 60.001688, 179.997583 60.001326, 179.996754 60.000761, 179.996419 60.000081, 179.99663 59.999389, 179.997353 59.998789, 179.99848 59.998374, 179.999837 59.998207, -179.99878 59.998312, -179.997583 59.998674, -179.996754 59.999238, -179.996419 59.999919, -179.99663 60.000611))" + assertEquals(expected, actual) } it("Passed ST_BestSRID") { diff --git a/spark/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala b/spark/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala index a4fb11eb3a..391e87d8f1 100644 --- a/spark/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala +++ b/spark/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala @@ -91,15 +91,6 @@ class functionTestScala extends TestBaseScala with Matchers with GeometrySample assert(functionDf.count() > 0); } - it("Passed ST_BestSRID") { - val polygonWktDf = sparkSession.read.format("csv").option("delimiter", "\t").option("header", "false").load(mixedWktGeometryInputLocation) - polygonWktDf.createOrReplaceTempView("polygontable") - val polygonDf = sparkSession.sql("select ST_GeomFromWKT(polygontable._c0) as countyshape from polygontable") - polygonDf.createOrReplaceTempView("polygondf") - val functionDf = sparkSession.sql("select ST_BestSRID(polygondf.countyshape) from polygondf") - assert(functionDf.count() > 0); - } - it("Passed ST_Envelope") { var polygonWktDf = sparkSession.read.format("csv").option("delimiter", "\t").option("header", "false").load(mixedWktGeometryInputLocation) polygonWktDf.createOrReplaceTempView("polygontable") From 977ee844a87d589785feb8801252622aca278160 Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Fri, 23 Feb 2024 23:04:47 -0500 Subject: [PATCH 22/54] move spheroid calculation to FunctionGeotools; Fix python test --- .../org/apache/sedona/common/Functions.java | 58 ++++++++++--------- .../sedona/common/FunctionsGeoTools.java | 29 ++++++++++ .../org/apache/sedona/flink/FunctionTest.java | 39 +++++-------- python/tests/sql/test_dataframe_api.py | 2 + python/tests/sql/test_function.py | 2 +- 5 files changed, 79 insertions(+), 51 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 9a93965872..d0d5d31512 100644 --- a/common/src/main/java/org/apache/sedona/common/Functions.java +++ b/common/src/main/java/org/apache/sedona/common/Functions.java @@ -43,8 +43,6 @@ import org.locationtech.jts.operation.valid.TopologyValidationError; import org.locationtech.jts.precision.GeometryPrecisionReducer; import org.locationtech.jts.simplify.TopologyPreservingSimplifier; -import org.opengis.referencing.FactoryException; -import org.opengis.referencing.operation.TransformException; import org.wololo.jts2geojson.GeoJSONWriter; import java.util.ArrayList; import java.util.Arrays; @@ -56,6 +54,7 @@ import java.util.stream.Collectors; import static com.google.common.geometry.S2.DBL_EPSILON; +import static org.apache.sedona.common.FunctionsGeoTools.bufferSpheroid; public class Functions { @@ -88,15 +87,15 @@ public static Geometry boundary(Geometry geometry) { return boundary; } - public static Geometry buffer(Geometry geometry, double radius) throws FactoryException, TransformException { + public static Geometry buffer(Geometry geometry, double radius) { return buffer(geometry, radius, false, ""); } - public static Geometry buffer(Geometry geometry, double radius, boolean useSpheroid) throws FactoryException, TransformException { + public static Geometry buffer(Geometry geometry, double radius, boolean useSpheroid) { return buffer(geometry, radius, useSpheroid, ""); } - public static Geometry buffer(Geometry geometry, double radius, boolean useSpheroid, String params) throws FactoryException, TransformException { + public static Geometry buffer(Geometry geometry, double radius, boolean useSpheroid, String params) { BufferParameters bufferParameters = new BufferParameters(); // Processing parameters @@ -114,27 +113,32 @@ public static Geometry buffer(Geometry geometry, double radius, boolean useSpher if (useSpheroid) { // Spheroidal buffering logic + try { + return bufferSpheroid(geometry, radius, bufferParameters); + } catch (RuntimeException e) { + throw new RuntimeException("Error processing spheroidal buffer", e); + } - // Determine the best SRID for spheroidal calculations - int bestCRS = bestSRID(geometry); - int originalCRS = geometry.getSRID(); - final int WGS84CRS = 4326; - - // If originalCRS is not set, use WGS84 as the originalCRS for transformation - String sourceCRSCode = (originalCRS == 0) ? "EPSG:" + WGS84CRS : "EPSG:" + originalCRS; - String targetCRSCode = "EPSG:" + bestCRS; - - // Transform the geometry to the selected SRID - Geometry transformedGeometry = FunctionsGeoTools.transform(geometry, sourceCRSCode, targetCRSCode); - - // Apply the buffer operation in the selected SRID - Geometry bufferedGeometry = BufferOp.bufferOp(transformedGeometry, radius, bufferParameters); - - // Transform back to the original SRID or to WGS 84 if original SRID was not set - int backTransformCRSCode = (originalCRS == 0) ? WGS84CRS : originalCRS; - Geometry bufferedResult = FunctionsGeoTools.transform(bufferedGeometry, targetCRSCode, "EPSG:" + backTransformCRSCode); - bufferedResult.setSRID(backTransformCRSCode); - return bufferedResult; +// // Determine the best SRID for spheroidal calculations +// int bestCRS = bestSRID(geometry); +// int originalCRS = geometry.getSRID(); +// final int WGS84CRS = 4326; +// +// // If originalCRS is not set, use WGS84 as the originalCRS for transformation +// String sourceCRSCode = (originalCRS == 0) ? "EPSG:" + WGS84CRS : "EPSG:" + originalCRS; +// String targetCRSCode = "EPSG:" + bestCRS; +// +// // Transform the geometry to the selected SRID +// Geometry transformedGeometry = FunctionsGeoTools.transform(geometry, sourceCRSCode, targetCRSCode); +// +// // Apply the buffer operation in the selected SRID +// Geometry bufferedGeometry = BufferOp.bufferOp(transformedGeometry, radius, bufferParameters); +// +// // Transform back to the original SRID or to WGS 84 if original SRID was not set +// int backTransformCRSCode = (originalCRS == 0) ? WGS84CRS : originalCRS; +// Geometry bufferedResult = FunctionsGeoTools.transform(bufferedGeometry, targetCRSCode, "EPSG:" + backTransformCRSCode); +// bufferedResult.setSRID(backTransformCRSCode); +// return bufferedResult; } else { // Existing planar buffer logic with params handling return BufferOp.bufferOp(geometry, radius, bufferParameters); @@ -226,8 +230,8 @@ public static int bestSRID(Geometry geometry) { // Calculate the center of the envelope double centerX = (envelope.getMinX() + envelope.getMaxX()) / 2.0; // centroid.getX(); double centerY = (envelope.getMinY() + envelope.getMaxY()) / 2.0; // centroid.getY(); - System.out.println("\ncenterX: "+ centerX); - System.out.println("centerY: "+ centerY); +// System.out.println("\ncenterX: "+ centerX); +// System.out.println("centerY: "+ centerY); // Calculate angular width and height double xwidth = Spheroid.angularWidth(envelope); diff --git a/common/src/main/java/org/apache/sedona/common/FunctionsGeoTools.java b/common/src/main/java/org/apache/sedona/common/FunctionsGeoTools.java index 09d47aa95a..f0b57b3717 100644 --- a/common/src/main/java/org/apache/sedona/common/FunctionsGeoTools.java +++ b/common/src/main/java/org/apache/sedona/common/FunctionsGeoTools.java @@ -20,6 +20,8 @@ import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.operation.buffer.BufferOp; +import org.locationtech.jts.operation.buffer.BufferParameters; import org.locationtech.jts.triangulate.VoronoiDiagramBuilder; import org.opengis.referencing.FactoryException; import org.opengis.referencing.NoSuchAuthorityCodeException; @@ -134,4 +136,31 @@ public static Geometry voronoiPolygons(Geometry geom, double tolerance, Geometry } return builder.getDiagram(FunctionsGeoTools.GEOMETRY_FACTORY); } + + public static Geometry bufferSpheroid(Geometry geometry, double radius, BufferParameters params){ + // Determine the best SRID for spheroidal calculations + int bestCRS = Functions.bestSRID(geometry); + int originalCRS = geometry.getSRID(); + final int WGS84CRS = 4326; + + // If originalCRS is not set, use WGS84 as the originalCRS for transformation + String sourceCRSCode = (originalCRS == 0) ? "EPSG:" + WGS84CRS : "EPSG:" + originalCRS; + String targetCRSCode = "EPSG:" + bestCRS; + + try { + // Transform the geometry to the selected SRID + Geometry transformedGeometry = transform(geometry, sourceCRSCode, targetCRSCode); + + // Apply the buffer operation in the selected SRID + Geometry bufferedGeometry = BufferOp.bufferOp(transformedGeometry, radius, params); + + // Transform back to the original SRID or to WGS 84 if original SRID was not set + int backTransformCRSCode = (originalCRS == 0) ? WGS84CRS : originalCRS; + Geometry bufferedResult = transform(bufferedGeometry, targetCRSCode, "EPSG:" + backTransformCRSCode); + bufferedResult.setSRID(backTransformCRSCode); + return bufferedResult; + } catch (FactoryException | TransformException e) { + throw new RuntimeException(e); + } + } } diff --git a/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java b/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java index d13dfc1f35..62d8730559 100644 --- a/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java +++ b/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java @@ -111,9 +111,8 @@ public void testBuffer() { assertEquals(expected, actual); actual = (String) first(tableEnv.sqlQuery("SELECT ST_AsText(ST_ReducePrecision(ST_Buffer(ST_GeomFromWKT('LINESTRING(0 0, 50 70, 70 -3)'), 10, true, 'endcap=square'), 4))")).getField(0); - expected = "POLYGON ((-91.187 30.452, -91.185 30.4505, -91.1851 30.4504, -91.1871 30.4519, -91.1891 30.4534, -91.189 30.4535, -91.187 30.452))"; - System.out.println(actual); -// assertEquals(expected, actual); + expected = "POLYGON ((50 70, 50.0001 70, 70.0001 -3, 70.0001 -3.0001, 69.9999 -3.0001, 50 69.9999, 0.0001 0, 0 -0.0001, -0.0001 0, 49.9999 70, 50 70))"; + assertEquals(expected, actual); actual = (String) first(tableEnv.sqlQuery("SELECT ST_AsText(ST_ReducePrecision(ST_Buffer(ST_GeomFromWKT('POLYGON((-120 30, -80 30, -80 50, -120 50, -120 30))'), 200, true, 'quad_segs=4'), 4))")).getField(0); expected = "POLYGON ((-120.0018 50, -120.0017 50.0004, -120.0013 50.0008, -120.0007 50.0011, -120 50.0012, -80 50.0012, -79.9993 50.0011, -79.9987 50.0008, -79.9983 50.0004, -79.9982 50, -79.9982 30, -79.9983 29.9994, -79.9987 29.9989, -79.9993 29.9986, -80 29.9984, -120 29.9984, -120.0007 29.9986, -120.0013 29.9989, -120.0017 29.9994, -120.0018 30, -120.0018 50))"; @@ -122,26 +121,20 @@ public void testBuffer() { @Test public void testBestSRID() { -// Table table1 = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('POINT (160 40)') AS geom"); -// table1 = table1.select(call(Functions.ST_BestSRID.class.getSimpleName(), $("geom"))); -// int result = (int) first(table1).getField(0); -// assertEquals(32657, result); -// -// Table table2 = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('LINESTRING(-91.185 30.4505, -91.187 30.452, -91.189 30.4535)') AS geom"); -// table2 = table2.select(call(Functions.ST_BestSRID.class.getSimpleName(), $("geom"))); -// result = (int) first(table2).getField(0); -// assertEquals(32615, result); - - Table table4 = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('LINESTRING(179.95 -80, -179.95 -80)') AS geom"); - table4 = table4.select(call(Functions.ST_BestSRID.class.getSimpleName(), $("geom"))); - int result = (int) first(table4).getField(0); - System.out.println(result); -// assertEquals(32615, result); - -// Table table3 = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('POLYGON((-120 30, -80 30, -80 50, -120 50, -120 30))') AS geom"); -// table3 = table3.select(call(Functions.ST_BestSRID.class.getSimpleName(), $("geom"))); -// result = (int) first(table3).getField(0); -// assertEquals(3395, result); + Table table1 = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('POINT (160 40)') AS geom"); + table1 = table1.select(call(Functions.ST_BestSRID.class.getSimpleName(), $("geom"))); + int result = (int) first(table1).getField(0); + assertEquals(32657, result); + + Table table2 = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('LINESTRING(-91.185 30.4505, -91.187 30.452, -91.189 30.4535)') AS geom"); + table2 = table2.select(call(Functions.ST_BestSRID.class.getSimpleName(), $("geom"))); + result = (int) first(table2).getField(0); + assertEquals(32615, result); + + Table table3 = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('POLYGON((-120 30, -80 30, -80 50, -120 50, -120 30))') AS geom"); + table3 = table3.select(call(Functions.ST_BestSRID.class.getSimpleName(), $("geom"))); + result = (int) first(table3).getField(0); + assertEquals(3395, result); } @Test diff --git a/python/tests/sql/test_dataframe_api.py b/python/tests/sql/test_dataframe_api.py index 0917bb8887..4def29ef63 100644 --- a/python/tests/sql/test_dataframe_api.py +++ b/python/tests/sql/test_dataframe_api.py @@ -235,6 +235,8 @@ (stf.ST_Boundary, (None,)), (stf.ST_Buffer, (None, 1.0)), (stf.ST_Buffer, ("", None)), + (stf.ST_Buffer, ("", 1.0, "")), + (stf.ST_Buffer, ("", 1.0, "", None)), (stf.ST_BuildArea, (None,)), (stf.ST_Centroid, (None,)), (stf.ST_Collect, (None,)), diff --git a/python/tests/sql/test_function.py b/python/tests/sql/test_function.py index 066e56fd8f..8891d0049f 100644 --- a/python/tests/sql/test_function.py +++ b/python/tests/sql/test_function.py @@ -100,7 +100,7 @@ def test_st_buffer(self): function_df = self.spark.sql("select ST_ReducePrecision(ST_Buffer(polygondf.countyshape, 1, true), 2) from polygondf") actual = function_df.take(1)[0][0].wkt - assert actual == "POLYGON ((-98.02 41.77, -98.02 41.78, -98.02 41.8, -98.02 41.81, -98.02 41.82, -98.02 41.83, -98.02 41.84, -98.02 41.85, -98.02 41.86, -98.02 41.87, -98.02 41.89, -98.02 41.9, -98.02 41.91, -98.02 41.92, -98.02 41.93, -98.02 41.95, -98.02 41.98, -98.02 42, -98.02 42.01, -98.02 42.02, -98.02 42.04, -98.02 42.05, -98.02 42.07, -98.02 42.09, -98.02 42.11, -98.02 42.12, -97.99 42.31, -97.93 42.5, -97.84 42.66, -97.72 42.81, -97.57 42.93, -97.4 43.02, -97.21 43.07, -97.02 43.09, -97 43.09, -96.98 43.09, -96.97 43.09, -96.96 43.09, -96.95 43.09, -96.94 43.09, -96.92 43.09, -96.91 43.09, -96.89 43.09, -96.88 43.09, -96.86 43.09, -96.84 43.09, -96.83 43.09, -96.82 43.09, -96.81 43.09, -96.8 43.09, -96.79 43.09, -96.78 43.09, -96.76 43.09, -96.74 43.09, -96.73 43.09, -96.71 43.09, -96.7 43.09, -96.69 43.09, -96.68 43.09, -96.66 43.09, -96.65 43.09, -96.64 43.09, -96.63 43.09, -96.62 43.09, -96.61 43.09, -96.6 43.09, -96.59 43.09, -96.58 43.09, -96.57 43.09, -96.56 43.09, -96.55 43.09, -96.36 43.07, -96.17 43.01, -96 42.92, -95.86 42.8, -95.73 42.66, -95.64 42.49, -95.58 42.31, -95.56 42.12, -95.56 42.1, -95.56 42.09, -95.56 42.08, -95.56 42.07, -95.56 42.06, -95.56 42.04, -95.56 42, -95.56 41.99, -95.56 41.98, -95.56 41.97, -95.56 41.96, -95.56 41.95, -95.56 41.94, -95.56 41.93, -95.56 41.92, -95.56 41.91, -95.56 41.9, -95.56 41.89, -95.56 41.88, -95.56 41.87, -95.56 41.86, -95.56 41.85, -95.56 41.83, -95.56 41.82, -95.56 41.81, -95.56 41.8, -95.56 41.79, -95.56 41.78, -95.56 41.77, -95.56 41.76, -95.56 41.75, -95.56 41.74, -95.58 41.54, -95.63 41.36, -95.72 41.19, -95.85 41.03, -96 40.91, -96.17 40.82, -96.36 40.76, -96.55 40.74, -96.56 40.74, -96.57 40.74, -96.58 40.74, -96.59 40.74, -96.6 40.74, -96.62 40.74, -96.63 40.74, -96.64 40.74, -96.65 40.74, -96.67 40.74, -96.68 40.74, -96.69 40.74, -96.7 40.74, -96.71 40.74, -96.72 40.74, -96.73 40.74, -96.74 40.74, -96.75 40.74, -96.76 40.74, -96.77 40.74, -96.78 40.74, -96.79 40.74, -96.8 40.74, -96.81 40.74, -96.82 40.74, -96.83 40.74, -96.85 40.74, -96.86 40.74, -96.88 40.74, -96.9 40.74, -96.91 40.74, -96.92 40.74, -96.93 40.74, -96.94 40.74, -96.95 40.74, -96.97 40.74, -96.98 40.74, -96.99 40.74, -97.01 40.74, -97.02 40.74, -97.22 40.76, -97.4 40.82, -97.57 40.91, -97.72 41.03, -97.85 41.18, -97.94 41.35, -98 41.54, -98.02 41.73, -98.02 41.75, -98.02 41.76, -98.02 41.77))" + assert actual == "POLYGON ((-97.02 42.01, -97.02 42.02, -97.02 42.03, -97.02 42.04, -97.02 42.05, -97.02 42.06, -97.02 42.07, -97.02 42.08, -97.02 42.09, -97.01 42.09, -97 42.09, -96.99 42.09, -96.98 42.09, -96.97 42.09, -96.96 42.09, -96.95 42.09, -96.94 42.09, -96.93 42.09, -96.92 42.09, -96.91 42.09, -96.9 42.09, -96.89 42.09, -96.88 42.09, -96.87 42.09, -96.86 42.09, -96.85 42.09, -96.84 42.09, -96.83 42.09, -96.82 42.09, -96.81 42.09, -96.8 42.09, -96.79 42.09, -96.78 42.09, -96.77 42.09, -96.76 42.09, -96.75 42.09, -96.74 42.09, -96.73 42.09, -96.72 42.09, -96.71 42.09, -96.7 42.09, -96.69 42.09, -96.68 42.09, -96.67 42.09, -96.66 42.09, -96.65 42.09, -96.64 42.09, -96.63 42.09, -96.62 42.09, -96.61 42.09, -96.6 42.09, -96.59 42.09, -96.58 42.09, -96.57 42.09, -96.56 42.09, -96.56 42.08, -96.56 42.07, -96.56 42.06, -96.56 42.05, -96.56 42.04, -96.56 42.03, -96.56 42.02, -96.55 42.02, -96.56 42, -96.56 41.99, -96.56 41.98, -96.56 41.97, -96.56 41.96, -96.56 41.95, -96.56 41.94, -96.56 41.93, -96.56 41.92, -96.56 41.91, -96.56 41.9, -96.56 41.89, -96.56 41.88, -96.56 41.87, -96.56 41.86, -96.56 41.85, -96.56 41.84, -96.56 41.83, -96.56 41.82, -96.56 41.81, -96.56 41.8, -96.56 41.79, -96.56 41.78, -96.56 41.77, -96.56 41.76, -96.56 41.75, -96.56 41.74, -96.57 41.74, -96.58 41.74, -96.59 41.74, -96.6 41.74, -96.61 41.74, -96.62 41.74, -96.63 41.74, -96.64 41.74, -96.65 41.74, -96.66 41.74, -96.67 41.74, -96.68 41.74, -96.69 41.74, -96.7 41.74, -96.71 41.74, -96.72 41.74, -96.73 41.74, -96.74 41.74, -96.75 41.74, -96.76 41.74, -96.77 41.74, -96.78 41.74, -96.79 41.74, -96.8 41.74, -96.81 41.74, -96.82 41.74, -96.83 41.74, -96.84 41.74, -96.85 41.74, -96.86 41.74, -96.87 41.74, -96.88 41.74, -96.89 41.74, -96.9 41.74, -96.91 41.74, -96.92 41.74, -96.93 41.74, -96.94 41.74, -96.95 41.74, -96.96 41.74, -96.97 41.74, -96.98 41.74, -96.99 41.74, -97 41.74, -97.01 41.74, -97.02 41.74, -97.02 41.75, -97.02 41.76, -97.02 41.77, -97.02 41.78, -97.02 41.79, -97.02 41.8, -97.02 41.81, -97.02 41.82, -97.02 41.83, -97.02 41.84, -97.02 41.85, -97.02 41.86, -97.02 41.87, -97.02 41.88, -97.02 41.89, -97.02 41.9, -97.02 41.91, -97.02 41.92, -97.02 41.93, -97.02 41.94, -97.02 41.95, -97.02 41.96, -97.02 41.97, -97.02 41.98, -97.02 41.99, -97.02 42, -97.02 42.01))" function_df = self.spark.sql("select ST_ReducePrecision(ST_Buffer(polygondf.countyshape, 10, false, 'endcap=square'), 2) from polygondf") actual = function_df.take(1)[0][0].wkt From ab72359b7c74962ef6e8cc6d93e1b8c702060798 Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Sun, 25 Feb 2024 18:27:04 -0500 Subject: [PATCH 23/54] Enforce lon/lat input geometries for ST_BestSRID and ST_Buffer spheroidal --- .../org/apache/sedona/common/Functions.java | 32 ++--------- .../sedona/common/FunctionsGeoTools.java | 2 +- .../apache/sedona/common/sphere/Spheroid.java | 9 +-- .../apache/sedona/common/FunctionsTest.java | 57 ++++++++++--------- .../org/apache/sedona/flink/FunctionTest.java | 16 +----- 5 files changed, 42 insertions(+), 74 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 d0d5d31512..b7ce9316b9 100644 --- a/common/src/main/java/org/apache/sedona/common/Functions.java +++ b/common/src/main/java/org/apache/sedona/common/Functions.java @@ -95,7 +95,7 @@ public static Geometry buffer(Geometry geometry, double radius, boolean useSpher return buffer(geometry, radius, useSpheroid, ""); } - public static Geometry buffer(Geometry geometry, double radius, boolean useSpheroid, String params) { + public static Geometry buffer(Geometry geometry, double radius, boolean useSpheroid, String params) throws IllegalArgumentException{ BufferParameters bufferParameters = new BufferParameters(); // Processing parameters @@ -118,27 +118,6 @@ public static Geometry buffer(Geometry geometry, double radius, boolean useSpher } catch (RuntimeException e) { throw new RuntimeException("Error processing spheroidal buffer", e); } - -// // Determine the best SRID for spheroidal calculations -// int bestCRS = bestSRID(geometry); -// int originalCRS = geometry.getSRID(); -// final int WGS84CRS = 4326; -// -// // If originalCRS is not set, use WGS84 as the originalCRS for transformation -// String sourceCRSCode = (originalCRS == 0) ? "EPSG:" + WGS84CRS : "EPSG:" + originalCRS; -// String targetCRSCode = "EPSG:" + bestCRS; -// -// // Transform the geometry to the selected SRID -// Geometry transformedGeometry = FunctionsGeoTools.transform(geometry, sourceCRSCode, targetCRSCode); -// -// // Apply the buffer operation in the selected SRID -// Geometry bufferedGeometry = BufferOp.bufferOp(transformedGeometry, radius, bufferParameters); -// -// // Transform back to the original SRID or to WGS 84 if original SRID was not set -// int backTransformCRSCode = (originalCRS == 0) ? WGS84CRS : originalCRS; -// Geometry bufferedResult = FunctionsGeoTools.transform(bufferedGeometry, targetCRSCode, "EPSG:" + backTransformCRSCode); -// bufferedResult.setSRID(backTransformCRSCode); -// return bufferedResult; } else { // Existing planar buffer logic with params handling return BufferOp.bufferOp(geometry, radius, bufferParameters); @@ -223,19 +202,18 @@ else if (singleParam[0].equalsIgnoreCase(listBufferParameters[5])) { return bufferParameters; } - public static int bestSRID(Geometry geometry) { + public static int bestSRID(Geometry geometry) throws IllegalArgumentException{ Envelope envelope = geometry.getEnvelopeInternal(); if (envelope.isNull()) return Spheroid.EPSG_WORLD_MERCATOR; // Fallback EPSG // Calculate the center of the envelope double centerX = (envelope.getMinX() + envelope.getMaxX()) / 2.0; // centroid.getX(); double centerY = (envelope.getMinY() + envelope.getMaxY()) / 2.0; // centroid.getY(); -// System.out.println("\ncenterX: "+ centerX); -// System.out.println("centerY: "+ centerY); // Calculate angular width and height - double xwidth = Spheroid.angularWidth(envelope); - double ywidth = Spheroid.angularHeight(envelope); + Double xwidth = Spheroid.angularWidth(envelope); + Double ywidth = Spheroid.angularHeight(envelope); + if (xwidth.isNaN() | ywidth.isNaN()) {throw new IllegalArgumentException("Only lon/lat coordinate systems are supported by ST_BestSRID");} // Prioritize polar regions for Lambert Azimuthal Equal Area projection if (centerY >= 70.0 && ywidth < 45.0) return Spheroid.EPSG_NORTH_LAMBERT; diff --git a/common/src/main/java/org/apache/sedona/common/FunctionsGeoTools.java b/common/src/main/java/org/apache/sedona/common/FunctionsGeoTools.java index f0b57b3717..db67c856e2 100644 --- a/common/src/main/java/org/apache/sedona/common/FunctionsGeoTools.java +++ b/common/src/main/java/org/apache/sedona/common/FunctionsGeoTools.java @@ -137,7 +137,7 @@ public static Geometry voronoiPolygons(Geometry geom, double tolerance, Geometry return builder.getDiagram(FunctionsGeoTools.GEOMETRY_FACTORY); } - public static Geometry bufferSpheroid(Geometry geometry, double radius, BufferParameters params){ + public static Geometry bufferSpheroid(Geometry geometry, double radius, BufferParameters params) throws IllegalArgumentException { // Determine the best SRID for spheroidal calculations int bestCRS = Functions.bestSRID(geometry); int originalCRS = geometry.getSRID(); diff --git a/common/src/main/java/org/apache/sedona/common/sphere/Spheroid.java b/common/src/main/java/org/apache/sedona/common/sphere/Spheroid.java index 7b06f00d73..42c1022fd4 100644 --- a/common/src/main/java/org/apache/sedona/common/sphere/Spheroid.java +++ b/common/src/main/java/org/apache/sedona/common/sphere/Spheroid.java @@ -26,6 +26,7 @@ import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.Geometry; +import static java.lang.Float.NaN; import static java.lang.Math.abs; public class Spheroid @@ -131,7 +132,7 @@ else if (geomType.equals("MultiPolygon") || geomType.equals("GeometryCollection" } } - public static double angularWidth(Envelope envelope) { + public static Double angularWidth(Envelope envelope) { double lon1 = envelope.getMinX(); double lon2 = envelope.getMaxX(); double lat = (envelope.getMinY() + envelope.getMaxY()) / 2; // Mid-latitude for width calculation @@ -141,12 +142,12 @@ public static double angularWidth(Envelope envelope) { double distance = g.s12; // Distance in meters // Convert distance to angular width in degrees - double angularWidth = Math.toDegrees(distance / (Geodesic.WGS84.EquatorialRadius() * Math.PI / 180)); + Double angularWidth = Math.toDegrees(distance / (Geodesic.WGS84.EquatorialRadius() * Math.PI / 180)); return angularWidth; } - public static double angularHeight(Envelope envelope) { + public static Double angularHeight(Envelope envelope) { double lat1 = envelope.getMinY(); double lat2 = envelope.getMaxY(); double lon = (envelope.getMinX() + envelope.getMaxX()) / 2; // Mid-longitude for height calculation @@ -156,7 +157,7 @@ public static double angularHeight(Envelope envelope) { double distance = g.s12; // Distance in meters // Convert distance to angular height in degrees - double angularHeight = Math.toDegrees(distance / (Geodesic.WGS84.EquatorialRadius() * Math.PI / 180)); + Double angularHeight = Math.toDegrees(distance / (Geodesic.WGS84.EquatorialRadius() * Math.PI / 180)); return angularHeight; } diff --git a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java index 92e0c74156..c8c4144e39 100644 --- a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java +++ b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java @@ -33,6 +33,8 @@ import java.util.*; import java.util.stream.Collectors; +import static org.apache.sedona.common.Constructors.geomFromEWKT; +import static org.apache.sedona.common.Constructors.geomFromWKT; import static org.junit.Assert.*; public class FunctionsTest extends TestBase { @@ -61,28 +63,28 @@ public void asEWKT() throws Exception{ Geometry geometry = geometryFactory.createPoint(new Coordinate(1.0, 2.0)); String actualResult = Functions.asEWKT(geometry); String expectedResult = "SRID=4236;POINT (1 2)"; - Geometry actual = Constructors.geomFromEWKT(expectedResult); + Geometry actual = geomFromEWKT(expectedResult); assertEquals(geometry, actual); assertEquals(expectedResult, actualResult); geometry = geometryFactory.createPoint(new Coordinate(1.0, 2.0, 3.0)); actualResult = Functions.asEWKT(geometry); expectedResult = "SRID=4236;POINT Z(1 2 3)"; - actual = Constructors.geomFromEWKT(expectedResult); + actual = geomFromEWKT(expectedResult); assertEquals(geometry, actual); assertEquals(expectedResult, actualResult); geometry = geometryFactory.createPoint(new CoordinateXYM(1.0, 2.0, 3.0)); actualResult = Functions.asEWKT(geometry); expectedResult = "SRID=4236;POINT M(1 2 3)"; - actual = Constructors.geomFromEWKT(expectedResult); + actual = geomFromEWKT(expectedResult); assertEquals(geometry, actual); assertEquals(expectedResult, actualResult); geometry = geometryFactory.createPoint(new CoordinateXYZM(1.0, 2.0, 3.0, 4.0)); actualResult = Functions.asEWKT(geometry); expectedResult = "SRID=4236;POINT ZM(1 2 3 4)"; - actual = Constructors.geomFromEWKT(expectedResult); + actual = geomFromEWKT(expectedResult); assertEquals(geometry, actual); assertEquals(expectedResult, actualResult); } @@ -92,28 +94,28 @@ public void asWKT() throws Exception { Geometry geometry = GEOMETRY_FACTORY.createPoint(new Coordinate(1.0, 2.0)); String actualResult = Functions.asWKT(geometry); String expectedResult = "POINT (1 2)"; - Geometry actual = Constructors.geomFromEWKT(expectedResult); + Geometry actual = geomFromEWKT(expectedResult); assertEquals(geometry, actual); assertEquals(expectedResult, actualResult); geometry = GEOMETRY_FACTORY.createPoint(new Coordinate(1.0, 2.0, 3.0)); actualResult = Functions.asWKT(geometry); expectedResult = "POINT Z(1 2 3)"; - actual = Constructors.geomFromEWKT(expectedResult); + actual = geomFromEWKT(expectedResult); assertEquals(geometry, actual); assertEquals(expectedResult, actualResult); geometry = GEOMETRY_FACTORY.createPoint(new CoordinateXYM(1.0, 2.0, 3.0)); actualResult = Functions.asWKT(geometry); expectedResult = "POINT M(1 2 3)"; - actual = Constructors.geomFromEWKT(expectedResult); + actual = geomFromEWKT(expectedResult); assertEquals(geometry, actual); assertEquals(expectedResult, actualResult); geometry = GEOMETRY_FACTORY.createPoint(new CoordinateXYZM(1.0, 2.0, 3.0, 4.0)); actualResult = Functions.asWKT(geometry); expectedResult = "POINT ZM(1 2 3 4)"; - actual = Constructors.geomFromEWKT(expectedResult); + actual = geomFromEWKT(expectedResult); assertEquals(geometry, actual); assertEquals(expectedResult, actualResult); } @@ -1151,7 +1153,7 @@ public void nRingsMultiPolygonMixed() throws Exception { } @Test - public void testBuffer() throws FactoryException, TransformException { + public void testBuffer() { Polygon polygon = GEOMETRY_FACTORY.createPolygon(coordArray(50, 50, 50, 150, 150, 150, 150, 50, 50, 50)); String actual = Functions.asWKT(Functions.reducePrecision(Functions.buffer(polygon, 15), 4)); String expected = "POLYGON ((47.0736 35.2882, 44.2597 36.1418, 41.6664 37.528, 39.3934 39.3934, 37.528 41.6664, 36.1418 44.2597, 35.2882 47.0736, 35 50, 35 150, 35.2882 152.9264, 36.1418 155.7403, 37.528 158.3336, 39.3934 160.6066, 41.6664 162.472, 44.2597 163.8582, 47.0736 164.7118, 50 165, 150 165, 152.9264 164.7118, 155.7403 163.8582, 158.3336 162.472, 160.6066 160.6066, 162.472 158.3336, 163.8582 155.7403, 164.7118 152.9264, 165 150, 165 50, 164.7118 47.0736, 163.8582 44.2597, 162.472 41.6664, 160.6066 39.3934, 158.3336 37.528, 155.7403 36.1418, 152.9264 35.2882, 150 35, 50 35, 47.0736 35.2882))"; @@ -1174,12 +1176,13 @@ public void testBuffer() throws FactoryException, TransformException { } @Test - public void testBufferSpheroidal() throws FactoryException, TransformException, ParseException { + public void testBufferSpheroidal() throws ParseException { Geometry polygon1 = GEOMETRY_FACTORY.createPolygon(coordArray(16.2500, 48.2500, 16.3500, 48.2500, 16.3500, 48.2000, 16.2500, 48.2000, 16.2500, 48.2500)); - Geometry polygon2 = Constructors.geomFromWKT("POLYGON((-120 30, -80 30, -80 50, -120 50, -120 30))", 4269); - Geometry point1 = Constructors.geomFromWKT("POINT(-180 60)", 4269); - Geometry linestring1 = Constructors.geomFromEWKT("LINESTRING(-91.185 30.4505, -91.187 30.452, -91.189 30.4535)"); - Geometry polygon3 = Constructors.geomFromWKT("POLYGON((-120 30, -80 30, -80 50, -120 50, -120 30))", 4269); + Geometry polygon2 = geomFromWKT("POLYGON((-120 30, -80 30, -80 50, -120 50, -120 30))", 4269); + Geometry point1 = geomFromWKT("POINT(-180 60)", 4269); + Geometry point2 = geomFromEWKT("POINT(10000 -500)"); + Geometry linestring1 = geomFromEWKT("LINESTRING(-91.185 30.4505, -91.187 30.452, -91.189 30.4535)"); + Geometry polygon3 = geomFromWKT("POLYGON((-120 30, -80 30, -80 50, -120 50, -120 30))", 4230); Geometry result1 = Functions.buffer(polygon1, 100, true); Geometry result2 = Functions.buffer(polygon2, 1000, true); @@ -1187,17 +1190,17 @@ public void testBufferSpheroidal() throws FactoryException, TransformException, Geometry result4 = Functions.buffer(linestring1, 10, true); Geometry result5 = Functions.buffer(polygon3, 10000, true); - Geometry expected1 = Constructors.geomFromEWKT("POLYGON ((16.248653057837092 48.24999999781262, 16.24867891451385 48.25017542568975, 16.24875549793627 48.25034411793438, 16.24887986793391 48.25049959710696, 16.249047249310912 48.25063589306856, 16.249251215157546 48.250747772240985, 16.249483933612066 48.25083093858835, 16.249736468600165 48.250882198599385, 16.249999123001867 48.25089958393145, 16.35000087730807 48.25089956809054, 16.35026352923699 48.250882182692266, 16.350516061632444 48.250830922668115, 16.35074877731957 48.25074775640202, 16.350952740192536 48.250635877460866, 16.351120118386593 48.25049958194538, 16.351244485020548 48.25034410350243, 16.35132106495956 48.25017541233756, 16.351346918125614 48.24999998594946, 16.351345606585333 48.199999998284284, 16.351319726848168 48.19982442741669, 16.351243088946557 48.19965560959, 16.351118640921797 48.19950003769327, 16.350951169518318 48.19936369511042, 16.350747116034892 48.19925182561274, 16.350514328568078 48.19916873170147, 16.350261760179283 48.199117609152964, 16.34999912459177 48.19910042412578, 16.25000087573864 48.19910040825405, 16.24973823767017 48.199117593214716, 16.249485666681007 48.199168715750886, 16.249252876439694 48.19925180974529, 16.24904881997628 48.199363679477166, 16.248881345384483 48.199500022510115, 16.248756893991796 48.199655595141486, 16.24868025260387 48.199824414053964, 16.2486543693546 48.19999998641755, 16.248653057837092 48.24999999781262))"); - Geometry expected2 = Constructors.geomFromEWKT("POLYGON ((-120.00898315284118 29.999999999999467, -120.00898315284118 49.999999999988724, -120.00881054407824 50.001129625613444, -120.0082993510474 50.002215815187945, -120.00746921861013 50.003216831381316, -120.00635204829044 50.00409421217581, -120.0049907723172 50.004814247955025, -120.00343770376273 50.00534927578318, -120.00175252618048 50.00567874131385, -119.99999999999999 50.005789987696524, -80 50.005789987696524, -79.9982474738195 50.00567874131385, -79.99656229623727 50.00534927578318, -79.99500922768277 50.004814247955025, -79.99364795170956 50.00409421217581, -79.99253078138989 50.003216831381316, -79.99170064895262 50.002215815187945, -79.99118945592176 50.001129625613444, -79.99101684715882 49.999999999988724, -79.99101684715882 29.999999999999467, -79.99118945592176 29.998474584401656, -79.99170064895262 29.997007767335376, -79.99253078138989 29.995655921596196, -79.99364795170956 29.994471003601635, -79.99500922768277 29.99349855586065, -79.99656229623727 29.99277595576926, -79.9982474738195 29.99233097818683, -80 29.992180727189663, -119.99999999999999 29.992180727189663, -120.00175252618048 29.99233097818683, -120.00343770376273 29.99277595576926, -120.0049907723172 29.99349855586065, -120.00635204829044 29.994471003601635, -120.00746921861013 29.995655921596196, -120.0082993510474 29.997007767335376, -120.00881054407824 29.998474584401656, -120.00898315284118 29.999999999999467))"); - Geometry expected3 = Constructors.geomFromEWKT("POLYGON ((-179.9982096288439 59.99995928994206, -179.9982598922349 59.99978513613418, -179.99837702566958 59.99961923978765, -179.99855652694714 59.99946797603053, -179.998791497433 59.99933715760413, -179.99907290724568 59.999231811518854, -179.9993899422849 59.99915598591007, -179.99973041976276 59.99911259450936, 179.9999187437241 59.999103304702594, 179.99957102955173 59.99912847347155, 179.99923979915093 59.999187133678575, 179.99893778069153 59.999277031220146, 179.99867658007233 59.99939471162435, 179.99846623498902 59.99953565276752, 179.9983148292063 59.99969443861621, 179.99822818185396 59.999864967323326, 179.9982096236965 60.00004068568767, 179.99825986898801 60.00021484097138, 179.99837698785996 60.00038074040053, 179.99855648032874 60.00053200837772, 179.99879144910062 60.00066283151926, 179.99907286455522 60.00076818209686, 179.9993899117333 60.00084401129125, 179.9997304059989 60.000887404824965, -179.99991873860708 60.00089669498742, -179.99957100633523 60.000871524742664, -179.9992397613717 60.000812861453035, -179.9989377341035 60.000722959691345, -179.99867653177037 60.00060527457202, -179.99846619232903 60.00046432893652, -179.99831479868513 60.000305539502236, -179.99822816812048 60.00013500866213, -179.9982096288439 59.99995928994206))"); - Geometry expected4 = Constructors.geomFromEWKT("POLYGON ((-91.18706814560424 30.451931798138848, -91.18906814731041 30.453431798551634, -91.18908219577726 30.453444627150773, -91.1890930855116 30.4534595836974, -91.18910039802604 30.453476093420573, -91.18910385230312 30.453493521861454, -91.18910331559482 30.453511199255043, -91.1890988085245 30.4535284462689, -91.18909050429437 30.453544600109474, -91.18907872203003 30.453559039992996, -91.18906391451668 30.453571211001968, -91.18904665079899 30.453580645410415, -91.18902759431288 30.453586980658557, -91.18900747739012 30.453589973285787, -91.18898707311482 30.453589508286903, -91.18896716561396 30.453585603531685, -91.18894851992349 30.453578409078172, -91.18893185258821 30.453568201405897, -91.18693185327697 30.45206820105564, -91.18493185405761 30.450568200774196, -91.18491780618592 30.450555372061554, -91.18490691698157 30.450540415431583, -91.18489960490953 30.450523905659963, -91.18489615096698 30.450506477208492, -91.18489668788492 30.450488799843015, -91.18490119502782 30.450471552894637, -91.18490949918679 30.450455399153434, -91.18492128123634 30.450440959397913, -91.18493608839827 30.450428788538883, -91.18495335164187 30.45041935429472, -91.1849724075514 30.45041301921735, -91.18499252382057 30.450410026759716, -91.18501292739447 30.450410491920046, -91.18503283417732 30.45041439682263, -91.18505147916453 30.450421591404723, -91.18506814584102 30.450431799183303, -91.18706814560424 30.451931798138848))"); - Geometry expected5 = Constructors.geomFromEWKT("POLYGON ((-120.08983152841193 29.999999999999467, -120.08983152841193 49.999999999988724, -120.08810544078257 50.011295055219406, -120.082993510474 50.02215353087995, -120.07469218610126 50.03215857448533, -120.06352048290445 50.040926345154375, -120.04990772317231 50.048120665845154, -120.03437703762728 50.05346582623596, -120.01752526180509 50.05675706193107, -119.99999999999999 50.057868324959486, -80 50.057868324959486, -79.98247473819491 50.05675706193107, -79.96562296237272 50.05346582623596, -79.95009227682765 50.048120665845154, -79.93647951709555 50.040926345154375, -79.92530781389875 50.03215857448533, -79.91700648952599 50.02215353087995, -79.91189455921744 50.011295055219406, -79.91016847158805 49.999999999988724, -79.91016847158805 29.999999999999467, -79.91189455921744 29.984744778413628, -79.91700648952599 29.970073573592575, -79.92530781389875 29.956550575939833, -79.93647951709555 29.944696041120597, -79.95009227682765 29.934966209460505, -79.96562296237272 29.927735669848445, -79.98247473819491 29.92328286155486, -80 29.92177928675603, -119.99999999999999 29.92177928675603, -120.01752526180509 29.92328286155486, -120.03437703762728 29.927735669848445, -120.04990772317231 29.934966209460505, -120.06352048290445 29.944696041120597, -120.07469218610126 29.956550575939833, -120.082993510474 29.970073573592575, -120.08810544078257 29.984744778413628, -120.08983152841193 29.999999999999467))"); + Geometry expected1 = geomFromEWKT("POLYGON ((16.248653057837092 48.24999999781262, 16.24867891451385 48.25017542568975, 16.24875549793627 48.25034411793438, 16.24887986793391 48.25049959710696, 16.249047249310912 48.25063589306856, 16.249251215157546 48.250747772240985, 16.249483933612066 48.25083093858835, 16.249736468600165 48.250882198599385, 16.249999123001867 48.25089958393145, 16.35000087730807 48.25089956809054, 16.35026352923699 48.250882182692266, 16.350516061632444 48.250830922668115, 16.35074877731957 48.25074775640202, 16.350952740192536 48.250635877460866, 16.351120118386593 48.25049958194538, 16.351244485020548 48.25034410350243, 16.35132106495956 48.25017541233756, 16.351346918125614 48.24999998594946, 16.351345606585333 48.199999998284284, 16.351319726848168 48.19982442741669, 16.351243088946557 48.19965560959, 16.351118640921797 48.19950003769327, 16.350951169518318 48.19936369511042, 16.350747116034892 48.19925182561274, 16.350514328568078 48.19916873170147, 16.350261760179283 48.199117609152964, 16.34999912459177 48.19910042412578, 16.25000087573864 48.19910040825405, 16.24973823767017 48.199117593214716, 16.249485666681007 48.199168715750886, 16.249252876439694 48.19925180974529, 16.24904881997628 48.199363679477166, 16.248881345384483 48.199500022510115, 16.248756893991796 48.199655595141486, 16.24868025260387 48.199824414053964, 16.2486543693546 48.19999998641755, 16.248653057837092 48.24999999781262))"); + Geometry expected2 = geomFromEWKT("POLYGON ((-120.00898315284118 29.999999999999467, -120.00898315284118 49.999999999988724, -120.00881054407824 50.001129625613444, -120.0082993510474 50.002215815187945, -120.00746921861013 50.003216831381316, -120.00635204829044 50.00409421217581, -120.0049907723172 50.004814247955025, -120.00343770376273 50.00534927578318, -120.00175252618048 50.00567874131385, -119.99999999999999 50.005789987696524, -80 50.005789987696524, -79.9982474738195 50.00567874131385, -79.99656229623727 50.00534927578318, -79.99500922768277 50.004814247955025, -79.99364795170956 50.00409421217581, -79.99253078138989 50.003216831381316, -79.99170064895262 50.002215815187945, -79.99118945592176 50.001129625613444, -79.99101684715882 49.999999999988724, -79.99101684715882 29.999999999999467, -79.99118945592176 29.998474584401656, -79.99170064895262 29.997007767335376, -79.99253078138989 29.995655921596196, -79.99364795170956 29.994471003601635, -79.99500922768277 29.99349855586065, -79.99656229623727 29.99277595576926, -79.9982474738195 29.99233097818683, -80 29.992180727189663, -119.99999999999999 29.992180727189663, -120.00175252618048 29.99233097818683, -120.00343770376273 29.99277595576926, -120.0049907723172 29.99349855586065, -120.00635204829044 29.994471003601635, -120.00746921861013 29.995655921596196, -120.0082993510474 29.997007767335376, -120.00881054407824 29.998474584401656, -120.00898315284118 29.999999999999467))"); + Geometry expected3 = geomFromEWKT("POLYGON ((-179.9982096288439 59.99995928994206, -179.9982598922349 59.99978513613418, -179.99837702566958 59.99961923978765, -179.99855652694714 59.99946797603053, -179.998791497433 59.99933715760413, -179.99907290724568 59.999231811518854, -179.9993899422849 59.99915598591007, -179.99973041976276 59.99911259450936, 179.9999187437241 59.999103304702594, 179.99957102955173 59.99912847347155, 179.99923979915093 59.999187133678575, 179.99893778069153 59.999277031220146, 179.99867658007233 59.99939471162435, 179.99846623498902 59.99953565276752, 179.9983148292063 59.99969443861621, 179.99822818185396 59.999864967323326, 179.9982096236965 60.00004068568767, 179.99825986898801 60.00021484097138, 179.99837698785996 60.00038074040053, 179.99855648032874 60.00053200837772, 179.99879144910062 60.00066283151926, 179.99907286455522 60.00076818209686, 179.9993899117333 60.00084401129125, 179.9997304059989 60.000887404824965, -179.99991873860708 60.00089669498742, -179.99957100633523 60.000871524742664, -179.9992397613717 60.000812861453035, -179.9989377341035 60.000722959691345, -179.99867653177037 60.00060527457202, -179.99846619232903 60.00046432893652, -179.99831479868513 60.000305539502236, -179.99822816812048 60.00013500866213, -179.9982096288439 59.99995928994206))"); + Geometry expected4 = geomFromEWKT("POLYGON ((-91.18706814560424 30.451931798138848, -91.18906814731041 30.453431798551634, -91.18908219577726 30.453444627150773, -91.1890930855116 30.4534595836974, -91.18910039802604 30.453476093420573, -91.18910385230312 30.453493521861454, -91.18910331559482 30.453511199255043, -91.1890988085245 30.4535284462689, -91.18909050429437 30.453544600109474, -91.18907872203003 30.453559039992996, -91.18906391451668 30.453571211001968, -91.18904665079899 30.453580645410415, -91.18902759431288 30.453586980658557, -91.18900747739012 30.453589973285787, -91.18898707311482 30.453589508286903, -91.18896716561396 30.453585603531685, -91.18894851992349 30.453578409078172, -91.18893185258821 30.453568201405897, -91.18693185327697 30.45206820105564, -91.18493185405761 30.450568200774196, -91.18491780618592 30.450555372061554, -91.18490691698157 30.450540415431583, -91.18489960490953 30.450523905659963, -91.18489615096698 30.450506477208492, -91.18489668788492 30.450488799843015, -91.18490119502782 30.450471552894637, -91.18490949918679 30.450455399153434, -91.18492128123634 30.450440959397913, -91.18493608839827 30.450428788538883, -91.18495335164187 30.45041935429472, -91.1849724075514 30.45041301921735, -91.18499252382057 30.450410026759716, -91.18501292739447 30.450410491920046, -91.18503283417732 30.45041439682263, -91.18505147916453 30.450421591404723, -91.18506814584102 30.450431799183303, -91.18706814560424 30.451931798138848))"); + Geometry expected5 = geomFromEWKT("POLYGON ((-120.08983152841193 29.999999999999467, -120.08983152841193 49.999999999988724, -120.08810544078257 50.011295055219406, -120.082993510474 50.02215353087995, -120.07469218610126 50.03215857448533, -120.06352048290445 50.040926345154375, -120.04990772317231 50.048120665845154, -120.03437703762728 50.05346582623596, -120.01752526180509 50.05675706193107, -119.99999999999999 50.057868324959486, -80 50.057868324959486, -79.98247473819491 50.05675706193107, -79.96562296237272 50.05346582623596, -79.95009227682765 50.048120665845154, -79.93647951709555 50.040926345154375, -79.92530781389875 50.03215857448533, -79.91700648952599 50.02215353087995, -79.91189455921744 50.011295055219406, -79.91016847158805 49.999999999988724, -79.91016847158805 29.999999999999467, -79.91189455921744 29.984744778413628, -79.91700648952599 29.970073573592575, -79.92530781389875 29.956550575939833, -79.93647951709555 29.944696041120597, -79.95009227682765 29.934966209460505, -79.96562296237272 29.927735669848445, -79.98247473819491 29.92328286155486, -80 29.92177928675603, -119.99999999999999 29.92177928675603, -120.01752526180509 29.92328286155486, -120.03437703762728 29.927735669848445, -120.04990772317231 29.934966209460505, -120.06352048290445 29.944696041120597, -120.07469218610126 29.956550575939833, -120.082993510474 29.970073573592575, -120.08810544078257 29.984744778413628, -120.08983152841193 29.999999999999467))"); - Geometry postgis_result1 = Constructors.geomFromEWKT("POLYGON((16.24865305783681 48.249999997812985,16.248678914513565 48.250175425690124,16.248755497935985 48.250344117934766,16.248879867933624 48.25049959710733,16.249047249310628 48.25063589306893,16.249251215157262 48.25074777224136,16.24948393361178 48.25083093858872,16.24973646859988 48.25088219859975,16.249999123001583 48.25089958393185,16.35000087730765 48.25089956809112,16.35026352923657 48.25088218269285,16.350516061632025 48.2508309226687,16.35074877731915 48.2507477564026,16.35095274019212 48.25063587746145,16.351120118386177 48.250499581945974,16.35124448502013 48.250344103503025,16.351321064959144 48.25017541233815,16.3513469181252 48.24999998595004,16.351345606584914 48.19999999828486,16.35131972684775 48.19982442741726,16.351243088946138 48.19965560959058,16.351118640921378 48.19950003769385,16.3509511695179 48.199363695110996,16.350747116034473 48.19925182561332,16.350514328567655 48.19916873170203,16.35026176017886 48.19911760915353,16.34999912459135 48.19910042412636,16.250000875738355 48.19910040825441,16.249738237669884 48.19911759321507,16.249485666680723 48.19916871575125,16.24925287643941 48.199251809745654,16.249048819975993 48.19936367947753,16.2488813453842 48.199500022510485,16.248756893991516 48.19965559514185,16.248680252603585 48.199824414054326,16.248654369354316 48.199999986417914,16.24865305783681 48.249999997812985))"); - Geometry postgis_result2 = Constructors.geomFromEWKT("POLYGON((-120.00898315284118 29.999999999999993,-120.00898315284118 49.99999999999999,-120.00881054407824 50.00112962562472,-120.0082993510474 50.00221581519921,-120.00746921861011 50.003216831392564,-120.00635204829044 50.004094212187034,-120.00499077231723 50.00481424796622,-120.00343770376273 50.00534927579437,-120.00175252618051 50.00567874132504,-119.99999999999999 50.00578998770771,-80 50.00578998770771,-79.99824747381949 50.00567874132504,-79.99656229623727 50.00534927579437,-79.99500922768276 50.00481424796622,-79.99364795170956 50.004094212187034,-79.99253078138987 50.003216831392564,-79.9917006489526 50.00221581519921,-79.99118945592174 50.00112962562472,-79.9910168471588 49.99999999999999,-79.9910168471588 29.999999999999993,-79.99118945592174 29.99847458440221,-79.9917006489526 29.997007767335962,-79.99253078138987 29.99565592159683,-79.99364795170956 29.99447100360229,-79.99500922768276 29.993498555861333,-79.99656229623727 29.992775955769954,-79.99824747381949 29.992330978187542,-80 29.992180727190387,-119.99999999999999 29.992180727190387,-120.00175252618051 29.992330978187542,-120.00343770376273 29.992775955769954,-120.00499077231723 29.993498555861333,-120.00635204829044 29.99447100360229,-120.00746921861011 29.99565592159683,-120.0082993510474 29.997007767335962,-120.00881054407824 29.99847458440221,-120.00898315284118 29.999999999999993))"); - Geometry postgis_result3 = Constructors.geomFromEWKT("POLYGON((-179.99820962883618 59.99995929000264,-179.99825989222717 59.999785136194745,-179.9983770256619 59.99961923984817,-179.99855652693947 59.99946797609105,-179.99879149742534 59.99933715766465,-179.999072907238 59.99923181157932,-179.99938994227725 59.9991559859705,-179.99973041975517 59.999112594569745,179.9999187437317 59.999103304762926,179.99957102955932 59.999128473531854,179.9992397991585 59.99918713373882,179.99893778069907 59.999277031280386,179.99867658007986 59.99939471168456,179.99846623499656 59.99953565282769,179.99831482921377 59.99969443867636,179.99822818186144 59.999864967383445,179.998209623704 60.00004068574784,179.9982598689955 60.00021484103153,179.9983769878675 60.000380740460706,179.99855648033625 60.00053200843791,179.99879144910815 60.000662831579476,179.99907286456278 60.00076818215714,179.99938991174088 60.00084401135155,179.99973040600648 60.00088740488531,-179.9999187385995 60.0008966950478,-179.99957100632759 60.00087152480308,-179.9992397613641 60.0008128615135,-179.99893773409582 60.000722959751855,-179.99867653176275 60.00060527463255,-179.99846619232136 60.00046432899707,-179.99831479867737 60.00030553956281,-179.99822816811277 60.000135008722694,-179.99820962883618 59.99995929000264))"); - Geometry postgis_result4 = Constructors.geomFromEWKT("POLYGON((-91.18706814560713 30.45193179814003,-91.1890681473133 30.453431798552806,-91.18908219578012 30.453444627151963,-91.18909308551447 30.453459583698564,-91.18910039802891 30.453476093421752,-91.18910385230598 30.453493521862622,-91.1891033155977 30.453511199256216,-91.18909880852735 30.453528446270074,-91.18909050429724 30.453544600110646,-91.18907872203292 30.453559039994175,-91.18906391451956 30.453571211003144,-91.18904665080186 30.453580645411602,-91.18902759431575 30.453586980659722,-91.189007477393 30.45358997328696,-91.1889870731177 30.453589508288072,-91.18896716561683 30.45358560353286,-91.18894851992636 30.453578409079352,-91.18893185259108 30.453568201407066,-91.18693185325601 30.45206820103894,-91.1849318540605 30.450568200775386,-91.18491780618884 30.450555372062745,-91.18490691698447 30.450540415432766,-91.18489960491243 30.45052390566114,-91.18489615096986 30.450506477209665,-91.18489668788781 30.450488799844198,-91.1849011950307 30.45047155289582,-91.18490949918969 30.45045539915463,-91.18492128123923 30.450440959399103,-91.18493608840117 30.45042878854006,-91.18495335164478 30.45041935429591,-91.18497240755428 30.450413019218534,-91.18499252382345 30.450410026760903,-91.18501292739734 30.450410491921236,-91.1850328341802 30.450414396823803,-91.18505147916741 30.450421591405906,-91.1850681458439 30.450431799184482,-91.18706814560713 30.45193179814003))"); - Geometry postgis_result5 = Constructors.geomFromEWKT("POLYGON((-120.08983152841193 29.999999999999993,-120.08983152841193 49.99999999999999,-120.08810544078257 50.011295055230505,-120.082993510474 50.02215353089088,-120.07469218610122 50.032158574496115,-120.06352048290445 50.040926345165026,-120.04990772317234 50.04812066585569,-120.03437703762728 50.0534658262464,-120.01752526180509 50.05675706194147,-119.99999999999999 50.05786832496988,-80 50.05786832496988,-79.98247473819491 50.05675706194147,-79.9656229623727 50.0534658262464,-79.95009227682766 50.04812066585569,-79.93647951709556 50.040926345165026,-79.92530781389875 50.032158574496115,-79.91700648952599 50.02215353089088,-79.91189455921743 50.011295055230505,-79.91016847158805 49.99999999999999,-79.91016847158805 29.999999999999993,-79.91189455921743 29.984744778414523,-79.91700648952599 29.970073573593833,-79.92530781389875 29.956550575941442,-79.93647951709556 29.944696041122494,-79.95009227682766 29.934966209462644,-79.9656229623727 29.927735669850765,-79.98247473819491 29.923282861557286,-80 29.921779286758486,-119.99999999999999 29.921779286758486,-120.01752526180509 29.923282861557286,-120.03437703762728 29.927735669850765,-120.04990772317234 29.934966209462644,-120.06352048290445 29.944696041122494,-120.07469218610122 29.956550575941442,-120.082993510474 29.970073573593833,-120.08810544078257 29.984744778414523,-120.08983152841193 29.999999999999993))"); + Geometry postgis_result1 = geomFromEWKT("POLYGON((16.24865305783681 48.249999997812985,16.248678914513565 48.250175425690124,16.248755497935985 48.250344117934766,16.248879867933624 48.25049959710733,16.249047249310628 48.25063589306893,16.249251215157262 48.25074777224136,16.24948393361178 48.25083093858872,16.24973646859988 48.25088219859975,16.249999123001583 48.25089958393185,16.35000087730765 48.25089956809112,16.35026352923657 48.25088218269285,16.350516061632025 48.2508309226687,16.35074877731915 48.2507477564026,16.35095274019212 48.25063587746145,16.351120118386177 48.250499581945974,16.35124448502013 48.250344103503025,16.351321064959144 48.25017541233815,16.3513469181252 48.24999998595004,16.351345606584914 48.19999999828486,16.35131972684775 48.19982442741726,16.351243088946138 48.19965560959058,16.351118640921378 48.19950003769385,16.3509511695179 48.199363695110996,16.350747116034473 48.19925182561332,16.350514328567655 48.19916873170203,16.35026176017886 48.19911760915353,16.34999912459135 48.19910042412636,16.250000875738355 48.19910040825441,16.249738237669884 48.19911759321507,16.249485666680723 48.19916871575125,16.24925287643941 48.199251809745654,16.249048819975993 48.19936367947753,16.2488813453842 48.199500022510485,16.248756893991516 48.19965559514185,16.248680252603585 48.199824414054326,16.248654369354316 48.199999986417914,16.24865305783681 48.249999997812985))"); + Geometry postgis_result2 = geomFromEWKT("POLYGON((-120.00898315284118 29.999999999999993,-120.00898315284118 49.99999999999999,-120.00881054407824 50.00112962562472,-120.0082993510474 50.00221581519921,-120.00746921861011 50.003216831392564,-120.00635204829044 50.004094212187034,-120.00499077231723 50.00481424796622,-120.00343770376273 50.00534927579437,-120.00175252618051 50.00567874132504,-119.99999999999999 50.00578998770771,-80 50.00578998770771,-79.99824747381949 50.00567874132504,-79.99656229623727 50.00534927579437,-79.99500922768276 50.00481424796622,-79.99364795170956 50.004094212187034,-79.99253078138987 50.003216831392564,-79.9917006489526 50.00221581519921,-79.99118945592174 50.00112962562472,-79.9910168471588 49.99999999999999,-79.9910168471588 29.999999999999993,-79.99118945592174 29.99847458440221,-79.9917006489526 29.997007767335962,-79.99253078138987 29.99565592159683,-79.99364795170956 29.99447100360229,-79.99500922768276 29.993498555861333,-79.99656229623727 29.992775955769954,-79.99824747381949 29.992330978187542,-80 29.992180727190387,-119.99999999999999 29.992180727190387,-120.00175252618051 29.992330978187542,-120.00343770376273 29.992775955769954,-120.00499077231723 29.993498555861333,-120.00635204829044 29.99447100360229,-120.00746921861011 29.99565592159683,-120.0082993510474 29.997007767335962,-120.00881054407824 29.99847458440221,-120.00898315284118 29.999999999999993))"); + Geometry postgis_result3 = geomFromEWKT("POLYGON((-179.99820962883618 59.99995929000264,-179.99825989222717 59.999785136194745,-179.9983770256619 59.99961923984817,-179.99855652693947 59.99946797609105,-179.99879149742534 59.99933715766465,-179.999072907238 59.99923181157932,-179.99938994227725 59.9991559859705,-179.99973041975517 59.999112594569745,179.9999187437317 59.999103304762926,179.99957102955932 59.999128473531854,179.9992397991585 59.99918713373882,179.99893778069907 59.999277031280386,179.99867658007986 59.99939471168456,179.99846623499656 59.99953565282769,179.99831482921377 59.99969443867636,179.99822818186144 59.999864967383445,179.998209623704 60.00004068574784,179.9982598689955 60.00021484103153,179.9983769878675 60.000380740460706,179.99855648033625 60.00053200843791,179.99879144910815 60.000662831579476,179.99907286456278 60.00076818215714,179.99938991174088 60.00084401135155,179.99973040600648 60.00088740488531,-179.9999187385995 60.0008966950478,-179.99957100632759 60.00087152480308,-179.9992397613641 60.0008128615135,-179.99893773409582 60.000722959751855,-179.99867653176275 60.00060527463255,-179.99846619232136 60.00046432899707,-179.99831479867737 60.00030553956281,-179.99822816811277 60.000135008722694,-179.99820962883618 59.99995929000264))"); + Geometry postgis_result4 = geomFromEWKT("POLYGON((-91.18706814560713 30.45193179814003,-91.1890681473133 30.453431798552806,-91.18908219578012 30.453444627151963,-91.18909308551447 30.453459583698564,-91.18910039802891 30.453476093421752,-91.18910385230598 30.453493521862622,-91.1891033155977 30.453511199256216,-91.18909880852735 30.453528446270074,-91.18909050429724 30.453544600110646,-91.18907872203292 30.453559039994175,-91.18906391451956 30.453571211003144,-91.18904665080186 30.453580645411602,-91.18902759431575 30.453586980659722,-91.189007477393 30.45358997328696,-91.1889870731177 30.453589508288072,-91.18896716561683 30.45358560353286,-91.18894851992636 30.453578409079352,-91.18893185259108 30.453568201407066,-91.18693185325601 30.45206820103894,-91.1849318540605 30.450568200775386,-91.18491780618884 30.450555372062745,-91.18490691698447 30.450540415432766,-91.18489960491243 30.45052390566114,-91.18489615096986 30.450506477209665,-91.18489668788781 30.450488799844198,-91.1849011950307 30.45047155289582,-91.18490949918969 30.45045539915463,-91.18492128123923 30.450440959399103,-91.18493608840117 30.45042878854006,-91.18495335164478 30.45041935429591,-91.18497240755428 30.450413019218534,-91.18499252382345 30.450410026760903,-91.18501292739734 30.450410491921236,-91.1850328341802 30.450414396823803,-91.18505147916741 30.450421591405906,-91.1850681458439 30.450431799184482,-91.18706814560713 30.45193179814003))"); + Geometry postgis_result5 = geomFromEWKT("POLYGON((-120.08983152841193 29.999999999999993,-120.08983152841193 49.99999999999999,-120.08810544078257 50.011295055230505,-120.082993510474 50.02215353089088,-120.07469218610122 50.032158574496115,-120.06352048290445 50.040926345165026,-120.04990772317234 50.04812066585569,-120.03437703762728 50.0534658262464,-120.01752526180509 50.05675706194147,-119.99999999999999 50.05786832496988,-80 50.05786832496988,-79.98247473819491 50.05675706194147,-79.9656229623727 50.0534658262464,-79.95009227682766 50.04812066585569,-79.93647951709556 50.040926345165026,-79.92530781389875 50.032158574496115,-79.91700648952599 50.02215353089088,-79.91189455921743 50.011295055230505,-79.91016847158805 49.99999999999999,-79.91016847158805 29.999999999999993,-79.91189455921743 29.984744778414523,-79.91700648952599 29.970073573593833,-79.92530781389875 29.956550575941442,-79.93647951709556 29.944696041122494,-79.95009227682766 29.934966209462644,-79.9656229623727 29.927735669850765,-79.98247473819491 29.923282861557286,-80 29.921779286758486,-119.99999999999999 29.921779286758486,-120.01752526180509 29.923282861557286,-120.03437703762728 29.927735669850765,-120.04990772317234 29.934966209462644,-120.06352048290445 29.944696041122494,-120.07469218610122 29.956550575941442,-120.082993510474 29.970073573593833,-120.08810544078257 29.984744778414523,-120.08983152841193 29.999999999999993))"); assertEquals(Functions.reducePrecision(expected1, 8), Functions.reducePrecision(result1, 8)); assertEquals(4326, Functions.getSRID(result1)); @@ -1240,7 +1243,7 @@ private static double comparePolygons(Geometry p1, Geometry p2) { } @Test - public void testBestSRID() { + public void testBestSRID() throws ParseException { int[][] testCases_special = { {0, -70, 3409}, // EPSG:3409 (Antarctic Polar Stereographic) {0, 70, 3574}, // EPSG:3575 (North Pole LAEA Alaska) @@ -1799,7 +1802,7 @@ public void geometryTypeWithMeasuredCollection() { } @Test - public void closestPoint() throws FactoryException, TransformException { + public void closestPoint() { Point point1 = GEOMETRY_FACTORY.createPoint(new Coordinate(1, 1)); LineString lineString1 = GEOMETRY_FACTORY.createLineString(coordArray(1, 0, 1, 1, 2, 1, 2, 0, 1, 0)); String expected1 = "POINT (1 1)"; @@ -1980,7 +1983,7 @@ public void transform() } @Test - public void voronoiPolygons() throws FactoryException, TransformException { + public void voronoiPolygons() { MultiPoint multiPoint = GEOMETRY_FACTORY.createMultiPointFromCoords(coordArray(0, 0, 2, 2)); Geometry actual1 = FunctionsGeoTools.voronoiPolygons(multiPoint, 0, null); assertEquals("GEOMETRYCOLLECTION (POLYGON ((-2 -2, -2 4, 4 -2, -2 -2)), POLYGON ((-2 4, 4 4, 4 -2, -2 4)))", diff --git a/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java b/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java index 62d8730559..92dd949a04 100644 --- a/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java +++ b/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java @@ -92,20 +92,6 @@ public void testBuffer() { expected = "POLYGON ((43.2156 77.3465, 44.8523 78.5733, 46.7044 79.4413, 48.6944 79.9144, 50.739 79.9727, 52.7527 79.6137, 54.6512 78.8525, 56.3552 77.7209, 57.7932 76.2663, 58.9052 74.5495, 59.6446 72.6424, 79.6446 -0.3576, 82.2869 -10.0022, 62.9978 -15.2869, 45.9128 47.0733, 8.1373 -5.8124, 2.325 -13.9497, -13.9497 -2.325, 41.8627 75.8124, 43.2156 77.3465))"; assertEquals(expected, actual); -// actual = (String) first(tableEnv.sqlQuery("SELECT ST_AsText(ST_ReducePrecision(ST_Buffer(ST_GeomFromWKT('LINESTRING(179.95 -10, -179.95 -10)'), 10), 4))")).getField(0); -// expected = "POLYGON ((43.2156 77.3465, 44.8523 78.5733, 46.7044 79.4413, 48.6944 79.9144, 50.739 79.9727, 52.7527 79.6137, 54.6512 78.8525, 56.3552 77.7209, 57.7932 76.2663, 58.9052 74.5495, 59.6446 72.6424, 79.6446 -0.3576, 82.2869 -10.0022, 62.9978 -15.2869, 45.9128 47.0733, 8.1373 -5.8124, 2.325 -13.9497, -13.9497 -2.325, 41.8627 75.8124, 43.2156 77.3465))"; -// System.out.println("actual: "+actual); -// String area = (String) first(tableEnv.sqlQuery("ST_AsText(ST_Area(ST_GeomFromWKT('POLYGON ((181.05 0, 181.05 -20, 170.95 -20, 160.95 -20, 160.95 0, 171.05 0, 181.05 0))')))")).getField(0); -// System.out.println("area: "+area); -// assertEquals(expected, actual); - -// actual = (String) first(tableEnv.sqlQuery("SELECT ST_AsText(ST_ReducePrecision(ST_Buffer(ST_GeomFromWKT('LINESTRING(179.95 -10, -179.95 -10)'), 10, true, 'endcap=square'), 4))")).getField(0); -// expected = "POLYGON ((43.2156 77.3465, 44.8523 78.5733, 46.7044 79.4413, 48.6944 79.9144, 50.739 79.9727, 52.7527 79.6137, 54.6512 78.8525, 56.3552 77.7209, 57.7932 76.2663, 58.9052 74.5495, 59.6446 72.6424, 79.6446 -0.3576, 82.2869 -10.0022, 62.9978 -15.2869, 45.9128 47.0733, 8.1373 -5.8124, 2.325 -13.9497, -13.9497 -2.325, 41.8627 75.8124, 43.2156 77.3465))"; -// System.out.println("actual: "+actual); -// area = (String) first(tableEnv.sqlQuery("SELECT ST_AsText(ST_Area(ST_ReducePrecision(ST_Buffer(ST_GeomFromWKT('LINESTRING(179.95 -10, -179.95 -10)'), 10, 'endcap=square', true), 4)))")).getField(0); -// System.out.println("area: "+area); -// assertEquals(expected, actual); - actual = (String) first(tableEnv.sqlQuery("SELECT ST_AsText(ST_ReducePrecision(ST_Buffer(ST_Point(100, 90), 200, false, 'quad_segs=4'), 4))")).getField(0); expected = "POLYGON ((284.7759 13.4633, 241.4214 -51.4214, 176.5367 -94.7759, 100 -110, 23.4633 -94.7759, -41.4214 -51.4214, -84.7759 13.4633, -100 90, -84.7759 166.5367, -41.4214 231.4214, 23.4633 274.7759, 100 290, 176.5367 274.7759, 241.4214 231.4214, 284.7759 166.5367, 300 90, 284.7759 13.4633))"; assertEquals(expected, actual); @@ -121,7 +107,7 @@ public void testBuffer() { @Test public void testBestSRID() { - Table table1 = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('POINT (160 40)') AS geom"); + Table table1 = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('POINT (1000 -500)', 3857) AS geom"); table1 = table1.select(call(Functions.ST_BestSRID.class.getSimpleName(), $("geom"))); int result = (int) first(table1).getField(0); assertEquals(32657, result); From 8f994fc7ad3bf771232aaf406a3b6053b896d8bf Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Sun, 25 Feb 2024 18:27:45 -0500 Subject: [PATCH 24/54] Docs: Update --- docs/api/flink/Function.md | 33 ++++++++++++++++++++------------- docs/api/sql/Function.md | 34 ++++++++++++++++++++++------------ 2 files changed, 42 insertions(+), 25 deletions(-) diff --git a/docs/api/flink/Function.md b/docs/api/flink/Function.md index e2eb133df4..2586fa79e5 100644 --- a/docs/api/flink/Function.md +++ b/docs/api/flink/Function.md @@ -555,18 +555,10 @@ Output: `LINESTRING Z(-1 -1 0, 10 5 5)` Introduction: Returns a geometry/geography that represents all points whose distance from this Geometry/geography is less than or equal to distance. The function supports both Planar/Euclidean and Spheroidal/Geodesic buffering (Since v1.6.0). -Buffer Style Parameters: - -The optional third parameter controls the buffer accuracy and style. Buffer accuracy is specified by the number of line segments approximating a quarter circle, with a default of 8 segments. Buffer style can be set by providing blank-separated key=value pairs in a list format. - -- `quad_segs=#` : Number of line segments utilized to approximate a quarter circle (default is 8). -- `endcap=round|flat|square` : End cap style (default is `round`). `butt` is an accepted synonym for `flat`. -- `join=round|mitre|bevel` : Join style (default is `round`). `miter` is an accepted synonym for `mitre`. -- `mitre_limit=#.#` : mitre ratio limit and it only affects mitred join style. `miter_limit` is an accepted synonym for `mitre_limit`. -- `side=both|left|right` : The option `left` or `right` enables a single-sided buffer operation on the geometry, with the buffered side aligned according to the direction of the line. This functionality is specific to LINESTRING geometry and has no impact on POINT or POLYGON geometries. By default, square end caps are applied. - Mode of buffer calculation (Since: `v1.6.0`): +The optional third parameter, `useSpheroid`, controls the mode of buffer calculation. + - Planar Buffering (default): When `useSpheroid` is false, `ST_Buffer` performs standard planar buffering based on the provided parameters. - Spheroidal Buffering: - When `useSpheroid` is set to true, the function returns the spheroidal buffer polygon for more accurate representation over the Earth. @@ -576,7 +568,22 @@ Mode of buffer calculation (Since: `v1.6.0`): - Finally, the buffered geometry is transformed back to its original SRID, or to WGS 84 if the original SRID was not set. !!!note -`ST_Buffer` throws an `IllegalArgumentException` if the correct format, parameters, or options are not provided. + As of now, spheroidal buffering only supports lon/lat coordinate systems and will throw an `IllegalArgumentException` for input geometries in meter based coordinate systems. +!!!note + Spheroidal buffering may not produce accurate output buffer for input geometries larger than a UTM zone. + +Buffer Style Parameters: + +The optional forth parameter controls the buffer accuracy and style. Buffer accuracy is specified by the number of line segments approximating a quarter circle, with a default of 8 segments. Buffer style can be set by providing blank-separated key=value pairs in a list format. + +- `quad_segs=#` : Number of line segments utilized to approximate a quarter circle (default is 8). +- `endcap=round|flat|square` : End cap style (default is `round`). `butt` is an accepted synonym for `flat`. +- `join=round|mitre|bevel` : Join style (default is `round`). `miter` is an accepted synonym for `mitre`. +- `mitre_limit=#.#` : mitre ratio limit and it only affects mitred join style. `miter_limit` is an accepted synonym for `mitre_limit`. +- `side=both|left|right` : The option `left` or `right` enables a single-sided buffer operation on the geometry, with the buffered side aligned according to the direction of the line. This functionality is specific to LINESTRING geometry and has no impact on POINT or POLYGON geometries. By default, square end caps are applied. + +!!!note + `ST_Buffer` throws an `IllegalArgumentException` if the correct format, parameters, or options are not provided. Format: @@ -589,7 +596,7 @@ ST_Buffer (A: Geometry, buffer: Double, bufferStyleParameters: String [Optional] Since: `v1.5.1` -Spark SQL Example: +SQL Example: ```sql SELECT ST_Buffer(ST_GeomFromWKT('POINT(0 0)'), 10) @@ -603,7 +610,7 @@ Output: 8 Segments   2 Segments -Spark SQL Example: +SQL Example: ```sql SELECT ST_Buffer(ST_GeomFromWKT('LINESTRING(0 0, 50 70, 100 100)'), 10, 'side=left') diff --git a/docs/api/sql/Function.md b/docs/api/sql/Function.md index 5bf27edb29..2006193f1e 100644 --- a/docs/api/sql/Function.md +++ b/docs/api/sql/Function.md @@ -551,19 +551,14 @@ Output: `LINESTRING Z(-1 -1 0, 10 5 5)` ## ST_Buffer -Introduction: Returns a geometry/geography that represents all points whose distance from this Geometry/geography is less than or equal to distance. The function supports both Planar/Euclidean and Spheroidal/Geodesic buffering (Since v1.6.0). -Buffer Style Parameters: - -The optional third parameter controls the buffer accuracy and style. Buffer accuracy is specified by the number of line segments approximating a quarter circle, with a default of 8 segments. Buffer style can be set by providing blank-separated key=value pairs in a list format. +## ST_Buffer -- `quad_segs=#` : Number of line segments utilized to approximate a quarter circle (default is 8). -- `endcap=round|flat|square` : End cap style (default is `round`). `butt` is an accepted synonym for `flat`. -- `join=round|mitre|bevel` : Join style (default is `round`). `miter` is an accepted synonym for `mitre`. -- `mitre_limit=#.#` : mitre ratio limit and it only affects mitred join style. `miter_limit` is an accepted synonym for `mitre_limit`. -- `side=both|left|right` : The option `left` or `right` enables a single-sided buffer operation on the geometry, with the buffered side aligned according to the direction of the line. This functionality is specific to LINESTRING geometry and has no impact on POINT or POLYGON geometries. By default, square end caps are applied. +Introduction: Returns a geometry/geography that represents all points whose distance from this Geometry/geography is less than or equal to distance. The function supports both Planar/Euclidean and Spheroidal/Geodesic buffering (Since v1.6.0). Mode of buffer calculation (Since: `v1.6.0`): +The optional third parameter, `useSpheroid`, controls the mode of buffer calculation. + - Planar Buffering (default): When `useSpheroid` is false, `ST_Buffer` performs standard planar buffering based on the provided parameters. - Spheroidal Buffering: - When `useSpheroid` is set to true, the function returns the spheroidal buffer polygon for more accurate representation over the Earth. @@ -572,6 +567,21 @@ Mode of buffer calculation (Since: `v1.6.0`): - The standard planar buffer operation is then applied in this coordinate system. - Finally, the buffered geometry is transformed back to its original SRID, or to WGS 84 if the original SRID was not set. +!!!note + As of now, spheroidal buffering only supports lon/lat coordinate systems and will throw an `IllegalArgumentException` for input geometries in meter based coordinate systems. +!!!note + Spheroidal buffering may not produce accurate output buffer for input geometries larger than a UTM zone. + +Buffer Style Parameters: + +The optional forth parameter controls the buffer accuracy and style. Buffer accuracy is specified by the number of line segments approximating a quarter circle, with a default of 8 segments. Buffer style can be set by providing blank-separated key=value pairs in a list format. + +- `quad_segs=#` : Number of line segments utilized to approximate a quarter circle (default is 8). +- `endcap=round|flat|square` : End cap style (default is `round`). `butt` is an accepted synonym for `flat`. +- `join=round|mitre|bevel` : Join style (default is `round`). `miter` is an accepted synonym for `mitre`. +- `mitre_limit=#.#` : mitre ratio limit and it only affects mitred join style. `miter_limit` is an accepted synonym for `mitre_limit`. +- `side=both|left|right` : The option `left` or `right` enables a single-sided buffer operation on the geometry, with the buffered side aligned according to the direction of the line. This functionality is specific to LINESTRING geometry and has no impact on POINT or POLYGON geometries. By default, square end caps are applied. + !!!note `ST_Buffer` throws an `IllegalArgumentException` if the correct format, parameters, or options are not provided. @@ -581,12 +591,12 @@ Format: ST_Buffer (A: Geometry, buffer: Double, bufferStyleParameters: String [Optional]) ``` ``` -ST_Buffer (A: Geometry, buffer: Double, bufferStyleParameters: String [Optional], useSpheroid: Boolean) +ST_Buffer (A: Geometry, buffer: Double, bufferStyleParameters: String [Optional], useSperoidal: Boolean) ``` Since: `v1.5.1` -Spark SQL Example: +SQL Example: ```sql SELECT ST_Buffer(ST_GeomFromWKT('POINT(0 0)'), 10) @@ -600,7 +610,7 @@ Output: 8 Segments   2 Segments -Spark SQL Example: +SQL Example: ```sql SELECT ST_Buffer(ST_GeomFromWKT('LINESTRING(0 0, 50 70, 100 100)'), 10, 'side=left') From 7542d7531b4e4d537411d1e7e9d7c25a87daa6af Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Sun, 25 Feb 2024 18:31:14 -0500 Subject: [PATCH 25/54] Fix typo --- .../apache/sedona/common/FunctionsTest.java | 74 ++++++++++--------- 1 file changed, 39 insertions(+), 35 deletions(-) diff --git a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java index c8c4144e39..bddd8d1989 100644 --- a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java +++ b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java @@ -1180,9 +1180,8 @@ public void testBufferSpheroidal() throws ParseException { Geometry polygon1 = GEOMETRY_FACTORY.createPolygon(coordArray(16.2500, 48.2500, 16.3500, 48.2500, 16.3500, 48.2000, 16.2500, 48.2000, 16.2500, 48.2500)); Geometry polygon2 = geomFromWKT("POLYGON((-120 30, -80 30, -80 50, -120 50, -120 30))", 4269); Geometry point1 = geomFromWKT("POINT(-180 60)", 4269); - Geometry point2 = geomFromEWKT("POINT(10000 -500)"); Geometry linestring1 = geomFromEWKT("LINESTRING(-91.185 30.4505, -91.187 30.452, -91.189 30.4535)"); - Geometry polygon3 = geomFromWKT("POLYGON((-120 30, -80 30, -80 50, -120 50, -120 30))", 4230); + Geometry polygon3 = geomFromWKT("POLYGON((-120 30, -80 30, -80 50, -120 50, -120 30))", 4269); Geometry result1 = Functions.buffer(polygon1, 100, true); Geometry result2 = Functions.buffer(polygon2, 1000, true); @@ -1251,6 +1250,10 @@ public void testBestSRID() throws ParseException { {-180, 60, 32660}, {180, 60, 32660}, {-180, -60, 32760}, {180, -60, 32760}, }; + Geometry geom1 = geomFromWKT("POLYGON((-120 30, -80 30, -80 50, -120 50, -120 30))", 4230); //geomFromWKT("POINT (10000 -500)", 3857); + int actualEPSG1 = Functions.bestSRID(geom1); + System.out.println("actualEPSG: "+actualEPSG1); + // Number of UTM zones int numZones = (177 - (-177)) / 6 + 1; int numZonesSouth = (177 - (-177)) / 6 + 1; @@ -1276,39 +1279,40 @@ public void testBestSRID() throws ParseException { indexSouth++; } - for (int[] testCase : testCases_special) { - Geometry geom = GEOMETRY_FACTORY.createPoint(new Coordinate(testCase[0], testCase[1])); - int actualEPSG = Functions.bestSRID(geom); - int expectedEPSG = testCase[2]; - assertEquals("Failed at coordinates (" + testCase[0] + ", " + testCase[1] + ")", expectedEPSG, actualEPSG); - } - - for (int[] testCase : testCases_UTMNorth) { - Geometry geom = GEOMETRY_FACTORY.createPoint(new Coordinate(testCase[0], testCase[1])); - int actualEPSG = Functions.bestSRID(geom); - int expectedEPSG = testCase[2]; - assertEquals("Failed at coordinates (" + testCase[0] + ", " + testCase[1] + ")", expectedEPSG, actualEPSG); - } - - for (int[] testCase : testCases_UTMSouth) { - Geometry geom = GEOMETRY_FACTORY.createPoint(new Coordinate(testCase[0], testCase[1])); - int actualEPSG = Functions.bestSRID(geom); - int expectedEPSG = testCase[2]; - assertEquals("Failed at coordinates (" + testCase[0] + ", " + testCase[1] + ")", expectedEPSG, actualEPSG); - } - - // Large geometry that does not fit into UTM or polar categories - Geometry geom = GEOMETRY_FACTORY.createPolygon(new Coordinate[] { - new Coordinate(-160, -40), - new Coordinate(-160, 40), - new Coordinate(160, 40), - new Coordinate(160, -40), - new Coordinate(-160, -40) - }); - int expectedEPSG = 3395; // EPSG code for World Mercator - int actualEPSG = Functions.bestSRID(geom); - - assertEquals("Expected World Mercator projection for wide range geometry", expectedEPSG, actualEPSG); +// for (int[] testCase : testCases_special) { +// Geometry geom = GEOMETRY_FACTORY.createPoint(new Coordinate(testCase[0], testCase[1])); +// int actualEPSG = Functions.bestSRID(geom); +// System.out.println("actualEPSG: "+actualEPSG); +// int expectedEPSG = testCase[2]; +// assertEquals("Failed at coordinates (" + testCase[0] + ", " + testCase[1] + ")", expectedEPSG, actualEPSG); +// } + +// for (int[] testCase : testCases_UTMNorth) { +// Geometry geom = GEOMETRY_FACTORY.createPoint(new Coordinate(testCase[0], testCase[1])); +// int actualEPSG = Functions.bestSRID(geom); +// int expectedEPSG = testCase[2]; +// assertEquals("Failed at coordinates (" + testCase[0] + ", " + testCase[1] + ")", expectedEPSG, actualEPSG); +// } +// +// for (int[] testCase : testCases_UTMSouth) { +// Geometry geom = GEOMETRY_FACTORY.createPoint(new Coordinate(testCase[0], testCase[1])); +// int actualEPSG = Functions.bestSRID(geom); +// int expectedEPSG = testCase[2]; +// assertEquals("Failed at coordinates (" + testCase[0] + ", " + testCase[1] + ")", expectedEPSG, actualEPSG); +// } +// +// // Large geometry that does not fit into UTM or polar categories +// Geometry geom = GEOMETRY_FACTORY.createPolygon(new Coordinate[] { +// new Coordinate(-160, -40), +// new Coordinate(-160, 40), +// new Coordinate(160, 40), +// new Coordinate(160, -40), +// new Coordinate(-160, -40) +// }); +// int expectedEPSG = 3395; // EPSG code for World Mercator +// int actualEPSG = Functions.bestSRID(geom); +// +// assertEquals("Expected World Mercator projection for wide range geometry", expectedEPSG, actualEPSG); } @Test From ddb795e50ab9f5e981bae8b114c319d93a2ebc7d Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Sun, 25 Feb 2024 18:34:28 -0500 Subject: [PATCH 26/54] Fix incorrect wrong type config --- python/tests/sql/test_dataframe_api.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/python/tests/sql/test_dataframe_api.py b/python/tests/sql/test_dataframe_api.py index 4def29ef63..0917bb8887 100644 --- a/python/tests/sql/test_dataframe_api.py +++ b/python/tests/sql/test_dataframe_api.py @@ -235,8 +235,6 @@ (stf.ST_Boundary, (None,)), (stf.ST_Buffer, (None, 1.0)), (stf.ST_Buffer, ("", None)), - (stf.ST_Buffer, ("", 1.0, "")), - (stf.ST_Buffer, ("", 1.0, "", None)), (stf.ST_BuildArea, (None,)), (stf.ST_Centroid, (None,)), (stf.ST_Collect, (None,)), From 754b2635f28180c2b1eac81074ac44190041a45b Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Sun, 25 Feb 2024 18:38:07 -0500 Subject: [PATCH 27/54] Fix python test --- python/tests/sql/test_function.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/tests/sql/test_function.py b/python/tests/sql/test_function.py index 8891d0049f..168e6f4675 100644 --- a/python/tests/sql/test_function.py +++ b/python/tests/sql/test_function.py @@ -108,7 +108,7 @@ def test_st_buffer(self): function_df = self.spark.sql("select ST_ReducePrecision(ST_Buffer(polygondf.countyshape, 10, true, 'endcap=square'), 2) from polygondf") actual = function_df.take(1)[0][0].wkt - assert actual == "POLYGON ((-107.02 42.06, -107.02 42.07, -107.02 42.09, -107.02 42.11, -107.02 42.32, -107.02 42.33, -107.01 42.42, -107.01 42.43, -106.77 44.33, -106.16 46.15, -105.22 47.82, -103.98 49.27, -102.48 50.47, -100.78 51.36, -98.94 51.9, -97.04 52.09, -97.03 52.09, -97.01 52.09, -96.95 52.09, -96.9 52.09, -96.81 52.09, -96.7 52.09, -96.68 52.09, -96.65 52.09, -96.55 52.09, -96.54 52.09, -96.49 52.09, -96.48 52.09, -94.58 51.89, -92.74 51.33, -91.04 50.43, -89.55 49.23, -88.32 47.76, -87.39 46.08, -86.79 44.25, -86.56 42.35, -86.56 42.18, -86.56 42.17, -86.56 42.1, -86.55 41.99, -86.56 41.9, -86.56 41.78, -86.56 41.77, -86.56 41.75, -86.56 41.73, -86.56 41.7, -86.75 39.76, -87.33 37.89, -88.25 36.17, -89.49 34.66, -91 33.43, -92.72 32.51, -94.59 31.94, -96.53 31.74, -96.55 31.74, -96.56 31.74, -96.57 31.74, -96.58 31.74, -96.6 31.74, -96.69 31.74, -96.72 31.74, -96.75 31.74, -96.94 31.74, -97.02 31.74, -97.04 31.74, -97.06 31.74, -98.99 31.94, -100.85 32.5, -102.56 33.42, -104.07 34.65, -105.31 36.14, -106.23 37.85, -106.81 39.71, -107.02 41.64, -107.02 41.75, -107.02 41.94, -107.02 41.96, -107.02 41.99, -107.02 42.01, -107.02 42.02, -107.02 42.03, -107.02 42.06))" + assert actual == "POLYGON ((-97.02 42.01, -97.02 42.02, -97.02 42.03, -97.02 42.04, -97.02 42.05, -97.02 42.06, -97.02 42.07, -97.02 42.08, -97.02 42.09, -97.01 42.09, -97 42.09, -96.99 42.09, -96.98 42.09, -96.97 42.09, -96.96 42.09, -96.95 42.09, -96.94 42.09, -96.93 42.09, -96.92 42.09, -96.91 42.09, -96.9 42.09, -96.89 42.09, -96.88 42.09, -96.87 42.09, -96.86 42.09, -96.85 42.09, -96.84 42.09, -96.83 42.09, -96.82 42.09, -96.81 42.09, -96.8 42.09, -96.79 42.09, -96.78 42.09, -96.77 42.09, -96.76 42.09, -96.75 42.09, -96.74 42.09, -96.73 42.09, -96.72 42.09, -96.71 42.09, -96.7 42.09, -96.69 42.09, -96.68 42.09, -96.67 42.09, -96.66 42.09, -96.65 42.09, -96.64 42.09, -96.63 42.09, -96.62 42.09, -96.61 42.09, -96.6 42.09, -96.59 42.09, -96.58 42.09, -96.57 42.09, -96.56 42.09, -96.56 42.08, -96.56 42.07, -96.56 42.06, -96.56 42.05, -96.56 42.04, -96.56 42.03, -96.55 42.03, -96.55 42.02, -96.56 42, -96.56 41.99, -96.56 41.98, -96.56 41.97, -96.56 41.96, -96.56 41.95, -96.56 41.94, -96.56 41.93, -96.56 41.92, -96.56 41.91, -96.56 41.9, -96.56 41.89, -96.56 41.88, -96.56 41.87, -96.56 41.86, -96.56 41.85, -96.56 41.84, -96.56 41.83, -96.56 41.82, -96.56 41.81, -96.56 41.8, -96.56 41.79, -96.56 41.78, -96.56 41.77, -96.56 41.76, -96.56 41.75, -96.56 41.74, -96.57 41.74, -96.58 41.74, -96.59 41.74, -96.6 41.74, -96.61 41.74, -96.62 41.74, -96.63 41.74, -96.64 41.74, -96.65 41.74, -96.66 41.74, -96.67 41.74, -96.68 41.74, -96.69 41.74, -96.7 41.74, -96.71 41.74, -96.72 41.74, -96.73 41.74, -96.74 41.74, -96.75 41.74, -96.76 41.74, -96.77 41.74, -96.78 41.74, -96.79 41.74, -96.8 41.74, -96.81 41.74, -96.82 41.74, -96.83 41.74, -96.84 41.74, -96.85 41.74, -96.86 41.74, -96.87 41.74, -96.88 41.74, -96.89 41.74, -96.9 41.74, -96.91 41.74, -96.92 41.74, -96.93 41.74, -96.94 41.74, -96.95 41.74, -96.96 41.74, -96.97 41.74, -96.98 41.74, -96.99 41.74, -97 41.74, -97.01 41.74, -97.02 41.74, -97.02 41.75, -97.02 41.76, -97.02 41.77, -97.02 41.78, -97.02 41.79, -97.02 41.8, -97.02 41.81, -97.02 41.82, -97.02 41.83, -97.02 41.84, -97.02 41.85, -97.02 41.86, -97.02 41.87, -97.02 41.88, -97.02 41.89, -97.02 41.9, -97.02 41.91, -97.02 41.92, -97.02 41.93, -97.02 41.94, -97.02 41.95, -97.02 41.96, -97.02 41.97, -97.02 41.98, -97.02 41.99, -97.02 42, -97.02 42.01))" def test_st_bestsrid(self): polygon_from_wkt = self.spark.read.format("csv"). \ From 632a87863d69496ce4412cee2d26b20ce7ea95e6 Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Mon, 26 Feb 2024 14:04:52 -0500 Subject: [PATCH 28/54] Add handling for geometries crossing the dateline --- .../org/apache/sedona/common/Functions.java | 124 ++++++++++++++++- .../sedona/common/FunctionsGeoTools.java | 7 +- .../org/apache/sedona/common/Predicates.java | 26 ++++ .../apache/sedona/common/FunctionsTest.java | 130 +++++++++++++----- docs/api/flink/Function.md | 2 +- .../org/apache/sedona/flink/FunctionTest.java | 2 +- 6 files changed, 249 insertions(+), 42 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 b7ce9316b9..da63c4fe5a 100644 --- a/common/src/main/java/org/apache/sedona/common/Functions.java +++ b/common/src/main/java/org/apache/sedona/common/Functions.java @@ -203,6 +203,10 @@ else if (singleParam[0].equalsIgnoreCase(listBufferParameters[5])) { } public static int bestSRID(Geometry geometry) throws IllegalArgumentException{ + // Shift longitudes of geometry crosses dateline + geometry = (Predicates.crossesDateLine(geometry)) ? shiftLongitude(geometry) : geometry; + + // Check envelope Envelope envelope = geometry.getEnvelopeInternal(); if (envelope.isNull()) return Spheroid.EPSG_WORLD_MERCATOR; // Fallback EPSG @@ -226,7 +230,7 @@ public static int bestSRID(Geometry geometry) throws IllegalArgumentException{ zone = 59; // UTM zone 60 } else { zone = (int)Math.floor((centerX + 180.0) / 6.0); - zone = Math.min(zone, 59); + zone = (zone > 59) ? zone-60 : zone; } return (centerY < 0.0) ? Spheroid.EPSG_SOUTH_UTM_START + zone : Spheroid.EPSG_NORTH_UTM_START + zone; } @@ -235,6 +239,124 @@ public static int bestSRID(Geometry geometry) throws IllegalArgumentException{ return Spheroid.EPSG_WORLD_MERCATOR; } + /** + * Corrects the longitudes of a geometry to be within the -180 to 180 range. + * This method modifies the original geometry. + * + * @param geometry The geometry to be corrected. + */ + public static void normalizeLongitude(Geometry geometry) { + if (geometry == null || geometry.isEmpty()) { + return; + } + for (Coordinate coord : geometry.getCoordinates()) { + // Normalize the longitude to be within -180 to 180 range + while (coord.x > 180) coord.x -= 360; + while (coord.x < -180) coord.x += 360; + } + } + + public static Geometry shiftLongitude(Geometry geometry) { + if (geometry instanceof Point) { + return shiftCoordinates((Point) geometry); + } else if (geometry instanceof LineString) { + return shiftCoordinates((LineString) geometry); + } else if (geometry instanceof Polygon) { + return shiftCoordinates((Polygon) geometry); + } else if (geometry instanceof MultiPoint) { + return shiftMultiGeometry((MultiPoint) geometry); + } else if (geometry instanceof MultiLineString) { + return shiftMultiGeometry((MultiLineString) geometry); + } else if (geometry instanceof MultiPolygon) { + return shiftMultiGeometry((MultiPolygon) geometry); + } else if (geometry instanceof GeometryCollection) { + return shiftMultiGeometry((GeometryCollection) geometry); + } + return geometry; + } + + private static Geometry shiftCoordinates(Point point) { + Coordinate newCoord = shiftCoordinate(point.getCoordinate()); + return point.getFactory().createPoint(newCoord); + } + + private static Geometry shiftCoordinates(LineString lineString) { + Coordinate[] newCoords = new Coordinate[lineString.getNumPoints()]; + for (int i = 0; i < lineString.getNumPoints(); i++) { + newCoords[i] = shiftCoordinate(lineString.getCoordinateN(i)); + } + return lineString.getFactory().createLineString(newCoords); + } + + private static Geometry shiftCoordinates(Polygon polygon) { + GeometryFactory factory = polygon.getFactory(); + + // Shift exterior ring and convert to LinearRing + LinearRing exteriorRing = shiftRingToLinearRing(polygon.getExteriorRing()); + + // Process interior rings + int numInteriorRings = polygon.getNumInteriorRing(); + LinearRing[] interiorRings = new LinearRing[numInteriorRings]; + for (int i = 0; i < numInteriorRings; i++) { + interiorRings[i] = shiftRingToLinearRing(polygon.getInteriorRingN(i)); + } + + return factory.createPolygon(exteriorRing, interiorRings); + } + + private static LinearRing shiftRingToLinearRing(LineString ring) { + GeometryFactory factory = ring.getFactory(); + Coordinate[] shiftedCoords = new Coordinate[ring.getNumPoints()]; + for (int i = 0; i < ring.getNumPoints(); i++) { + shiftedCoords[i] = shiftCoordinate(ring.getCoordinateN(i)); + } + return factory.createLinearRing(shiftedCoords); + } + + private static Geometry shiftMultiGeometry(GeometryCollection geometryCollection) { + int numGeometries = geometryCollection.getNumGeometries(); + GeometryFactory factory = geometryCollection.getFactory(); + + if (geometryCollection instanceof MultiPoint) { + Point[] points = new Point[numGeometries]; + for (int i = 0; i < numGeometries; i++) { + points[i] = (Point) shiftLongitude(geometryCollection.getGeometryN(i)); + } + return factory.createMultiPoint(points); + } else if (geometryCollection instanceof MultiLineString) { + LineString[] lineStrings = new LineString[numGeometries]; + for (int i = 0; i < numGeometries; i++) { + lineStrings[i] = (LineString) shiftLongitude(geometryCollection.getGeometryN(i)); + } + return factory.createMultiLineString(lineStrings); + } else if (geometryCollection instanceof MultiPolygon) { + Polygon[] polygons = new Polygon[numGeometries]; + for (int i = 0; i < numGeometries; i++) { + polygons[i] = (Polygon) shiftLongitude(geometryCollection.getGeometryN(i)); + } + return factory.createMultiPolygon(polygons); + } else { + // For other geometry collections, just create a new collection with shifted geometries + Geometry[] geometries = new Geometry[numGeometries]; + for (int i = 0; i < numGeometries; i++) { + geometries[i] = shiftLongitude(geometryCollection.getGeometryN(i)); + } + return factory.createGeometryCollection(geometries); + } + } + + + private static Coordinate shiftCoordinate(Coordinate coord) { + double newX = coord.x; + if (newX < 0) { + newX += 360; + } else if (newX > 180) { + newX -= 360; + } + return new Coordinate(newX, coord.y); + } + + public static Geometry envelope(Geometry geometry) { return geometry.getEnvelope(); } diff --git a/common/src/main/java/org/apache/sedona/common/FunctionsGeoTools.java b/common/src/main/java/org/apache/sedona/common/FunctionsGeoTools.java index db67c856e2..71fc06b3f1 100644 --- a/common/src/main/java/org/apache/sedona/common/FunctionsGeoTools.java +++ b/common/src/main/java/org/apache/sedona/common/FunctionsGeoTools.java @@ -143,6 +143,9 @@ public static Geometry bufferSpheroid(Geometry geometry, double radius, BufferPa int originalCRS = geometry.getSRID(); final int WGS84CRS = 4326; + // Shift longitude if geometry crosses dateline + geometry = (Predicates.crossesDateLine(geometry)) ? Functions.shiftLongitude(geometry) : geometry; + // If originalCRS is not set, use WGS84 as the originalCRS for transformation String sourceCRSCode = (originalCRS == 0) ? "EPSG:" + WGS84CRS : "EPSG:" + originalCRS; String targetCRSCode = "EPSG:" + bestCRS; @@ -150,7 +153,6 @@ public static Geometry bufferSpheroid(Geometry geometry, double radius, BufferPa try { // Transform the geometry to the selected SRID Geometry transformedGeometry = transform(geometry, sourceCRSCode, targetCRSCode); - // Apply the buffer operation in the selected SRID Geometry bufferedGeometry = BufferOp.bufferOp(transformedGeometry, radius, params); @@ -158,6 +160,9 @@ public static Geometry bufferSpheroid(Geometry geometry, double radius, BufferPa int backTransformCRSCode = (originalCRS == 0) ? WGS84CRS : originalCRS; Geometry bufferedResult = transform(bufferedGeometry, targetCRSCode, "EPSG:" + backTransformCRSCode); bufferedResult.setSRID(backTransformCRSCode); + + // Normalize longitudes between -180 and 180 + Functions.normalizeLongitude(bufferedResult); return bufferedResult; } catch (FactoryException | TransformException e) { throw new RuntimeException(e); diff --git a/common/src/main/java/org/apache/sedona/common/Predicates.java b/common/src/main/java/org/apache/sedona/common/Predicates.java index df94a0c5d0..b8106d453b 100644 --- a/common/src/main/java/org/apache/sedona/common/Predicates.java +++ b/common/src/main/java/org/apache/sedona/common/Predicates.java @@ -13,6 +13,7 @@ */ package org.apache.sedona.common; +import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; import org.apache.sedona.common.sphere.Spheroid; @@ -62,4 +63,29 @@ public static boolean dWithin(Geometry leftGeometry, Geometry rightGeometry, dou return leftGeometry.isWithinDistance(rightGeometry, distance); } } + + /** + * Checks if a geometry crosses the International Date Line. + * + * @param geometry The geometry to check. + * @return True if the geometry crosses the Date Line, false otherwise. + */ + public static boolean crossesDateLine(Geometry geometry) { + if (geometry == null || geometry.isEmpty()) { + return false; + } + + boolean crossesDateLine = false; + Coordinate previous = null; + + for (Coordinate coord : geometry.getCoordinates()) { + if (previous != null && Math.abs(coord.x - previous.x) > 180) { + crossesDateLine = true; + break; + } + previous = coord; + } + + return crossesDateLine; + } } diff --git a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java index bddd8d1989..47a0b84240 100644 --- a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java +++ b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java @@ -1182,18 +1182,34 @@ public void testBufferSpheroidal() throws ParseException { Geometry point1 = geomFromWKT("POINT(-180 60)", 4269); Geometry linestring1 = geomFromEWKT("LINESTRING(-91.185 30.4505, -91.187 30.452, -91.189 30.4535)"); Geometry polygon3 = geomFromWKT("POLYGON((-120 30, -80 30, -80 50, -120 50, -120 30))", 4269); + Geometry linestring2 = geomFromEWKT("LINESTRING(0.05 15, -0.05 15)"); + + // Geometries crossing dateline + Geometry linestring3 = geomFromEWKT("LINESTRING(179.95 15, -179.95 15)"); + Geometry polygon4 = geomFromEWKT("POLYGON((179 -15, 179 15, -179 15, -179 -15, 179 -15))"); + Geometry polygon5 = geomFromEWKT("POLYGON((178 -10, 178 10, -178 10, -178 -10, 178 -10))"); Geometry result1 = Functions.buffer(polygon1, 100, true); Geometry result2 = Functions.buffer(polygon2, 1000, true); Geometry result3 = Functions.buffer(point1, 100, true); Geometry result4 = Functions.buffer(linestring1, 10, true); Geometry result5 = Functions.buffer(polygon3, 10000, true); + Geometry result6 = Functions.buffer(linestring2, 10000, true); + + // Geometries crossing dateline + Geometry result7 = Functions.buffer(linestring3, 1000000, true); + Geometry result8 = Functions.buffer(polygon4, 100, true); + Geometry result9 = Functions.buffer(polygon5, 1000000, true); Geometry expected1 = geomFromEWKT("POLYGON ((16.248653057837092 48.24999999781262, 16.24867891451385 48.25017542568975, 16.24875549793627 48.25034411793438, 16.24887986793391 48.25049959710696, 16.249047249310912 48.25063589306856, 16.249251215157546 48.250747772240985, 16.249483933612066 48.25083093858835, 16.249736468600165 48.250882198599385, 16.249999123001867 48.25089958393145, 16.35000087730807 48.25089956809054, 16.35026352923699 48.250882182692266, 16.350516061632444 48.250830922668115, 16.35074877731957 48.25074775640202, 16.350952740192536 48.250635877460866, 16.351120118386593 48.25049958194538, 16.351244485020548 48.25034410350243, 16.35132106495956 48.25017541233756, 16.351346918125614 48.24999998594946, 16.351345606585333 48.199999998284284, 16.351319726848168 48.19982442741669, 16.351243088946557 48.19965560959, 16.351118640921797 48.19950003769327, 16.350951169518318 48.19936369511042, 16.350747116034892 48.19925182561274, 16.350514328568078 48.19916873170147, 16.350261760179283 48.199117609152964, 16.34999912459177 48.19910042412578, 16.25000087573864 48.19910040825405, 16.24973823767017 48.199117593214716, 16.249485666681007 48.199168715750886, 16.249252876439694 48.19925180974529, 16.24904881997628 48.199363679477166, 16.248881345384483 48.199500022510115, 16.248756893991796 48.199655595141486, 16.24868025260387 48.199824414053964, 16.2486543693546 48.19999998641755, 16.248653057837092 48.24999999781262))"); Geometry expected2 = geomFromEWKT("POLYGON ((-120.00898315284118 29.999999999999467, -120.00898315284118 49.999999999988724, -120.00881054407824 50.001129625613444, -120.0082993510474 50.002215815187945, -120.00746921861013 50.003216831381316, -120.00635204829044 50.00409421217581, -120.0049907723172 50.004814247955025, -120.00343770376273 50.00534927578318, -120.00175252618048 50.00567874131385, -119.99999999999999 50.005789987696524, -80 50.005789987696524, -79.9982474738195 50.00567874131385, -79.99656229623727 50.00534927578318, -79.99500922768277 50.004814247955025, -79.99364795170956 50.00409421217581, -79.99253078138989 50.003216831381316, -79.99170064895262 50.002215815187945, -79.99118945592176 50.001129625613444, -79.99101684715882 49.999999999988724, -79.99101684715882 29.999999999999467, -79.99118945592176 29.998474584401656, -79.99170064895262 29.997007767335376, -79.99253078138989 29.995655921596196, -79.99364795170956 29.994471003601635, -79.99500922768277 29.99349855586065, -79.99656229623727 29.99277595576926, -79.9982474738195 29.99233097818683, -80 29.992180727189663, -119.99999999999999 29.992180727189663, -120.00175252618048 29.99233097818683, -120.00343770376273 29.99277595576926, -120.0049907723172 29.99349855586065, -120.00635204829044 29.994471003601635, -120.00746921861013 29.995655921596196, -120.0082993510474 29.997007767335376, -120.00881054407824 29.998474584401656, -120.00898315284118 29.999999999999467))"); Geometry expected3 = geomFromEWKT("POLYGON ((-179.9982096288439 59.99995928994206, -179.9982598922349 59.99978513613418, -179.99837702566958 59.99961923978765, -179.99855652694714 59.99946797603053, -179.998791497433 59.99933715760413, -179.99907290724568 59.999231811518854, -179.9993899422849 59.99915598591007, -179.99973041976276 59.99911259450936, 179.9999187437241 59.999103304702594, 179.99957102955173 59.99912847347155, 179.99923979915093 59.999187133678575, 179.99893778069153 59.999277031220146, 179.99867658007233 59.99939471162435, 179.99846623498902 59.99953565276752, 179.9983148292063 59.99969443861621, 179.99822818185396 59.999864967323326, 179.9982096236965 60.00004068568767, 179.99825986898801 60.00021484097138, 179.99837698785996 60.00038074040053, 179.99855648032874 60.00053200837772, 179.99879144910062 60.00066283151926, 179.99907286455522 60.00076818209686, 179.9993899117333 60.00084401129125, 179.9997304059989 60.000887404824965, -179.99991873860708 60.00089669498742, -179.99957100633523 60.000871524742664, -179.9992397613717 60.000812861453035, -179.9989377341035 60.000722959691345, -179.99867653177037 60.00060527457202, -179.99846619232903 60.00046432893652, -179.99831479868513 60.000305539502236, -179.99822816812048 60.00013500866213, -179.9982096288439 59.99995928994206))"); Geometry expected4 = geomFromEWKT("POLYGON ((-91.18706814560424 30.451931798138848, -91.18906814731041 30.453431798551634, -91.18908219577726 30.453444627150773, -91.1890930855116 30.4534595836974, -91.18910039802604 30.453476093420573, -91.18910385230312 30.453493521861454, -91.18910331559482 30.453511199255043, -91.1890988085245 30.4535284462689, -91.18909050429437 30.453544600109474, -91.18907872203003 30.453559039992996, -91.18906391451668 30.453571211001968, -91.18904665079899 30.453580645410415, -91.18902759431288 30.453586980658557, -91.18900747739012 30.453589973285787, -91.18898707311482 30.453589508286903, -91.18896716561396 30.453585603531685, -91.18894851992349 30.453578409078172, -91.18893185258821 30.453568201405897, -91.18693185327697 30.45206820105564, -91.18493185405761 30.450568200774196, -91.18491780618592 30.450555372061554, -91.18490691698157 30.450540415431583, -91.18489960490953 30.450523905659963, -91.18489615096698 30.450506477208492, -91.18489668788492 30.450488799843015, -91.18490119502782 30.450471552894637, -91.18490949918679 30.450455399153434, -91.18492128123634 30.450440959397913, -91.18493608839827 30.450428788538883, -91.18495335164187 30.45041935429472, -91.1849724075514 30.45041301921735, -91.18499252382057 30.450410026759716, -91.18501292739447 30.450410491920046, -91.18503283417732 30.45041439682263, -91.18505147916453 30.450421591404723, -91.18506814584102 30.450431799183303, -91.18706814560424 30.451931798138848))"); Geometry expected5 = geomFromEWKT("POLYGON ((-120.08983152841193 29.999999999999467, -120.08983152841193 49.999999999988724, -120.08810544078257 50.011295055219406, -120.082993510474 50.02215353087995, -120.07469218610126 50.03215857448533, -120.06352048290445 50.040926345154375, -120.04990772317231 50.048120665845154, -120.03437703762728 50.05346582623596, -120.01752526180509 50.05675706193107, -119.99999999999999 50.057868324959486, -80 50.057868324959486, -79.98247473819491 50.05675706193107, -79.96562296237272 50.05346582623596, -79.95009227682765 50.048120665845154, -79.93647951709555 50.040926345154375, -79.92530781389875 50.03215857448533, -79.91700648952599 50.02215353087995, -79.91189455921744 50.011295055219406, -79.91016847158805 49.999999999988724, -79.91016847158805 29.999999999999467, -79.91189455921744 29.984744778413628, -79.91700648952599 29.970073573592575, -79.92530781389875 29.956550575939833, -79.93647951709555 29.944696041120597, -79.95009227682765 29.934966209460505, -79.96562296237272 29.927735669848445, -79.98247473819491 29.92328286155486, -80 29.92177928675603, -119.99999999999999 29.92177928675603, -120.01752526180509 29.92328286155486, -120.03437703762728 29.927735669848445, -120.04990772317231 29.934966209460505, -120.06352048290445 29.944696041120597, -120.07469218610126 29.956550575939833, -120.082993510474 29.970073573592575, -120.08810544078257 29.984744778413628, -120.08983152841193 29.999999999999467))"); + Geometry expected6 = geomFromEWKT("POLYGON ((-0.0499827380800979 14.90970764615526, -0.0680979441919658 14.911439289332314, -0.0855181036092048 14.916572765433372, -0.1015745790932317 14.924910889089798, -0.1156509498611482 14.936133480337729, -0.1272066059871122 14.949809631917493, -0.1357974717923214 14.965414213210906, -0.1410930699533803 14.982347984759812, -0.1428892711961725 14.9999605602647, -0.1411162325232574 15.017575343788987, -0.1358412050267848 15.034515492689351, -0.1272660846886664 15.050129914661689, -0.1157197795798512 15.063818302233363, -0.101645667253286 15.07505424080832, -0.0855846090787039 15.083405496399754, -0.068154165764569 15.088550694439519, -0.0500248124892859 15.090291738028835, 0.0500174017959422 15.0902994910598, 0.0681488768495457 15.08856105434859, 0.0855824246963393 15.08341775106032, 0.1016472676386697 15.075067339333813, 0.1157254254859488 15.06383098366898, 0.1272755583302725 15.050140871793444, 0.1358538186573691 15.034523547105394, 0.1411309042551145 15.017579606456339, 0.1429046575925539 14.999960553897546, 0.1411077364057122 14.982343709853023, 0.1358100760363371 14.965406147948107, 0.1272160681615492 14.949798665976873, 0.1156565845591405 14.936120792425546, 0.1015761705812689 14.9248977863495, 0.0855159139181673 14.916560508451733, 0.0680926538252635 14.911428928455091, 0.0499753291173759 14.909699892939221, -0.0499827380800979 14.90970764615526))"); + Geometry expected7 = geomFromEWKT("POLYGON ((-179.908607846266 24.025403116254306, -177.99876698499668 23.825660822056278, -176.1851748144233 23.267372220454533, -174.54862792786022 22.380090414241554, -173.15522797402008 21.20765453023379, -172.05367901642606 19.803908497939666, -171.27542809803106 18.22850022788734, -170.83670228698406 16.543486102335997, -170.7412636921777 14.811036901560918, -170.9829095910743 13.092142713348924, -171.5471495735382 11.445952123266512, -172.41190582467604 9.929276420592164, -173.54741832839647 8.595821727211286, -174.91577148479777 7.494852554680671, -176.47058207976102 6.669211334615649, -178.157376393358 6.152879352694568, -179.9150124655878 5.968498588196282, 179.9878642700213 5.967715967762297, 178.22454996382154 6.1243064565567416, 176.52190735292885 6.617190677478861, 174.94354089208682 7.427303247307942, 173.5475157235323 8.522934832546376, 172.38432738193248 9.861905503007021, 171.4959038990676 11.393986435009106, 170.9153886431351 13.063103234007537, 170.66721922346738 14.80905645479948, 170.76695775874103 16.568760623485606, 171.22043647400534 18.277247812024235, 172.02203612722943 19.86884329704546, 173.1522881340142 21.278955415388584, 174.5754355707626 22.446796923405344, 176.2379815998183 23.319060447498003, 178.06938085914476 23.854152382752126, 179.98566424517946 24.026184454399257, -179.908607846266 24.025403116254306))"); + Geometry expected8 = geomFromEWKT("POLYGON ((179 -15.000873160293315, 178.999824747382 -15.000856382797105, 178.99965622962375 -15.000806695050459, 178.9995009227683 -15.000726006503347, 178.999364795171 -15.000617417938033, 178.99925307813902 -15.000485102312915, 178.9991700648953 -15.000334144403906, 178.99911894559222 -15.00017034540551, 178.9991016847159 -14.999999999999092, 178.9991016847159 14.99999999999908, 178.99911894559222 15.00017034540551, 178.9991700648953 15.000334144403906, 178.99925307813902 15.000485102312902, 178.999364795171 15.000617417938045, 178.9995009227683 15.000726006503333, 178.99965622962375 15.000806695050459, 178.999824747382 15.000856382797105, 179 15.000873160293315, -179.00000000000003 15.000873160293315, -178.99982474738198 15.000856382797105, -178.99965622962375 15.000806695050459, -178.9995009227683 15.000726006503333, -178.99936479517098 15.000617417938045, -178.99925307813902 15.000485102312902, -178.99917006489528 15.000334144403906, -178.9991189455922 15.00017034540551, -178.99910168471592 14.99999999999908, -178.99910168471592 -14.999999999999092, -178.9991189455922 -15.00017034540551, -178.99917006489528 -15.000334144403906, -178.99925307813902 -15.000485102312915, -178.99936479517098 -15.000617417938033, -178.9995009227683 -15.000726006503347, -178.99965622962375 -15.000806695050459, -178.99982474738198 -15.000856382797105, -179.00000000000003 -15.000873160293315, 179 -15.000873160293315))"); + Geometry expected9 = geomFromEWKT("POLYGON ((178 -18.747248844374, 176.24747381949112 -18.582729101204464, 174.5622962372712 -18.0945531116537, 173.0092276827665 -17.29887379200776, 171.64795170955566 -16.222572896097414, 170.53078138987692 -14.903037762363958, 169.70064895259912 -13.387566659631126, 169.18945592174327 -11.73221950803158, 169.01684715880478 -9.999999999999265, 169.01684715880478 9.999999999999238, 169.18945592174327 11.73221950803157, 169.70064895259912 13.387566659631101, 170.53078138987692 14.903037762363944, 171.64795170955566 16.2225728960974, 173.0092276827665 17.298873792007747, 174.5622962372712 18.094553111653685, 176.24747381949112 18.582729101204453, 178 18.747248844373974, -178 18.747248844373974, -176.24747381949115 18.582729101204453, -174.56229623727123 18.094553111653685, -173.00922768276646 17.298873792007747, -171.64795170955566 16.2225728960974, -170.5307813898769 14.903037762363944, -169.70064895259912 13.387566659631101, -169.18945592174325 11.73221950803157, -169.0168471588048 9.999999999999238, -169.0168471588048 -9.999999999999265, -169.18945592174325 -11.73221950803158, -169.70064895259912 -13.387566659631126, -170.5307813898769 -14.903037762363972, -171.64795170955566 -16.222572896097414, -173.00922768276646 -17.29887379200776, -174.56229623727123 -18.0945531116537, -176.24747381949115 -18.582729101204464, -178 -18.747248844374, 178 -18.747248844374))"); Geometry postgis_result1 = geomFromEWKT("POLYGON((16.24865305783681 48.249999997812985,16.248678914513565 48.250175425690124,16.248755497935985 48.250344117934766,16.248879867933624 48.25049959710733,16.249047249310628 48.25063589306893,16.249251215157262 48.25074777224136,16.24948393361178 48.25083093858872,16.24973646859988 48.25088219859975,16.249999123001583 48.25089958393185,16.35000087730765 48.25089956809112,16.35026352923657 48.25088218269285,16.350516061632025 48.2508309226687,16.35074877731915 48.2507477564026,16.35095274019212 48.25063587746145,16.351120118386177 48.250499581945974,16.35124448502013 48.250344103503025,16.351321064959144 48.25017541233815,16.3513469181252 48.24999998595004,16.351345606584914 48.19999999828486,16.35131972684775 48.19982442741726,16.351243088946138 48.19965560959058,16.351118640921378 48.19950003769385,16.3509511695179 48.199363695110996,16.350747116034473 48.19925182561332,16.350514328567655 48.19916873170203,16.35026176017886 48.19911760915353,16.34999912459135 48.19910042412636,16.250000875738355 48.19910040825441,16.249738237669884 48.19911759321507,16.249485666680723 48.19916871575125,16.24925287643941 48.199251809745654,16.249048819975993 48.19936367947753,16.2488813453842 48.199500022510485,16.248756893991516 48.19965559514185,16.248680252603585 48.199824414054326,16.248654369354316 48.199999986417914,16.24865305783681 48.249999997812985))"); Geometry postgis_result2 = geomFromEWKT("POLYGON((-120.00898315284118 29.999999999999993,-120.00898315284118 49.99999999999999,-120.00881054407824 50.00112962562472,-120.0082993510474 50.00221581519921,-120.00746921861011 50.003216831392564,-120.00635204829044 50.004094212187034,-120.00499077231723 50.00481424796622,-120.00343770376273 50.00534927579437,-120.00175252618051 50.00567874132504,-119.99999999999999 50.00578998770771,-80 50.00578998770771,-79.99824747381949 50.00567874132504,-79.99656229623727 50.00534927579437,-79.99500922768276 50.00481424796622,-79.99364795170956 50.004094212187034,-79.99253078138987 50.003216831392564,-79.9917006489526 50.00221581519921,-79.99118945592174 50.00112962562472,-79.9910168471588 49.99999999999999,-79.9910168471588 29.999999999999993,-79.99118945592174 29.99847458440221,-79.9917006489526 29.997007767335962,-79.99253078138987 29.99565592159683,-79.99364795170956 29.99447100360229,-79.99500922768276 29.993498555861333,-79.99656229623727 29.992775955769954,-79.99824747381949 29.992330978187542,-80 29.992180727190387,-119.99999999999999 29.992180727190387,-120.00175252618051 29.992330978187542,-120.00343770376273 29.992775955769954,-120.00499077231723 29.993498555861333,-120.00635204829044 29.99447100360229,-120.00746921861011 29.99565592159683,-120.0082993510474 29.997007767335962,-120.00881054407824 29.99847458440221,-120.00898315284118 29.999999999999993))"); @@ -1225,6 +1241,48 @@ public void testBufferSpheroidal() throws ParseException { assertEquals(4269, Functions.getSRID(result5)); assertEquals(Spheroid.area(postgis_result5), Spheroid.area(result5), 10); assertEquals(7.424558176442617E-8, comparePolygons(postgis_result5, result5), FP_TOLERANCE2); + + assertEquals(Functions.reducePrecision(expected6, 8), Functions.reducePrecision(result6, 8)); + assertEquals(4326, Functions.getSRID(result6)); + + assertEquals(Functions.reducePrecision(expected7, 8), Functions.reducePrecision(result7, 8)); + assertEquals(4326, Functions.getSRID(result6)); + + assertEquals(Functions.reducePrecision(expected8, 8), Functions.reducePrecision(result8, 8)); + assertEquals(4326, Functions.getSRID(result6)); + + assertEquals(Functions.reducePrecision(expected9, 8), Functions.reducePrecision(result9, 8)); + assertEquals(4326, Functions.getSRID(result6)); + } + + @Test + public void testShiftLongitude() throws ParseException { + Geometry point = geomFromWKT("POINT(-175 10)", 4230); + Geometry linestring1 = geomFromEWKT("LINESTRING(179 10, -179 10)"); + Geometry linestring2 = geomFromEWKT("LINESTRING(179 10, 181 10)"); + Geometry polygon = geomFromEWKT("POLYGON((179 10, -179 10, -179 20, 179 20, 179 10))"); + Geometry multiPoint = geomFromEWKT("MULTIPOINT((179 10), (-179 10))"); + Geometry multiLineString = geomFromEWKT("MULTILINESTRING((179 10, -179 10), (179 20, 181 20))"); + Geometry multiPolygon = geomFromEWKT("MULTIPOLYGON(((179 10, -179 10, -179 20, 179 20, 179 10)), ((-185 10, -185 20, -175 20, -175 10, -185 10)))"); + Geometry geomCollection = geomFromEWKT("GEOMETRYCOLLECTION(POINT(190 10), LINESTRING(179 10, -179 10))"); + + Geometry expected1 = geomFromWKT("POINT (185 10)", 4230); + Geometry expected2 = geomFromEWKT("LINESTRING (179 10, 181 10)"); + Geometry expected3 = geomFromEWKT("LINESTRING (179 10, -179 10)"); + Geometry expected4 = geomFromEWKT("POLYGON ((179 10, 181 10, 181 20, 179 20, 179 10))"); + Geometry expected5 = geomFromEWKT("MULTIPOINT ((179 10), (181 10))"); + Geometry expected6 = geomFromEWKT("MULTILINESTRING ((179 10, 181 10), (179 20, -179 20))"); + Geometry expected7 = geomFromEWKT("MULTIPOLYGON (((179 10, 181 10, 181 20, 179 20, 179 10)), ((175 10, 175 20, 185 20, 185 10, 175 10)))"); + Geometry expected8 = geomFromEWKT("GEOMETRYCOLLECTION (POINT (-170 10), LINESTRING (179 10, 181 10))"); + + assertEquals(expected1, Functions.shiftLongitude(point)); + assertEquals(expected2, Functions.shiftLongitude(linestring1)); + assertEquals(expected3, Functions.shiftLongitude(linestring2)); + assertEquals(expected4, Functions.shiftLongitude(polygon)); + assertEquals(expected5, Functions.shiftLongitude(multiPoint)); + assertEquals(expected6, Functions.shiftLongitude(multiLineString)); + assertEquals(expected7, Functions.shiftLongitude(multiPolygon)); + assertEquals(expected8, Functions.shiftLongitude(geomCollection)); } private static double comparePolygons(Geometry p1, Geometry p2) { @@ -1250,10 +1308,6 @@ public void testBestSRID() throws ParseException { {-180, 60, 32660}, {180, 60, 32660}, {-180, -60, 32760}, {180, -60, 32760}, }; - Geometry geom1 = geomFromWKT("POLYGON((-120 30, -80 30, -80 50, -120 50, -120 30))", 4230); //geomFromWKT("POINT (10000 -500)", 3857); - int actualEPSG1 = Functions.bestSRID(geom1); - System.out.println("actualEPSG: "+actualEPSG1); - // Number of UTM zones int numZones = (177 - (-177)) / 6 + 1; int numZonesSouth = (177 - (-177)) / 6 + 1; @@ -1279,40 +1333,40 @@ public void testBestSRID() throws ParseException { indexSouth++; } -// for (int[] testCase : testCases_special) { -// Geometry geom = GEOMETRY_FACTORY.createPoint(new Coordinate(testCase[0], testCase[1])); -// int actualEPSG = Functions.bestSRID(geom); -// System.out.println("actualEPSG: "+actualEPSG); -// int expectedEPSG = testCase[2]; -// assertEquals("Failed at coordinates (" + testCase[0] + ", " + testCase[1] + ")", expectedEPSG, actualEPSG); -// } - -// for (int[] testCase : testCases_UTMNorth) { -// Geometry geom = GEOMETRY_FACTORY.createPoint(new Coordinate(testCase[0], testCase[1])); -// int actualEPSG = Functions.bestSRID(geom); -// int expectedEPSG = testCase[2]; -// assertEquals("Failed at coordinates (" + testCase[0] + ", " + testCase[1] + ")", expectedEPSG, actualEPSG); -// } -// -// for (int[] testCase : testCases_UTMSouth) { -// Geometry geom = GEOMETRY_FACTORY.createPoint(new Coordinate(testCase[0], testCase[1])); -// int actualEPSG = Functions.bestSRID(geom); -// int expectedEPSG = testCase[2]; -// assertEquals("Failed at coordinates (" + testCase[0] + ", " + testCase[1] + ")", expectedEPSG, actualEPSG); -// } -// -// // Large geometry that does not fit into UTM or polar categories -// Geometry geom = GEOMETRY_FACTORY.createPolygon(new Coordinate[] { -// new Coordinate(-160, -40), -// new Coordinate(-160, 40), -// new Coordinate(160, 40), -// new Coordinate(160, -40), -// new Coordinate(-160, -40) -// }); -// int expectedEPSG = 3395; // EPSG code for World Mercator -// int actualEPSG = Functions.bestSRID(geom); -// -// assertEquals("Expected World Mercator projection for wide range geometry", expectedEPSG, actualEPSG); + for (int[] testCase : testCases_special) { + Geometry geom = GEOMETRY_FACTORY.createPoint(new Coordinate(testCase[0], testCase[1])); + int actualEPSG = Functions.bestSRID(geom); + System.out.println("actualEPSG: "+actualEPSG); + int expectedEPSG = testCase[2]; + assertEquals("Failed at coordinates (" + testCase[0] + ", " + testCase[1] + ")", expectedEPSG, actualEPSG); + } + + for (int[] testCase : testCases_UTMNorth) { + Geometry geom = GEOMETRY_FACTORY.createPoint(new Coordinate(testCase[0], testCase[1])); + int actualEPSG = Functions.bestSRID(geom); + int expectedEPSG = testCase[2]; + assertEquals("Failed at coordinates (" + testCase[0] + ", " + testCase[1] + ")", expectedEPSG, actualEPSG); + } + + for (int[] testCase : testCases_UTMSouth) { + Geometry geom = GEOMETRY_FACTORY.createPoint(new Coordinate(testCase[0], testCase[1])); + int actualEPSG = Functions.bestSRID(geom); + int expectedEPSG = testCase[2]; + assertEquals("Failed at coordinates (" + testCase[0] + ", " + testCase[1] + ")", expectedEPSG, actualEPSG); + } + + // Large geometry that does not fit into UTM or polar categories + Geometry geom = GEOMETRY_FACTORY.createPolygon(new Coordinate[] { + new Coordinate(-160, -40), + new Coordinate(-160, 40), + new Coordinate(160, 40), + new Coordinate(160, -40), + new Coordinate(-160, -40) + }); + int expectedEPSG = 3395; // EPSG code for World Mercator + int actualEPSG = Functions.bestSRID(geom); + + assertEquals("Expected World Mercator projection for wide range geometry", expectedEPSG, actualEPSG); } @Test diff --git a/docs/api/flink/Function.md b/docs/api/flink/Function.md index 2586fa79e5..fb31c89642 100644 --- a/docs/api/flink/Function.md +++ b/docs/api/flink/Function.md @@ -570,7 +570,7 @@ The optional third parameter, `useSpheroid`, controls the mode of buffer calcula !!!note As of now, spheroidal buffering only supports lon/lat coordinate systems and will throw an `IllegalArgumentException` for input geometries in meter based coordinate systems. !!!note - Spheroidal buffering may not produce accurate output buffer for input geometries larger than a UTM zone. + Spheroidal buffering may not produce accurate output buffer for input geometries larger than a UTM zone. Buffer Style Parameters: diff --git a/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java b/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java index 92dd949a04..646221fd99 100644 --- a/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java +++ b/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java @@ -107,7 +107,7 @@ public void testBuffer() { @Test public void testBestSRID() { - Table table1 = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('POINT (1000 -500)', 3857) AS geom"); + Table table1 = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('POINT (160 40)') AS geom"); table1 = table1.select(call(Functions.ST_BestSRID.class.getSimpleName(), $("geom"))); int result = (int) first(table1).getField(0); assertEquals(32657, result); From 1c075509c74820b4e3bb5905e32cb096d4032d24 Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Mon, 26 Feb 2024 20:20:56 -0500 Subject: [PATCH 29/54] add geometryChanged() to normalizeLongitude() --- common/src/main/java/org/apache/sedona/common/Functions.java | 1 + 1 file changed, 1 insertion(+) 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 da63c4fe5a..ef64fc1dd4 100644 --- a/common/src/main/java/org/apache/sedona/common/Functions.java +++ b/common/src/main/java/org/apache/sedona/common/Functions.java @@ -254,6 +254,7 @@ public static void normalizeLongitude(Geometry geometry) { while (coord.x > 180) coord.x -= 360; while (coord.x < -180) coord.x += 360; } + geometry.geometryChanged(); } public static Geometry shiftLongitude(Geometry geometry) { From 9dbe575d9601e4de88de682d283e7bd4514b38e8 Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Tue, 27 Feb 2024 21:59:40 -0500 Subject: [PATCH 30/54] Use CoordinateSequenceFilter for ST_ShiftLongitude --- .../org/apache/sedona/common/Functions.java | 223 ++++++++++-------- .../sedona/common/FunctionsGeoTools.java | 5 +- .../apache/sedona/common/FunctionsTest.java | 34 ++- 3 files changed, 156 insertions(+), 106 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 ef64fc1dd4..42472580b6 100644 --- a/common/src/main/java/org/apache/sedona/common/Functions.java +++ b/common/src/main/java/org/apache/sedona/common/Functions.java @@ -204,7 +204,10 @@ else if (singleParam[0].equalsIgnoreCase(listBufferParameters[5])) { public static int bestSRID(Geometry geometry) throws IllegalArgumentException{ // Shift longitudes of geometry crosses dateline - geometry = (Predicates.crossesDateLine(geometry)) ? shiftLongitude(geometry) : geometry; + if (Predicates.crossesDateLine(geometry)) { + shiftLongitude(geometry); + } + // geometry = (Predicates.crossesDateLine(geometry)) ? shiftLongitude(geometry) : geometry; // Check envelope Envelope envelope = geometry.getEnvelopeInternal(); @@ -257,105 +260,131 @@ public static void normalizeLongitude(Geometry geometry) { geometry.geometryChanged(); } - public static Geometry shiftLongitude(Geometry geometry) { - if (geometry instanceof Point) { - return shiftCoordinates((Point) geometry); - } else if (geometry instanceof LineString) { - return shiftCoordinates((LineString) geometry); - } else if (geometry instanceof Polygon) { - return shiftCoordinates((Polygon) geometry); - } else if (geometry instanceof MultiPoint) { - return shiftMultiGeometry((MultiPoint) geometry); - } else if (geometry instanceof MultiLineString) { - return shiftMultiGeometry((MultiLineString) geometry); - } else if (geometry instanceof MultiPolygon) { - return shiftMultiGeometry((MultiPolygon) geometry); - } else if (geometry instanceof GeometryCollection) { - return shiftMultiGeometry((GeometryCollection) geometry); - } - return geometry; - } - - private static Geometry shiftCoordinates(Point point) { - Coordinate newCoord = shiftCoordinate(point.getCoordinate()); - return point.getFactory().createPoint(newCoord); - } - - private static Geometry shiftCoordinates(LineString lineString) { - Coordinate[] newCoords = new Coordinate[lineString.getNumPoints()]; - for (int i = 0; i < lineString.getNumPoints(); i++) { - newCoords[i] = shiftCoordinate(lineString.getCoordinateN(i)); - } - return lineString.getFactory().createLineString(newCoords); - } - - private static Geometry shiftCoordinates(Polygon polygon) { - GeometryFactory factory = polygon.getFactory(); - - // Shift exterior ring and convert to LinearRing - LinearRing exteriorRing = shiftRingToLinearRing(polygon.getExteriorRing()); - - // Process interior rings - int numInteriorRings = polygon.getNumInteriorRing(); - LinearRing[] interiorRings = new LinearRing[numInteriorRings]; - for (int i = 0; i < numInteriorRings; i++) { - interiorRings[i] = shiftRingToLinearRing(polygon.getInteriorRingN(i)); - } - - return factory.createPolygon(exteriorRing, interiorRings); - } - - private static LinearRing shiftRingToLinearRing(LineString ring) { - GeometryFactory factory = ring.getFactory(); - Coordinate[] shiftedCoords = new Coordinate[ring.getNumPoints()]; - for (int i = 0; i < ring.getNumPoints(); i++) { - shiftedCoords[i] = shiftCoordinate(ring.getCoordinateN(i)); - } - return factory.createLinearRing(shiftedCoords); - } - - private static Geometry shiftMultiGeometry(GeometryCollection geometryCollection) { - int numGeometries = geometryCollection.getNumGeometries(); - GeometryFactory factory = geometryCollection.getFactory(); - - if (geometryCollection instanceof MultiPoint) { - Point[] points = new Point[numGeometries]; - for (int i = 0; i < numGeometries; i++) { - points[i] = (Point) shiftLongitude(geometryCollection.getGeometryN(i)); - } - return factory.createMultiPoint(points); - } else if (geometryCollection instanceof MultiLineString) { - LineString[] lineStrings = new LineString[numGeometries]; - for (int i = 0; i < numGeometries; i++) { - lineStrings[i] = (LineString) shiftLongitude(geometryCollection.getGeometryN(i)); - } - return factory.createMultiLineString(lineStrings); - } else if (geometryCollection instanceof MultiPolygon) { - Polygon[] polygons = new Polygon[numGeometries]; - for (int i = 0; i < numGeometries; i++) { - polygons[i] = (Polygon) shiftLongitude(geometryCollection.getGeometryN(i)); - } - return factory.createMultiPolygon(polygons); - } else { - // For other geometry collections, just create a new collection with shifted geometries - Geometry[] geometries = new Geometry[numGeometries]; - for (int i = 0; i < numGeometries; i++) { - geometries[i] = shiftLongitude(geometryCollection.getGeometryN(i)); + public static void shiftLongitude(Geometry geometry) { + geometry.apply(new CoordinateSequenceFilter() { + @Override + public void filter(CoordinateSequence seq, int i) { + double newX = seq.getX(i); + if (newX < 0) { + newX += 360; + } else if (newX > 180) { + newX -= 360; + } + seq.setOrdinate(i, CoordinateSequence.X, newX); } - return factory.createGeometryCollection(geometries); - } - } + @Override + public boolean isDone() { + return false; + } - private static Coordinate shiftCoordinate(Coordinate coord) { - double newX = coord.x; - if (newX < 0) { - newX += 360; - } else if (newX > 180) { - newX -= 360; - } - return new Coordinate(newX, coord.y); - } + @Override + public boolean isGeometryChanged() { + return true; + } + }); + } + + +// public static Geometry shiftLongitude(Geometry geometry) { +// if (geometry instanceof Point) { +// return shiftCoordinates((Point) geometry); +// } else if (geometry instanceof LineString) { +// return shiftCoordinates((LineString) geometry); +// } else if (geometry instanceof Polygon) { +// return shiftCoordinates((Polygon) geometry); +// } else if (geometry instanceof MultiPoint) { +// return shiftMultiGeometry((MultiPoint) geometry); +// } else if (geometry instanceof MultiLineString) { +// return shiftMultiGeometry((MultiLineString) geometry); +// } else if (geometry instanceof MultiPolygon) { +// return shiftMultiGeometry((MultiPolygon) geometry); +// } else if (geometry instanceof GeometryCollection) { +// return shiftMultiGeometry((GeometryCollection) geometry); +// } +// return geometry; +// } +// +// private static Geometry shiftCoordinates(Point point) { +// Coordinate newCoord = shiftCoordinate(point.getCoordinate()); +// return point.getFactory().createPoint(newCoord); +// } +// +// private static Geometry shiftCoordinates(LineString lineString) { +// Coordinate[] newCoords = new Coordinate[lineString.getNumPoints()]; +// for (int i = 0; i < lineString.getNumPoints(); i++) { +// newCoords[i] = shiftCoordinate(lineString.getCoordinateN(i)); +// } +// return lineString.getFactory().createLineString(newCoords); +// } +// +// private static Geometry shiftCoordinates(Polygon polygon) { +// GeometryFactory factory = polygon.getFactory(); +// +// // Shift exterior ring and convert to LinearRing +// LinearRing exteriorRing = shiftRingToLinearRing(polygon.getExteriorRing()); +// +// // Process interior rings +// int numInteriorRings = polygon.getNumInteriorRing(); +// LinearRing[] interiorRings = new LinearRing[numInteriorRings]; +// for (int i = 0; i < numInteriorRings; i++) { +// interiorRings[i] = shiftRingToLinearRing(polygon.getInteriorRingN(i)); +// } +// +// return factory.createPolygon(exteriorRing, interiorRings); +// } +// +// private static LinearRing shiftRingToLinearRing(LineString ring) { +// GeometryFactory factory = ring.getFactory(); +// Coordinate[] shiftedCoords = new Coordinate[ring.getNumPoints()]; +// for (int i = 0; i < ring.getNumPoints(); i++) { +// shiftedCoords[i] = shiftCoordinate(ring.getCoordinateN(i)); +// } +// return factory.createLinearRing(shiftedCoords); +// } +// +// private static Geometry shiftMultiGeometry(GeometryCollection geometryCollection) { +// int numGeometries = geometryCollection.getNumGeometries(); +// GeometryFactory factory = geometryCollection.getFactory(); +// +// if (geometryCollection instanceof MultiPoint) { +// Point[] points = new Point[numGeometries]; +// for (int i = 0; i < numGeometries; i++) { +// points[i] = (Point) shiftLongitude(geometryCollection.getGeometryN(i)); +// } +// return factory.createMultiPoint(points); +// } else if (geometryCollection instanceof MultiLineString) { +// LineString[] lineStrings = new LineString[numGeometries]; +// for (int i = 0; i < numGeometries; i++) { +// lineStrings[i] = (LineString) shiftLongitude(geometryCollection.getGeometryN(i)); +// } +// return factory.createMultiLineString(lineStrings); +// } else if (geometryCollection instanceof MultiPolygon) { +// Polygon[] polygons = new Polygon[numGeometries]; +// for (int i = 0; i < numGeometries; i++) { +// polygons[i] = (Polygon) shiftLongitude(geometryCollection.getGeometryN(i)); +// } +// return factory.createMultiPolygon(polygons); +// } else { +// // For other geometry collections, just create a new collection with shifted geometries +// Geometry[] geometries = new Geometry[numGeometries]; +// for (int i = 0; i < numGeometries; i++) { +// geometries[i] = shiftLongitude(geometryCollection.getGeometryN(i)); +// } +// return factory.createGeometryCollection(geometries); +// } +// } +// +// +// private static Coordinate shiftCoordinate(Coordinate coord) { +// double newX = coord.x; +// if (newX < 0) { +// newX += 360; +// } else if (newX > 180) { +// newX -= 360; +// } +// return new Coordinate(newX, coord.y); +// } public static Geometry envelope(Geometry geometry) { diff --git a/common/src/main/java/org/apache/sedona/common/FunctionsGeoTools.java b/common/src/main/java/org/apache/sedona/common/FunctionsGeoTools.java index 71fc06b3f1..4211be9977 100644 --- a/common/src/main/java/org/apache/sedona/common/FunctionsGeoTools.java +++ b/common/src/main/java/org/apache/sedona/common/FunctionsGeoTools.java @@ -144,7 +144,10 @@ public static Geometry bufferSpheroid(Geometry geometry, double radius, BufferPa final int WGS84CRS = 4326; // Shift longitude if geometry crosses dateline - geometry = (Predicates.crossesDateLine(geometry)) ? Functions.shiftLongitude(geometry) : geometry; + if (Predicates.crossesDateLine(geometry)) { + Functions.shiftLongitude(geometry); + } +// geometry = (Predicates.crossesDateLine(geometry)) ? Functions.shiftLongitude(geometry) : geometry; // If originalCRS is not set, use WGS84 as the originalCRS for transformation String sourceCRSCode = (originalCRS == 0) ? "EPSG:" + WGS84CRS : "EPSG:" + originalCRS; diff --git a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java index 47a0b84240..3e264156a4 100644 --- a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java +++ b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java @@ -1275,14 +1275,32 @@ public void testShiftLongitude() throws ParseException { Geometry expected7 = geomFromEWKT("MULTIPOLYGON (((179 10, 181 10, 181 20, 179 20, 179 10)), ((175 10, 175 20, 185 20, 185 10, 175 10)))"); Geometry expected8 = geomFromEWKT("GEOMETRYCOLLECTION (POINT (-170 10), LINESTRING (179 10, 181 10))"); - assertEquals(expected1, Functions.shiftLongitude(point)); - assertEquals(expected2, Functions.shiftLongitude(linestring1)); - assertEquals(expected3, Functions.shiftLongitude(linestring2)); - assertEquals(expected4, Functions.shiftLongitude(polygon)); - assertEquals(expected5, Functions.shiftLongitude(multiPoint)); - assertEquals(expected6, Functions.shiftLongitude(multiLineString)); - assertEquals(expected7, Functions.shiftLongitude(multiPolygon)); - assertEquals(expected8, Functions.shiftLongitude(geomCollection)); + Functions.shiftLongitude(point); + Functions.shiftLongitude(linestring1); + Functions.shiftLongitude(linestring2); + Functions.shiftLongitude(polygon); + Functions.shiftLongitude(multiPoint); + Functions.shiftLongitude(multiLineString); + Functions.shiftLongitude(multiPolygon); + Functions.shiftLongitude(geomCollection); + + assertEquals(expected1, point); + assertEquals(expected2, linestring1); + assertEquals(expected3, linestring2); + assertEquals(expected4, polygon); + assertEquals(expected5, multiPoint); + assertEquals(expected6, multiLineString); + assertEquals(expected7, multiPolygon); + assertEquals(expected8, geomCollection); + +// assertEquals(expected1, Functions.shiftLongitude(point)); +// assertEquals(expected2, Functions.shiftLongitude(linestring1)); +// assertEquals(expected3, Functions.shiftLongitude(linestring2)); +// assertEquals(expected4, Functions.shiftLongitude(polygon)); +// assertEquals(expected5, Functions.shiftLongitude(multiPoint)); +// assertEquals(expected6, Functions.shiftLongitude(multiLineString)); +// assertEquals(expected7, Functions.shiftLongitude(multiPolygon)); +// assertEquals(expected8, Functions.shiftLongitude(geomCollection)); } private static double comparePolygons(Geometry p1, Geometry p2) { From fd3712f66a373bc0e984d91e6f4271d6ee0a1547 Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Tue, 27 Feb 2024 22:31:35 -0500 Subject: [PATCH 31/54] change return type to Geometry for shiftLongitude --- common/src/main/java/org/apache/sedona/common/Functions.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 42472580b6..8b7f59b53b 100644 --- a/common/src/main/java/org/apache/sedona/common/Functions.java +++ b/common/src/main/java/org/apache/sedona/common/Functions.java @@ -260,7 +260,7 @@ public static void normalizeLongitude(Geometry geometry) { geometry.geometryChanged(); } - public static void shiftLongitude(Geometry geometry) { + public static Geometry shiftLongitude(Geometry geometry) { geometry.apply(new CoordinateSequenceFilter() { @Override public void filter(CoordinateSequence seq, int i) { @@ -283,6 +283,7 @@ public boolean isGeometryChanged() { return true; } }); + return geometry; } From 0c882c0c05aa6083b8d74c72fd573729c2e59b9c Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Wed, 28 Feb 2024 13:55:38 -0500 Subject: [PATCH 32/54] Remove duplicate functions and tests --- .../org/apache/sedona/common/Functions.java | 128 ------------------ .../apache/sedona/common/FunctionsTest.java | 67 ++------- 2 files changed, 8 insertions(+), 187 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 4d326be972..d7148b475b 100644 --- a/common/src/main/java/org/apache/sedona/common/Functions.java +++ b/common/src/main/java/org/apache/sedona/common/Functions.java @@ -286,108 +286,6 @@ public boolean isGeometryChanged() { return geometry; } - -// public static Geometry shiftLongitude(Geometry geometry) { -// if (geometry instanceof Point) { -// return shiftCoordinates((Point) geometry); -// } else if (geometry instanceof LineString) { -// return shiftCoordinates((LineString) geometry); -// } else if (geometry instanceof Polygon) { -// return shiftCoordinates((Polygon) geometry); -// } else if (geometry instanceof MultiPoint) { -// return shiftMultiGeometry((MultiPoint) geometry); -// } else if (geometry instanceof MultiLineString) { -// return shiftMultiGeometry((MultiLineString) geometry); -// } else if (geometry instanceof MultiPolygon) { -// return shiftMultiGeometry((MultiPolygon) geometry); -// } else if (geometry instanceof GeometryCollection) { -// return shiftMultiGeometry((GeometryCollection) geometry); -// } -// return geometry; -// } -// -// private static Geometry shiftCoordinates(Point point) { -// Coordinate newCoord = shiftCoordinate(point.getCoordinate()); -// return point.getFactory().createPoint(newCoord); -// } -// -// private static Geometry shiftCoordinates(LineString lineString) { -// Coordinate[] newCoords = new Coordinate[lineString.getNumPoints()]; -// for (int i = 0; i < lineString.getNumPoints(); i++) { -// newCoords[i] = shiftCoordinate(lineString.getCoordinateN(i)); -// } -// return lineString.getFactory().createLineString(newCoords); -// } -// -// private static Geometry shiftCoordinates(Polygon polygon) { -// GeometryFactory factory = polygon.getFactory(); -// -// // Shift exterior ring and convert to LinearRing -// LinearRing exteriorRing = shiftRingToLinearRing(polygon.getExteriorRing()); -// -// // Process interior rings -// int numInteriorRings = polygon.getNumInteriorRing(); -// LinearRing[] interiorRings = new LinearRing[numInteriorRings]; -// for (int i = 0; i < numInteriorRings; i++) { -// interiorRings[i] = shiftRingToLinearRing(polygon.getInteriorRingN(i)); -// } -// -// return factory.createPolygon(exteriorRing, interiorRings); -// } -// -// private static LinearRing shiftRingToLinearRing(LineString ring) { -// GeometryFactory factory = ring.getFactory(); -// Coordinate[] shiftedCoords = new Coordinate[ring.getNumPoints()]; -// for (int i = 0; i < ring.getNumPoints(); i++) { -// shiftedCoords[i] = shiftCoordinate(ring.getCoordinateN(i)); -// } -// return factory.createLinearRing(shiftedCoords); -// } -// -// private static Geometry shiftMultiGeometry(GeometryCollection geometryCollection) { -// int numGeometries = geometryCollection.getNumGeometries(); -// GeometryFactory factory = geometryCollection.getFactory(); -// -// if (geometryCollection instanceof MultiPoint) { -// Point[] points = new Point[numGeometries]; -// for (int i = 0; i < numGeometries; i++) { -// points[i] = (Point) shiftLongitude(geometryCollection.getGeometryN(i)); -// } -// return factory.createMultiPoint(points); -// } else if (geometryCollection instanceof MultiLineString) { -// LineString[] lineStrings = new LineString[numGeometries]; -// for (int i = 0; i < numGeometries; i++) { -// lineStrings[i] = (LineString) shiftLongitude(geometryCollection.getGeometryN(i)); -// } -// return factory.createMultiLineString(lineStrings); -// } else if (geometryCollection instanceof MultiPolygon) { -// Polygon[] polygons = new Polygon[numGeometries]; -// for (int i = 0; i < numGeometries; i++) { -// polygons[i] = (Polygon) shiftLongitude(geometryCollection.getGeometryN(i)); -// } -// return factory.createMultiPolygon(polygons); -// } else { -// // For other geometry collections, just create a new collection with shifted geometries -// Geometry[] geometries = new Geometry[numGeometries]; -// for (int i = 0; i < numGeometries; i++) { -// geometries[i] = shiftLongitude(geometryCollection.getGeometryN(i)); -// } -// return factory.createGeometryCollection(geometries); -// } -// } -// -// -// private static Coordinate shiftCoordinate(Coordinate coord) { -// double newX = coord.x; -// if (newX < 0) { -// newX += 360; -// } else if (newX > 180) { -// newX -= 360; -// } -// return new Coordinate(newX, coord.y); -// } - - public static Geometry envelope(Geometry geometry) { return geometry.getEnvelope(); } @@ -409,32 +307,6 @@ public static Geometry normalize(Geometry geometry) { return geometry; } - public static Geometry shiftLongitude(Geometry geometry) { - geometry.apply(new CoordinateSequenceFilter() { - @Override - public void filter(CoordinateSequence seq, int i) { - double newX = seq.getX(i); - if (newX < 0) { - newX += 360; - } else if (newX > 180) { - newX -= 360; - } - seq.setOrdinate(i, CoordinateSequence.X, newX); - } - - @Override - public boolean isDone() { - return false; - } - - @Override - public boolean isGeometryChanged() { - return true; - } - }); - return geometry; - } - public static Double x(Geometry geometry) { if (geometry instanceof Point) { return geometry.getCoordinate().x; diff --git a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java index 775f53ac62..47a0b84240 100644 --- a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java +++ b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java @@ -1275,32 +1275,14 @@ public void testShiftLongitude() throws ParseException { Geometry expected7 = geomFromEWKT("MULTIPOLYGON (((179 10, 181 10, 181 20, 179 20, 179 10)), ((175 10, 175 20, 185 20, 185 10, 175 10)))"); Geometry expected8 = geomFromEWKT("GEOMETRYCOLLECTION (POINT (-170 10), LINESTRING (179 10, 181 10))"); - Functions.shiftLongitude(point); - Functions.shiftLongitude(linestring1); - Functions.shiftLongitude(linestring2); - Functions.shiftLongitude(polygon); - Functions.shiftLongitude(multiPoint); - Functions.shiftLongitude(multiLineString); - Functions.shiftLongitude(multiPolygon); - Functions.shiftLongitude(geomCollection); - - assertEquals(expected1, point); - assertEquals(expected2, linestring1); - assertEquals(expected3, linestring2); - assertEquals(expected4, polygon); - assertEquals(expected5, multiPoint); - assertEquals(expected6, multiLineString); - assertEquals(expected7, multiPolygon); - assertEquals(expected8, geomCollection); - -// assertEquals(expected1, Functions.shiftLongitude(point)); -// assertEquals(expected2, Functions.shiftLongitude(linestring1)); -// assertEquals(expected3, Functions.shiftLongitude(linestring2)); -// assertEquals(expected4, Functions.shiftLongitude(polygon)); -// assertEquals(expected5, Functions.shiftLongitude(multiPoint)); -// assertEquals(expected6, Functions.shiftLongitude(multiLineString)); -// assertEquals(expected7, Functions.shiftLongitude(multiPolygon)); -// assertEquals(expected8, Functions.shiftLongitude(geomCollection)); + assertEquals(expected1, Functions.shiftLongitude(point)); + assertEquals(expected2, Functions.shiftLongitude(linestring1)); + assertEquals(expected3, Functions.shiftLongitude(linestring2)); + assertEquals(expected4, Functions.shiftLongitude(polygon)); + assertEquals(expected5, Functions.shiftLongitude(multiPoint)); + assertEquals(expected6, Functions.shiftLongitude(multiLineString)); + assertEquals(expected7, Functions.shiftLongitude(multiPolygon)); + assertEquals(expected8, Functions.shiftLongitude(geomCollection)); } private static double comparePolygons(Geometry p1, Geometry p2) { @@ -1387,39 +1369,6 @@ public void testBestSRID() throws ParseException { assertEquals("Expected World Mercator projection for wide range geometry", expectedEPSG, actualEPSG); } - @Test - public void testShiftLongitude() throws ParseException { - Geometry point = geomFromWKT("POINT(-175 10)", 4230); - Geometry linestring1 = geomFromEWKT("LINESTRING(179 10, -179 10)"); - Geometry linestring2 = geomFromEWKT("LINESTRING(179 10, 181 10)"); - Geometry polygon = geomFromEWKT("POLYGON((179 10, -179 10, -179 20, 179 20, 179 10))"); - Geometry multiPoint = geomFromEWKT("MULTIPOINT((179 10), (-179 10))"); - Geometry multiLineString = geomFromEWKT("MULTILINESTRING((179 10, -179 10), (179 20, 181 20))"); - Geometry multiPolygon = geomFromEWKT("MULTIPOLYGON(((179 10, -179 10, -179 20, 179 20, 179 10)), ((-185 10, -185 20, -175 20, -175 10, -185 10)))"); - Geometry geomCollection = geomFromEWKT("GEOMETRYCOLLECTION(POINT(190 10), LINESTRING(179 10, -179 10))"); - Geometry polygonRing = geomFromEWKT("POLYGON((-170 -20, 170 -20, 170 20, -170 20, -170 -20), (-175 -10, 175 -10, 175 10, -175 10, -175 -10))"); - - Geometry expected1 = geomFromWKT("POINT (185 10)", 4230); - Geometry expected2 = geomFromEWKT("LINESTRING (179 10, 181 10)"); - Geometry expected3 = geomFromEWKT("LINESTRING (179 10, -179 10)"); - Geometry expected4 = geomFromEWKT("POLYGON ((179 10, 181 10, 181 20, 179 20, 179 10))"); - Geometry expected5 = geomFromEWKT("MULTIPOINT ((179 10), (181 10))"); - Geometry expected6 = geomFromEWKT("MULTILINESTRING ((179 10, 181 10), (179 20, -179 20))"); - Geometry expected7 = geomFromEWKT("MULTIPOLYGON (((179 10, 181 10, 181 20, 179 20, 179 10)), ((175 10, 175 20, 185 20, 185 10, 175 10)))"); - Geometry expected8 = geomFromEWKT("GEOMETRYCOLLECTION (POINT (-170 10), LINESTRING (179 10, 181 10))"); - Geometry expected9 = geomFromEWKT("POLYGON ((190 -20, 170 -20, 170 20, 190 20, 190 -20), (185 -10, 175 -10, 175 10, 185 10, 185 -10))"); - - assertEquals(expected1, Functions.shiftLongitude(point)); - assertEquals(expected2, Functions.shiftLongitude(linestring1)); - assertEquals(expected3, Functions.shiftLongitude(linestring2)); - assertEquals(expected4, Functions.shiftLongitude(polygon)); - assertEquals(expected5, Functions.shiftLongitude(multiPoint)); - assertEquals(expected6, Functions.shiftLongitude(multiLineString)); - assertEquals(expected7, Functions.shiftLongitude(multiPolygon)); - assertEquals(expected8, Functions.shiftLongitude(geomCollection)); - assertEquals(expected9, Functions.shiftLongitude(polygonRing)); - } - @Test public void nRingsUnsupported() { LineString lineString = GEOMETRY_FACTORY.createLineString(coordArray3d(0, 1, 1, 1, 2, 1, 1, 2, 2)); From 64ce098ae7ddc087f2137a0796b10fd52e80df5f Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Wed, 28 Feb 2024 18:11:32 -0500 Subject: [PATCH 33/54] Fix ST_Buffer definition in python --- python/sedona/sql/st_functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/sedona/sql/st_functions.py b/python/sedona/sql/st_functions.py index 1ee2d7239e..49b98b6781 100644 --- a/python/sedona/sql/st_functions.py +++ b/python/sedona/sql/st_functions.py @@ -340,7 +340,7 @@ def ST_Boundary(geometry: ColumnOrName) -> Column: @validate_argument_types -def ST_Buffer(geometry: ColumnOrName, buffer: ColumnOrNameOrNumber, parameters: Optional[Union[ColumnOrName, str]] = None, useSpheroid: Optional[Union[ColumnOrName, bool]] = None) -> Column: +def ST_Buffer(geometry: ColumnOrName, buffer: ColumnOrNameOrNumber, useSpheroid: Optional[Union[ColumnOrName, bool]] = None, parameters: Optional[Union[ColumnOrName, str]] = None) -> Column: """Calculate a geometry that represents all points whose distance from the input geometry column is equal to or less than a given amount. From 1a4e9f5bce4cbc04f3b3d1ed1cf62158316b76ed Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Wed, 28 Feb 2024 18:12:26 -0500 Subject: [PATCH 34/54] Add support in snowflake; add snowflake docs --- docs/api/flink/Function.md | 2 +- docs/api/snowflake/vector-data/Function.md | 72 +++++++++++++++++-- docs/api/sql/Function.md | 2 - .../snowflake/snowsql/TestFunctions.java | 4 ++ .../snowflake/snowsql/TestFunctionsV2.java | 4 ++ .../apache/sedona/snowflake/snowsql/UDFs.java | 23 ++++++ .../sedona/snowflake/snowsql/UDFsV2.java | 23 ++++++ 7 files changed, 121 insertions(+), 9 deletions(-) diff --git a/docs/api/flink/Function.md b/docs/api/flink/Function.md index a7870e6b6f..f27f5f4491 100644 --- a/docs/api/flink/Function.md +++ b/docs/api/flink/Function.md @@ -568,7 +568,7 @@ The optional third parameter, `useSpheroid`, controls the mode of buffer calcula - Finally, the buffered geometry is transformed back to its original SRID, or to WGS 84 if the original SRID was not set. !!!note - As of now, spheroidal buffering only supports lon/lat coordinate systems and will throw an `IllegalArgumentException` for input geometries in meter based coordinate systems. + Spheroidal buffering only supports lon/lat coordinate systems and will throw an `IllegalArgumentException` for input geometries in meter based coordinate systems. !!!note Spheroidal buffering may not produce accurate output buffer for input geometries larger than a UTM zone. diff --git a/docs/api/snowflake/vector-data/Function.md b/docs/api/snowflake/vector-data/Function.md index 36a67eea90..33804e377e 100644 --- a/docs/api/snowflake/vector-data/Function.md +++ b/docs/api/snowflake/vector-data/Function.md @@ -389,15 +389,75 @@ Output: `LINESTRING Z(-1 -1 0, 10 5 5)` ## ST_Buffer -Introduction: Returns a geometry/geography that represents all points whose distance from this Geometry/geography is less than or equal to distance. +Introduction: Returns a geometry/geography that represents all points whose distance from this Geometry/geography is less than or equal to distance. The function supports both Planar/Euclidean and Spheroidal/Geodesic buffering (Since v1.6.0). -Format: `ST_Buffer (A:geometry, buffer: Double)` +Mode of buffer calculation (Since: `v1.6.0`): + +The optional third parameter, `useSpheroid`, controls the mode of buffer calculation. + +- Planar Buffering (default): When `useSpheroid` is false, `ST_Buffer` performs standard planar buffering based on the provided parameters. +- Spheroidal Buffering: + - When `useSpheroid` is set to true, the function returns the spheroidal buffer polygon for more accurate representation over the Earth. + - ST_Buffer first determines the most appropriate Spatial Reference Identifier (SRID) for a given geometry, based on its spatial extent and location, using `ST_BestSRID`. + - The geometry is then transformed from its original SRID to the selected SRID. If the input geometry does not have a set SRID, `ST_Buffer` defaults to using WGS 84 (SRID 4326) as its original SRID. + - The standard planar buffer operation is then applied in this coordinate system. + - Finally, the buffered geometry is transformed back to its original SRID, or to WGS 84 if the original SRID was not set. + +!!!note + Spheroidal buffering only supports lon/lat coordinate systems and will throw an `IllegalArgumentException` for input geometries in meter based coordinate systems. +!!!note + Spheroidal buffering may not produce accurate output buffer for input geometries larger than a UTM zone. + +Buffer Style Parameters: + +The optional forth parameter controls the buffer accuracy and style. Buffer accuracy is specified by the number of line segments approximating a quarter circle, with a default of 8 segments. Buffer style can be set by providing blank-separated key=value pairs in a list format. + +- `quad_segs=#` : Number of line segments utilized to approximate a quarter circle (default is 8). +- `endcap=round|flat|square` : End cap style (default is `round`). `butt` is an accepted synonym for `flat`. +- `join=round|mitre|bevel` : Join style (default is `round`). `miter` is an accepted synonym for `mitre`. +- `mitre_limit=#.#` : mitre ratio limit and it only affects mitred join style. `miter_limit` is an accepted synonym for `mitre_limit`. +- `side=both|left|right` : The option `left` or `right` enables a single-sided buffer operation on the geometry, with the buffered side aligned according to the direction of the line. This functionality is specific to LINESTRING geometry and has no impact on POINT or POLYGON geometries. By default, square end caps are applied. + +!!!note + `ST_Buffer` throws an `IllegalArgumentException` if the correct format, parameters, or options are not provided. + +Format: -SQL example: -```SQL -SELECT ST_Buffer(polygondf.countyshape, 1) -FROM polygondf ``` +ST_Buffer (A: Geometry, buffer: Double, bufferStyleParameters: String [Optional]) +``` +``` +ST_Buffer (A: Geometry, buffer: Double, bufferStyleParameters: String [Optional], useSperoidal: Boolean) +``` + +Since: `v1.5.1` + +SQL Example: + +```sql +SELECT ST_Buffer(ST_GeomFromWKT('POINT(0 0)'), 10) +SELECT ST_Buffer(ST_GeomFromWKT('POINT(0 0)'), 10, 'quad_segs=2') +``` + +Output: + +Point buffer with 8 quadrant segments +Point buffer with 2 quadrant segments + +8 Segments   2 Segments + +SQL Example: + +```sql +SELECT ST_Buffer(ST_GeomFromWKT('LINESTRING(0 0, 50 70, 100 100)'), 10, 'side=left') +``` + +Output: + +Original Linestring +Original Linestring with buffer on the left side + +Original Linestring   Left side buffed Linestring ## ST_BuildArea diff --git a/docs/api/sql/Function.md b/docs/api/sql/Function.md index 42c4bf901a..b8496308fb 100644 --- a/docs/api/sql/Function.md +++ b/docs/api/sql/Function.md @@ -551,8 +551,6 @@ Output: `LINESTRING Z(-1 -1 0, 10 5 5)` ## ST_Buffer -## ST_Buffer - Introduction: Returns a geometry/geography that represents all points whose distance from this Geometry/geography is less than or equal to distance. The function supports both Planar/Euclidean and Spheroidal/Geodesic buffering (Since v1.6.0). Mode of buffer calculation (Since: `v1.6.0`): diff --git a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java index b5ee84e47f..2e6f445740 100644 --- a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java +++ b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java @@ -190,6 +190,10 @@ public void test_ST_Buffer() { "select sedona.ST_AsText(sedona.ST_Buffer(sedona.ST_GeomFromText('POINT (0 1)'), 1))", "POLYGON ((1 1, 0.9807852804032304 0.8049096779838718, 0.9238795325112867 0.6173165676349102, 0.8314696123025452 0.4444297669803978, 0.7071067811865476 0.2928932188134525, 0.5555702330196023 0.1685303876974548, 0.3826834323650898 0.0761204674887133, 0.1950903220161283 0.0192147195967696, 0.0000000000000001 0, -0.1950903220161282 0.0192147195967696, -0.3826834323650897 0.0761204674887133, -0.555570233019602 0.1685303876974547, -0.7071067811865475 0.2928932188134524, -0.8314696123025453 0.4444297669803978, -0.9238795325112867 0.6173165676349102, -0.9807852804032304 0.8049096779838714, -1 0.9999999999999999, -0.9807852804032304 1.1950903220161284, -0.9238795325112868 1.3826834323650896, -0.8314696123025455 1.555570233019602, -0.7071067811865477 1.7071067811865475, -0.5555702330196022 1.8314696123025453, -0.3826834323650903 1.9238795325112865, -0.1950903220161287 1.9807852804032304, -0.0000000000000002 2, 0.1950903220161283 1.9807852804032304, 0.38268343236509 1.9238795325112865, 0.5555702330196018 1.8314696123025453, 0.7071067811865474 1.7071067811865477, 0.8314696123025452 1.5555702330196022, 0.9238795325112865 1.3826834323650905, 0.9807852804032303 1.1950903220161286, 1 1))" ); + verifySqlSingleRes( + "select sedona.ST_AsText(sedona.ST_Buffer(sedona.ST_GeomFromText('LINESTRING(0.05 15, -0.05 15)'), 10000, true))", + "POLYGON ((-0.0499827380800979 14.90970764615526, -0.0680979441919658 14.911439289332314, -0.0855181036092048 14.916572765433372, -0.1015745790932317 14.924910889089798, -0.1156509498611482 14.936133480337729, -0.1272066059871122 14.949809631917493, -0.1357974717923214 14.965414213210906, -0.1410930699533803 14.982347984759812, -0.1428892711961725 14.9999605602647, -0.1411162325232574 15.017575343788987, -0.1358412050267848 15.034515492689351, -0.1272660846886664 15.050129914661689, -0.1157197795798512 15.063818302233363, -0.101645667253286 15.07505424080832, -0.0855846090787039 15.083405496399754, -0.068154165764569 15.088550694439519, -0.0500248124892859 15.090291738028835, 0.0500174017959422 15.0902994910598, 0.0681488768495457 15.08856105434859, 0.0855824246963393 15.08341775106032, 0.1016472676386697 15.075067339333813, 0.1157254254859488 15.06383098366898, 0.1272755583302725 15.050140871793444, 0.1358538186573691 15.034523547105394, 0.1411309042551145 15.017579606456339, 0.1429046575925539 14.999960553897546, 0.1411077364057122 14.982343709853023, 0.1358100760363371 14.965406147948107, 0.1272160681615492 14.949798665976873, 0.1156565845591405 14.936120792425546, 0.1015761705812689 14.9248977863495, 0.0855159139181673 14.916560508451733, 0.0680926538252635 14.911428928455091, 0.0499753291173759 14.909699892939221, -0.0499827380800979 14.90970764615526))" + ); } @Test diff --git a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java index bb8fd0fb66..4d77bde50d 100644 --- a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java +++ b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java @@ -213,6 +213,10 @@ public void test_ST_Buffer() { "select ST_AsText(sedona.ST_Buffer(ST_GeometryFromWKT('POINT (0 1)'), 1))", "POLYGON((1 1,0.9807852804 0.804909678,0.9238795325 0.6173165676,0.8314696123 0.444429767,0.7071067812 0.2928932188,0.555570233 0.1685303877,0.3826834324 0.07612046749,0.195090322 0.0192147196,6.123233996e-17 0,-0.195090322 0.0192147196,-0.3826834324 0.07612046749,-0.555570233 0.1685303877,-0.7071067812 0.2928932188,-0.8314696123 0.444429767,-0.9238795325 0.6173165676,-0.9807852804 0.804909678,-1 1,-0.9807852804 1.195090322,-0.9238795325 1.382683432,-0.8314696123 1.555570233,-0.7071067812 1.707106781,-0.555570233 1.831469612,-0.3826834324 1.923879533,-0.195090322 1.98078528,-1.836970199e-16 2,0.195090322 1.98078528,0.3826834324 1.923879533,0.555570233 1.831469612,0.7071067812 1.707106781,0.8314696123 1.555570233,0.9238795325 1.382683432,0.9807852804 1.195090322,1 1))" ); + verifySqlSingleRes( + "select ST_AsText(sedona.ST_Buffer(ST_GeometryFromWKT('LINESTRING(0.05 15, -0.05 15)'), 10000, true))", + "POLYGON ((-0.0499827380800979 14.90970764615526, -0.0680979441919658 14.911439289332314, -0.0855181036092048 14.916572765433372, -0.1015745790932317 14.924910889089798, -0.1156509498611482 14.936133480337729, -0.1272066059871122 14.949809631917493, -0.1357974717923214 14.965414213210906, -0.1410930699533803 14.982347984759812, -0.1428892711961725 14.9999605602647, -0.1411162325232574 15.017575343788987, -0.1358412050267848 15.034515492689351, -0.1272660846886664 15.050129914661689, -0.1157197795798512 15.063818302233363, -0.101645667253286 15.07505424080832, -0.0855846090787039 15.083405496399754, -0.068154165764569 15.088550694439519, -0.0500248124892859 15.090291738028835, 0.0500174017959422 15.0902994910598, 0.0681488768495457 15.08856105434859, 0.0855824246963393 15.08341775106032, 0.1016472676386697 15.075067339333813, 0.1157254254859488 15.06383098366898, 0.1272755583302725 15.050140871793444, 0.1358538186573691 15.034523547105394, 0.1411309042551145 15.017579606456339, 0.1429046575925539 14.999960553897546, 0.1411077364057122 14.982343709853023, 0.1358100760363371 14.965406147948107, 0.1272160681615492 14.949798665976873, 0.1156565845591405 14.936120792425546, 0.1015761705812689 14.9248977863495, 0.0855159139181673 14.916560508451733, 0.0680926538252635 14.911428928455091, 0.0499753291173759 14.909699892939221, -0.0499827380800979 14.90970764615526))" + ); } @Test public void test_ST_BuildArea() { diff --git a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFs.java b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFs.java index 3c1f49dd70..cd046cac64 100644 --- a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFs.java +++ b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFs.java @@ -170,6 +170,29 @@ public static byte[] ST_Buffer(byte[] geometry, double radius) throws FactoryExc ); } + @UDFAnnotations.ParamMeta(argNames = {"geometry", "radius", "useSpheroid"}) + public static byte[] ST_Buffer(byte[] geometry, double radius, boolean useSpheroid) throws FactoryException, TransformException { + return GeometrySerde.serialize( + Functions.buffer( + GeometrySerde.deserialize(geometry), + radius, + useSpheroid + ) + ); + } + + @UDFAnnotations.ParamMeta(argNames = {"geometry", "radius", "useSpheroid", "parameters"}) + public static byte[] ST_Buffer(byte[] geometry, double radius, boolean useSpheroid, String parameters) throws FactoryException, TransformException { + return GeometrySerde.serialize( + Functions.buffer( + GeometrySerde.deserialize(geometry), + radius, + useSpheroid, + parameters + ) + ); + } + @UDFAnnotations.ParamMeta(argNames = {"geometry"}) public static int ST_BestSRID(byte[] geometry) { return Functions.bestSRID( diff --git a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java index 95bfa0c3c3..5498820ff5 100644 --- a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java +++ b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java @@ -199,6 +199,29 @@ public static String ST_Buffer(String geometry, double radius) throws FactoryExc ); } + @UDFAnnotations.ParamMeta(argNames = {"geometry", "radius", "useSpheroid"}, argTypes = {"Geometry", "double", "boolean"}, returnTypes = "Geometry") + public static String ST_Buffer(String geometry, double radius, boolean useSpheroid) throws FactoryException, TransformException { + return GeometrySerde.serGeoJson( + Functions.buffer( + GeometrySerde.deserGeoJson(geometry), + radius, + useSpheroid + ) + ); + } + + @UDFAnnotations.ParamMeta(argNames = {"geometry", "radius", "useSpheroid", "parameters"}, argTypes = {"Geometry", "double", "boolean", "String"}, returnTypes = "Geometry") + public static String ST_Buffer(String geometry, double radius, boolean useSpheroid, String parameters) throws FactoryException, TransformException { + return GeometrySerde.serGeoJson( + Functions.buffer( + GeometrySerde.deserGeoJson(geometry), + radius, + useSpheroid, + parameters + ) + ); + } + @UDFAnnotations.ParamMeta(argNames = {"geometry"}, argTypes = {"Geometry"}, returnTypes = "Geometry") public static String ST_BuildArea(String geometry) { return GeometrySerde.serGeoJson( From afb528c6c69e6270f4a37c302fdc3a71d22a2318 Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Wed, 28 Feb 2024 18:31:46 -0500 Subject: [PATCH 35/54] Fix class definition for snowflake --- .../java/org/apache/sedona/snowflake/snowsql/TestFunctions.java | 1 + .../org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java | 1 + 2 files changed, 2 insertions(+) diff --git a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java index 2e6f445740..6230ed29d6 100644 --- a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java +++ b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java @@ -190,6 +190,7 @@ public void test_ST_Buffer() { "select sedona.ST_AsText(sedona.ST_Buffer(sedona.ST_GeomFromText('POINT (0 1)'), 1))", "POLYGON ((1 1, 0.9807852804032304 0.8049096779838718, 0.9238795325112867 0.6173165676349102, 0.8314696123025452 0.4444297669803978, 0.7071067811865476 0.2928932188134525, 0.5555702330196023 0.1685303876974548, 0.3826834323650898 0.0761204674887133, 0.1950903220161283 0.0192147195967696, 0.0000000000000001 0, -0.1950903220161282 0.0192147195967696, -0.3826834323650897 0.0761204674887133, -0.555570233019602 0.1685303876974547, -0.7071067811865475 0.2928932188134524, -0.8314696123025453 0.4444297669803978, -0.9238795325112867 0.6173165676349102, -0.9807852804032304 0.8049096779838714, -1 0.9999999999999999, -0.9807852804032304 1.1950903220161284, -0.9238795325112868 1.3826834323650896, -0.8314696123025455 1.555570233019602, -0.7071067811865477 1.7071067811865475, -0.5555702330196022 1.8314696123025453, -0.3826834323650903 1.9238795325112865, -0.1950903220161287 1.9807852804032304, -0.0000000000000002 2, 0.1950903220161283 1.9807852804032304, 0.38268343236509 1.9238795325112865, 0.5555702330196018 1.8314696123025453, 0.7071067811865474 1.7071067811865477, 0.8314696123025452 1.5555702330196022, 0.9238795325112865 1.3826834323650905, 0.9807852804032303 1.1950903220161286, 1 1))" ); + registerUDF("ST_Buffer", byte[].class, double.class, boolean.class); verifySqlSingleRes( "select sedona.ST_AsText(sedona.ST_Buffer(sedona.ST_GeomFromText('LINESTRING(0.05 15, -0.05 15)'), 10000, true))", "POLYGON ((-0.0499827380800979 14.90970764615526, -0.0680979441919658 14.911439289332314, -0.0855181036092048 14.916572765433372, -0.1015745790932317 14.924910889089798, -0.1156509498611482 14.936133480337729, -0.1272066059871122 14.949809631917493, -0.1357974717923214 14.965414213210906, -0.1410930699533803 14.982347984759812, -0.1428892711961725 14.9999605602647, -0.1411162325232574 15.017575343788987, -0.1358412050267848 15.034515492689351, -0.1272660846886664 15.050129914661689, -0.1157197795798512 15.063818302233363, -0.101645667253286 15.07505424080832, -0.0855846090787039 15.083405496399754, -0.068154165764569 15.088550694439519, -0.0500248124892859 15.090291738028835, 0.0500174017959422 15.0902994910598, 0.0681488768495457 15.08856105434859, 0.0855824246963393 15.08341775106032, 0.1016472676386697 15.075067339333813, 0.1157254254859488 15.06383098366898, 0.1272755583302725 15.050140871793444, 0.1358538186573691 15.034523547105394, 0.1411309042551145 15.017579606456339, 0.1429046575925539 14.999960553897546, 0.1411077364057122 14.982343709853023, 0.1358100760363371 14.965406147948107, 0.1272160681615492 14.949798665976873, 0.1156565845591405 14.936120792425546, 0.1015761705812689 14.9248977863495, 0.0855159139181673 14.916560508451733, 0.0680926538252635 14.911428928455091, 0.0499753291173759 14.909699892939221, -0.0499827380800979 14.90970764615526))" diff --git a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java index 4d77bde50d..7a93d63636 100644 --- a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java +++ b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java @@ -213,6 +213,7 @@ public void test_ST_Buffer() { "select ST_AsText(sedona.ST_Buffer(ST_GeometryFromWKT('POINT (0 1)'), 1))", "POLYGON((1 1,0.9807852804 0.804909678,0.9238795325 0.6173165676,0.8314696123 0.444429767,0.7071067812 0.2928932188,0.555570233 0.1685303877,0.3826834324 0.07612046749,0.195090322 0.0192147196,6.123233996e-17 0,-0.195090322 0.0192147196,-0.3826834324 0.07612046749,-0.555570233 0.1685303877,-0.7071067812 0.2928932188,-0.8314696123 0.444429767,-0.9238795325 0.6173165676,-0.9807852804 0.804909678,-1 1,-0.9807852804 1.195090322,-0.9238795325 1.382683432,-0.8314696123 1.555570233,-0.7071067812 1.707106781,-0.555570233 1.831469612,-0.3826834324 1.923879533,-0.195090322 1.98078528,-1.836970199e-16 2,0.195090322 1.98078528,0.3826834324 1.923879533,0.555570233 1.831469612,0.7071067812 1.707106781,0.8314696123 1.555570233,0.9238795325 1.382683432,0.9807852804 1.195090322,1 1))" ); + registerUDFV2("ST_Buffer", String.class, double.class, boolean.class); verifySqlSingleRes( "select ST_AsText(sedona.ST_Buffer(ST_GeometryFromWKT('LINESTRING(0.05 15, -0.05 15)'), 10000, true))", "POLYGON ((-0.0499827380800979 14.90970764615526, -0.0680979441919658 14.911439289332314, -0.0855181036092048 14.916572765433372, -0.1015745790932317 14.924910889089798, -0.1156509498611482 14.936133480337729, -0.1272066059871122 14.949809631917493, -0.1357974717923214 14.965414213210906, -0.1410930699533803 14.982347984759812, -0.1428892711961725 14.9999605602647, -0.1411162325232574 15.017575343788987, -0.1358412050267848 15.034515492689351, -0.1272660846886664 15.050129914661689, -0.1157197795798512 15.063818302233363, -0.101645667253286 15.07505424080832, -0.0855846090787039 15.083405496399754, -0.068154165764569 15.088550694439519, -0.0500248124892859 15.090291738028835, 0.0500174017959422 15.0902994910598, 0.0681488768495457 15.08856105434859, 0.0855824246963393 15.08341775106032, 0.1016472676386697 15.075067339333813, 0.1157254254859488 15.06383098366898, 0.1272755583302725 15.050140871793444, 0.1358538186573691 15.034523547105394, 0.1411309042551145 15.017579606456339, 0.1429046575925539 14.999960553897546, 0.1411077364057122 14.982343709853023, 0.1358100760363371 14.965406147948107, 0.1272160681615492 14.949798665976873, 0.1156565845591405 14.936120792425546, 0.1015761705812689 14.9248977863495, 0.0855159139181673 14.916560508451733, 0.0680926538252635 14.911428928455091, 0.0499753291173759 14.909699892939221, -0.0499827380800979 14.90970764615526))" From a1674ac9e26eaccbf947abea1258cbac8bd3e7db Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Wed, 28 Feb 2024 19:21:52 -0500 Subject: [PATCH 36/54] update python test case --- python/tests/sql/test_dataframe_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/tests/sql/test_dataframe_api.py b/python/tests/sql/test_dataframe_api.py index 0ecfd7dc3e..516880a1dd 100644 --- a/python/tests/sql/test_dataframe_api.py +++ b/python/tests/sql/test_dataframe_api.py @@ -74,7 +74,7 @@ (stf.ST_BestSRID, ("geom",), "triangle_geom", "", 3395), (stf.ST_Boundary, ("geom",), "triangle_geom", "", "LINESTRING (0 0, 1 0, 1 1, 0 0)"), (stf.ST_Buffer, ("point", 1.0), "point_geom", "ST_ReducePrecision(geom, 2)", "POLYGON ((0.98 0.8, 0.92 0.62, 0.83 0.44, 0.71 0.29, 0.56 0.17, 0.38 0.08, 0.2 0.02, 0 0, -0.2 0.02, -0.38 0.08, -0.56 0.17, -0.71 0.29, -0.83 0.44, -0.92 0.62, -0.98 0.8, -1 1, -0.98 1.2, -0.92 1.38, -0.83 1.56, -0.71 1.71, -0.56 1.83, -0.38 1.92, -0.2 1.98, 0 2, 0.2 1.98, 0.38 1.92, 0.56 1.83, 0.71 1.71, 0.83 1.56, 0.92 1.38, 0.98 1.2, 1 1, 0.98 0.8))"), - (stf.ST_Buffer, ("point", 1.0, True), "point_geom", "ST_ReducePrecision(geom, 2)", "POLYGON ((0.98 0.8, 0.92 0.62, 0.83 0.44, 0.71 0.29, 0.56 0.17, 0.38 0.08, 0.2 0.02, 0 0, -0.2 0.02, -0.38 0.08, -0.56 0.17, -0.71 0.29, -0.83 0.44, -0.92 0.62, -0.98 0.8, -1 1, -0.98 1.2, -0.92 1.38, -0.83 1.56, -0.71 1.71, -0.56 1.83, -0.38 1.92, -0.2 1.98, 0 2, 0.2 1.98, 0.38 1.92, 0.56 1.83, 0.71 1.71, 0.83 1.56, 0.92 1.38, 0.98 1.2, 1 1, 0.98 0.8))"), + (stf.ST_Buffer, ("point", 1.0, True), "point_geom", "", "POLYGON ((0.98 0.8, 0.92 0.62, 0.83 0.44, 0.71 0.29, 0.56 0.17, 0.38 0.08, 0.2 0.02, 0 0, -0.2 0.02, -0.38 0.08, -0.56 0.17, -0.71 0.29, -0.83 0.44, -0.92 0.62, -0.98 0.8, -1 1, -0.98 1.2, -0.92 1.38, -0.83 1.56, -0.71 1.71, -0.56 1.83, -0.38 1.92, -0.2 1.98, 0 2, 0.2 1.98, 0.38 1.92, 0.56 1.83, 0.71 1.71, 0.83 1.56, 0.92 1.38, 0.98 1.2, 1 1, 0.98 0.8))"), (stf.ST_BuildArea, ("geom",), "multiline_geom", "ST_Normalize(geom)", "POLYGON ((0 0, 1 1, 1 0, 0 0))"), (stf.ST_BoundingDiagonal, ("geom",), "square_geom", "ST_BoundingDiagonal(geom)", "LINESTRING (1 0, 2 1)"), (stf.ST_Centroid, ("geom",), "triangle_geom", "ST_ReducePrecision(geom, 2)", "POINT (0.67 0.33)"), From 8af5ec3978e07dc76b7f4514d16dfa630ec82323 Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Wed, 28 Feb 2024 19:36:41 -0500 Subject: [PATCH 37/54] Fix python test --- python/tests/sql/test_dataframe_api.py | 2 +- python/tests/streaming/spark/test_constructor_functions.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/python/tests/sql/test_dataframe_api.py b/python/tests/sql/test_dataframe_api.py index 516880a1dd..822a3db194 100644 --- a/python/tests/sql/test_dataframe_api.py +++ b/python/tests/sql/test_dataframe_api.py @@ -74,7 +74,7 @@ (stf.ST_BestSRID, ("geom",), "triangle_geom", "", 3395), (stf.ST_Boundary, ("geom",), "triangle_geom", "", "LINESTRING (0 0, 1 0, 1 1, 0 0)"), (stf.ST_Buffer, ("point", 1.0), "point_geom", "ST_ReducePrecision(geom, 2)", "POLYGON ((0.98 0.8, 0.92 0.62, 0.83 0.44, 0.71 0.29, 0.56 0.17, 0.38 0.08, 0.2 0.02, 0 0, -0.2 0.02, -0.38 0.08, -0.56 0.17, -0.71 0.29, -0.83 0.44, -0.92 0.62, -0.98 0.8, -1 1, -0.98 1.2, -0.92 1.38, -0.83 1.56, -0.71 1.71, -0.56 1.83, -0.38 1.92, -0.2 1.98, 0 2, 0.2 1.98, 0.38 1.92, 0.56 1.83, 0.71 1.71, 0.83 1.56, 0.92 1.38, 0.98 1.2, 1 1, 0.98 0.8))"), - (stf.ST_Buffer, ("point", 1.0, True), "point_geom", "", "POLYGON ((0.98 0.8, 0.92 0.62, 0.83 0.44, 0.71 0.29, 0.56 0.17, 0.38 0.08, 0.2 0.02, 0 0, -0.2 0.02, -0.38 0.08, -0.56 0.17, -0.71 0.29, -0.83 0.44, -0.92 0.62, -0.98 0.8, -1 1, -0.98 1.2, -0.92 1.38, -0.83 1.56, -0.71 1.71, -0.56 1.83, -0.38 1.92, -0.2 1.98, 0 2, 0.2 1.98, 0.38 1.92, 0.56 1.83, 0.71 1.71, 0.83 1.56, 0.92 1.38, 0.98 1.2, 1 1, 0.98 0.8))"), + (stf.ST_Buffer, ("point", 1.0, True), "point_geom", "", "POLYGON ((0.0000089758113634 1.0000000082631704, 0.0000088049473096 0.9999982455016537, 0.0000082957180969 0.9999965501645043, 0.0000074676931201 0.9999949874025787, 0.0000063526929175 0.9999936172719434, 0.0000049935663218 0.9999924924259491, 0.000003442543808 0.9999916560917964, 0.0000017592303026 0.9999911404093366, 0.0000000083146005 0.999990965195956, -0.0000017429165916 0.9999911371850047, -0.0000034271644398 0.9999916497670388, -0.0000049797042467 0.9999924832438165, -0.0000063408727792 0.9999936055852924, -0.0000074583610959 0.9999949736605123, -0.0000082892247492 0.9999965348951139, -0.0000088015341156 0.999998229291728, -0.0000089756014341 0.9999999917356441, -0.0000088047373942 1.0000017544971334, -0.0000082955082053 1.000003449834261, -0.0000074674832591 1.000005012596174, -0.000006352483086 1.0000063827268086, -0.0000049933565169 1.000007507572813, -0.0000034423340222 1.0000083439069856, -0.0000017590205255 1.0000088595894723, -0.0000000081048183 1.0000090348028825, 0.0000017431263877 1.0000088628138615, 0.0000034273742602 1.0000083502318489, 0.000004979914097 1.0000075167550835, 0.0000063410826597 1.000006394413609, 0.000007458571003 1.0000050263383786, 0.000008289434675 1.000003465103757, 0.0000088017440489 1.0000017707071163, 0.0000089758113634 1.0000000082631704))"), (stf.ST_BuildArea, ("geom",), "multiline_geom", "ST_Normalize(geom)", "POLYGON ((0 0, 1 1, 1 0, 0 0))"), (stf.ST_BoundingDiagonal, ("geom",), "square_geom", "ST_BoundingDiagonal(geom)", "LINESTRING (1 0, 2 1)"), (stf.ST_Centroid, ("geom",), "triangle_geom", "ST_ReducePrecision(geom, 2)", "POINT (0.67 0.33)"), diff --git a/python/tests/streaming/spark/test_constructor_functions.py b/python/tests/streaming/spark/test_constructor_functions.py index e31bc741d8..1afaf78160 100644 --- a/python/tests/streaming/spark/test_constructor_functions.py +++ b/python/tests/streaming/spark/test_constructor_functions.py @@ -48,7 +48,7 @@ .with_transform("ST_AREA")), (SuiteContainer.empty() .with_function_name("ST_Buffer") - .with_arguments(["ST_GeomFromText('POINT (21 52)')", "1.0", True]) + .with_arguments(["ST_GeomFromText('POINT (21 52)')", "1.0", "true"]) .with_expected_result(3.1214451522580533) .with_transform("ST_AREA")), (SuiteContainer.empty() From 12c57606ea5051ae6b5011eda1f5f3ffa076b5f6 Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Wed, 28 Feb 2024 19:56:25 -0500 Subject: [PATCH 38/54] Fix python test --- python/tests/streaming/spark/test_constructor_functions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/tests/streaming/spark/test_constructor_functions.py b/python/tests/streaming/spark/test_constructor_functions.py index 1afaf78160..1be6d961b4 100644 --- a/python/tests/streaming/spark/test_constructor_functions.py +++ b/python/tests/streaming/spark/test_constructor_functions.py @@ -48,8 +48,8 @@ .with_transform("ST_AREA")), (SuiteContainer.empty() .with_function_name("ST_Buffer") - .with_arguments(["ST_GeomFromText('POINT (21 52)')", "1.0", "true"]) - .with_expected_result(3.1214451522580533) + .with_arguments(["ST_GeomFromText('POINT (21 52)')", "100000", "true"]) + .with_expected_result(4.088135158017784) .with_transform("ST_AREA")), (SuiteContainer.empty() .with_function_name("ST_Distance") From 91a7845ef1259c3637c14c9d1d2aef52d7040e79 Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Wed, 28 Feb 2024 22:12:52 -0500 Subject: [PATCH 39/54] Update --- .../org/apache/sedona/common/Functions.java | 104 ++++++++++-------- .../sedona/common/FunctionsGeoTools.java | 2 +- 2 files changed, 62 insertions(+), 44 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 b39c291df2..ad1ba3fb6c 100644 --- a/common/src/main/java/org/apache/sedona/common/Functions.java +++ b/common/src/main/java/org/apache/sedona/common/Functions.java @@ -95,7 +95,7 @@ public static Geometry buffer(Geometry geometry, double radius, boolean useSpher return buffer(geometry, radius, useSpheroid, ""); } - public static Geometry buffer(Geometry geometry, double radius, boolean useSpheroid, String params) throws IllegalArgumentException{ + public static Geometry buffer(Geometry geometry, double radius, boolean useSpheroid, String params) throws IllegalArgumentException { BufferParameters bufferParameters = new BufferParameters(); // Processing parameters @@ -134,7 +134,7 @@ private static BufferParameters parseBufferParams(String params) { BufferParameters bufferParameters = new BufferParameters(); String[] listParams = params.split(" "); - for (String param: listParams) { + for (String param : listParams) { String[] singleParam = param.split("="); if (singleParam.length != 2) { @@ -150,7 +150,7 @@ private static BufferParameters parseBufferParams(String params) { } } // Set end cap style - else if (singleParam[0].equalsIgnoreCase(listBufferParameters[1])) { + else if (singleParam[0].equalsIgnoreCase(listBufferParameters[1])) { if (singleParam[1].equalsIgnoreCase(endcapOptions[0])) { bufferParameters.setEndCapStyle(BufferParameters.CAP_ROUND); } else if (singleParam[1].equalsIgnoreCase(endcapOptions[1]) || singleParam[1].equalsIgnoreCase(endcapOptions[2])) { @@ -202,25 +202,26 @@ else if (singleParam[0].equalsIgnoreCase(listBufferParameters[5])) { return bufferParameters; } - public static int bestSRID(Geometry geometry) throws IllegalArgumentException{ - // Shift longitudes of geometry crosses dateline - if (Predicates.crossesDateLine(geometry)) { + public static int bestSRID(Geometry geometry) throws IllegalArgumentException { + // Shift longitudes if geometry crosses dateline + if (crossesDateLine(geometry)) { shiftLongitude(geometry); } - // geometry = (Predicates.crossesDateLine(geometry)) ? shiftLongitude(geometry) : geometry; // Check envelope Envelope envelope = geometry.getEnvelopeInternal(); if (envelope.isNull()) return Spheroid.EPSG_WORLD_MERCATOR; // Fallback EPSG // Calculate the center of the envelope - double centerX = (envelope.getMinX() + envelope.getMaxX()) / 2.0; // centroid.getX(); + double centerX = (envelope.getMinX() + envelope.getMaxX()) / 2.0; // centroid.getX(); double centerY = (envelope.getMinY() + envelope.getMaxY()) / 2.0; // centroid.getY(); // Calculate angular width and height Double xwidth = Spheroid.angularWidth(envelope); Double ywidth = Spheroid.angularHeight(envelope); - if (xwidth.isNaN() | ywidth.isNaN()) {throw new IllegalArgumentException("Only lon/lat coordinate systems are supported by ST_BestSRID");} + if (xwidth.isNaN() | ywidth.isNaN()) { + throw new IllegalArgumentException("Only lon/lat coordinate systems are supported by ST_BestSRID"); + } // Prioritize polar regions for Lambert Azimuthal Equal Area projection if (centerY >= 70.0 && ywidth < 45.0) return Spheroid.EPSG_NORTH_LAMBERT; @@ -232,8 +233,8 @@ public static int bestSRID(Geometry geometry) throws IllegalArgumentException{ if (centerX == -180.0 || centerX == 180.0) { zone = 59; // UTM zone 60 } else { - zone = (int)Math.floor((centerX + 180.0) / 6.0); - zone = (zone > 59) ? zone-60 : zone; + zone = (int) Math.floor((centerX + 180.0) / 6.0); + zone = (zone > 59) ? zone - 60 : zone; } return (centerY < 0.0) ? Spheroid.EPSG_SOUTH_UTM_START + zone : Spheroid.EPSG_NORTH_UTM_START + zone; } @@ -252,19 +253,38 @@ public static void normalizeLongitude(Geometry geometry) { if (geometry == null || geometry.isEmpty()) { return; } - for (Coordinate coord : geometry.getCoordinates()) { - // Normalize the longitude to be within -180 to 180 range - while (coord.x > 180) coord.x -= 360; - while (coord.x < -180) coord.x += 360; - } - geometry.geometryChanged(); + + geometry.apply(new CoordinateSequenceFilter() { + @Override + public void filter(CoordinateSequence seq, int i) { + double x = seq.getX(i); + // Normalize the longitude to be within -180 to 180 range + while (x > 180) x -= 360; + while (x < -180) x += 360; + seq.setOrdinate(i, CoordinateSequence.X, x); + } + + @Override + public boolean isDone() { + return false; // Continue processing until all coordinates are processed + } + + @Override + public boolean isGeometryChanged() { + return true; // The geometry is changed as we are modifying the coordinates + } + }); + + geometry.geometryChanged(); // Notify the geometry that its coordinates have been changed + } + /** * Checks if a geometry crosses the International Date Line. * * @param geometry The geometry to check. * @return True if the geometry crosses the Date Line, false otherwise. */ - public static boolean crossesDateLine(Geometry geometry) { + public static boolean crossesDateLine (Geometry geometry){ if (geometry == null || geometry.isEmpty()) { return false; } @@ -303,28 +323,7 @@ public boolean isGeometryChanged() { return filter.isDone(); } - public static Geometry envelope(Geometry geometry) { - return geometry.getEnvelope(); - } - - public static double distance(Geometry left, Geometry right) { - return left.distance(right); - } - - public static double distance3d(Geometry left, Geometry right) { - return new Distance3DOp(left, right).distance(); - } - - public static double length(Geometry geometry) { - return geometry.getLength(); - } - - public static Geometry normalize(Geometry geometry) { - geometry.normalize(); - return geometry; - } - - public static Geometry shiftLongitude(Geometry geometry) { + public static Geometry shiftLongitude (Geometry geometry){ geometry.apply(new CoordinateSequenceFilter() { @Override public void filter(CoordinateSequence seq, int i) { @@ -350,6 +349,27 @@ public boolean isGeometryChanged() { return geometry; } + public static Geometry envelope(Geometry geometry) { + return geometry.getEnvelope(); + } + + public static double distance(Geometry left, Geometry right) { + return left.distance(right); + } + + public static double distance3d(Geometry left, Geometry right) { + return new Distance3DOp(left, right).distance(); + } + + public static double length(Geometry geometry) { + return geometry.getLength(); + } + + public static Geometry normalize(Geometry geometry) { + geometry.normalize(); + return geometry; + } + public static Double x(Geometry geometry) { if (geometry instanceof Point) { return geometry.getCoordinate().x; @@ -382,8 +402,6 @@ public static double xMin(Geometry geometry) { public static double xMax(Geometry geometry) { Coordinate[] points = geometry.getCoordinates(); - double max = - Double.MAX_VALUE; - for (int i=0; i < points.length; i++) { max = Math.max(points[i].getX(), max); } return max; @@ -1382,7 +1400,7 @@ public static Geometry boundingDiagonal(Geometry geometry) { startCoordinate = new Coordinate(startX, startY); endCoordinate = new Coordinate(endX, endY); } - return GEOMETRY_FACTORY.createLineString(new Coordinate[] {startCoordinate, endCoordinate}); + return GEOMETRY_FACTORY.createLineString(new Coordinate[]{startCoordinate, endCoordinate}); } } diff --git a/common/src/main/java/org/apache/sedona/common/FunctionsGeoTools.java b/common/src/main/java/org/apache/sedona/common/FunctionsGeoTools.java index 4211be9977..b47c5ce8f7 100644 --- a/common/src/main/java/org/apache/sedona/common/FunctionsGeoTools.java +++ b/common/src/main/java/org/apache/sedona/common/FunctionsGeoTools.java @@ -144,7 +144,7 @@ public static Geometry bufferSpheroid(Geometry geometry, double radius, BufferPa final int WGS84CRS = 4326; // Shift longitude if geometry crosses dateline - if (Predicates.crossesDateLine(geometry)) { + if (Functions.crossesDateLine(geometry)) { Functions.shiftLongitude(geometry); } // geometry = (Predicates.crossesDateLine(geometry)) ? Functions.shiftLongitude(geometry) : geometry; From 2227a88fc75b3e3628c9c216be2bf22367fc413b Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Wed, 28 Feb 2024 22:15:52 -0500 Subject: [PATCH 40/54] Update --- .../org/apache/sedona/common/Functions.java | 355 +++++++++--------- 1 file changed, 181 insertions(+), 174 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 ad1ba3fb6c..8f6f2a9fe7 100644 --- a/common/src/main/java/org/apache/sedona/common/Functions.java +++ b/common/src/main/java/org/apache/sedona/common/Functions.java @@ -349,129 +349,131 @@ public boolean isGeometryChanged() { return geometry; } - public static Geometry envelope(Geometry geometry) { + public static Geometry envelope (Geometry geometry){ return geometry.getEnvelope(); } - public static double distance(Geometry left, Geometry right) { + public static double distance (Geometry left, Geometry right){ return left.distance(right); } - public static double distance3d(Geometry left, Geometry right) { + public static double distance3d (Geometry left, Geometry right){ return new Distance3DOp(left, right).distance(); } - public static double length(Geometry geometry) { + public static double length (Geometry geometry){ return geometry.getLength(); } - public static Geometry normalize(Geometry geometry) { + public static Geometry normalize (Geometry geometry){ geometry.normalize(); return geometry; } - public static Double x(Geometry geometry) { + public static Double x (Geometry geometry){ if (geometry instanceof Point) { return geometry.getCoordinate().x; } return null; } - public static Double y(Geometry geometry) { + public static Double y (Geometry geometry){ if (geometry instanceof Point) { return geometry.getCoordinate().y; } return null; } - public static Double z(Geometry geometry) { + public static Double z (Geometry geometry){ if (geometry instanceof Point) { return geometry.getCoordinate().z; } return null; } - public static double xMin(Geometry geometry) { + public static double xMin (Geometry geometry){ Coordinate[] points = geometry.getCoordinates(); double min = Double.MAX_VALUE; - for(int i=0; i < points.length; i++){ + for (int i = 0; i < points.length; i++) { min = Math.min(points[i].getX(), min); } return min; } - public static double xMax(Geometry geometry) { + public static double xMax (Geometry geometry){ Coordinate[] points = geometry.getCoordinates(); + double max = -Double.MAX_VALUE; + for (int i = 0; i < points.length; i++) { max = Math.max(points[i].getX(), max); } return max; } - public static double yMin(Geometry geometry) { + public static double yMin (Geometry geometry){ Coordinate[] points = geometry.getCoordinates(); double min = Double.MAX_VALUE; - for(int i=0; i < points.length; i++){ + for (int i = 0; i < points.length; i++) { min = Math.min(points[i].getY(), min); } return min; } - public static double yMax(Geometry geometry) { + public static double yMax (Geometry geometry){ Coordinate[] points = geometry.getCoordinates(); - double max = - Double.MAX_VALUE; - for (int i=0; i < points.length; i++) { + double max = -Double.MAX_VALUE; + for (int i = 0; i < points.length; i++) { max = Math.max(points[i].getY(), max); } return max; } - public static Double zMax(Geometry geometry) { + public static Double zMax (Geometry geometry){ Coordinate[] points = geometry.getCoordinates(); - double max = - Double.MAX_VALUE; - for (int i=0; i < points.length; i++) { - if(java.lang.Double.isNaN(points[i].getZ())) + double max = -Double.MAX_VALUE; + for (int i = 0; i < points.length; i++) { + if (java.lang.Double.isNaN(points[i].getZ())) continue; max = Math.max(points[i].getZ(), max); } return max == -Double.MAX_VALUE ? null : max; } - public static Double zMin(Geometry geometry) { + public static Double zMin (Geometry geometry){ Coordinate[] points = geometry.getCoordinates(); double min = Double.MAX_VALUE; - for(int i=0; i < points.length; i++){ - if(java.lang.Double.isNaN(points[i].getZ())) + for (int i = 0; i < points.length; i++) { + if (java.lang.Double.isNaN(points[i].getZ())) continue; min = Math.min(points[i].getZ(), min); } return min == Double.MAX_VALUE ? null : min; } - public static Geometry flipCoordinates(Geometry geometry) { + public static Geometry flipCoordinates (Geometry geometry){ GeomUtils.flipCoordinates(geometry); return geometry; } - public static String geohash(Geometry geometry, int precision) { + public static String geohash (Geometry geometry,int precision){ return GeometryGeoHashEncoder.calculate(geometry, precision); } - public static Geometry pointOnSurface(Geometry geometry) { + public static Geometry pointOnSurface (Geometry geometry){ return GeomUtils.getInteriorPoint(geometry); } - public static Geometry reverse(Geometry geometry) { + public static Geometry reverse (Geometry geometry){ return geometry.reverse(); } - public static Geometry geometryN(Geometry geometry, int n) { + public static Geometry geometryN (Geometry geometry,int n){ if (n < geometry.getNumGeometries()) { return geometry.getGeometryN(n); } return null; } - public static Geometry interiorRingN(Geometry geometry, int n) { + public static Geometry interiorRingN (Geometry geometry,int n){ if (geometry instanceof Polygon) { Polygon polygon = (Polygon) geometry; if (n < polygon.getNumInteriorRing()) { @@ -485,14 +487,14 @@ public static Geometry interiorRingN(Geometry geometry, int n) { return null; } - public static Geometry pointN(Geometry geometry, int n) { - if(!(geometry instanceof LineString)) { + public static Geometry pointN (Geometry geometry,int n){ + if (!(geometry instanceof LineString)) { return null; } - return GeomUtils.getNthPoint((LineString)geometry, n); + return GeomUtils.getNthPoint((LineString) geometry, n); } - public static Geometry exteriorRing(Geometry geometry) { + public static Geometry exteriorRing (Geometry geometry){ Geometry ring = GeomUtils.getExteriorRing(geometry); if (ring instanceof LinearRing) { ring = GEOMETRY_FACTORY.createLineString(ring.getCoordinates()); @@ -500,23 +502,23 @@ public static Geometry exteriorRing(Geometry geometry) { return ring; } - public static String asEWKT(Geometry geometry) { + public static String asEWKT (Geometry geometry){ return GeomUtils.getEWKT(geometry); } - public static String asWKT(Geometry geometry) { + public static String asWKT (Geometry geometry){ return GeomUtils.getWKT(geometry); } - public static byte[] asEWKB(Geometry geometry) { + public static byte[] asEWKB (Geometry geometry){ return GeomUtils.getEWKB(geometry); } - public static byte[] asWKB(Geometry geometry) { + public static byte[] asWKB (Geometry geometry){ return GeomUtils.getWKB(geometry); } - public static String asGeoJson(Geometry geometry) { + public static String asGeoJson (Geometry geometry){ if (geometry == null) { return null; } @@ -524,60 +526,60 @@ public static String asGeoJson(Geometry geometry) { return writer.write(geometry).toString(); } - public static int nPoints(Geometry geometry) { + public static int nPoints (Geometry geometry){ return geometry.getNumPoints(); } - public static int nDims(Geometry geometry) { - int count_dimension =0; + public static int nDims (Geometry geometry){ + int count_dimension = 0; Coordinate geom = geometry.getCoordinate(); Double x_cord = geom.getX(); Double y_cord = geom.getY(); Double z_cord = geom.getZ(); Double m_cord = geom.getM(); - if(!java.lang.Double.isNaN(x_cord)) + if (!java.lang.Double.isNaN(x_cord)) count_dimension++; - if(!java.lang.Double.isNaN(y_cord)) + if (!java.lang.Double.isNaN(y_cord)) count_dimension++; - if(!java.lang.Double.isNaN(z_cord)) + if (!java.lang.Double.isNaN(z_cord)) count_dimension++; - if(!java.lang.Double.isNaN(m_cord)) + if (!java.lang.Double.isNaN(m_cord)) count_dimension++; return count_dimension; } - public static int numGeometries(Geometry geometry) { + public static int numGeometries (Geometry geometry){ return geometry.getNumGeometries(); } - public static Integer numInteriorRings(Geometry geometry) { + public static Integer numInteriorRings (Geometry geometry){ if (geometry instanceof Polygon) { return ((Polygon) geometry).getNumInteriorRing(); } return null; } - public static String asGML(Geometry geometry) { + public static String asGML (Geometry geometry){ return new GMLWriter().write(geometry); } - public static String asKML(Geometry geometry) { + public static String asKML (Geometry geometry){ return new KMLWriter().write(geometry); } - public static Geometry force2D(Geometry geometry) { + public static Geometry force2D (Geometry geometry){ return GeomUtils.get2dGeom(geometry); } - public static boolean isEmpty(Geometry geometry) { + public static boolean isEmpty (Geometry geometry){ return geometry.isEmpty(); } - public static Geometry buildArea(Geometry geometry) { + public static Geometry buildArea (Geometry geometry){ return GeomUtils.buildArea(geometry); } - public static Geometry setSRID(Geometry geometry, int srid) { + public static Geometry setSRID (Geometry geometry,int srid){ if (geometry == null) { return null; } @@ -585,14 +587,14 @@ public static Geometry setSRID(Geometry geometry, int srid) { return factory.createGeometry(geometry); } - public static int getSRID(Geometry geometry) { + public static int getSRID (Geometry geometry){ if (geometry == null) { return 0; } return geometry.getSRID(); } - public static boolean isClosed(Geometry geometry) { + public static boolean isClosed (Geometry geometry){ if (geometry instanceof Circle || geometry instanceof MultiPoint || geometry instanceof MultiPolygon || geometry instanceof Point || geometry instanceof Polygon) { return true; } else if (geometry instanceof LineString) { @@ -605,19 +607,19 @@ public static boolean isClosed(Geometry geometry) { return false; } - public static boolean isRing(Geometry geometry) { + public static boolean isRing (Geometry geometry){ return geometry instanceof LineString && ((LineString) geometry).isClosed() && geometry.isSimple(); } - public static boolean isSimple(Geometry geometry) { + public static boolean isSimple (Geometry geometry){ return new IsSimpleOp(geometry).isSimple(); } - public static boolean isValid(Geometry geometry) { + public static boolean isValid (Geometry geometry){ return isValid(geometry, OGC_SFS_VALIDITY); } - public static boolean isValid(Geometry geom, int flag) { + public static boolean isValid (Geometry geom,int flag){ IsValidOp isValidOp = new IsValidOp(geom); // Set the validity model based on flags @@ -630,11 +632,11 @@ public static boolean isValid(Geometry geom, int flag) { return isValidOp.isValid(); } - public static Geometry addPoint(Geometry linestring, Geometry point) { + public static Geometry addPoint (Geometry linestring, Geometry point){ return addPoint(linestring, point, -1); } - public static Geometry addPoint(Geometry linestring, Geometry point, int position) { + public static Geometry addPoint (Geometry linestring, Geometry point,int position){ if (linestring instanceof LineString && point instanceof Point) { List coordinates = new ArrayList<>(Arrays.asList(linestring.getCoordinates())); if (-1 <= position && position <= coordinates.size()) { @@ -649,14 +651,14 @@ public static Geometry addPoint(Geometry linestring, Geometry point, int positio return null; } - public static Geometry removePoint(Geometry linestring) { + public static Geometry removePoint (Geometry linestring){ if (linestring != null) { return removePoint(linestring, -1); } return null; } - public static Geometry removePoint(Geometry linestring, int position) { + public static Geometry removePoint (Geometry linestring,int position){ if (linestring instanceof LineString) { List coordinates = new ArrayList<>(Arrays.asList(linestring.getCoordinates())); if (2 < coordinates.size() && position < coordinates.size()) { @@ -670,7 +672,7 @@ public static Geometry removePoint(Geometry linestring, int position) { return null; } - public static Geometry setPoint(Geometry linestring, int position, Geometry point) { + public static Geometry setPoint (Geometry linestring,int position, Geometry point){ if (linestring instanceof LineString) { List coordinates = new ArrayList<>(Arrays.asList(linestring.getCoordinates())); if (-coordinates.size() <= position && position < coordinates.size()) { @@ -685,29 +687,28 @@ public static Geometry setPoint(Geometry linestring, int position, Geometry poin return null; } - public static Geometry lineFromMultiPoint(Geometry geometry) { - if(!(geometry instanceof MultiPoint)) { + public static Geometry lineFromMultiPoint (Geometry geometry){ + if (!(geometry instanceof MultiPoint)) { return null; } List coordinates = new ArrayList<>(); - for(Coordinate c : geometry.getCoordinates()){ + for (Coordinate c : geometry.getCoordinates()) { coordinates.add(c); } return GEOMETRY_FACTORY.createLineString(coordinates.toArray(new Coordinate[0])); } - public static Geometry closestPoint(Geometry left, Geometry right) { + public static Geometry closestPoint (Geometry left, Geometry right){ DistanceOp distanceOp = new DistanceOp(left, right); try { Coordinate[] closestPoints = distanceOp.nearestPoints(); return GEOMETRY_FACTORY.createPoint(closestPoints[0]); - } - catch (Exception e) { + } catch (Exception e) { throw new IllegalArgumentException("ST_ClosestPoint doesn't support empty geometry object."); } } - public static Geometry concaveHull(Geometry geometry, double pctConvex, boolean allowHoles){ + public static Geometry concaveHull (Geometry geometry,double pctConvex, boolean allowHoles){ ConcaveHull concave_hull = new ConcaveHull(geometry); concave_hull.setMaximumEdgeLengthRatio(pctConvex); concave_hull.setHolesAllowed(allowHoles); @@ -715,15 +716,15 @@ public static Geometry concaveHull(Geometry geometry, double pctConvex, boolean return concave_hull.getHull(); } - public static Geometry convexHull(Geometry geometry) { + public static Geometry convexHull (Geometry geometry){ return geometry.convexHull(); } - public static Geometry getCentroid(Geometry geometry) { + public static Geometry getCentroid (Geometry geometry){ return geometry.getCentroid(); } - public static Geometry intersection(Geometry leftGeometry, Geometry rightGeometry) { + public static Geometry intersection (Geometry leftGeometry, Geometry rightGeometry){ boolean isIntersects = leftGeometry.intersects(rightGeometry); if (!isIntersects) { return EMPTY_POLYGON; @@ -737,18 +738,18 @@ public static Geometry intersection(Geometry leftGeometry, Geometry rightGeometr return leftGeometry.intersection(rightGeometry); } - public static Geometry makeValid(Geometry geometry, boolean keepCollapsed) { + public static Geometry makeValid (Geometry geometry,boolean keepCollapsed){ GeometryFixer fixer = new GeometryFixer(geometry); fixer.setKeepCollapsed(keepCollapsed); return fixer.getResult(); } - public static Geometry reducePrecision(Geometry geometry, int precisionScale) { + public static Geometry reducePrecision (Geometry geometry,int precisionScale){ GeometryPrecisionReducer precisionReduce = new GeometryPrecisionReducer(new PrecisionModel(Math.pow(10, precisionScale))); return precisionReduce.reduce(geometry); } - public static Geometry lineMerge(Geometry geometry) { + public static Geometry lineMerge (Geometry geometry){ if (geometry instanceof MultiLineString) { MultiLineString multiLineString = (MultiLineString) geometry; int numLineStrings = multiLineString.getNumGeometries(); @@ -768,7 +769,7 @@ public static Geometry lineMerge(Geometry geometry) { return EMPTY_GEOMETRY_COLLECTION; } - public static Geometry minimumBoundingCircle(Geometry geometry, int quadrantSegments) { + public static Geometry minimumBoundingCircle (Geometry geometry,int quadrantSegments){ MinimumBoundingCircle minimumBoundingCircle = new MinimumBoundingCircle(geometry); Coordinate centre = minimumBoundingCircle.getCentre(); double radius = minimumBoundingCircle.getRadius(); @@ -784,7 +785,7 @@ public static Geometry minimumBoundingCircle(Geometry geometry, int quadrantSegm return circle; } - public static Pair minimumBoundingRadius(Geometry geometry) { + public static Pair minimumBoundingRadius (Geometry geometry){ MinimumBoundingCircle minimumBoundingCircle = new MinimumBoundingCircle(geometry); Coordinate coods = minimumBoundingCircle.getCentre(); double radius = minimumBoundingCircle.getRadius(); @@ -792,28 +793,28 @@ public static Pair minimumBoundingRadius(Geometry geometry) { return Pair.of(centre, radius); } - public static Geometry lineSubString(Geometry geom, double fromFraction, double toFraction) { + public static Geometry lineSubString (Geometry geom,double fromFraction, double toFraction){ double length = geom.getLength(); LengthIndexedLine indexedLine = new LengthIndexedLine(geom); Geometry subLine = indexedLine.extractLine(length * fromFraction, length * toFraction); return subLine; } - public static Geometry lineInterpolatePoint(Geometry geom, double fraction) { + public static Geometry lineInterpolatePoint (Geometry geom,double fraction){ double length = geom.getLength(); LengthIndexedLine indexedLine = new LengthIndexedLine(geom); Coordinate interPoint = indexedLine.extractPoint(length * fraction); return GEOMETRY_FACTORY.createPoint(interPoint); } - public static double lineLocatePoint(Geometry geom, Geometry point) + public static double lineLocatePoint (Geometry geom, Geometry point) { double length = geom.getLength(); LengthIndexedLine indexedLine = new LengthIndexedLine(geom); return indexedLine.indexOf(point.getCoordinate()) / length; } - public static Geometry difference(Geometry leftGeometry, Geometry rightGeometry) { + public static Geometry difference (Geometry leftGeometry, Geometry rightGeometry){ boolean isIntersects = leftGeometry.intersects(rightGeometry); if (!isIntersects) { return leftGeometry; @@ -824,12 +825,12 @@ public static Geometry difference(Geometry leftGeometry, Geometry rightGeometry) } } - public static Geometry split(Geometry input, Geometry blade) { + public static Geometry split (Geometry input, Geometry blade){ // check input geometry return new GeometrySplitter(GEOMETRY_FACTORY).split(input, blade); } - public static Integer dimension(Geometry geometry) { + public static Integer dimension (Geometry geometry){ Integer dimension = geometry.getDimension(); // unknown dimension such as an empty GEOMETRYCOLLECTION if (dimension < 0) { @@ -844,7 +845,7 @@ public static Integer dimension(Geometry geometry) { * @param level integer, minimum level of cells covering the geometry * @return List of coordinates */ - public static Long[] s2CellIDs(Geometry input, int level) { + public static Long[] s2CellIDs (Geometry input,int level){ HashSet cellIds = new HashSet<>(); List geoms = GeomUtils.extractGeometryCollection(input); for (Geometry geom : geoms) { @@ -865,7 +866,7 @@ public static Long[] s2CellIDs(Geometry input, int level) { * @param fullCover whether enforce full cover or not. * @return */ - public static Long[] h3CellIDs(Geometry input, int level, boolean fullCover) { + public static Long[] h3CellIDs (Geometry input,int level, boolean fullCover){ if (level < 0 || level > 15) { throw new IllegalArgumentException("level must be between 0 and 15"); } @@ -877,11 +878,11 @@ public static Long[] h3CellIDs(Geometry input, int level, boolean fullCover) { cellIds.addAll(H3Utils.polygonToCells((Polygon) geom, level, fullCover)); } else if (geom instanceof LineString) { cellIds.addAll(H3Utils.lineStringToCells((LineString) geom, level, fullCover)); - } else if (geom instanceof Point){ + } else if (geom instanceof Point) { cellIds.add(H3Utils.coordinateToCell(geom.getCoordinate(), level)); } else { // if not type of polygon, point or lienSting, we cover its MBR - cellIds.addAll(H3Utils.polygonToCells((Polygon)geom.getEnvelope(), level, fullCover)); + cellIds.addAll(H3Utils.polygonToCells((Polygon) geom.getEnvelope(), level, fullCover)); } } return cellIds.toArray(new Long[0]); @@ -893,7 +894,7 @@ public static Long[] h3CellIDs(Geometry input, int level, boolean fullCover) { * @param cell2 destination cell * @return */ - public static long h3CellDistance(long cell1, long cell2) { + public static long h3CellDistance ( long cell1, long cell2){ int resolution = H3Utils.h3.getResolution(cell1); if (resolution != H3Utils.h3.getResolution(cell2)) { throw new IllegalArgumentException("The argument cells should be of the same resolution"); @@ -918,7 +919,7 @@ public static long h3CellDistance(long cell1, long cell2) { * @param exactDistance: if exactDistance is true, it will only return the cells on the exact kth ring, else will return all 0 - kth neighbors * @return */ - public static Long[] h3KRing(long cell, int k, boolean exactDistance) { + public static Long[] h3KRing ( long cell, int k, boolean exactDistance){ Set cells = new LinkedHashSet<>(H3Utils.h3.gridDisk(cell, k)); if (exactDistance && k > 0) { List tbdCells = H3Utils.h3.gridDisk(cell, k - 1); @@ -932,7 +933,7 @@ public static Long[] h3KRing(long cell, int k, boolean exactDistance) { * @param cells: the set of cells * @return Multiple Polygons reversed */ - public static Geometry h3ToGeom(long[] cells) { + public static Geometry h3ToGeom ( long[] cells){ GeometryFactory geomFactory = new GeometryFactory(); Collection h3 = Arrays.stream(cells).boxed().collect(Collectors.toList()); return geomFactory.createMultiPolygon( @@ -953,15 +954,15 @@ public static Geometry h3ToGeom(long[] cells) { } // create static function named simplifyPreserveTopology - public static Geometry simplifyPreserveTopology(Geometry geometry, double distanceTolerance) { + public static Geometry simplifyPreserveTopology (Geometry geometry,double distanceTolerance){ return TopologyPreservingSimplifier.simplify(geometry, distanceTolerance); } - public static String geometryType(Geometry geometry) { + public static String geometryType (Geometry geometry){ return "ST_" + geometry.getGeometryType(); } - public static String geometryTypeWithMeasured(Geometry geometry) { + public static String geometryTypeWithMeasured (Geometry geometry){ String geometryType = geometry.getGeometryType().toUpperCase(); if (GeomUtils.isMeasuredGeometry(geometry)) { geometryType += "M"; @@ -969,7 +970,7 @@ public static String geometryTypeWithMeasured(Geometry geometry) { return geometryType; } - public static Geometry startPoint(Geometry geometry) { + public static Geometry startPoint (Geometry geometry){ if (geometry instanceof LineString) { LineString line = (LineString) geometry; return line.getStartPoint(); @@ -977,7 +978,7 @@ public static Geometry startPoint(Geometry geometry) { return null; } - public static Geometry endPoint(Geometry geometry) { + public static Geometry endPoint (Geometry geometry){ if (geometry instanceof LineString) { LineString line = (LineString) geometry; return line.getEndPoint(); @@ -985,7 +986,7 @@ public static Geometry endPoint(Geometry geometry) { return null; } - public static Geometry[] dump(Geometry geometry) { + public static Geometry[] dump (Geometry geometry){ int numGeom = geometry.getNumGeometries(); if (geometry instanceof GeometryCollection) { Geometry[] geoms = new Geometry[geometry.getNumGeometries()]; @@ -994,56 +995,55 @@ public static Geometry[] dump(Geometry geometry) { } return geoms; } else { - return new Geometry[] {geometry}; + return new Geometry[]{geometry}; } } - public static Geometry[] dumpPoints(Geometry geometry) { + public static Geometry[] dumpPoints (Geometry geometry){ return Arrays.stream(geometry.getCoordinates()).map(GEOMETRY_FACTORY::createPoint).toArray(Point[]::new); } - public static Geometry symDifference(Geometry leftGeom, Geometry rightGeom) { + public static Geometry symDifference (Geometry leftGeom, Geometry rightGeom){ return leftGeom.symDifference(rightGeom); } - public static Geometry union(Geometry leftGeom, Geometry rightGeom) { + public static Geometry union (Geometry leftGeom, Geometry rightGeom){ return leftGeom.union(rightGeom); } - public static Geometry createMultiGeometryFromOneElement(Geometry geometry) { + public static Geometry createMultiGeometryFromOneElement (Geometry geometry){ if (geometry instanceof Circle) { - return GEOMETRY_FACTORY.createGeometryCollection(new Circle[] {(Circle) geometry}); + return GEOMETRY_FACTORY.createGeometryCollection(new Circle[]{(Circle) geometry}); } else if (geometry instanceof GeometryCollection) { return geometry; - } else if (geometry instanceof LineString) { + } else if (geometry instanceof LineString) { return GEOMETRY_FACTORY.createMultiLineString(new LineString[]{(LineString) geometry}); } else if (geometry instanceof Point) { - return GEOMETRY_FACTORY.createMultiPoint(new Point[] {(Point) geometry}); + return GEOMETRY_FACTORY.createMultiPoint(new Point[]{(Point) geometry}); } else if (geometry instanceof Polygon) { - return GEOMETRY_FACTORY.createMultiPolygon(new Polygon[] {(Polygon) geometry}); + return GEOMETRY_FACTORY.createMultiPolygon(new Polygon[]{(Polygon) geometry}); } else { return GEOMETRY_FACTORY.createGeometryCollection(); } } - public static Geometry[] subDivide(Geometry geometry, int maxVertices) { + public static Geometry[] subDivide (Geometry geometry,int maxVertices){ return GeometrySubDivider.subDivide(geometry, maxVertices); } - public static Geometry makeLine(Geometry geom1, Geometry geom2) { + public static Geometry makeLine (Geometry geom1, Geometry geom2){ Geometry[] geoms = new Geometry[]{geom1, geom2}; return makeLine(geoms); } - public static Geometry makeLine(Geometry[] geoms) { + public static Geometry makeLine (Geometry[]geoms){ ArrayList coordinates = new ArrayList<>(); for (Geometry geom : geoms) { if (geom instanceof Point || geom instanceof MultiPoint || geom instanceof LineString) { for (Coordinate coord : geom.getCoordinates()) { coordinates.add(coord); } - } - else { + } else { throw new IllegalArgumentException("ST_MakeLine only supports Point, MultiPoint and LineString geometries"); } } @@ -1052,10 +1052,10 @@ public static Geometry makeLine(Geometry[] geoms) { return GEOMETRY_FACTORY.createLineString(coords); } - public static Geometry makePolygon(Geometry shell, Geometry[] holes) { + public static Geometry makePolygon (Geometry shell, Geometry[]holes){ try { if (holes != null) { - LinearRing[] interiorRings = Arrays.stream(holes).filter( + LinearRing[] interiorRings = Arrays.stream(holes).filter( h -> h != null && !h.isEmpty() && h instanceof LineString && ((LineString) h).isClosed() ).map( h -> GEOMETRY_FACTORY.createLinearRing(h.getCoordinates()) @@ -1079,27 +1079,25 @@ public static Geometry makePolygon(Geometry shell, Geometry[] holes) { } } - public static Geometry makepolygonWithSRID(Geometry lineString, Integer srid) { + public static Geometry makepolygonWithSRID (Geometry lineString, Integer srid){ Geometry geom = makePolygon(lineString, null); - if(geom != null) { + if (geom != null) { geom.setSRID(srid); } return geom; } - public static Geometry createMultiGeometry(Geometry[] geometries) { - if (geometries.length > 1){ + public static Geometry createMultiGeometry (Geometry[]geometries){ + if (geometries.length > 1) { return GEOMETRY_FACTORY.buildGeometry(Arrays.asList(geometries)); - } - else if(geometries.length==1){ + } else if (geometries.length == 1) { return createMultiGeometryFromOneElement(geometries[0]); - } - else{ + } else { return GEOMETRY_FACTORY.createGeometryCollection(); } } - public static Geometry collectionExtract(Geometry geometry, Integer geomType) { + public static Geometry collectionExtract (Geometry geometry, Integer geomType){ if (geomType == null) { return collectionExtract(geometry); } @@ -1128,7 +1126,7 @@ public static Geometry collectionExtract(Geometry geometry, Integer geomType) { return Functions.createMultiGeometry(geometries.toArray(new Geometry[0])); } - public static Geometry collectionExtract(Geometry geometry) { + public static Geometry collectionExtract (Geometry geometry){ List geometries = GeomUtils.extractGeometryCollection(geometry); Polygon[] polygons = geometries.stream().filter(g -> g instanceof Polygon).toArray(Polygon[]::new); if (polygons.length > 0) { @@ -1149,20 +1147,20 @@ public static Geometry collectionExtract(Geometry geometry) { // ported from https://github.com/postgis/postgis/blob/f6ed58d1fdc865d55d348212d02c11a10aeb2b30/liblwgeom/lwgeom_median.c // geometry ST_GeometricMedian ( geometry g , float8 tolerance , int max_iter , boolean fail_if_not_converged ); - private static double distance3d(Coordinate p1, Coordinate p2) { + private static double distance3d (Coordinate p1, Coordinate p2){ double dx = p2.x - p1.x; double dy = p2.y - p1.y; double dz = p2.z - p1.z; return Math.sqrt(dx * dx + dy * dy + dz * dz); } - private static void distances(Coordinate curr, Coordinate[] points, double[] distances) { - for(int i = 0; i < points.length; i++) { + private static void distances (Coordinate curr, Coordinate[]points,double[] distances){ + for (int i = 0; i < points.length; i++) { distances[i] = distance3d(curr, points[i]); } } - private static double iteratePoints(Coordinate curr, Coordinate[] points, double[] distances) { + private static double iteratePoints (Coordinate curr, Coordinate[]points,double[] distances){ Coordinate next = new Coordinate(0, 0, 0); double delta = 0; double denom = 0; @@ -1218,13 +1216,13 @@ private static double iteratePoints(Coordinate curr, Coordinate[] points, double dz += (coordinate.z - curr.z) / distance; } } - double dSqr = Math.sqrt(dx*dx + dy*dy + dz*dz); + double dSqr = Math.sqrt(dx * dx + dy * dy + dz * dz); /* Avoid division by zero if the intermediate point is the median */ if (dSqr > DBL_EPSILON) { double rInv = Math.max(0, 1.0 / dSqr); - next.x = (1.0 - rInv)*next.x + rInv*curr.x; - next.y = (1.0 - rInv)*next.y + rInv*curr.y; - next.z = (1.0 - rInv)*next.z + rInv*curr.z; + next.x = (1.0 - rInv) * next.x + rInv * curr.x; + next.y = (1.0 - rInv) * next.y + rInv * curr.y; + next.z = (1.0 - rInv) * next.z + rInv * curr.z; } } delta = distance3d(curr, next); @@ -1235,7 +1233,7 @@ private static double iteratePoints(Coordinate curr, Coordinate[] points, double return delta; } - private static Coordinate initGuess(Coordinate[] points) { + private static Coordinate initGuess (Coordinate[]points){ Coordinate guess = new Coordinate(0, 0, 0); for (Coordinate point : points) { guess.x += point.x / points.length; @@ -1245,22 +1243,22 @@ private static Coordinate initGuess(Coordinate[] points) { return guess; } - private static Coordinate[] extractCoordinates(Geometry geometry) { + private static Coordinate[] extractCoordinates (Geometry geometry){ Coordinate[] points = geometry.getCoordinates(); - if(points.length == 0) + if (points.length == 0) return points; Coordinate[] coordinates = new Coordinate[points.length]; - for(int i = 0; i < points.length; i++) { + for (int i = 0; i < points.length; i++) { boolean is3d = !Double.isNaN(points[i].z); coordinates[i] = points[i].copy(); - if(!is3d) + if (!is3d) coordinates[i].z = 0.0; } return coordinates; } - public static int numPoints(Geometry geometry) throws Exception { + public static int numPoints (Geometry geometry) throws Exception { String geometryType = geometry.getGeometryType(); if (!(Geometry.TYPENAME_LINESTRING.equalsIgnoreCase(geometryType))) { throw new IllegalArgumentException("Unsupported geometry type: " + geometryType + ", only LineString geometry is supported."); @@ -1268,15 +1266,15 @@ public static int numPoints(Geometry geometry) throws Exception { return geometry.getNumPoints(); } - public static Geometry force3D(Geometry geometry, double zValue) { + public static Geometry force3D (Geometry geometry,double zValue){ return GeomUtils.get3DGeom(geometry, zValue); } - public static Geometry force3D(Geometry geometry) { + public static Geometry force3D (Geometry geometry){ return GeomUtils.get3DGeom(geometry, 0.0); } - public static Integer nRings(Geometry geometry) throws Exception { + public static Integer nRings (Geometry geometry) throws Exception { String geometryType = geometry.getGeometryType(); if (!(geometry instanceof Polygon || geometry instanceof MultiPolygon)) { throw new IllegalArgumentException("Unsupported geometry type: " + geometryType + ", only Polygon or MultiPolygon geometries are supported."); @@ -1285,7 +1283,7 @@ public static Integer nRings(Geometry geometry) throws Exception { if (geometry instanceof Polygon) { Polygon polygon = (Polygon) geometry; numRings = GeomUtils.getPolygonNumRings(polygon); - }else { + } else { MultiPolygon multiPolygon = (MultiPolygon) geometry; int numPolygons = multiPolygon.getNumGeometries(); for (int i = 0; i < numPolygons; i++) { @@ -1296,75 +1294,78 @@ public static Integer nRings(Geometry geometry) throws Exception { return numRings; } - public static Geometry translate(Geometry geometry, double deltaX, double deltaY, double deltaZ) { + public static Geometry translate (Geometry geometry,double deltaX, double deltaY, double deltaZ){ if (!geometry.isEmpty()) { GeomUtils.translateGeom(geometry, deltaX, deltaY, deltaZ); } return geometry; } - public static Geometry translate(Geometry geometry, double deltaX, double deltaY) { + public static Geometry translate (Geometry geometry,double deltaX, double deltaY){ if (!geometry.isEmpty()) { GeomUtils.translateGeom(geometry, deltaX, deltaY, 0.0); } return geometry; } - public static Geometry affine(Geometry geometry, double a, double b, double c, double d, double e, double f, double g, double h, double i, double xOff, double yOff, - double zOff) { + public static Geometry affine (Geometry geometry,double a, double b, double c, double d, double e, double f, + double g, double h, double i, double xOff, double yOff, + double zOff){ if (!geometry.isEmpty()) { GeomUtils.affineGeom(geometry, a, b, c, d, e, f, g, h, i, xOff, yOff, zOff); } return geometry; } - public static Geometry affine(Geometry geometry, double a, double b, double d, double e, double xOff, double yOff) { + public static Geometry affine (Geometry geometry,double a, double b, double d, double e, double xOff, + double yOff){ if (!geometry.isEmpty()) { GeomUtils.affineGeom(geometry, a, b, null, d, e, null, null, null, null, xOff, yOff, null); } return geometry; } - public static Geometry geometricMedian(Geometry geometry, double tolerance, int maxIter, boolean failIfNotConverged) throws Exception { + public static Geometry geometricMedian (Geometry geometry,double tolerance, int maxIter, + boolean failIfNotConverged) throws Exception { String geometryType = geometry.getGeometryType(); - if(!(Geometry.TYPENAME_POINT.equals(geometryType) || Geometry.TYPENAME_MULTIPOINT.equals(geometryType))) { + if (!(Geometry.TYPENAME_POINT.equals(geometryType) || Geometry.TYPENAME_MULTIPOINT.equals(geometryType))) { throw new Exception("Unsupported geometry type: " + geometryType); } Coordinate[] coordinates = extractCoordinates(geometry); - if(coordinates.length == 0) + if (coordinates.length == 0) return new Point(null, GEOMETRY_FACTORY); Coordinate median = initGuess(coordinates); double delta = Double.MAX_VALUE; double[] distances = new double[coordinates.length]; // preallocate to reduce gc pressure for large iterations - for(int i = 0; i < maxIter && delta > tolerance; i++) + for (int i = 0; i < maxIter && delta > tolerance; i++) delta = iteratePoints(median, coordinates, distances); if (failIfNotConverged && delta > tolerance) throw new Exception(String.format("Median failed to converge within %.1E after %d iterations.", tolerance, maxIter)); boolean is3d = !Double.isNaN(geometry.getCoordinate().z); - if(!is3d) + if (!is3d) median.z = Double.NaN; Point point = new Point(new CoordinateArraySequence(new Coordinate[]{median}), GEOMETRY_FACTORY); point.setSRID(geometry.getSRID()); return point; } - public static Geometry geometricMedian(Geometry geometry, double tolerance, int maxIter) throws Exception { + public static Geometry geometricMedian (Geometry geometry,double tolerance, int maxIter) throws Exception { return geometricMedian(geometry, tolerance, maxIter, false); } - public static Geometry geometricMedian(Geometry geometry, double tolerance) throws Exception { + public static Geometry geometricMedian (Geometry geometry,double tolerance) throws Exception { return geometricMedian(geometry, tolerance, DEFAULT_MAX_ITER, false); } - public static Geometry geometricMedian(Geometry geometry) throws Exception { + public static Geometry geometricMedian (Geometry geometry) throws Exception { return geometricMedian(geometry, DEFAULT_TOLERANCE, DEFAULT_MAX_ITER, false); } - public static double frechetDistance(Geometry g1, Geometry g2) { + public static double frechetDistance (Geometry g1, Geometry g2){ return GeomUtils.getFrechetDistance(g1, g2); } - public static boolean isCollection(Geometry geometry) { + public static boolean isCollection (Geometry geometry){ String geoType = geometry.getGeometryType(); return Geometry.TYPENAME_GEOMETRYCOLLECTION.equalsIgnoreCase(geoType) || Geometry.TYPENAME_MULTIPOINT.equalsIgnoreCase(geoType) || @@ -1372,10 +1373,10 @@ public static boolean isCollection(Geometry geometry) { Geometry.TYPENAME_MULTILINESTRING.equalsIgnoreCase(geoType); } - public static Geometry boundingDiagonal(Geometry geometry) { + public static Geometry boundingDiagonal (Geometry geometry){ if (geometry.isEmpty()) { return GEOMETRY_FACTORY.createLineString(); - }else { + } else { Double startX = null, startY = null, startZ = null, endX = null, endY = null, endZ = null; boolean is3d = !Double.isNaN(geometry.getCoordinate().z); @@ -1396,7 +1397,7 @@ public static Geometry boundingDiagonal(Geometry geometry) { if (is3d) { startCoordinate = new Coordinate(startX, startY, startZ); endCoordinate = new Coordinate(endX, endY, endZ); - }else { + } else { startCoordinate = new Coordinate(startX, startY); endCoordinate = new Coordinate(endX, endY); } @@ -1404,23 +1405,29 @@ public static Geometry boundingDiagonal(Geometry geometry) { } } - public static double angle(Geometry point1, Geometry point2, Geometry point3, Geometry point4) throws IllegalArgumentException { + public static double angle (Geometry point1, Geometry point2, Geometry point3, Geometry point4) throws IllegalArgumentException { if (point3 == null && point4 == null) return Functions.angle(point1, point2); else if (point4 == null) return Functions.angle(point1, point2, point3); - if (GeomUtils.isAnyGeomEmpty(point1, point2, point3, point4)) throw new IllegalArgumentException("ST_Angle cannot support empty geometries."); - if (!(point1 instanceof Point && point2 instanceof Point && point3 instanceof Point && point4 instanceof Point)) throw new IllegalArgumentException("ST_Angle supports either only POINT or only LINESTRING geometries."); + if (GeomUtils.isAnyGeomEmpty(point1, point2, point3, point4)) + throw new IllegalArgumentException("ST_Angle cannot support empty geometries."); + if (!(point1 instanceof Point && point2 instanceof Point && point3 instanceof Point && point4 instanceof Point)) + throw new IllegalArgumentException("ST_Angle supports either only POINT or only LINESTRING geometries."); return GeomUtils.calcAngle(point1.getCoordinate(), point2.getCoordinate(), point3.getCoordinate(), point4.getCoordinate()); } - public static double angle(Geometry point1, Geometry point2, Geometry point3) throws IllegalArgumentException { - if (GeomUtils.isAnyGeomEmpty(point1, point2, point3)) throw new IllegalArgumentException("ST_Angle cannot support empty geometries."); - if (!(point1 instanceof Point && point2 instanceof Point && point3 instanceof Point)) throw new IllegalArgumentException("ST_Angle supports either only POINT or only LINESTRING geometries."); + public static double angle (Geometry point1, Geometry point2, Geometry point3) throws IllegalArgumentException { + if (GeomUtils.isAnyGeomEmpty(point1, point2, point3)) + throw new IllegalArgumentException("ST_Angle cannot support empty geometries."); + if (!(point1 instanceof Point && point2 instanceof Point && point3 instanceof Point)) + throw new IllegalArgumentException("ST_Angle supports either only POINT or only LINESTRING geometries."); return GeomUtils.calcAngle(point2.getCoordinate(), point1.getCoordinate(), point2.getCoordinate(), point3.getCoordinate()); } - public static double angle(Geometry line1, Geometry line2) throws IllegalArgumentException { - if (GeomUtils.isAnyGeomEmpty(line1, line2)) throw new IllegalArgumentException("ST_Angle cannot support empty geometries."); - if (!(line1 instanceof LineString && line2 instanceof LineString)) throw new IllegalArgumentException("ST_Angle supports either only POINT or only LINESTRING geometries."); + public static double angle (Geometry line1, Geometry line2) throws IllegalArgumentException { + if (GeomUtils.isAnyGeomEmpty(line1, line2)) + throw new IllegalArgumentException("ST_Angle cannot support empty geometries."); + if (!(line1 instanceof LineString && line2 instanceof LineString)) + throw new IllegalArgumentException("ST_Angle supports either only POINT or only LINESTRING geometries."); Coordinate[] startEndLine1 = GeomUtils.getStartEndCoordinates(line1); Coordinate[] startEndLine2 = GeomUtils.getStartEndCoordinates(line2); assert startEndLine1 != null; @@ -1428,23 +1435,23 @@ public static double angle(Geometry line1, Geometry line2) throws IllegalArgumen return GeomUtils.calcAngle(startEndLine1[0], startEndLine1[1], startEndLine2[0], startEndLine2[1]); } - public static double degrees(double angleInRadian) { + public static double degrees ( double angleInRadian){ return GeomUtils.toDegrees(angleInRadian); } - public static Double hausdorffDistance(Geometry g1, Geometry g2, double densityFrac) { + public static Double hausdorffDistance (Geometry g1, Geometry g2,double densityFrac){ return GeomUtils.getHausdorffDistance(g1, g2, densityFrac); } - public static Double hausdorffDistance(Geometry g1, Geometry g2) { + public static Double hausdorffDistance (Geometry g1, Geometry g2){ return GeomUtils.getHausdorffDistance(g1, g2, -1); } - public static String isValidReason(Geometry geom) { + public static String isValidReason (Geometry geom){ return isValidReason(geom, OGC_SFS_VALIDITY); } - public static String isValidReason(Geometry geom, int flag) { + public static String isValidReason (Geometry geom,int flag){ IsValidOp isValidOp = new IsValidOp(geom); // Set the validity model based on flags From b73d2adb31d3a1e156cbd50297036042d76b679d Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Wed, 28 Feb 2024 22:22:46 -0500 Subject: [PATCH 41/54] Fix snowflake error --- .../java/org/apache/sedona/snowflake/snowsql/UDFs.java | 8 +++----- .../java/org/apache/sedona/snowflake/snowsql/UDFsV2.java | 8 +++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFs.java b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFs.java index 14973a73b1..10685687e2 100644 --- a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFs.java +++ b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFs.java @@ -25,8 +25,6 @@ import org.locationtech.jts.geom.Point; import org.locationtech.jts.io.ParseException; import org.locationtech.jts.io.WKBWriter; -import org.opengis.referencing.FactoryException; -import org.opengis.referencing.operation.TransformException; import org.xml.sax.SAXException; import javax.xml.parsers.ParserConfigurationException; @@ -161,7 +159,7 @@ public static byte[] ST_BoundingDiagonal(byte[] geometry) { } @UDFAnnotations.ParamMeta(argNames = {"geometry", "radius"}) - public static byte[] ST_Buffer(byte[] geometry, double radius) throws FactoryException, TransformException { + public static byte[] ST_Buffer(byte[] geometry, double radius) throws IllegalArgumentException { return GeometrySerde.serialize( Functions.buffer( GeometrySerde.deserialize(geometry), @@ -171,7 +169,7 @@ public static byte[] ST_Buffer(byte[] geometry, double radius) throws FactoryExc } @UDFAnnotations.ParamMeta(argNames = {"geometry", "radius", "useSpheroid"}) - public static byte[] ST_Buffer(byte[] geometry, double radius, boolean useSpheroid) throws FactoryException, TransformException { + public static byte[] ST_Buffer(byte[] geometry, double radius, boolean useSpheroid) throws IllegalArgumentException { return GeometrySerde.serialize( Functions.buffer( GeometrySerde.deserialize(geometry), @@ -182,7 +180,7 @@ public static byte[] ST_Buffer(byte[] geometry, double radius, boolean useSphero } @UDFAnnotations.ParamMeta(argNames = {"geometry", "radius", "useSpheroid", "parameters"}) - public static byte[] ST_Buffer(byte[] geometry, double radius, boolean useSpheroid, String parameters) throws FactoryException, TransformException { + public static byte[] ST_Buffer(byte[] geometry, double radius, boolean useSpheroid, String parameters) throws IllegalArgumentException { return GeometrySerde.serialize( Functions.buffer( GeometrySerde.deserialize(geometry), diff --git a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java index b5993e691f..be6c09c6bc 100644 --- a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java +++ b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java @@ -21,8 +21,6 @@ import org.apache.sedona.snowflake.snowsql.annotations.UDFAnnotations; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.Point; -import org.opengis.referencing.FactoryException; -import org.opengis.referencing.operation.TransformException; import java.io.IOException; @@ -190,7 +188,7 @@ public static String ST_ShiftLongitude(String geometry) { } @UDFAnnotations.ParamMeta(argNames = {"geometry", "radius"}, argTypes = {"Geometry", "double"}, returnTypes = "Geometry") - public static String ST_Buffer(String geometry, double radius) throws FactoryException, TransformException { + public static String ST_Buffer(String geometry, double radius) throws IllegalArgumentException { return GeometrySerde.serGeoJson( Functions.buffer( GeometrySerde.deserGeoJson(geometry), @@ -200,7 +198,7 @@ public static String ST_Buffer(String geometry, double radius) throws FactoryExc } @UDFAnnotations.ParamMeta(argNames = {"geometry", "radius", "useSpheroid"}, argTypes = {"Geometry", "double", "boolean"}, returnTypes = "Geometry") - public static String ST_Buffer(String geometry, double radius, boolean useSpheroid) throws FactoryException, TransformException { + public static String ST_Buffer(String geometry, double radius, boolean useSpheroid) throws IllegalArgumentException { return GeometrySerde.serGeoJson( Functions.buffer( GeometrySerde.deserGeoJson(geometry), @@ -211,7 +209,7 @@ public static String ST_Buffer(String geometry, double radius, boolean useSphero } @UDFAnnotations.ParamMeta(argNames = {"geometry", "radius", "useSpheroid", "parameters"}, argTypes = {"Geometry", "double", "boolean", "String"}, returnTypes = "Geometry") - public static String ST_Buffer(String geometry, double radius, boolean useSpheroid, String parameters) throws FactoryException, TransformException { + public static String ST_Buffer(String geometry, double radius, boolean useSpheroid, String parameters) throws IllegalArgumentException { return GeometrySerde.serGeoJson( Functions.buffer( GeometrySerde.deserGeoJson(geometry), From 0d9071d015d14b80f8abcb11bccb9f89e5639496 Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Wed, 28 Feb 2024 22:55:52 -0500 Subject: [PATCH 42/54] Update snowflake test --- .../org/apache/sedona/snowflake/snowsql/TestFunctions.java | 4 ++-- .../org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java index bde1024e1b..d25aa4b127 100644 --- a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java +++ b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java @@ -192,8 +192,8 @@ public void test_ST_Buffer() { ); registerUDF("ST_Buffer", byte[].class, double.class, boolean.class); verifySqlSingleRes( - "select sedona.ST_AsText(sedona.ST_Buffer(sedona.ST_GeomFromText('LINESTRING(0.05 15, -0.05 15)'), 10000, true))", - "POLYGON ((-0.0499827380800979 14.90970764615526, -0.0680979441919658 14.911439289332314, -0.0855181036092048 14.916572765433372, -0.1015745790932317 14.924910889089798, -0.1156509498611482 14.936133480337729, -0.1272066059871122 14.949809631917493, -0.1357974717923214 14.965414213210906, -0.1410930699533803 14.982347984759812, -0.1428892711961725 14.9999605602647, -0.1411162325232574 15.017575343788987, -0.1358412050267848 15.034515492689351, -0.1272660846886664 15.050129914661689, -0.1157197795798512 15.063818302233363, -0.101645667253286 15.07505424080832, -0.0855846090787039 15.083405496399754, -0.068154165764569 15.088550694439519, -0.0500248124892859 15.090291738028835, 0.0500174017959422 15.0902994910598, 0.0681488768495457 15.08856105434859, 0.0855824246963393 15.08341775106032, 0.1016472676386697 15.075067339333813, 0.1157254254859488 15.06383098366898, 0.1272755583302725 15.050140871793444, 0.1358538186573691 15.034523547105394, 0.1411309042551145 15.017579606456339, 0.1429046575925539 14.999960553897546, 0.1411077364057122 14.982343709853023, 0.1358100760363371 14.965406147948107, 0.1272160681615492 14.949798665976873, 0.1156565845591405 14.936120792425546, 0.1015761705812689 14.9248977863495, 0.0855159139181673 14.916560508451733, 0.0680926538252635 14.911428928455091, 0.0499753291173759 14.909699892939221, -0.0499827380800979 14.90970764615526))" + "select sedona.ST_AsText(sedona.ST_ReducePrecision(sedona.ST_Buffer(sedona.ST_GeomFromText('LINESTRING(0.05 15, -0.05 15)'), 10000, true)), 8)", + "POLYGON ((-0.06809794 14.91143929, -0.0855181 14.91657277, -0.10157458 14.92491089, -0.11565095 14.93613348, -0.12720661 14.94980963, -0.13579747 14.96541421, -0.14109307 14.98234798, -0.14288927 14.99996056, -0.14111623 15.01757534, -0.13584121 15.03451549, -0.12726608 15.05012991, -0.11571978 15.0638183, -0.10164567 15.07505424, -0.08558461 15.0834055, -0.06815417 15.08855069, -0.05002481 15.09029174, 0.0500174 15.09029949, 0.06814888 15.08856105, 0.08558242 15.08341775, 0.10164727 15.07506734, 0.11572543 15.06383098, 0.12727556 15.05014087, 0.13585382 15.03452355, 0.1411309 15.01757961, 0.14290466 14.99996055, 0.14110774 14.98234371, 0.13581008 14.96540615, 0.12721607 14.94979867, 0.11565658 14.93612079, 0.10157617 14.92489779, 0.08551591 14.91656051, 0.06809265 14.91142893, 0.04997533 14.90969989, -0.04998274 14.90970765, -0.06809794 14.91143929))" ); } diff --git a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java index 7bf5e7b219..8d876b5ed6 100644 --- a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java +++ b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java @@ -215,8 +215,8 @@ public void test_ST_Buffer() { ); registerUDFV2("ST_Buffer", String.class, double.class, boolean.class); verifySqlSingleRes( - "select ST_AsText(sedona.ST_Buffer(ST_GeometryFromWKT('LINESTRING(0.05 15, -0.05 15)'), 10000, true))", - "POLYGON ((-0.0499827380800979 14.90970764615526, -0.0680979441919658 14.911439289332314, -0.0855181036092048 14.916572765433372, -0.1015745790932317 14.924910889089798, -0.1156509498611482 14.936133480337729, -0.1272066059871122 14.949809631917493, -0.1357974717923214 14.965414213210906, -0.1410930699533803 14.982347984759812, -0.1428892711961725 14.9999605602647, -0.1411162325232574 15.017575343788987, -0.1358412050267848 15.034515492689351, -0.1272660846886664 15.050129914661689, -0.1157197795798512 15.063818302233363, -0.101645667253286 15.07505424080832, -0.0855846090787039 15.083405496399754, -0.068154165764569 15.088550694439519, -0.0500248124892859 15.090291738028835, 0.0500174017959422 15.0902994910598, 0.0681488768495457 15.08856105434859, 0.0855824246963393 15.08341775106032, 0.1016472676386697 15.075067339333813, 0.1157254254859488 15.06383098366898, 0.1272755583302725 15.050140871793444, 0.1358538186573691 15.034523547105394, 0.1411309042551145 15.017579606456339, 0.1429046575925539 14.999960553897546, 0.1411077364057122 14.982343709853023, 0.1358100760363371 14.965406147948107, 0.1272160681615492 14.949798665976873, 0.1156565845591405 14.936120792425546, 0.1015761705812689 14.9248977863495, 0.0855159139181673 14.916560508451733, 0.0680926538252635 14.911428928455091, 0.0499753291173759 14.909699892939221, -0.0499827380800979 14.90970764615526))" + "select sedona.ST_AsText(sedona.ST_ReducePrecision(sedona.ST_Buffer(sedona.ST_GeomFromText('LINESTRING(0.05 15, -0.05 15)'), 10000, true)), 8)", + "POLYGON ((-0.06809794 14.91143929, -0.0855181 14.91657277, -0.10157458 14.92491089, -0.11565095 14.93613348, -0.12720661 14.94980963, -0.13579747 14.96541421, -0.14109307 14.98234798, -0.14288927 14.99996056, -0.14111623 15.01757534, -0.13584121 15.03451549, -0.12726608 15.05012991, -0.11571978 15.0638183, -0.10164567 15.07505424, -0.08558461 15.0834055, -0.06815417 15.08855069, -0.05002481 15.09029174, 0.0500174 15.09029949, 0.06814888 15.08856105, 0.08558242 15.08341775, 0.10164727 15.07506734, 0.11572543 15.06383098, 0.12727556 15.05014087, 0.13585382 15.03452355, 0.1411309 15.01757961, 0.14290466 14.99996055, 0.14110774 14.98234371, 0.13581008 14.96540615, 0.12721607 14.94979867, 0.11565658 14.93612079, 0.10157617 14.92489779, 0.08551591 14.91656051, 0.06809265 14.91142893, 0.04997533 14.90969989, -0.04998274 14.90970765, -0.06809794 14.91143929))" ); } @Test From 3282d5e704be08348db7a1883e4f1f81b6c226ec Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Wed, 28 Feb 2024 23:14:17 -0500 Subject: [PATCH 43/54] fix snowflake test --- .../java/org/apache/sedona/snowflake/snowsql/TestFunctions.java | 2 +- .../org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java index d25aa4b127..4d06018778 100644 --- a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java +++ b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java @@ -192,7 +192,7 @@ public void test_ST_Buffer() { ); registerUDF("ST_Buffer", byte[].class, double.class, boolean.class); verifySqlSingleRes( - "select sedona.ST_AsText(sedona.ST_ReducePrecision(sedona.ST_Buffer(sedona.ST_GeomFromText('LINESTRING(0.05 15, -0.05 15)'), 10000, true)), 8)", + "select sedona.ST_AsText(sedona.ST_ReducePrecision(sedona.ST_Buffer(sedona.ST_GeomFromText('LINESTRING(0.05 15, -0.05 15)'), 10000, true), 8))", "POLYGON ((-0.06809794 14.91143929, -0.0855181 14.91657277, -0.10157458 14.92491089, -0.11565095 14.93613348, -0.12720661 14.94980963, -0.13579747 14.96541421, -0.14109307 14.98234798, -0.14288927 14.99996056, -0.14111623 15.01757534, -0.13584121 15.03451549, -0.12726608 15.05012991, -0.11571978 15.0638183, -0.10164567 15.07505424, -0.08558461 15.0834055, -0.06815417 15.08855069, -0.05002481 15.09029174, 0.0500174 15.09029949, 0.06814888 15.08856105, 0.08558242 15.08341775, 0.10164727 15.07506734, 0.11572543 15.06383098, 0.12727556 15.05014087, 0.13585382 15.03452355, 0.1411309 15.01757961, 0.14290466 14.99996055, 0.14110774 14.98234371, 0.13581008 14.96540615, 0.12721607 14.94979867, 0.11565658 14.93612079, 0.10157617 14.92489779, 0.08551591 14.91656051, 0.06809265 14.91142893, 0.04997533 14.90969989, -0.04998274 14.90970765, -0.06809794 14.91143929))" ); } diff --git a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java index 8d876b5ed6..3cdffa819a 100644 --- a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java +++ b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java @@ -215,7 +215,7 @@ public void test_ST_Buffer() { ); registerUDFV2("ST_Buffer", String.class, double.class, boolean.class); verifySqlSingleRes( - "select sedona.ST_AsText(sedona.ST_ReducePrecision(sedona.ST_Buffer(sedona.ST_GeomFromText('LINESTRING(0.05 15, -0.05 15)'), 10000, true)), 8)", + "select sedona.ST_AsText(sedona.ST_ReducePrecision(sedona.ST_Buffer(sedona.ST_GeomFromText('LINESTRING(0.05 15, -0.05 15)'), 10000, true), 8))", "POLYGON ((-0.06809794 14.91143929, -0.0855181 14.91657277, -0.10157458 14.92491089, -0.11565095 14.93613348, -0.12720661 14.94980963, -0.13579747 14.96541421, -0.14109307 14.98234798, -0.14288927 14.99996056, -0.14111623 15.01757534, -0.13584121 15.03451549, -0.12726608 15.05012991, -0.11571978 15.0638183, -0.10164567 15.07505424, -0.08558461 15.0834055, -0.06815417 15.08855069, -0.05002481 15.09029174, 0.0500174 15.09029949, 0.06814888 15.08856105, 0.08558242 15.08341775, 0.10164727 15.07506734, 0.11572543 15.06383098, 0.12727556 15.05014087, 0.13585382 15.03452355, 0.1411309 15.01757961, 0.14290466 14.99996055, 0.14110774 14.98234371, 0.13581008 14.96540615, 0.12721607 14.94979867, 0.11565658 14.93612079, 0.10157617 14.92489779, 0.08551591 14.91656051, 0.06809265 14.91142893, 0.04997533 14.90969989, -0.04998274 14.90970765, -0.06809794 14.91143929))" ); } From 05815bd923bbbcca8d6a1c197cc84db3dda42043 Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Wed, 28 Feb 2024 23:52:57 -0500 Subject: [PATCH 44/54] fix snowflake test --- .../java/org/apache/sedona/snowflake/snowsql/TestFunctions.java | 2 +- .../org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java index 4d06018778..8aff01e687 100644 --- a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java +++ b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java @@ -192,7 +192,7 @@ public void test_ST_Buffer() { ); registerUDF("ST_Buffer", byte[].class, double.class, boolean.class); verifySqlSingleRes( - "select sedona.ST_AsText(sedona.ST_ReducePrecision(sedona.ST_Buffer(sedona.ST_GeomFromText('LINESTRING(0.05 15, -0.05 15)'), 10000, true), 8))", + "select ST_AsText(ST_ReducePrecision(ST_GeomFromText(sedona.ST_AsText(sedona.ST_Buffer(sedona.ST_GeomFromText('LINESTRING(0.05 15, -0.05 15)'), 10000, true))), 8))", "POLYGON ((-0.06809794 14.91143929, -0.0855181 14.91657277, -0.10157458 14.92491089, -0.11565095 14.93613348, -0.12720661 14.94980963, -0.13579747 14.96541421, -0.14109307 14.98234798, -0.14288927 14.99996056, -0.14111623 15.01757534, -0.13584121 15.03451549, -0.12726608 15.05012991, -0.11571978 15.0638183, -0.10164567 15.07505424, -0.08558461 15.0834055, -0.06815417 15.08855069, -0.05002481 15.09029174, 0.0500174 15.09029949, 0.06814888 15.08856105, 0.08558242 15.08341775, 0.10164727 15.07506734, 0.11572543 15.06383098, 0.12727556 15.05014087, 0.13585382 15.03452355, 0.1411309 15.01757961, 0.14290466 14.99996055, 0.14110774 14.98234371, 0.13581008 14.96540615, 0.12721607 14.94979867, 0.11565658 14.93612079, 0.10157617 14.92489779, 0.08551591 14.91656051, 0.06809265 14.91142893, 0.04997533 14.90969989, -0.04998274 14.90970765, -0.06809794 14.91143929))" ); } diff --git a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java index 3cdffa819a..866a51c9d5 100644 --- a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java +++ b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java @@ -215,7 +215,7 @@ public void test_ST_Buffer() { ); registerUDFV2("ST_Buffer", String.class, double.class, boolean.class); verifySqlSingleRes( - "select sedona.ST_AsText(sedona.ST_ReducePrecision(sedona.ST_Buffer(sedona.ST_GeomFromText('LINESTRING(0.05 15, -0.05 15)'), 10000, true), 8))", + "select ST_AsText(ST_ReducePrecision(ST_GeomFromText(sedona.ST_AsText(sedona.ST_Buffer(sedona.ST_GeomFromText('LINESTRING(0.05 15, -0.05 15)'), 10000, true))), 8))", "POLYGON ((-0.06809794 14.91143929, -0.0855181 14.91657277, -0.10157458 14.92491089, -0.11565095 14.93613348, -0.12720661 14.94980963, -0.13579747 14.96541421, -0.14109307 14.98234798, -0.14288927 14.99996056, -0.14111623 15.01757534, -0.13584121 15.03451549, -0.12726608 15.05012991, -0.11571978 15.0638183, -0.10164567 15.07505424, -0.08558461 15.0834055, -0.06815417 15.08855069, -0.05002481 15.09029174, 0.0500174 15.09029949, 0.06814888 15.08856105, 0.08558242 15.08341775, 0.10164727 15.07506734, 0.11572543 15.06383098, 0.12727556 15.05014087, 0.13585382 15.03452355, 0.1411309 15.01757961, 0.14290466 14.99996055, 0.14110774 14.98234371, 0.13581008 14.96540615, 0.12721607 14.94979867, 0.11565658 14.93612079, 0.10157617 14.92489779, 0.08551591 14.91656051, 0.06809265 14.91142893, 0.04997533 14.90969989, -0.04998274 14.90970765, -0.06809794 14.91143929))" ); } From 48b9aaf6ca1da22181f7d578627f5880a1e891a0 Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Thu, 29 Feb 2024 00:11:59 -0500 Subject: [PATCH 45/54] fix snowflake test --- .../java/org/apache/sedona/snowflake/snowsql/TestFunctions.java | 2 +- .../org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java index 8aff01e687..234418e02a 100644 --- a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java +++ b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java @@ -193,7 +193,7 @@ public void test_ST_Buffer() { registerUDF("ST_Buffer", byte[].class, double.class, boolean.class); verifySqlSingleRes( "select ST_AsText(ST_ReducePrecision(ST_GeomFromText(sedona.ST_AsText(sedona.ST_Buffer(sedona.ST_GeomFromText('LINESTRING(0.05 15, -0.05 15)'), 10000, true))), 8))", - "POLYGON ((-0.06809794 14.91143929, -0.0855181 14.91657277, -0.10157458 14.92491089, -0.11565095 14.93613348, -0.12720661 14.94980963, -0.13579747 14.96541421, -0.14109307 14.98234798, -0.14288927 14.99996056, -0.14111623 15.01757534, -0.13584121 15.03451549, -0.12726608 15.05012991, -0.11571978 15.0638183, -0.10164567 15.07505424, -0.08558461 15.0834055, -0.06815417 15.08855069, -0.05002481 15.09029174, 0.0500174 15.09029949, 0.06814888 15.08856105, 0.08558242 15.08341775, 0.10164727 15.07506734, 0.11572543 15.06383098, 0.12727556 15.05014087, 0.13585382 15.03452355, 0.1411309 15.01757961, 0.14290466 14.99996055, 0.14110774 14.98234371, 0.13581008 14.96540615, 0.12721607 14.94979867, 0.11565658 14.93612079, 0.10157617 14.92489779, 0.08551591 14.91656051, 0.06809265 14.91142893, 0.04997533 14.90969989, -0.04998274 14.90970765, -0.06809794 14.91143929))" + "POLYGON((-0.06809794 14.91143929, -0.0855181 14.91657277, -0.10157458 14.92491089, -0.11565095 14.93613348, -0.12720661 14.94980963, -0.13579747 14.96541421, -0.14109307 14.98234798, -0.14288927 14.99996056, -0.14111623 15.01757534, -0.13584121 15.03451549, -0.12726608 15.05012991, -0.11571978 15.0638183, -0.10164567 15.07505424, -0.08558461 15.0834055, -0.06815417 15.08855069, -0.05002481 15.09029174, 0.0500174 15.09029949, 0.06814888 15.08856105, 0.08558242 15.08341775, 0.10164727 15.07506734, 0.11572543 15.06383098, 0.12727556 15.05014087, 0.13585382 15.03452355, 0.1411309 15.01757961, 0.14290466 14.99996055, 0.14110774 14.98234371, 0.13581008 14.96540615, 0.12721607 14.94979867, 0.11565658 14.93612079, 0.10157617 14.92489779, 0.08551591 14.91656051, 0.06809265 14.91142893, 0.04997533 14.90969989, -0.04998274 14.90970765, -0.06809794 14.91143929))" ); } diff --git a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java index 866a51c9d5..0000445736 100644 --- a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java +++ b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java @@ -216,7 +216,7 @@ public void test_ST_Buffer() { registerUDFV2("ST_Buffer", String.class, double.class, boolean.class); verifySqlSingleRes( "select ST_AsText(ST_ReducePrecision(ST_GeomFromText(sedona.ST_AsText(sedona.ST_Buffer(sedona.ST_GeomFromText('LINESTRING(0.05 15, -0.05 15)'), 10000, true))), 8))", - "POLYGON ((-0.06809794 14.91143929, -0.0855181 14.91657277, -0.10157458 14.92491089, -0.11565095 14.93613348, -0.12720661 14.94980963, -0.13579747 14.96541421, -0.14109307 14.98234798, -0.14288927 14.99996056, -0.14111623 15.01757534, -0.13584121 15.03451549, -0.12726608 15.05012991, -0.11571978 15.0638183, -0.10164567 15.07505424, -0.08558461 15.0834055, -0.06815417 15.08855069, -0.05002481 15.09029174, 0.0500174 15.09029949, 0.06814888 15.08856105, 0.08558242 15.08341775, 0.10164727 15.07506734, 0.11572543 15.06383098, 0.12727556 15.05014087, 0.13585382 15.03452355, 0.1411309 15.01757961, 0.14290466 14.99996055, 0.14110774 14.98234371, 0.13581008 14.96540615, 0.12721607 14.94979867, 0.11565658 14.93612079, 0.10157617 14.92489779, 0.08551591 14.91656051, 0.06809265 14.91142893, 0.04997533 14.90969989, -0.04998274 14.90970765, -0.06809794 14.91143929))" + "POLYGON((-0.06809794 14.91143929, -0.0855181 14.91657277, -0.10157458 14.92491089, -0.11565095 14.93613348, -0.12720661 14.94980963, -0.13579747 14.96541421, -0.14109307 14.98234798, -0.14288927 14.99996056, -0.14111623 15.01757534, -0.13584121 15.03451549, -0.12726608 15.05012991, -0.11571978 15.0638183, -0.10164567 15.07505424, -0.08558461 15.0834055, -0.06815417 15.08855069, -0.05002481 15.09029174, 0.0500174 15.09029949, 0.06814888 15.08856105, 0.08558242 15.08341775, 0.10164727 15.07506734, 0.11572543 15.06383098, 0.12727556 15.05014087, 0.13585382 15.03452355, 0.1411309 15.01757961, 0.14290466 14.99996055, 0.14110774 14.98234371, 0.13581008 14.96540615, 0.12721607 14.94979867, 0.11565658 14.93612079, 0.10157617 14.92489779, 0.08551591 14.91656051, 0.06809265 14.91142893, 0.04997533 14.90969989, -0.04998274 14.90970765, -0.06809794 14.91143929))" ); } @Test From 52d2ad8719c1fa2051c1bfdca21c55fb020b77b4 Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Thu, 29 Feb 2024 11:53:00 -0500 Subject: [PATCH 46/54] fix snowflake test --- .../java/org/apache/sedona/snowflake/snowsql/TestFunctions.java | 2 +- .../org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java index 234418e02a..63c54ae993 100644 --- a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java +++ b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java @@ -193,7 +193,7 @@ public void test_ST_Buffer() { registerUDF("ST_Buffer", byte[].class, double.class, boolean.class); verifySqlSingleRes( "select ST_AsText(ST_ReducePrecision(ST_GeomFromText(sedona.ST_AsText(sedona.ST_Buffer(sedona.ST_GeomFromText('LINESTRING(0.05 15, -0.05 15)'), 10000, true))), 8))", - "POLYGON((-0.06809794 14.91143929, -0.0855181 14.91657277, -0.10157458 14.92491089, -0.11565095 14.93613348, -0.12720661 14.94980963, -0.13579747 14.96541421, -0.14109307 14.98234798, -0.14288927 14.99996056, -0.14111623 15.01757534, -0.13584121 15.03451549, -0.12726608 15.05012991, -0.11571978 15.0638183, -0.10164567 15.07505424, -0.08558461 15.0834055, -0.06815417 15.08855069, -0.05002481 15.09029174, 0.0500174 15.09029949, 0.06814888 15.08856105, 0.08558242 15.08341775, 0.10164727 15.07506734, 0.11572543 15.06383098, 0.12727556 15.05014087, 0.13585382 15.03452355, 0.1411309 15.01757961, 0.14290466 14.99996055, 0.14110774 14.98234371, 0.13581008 14.96540615, 0.12721607 14.94979867, 0.11565658 14.93612079, 0.10157617 14.92489779, 0.08551591 14.91656051, 0.06809265 14.91142893, 0.04997533 14.90969989, -0.04998274 14.90970765, -0.06809794 14.91143929))" + "POLYGON((-0.06809794 14.91143929,-0.0855181 14.91657277,-0.10157458 14.92491089,-0.11565095 14.93613348,-0.12720661 14.94980963,-0.13579747 14.96541421,-0.14109307 14.98234798,-0.14288927 14.99996056,-0.14111623 15.01757534,-0.13584121 15.03451549,-0.12726608 15.05012991,-0.11571978 15.0638183,-0.10164567 15.07505424,-0.08558461 15.0834055,-0.06815417 15.08855069,-0.05002481 15.09029174,0.0500174 15.09029949,0.06814888 15.08856105,0.08558242 15.08341775,0.10164727 15.07506734,0.11572543 15.06383098,0.12727556 15.05014087,0.13585382 15.03452355,0.1411309 15.01757961,0.14290466 14.99996055,0.14110774 14.98234371,0.13581008 14.96540615,0.12721607 14.94979867,0.11565658 14.93612079,0.10157617 14.92489779,0.08551591 14.91656051,0.06809265 14.91142893,0.04997533 14.90969989,-0.04998274 14.90970765,-0.06809794 14.91143929))" ); } diff --git a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java index 0000445736..122bc183b2 100644 --- a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java +++ b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java @@ -216,7 +216,7 @@ public void test_ST_Buffer() { registerUDFV2("ST_Buffer", String.class, double.class, boolean.class); verifySqlSingleRes( "select ST_AsText(ST_ReducePrecision(ST_GeomFromText(sedona.ST_AsText(sedona.ST_Buffer(sedona.ST_GeomFromText('LINESTRING(0.05 15, -0.05 15)'), 10000, true))), 8))", - "POLYGON((-0.06809794 14.91143929, -0.0855181 14.91657277, -0.10157458 14.92491089, -0.11565095 14.93613348, -0.12720661 14.94980963, -0.13579747 14.96541421, -0.14109307 14.98234798, -0.14288927 14.99996056, -0.14111623 15.01757534, -0.13584121 15.03451549, -0.12726608 15.05012991, -0.11571978 15.0638183, -0.10164567 15.07505424, -0.08558461 15.0834055, -0.06815417 15.08855069, -0.05002481 15.09029174, 0.0500174 15.09029949, 0.06814888 15.08856105, 0.08558242 15.08341775, 0.10164727 15.07506734, 0.11572543 15.06383098, 0.12727556 15.05014087, 0.13585382 15.03452355, 0.1411309 15.01757961, 0.14290466 14.99996055, 0.14110774 14.98234371, 0.13581008 14.96540615, 0.12721607 14.94979867, 0.11565658 14.93612079, 0.10157617 14.92489779, 0.08551591 14.91656051, 0.06809265 14.91142893, 0.04997533 14.90969989, -0.04998274 14.90970765, -0.06809794 14.91143929))" + "POLYGON((-0.06809794 14.91143929,-0.0855181 14.91657277,-0.10157458 14.92491089,-0.11565095 14.93613348,-0.12720661 14.94980963,-0.13579747 14.96541421,-0.14109307 14.98234798,-0.14288927 14.99996056,-0.14111623 15.01757534,-0.13584121 15.03451549,-0.12726608 15.05012991,-0.11571978 15.0638183,-0.10164567 15.07505424,-0.08558461 15.0834055,-0.06815417 15.08855069,-0.05002481 15.09029174,0.0500174 15.09029949,0.06814888 15.08856105,0.08558242 15.08341775,0.10164727 15.07506734,0.11572543 15.06383098,0.12727556 15.05014087,0.13585382 15.03452355,0.1411309 15.01757961,0.14290466 14.99996055,0.14110774 14.98234371,0.13581008 14.96540615,0.12721607 14.94979867,0.11565658 14.93612079,0.10157617 14.92489779,0.08551591 14.91656051,0.06809265 14.91142893,0.04997533 14.90969989,-0.04998274 14.90970765,-0.06809794 14.91143929))" ); } @Test From 5b60a386196253ec0221d4e3bd35dd066a50905b Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Thu, 29 Feb 2024 13:57:39 -0500 Subject: [PATCH 47/54] fix snowflake test --- .../org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java index 122bc183b2..8df90eb185 100644 --- a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java +++ b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java @@ -215,7 +215,7 @@ public void test_ST_Buffer() { ); registerUDFV2("ST_Buffer", String.class, double.class, boolean.class); verifySqlSingleRes( - "select ST_AsText(ST_ReducePrecision(ST_GeomFromText(sedona.ST_AsText(sedona.ST_Buffer(sedona.ST_GeomFromText('LINESTRING(0.05 15, -0.05 15)'), 10000, true))), 8))", + "select ST_AsText(ST_ReducePrecision(ST_GeomFromText(sedona.ST_AsText(sedona.ST_Buffer(sedona.ST_GeometryFromWKT('LINESTRING(0.05 15, -0.05 15)'), 10000, true))), 8))", "POLYGON((-0.06809794 14.91143929,-0.0855181 14.91657277,-0.10157458 14.92491089,-0.11565095 14.93613348,-0.12720661 14.94980963,-0.13579747 14.96541421,-0.14109307 14.98234798,-0.14288927 14.99996056,-0.14111623 15.01757534,-0.13584121 15.03451549,-0.12726608 15.05012991,-0.11571978 15.0638183,-0.10164567 15.07505424,-0.08558461 15.0834055,-0.06815417 15.08855069,-0.05002481 15.09029174,0.0500174 15.09029949,0.06814888 15.08856105,0.08558242 15.08341775,0.10164727 15.07506734,0.11572543 15.06383098,0.12727556 15.05014087,0.13585382 15.03452355,0.1411309 15.01757961,0.14290466 14.99996055,0.14110774 14.98234371,0.13581008 14.96540615,0.12721607 14.94979867,0.11565658 14.93612079,0.10157617 14.92489779,0.08551591 14.91656051,0.06809265 14.91142893,0.04997533 14.90969989,-0.04998274 14.90970765,-0.06809794 14.91143929))" ); } From 94272c578493b896950e84691689a8b1fd11d414 Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Thu, 29 Feb 2024 14:36:16 -0500 Subject: [PATCH 48/54] fix snowflake test --- .../org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java index 8df90eb185..031ff5d6ae 100644 --- a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java +++ b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java @@ -215,7 +215,7 @@ public void test_ST_Buffer() { ); registerUDFV2("ST_Buffer", String.class, double.class, boolean.class); verifySqlSingleRes( - "select ST_AsText(ST_ReducePrecision(ST_GeomFromText(sedona.ST_AsText(sedona.ST_Buffer(sedona.ST_GeometryFromWKT('LINESTRING(0.05 15, -0.05 15)'), 10000, true))), 8))", + "select ST_AsText(ST_ReducePrecision(ST_GeomFromText(sedona.ST_AsText(sedona.ST_Buffer(ST_GeometryFromWKT('LINESTRING(0.05 15, -0.05 15)'), 10000, true))), 8))", "POLYGON((-0.06809794 14.91143929,-0.0855181 14.91657277,-0.10157458 14.92491089,-0.11565095 14.93613348,-0.12720661 14.94980963,-0.13579747 14.96541421,-0.14109307 14.98234798,-0.14288927 14.99996056,-0.14111623 15.01757534,-0.13584121 15.03451549,-0.12726608 15.05012991,-0.11571978 15.0638183,-0.10164567 15.07505424,-0.08558461 15.0834055,-0.06815417 15.08855069,-0.05002481 15.09029174,0.0500174 15.09029949,0.06814888 15.08856105,0.08558242 15.08341775,0.10164727 15.07506734,0.11572543 15.06383098,0.12727556 15.05014087,0.13585382 15.03452355,0.1411309 15.01757961,0.14290466 14.99996055,0.14110774 14.98234371,0.13581008 14.96540615,0.12721607 14.94979867,0.11565658 14.93612079,0.10157617 14.92489779,0.08551591 14.91656051,0.06809265 14.91142893,0.04997533 14.90969989,-0.04998274 14.90970765,-0.06809794 14.91143929))" ); } From 17768cec66dd6e6e9ed239ecea74b0107da40d5a Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Thu, 29 Feb 2024 16:47:02 -0500 Subject: [PATCH 49/54] fix snowflake test --- .../org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java index 031ff5d6ae..b22a9feac4 100644 --- a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java +++ b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java @@ -215,7 +215,7 @@ public void test_ST_Buffer() { ); registerUDFV2("ST_Buffer", String.class, double.class, boolean.class); verifySqlSingleRes( - "select ST_AsText(ST_ReducePrecision(ST_GeomFromText(sedona.ST_AsText(sedona.ST_Buffer(ST_GeometryFromWKT('LINESTRING(0.05 15, -0.05 15)'), 10000, true))), 8))", + "select ST_AsText(ST_ReducePrecision(ST_GeomFromWKB(sedona.ST_Buffer(ST_GeometryFromWKT('LINESTRING(0.05 15, -0.05 15)'), 10000, true)), 8))", "POLYGON((-0.06809794 14.91143929,-0.0855181 14.91657277,-0.10157458 14.92491089,-0.11565095 14.93613348,-0.12720661 14.94980963,-0.13579747 14.96541421,-0.14109307 14.98234798,-0.14288927 14.99996056,-0.14111623 15.01757534,-0.13584121 15.03451549,-0.12726608 15.05012991,-0.11571978 15.0638183,-0.10164567 15.07505424,-0.08558461 15.0834055,-0.06815417 15.08855069,-0.05002481 15.09029174,0.0500174 15.09029949,0.06814888 15.08856105,0.08558242 15.08341775,0.10164727 15.07506734,0.11572543 15.06383098,0.12727556 15.05014087,0.13585382 15.03452355,0.1411309 15.01757961,0.14290466 14.99996055,0.14110774 14.98234371,0.13581008 14.96540615,0.12721607 14.94979867,0.11565658 14.93612079,0.10157617 14.92489779,0.08551591 14.91656051,0.06809265 14.91142893,0.04997533 14.90969989,-0.04998274 14.90970765,-0.06809794 14.91143929))" ); } From cc3a1c4f70922dbd0e84fae31e1a8a0263701c86 Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Thu, 29 Feb 2024 17:09:47 -0500 Subject: [PATCH 50/54] fix snowflake test --- .../org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java index b22a9feac4..90517b7f42 100644 --- a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java +++ b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java @@ -215,7 +215,7 @@ public void test_ST_Buffer() { ); registerUDFV2("ST_Buffer", String.class, double.class, boolean.class); verifySqlSingleRes( - "select ST_AsText(ST_ReducePrecision(ST_GeomFromWKB(sedona.ST_Buffer(ST_GeometryFromWKT('LINESTRING(0.05 15, -0.05 15)'), 10000, true)), 8))", + "select ST_AsText(ST_ReducePrecision(sedona.ST_Buffer(ST_GeometryFromWKT('LINESTRING(0.05 15, -0.05 15)'), 10000, true), 8))", "POLYGON((-0.06809794 14.91143929,-0.0855181 14.91657277,-0.10157458 14.92491089,-0.11565095 14.93613348,-0.12720661 14.94980963,-0.13579747 14.96541421,-0.14109307 14.98234798,-0.14288927 14.99996056,-0.14111623 15.01757534,-0.13584121 15.03451549,-0.12726608 15.05012991,-0.11571978 15.0638183,-0.10164567 15.07505424,-0.08558461 15.0834055,-0.06815417 15.08855069,-0.05002481 15.09029174,0.0500174 15.09029949,0.06814888 15.08856105,0.08558242 15.08341775,0.10164727 15.07506734,0.11572543 15.06383098,0.12727556 15.05014087,0.13585382 15.03452355,0.1411309 15.01757961,0.14290466 14.99996055,0.14110774 14.98234371,0.13581008 14.96540615,0.12721607 14.94979867,0.11565658 14.93612079,0.10157617 14.92489779,0.08551591 14.91656051,0.06809265 14.91142893,0.04997533 14.90969989,-0.04998274 14.90970765,-0.06809794 14.91143929))" ); } From 2d05970b1f2b8c111ec9ad320c8327a15d4248ad Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Thu, 29 Feb 2024 17:34:37 -0500 Subject: [PATCH 51/54] Update Docs --- docs/api/flink/Function.md | 2 +- docs/api/snowflake/vector-data/Function.md | 2 +- docs/api/sql/Function.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/api/flink/Function.md b/docs/api/flink/Function.md index 0e00284940..0c518dd0c1 100644 --- a/docs/api/flink/Function.md +++ b/docs/api/flink/Function.md @@ -553,7 +553,7 @@ Output: `LINESTRING Z(-1 -1 0, 10 5 5)` ## ST_Buffer -Introduction: Returns a geometry/geography that represents all points whose distance from this Geometry/geography is less than or equal to distance. The function supports both Planar/Euclidean and Spheroidal/Geodesic buffering (Since v1.6.0). +Introduction: Returns a geometry/geography that represents all points whose distance from this Geometry/geography is less than or equal to distance. The function supports both Planar/Euclidean and Spheroidal/Geodesic buffering (Since v1.6.0). Spheroidal buffer also supports geometries crossing the International Date Line (IDL). Mode of buffer calculation (Since: `v1.6.0`): diff --git a/docs/api/snowflake/vector-data/Function.md b/docs/api/snowflake/vector-data/Function.md index 8b3ead973a..f85a014023 100644 --- a/docs/api/snowflake/vector-data/Function.md +++ b/docs/api/snowflake/vector-data/Function.md @@ -389,7 +389,7 @@ Output: `LINESTRING Z(-1 -1 0, 10 5 5)` ## ST_Buffer -Introduction: Returns a geometry/geography that represents all points whose distance from this Geometry/geography is less than or equal to distance. The function supports both Planar/Euclidean and Spheroidal/Geodesic buffering (Since v1.6.0). +Introduction: Returns a geometry/geography that represents all points whose distance from this Geometry/geography is less than or equal to distance. The function supports both Planar/Euclidean and Spheroidal/Geodesic buffering (Since v1.6.0). Spheroidal buffer also supports geometries crossing the International Date Line (IDL). Mode of buffer calculation (Since: `v1.6.0`): diff --git a/docs/api/sql/Function.md b/docs/api/sql/Function.md index 14f4cbd3c5..a332611e28 100644 --- a/docs/api/sql/Function.md +++ b/docs/api/sql/Function.md @@ -551,7 +551,7 @@ Output: `LINESTRING Z(-1 -1 0, 10 5 5)` ## ST_Buffer -Introduction: Returns a geometry/geography that represents all points whose distance from this Geometry/geography is less than or equal to distance. The function supports both Planar/Euclidean and Spheroidal/Geodesic buffering (Since v1.6.0). +Introduction: Returns a geometry/geography that represents all points whose distance from this Geometry/geography is less than or equal to distance. The function supports both Planar/Euclidean and Spheroidal/Geodesic buffering (Since v1.6.0). Spheroidal buffer also supports geometries crossing the International Date Line (IDL). Mode of buffer calculation (Since: `v1.6.0`): From c05cc4ef35447299b8893c199b24f05e89f57a1b Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Sun, 3 Mar 2024 12:57:33 -0500 Subject: [PATCH 52/54] Address comments --- docs/api/flink/Function.md | 11 +++++++---- docs/api/snowflake/vector-data/Function.md | 11 +++++++---- docs/api/sql/Function.md | 11 +++++++---- .../org/apache/sedona/sql/functionTestScala.scala | 6 +++++- 4 files changed, 26 insertions(+), 13 deletions(-) diff --git a/docs/api/flink/Function.md b/docs/api/flink/Function.md index 0c518dd0c1..8625c7d0c5 100644 --- a/docs/api/flink/Function.md +++ b/docs/api/flink/Function.md @@ -588,10 +588,13 @@ The optional forth parameter controls the buffer accuracy and style. Buffer accu Format: ``` -ST_Buffer (A: Geometry, buffer: Double, bufferStyleParameters: String [Optional]) +ST_Buffer (A: Geometry, buffer: Double) ``` ``` -ST_Buffer (A: Geometry, buffer: Double, bufferStyleParameters: String [Optional], useSperoidal: Boolean) +ST_Buffer (A: Geometry, buffer: Double, useSpheroid: Boolean) +``` +``` +ST_Buffer (A: Geometry, buffer: Double, useSpheroid: Boolean, bufferStyleParameters: String) ``` Since: `v1.5.1` @@ -600,7 +603,7 @@ SQL Example: ```sql SELECT ST_Buffer(ST_GeomFromWKT('POINT(0 0)'), 10) -SELECT ST_Buffer(ST_GeomFromWKT('POINT(0 0)'), 10, 'quad_segs=2') +SELECT ST_Buffer(ST_GeomFromWKT('POINT(0 0)'), 10, false, 'quad_segs=2') ``` Output: @@ -613,7 +616,7 @@ Output: SQL Example: ```sql -SELECT ST_Buffer(ST_GeomFromWKT('LINESTRING(0 0, 50 70, 100 100)'), 10, 'side=left') +SELECT ST_Buffer(ST_GeomFromWKT('LINESTRING(0 0, 50 70, 100 100)'), 10, false, 'side=left') ``` Output: diff --git a/docs/api/snowflake/vector-data/Function.md b/docs/api/snowflake/vector-data/Function.md index f85a014023..89d8116efa 100644 --- a/docs/api/snowflake/vector-data/Function.md +++ b/docs/api/snowflake/vector-data/Function.md @@ -424,10 +424,13 @@ The optional forth parameter controls the buffer accuracy and style. Buffer accu Format: ``` -ST_Buffer (A: Geometry, buffer: Double, bufferStyleParameters: String [Optional]) +ST_Buffer (A: Geometry, buffer: Double) ``` ``` -ST_Buffer (A: Geometry, buffer: Double, bufferStyleParameters: String [Optional], useSperoidal: Boolean) +ST_Buffer (A: Geometry, buffer: Double, useSpheroid: Boolean) +``` +``` +ST_Buffer (A: Geometry, buffer: Double, useSpheroid: Boolean, bufferStyleParameters: String) ``` Since: `v1.5.1` @@ -436,7 +439,7 @@ SQL Example: ```sql SELECT ST_Buffer(ST_GeomFromWKT('POINT(0 0)'), 10) -SELECT ST_Buffer(ST_GeomFromWKT('POINT(0 0)'), 10, 'quad_segs=2') +SELECT ST_Buffer(ST_GeomFromWKT('POINT(0 0)'), 10, false, 'quad_segs=2') ``` Output: @@ -449,7 +452,7 @@ Output: SQL Example: ```sql -SELECT ST_Buffer(ST_GeomFromWKT('LINESTRING(0 0, 50 70, 100 100)'), 10, 'side=left') +SELECT ST_Buffer(ST_GeomFromWKT('LINESTRING(0 0, 50 70, 100 100)'), 10, false, 'side=left') ``` Output: diff --git a/docs/api/sql/Function.md b/docs/api/sql/Function.md index a332611e28..4d867e885f 100644 --- a/docs/api/sql/Function.md +++ b/docs/api/sql/Function.md @@ -586,10 +586,13 @@ The optional forth parameter controls the buffer accuracy and style. Buffer accu Format: ``` -ST_Buffer (A: Geometry, buffer: Double, bufferStyleParameters: String [Optional]) +ST_Buffer (A: Geometry, buffer: Double) ``` ``` -ST_Buffer (A: Geometry, buffer: Double, bufferStyleParameters: String [Optional], useSperoidal: Boolean) +ST_Buffer (A: Geometry, buffer: Double, useSpheroid: Boolean) +``` +``` +ST_Buffer (A: Geometry, buffer: Double, useSpheroid: Boolean, bufferStyleParameters: String) ``` Since: `v1.5.1` @@ -598,7 +601,7 @@ SQL Example: ```sql SELECT ST_Buffer(ST_GeomFromWKT('POINT(0 0)'), 10) -SELECT ST_Buffer(ST_GeomFromWKT('POINT(0 0)'), 10, 'quad_segs=2') +SELECT ST_Buffer(ST_GeomFromWKT('POINT(0 0)'), 10, false, 'quad_segs=2') ``` Output: @@ -611,7 +614,7 @@ Output: SQL Example: ```sql -SELECT ST_Buffer(ST_GeomFromWKT('LINESTRING(0 0, 50 70, 100 100)'), 10, 'side=left') +SELECT ST_Buffer(ST_GeomFromWKT('LINESTRING(0 0, 50 70, 100 100)'), 10, false, 'side=left') ``` Output: diff --git a/spark/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala b/spark/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala index 4aab0109e3..0efa67ee4a 100644 --- a/spark/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala +++ b/spark/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala @@ -91,7 +91,11 @@ class functionTestScala extends TestBaseScala with Matchers with GeometrySample polygonWktDf.createOrReplaceTempView("polygontable") val polygonDf = sparkSession.sql("select ST_GeomFromWKT(polygontable._c0) as countyshape from polygontable") polygonDf.createOrReplaceTempView("polygondf") - val functionDf = sparkSession.sql("select ST_Buffer(polygondf.countyshape, 1, true) from polygondf") + + var functionDf = sparkSession.sql("select ST_Buffer(polygondf.countyshape, 1, true) from polygondf") + assert(functionDf.count() > 0); + + functionDf = sparkSession.sql("select ST_Buffer(polygondf.countyshape, 1, true, 'quad_segs=2') from polygondf") assert(functionDf.count() > 0); } From 877db2d30e98381bd8688ef330c255dd70742018 Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Sun, 3 Mar 2024 22:03:20 -0500 Subject: [PATCH 53/54] Address comments --- .../org/apache/sedona/common/Functions.java | 355 +++++++++--------- 1 file changed, 175 insertions(+), 180 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 8f6f2a9fe7..3177cf2fa4 100644 --- a/common/src/main/java/org/apache/sedona/common/Functions.java +++ b/common/src/main/java/org/apache/sedona/common/Functions.java @@ -134,7 +134,7 @@ private static BufferParameters parseBufferParams(String params) { BufferParameters bufferParameters = new BufferParameters(); String[] listParams = params.split(" "); - for (String param : listParams) { + for (String param: listParams) { String[] singleParam = param.split("="); if (singleParam.length != 2) { @@ -150,7 +150,7 @@ private static BufferParameters parseBufferParams(String params) { } } // Set end cap style - else if (singleParam[0].equalsIgnoreCase(listBufferParameters[1])) { + else if (singleParam[0].equalsIgnoreCase(listBufferParameters[1])) { if (singleParam[1].equalsIgnoreCase(endcapOptions[0])) { bufferParameters.setEndCapStyle(BufferParameters.CAP_ROUND); } else if (singleParam[1].equalsIgnoreCase(endcapOptions[1]) || singleParam[1].equalsIgnoreCase(endcapOptions[2])) { @@ -284,7 +284,7 @@ public boolean isGeometryChanged() { * @param geometry The geometry to check. * @return True if the geometry crosses the Date Line, false otherwise. */ - public static boolean crossesDateLine (Geometry geometry){ + public static boolean crossesDateLine(Geometry geometry) { if (geometry == null || geometry.isEmpty()) { return false; } @@ -370,110 +370,110 @@ public static Geometry normalize (Geometry geometry){ return geometry; } - public static Double x (Geometry geometry){ + public static Double x(Geometry geometry) { if (geometry instanceof Point) { return geometry.getCoordinate().x; } return null; } - public static Double y (Geometry geometry){ + public static Double y(Geometry geometry) { if (geometry instanceof Point) { return geometry.getCoordinate().y; } return null; } - public static Double z (Geometry geometry){ + public static Double z(Geometry geometry) { if (geometry instanceof Point) { return geometry.getCoordinate().z; } return null; } - public static double xMin (Geometry geometry){ + public static double xMin(Geometry geometry) { Coordinate[] points = geometry.getCoordinates(); double min = Double.MAX_VALUE; - for (int i = 0; i < points.length; i++) { + for(int i=0; i < points.length; i++){ min = Math.min(points[i].getX(), min); } return min; } - public static double xMax (Geometry geometry){ + public static double xMax(Geometry geometry) { Coordinate[] points = geometry.getCoordinates(); - double max = -Double.MAX_VALUE; - for (int i = 0; i < points.length; i++) { + double max = - Double.MAX_VALUE; + for (int i=0; i < points.length; i++) { max = Math.max(points[i].getX(), max); } return max; } - public static double yMin (Geometry geometry){ + public static double yMin(Geometry geometry) { Coordinate[] points = geometry.getCoordinates(); double min = Double.MAX_VALUE; - for (int i = 0; i < points.length; i++) { + for(int i=0; i < points.length; i++){ min = Math.min(points[i].getY(), min); } return min; } - public static double yMax (Geometry geometry){ + public static double yMax(Geometry geometry) { Coordinate[] points = geometry.getCoordinates(); - double max = -Double.MAX_VALUE; - for (int i = 0; i < points.length; i++) { + double max = - Double.MAX_VALUE; + for (int i=0; i < points.length; i++) { max = Math.max(points[i].getY(), max); } return max; } - public static Double zMax (Geometry geometry){ + public static Double zMax(Geometry geometry) { Coordinate[] points = geometry.getCoordinates(); - double max = -Double.MAX_VALUE; - for (int i = 0; i < points.length; i++) { - if (java.lang.Double.isNaN(points[i].getZ())) + double max = - Double.MAX_VALUE; + for (int i=0; i < points.length; i++) { + if(java.lang.Double.isNaN(points[i].getZ())) continue; max = Math.max(points[i].getZ(), max); } return max == -Double.MAX_VALUE ? null : max; } - public static Double zMin (Geometry geometry){ + public static Double zMin(Geometry geometry) { Coordinate[] points = geometry.getCoordinates(); double min = Double.MAX_VALUE; - for (int i = 0; i < points.length; i++) { - if (java.lang.Double.isNaN(points[i].getZ())) + for(int i=0; i < points.length; i++){ + if(java.lang.Double.isNaN(points[i].getZ())) continue; min = Math.min(points[i].getZ(), min); } return min == Double.MAX_VALUE ? null : min; } - public static Geometry flipCoordinates (Geometry geometry){ + public static Geometry flipCoordinates(Geometry geometry) { GeomUtils.flipCoordinates(geometry); return geometry; } - public static String geohash (Geometry geometry,int precision){ + public static String geohash(Geometry geometry, int precision) { return GeometryGeoHashEncoder.calculate(geometry, precision); } - public static Geometry pointOnSurface (Geometry geometry){ + public static Geometry pointOnSurface(Geometry geometry) { return GeomUtils.getInteriorPoint(geometry); } - public static Geometry reverse (Geometry geometry){ + public static Geometry reverse(Geometry geometry) { return geometry.reverse(); } - public static Geometry geometryN (Geometry geometry,int n){ + public static Geometry geometryN(Geometry geometry, int n) { if (n < geometry.getNumGeometries()) { return geometry.getGeometryN(n); } return null; } - public static Geometry interiorRingN (Geometry geometry,int n){ + public static Geometry interiorRingN(Geometry geometry, int n) { if (geometry instanceof Polygon) { Polygon polygon = (Polygon) geometry; if (n < polygon.getNumInteriorRing()) { @@ -487,14 +487,14 @@ public static Geometry interiorRingN (Geometry geometry,int n){ return null; } - public static Geometry pointN (Geometry geometry,int n){ - if (!(geometry instanceof LineString)) { + public static Geometry pointN(Geometry geometry, int n) { + if(!(geometry instanceof LineString)) { return null; } - return GeomUtils.getNthPoint((LineString) geometry, n); + return GeomUtils.getNthPoint((LineString)geometry, n); } - public static Geometry exteriorRing (Geometry geometry){ + public static Geometry exteriorRing(Geometry geometry) { Geometry ring = GeomUtils.getExteriorRing(geometry); if (ring instanceof LinearRing) { ring = GEOMETRY_FACTORY.createLineString(ring.getCoordinates()); @@ -502,23 +502,23 @@ public static Geometry exteriorRing (Geometry geometry){ return ring; } - public static String asEWKT (Geometry geometry){ + public static String asEWKT(Geometry geometry) { return GeomUtils.getEWKT(geometry); } - public static String asWKT (Geometry geometry){ + public static String asWKT(Geometry geometry) { return GeomUtils.getWKT(geometry); } - public static byte[] asEWKB (Geometry geometry){ + public static byte[] asEWKB(Geometry geometry) { return GeomUtils.getEWKB(geometry); } - public static byte[] asWKB (Geometry geometry){ + public static byte[] asWKB(Geometry geometry) { return GeomUtils.getWKB(geometry); } - public static String asGeoJson (Geometry geometry){ + public static String asGeoJson(Geometry geometry) { if (geometry == null) { return null; } @@ -526,60 +526,60 @@ public static String asGeoJson (Geometry geometry){ return writer.write(geometry).toString(); } - public static int nPoints (Geometry geometry){ + public static int nPoints(Geometry geometry) { return geometry.getNumPoints(); } - public static int nDims (Geometry geometry){ - int count_dimension = 0; + public static int nDims(Geometry geometry) { + int count_dimension =0; Coordinate geom = geometry.getCoordinate(); Double x_cord = geom.getX(); Double y_cord = geom.getY(); Double z_cord = geom.getZ(); Double m_cord = geom.getM(); - if (!java.lang.Double.isNaN(x_cord)) + if(!java.lang.Double.isNaN(x_cord)) count_dimension++; - if (!java.lang.Double.isNaN(y_cord)) + if(!java.lang.Double.isNaN(y_cord)) count_dimension++; - if (!java.lang.Double.isNaN(z_cord)) + if(!java.lang.Double.isNaN(z_cord)) count_dimension++; - if (!java.lang.Double.isNaN(m_cord)) + if(!java.lang.Double.isNaN(m_cord)) count_dimension++; return count_dimension; } - public static int numGeometries (Geometry geometry){ + public static int numGeometries(Geometry geometry) { return geometry.getNumGeometries(); } - public static Integer numInteriorRings (Geometry geometry){ + public static Integer numInteriorRings(Geometry geometry) { if (geometry instanceof Polygon) { return ((Polygon) geometry).getNumInteriorRing(); } return null; } - public static String asGML (Geometry geometry){ + public static String asGML(Geometry geometry) { return new GMLWriter().write(geometry); } - public static String asKML (Geometry geometry){ + public static String asKML(Geometry geometry) { return new KMLWriter().write(geometry); } - public static Geometry force2D (Geometry geometry){ + public static Geometry force2D(Geometry geometry) { return GeomUtils.get2dGeom(geometry); } - public static boolean isEmpty (Geometry geometry){ + public static boolean isEmpty(Geometry geometry) { return geometry.isEmpty(); } - public static Geometry buildArea (Geometry geometry){ + public static Geometry buildArea(Geometry geometry) { return GeomUtils.buildArea(geometry); } - public static Geometry setSRID (Geometry geometry,int srid){ + public static Geometry setSRID(Geometry geometry, int srid) { if (geometry == null) { return null; } @@ -587,14 +587,14 @@ public static Geometry setSRID (Geometry geometry,int srid){ return factory.createGeometry(geometry); } - public static int getSRID (Geometry geometry){ + public static int getSRID(Geometry geometry) { if (geometry == null) { return 0; } return geometry.getSRID(); } - public static boolean isClosed (Geometry geometry){ + public static boolean isClosed(Geometry geometry) { if (geometry instanceof Circle || geometry instanceof MultiPoint || geometry instanceof MultiPolygon || geometry instanceof Point || geometry instanceof Polygon) { return true; } else if (geometry instanceof LineString) { @@ -607,19 +607,19 @@ public static boolean isClosed (Geometry geometry){ return false; } - public static boolean isRing (Geometry geometry){ + public static boolean isRing(Geometry geometry) { return geometry instanceof LineString && ((LineString) geometry).isClosed() && geometry.isSimple(); } - public static boolean isSimple (Geometry geometry){ + public static boolean isSimple(Geometry geometry) { return new IsSimpleOp(geometry).isSimple(); } - public static boolean isValid (Geometry geometry){ + public static boolean isValid(Geometry geometry) { return isValid(geometry, OGC_SFS_VALIDITY); } - public static boolean isValid (Geometry geom,int flag){ + public static boolean isValid(Geometry geom, int flag) { IsValidOp isValidOp = new IsValidOp(geom); // Set the validity model based on flags @@ -632,11 +632,11 @@ public static boolean isValid (Geometry geom,int flag){ return isValidOp.isValid(); } - public static Geometry addPoint (Geometry linestring, Geometry point){ + public static Geometry addPoint(Geometry linestring, Geometry point) { return addPoint(linestring, point, -1); } - public static Geometry addPoint (Geometry linestring, Geometry point,int position){ + public static Geometry addPoint(Geometry linestring, Geometry point, int position) { if (linestring instanceof LineString && point instanceof Point) { List coordinates = new ArrayList<>(Arrays.asList(linestring.getCoordinates())); if (-1 <= position && position <= coordinates.size()) { @@ -651,14 +651,14 @@ public static Geometry addPoint (Geometry linestring, Geometry point,int positio return null; } - public static Geometry removePoint (Geometry linestring){ + public static Geometry removePoint(Geometry linestring) { if (linestring != null) { return removePoint(linestring, -1); } return null; } - public static Geometry removePoint (Geometry linestring,int position){ + public static Geometry removePoint(Geometry linestring, int position) { if (linestring instanceof LineString) { List coordinates = new ArrayList<>(Arrays.asList(linestring.getCoordinates())); if (2 < coordinates.size() && position < coordinates.size()) { @@ -672,7 +672,7 @@ public static Geometry removePoint (Geometry linestring,int position){ return null; } - public static Geometry setPoint (Geometry linestring,int position, Geometry point){ + public static Geometry setPoint(Geometry linestring, int position, Geometry point) { if (linestring instanceof LineString) { List coordinates = new ArrayList<>(Arrays.asList(linestring.getCoordinates())); if (-coordinates.size() <= position && position < coordinates.size()) { @@ -687,28 +687,29 @@ public static Geometry setPoint (Geometry linestring,int position, Geometry poin return null; } - public static Geometry lineFromMultiPoint (Geometry geometry){ - if (!(geometry instanceof MultiPoint)) { + public static Geometry lineFromMultiPoint(Geometry geometry) { + if(!(geometry instanceof MultiPoint)) { return null; } List coordinates = new ArrayList<>(); - for (Coordinate c : geometry.getCoordinates()) { + for(Coordinate c : geometry.getCoordinates()){ coordinates.add(c); } return GEOMETRY_FACTORY.createLineString(coordinates.toArray(new Coordinate[0])); } - public static Geometry closestPoint (Geometry left, Geometry right){ + public static Geometry closestPoint(Geometry left, Geometry right) { DistanceOp distanceOp = new DistanceOp(left, right); try { Coordinate[] closestPoints = distanceOp.nearestPoints(); return GEOMETRY_FACTORY.createPoint(closestPoints[0]); - } catch (Exception e) { + } + catch (Exception e) { throw new IllegalArgumentException("ST_ClosestPoint doesn't support empty geometry object."); } } - public static Geometry concaveHull (Geometry geometry,double pctConvex, boolean allowHoles){ + public static Geometry concaveHull(Geometry geometry, double pctConvex, boolean allowHoles){ ConcaveHull concave_hull = new ConcaveHull(geometry); concave_hull.setMaximumEdgeLengthRatio(pctConvex); concave_hull.setHolesAllowed(allowHoles); @@ -716,15 +717,15 @@ public static Geometry concaveHull (Geometry geometry,double pctConvex, boolean return concave_hull.getHull(); } - public static Geometry convexHull (Geometry geometry){ + public static Geometry convexHull(Geometry geometry) { return geometry.convexHull(); } - public static Geometry getCentroid (Geometry geometry){ + public static Geometry getCentroid(Geometry geometry) { return geometry.getCentroid(); } - public static Geometry intersection (Geometry leftGeometry, Geometry rightGeometry){ + public static Geometry intersection(Geometry leftGeometry, Geometry rightGeometry) { boolean isIntersects = leftGeometry.intersects(rightGeometry); if (!isIntersects) { return EMPTY_POLYGON; @@ -738,18 +739,18 @@ public static Geometry intersection (Geometry leftGeometry, Geometry rightGeomet return leftGeometry.intersection(rightGeometry); } - public static Geometry makeValid (Geometry geometry,boolean keepCollapsed){ + public static Geometry makeValid(Geometry geometry, boolean keepCollapsed) { GeometryFixer fixer = new GeometryFixer(geometry); fixer.setKeepCollapsed(keepCollapsed); return fixer.getResult(); } - public static Geometry reducePrecision (Geometry geometry,int precisionScale){ + public static Geometry reducePrecision(Geometry geometry, int precisionScale) { GeometryPrecisionReducer precisionReduce = new GeometryPrecisionReducer(new PrecisionModel(Math.pow(10, precisionScale))); return precisionReduce.reduce(geometry); } - public static Geometry lineMerge (Geometry geometry){ + public static Geometry lineMerge(Geometry geometry) { if (geometry instanceof MultiLineString) { MultiLineString multiLineString = (MultiLineString) geometry; int numLineStrings = multiLineString.getNumGeometries(); @@ -769,7 +770,7 @@ public static Geometry lineMerge (Geometry geometry){ return EMPTY_GEOMETRY_COLLECTION; } - public static Geometry minimumBoundingCircle (Geometry geometry,int quadrantSegments){ + public static Geometry minimumBoundingCircle(Geometry geometry, int quadrantSegments) { MinimumBoundingCircle minimumBoundingCircle = new MinimumBoundingCircle(geometry); Coordinate centre = minimumBoundingCircle.getCentre(); double radius = minimumBoundingCircle.getRadius(); @@ -785,7 +786,7 @@ public static Geometry minimumBoundingCircle (Geometry geometry,int quadrantSegm return circle; } - public static Pair minimumBoundingRadius (Geometry geometry){ + public static Pair minimumBoundingRadius(Geometry geometry) { MinimumBoundingCircle minimumBoundingCircle = new MinimumBoundingCircle(geometry); Coordinate coods = minimumBoundingCircle.getCentre(); double radius = minimumBoundingCircle.getRadius(); @@ -793,28 +794,28 @@ public static Pair minimumBoundingRadius (Geometry geometry){ return Pair.of(centre, radius); } - public static Geometry lineSubString (Geometry geom,double fromFraction, double toFraction){ + public static Geometry lineSubString(Geometry geom, double fromFraction, double toFraction) { double length = geom.getLength(); LengthIndexedLine indexedLine = new LengthIndexedLine(geom); Geometry subLine = indexedLine.extractLine(length * fromFraction, length * toFraction); return subLine; } - public static Geometry lineInterpolatePoint (Geometry geom,double fraction){ + public static Geometry lineInterpolatePoint(Geometry geom, double fraction) { double length = geom.getLength(); LengthIndexedLine indexedLine = new LengthIndexedLine(geom); Coordinate interPoint = indexedLine.extractPoint(length * fraction); return GEOMETRY_FACTORY.createPoint(interPoint); } - public static double lineLocatePoint (Geometry geom, Geometry point) + public static double lineLocatePoint(Geometry geom, Geometry point) { double length = geom.getLength(); LengthIndexedLine indexedLine = new LengthIndexedLine(geom); return indexedLine.indexOf(point.getCoordinate()) / length; } - public static Geometry difference (Geometry leftGeometry, Geometry rightGeometry){ + public static Geometry difference(Geometry leftGeometry, Geometry rightGeometry) { boolean isIntersects = leftGeometry.intersects(rightGeometry); if (!isIntersects) { return leftGeometry; @@ -825,12 +826,12 @@ public static Geometry difference (Geometry leftGeometry, Geometry rightGeometry } } - public static Geometry split (Geometry input, Geometry blade){ + public static Geometry split(Geometry input, Geometry blade) { // check input geometry return new GeometrySplitter(GEOMETRY_FACTORY).split(input, blade); } - public static Integer dimension (Geometry geometry){ + public static Integer dimension(Geometry geometry) { Integer dimension = geometry.getDimension(); // unknown dimension such as an empty GEOMETRYCOLLECTION if (dimension < 0) { @@ -845,7 +846,7 @@ public static Integer dimension (Geometry geometry){ * @param level integer, minimum level of cells covering the geometry * @return List of coordinates */ - public static Long[] s2CellIDs (Geometry input,int level){ + public static Long[] s2CellIDs(Geometry input, int level) { HashSet cellIds = new HashSet<>(); List geoms = GeomUtils.extractGeometryCollection(input); for (Geometry geom : geoms) { @@ -866,7 +867,7 @@ public static Long[] s2CellIDs (Geometry input,int level){ * @param fullCover whether enforce full cover or not. * @return */ - public static Long[] h3CellIDs (Geometry input,int level, boolean fullCover){ + public static Long[] h3CellIDs(Geometry input, int level, boolean fullCover) { if (level < 0 || level > 15) { throw new IllegalArgumentException("level must be between 0 and 15"); } @@ -878,11 +879,11 @@ public static Long[] h3CellIDs (Geometry input,int level, boolean fullCover){ cellIds.addAll(H3Utils.polygonToCells((Polygon) geom, level, fullCover)); } else if (geom instanceof LineString) { cellIds.addAll(H3Utils.lineStringToCells((LineString) geom, level, fullCover)); - } else if (geom instanceof Point) { + } else if (geom instanceof Point){ cellIds.add(H3Utils.coordinateToCell(geom.getCoordinate(), level)); } else { // if not type of polygon, point or lienSting, we cover its MBR - cellIds.addAll(H3Utils.polygonToCells((Polygon) geom.getEnvelope(), level, fullCover)); + cellIds.addAll(H3Utils.polygonToCells((Polygon)geom.getEnvelope(), level, fullCover)); } } return cellIds.toArray(new Long[0]); @@ -894,7 +895,7 @@ public static Long[] h3CellIDs (Geometry input,int level, boolean fullCover){ * @param cell2 destination cell * @return */ - public static long h3CellDistance ( long cell1, long cell2){ + public static long h3CellDistance(long cell1, long cell2) { int resolution = H3Utils.h3.getResolution(cell1); if (resolution != H3Utils.h3.getResolution(cell2)) { throw new IllegalArgumentException("The argument cells should be of the same resolution"); @@ -919,7 +920,7 @@ public static long h3CellDistance ( long cell1, long cell2){ * @param exactDistance: if exactDistance is true, it will only return the cells on the exact kth ring, else will return all 0 - kth neighbors * @return */ - public static Long[] h3KRing ( long cell, int k, boolean exactDistance){ + public static Long[] h3KRing(long cell, int k, boolean exactDistance) { Set cells = new LinkedHashSet<>(H3Utils.h3.gridDisk(cell, k)); if (exactDistance && k > 0) { List tbdCells = H3Utils.h3.gridDisk(cell, k - 1); @@ -933,7 +934,7 @@ public static Long[] h3KRing ( long cell, int k, boolean exactDistance){ * @param cells: the set of cells * @return Multiple Polygons reversed */ - public static Geometry h3ToGeom ( long[] cells){ + public static Geometry h3ToGeom(long[] cells) { GeometryFactory geomFactory = new GeometryFactory(); Collection h3 = Arrays.stream(cells).boxed().collect(Collectors.toList()); return geomFactory.createMultiPolygon( @@ -954,15 +955,15 @@ public static Geometry h3ToGeom ( long[] cells){ } // create static function named simplifyPreserveTopology - public static Geometry simplifyPreserveTopology (Geometry geometry,double distanceTolerance){ + public static Geometry simplifyPreserveTopology(Geometry geometry, double distanceTolerance) { return TopologyPreservingSimplifier.simplify(geometry, distanceTolerance); } - public static String geometryType (Geometry geometry){ + public static String geometryType(Geometry geometry) { return "ST_" + geometry.getGeometryType(); } - public static String geometryTypeWithMeasured (Geometry geometry){ + public static String geometryTypeWithMeasured(Geometry geometry) { String geometryType = geometry.getGeometryType().toUpperCase(); if (GeomUtils.isMeasuredGeometry(geometry)) { geometryType += "M"; @@ -970,7 +971,7 @@ public static String geometryTypeWithMeasured (Geometry geometry){ return geometryType; } - public static Geometry startPoint (Geometry geometry){ + public static Geometry startPoint(Geometry geometry) { if (geometry instanceof LineString) { LineString line = (LineString) geometry; return line.getStartPoint(); @@ -978,7 +979,7 @@ public static Geometry startPoint (Geometry geometry){ return null; } - public static Geometry endPoint (Geometry geometry){ + public static Geometry endPoint(Geometry geometry) { if (geometry instanceof LineString) { LineString line = (LineString) geometry; return line.getEndPoint(); @@ -986,7 +987,7 @@ public static Geometry endPoint (Geometry geometry){ return null; } - public static Geometry[] dump (Geometry geometry){ + public static Geometry[] dump(Geometry geometry) { int numGeom = geometry.getNumGeometries(); if (geometry instanceof GeometryCollection) { Geometry[] geoms = new Geometry[geometry.getNumGeometries()]; @@ -995,55 +996,56 @@ public static Geometry[] dump (Geometry geometry){ } return geoms; } else { - return new Geometry[]{geometry}; + return new Geometry[] {geometry}; } } - public static Geometry[] dumpPoints (Geometry geometry){ + public static Geometry[] dumpPoints(Geometry geometry) { return Arrays.stream(geometry.getCoordinates()).map(GEOMETRY_FACTORY::createPoint).toArray(Point[]::new); } - public static Geometry symDifference (Geometry leftGeom, Geometry rightGeom){ + public static Geometry symDifference(Geometry leftGeom, Geometry rightGeom) { return leftGeom.symDifference(rightGeom); } - public static Geometry union (Geometry leftGeom, Geometry rightGeom){ + public static Geometry union(Geometry leftGeom, Geometry rightGeom) { return leftGeom.union(rightGeom); } - public static Geometry createMultiGeometryFromOneElement (Geometry geometry){ + public static Geometry createMultiGeometryFromOneElement(Geometry geometry) { if (geometry instanceof Circle) { - return GEOMETRY_FACTORY.createGeometryCollection(new Circle[]{(Circle) geometry}); + return GEOMETRY_FACTORY.createGeometryCollection(new Circle[] {(Circle) geometry}); } else if (geometry instanceof GeometryCollection) { return geometry; - } else if (geometry instanceof LineString) { + } else if (geometry instanceof LineString) { return GEOMETRY_FACTORY.createMultiLineString(new LineString[]{(LineString) geometry}); } else if (geometry instanceof Point) { - return GEOMETRY_FACTORY.createMultiPoint(new Point[]{(Point) geometry}); + return GEOMETRY_FACTORY.createMultiPoint(new Point[] {(Point) geometry}); } else if (geometry instanceof Polygon) { - return GEOMETRY_FACTORY.createMultiPolygon(new Polygon[]{(Polygon) geometry}); + return GEOMETRY_FACTORY.createMultiPolygon(new Polygon[] {(Polygon) geometry}); } else { return GEOMETRY_FACTORY.createGeometryCollection(); } } - public static Geometry[] subDivide (Geometry geometry,int maxVertices){ + public static Geometry[] subDivide(Geometry geometry, int maxVertices) { return GeometrySubDivider.subDivide(geometry, maxVertices); } - public static Geometry makeLine (Geometry geom1, Geometry geom2){ + public static Geometry makeLine(Geometry geom1, Geometry geom2) { Geometry[] geoms = new Geometry[]{geom1, geom2}; return makeLine(geoms); } - public static Geometry makeLine (Geometry[]geoms){ + public static Geometry makeLine(Geometry[] geoms) { ArrayList coordinates = new ArrayList<>(); for (Geometry geom : geoms) { if (geom instanceof Point || geom instanceof MultiPoint || geom instanceof LineString) { for (Coordinate coord : geom.getCoordinates()) { coordinates.add(coord); } - } else { + } + else { throw new IllegalArgumentException("ST_MakeLine only supports Point, MultiPoint and LineString geometries"); } } @@ -1052,10 +1054,10 @@ public static Geometry makeLine (Geometry[]geoms){ return GEOMETRY_FACTORY.createLineString(coords); } - public static Geometry makePolygon (Geometry shell, Geometry[]holes){ + public static Geometry makePolygon(Geometry shell, Geometry[] holes) { try { if (holes != null) { - LinearRing[] interiorRings = Arrays.stream(holes).filter( + LinearRing[] interiorRings = Arrays.stream(holes).filter( h -> h != null && !h.isEmpty() && h instanceof LineString && ((LineString) h).isClosed() ).map( h -> GEOMETRY_FACTORY.createLinearRing(h.getCoordinates()) @@ -1079,25 +1081,27 @@ public static Geometry makePolygon (Geometry shell, Geometry[]holes){ } } - public static Geometry makepolygonWithSRID (Geometry lineString, Integer srid){ + public static Geometry makepolygonWithSRID(Geometry lineString, Integer srid) { Geometry geom = makePolygon(lineString, null); - if (geom != null) { + if(geom != null) { geom.setSRID(srid); } return geom; } - public static Geometry createMultiGeometry (Geometry[]geometries){ - if (geometries.length > 1) { + public static Geometry createMultiGeometry(Geometry[] geometries) { + if (geometries.length > 1){ return GEOMETRY_FACTORY.buildGeometry(Arrays.asList(geometries)); - } else if (geometries.length == 1) { + } + else if(geometries.length==1){ return createMultiGeometryFromOneElement(geometries[0]); - } else { + } + else{ return GEOMETRY_FACTORY.createGeometryCollection(); } } - public static Geometry collectionExtract (Geometry geometry, Integer geomType){ + public static Geometry collectionExtract(Geometry geometry, Integer geomType) { if (geomType == null) { return collectionExtract(geometry); } @@ -1126,7 +1130,7 @@ public static Geometry collectionExtract (Geometry geometry, Integer geomType){ return Functions.createMultiGeometry(geometries.toArray(new Geometry[0])); } - public static Geometry collectionExtract (Geometry geometry){ + public static Geometry collectionExtract(Geometry geometry) { List geometries = GeomUtils.extractGeometryCollection(geometry); Polygon[] polygons = geometries.stream().filter(g -> g instanceof Polygon).toArray(Polygon[]::new); if (polygons.length > 0) { @@ -1147,20 +1151,20 @@ public static Geometry collectionExtract (Geometry geometry){ // ported from https://github.com/postgis/postgis/blob/f6ed58d1fdc865d55d348212d02c11a10aeb2b30/liblwgeom/lwgeom_median.c // geometry ST_GeometricMedian ( geometry g , float8 tolerance , int max_iter , boolean fail_if_not_converged ); - private static double distance3d (Coordinate p1, Coordinate p2){ + private static double distance3d(Coordinate p1, Coordinate p2) { double dx = p2.x - p1.x; double dy = p2.y - p1.y; double dz = p2.z - p1.z; return Math.sqrt(dx * dx + dy * dy + dz * dz); } - private static void distances (Coordinate curr, Coordinate[]points,double[] distances){ - for (int i = 0; i < points.length; i++) { + private static void distances(Coordinate curr, Coordinate[] points, double[] distances) { + for(int i = 0; i < points.length; i++) { distances[i] = distance3d(curr, points[i]); } } - private static double iteratePoints (Coordinate curr, Coordinate[]points,double[] distances){ + private static double iteratePoints(Coordinate curr, Coordinate[] points, double[] distances) { Coordinate next = new Coordinate(0, 0, 0); double delta = 0; double denom = 0; @@ -1216,13 +1220,13 @@ private static double iteratePoints (Coordinate curr, Coordinate[]points,double[ dz += (coordinate.z - curr.z) / distance; } } - double dSqr = Math.sqrt(dx * dx + dy * dy + dz * dz); + double dSqr = Math.sqrt(dx*dx + dy*dy + dz*dz); /* Avoid division by zero if the intermediate point is the median */ if (dSqr > DBL_EPSILON) { double rInv = Math.max(0, 1.0 / dSqr); - next.x = (1.0 - rInv) * next.x + rInv * curr.x; - next.y = (1.0 - rInv) * next.y + rInv * curr.y; - next.z = (1.0 - rInv) * next.z + rInv * curr.z; + next.x = (1.0 - rInv)*next.x + rInv*curr.x; + next.y = (1.0 - rInv)*next.y + rInv*curr.y; + next.z = (1.0 - rInv)*next.z + rInv*curr.z; } } delta = distance3d(curr, next); @@ -1233,7 +1237,7 @@ private static double iteratePoints (Coordinate curr, Coordinate[]points,double[ return delta; } - private static Coordinate initGuess (Coordinate[]points){ + private static Coordinate initGuess(Coordinate[] points) { Coordinate guess = new Coordinate(0, 0, 0); for (Coordinate point : points) { guess.x += point.x / points.length; @@ -1243,22 +1247,22 @@ private static Coordinate initGuess (Coordinate[]points){ return guess; } - private static Coordinate[] extractCoordinates (Geometry geometry){ + private static Coordinate[] extractCoordinates(Geometry geometry) { Coordinate[] points = geometry.getCoordinates(); - if (points.length == 0) + if(points.length == 0) return points; Coordinate[] coordinates = new Coordinate[points.length]; - for (int i = 0; i < points.length; i++) { + for(int i = 0; i < points.length; i++) { boolean is3d = !Double.isNaN(points[i].z); coordinates[i] = points[i].copy(); - if (!is3d) + if(!is3d) coordinates[i].z = 0.0; } return coordinates; } - public static int numPoints (Geometry geometry) throws Exception { + public static int numPoints(Geometry geometry) throws Exception { String geometryType = geometry.getGeometryType(); if (!(Geometry.TYPENAME_LINESTRING.equalsIgnoreCase(geometryType))) { throw new IllegalArgumentException("Unsupported geometry type: " + geometryType + ", only LineString geometry is supported."); @@ -1266,15 +1270,15 @@ public static int numPoints (Geometry geometry) throws Exception { return geometry.getNumPoints(); } - public static Geometry force3D (Geometry geometry,double zValue){ + public static Geometry force3D(Geometry geometry, double zValue) { return GeomUtils.get3DGeom(geometry, zValue); } - public static Geometry force3D (Geometry geometry){ + public static Geometry force3D(Geometry geometry) { return GeomUtils.get3DGeom(geometry, 0.0); } - public static Integer nRings (Geometry geometry) throws Exception { + public static Integer nRings(Geometry geometry) throws Exception { String geometryType = geometry.getGeometryType(); if (!(geometry instanceof Polygon || geometry instanceof MultiPolygon)) { throw new IllegalArgumentException("Unsupported geometry type: " + geometryType + ", only Polygon or MultiPolygon geometries are supported."); @@ -1283,7 +1287,7 @@ public static Integer nRings (Geometry geometry) throws Exception { if (geometry instanceof Polygon) { Polygon polygon = (Polygon) geometry; numRings = GeomUtils.getPolygonNumRings(polygon); - } else { + }else { MultiPolygon multiPolygon = (MultiPolygon) geometry; int numPolygons = multiPolygon.getNumGeometries(); for (int i = 0; i < numPolygons; i++) { @@ -1294,78 +1298,75 @@ public static Integer nRings (Geometry geometry) throws Exception { return numRings; } - public static Geometry translate (Geometry geometry,double deltaX, double deltaY, double deltaZ){ + public static Geometry translate(Geometry geometry, double deltaX, double deltaY, double deltaZ) { if (!geometry.isEmpty()) { GeomUtils.translateGeom(geometry, deltaX, deltaY, deltaZ); } return geometry; } - public static Geometry translate (Geometry geometry,double deltaX, double deltaY){ + public static Geometry translate(Geometry geometry, double deltaX, double deltaY) { if (!geometry.isEmpty()) { GeomUtils.translateGeom(geometry, deltaX, deltaY, 0.0); } return geometry; } - public static Geometry affine (Geometry geometry,double a, double b, double c, double d, double e, double f, - double g, double h, double i, double xOff, double yOff, - double zOff){ + public static Geometry affine(Geometry geometry, double a, double b, double c, double d, double e, double f, double g, double h, double i, double xOff, double yOff, + double zOff) { if (!geometry.isEmpty()) { GeomUtils.affineGeom(geometry, a, b, c, d, e, f, g, h, i, xOff, yOff, zOff); } return geometry; } - public static Geometry affine (Geometry geometry,double a, double b, double d, double e, double xOff, - double yOff){ + public static Geometry affine(Geometry geometry, double a, double b, double d, double e, double xOff, double yOff) { if (!geometry.isEmpty()) { GeomUtils.affineGeom(geometry, a, b, null, d, e, null, null, null, null, xOff, yOff, null); } return geometry; } - public static Geometry geometricMedian (Geometry geometry,double tolerance, int maxIter, - boolean failIfNotConverged) throws Exception { + public static Geometry geometricMedian(Geometry geometry, double tolerance, int maxIter, boolean failIfNotConverged) throws Exception { String geometryType = geometry.getGeometryType(); - if (!(Geometry.TYPENAME_POINT.equals(geometryType) || Geometry.TYPENAME_MULTIPOINT.equals(geometryType))) { + if(!(Geometry.TYPENAME_POINT.equals(geometryType) || Geometry.TYPENAME_MULTIPOINT.equals(geometryType))) { throw new Exception("Unsupported geometry type: " + geometryType); } Coordinate[] coordinates = extractCoordinates(geometry); - if (coordinates.length == 0) + if(coordinates.length == 0) return new Point(null, GEOMETRY_FACTORY); Coordinate median = initGuess(coordinates); double delta = Double.MAX_VALUE; double[] distances = new double[coordinates.length]; // preallocate to reduce gc pressure for large iterations - for (int i = 0; i < maxIter && delta > tolerance; i++) + for(int i = 0; i < maxIter && delta > tolerance; i++) delta = iteratePoints(median, coordinates, distances); if (failIfNotConverged && delta > tolerance) throw new Exception(String.format("Median failed to converge within %.1E after %d iterations.", tolerance, maxIter)); boolean is3d = !Double.isNaN(geometry.getCoordinate().z); - if (!is3d) + if(!is3d) median.z = Double.NaN; Point point = new Point(new CoordinateArraySequence(new Coordinate[]{median}), GEOMETRY_FACTORY); point.setSRID(geometry.getSRID()); return point; } - public static Geometry geometricMedian (Geometry geometry,double tolerance, int maxIter) throws Exception { + public static Geometry geometricMedian(Geometry geometry, double tolerance, int maxIter) throws Exception { return geometricMedian(geometry, tolerance, maxIter, false); } - public static Geometry geometricMedian (Geometry geometry,double tolerance) throws Exception { + public static Geometry geometricMedian(Geometry geometry, double tolerance) throws Exception { return geometricMedian(geometry, tolerance, DEFAULT_MAX_ITER, false); } - public static Geometry geometricMedian (Geometry geometry) throws Exception { + public static Geometry geometricMedian(Geometry geometry) throws Exception { return geometricMedian(geometry, DEFAULT_TOLERANCE, DEFAULT_MAX_ITER, false); } - public static double frechetDistance (Geometry g1, Geometry g2){ + public static double frechetDistance(Geometry g1, Geometry g2) { return GeomUtils.getFrechetDistance(g1, g2); } - public static boolean isCollection (Geometry geometry){ + public static boolean isCollection(Geometry geometry) { String geoType = geometry.getGeometryType(); return Geometry.TYPENAME_GEOMETRYCOLLECTION.equalsIgnoreCase(geoType) || Geometry.TYPENAME_MULTIPOINT.equalsIgnoreCase(geoType) || @@ -1373,10 +1374,10 @@ public static boolean isCollection (Geometry geometry){ Geometry.TYPENAME_MULTILINESTRING.equalsIgnoreCase(geoType); } - public static Geometry boundingDiagonal (Geometry geometry){ + public static Geometry boundingDiagonal(Geometry geometry) { if (geometry.isEmpty()) { return GEOMETRY_FACTORY.createLineString(); - } else { + }else { Double startX = null, startY = null, startZ = null, endX = null, endY = null, endZ = null; boolean is3d = !Double.isNaN(geometry.getCoordinate().z); @@ -1397,37 +1398,31 @@ public static Geometry boundingDiagonal (Geometry geometry){ if (is3d) { startCoordinate = new Coordinate(startX, startY, startZ); endCoordinate = new Coordinate(endX, endY, endZ); - } else { + }else { startCoordinate = new Coordinate(startX, startY); endCoordinate = new Coordinate(endX, endY); } - return GEOMETRY_FACTORY.createLineString(new Coordinate[]{startCoordinate, endCoordinate}); + return GEOMETRY_FACTORY.createLineString(new Coordinate[] {startCoordinate, endCoordinate}); } } - public static double angle (Geometry point1, Geometry point2, Geometry point3, Geometry point4) throws IllegalArgumentException { + public static double angle(Geometry point1, Geometry point2, Geometry point3, Geometry point4) throws IllegalArgumentException { if (point3 == null && point4 == null) return Functions.angle(point1, point2); else if (point4 == null) return Functions.angle(point1, point2, point3); - if (GeomUtils.isAnyGeomEmpty(point1, point2, point3, point4)) - throw new IllegalArgumentException("ST_Angle cannot support empty geometries."); - if (!(point1 instanceof Point && point2 instanceof Point && point3 instanceof Point && point4 instanceof Point)) - throw new IllegalArgumentException("ST_Angle supports either only POINT or only LINESTRING geometries."); + if (GeomUtils.isAnyGeomEmpty(point1, point2, point3, point4)) throw new IllegalArgumentException("ST_Angle cannot support empty geometries."); + if (!(point1 instanceof Point && point2 instanceof Point && point3 instanceof Point && point4 instanceof Point)) throw new IllegalArgumentException("ST_Angle supports either only POINT or only LINESTRING geometries."); return GeomUtils.calcAngle(point1.getCoordinate(), point2.getCoordinate(), point3.getCoordinate(), point4.getCoordinate()); } - public static double angle (Geometry point1, Geometry point2, Geometry point3) throws IllegalArgumentException { - if (GeomUtils.isAnyGeomEmpty(point1, point2, point3)) - throw new IllegalArgumentException("ST_Angle cannot support empty geometries."); - if (!(point1 instanceof Point && point2 instanceof Point && point3 instanceof Point)) - throw new IllegalArgumentException("ST_Angle supports either only POINT or only LINESTRING geometries."); + public static double angle(Geometry point1, Geometry point2, Geometry point3) throws IllegalArgumentException { + if (GeomUtils.isAnyGeomEmpty(point1, point2, point3)) throw new IllegalArgumentException("ST_Angle cannot support empty geometries."); + if (!(point1 instanceof Point && point2 instanceof Point && point3 instanceof Point)) throw new IllegalArgumentException("ST_Angle supports either only POINT or only LINESTRING geometries."); return GeomUtils.calcAngle(point2.getCoordinate(), point1.getCoordinate(), point2.getCoordinate(), point3.getCoordinate()); } - public static double angle (Geometry line1, Geometry line2) throws IllegalArgumentException { - if (GeomUtils.isAnyGeomEmpty(line1, line2)) - throw new IllegalArgumentException("ST_Angle cannot support empty geometries."); - if (!(line1 instanceof LineString && line2 instanceof LineString)) - throw new IllegalArgumentException("ST_Angle supports either only POINT or only LINESTRING geometries."); + public static double angle(Geometry line1, Geometry line2) throws IllegalArgumentException { + if (GeomUtils.isAnyGeomEmpty(line1, line2)) throw new IllegalArgumentException("ST_Angle cannot support empty geometries."); + if (!(line1 instanceof LineString && line2 instanceof LineString)) throw new IllegalArgumentException("ST_Angle supports either only POINT or only LINESTRING geometries."); Coordinate[] startEndLine1 = GeomUtils.getStartEndCoordinates(line1); Coordinate[] startEndLine2 = GeomUtils.getStartEndCoordinates(line2); assert startEndLine1 != null; @@ -1435,23 +1430,23 @@ public static double angle (Geometry line1, Geometry line2) throws IllegalArgume return GeomUtils.calcAngle(startEndLine1[0], startEndLine1[1], startEndLine2[0], startEndLine2[1]); } - public static double degrees ( double angleInRadian){ + public static double degrees(double angleInRadian) { return GeomUtils.toDegrees(angleInRadian); } - public static Double hausdorffDistance (Geometry g1, Geometry g2,double densityFrac){ + public static Double hausdorffDistance(Geometry g1, Geometry g2, double densityFrac) { return GeomUtils.getHausdorffDistance(g1, g2, densityFrac); } - public static Double hausdorffDistance (Geometry g1, Geometry g2){ + public static Double hausdorffDistance(Geometry g1, Geometry g2) { return GeomUtils.getHausdorffDistance(g1, g2, -1); } - public static String isValidReason (Geometry geom){ + public static String isValidReason(Geometry geom) { return isValidReason(geom, OGC_SFS_VALIDITY); } - public static String isValidReason (Geometry geom,int flag){ + public static String isValidReason(Geometry geom, int flag) { IsValidOp isValidOp = new IsValidOp(geom); // Set the validity model based on flags From 8bef0130cb00adf3c5c91f846c608f1f0cc5c0ac Mon Sep 17 00:00:00 2001 From: Pranav Toggi Date: Sun, 3 Mar 2024 22:44:41 -0500 Subject: [PATCH 54/54] Address comments - update docs --- docs/api/flink/Function.md | 4 ++-- docs/api/snowflake/vector-data/Function.md | 2 +- docs/api/sql/Function.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/api/flink/Function.md b/docs/api/flink/Function.md index 8625c7d0c5..799a58c546 100644 --- a/docs/api/flink/Function.md +++ b/docs/api/flink/Function.md @@ -557,11 +557,11 @@ Introduction: Returns a geometry/geography that represents all points whose dist Mode of buffer calculation (Since: `v1.6.0`): -The optional third parameter, `useSpheroid`, controls the mode of buffer calculation. +The optional third parameter, `useSpheroid`, controls the mode of buffer calculation. - Planar Buffering (default): When `useSpheroid` is false, `ST_Buffer` performs standard planar buffering based on the provided parameters. - Spheroidal Buffering: - - When `useSpheroid` is set to true, the function returns the spheroidal buffer polygon for more accurate representation over the Earth. + - When `useSpheroid` is set to true, the function returns the spheroidal buffer polygon for more accurate representation over the Earth. In this mode, the unit of the buffer distance is interpreted as meters. - ST_Buffer first determines the most appropriate Spatial Reference Identifier (SRID) for a given geometry, based on its spatial extent and location, using `ST_BestSRID`. - The geometry is then transformed from its original SRID to the selected SRID. If the input geometry does not have a set SRID, `ST_Buffer` defaults to using WGS 84 (SRID 4326) as its original SRID. - The standard planar buffer operation is then applied in this coordinate system. diff --git a/docs/api/snowflake/vector-data/Function.md b/docs/api/snowflake/vector-data/Function.md index 89d8116efa..cee7b315d9 100644 --- a/docs/api/snowflake/vector-data/Function.md +++ b/docs/api/snowflake/vector-data/Function.md @@ -397,7 +397,7 @@ The optional third parameter, `useSpheroid`, controls the mode of buffer calcula - Planar Buffering (default): When `useSpheroid` is false, `ST_Buffer` performs standard planar buffering based on the provided parameters. - Spheroidal Buffering: - - When `useSpheroid` is set to true, the function returns the spheroidal buffer polygon for more accurate representation over the Earth. + - When `useSpheroid` is set to true, the function returns the spheroidal buffer polygon for more accurate representation over the Earth. In this mode, the unit of the buffer distance is interpreted as meters. - ST_Buffer first determines the most appropriate Spatial Reference Identifier (SRID) for a given geometry, based on its spatial extent and location, using `ST_BestSRID`. - The geometry is then transformed from its original SRID to the selected SRID. If the input geometry does not have a set SRID, `ST_Buffer` defaults to using WGS 84 (SRID 4326) as its original SRID. - The standard planar buffer operation is then applied in this coordinate system. diff --git a/docs/api/sql/Function.md b/docs/api/sql/Function.md index 4d867e885f..a5e8c4d2ed 100644 --- a/docs/api/sql/Function.md +++ b/docs/api/sql/Function.md @@ -559,7 +559,7 @@ The optional third parameter, `useSpheroid`, controls the mode of buffer calcula - Planar Buffering (default): When `useSpheroid` is false, `ST_Buffer` performs standard planar buffering based on the provided parameters. - Spheroidal Buffering: - - When `useSpheroid` is set to true, the function returns the spheroidal buffer polygon for more accurate representation over the Earth. + - When `useSpheroid` is set to true, the function returns the spheroidal buffer polygon for more accurate representation over the Earth. In this mode, the unit of the buffer distance is interpreted as meters. - ST_Buffer first determines the most appropriate Spatial Reference Identifier (SRID) for a given geometry, based on its spatial extent and location, using `ST_BestSRID`. - The geometry is then transformed from its original SRID to the selected SRID. If the input geometry does not have a set SRID, `ST_Buffer` defaults to using WGS 84 (SRID 4326) as its original SRID. - The standard planar buffer operation is then applied in this coordinate system.