This is an automated email from the ASF dual-hosted git repository.
jiayu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sedona.git
The following commit(s) were added to refs/heads/master by this push:
new 658bf6054 [SEDONA-626] Make ST functions return geometries with
correct SRIDs (#1521)
658bf6054 is described below
commit 658bf60548be0fa3c60ea8b0a9637dabab4b30bc
Author: Kristin Cowalcijk <[email protected]>
AuthorDate: Sun Jul 14 09:59:19 2024 +0800
[SEDONA-626] Make ST functions return geometries with correct SRIDs (#1521)
* Deserialized geometries have factories with proper SRID values
* The SRIDs of geometries could be persisted in transformations
* Fix handling of SRIDs for hex-wkb constructors
* Fix handling of SRIDs for hex-wkb constructors
* Add Spark tests for SRID preserving ST functions
---
.../org/apache/sedona/common/Constructors.java | 33 +++--
.../java/org/apache/sedona/common/Functions.java | 144 ++++++++++---------
.../apache/sedona/common/FunctionsGeoTools.java | 41 +++++-
.../sedona/common/geometryObjects/Circle.java | 3 +-
.../common/geometrySerde/GeometrySerializer.java | 124 +++++++++--------
.../sedona/common/raster/GeometryFunctions.java | 18 +--
.../sedona/common/simplify/BaseSimplifier.java | 6 +-
.../simplify/GeometryCollectionSimplifier.java | 1 +
.../common/simplify/LineStringSimplifier.java | 2 +
.../sedona/common/simplify/PolygonSimplifier.java | 4 +-
.../common/subDivide/GeometrySubDivider.java | 1 -
.../sedona/common/subDivide/PivotFinder.java | 3 +-
.../apache/sedona/common/utils/FormatUtils.java | 8 +-
.../org/apache/sedona/common/utils/GeomUtils.java | 29 ++--
.../common/utils/GeometryForce3DTransformer.java | 52 +++++++
.../org/apache/sedona/common/utils/H3Utils.java | 3 +-
.../org/apache/sedona/common/ConstructorsTest.java | 42 ++++++
.../org/apache/sedona/common/FunctionsTest.java | 67 ++++++---
docs/api/sql/Constructor.md | 36 -----
.../sedona/flink/expressions/Constructors.java | 23 ++-
.../sedona/snowflake/snowsql/GeometrySerde.java | 4 +-
.../org/apache/sedona/snowflake/snowsql/UDFs.java | 6 +-
.../sql/sedona_sql/expressions/Constructors.scala | 29 ++--
.../sql/sedona_sql/expressions/Functions.scala | 4 +-
.../sedona_sql/expressions/st_constructors.scala | 12 +-
.../org/apache/sedona/sql/PreserveSRIDSuite.scala | 154 +++++++++++++++++++++
.../apache/sedona/sql/constructorTestScala.scala | 11 ++
.../org/apache/sedona/sql/functionTestScala.scala | 14 +-
28 files changed, 581 insertions(+), 293 deletions(-)
diff --git a/common/src/main/java/org/apache/sedona/common/Constructors.java
b/common/src/main/java/org/apache/sedona/common/Constructors.java
index 572fba660..4789f4430 100644
--- a/common/src/main/java/org/apache/sedona/common/Constructors.java
+++ b/common/src/main/java/org/apache/sedona/common/Constructors.java
@@ -66,16 +66,28 @@ public class Constructors {
}
public static Geometry geomFromWKB(byte[] wkb) throws ParseException {
- return new WKBReader().read(wkb);
+ return geomFromWKB(wkb, -1);
+ }
+
+ public static Geometry geomFromWKB(byte[] wkb, int SRID) throws
ParseException {
+ Geometry geom = new WKBReader().read(wkb);
+ if (geom.getFactory().getSRID() != geom.getSRID() || (SRID >= 0 &&
geom.getSRID() != SRID)) {
+ // Make sure that the geometry and the geometry factory have the correct
SRID
+ if (SRID < 0) {
+ SRID = geom.getSRID();
+ }
+ return Functions.setSRID(geom, SRID);
+ } else {
+ return geom;
+ }
}
public static Geometry pointFromWKB(byte[] wkb) throws ParseException {
- return pointFromWKB(wkb, 0);
+ return pointFromWKB(wkb, -1);
}
public static Geometry pointFromWKB(byte[] wkb, int srid) throws
ParseException {
- GeometryFactory geometryFactory = new GeometryFactory(new
PrecisionModel(), srid);
- Geometry geom = new WKBReader(geometryFactory).read(wkb);
+ Geometry geom = geomFromWKB(wkb, srid);
if (!(geom instanceof Point)) {
return null;
}
@@ -83,12 +95,11 @@ public class Constructors {
}
public static Geometry lineFromWKB(byte[] wkb) throws ParseException {
- return lineFromWKB(wkb, 0);
+ return lineFromWKB(wkb, -1);
}
public static Geometry lineFromWKB(byte[] wkb, int srid) throws
ParseException {
- GeometryFactory geometryFactory = new GeometryFactory(new
PrecisionModel(), srid);
- Geometry geom = new WKBReader(geometryFactory).read(wkb);
+ Geometry geom = geomFromWKB(wkb, srid);
if (!(geom instanceof LineString)) {
return null;
}
@@ -141,17 +152,15 @@ public class Constructors {
*/
public static Geometry point(double x, double y) {
// See srid parameter discussion in
https://issues.apache.org/jira/browse/SEDONA-234
- GeometryFactory geometryFactory = new GeometryFactory();
- return geometryFactory.createPoint(new Coordinate(x, y));
+ return GEOMETRY_FACTORY.createPoint(new Coordinate(x, y));
}
public static Geometry makePointM(double x, double y, double m) {
- GeometryFactory geometryFactory = new GeometryFactory();
- return geometryFactory.createPoint(new CoordinateXYM(x, y, m));
+ return GEOMETRY_FACTORY.createPoint(new CoordinateXYM(x, y, m));
}
public static Geometry makePoint(Double x, Double y, Double z, Double m) {
- GeometryFactory geometryFactory = new GeometryFactory();
+ GeometryFactory geometryFactory = GEOMETRY_FACTORY;
if (x == null || y == null) {
return null;
}
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 ebedb1762..a43519db1 100644
--- a/common/src/main/java/org/apache/sedona/common/Functions.java
+++ b/common/src/main/java/org/apache/sedona/common/Functions.java
@@ -37,7 +37,6 @@ import
org.locationtech.jts.algorithm.construct.LargestEmptyCircle;
import org.locationtech.jts.algorithm.construct.MaximumInscribedCircle;
import org.locationtech.jts.algorithm.hull.ConcaveHull;
import org.locationtech.jts.geom.*;
-import org.locationtech.jts.geom.impl.CoordinateArraySequence;
import org.locationtech.jts.geom.util.AffineTransformation;
import org.locationtech.jts.geom.util.GeometryFixer;
import org.locationtech.jts.io.ByteOrderValues;
@@ -65,10 +64,6 @@ import
org.locationtech.jts.triangulate.polygon.ConstrainedDelaunayTriangulator;
import org.wololo.jts2geojson.GeoJSONWriter;
public class Functions {
- private static final GeometryFactory GEOMETRY_FACTORY = new
GeometryFactory();
- private static Geometry EMPTY_POLYGON = GEOMETRY_FACTORY.createPolygon(null,
null);
- private static GeometryCollection EMPTY_GEOMETRY_COLLECTION =
- GEOMETRY_FACTORY.createGeometryCollection(null);
private static final double DEFAULT_TOLERANCE = 1e-6;
private static final int DEFAULT_MAX_ITER = 1000;
private static final int OGC_SFS_VALIDITY = 0; // Use usual OGC SFS validity
semantics
@@ -90,7 +85,7 @@ public class Functions {
public static Geometry boundary(Geometry geometry) {
Geometry boundary = geometry.getBoundary();
if (boundary instanceof LinearRing) {
- boundary = GEOMETRY_FACTORY.createLineString(boundary.getCoordinates());
+ boundary =
geometry.getFactory().createLineString(boundary.getCoordinates());
}
return boundary;
}
@@ -546,7 +541,11 @@ public class Functions {
public static Geometry geometryN(Geometry geometry, int n) {
if (n < geometry.getNumGeometries()) {
- return geometry.getGeometryN(n);
+ Geometry subGeom = geometry.getGeometryN(n);
+ if (subGeom.getSRID() == geometry.getSRID()) {
+ return subGeom;
+ }
+ return setSRID(subGeom, geometry.getSRID());
}
return null;
}
@@ -557,7 +556,7 @@ public class Functions {
if (n < polygon.getNumInteriorRing()) {
Geometry interiorRing = polygon.getInteriorRingN(n);
if (interiorRing instanceof LinearRing) {
- interiorRing =
GEOMETRY_FACTORY.createLineString(interiorRing.getCoordinates());
+ interiorRing =
polygon.getFactory().createLineString(interiorRing.getCoordinates());
}
return interiorRing;
}
@@ -575,7 +574,7 @@ public class Functions {
public static Geometry exteriorRing(Geometry geometry) {
Geometry ring = GeomUtils.getExteriorRing(geometry);
if (ring instanceof LinearRing) {
- ring = GEOMETRY_FACTORY.createLineString(ring.getCoordinates());
+ ring = geometry.getFactory().createLineString(ring.getCoordinates());
}
return ring;
}
@@ -763,7 +762,7 @@ public class Functions {
} else {
coordinates.add(position, point.getCoordinate());
}
- return GEOMETRY_FACTORY.createLineString(coordinates.toArray(new
Coordinate[0]));
+ return
linestring.getFactory().createLineString(coordinates.toArray(new
Coordinate[0]));
}
}
return null;
@@ -784,7 +783,7 @@ public class Functions {
position = coordinates.size() - 1;
}
coordinates.remove(position);
- return GEOMETRY_FACTORY.createLineString(coordinates.toArray(new
Coordinate[0]));
+ return
linestring.getFactory().createLineString(coordinates.toArray(new
Coordinate[0]));
}
}
return null;
@@ -799,7 +798,7 @@ public class Functions {
} else {
coordinates.set(position, point.getCoordinate());
}
- return GEOMETRY_FACTORY.createLineString(coordinates.toArray(new
Coordinate[0]));
+ return
linestring.getFactory().createLineString(coordinates.toArray(new
Coordinate[0]));
}
}
return null;
@@ -813,14 +812,14 @@ public class Functions {
for (Coordinate c : geometry.getCoordinates()) {
coordinates.add(c);
}
- return GEOMETRY_FACTORY.createLineString(coordinates.toArray(new
Coordinate[0]));
+ return geometry.getFactory().createLineString(coordinates.toArray(new
Coordinate[0]));
}
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]);
+ return left.getFactory().createPoint(closestPoints[0]);
} catch (Exception e) {
throw new IllegalArgumentException("ST_ClosestPoint doesn't support
empty geometry object.");
}
@@ -884,7 +883,7 @@ public class Functions {
public static Geometry intersection(Geometry leftGeometry, Geometry
rightGeometry) {
boolean isIntersects = leftGeometry.intersects(rightGeometry);
if (!isIntersects) {
- return EMPTY_POLYGON;
+ return leftGeometry.getFactory().createPolygon();
}
if (leftGeometry.contains(rightGeometry)) {
return rightGeometry;
@@ -925,7 +924,7 @@ public class Functions {
return geometry;
}
}
- return EMPTY_GEOMETRY_COLLECTION;
+ return geometry.getFactory().createGeometryCollection();
}
public static Geometry minimumBoundingCircle(Geometry geometry, int
quadrantSegments) {
@@ -975,7 +974,7 @@ public class Functions {
MinimumBoundingCircle minimumBoundingCircle = new
MinimumBoundingCircle(geometry);
Coordinate coods = minimumBoundingCircle.getCentre();
double radius = minimumBoundingCircle.getRadius();
- Point centre = GEOMETRY_FACTORY.createPoint(coods);
+ Point centre = geometry.getFactory().createPoint(coods);
return Pair.of(centre, radius);
}
@@ -998,7 +997,7 @@ public class Functions {
double length = geom.getLength();
LengthIndexedLine indexedLine = new LengthIndexedLine(geom);
Coordinate interPoint = indexedLine.extractPoint(length * fraction);
- return GEOMETRY_FACTORY.createPoint(interPoint);
+ return geom.getFactory().createPoint(interPoint);
}
/**
@@ -1023,7 +1022,7 @@ public class Functions {
polygons.add((Polygon) transformCW(polygon));
}
- return new GeometryFactory().createMultiPolygon(polygons.toArray(new
Polygon[0]));
+ return geom.getFactory().createMultiPolygon(polygons.toArray(new
Polygon[0]));
}
// Non-polygonal geometries are returned unchanged
return geom;
@@ -1038,7 +1037,8 @@ public class Functions {
interiorRings.add(transformCW(polygon.getInteriorRingN(i), false));
}
- return new GeometryFactory(polygon.getPrecisionModel(), polygon.getSRID())
+ return polygon
+ .getFactory()
.createPolygon(exteriorRingEnforced, interiorRings.toArray(new
LinearRing[0]));
}
@@ -1135,7 +1135,7 @@ public class Functions {
polygons.add((Polygon) transformCCW(polygon));
}
- return new GeometryFactory().createMultiPolygon(polygons.toArray(new
Polygon[0]));
+ return geom.getFactory().createMultiPolygon(polygons.toArray(new
Polygon[0]));
}
// Non-polygonal geometries are returned unchanged
return geom;
@@ -1150,7 +1150,8 @@ public class Functions {
interiorRings.add(transformCCW(polygon.getInteriorRingN(i), false));
}
- return new GeometryFactory(polygon.getPrecisionModel(), polygon.getSRID())
+ return polygon
+ .getFactory()
.createPolygon(exteriorRingEnforced, interiorRings.toArray(new
LinearRing[0]));
}
@@ -1240,7 +1241,7 @@ public class Functions {
if (!isIntersects) {
return leftGeometry;
} else if (rightGeometry.contains(leftGeometry)) {
- return EMPTY_POLYGON;
+ return leftGeometry.getFactory().createPolygon();
} else {
return leftGeometry.difference(rightGeometry);
}
@@ -1248,7 +1249,7 @@ public class Functions {
public static Geometry split(Geometry input, Geometry blade) {
// check input geometry
- return new GeometrySplitter(GEOMETRY_FACTORY).split(input, blade);
+ return new GeometrySplitter(input.getFactory()).split(input, blade);
}
public static Integer dimension(Geometry geometry) {
@@ -1475,9 +1476,8 @@ public class Functions {
}
public static Geometry[] dumpPoints(Geometry geometry) {
- return Arrays.stream(geometry.getCoordinates())
- .map(GEOMETRY_FACTORY::createPoint)
- .toArray(Point[]::new);
+ GeometryFactory factory = geometry.getFactory();
+ return
Arrays.stream(geometry.getCoordinates()).map(factory::createPoint).toArray(Point[]::new);
}
public static Geometry symDifference(Geometry leftGeom, Geometry rightGeom) {
@@ -1489,7 +1489,8 @@ public class Functions {
}
public static Geometry union(Geometry[] geoms) {
- return GEOMETRY_FACTORY.createGeometryCollection(geoms).union();
+ GeometryFactory factory = (geoms.length > 0) ? geoms[0].getFactory() : new
GeometryFactory();
+ return factory.createGeometryCollection(geoms).union();
}
public static Geometry unaryUnion(Geometry geom) {
@@ -1497,18 +1498,19 @@ public class Functions {
}
public static Geometry createMultiGeometryFromOneElement(Geometry geometry) {
+ GeometryFactory factory = geometry.getFactory();
if (geometry instanceof Circle) {
- return GEOMETRY_FACTORY.createGeometryCollection(new Circle[] {(Circle)
geometry});
+ return factory.createGeometryCollection(new Circle[] {(Circle)
geometry});
} else if (geometry instanceof GeometryCollection) {
return geometry;
} else if (geometry instanceof LineString) {
- return GEOMETRY_FACTORY.createMultiLineString(new LineString[]
{(LineString) geometry});
+ return factory.createMultiLineString(new LineString[] {(LineString)
geometry});
} else if (geometry instanceof Point) {
- return GEOMETRY_FACTORY.createMultiPoint(new Point[] {(Point) geometry});
+ return factory.createMultiPoint(new Point[] {(Point) geometry});
} else if (geometry instanceof Polygon) {
- return GEOMETRY_FACTORY.createMultiPolygon(new Polygon[] {(Polygon)
geometry});
+ return factory.createMultiPolygon(new Polygon[] {(Polygon) geometry});
} else {
- return GEOMETRY_FACTORY.createGeometryCollection();
+ return factory.createGeometryCollection();
}
}
@@ -1540,10 +1542,16 @@ public class Functions {
}
Coordinate[] coords = coordinates.toArray(new Coordinate[0]);
- return GEOMETRY_FACTORY.createLineString(coords);
+ GeometryFactory factory = (geoms.length > 0) ? geoms[0].getFactory() : new
GeometryFactory();
+ return factory.createLineString(coords);
}
public static Geometry makePolygon(Geometry shell, Geometry[] holes) {
+ GeometryFactory factory = shell.getFactory();
+ return makePolygon(shell, holes, factory);
+ }
+
+ public static Geometry makePolygon(Geometry shell, Geometry[] holes,
GeometryFactory factory) {
try {
if (holes != null) {
LinearRing[] interiorRings =
@@ -1554,11 +1562,11 @@ public class Functions {
&& !h.isEmpty()
&& h instanceof LineString
&& ((LineString) h).isClosed())
- .map(h ->
GEOMETRY_FACTORY.createLinearRing(h.getCoordinates()))
+ .map(h -> factory.createLinearRing(h.getCoordinates()))
.toArray(LinearRing[]::new);
if (interiorRings.length != 0) {
- return GEOMETRY_FACTORY.createPolygon(
- GEOMETRY_FACTORY.createLinearRing(shell.getCoordinates()),
+ return factory.createPolygon(
+ factory.createLinearRing(shell.getCoordinates()),
Arrays.stream(holes)
.filter(
h ->
@@ -1566,32 +1574,30 @@ public class Functions {
&& !h.isEmpty()
&& h instanceof LineString
&& ((LineString) h).isClosed())
- .map(h ->
GEOMETRY_FACTORY.createLinearRing(h.getCoordinates()))
+ .map(h -> factory.createLinearRing(h.getCoordinates()))
.toArray(LinearRing[]::new));
}
}
- return GEOMETRY_FACTORY.createPolygon(
- GEOMETRY_FACTORY.createLinearRing(shell.getCoordinates()));
+ return
factory.createPolygon(factory.createLinearRing(shell.getCoordinates()));
} catch (IllegalArgumentException e) {
return null;
}
}
public static Geometry makepolygonWithSRID(Geometry lineString, Integer
srid) {
- Geometry geom = makePolygon(lineString, null);
- if (geom != null) {
- geom.setSRID(srid);
- }
- return geom;
+ GeometryFactory factory =
+ (srid != null) ? new GeometryFactory(new PrecisionModel(), srid) :
lineString.getFactory();
+ return makePolygon(lineString, null, factory);
}
public static Geometry createMultiGeometry(Geometry[] geometries) {
if (geometries.length > 1) {
- return GEOMETRY_FACTORY.buildGeometry(Arrays.asList(geometries));
+ return
geometries[0].getFactory().buildGeometry(Arrays.asList(geometries));
} else if (geometries.length == 1) {
return createMultiGeometryFromOneElement(geometries[0]);
} else {
- return GEOMETRY_FACTORY.createGeometryCollection();
+ GeometryFactory factory = new GeometryFactory();
+ return factory.createGeometryCollection();
}
}
@@ -1599,20 +1605,21 @@ public class Functions {
if (geomType == null) {
return collectionExtract(geometry);
}
+ GeometryFactory factory = geometry.getFactory();
Class<? extends Geometry> geomClass;
GeometryCollection emptyResult;
switch (geomType) {
case 1:
geomClass = Point.class;
- emptyResult = GEOMETRY_FACTORY.createMultiPoint();
+ emptyResult = factory.createMultiPoint();
break;
case 2:
geomClass = LineString.class;
- emptyResult = GEOMETRY_FACTORY.createMultiLineString();
+ emptyResult = factory.createMultiLineString();
break;
case 3:
geomClass = Polygon.class;
- emptyResult = GEOMETRY_FACTORY.createMultiPolygon();
+ emptyResult = factory.createMultiPolygon();
break;
default:
throw new IllegalArgumentException("Invalid geometry type");
@@ -1626,21 +1633,22 @@ public class Functions {
public static Geometry collectionExtract(Geometry geometry) {
List<Geometry> geometries = GeomUtils.extractGeometryCollection(geometry);
+ GeometryFactory factory = geometry.getFactory();
Polygon[] polygons =
geometries.stream().filter(g -> g instanceof
Polygon).toArray(Polygon[]::new);
if (polygons.length > 0) {
- return GEOMETRY_FACTORY.createMultiPolygon(polygons);
+ return factory.createMultiPolygon(polygons);
}
LineString[] lines =
geometries.stream().filter(g -> g instanceof
LineString).toArray(LineString[]::new);
if (lines.length > 0) {
- return GEOMETRY_FACTORY.createMultiLineString(lines);
+ return factory.createMultiLineString(lines);
}
Point[] points = geometries.stream().filter(g -> g instanceof
Point).toArray(Point[]::new);
if (points.length > 0) {
- return GEOMETRY_FACTORY.createMultiPoint(points);
+ return factory.createMultiPoint(points);
}
- return GEOMETRY_FACTORY.createGeometryCollection();
+ return factory.createGeometryCollection();
}
// ported from
@@ -1785,15 +1793,15 @@ public class Functions {
}
public static Geometry force3D(Geometry geometry, double zValue) {
- return GeomUtils.get3DGeom(geometry, zValue);
+ return GeometryForce3DTransformer.transform(geometry, zValue);
}
public static Geometry force3D(Geometry geometry) {
- return GeomUtils.get3DGeom(geometry, 0.0);
+ return GeometryForce3DTransformer.transform(geometry, 0.0);
}
public static Geometry forceCollection(Geometry geom) {
- return new
GeometryFactory().createGeometryCollection(convertGeometryToArray(geom));
+ return
geom.getFactory().createGeometryCollection(convertGeometryToArray(geom));
}
private static Geometry[] convertGeometryToArray(Geometry geom) {
@@ -1872,13 +1880,14 @@ public class Functions {
public static Geometry geometricMedian(
Geometry geometry, double tolerance, int maxIter, boolean
failIfNotConverged)
throws Exception {
+ GeometryFactory factory = geometry.getFactory();
String geometryType = geometry.getGeometryType();
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) return new Point(null, GEOMETRY_FACTORY);
+ if (coordinates.length == 0) return new Point(null, factory);
Coordinate median = initGuess(coordinates);
double delta = Double.MAX_VALUE;
double[] distances =
@@ -1891,10 +1900,7 @@ public class Functions {
"Median failed to converge within %.1E after %d iterations.",
tolerance, maxIter));
boolean is3d = !Double.isNaN(geometry.getCoordinate().z);
if (!is3d) median.z = Double.NaN;
- Point point =
- new Point(new CoordinateArraySequence(new Coordinate[] {median}),
GEOMETRY_FACTORY);
- point.setSRID(geometry.getSRID());
- return point;
+ return factory.createPoint(median);
}
public static Geometry geometricMedian(Geometry geometry, double tolerance,
int maxIter)
@@ -1923,8 +1929,9 @@ public class Functions {
}
public static Geometry boundingDiagonal(Geometry geometry) {
+ GeometryFactory factory = geometry.getFactory();
if (geometry.isEmpty()) {
- return GEOMETRY_FACTORY.createLineString();
+ return factory.createLineString();
} else {
Double startX = null, startY = null, startZ = null, endX = null, endY =
null, endZ = null;
boolean is3d = !Double.isNaN(geometry.getCoordinate().z);
@@ -1949,7 +1956,7 @@ public class Functions {
startCoordinate = new Coordinate(startX, startY);
endCoordinate = new Coordinate(endX, endY);
}
- return GEOMETRY_FACTORY.createLineString(new Coordinate[]
{startCoordinate, endCoordinate});
+ return factory.createLineString(new Coordinate[] {startCoordinate,
endCoordinate});
}
}
@@ -2064,8 +2071,9 @@ public class Functions {
* @return A GeometryCollection containing the resultant polygons.
*/
public static Geometry polygonize(Geometry geometry) {
+ GeometryFactory factory = (geometry != null) ? geometry.getFactory() : new
GeometryFactory();
if (geometry == null || geometry.isEmpty()) {
- return GEOMETRY_FACTORY.createGeometryCollection(null);
+ return factory.createGeometryCollection(null);
}
if (geometry instanceof GeometryCollection) {
@@ -2078,9 +2086,9 @@ public class Functions {
Collection polygons = polygonizer.getPolygons();
Geometry[] polyArray = (Geometry[]) polygons.toArray(new Geometry[0]);
- return GEOMETRY_FACTORY.createGeometryCollection(polyArray);
+ return factory.createGeometryCollection(polyArray);
} else {
- return GEOMETRY_FACTORY.createGeometryCollection(null);
+ return factory.createGeometryCollection(null);
}
}
@@ -2100,7 +2108,7 @@ public class Functions {
Coordinate[] coordinates = geometry.getCoordinates();
// Creating a MultiPoint from the extracted coordinates
- return GEOMETRY_FACTORY.createMultiPointFromCoords(coordinates);
+ return geometry.getFactory().createMultiPointFromCoords(coordinates);
}
/**
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 1691b79ae..28bf6b904 100644
--- a/common/src/main/java/org/apache/sedona/common/FunctionsGeoTools.java
+++ b/common/src/main/java/org/apache/sedona/common/FunctionsGeoTools.java
@@ -18,24 +18,24 @@
*/
package org.apache.sedona.common;
+import java.util.Set;
import org.geotools.geometry.jts.JTS;
import org.geotools.referencing.CRS;
import org.geotools.referencing.ReferencingFactoryFinder;
import org.geotools.util.factory.Hints;
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;
+import org.opengis.referencing.ReferenceIdentifier;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
public class FunctionsGeoTools {
- private static final GeometryFactory GEOMETRY_FACTORY = new
GeometryFactory();
public static Geometry transform(Geometry geometry, String targetCRS)
throws FactoryException, TransformException {
@@ -82,11 +82,39 @@ public class FunctionsGeoTools {
}
}
CoordinateReferenceSystem sourceCRS = parseCRSString(sourceCRScode);
+ int targetSRID = crsToSRID(targetCRS);
// If sourceCRS and targetCRS are equal, return the geometry unchanged
if (!CRS.equalsIgnoreMetadata(sourceCRS, targetCRS)) {
MathTransform transform = CRS.findMathTransform(sourceCRS, targetCRS,
lenient);
- return JTS.transform(geometry, transform);
- } else return geometry;
+ Geometry transformed = JTS.transform(geometry, transform);
+ transformed = Functions.setSRID(transformed, targetSRID);
+ transformed.setUserData(geometry.getUserData());
+ return transformed;
+ } else {
+ if (geometry.getSRID() != targetSRID) {
+ Geometry transformed = Functions.setSRID(geometry, targetSRID);
+ transformed.setUserData(geometry.getUserData());
+ return transformed;
+ } else {
+ return geometry;
+ }
+ }
+ }
+
+ /**
+ * Get the SRID of a CRS. We use the EPSG code of the CRS if available.
+ *
+ * @param crs CoordinateReferenceSystem
+ * @return SRID
+ */
+ public static int crsToSRID(CoordinateReferenceSystem crs) {
+ Set<ReferenceIdentifier> crsIds = crs.getIdentifiers();
+ for (ReferenceIdentifier crsId : crsIds) {
+ if ("EPSG".equals(crsId.getCodeSpace())) {
+ return Integer.parseInt(crsId.getCode());
+ }
+ }
+ return 0;
}
/**
@@ -138,7 +166,7 @@ public class FunctionsGeoTools {
e.expandBy(Math.max(e.getWidth(), e.getHeight()));
builder.setClipEnvelope(e);
}
- return builder.getDiagram(FunctionsGeoTools.GEOMETRY_FACTORY);
+ return builder.getDiagram(geom.getFactory());
}
public static Geometry bufferSpheroid(Geometry geometry, double radius,
BufferParameters params)
@@ -152,7 +180,7 @@ public class FunctionsGeoTools {
if (Functions.crossesDateLine(geometry)) {
Functions.shiftLongitude(geometry);
}
- // geometry = (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
@@ -169,7 +197,6 @@ public class FunctionsGeoTools {
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);
diff --git
a/common/src/main/java/org/apache/sedona/common/geometryObjects/Circle.java
b/common/src/main/java/org/apache/sedona/common/geometryObjects/Circle.java
index 702b4f811..05eb08e80 100644
--- a/common/src/main/java/org/apache/sedona/common/geometryObjects/Circle.java
+++ b/common/src/main/java/org/apache/sedona/common/geometryObjects/Circle.java
@@ -27,7 +27,6 @@ import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryCollection;
import org.locationtech.jts.geom.GeometryComponentFilter;
-import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.GeometryFilter;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.geom.Point;
@@ -54,7 +53,7 @@ public class Circle extends Geometry {
* @param givenRadius the given radius
*/
public Circle(Geometry centerGeometry, Double givenRadius) {
- super(new GeometryFactory(centerGeometry.getPrecisionModel()));
+ super(centerGeometry.getFactory());
this.centerGeometry = centerGeometry;
Envelope centerGeometryMBR = this.centerGeometry.getEnvelopeInternal();
this.centerPoint =
diff --git
a/common/src/main/java/org/apache/sedona/common/geometrySerde/GeometrySerializer.java
b/common/src/main/java/org/apache/sedona/common/geometrySerde/GeometrySerializer.java
index 5d337076e..508a62901 100644
---
a/common/src/main/java/org/apache/sedona/common/geometrySerde/GeometrySerializer.java
+++
b/common/src/main/java/org/apache/sedona/common/geometrySerde/GeometrySerializer.java
@@ -30,11 +30,12 @@ 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.io.WKBConstants;
public class GeometrySerializer {
private static final Coordinate NULL_COORDINATE = new Coordinate(Double.NaN,
Double.NaN);
- private static final GeometryFactory FACTORY = new GeometryFactory();
+ private static final PrecisionModel PRECISION_MODEL = new PrecisionModel();
public static byte[] serialize(Geometry geometry) {
GeometryBuffer buffer;
@@ -65,6 +66,10 @@ public class GeometrySerializer {
}
public static Geometry deserialize(GeometryBuffer buffer) {
+ return deserialize(buffer, null);
+ }
+
+ public static Geometry deserialize(GeometryBuffer buffer, GeometryFactory
factory) {
checkBufferSize(buffer, 8);
int preambleByte = buffer.getByte(0) & 0xFF;
int wkbType = preambleByte >> 4;
@@ -78,25 +83,28 @@ public class GeometrySerializer {
int srid0 = buffer.getByte(3) & 0xFF;
srid = (srid2 | srid1 | srid0);
}
- return deserialize(buffer, wkbType, srid);
+ if (factory == null) {
+ factory = createGeometryFactory(srid);
+ }
+ return deserialize(buffer, wkbType, factory);
}
- private static Geometry deserialize(GeometryBuffer buffer, int wkbType, int
srid) {
+ private static Geometry deserialize(GeometryBuffer buffer, int wkbType,
GeometryFactory factory) {
switch (wkbType) {
case WKBConstants.wkbPoint:
- return deserializePoint(buffer, srid);
+ return deserializePoint(buffer, factory);
case WKBConstants.wkbMultiPoint:
- return deserializeMultiPoint(buffer, srid);
+ return deserializeMultiPoint(buffer, factory);
case WKBConstants.wkbLineString:
- return deserializeLineString(buffer, srid);
+ return deserializeLineString(buffer, factory);
case WKBConstants.wkbMultiLineString:
- return deserializeMultiLineString(buffer, srid);
+ return deserializeMultiLineString(buffer, factory);
case WKBConstants.wkbPolygon:
- return deserializePolygon(buffer, srid);
+ return deserializePolygon(buffer, factory);
case WKBConstants.wkbMultiPolygon:
- return deserializeMultiPolygon(buffer, srid);
+ return deserializeMultiPolygon(buffer, factory);
case WKBConstants.wkbGeometryCollection:
- return deserializeGeometryCollection(buffer, srid);
+ return deserializeGeometryCollection(buffer, factory);
default:
throw new IllegalArgumentException(
"Cannot deserialize buffer containing unknown geometry type ID: "
+ wkbType);
@@ -116,21 +124,20 @@ public class GeometrySerializer {
return buffer;
}
- private static Point deserializePoint(GeometryBuffer buffer, int srid) {
+ private static Point deserializePoint(GeometryBuffer buffer, GeometryFactory
factory) {
CoordinateType coordType = buffer.getCoordinateType();
int numCoordinates = getBoundedInt(buffer, 4);
Point point;
if (numCoordinates == 0) {
- point = FACTORY.createPoint();
+ point = factory.createPoint();
buffer.mark(8);
} else {
int bufferSize = 8 + coordType.bytes;
checkBufferSize(buffer, bufferSize);
CoordinateSequence coordinates = buffer.getCoordinate(8);
- point = FACTORY.createPoint(coordinates);
+ point = factory.createPoint(coordinates);
buffer.mark(bufferSize);
}
- point.setSRID(srid);
return point;
}
@@ -158,7 +165,7 @@ public class GeometrySerializer {
return buffer;
}
- private static MultiPoint deserializeMultiPoint(GeometryBuffer buffer, int
srid) {
+ private static MultiPoint deserializeMultiPoint(GeometryBuffer buffer,
GeometryFactory factory) {
CoordinateType coordType = buffer.getCoordinateType();
int numPoints = getBoundedInt(buffer, 4);
int bufferSize = 8 + numPoints * coordType.bytes;
@@ -168,16 +175,13 @@ public class GeometrySerializer {
CoordinateSequence coordinates = buffer.getCoordinate(8 + i *
coordType.bytes);
Coordinate coordinate = coordinates.getCoordinate(0);
if (Double.isNaN(coordinate.x)) {
- points[i] = FACTORY.createPoint();
+ points[i] = factory.createPoint();
} else {
- points[i] = FACTORY.createPoint(coordinates);
+ points[i] = factory.createPoint(coordinates);
}
- points[i].setSRID(srid);
}
buffer.mark(bufferSize);
- MultiPoint multiPoint = FACTORY.createMultiPoint(points);
- multiPoint.setSRID(srid);
- return multiPoint;
+ return factory.createMultiPoint(points);
}
private static GeometryBuffer serializeLineString(LineString lineString) {
@@ -200,16 +204,14 @@ public class GeometrySerializer {
return buffer;
}
- private static LineString deserializeLineString(GeometryBuffer buffer, int
srid) {
+ private static LineString deserializeLineString(GeometryBuffer buffer,
GeometryFactory factory) {
CoordinateType coordType = buffer.getCoordinateType();
int numCoordinates = getBoundedInt(buffer, 4);
int bufferSize = 8 + numCoordinates * coordType.bytes;
checkBufferSize(buffer, bufferSize);
CoordinateSequence coordinates = buffer.getCoordinates(8, numCoordinates);
buffer.mark(bufferSize);
- LineString lineString = FACTORY.createLineString(coordinates);
- lineString.setSRID(srid);
- return lineString;
+ return factory.createLineString(coordinates);
}
private static GeometryBuffer serializeMultiLineString(MultiLineString
multiLineString) {
@@ -226,7 +228,8 @@ public class GeometrySerializer {
multiLineString.getSRID(),
bufferSize,
numCoordinates);
- GeomPartSerializer serializer = new GeomPartSerializer(buffer,
coordsOffset, numOffset);
+ GeomPartSerializer serializer =
+ new GeomPartSerializer(buffer, coordsOffset, numOffset,
multiLineString.getFactory());
serializer.writeInt(numLineStrings);
for (int k = 0; k < numLineStrings; k++) {
LineString ls = (LineString) multiLineString.getGeometryN(k);
@@ -236,24 +239,23 @@ public class GeometrySerializer {
return buffer;
}
- private static MultiLineString deserializeMultiLineString(GeometryBuffer
buffer, int srid) {
+ private static MultiLineString deserializeMultiLineString(
+ GeometryBuffer buffer, GeometryFactory factory) {
CoordinateType coordType = buffer.getCoordinateType();
int numCoordinates = getBoundedInt(buffer, 4);
int coordsOffset = 8;
int numOffset = 8 + numCoordinates * coordType.bytes;
- GeomPartSerializer serializer = new GeomPartSerializer(buffer,
coordsOffset, numOffset);
+ GeomPartSerializer serializer =
+ new GeomPartSerializer(buffer, coordsOffset, numOffset, factory);
int numLineStrings = serializer.checkedReadBoundedInt();
serializer.checkRemainingIntsAtLeast(numLineStrings);
LineString[] lineStrings = new LineString[numLineStrings];
for (int k = 0; k < numLineStrings; k++) {
LineString ls = serializer.readLineString();
- ls.setSRID(srid);
lineStrings[k] = ls;
}
serializer.markEndOfBuffer();
- MultiLineString multiLineString =
FACTORY.createMultiLineString(lineStrings);
- multiLineString.setSRID(srid);
- return multiLineString;
+ return factory.createMultiLineString(lineStrings);
}
private static GeometryBuffer serializePolygon(Polygon polygon) {
@@ -272,27 +274,26 @@ public class GeometrySerializer {
GeometryBuffer buffer =
createGeometryBuffer(
WKBConstants.wkbPolygon, coordType, polygon.getSRID(), bufferSize,
numCoordinates);
- GeomPartSerializer serializer = new GeomPartSerializer(buffer,
coordsOffset, numRingsOffset);
+ GeomPartSerializer serializer =
+ new GeomPartSerializer(buffer, coordsOffset, numRingsOffset,
polygon.getFactory());
serializer.write(polygon);
assert bufferSize == serializer.intsOffset;
return buffer;
}
- private static Polygon deserializePolygon(GeometryBuffer buffer, int srid) {
+ private static Polygon deserializePolygon(GeometryBuffer buffer,
GeometryFactory factory) {
CoordinateType coordType = buffer.getCoordinateType();
int numCoordinates = getBoundedInt(buffer, 4);
if (numCoordinates == 0) {
buffer.mark(8);
- Polygon polygon = FACTORY.createPolygon();
- polygon.setSRID(srid);
- return polygon;
+ return factory.createPolygon();
}
int coordsOffset = 8;
int numRingsOffset = 8 + numCoordinates * coordType.bytes;
- GeomPartSerializer serializer = new GeomPartSerializer(buffer,
coordsOffset, numRingsOffset);
+ GeomPartSerializer serializer =
+ new GeomPartSerializer(buffer, coordsOffset, numRingsOffset, factory);
Polygon polygon = serializer.readPolygon();
serializer.markEndOfBuffer();
- polygon.setSRID(srid);
return polygon;
}
@@ -319,7 +320,8 @@ public class GeometrySerializer {
multiPolygon.getSRID(),
bufferSize,
numCoordinates);
- GeomPartSerializer serializer = new GeomPartSerializer(buffer,
coordsOffset, numPolygonsOffset);
+ GeomPartSerializer serializer =
+ new GeomPartSerializer(buffer, coordsOffset, numPolygonsOffset,
multiPolygon.getFactory());
serializer.writeInt(numPolygons);
for (int k = 0; k < numPolygons; k++) {
Polygon polygon = (Polygon) multiPolygon.getGeometryN(k);
@@ -329,23 +331,22 @@ public class GeometrySerializer {
return buffer;
}
- private static MultiPolygon deserializeMultiPolygon(GeometryBuffer buffer,
int srid) {
+ private static MultiPolygon deserializeMultiPolygon(
+ GeometryBuffer buffer, GeometryFactory factory) {
CoordinateType coordType = buffer.getCoordinateType();
int numCoordinates = getBoundedInt(buffer, 4);
int coordsOffset = 8;
int numPolygonsOffset = 8 + numCoordinates * coordType.bytes;
- GeomPartSerializer serializer = new GeomPartSerializer(buffer,
coordsOffset, numPolygonsOffset);
+ GeomPartSerializer serializer =
+ new GeomPartSerializer(buffer, coordsOffset, numPolygonsOffset,
factory);
int numPolygons = serializer.checkedReadBoundedInt();
Polygon[] polygons = new Polygon[numPolygons];
for (int k = 0; k < numPolygons; k++) {
Polygon polygon = serializer.readPolygon();
- polygon.setSRID(srid);
polygons[k] = polygon;
}
serializer.markEndOfBuffer();
- MultiPolygon multiPolygon = FACTORY.createMultiPolygon(polygons);
- multiPolygon.setSRID(srid);
- return multiPolygon;
+ return factory.createMultiPolygon(polygons);
}
private static GeometryBuffer serializeGeometryCollection(GeometryCollection
geometryCollection) {
@@ -383,28 +384,24 @@ public class GeometrySerializer {
return buffer;
}
- private static GeometryCollection
deserializeGeometryCollection(GeometryBuffer buffer, int srid) {
+ private static GeometryCollection deserializeGeometryCollection(
+ GeometryBuffer buffer, GeometryFactory factory) {
int numGeometries = getBoundedInt(buffer, 4);
if (numGeometries == 0) {
buffer.mark(8);
- GeometryCollection geometryCollection =
FACTORY.createGeometryCollection();
- geometryCollection.setSRID(srid);
- return geometryCollection;
+ return factory.createGeometryCollection();
}
Geometry[] geometries = new Geometry[numGeometries];
int offset = 8;
for (int k = 0; k < numGeometries; k++) {
GeometryBuffer geomBuffer = buffer.slice(offset);
- Geometry geometry = deserialize(geomBuffer);
+ Geometry geometry = deserialize(geomBuffer, factory);
int geomLength = alignedOffset(geomBuffer.getMark());
- geometry.setSRID(srid);
geometries[k] = geometry;
offset += geomLength;
}
buffer.mark(offset);
- GeometryCollection geometryCollection =
FACTORY.createGeometryCollection(geometries);
- geometryCollection.setSRID(srid);
- return geometryCollection;
+ return factory.createGeometryCollection(geometries);
}
private static GeometryBuffer createGeometryBuffer(
@@ -475,33 +472,40 @@ public class GeometrySerializer {
return (offset + 7) & ~7;
}
+ private static GeometryFactory createGeometryFactory(int srid) {
+ return new GeometryFactory(PRECISION_MODEL, srid);
+ }
+
static class GeomPartSerializer {
final GeometryBuffer buffer;
int coordsOffset;
final int coordsEndOffset;
int intsOffset;
+ final GeometryFactory factory;
- GeomPartSerializer(GeometryBuffer buffer, int coordsOffset, int
intsOffset) {
+ GeomPartSerializer(
+ GeometryBuffer buffer, int coordsOffset, int intsOffset,
GeometryFactory factory) {
this.buffer = buffer;
this.coordsOffset = coordsOffset;
this.coordsEndOffset = intsOffset;
this.intsOffset = intsOffset;
+ this.factory = factory;
}
LineString readLineString() {
CoordinateSequence coordinates = readCoordinates();
- return FACTORY.createLineString(coordinates);
+ return factory.createLineString(coordinates);
}
LinearRing readRing() {
CoordinateSequence coordinates = readCoordinates();
- return FACTORY.createLinearRing(coordinates);
+ return factory.createLinearRing(coordinates);
}
Polygon readPolygon() {
int numRings = checkedReadBoundedInt();
if (numRings == 0) {
- return FACTORY.createPolygon();
+ return factory.createPolygon();
}
checkRemainingIntsAtLeast(numRings);
int numInteriorRings = numRings - 1;
@@ -510,7 +514,7 @@ public class GeometrySerializer {
for (int k = 0; k < numInteriorRings; k++) {
holes[k] = readRing();
}
- return FACTORY.createPolygon(shell, holes);
+ return factory.createPolygon(shell, holes);
}
CoordinateSequence readCoordinates() {
diff --git
a/common/src/main/java/org/apache/sedona/common/raster/GeometryFunctions.java
b/common/src/main/java/org/apache/sedona/common/raster/GeometryFunctions.java
index 2d32fc93d..19e0ef1fd 100644
---
a/common/src/main/java/org/apache/sedona/common/raster/GeometryFunctions.java
+++
b/common/src/main/java/org/apache/sedona/common/raster/GeometryFunctions.java
@@ -24,7 +24,6 @@ import org.geotools.coverage.grid.GridCoordinates2D;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.geometry.Envelope2D;
import org.locationtech.jts.geom.*;
-import org.locationtech.jts.geom.impl.CoordinateArraySequence;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.operation.TransformException;
@@ -110,16 +109,13 @@ public class GeometryFunctions {
raster, minX,
maxY + 1); // need bottom left coordinate, which is upper left of
y + 1 pixel
return geometryFactory.createPolygon(
- new LinearRing(
- new CoordinateArraySequence(
- new Coordinate[] {
- convertToCoordinate(worldUpperLeft),
- convertToCoordinate(worldUpperRight),
- convertToCoordinate(worldLowerRight),
- convertToCoordinate(worldLowerLeft),
- convertToCoordinate(worldUpperLeft)
- }),
- geometryFactory));
+ new Coordinate[] {
+ convertToCoordinate(worldUpperLeft),
+ convertToCoordinate(worldUpperRight),
+ convertToCoordinate(worldLowerRight),
+ convertToCoordinate(worldLowerLeft),
+ convertToCoordinate(worldUpperLeft)
+ });
}
private static Coordinate convertToCoordinate(Point2D point) {
diff --git
a/common/src/main/java/org/apache/sedona/common/simplify/BaseSimplifier.java
b/common/src/main/java/org/apache/sedona/common/simplify/BaseSimplifier.java
index e852b889e..4979efc71 100644
--- a/common/src/main/java/org/apache/sedona/common/simplify/BaseSimplifier.java
+++ b/common/src/main/java/org/apache/sedona/common/simplify/BaseSimplifier.java
@@ -18,8 +18,4 @@
*/
package org.apache.sedona.common.simplify;
-import org.locationtech.jts.geom.GeometryFactory;
-
-public abstract class BaseSimplifier {
- protected static GeometryFactory geometryFactory = new GeometryFactory();
-}
+public abstract class BaseSimplifier {}
diff --git
a/common/src/main/java/org/apache/sedona/common/simplify/GeometryCollectionSimplifier.java
b/common/src/main/java/org/apache/sedona/common/simplify/GeometryCollectionSimplifier.java
index be40d3597..914b6e5dd 100644
---
a/common/src/main/java/org/apache/sedona/common/simplify/GeometryCollectionSimplifier.java
+++
b/common/src/main/java/org/apache/sedona/common/simplify/GeometryCollectionSimplifier.java
@@ -57,6 +57,7 @@ public class GeometryCollectionSimplifier extends
BaseSimplifier {
.map(Geometry::getGeometryType)
.distinct()
.toArray(String[]::new);
+ GeometryFactory geometryFactory = geom.getFactory();
if (distinctGeometries.length == 1) {
switch (distinctGeometries[0]) {
case Geometry.TYPENAME_LINESTRING:
diff --git
a/common/src/main/java/org/apache/sedona/common/simplify/LineStringSimplifier.java
b/common/src/main/java/org/apache/sedona/common/simplify/LineStringSimplifier.java
index 4c49ed0d2..7d9cb64cb 100644
---
a/common/src/main/java/org/apache/sedona/common/simplify/LineStringSimplifier.java
+++
b/common/src/main/java/org/apache/sedona/common/simplify/LineStringSimplifier.java
@@ -21,6 +21,7 @@ package org.apache.sedona.common.simplify;
import org.apache.commons.lang3.ArrayUtils;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.geom.GeometryFactory;
public class LineStringSimplifier extends BaseSimplifier {
@@ -28,6 +29,7 @@ public class LineStringSimplifier extends BaseSimplifier {
Coordinate[] simplified =
CoordinatesSimplifier.simplifyInPlace(geom.getCoordinates(), epsilon,
2);
+ GeometryFactory geometryFactory = geom.getFactory();
if (simplified.length == 1) {
if (preserveCollapsed)
return geometryFactory.createLineString(ArrayUtils.addAll(simplified,
simplified));
diff --git
a/common/src/main/java/org/apache/sedona/common/simplify/PolygonSimplifier.java
b/common/src/main/java/org/apache/sedona/common/simplify/PolygonSimplifier.java
index ed2d560a6..593d3376b 100644
---
a/common/src/main/java/org/apache/sedona/common/simplify/PolygonSimplifier.java
+++
b/common/src/main/java/org/apache/sedona/common/simplify/PolygonSimplifier.java
@@ -18,12 +18,11 @@
*/
package org.apache.sedona.common.simplify;
-import static org.apache.sedona.common.simplify.BaseSimplifier.geometryFactory;
-
import java.util.ArrayList;
import java.util.List;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.LinearRing;
import org.locationtech.jts.geom.Polygon;
@@ -32,6 +31,7 @@ public class PolygonSimplifier {
LinearRing exteriorRing = geom.getExteriorRing();
int minPointsExternal = preserveCollapsed ? 4 : 0;
+ GeometryFactory geometryFactory = geom.getFactory();
LinearRing simplifiedExterior =
geometryFactory.createLinearRing(
CoordinatesSimplifier.simplifyInPlace(
diff --git
a/common/src/main/java/org/apache/sedona/common/subDivide/GeometrySubDivider.java
b/common/src/main/java/org/apache/sedona/common/subDivide/GeometrySubDivider.java
index ad9399ebf..c614e65f5 100644
---
a/common/src/main/java/org/apache/sedona/common/subDivide/GeometrySubDivider.java
+++
b/common/src/main/java/org/apache/sedona/common/subDivide/GeometrySubDivider.java
@@ -27,7 +27,6 @@ import org.geotools.geometry.jts.JTS;
import org.locationtech.jts.geom.*;
public class GeometrySubDivider {
- private static GeometryFactory geometryFactory = new GeometryFactory();
private static final double FP_TOLERANCE = 1e-12;
diff --git
a/common/src/main/java/org/apache/sedona/common/subDivide/PivotFinder.java
b/common/src/main/java/org/apache/sedona/common/subDivide/PivotFinder.java
index f3b71ba91..0bcf1a8c8 100644
--- a/common/src/main/java/org/apache/sedona/common/subDivide/PivotFinder.java
+++ b/common/src/main/java/org/apache/sedona/common/subDivide/PivotFinder.java
@@ -27,8 +27,6 @@ public class PivotFinder {
private static final double DBL_MAX = Double.MAX_VALUE;
- private static GeometryFactory geometryFactory = new GeometryFactory();
-
public static double findPivot(
Geometry geom, boolean splitOrdinate, double center, int
numberOfVertices) {
double pivot = DBL_MAX;
@@ -41,6 +39,7 @@ public class PivotFinder {
// if the shell is too small, use the largest hole
if (numberOfVertices >= 2 * lwPoly.getExteriorRing().getNumPoints()) {
// find the hole with the largest area and assign to ringtotrim
+ GeometryFactory geometryFactory = geom.getFactory();
double maxArea =
geometryFactory.createPolygon(lwPoly.getExteriorRing()).getArea();
for (int i = 0; i < lwPoly.getNumInteriorRing(); i++) {
LinearRing curHole = lwPoly.getInteriorRingN(i);
diff --git
a/common/src/main/java/org/apache/sedona/common/utils/FormatUtils.java
b/common/src/main/java/org/apache/sedona/common/utils/FormatUtils.java
index 2bdc4bde5..494f97bbb 100644
--- a/common/src/main/java/org/apache/sedona/common/utils/FormatUtils.java
+++ b/common/src/main/java/org/apache/sedona/common/utils/FormatUtils.java
@@ -22,6 +22,7 @@ import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.*;
+import org.apache.sedona.common.Functions;
import org.apache.sedona.common.enums.FileDataSplitter;
import org.apache.sedona.common.enums.GeometryType;
import org.locationtech.jts.geom.*;
@@ -208,7 +209,12 @@ public class FormatUtils<T extends Geometry> implements
Serializable {
// For some unknown reasons, the wkb reader cannot be used in transient
variable like the wkt
// reader.
WKBReader wkbReader = new WKBReader();
- final Geometry geometry = wkbReader.read(aux);
+ Geometry geometry = wkbReader.read(aux);
+ if (geometry.getSRID() != geometry.getFactory().getSRID()) {
+ // Make sure that the geometry factory has the correct SRID when the
parsed WKB
+ // contains a non-zero SRID (EWKB)
+ geometry = Functions.setSRID(geometry, geometry.getSRID());
+ }
handleNonSpatialDataToGeometry(geometry, Arrays.asList(columns));
return geometry;
diff --git a/common/src/main/java/org/apache/sedona/common/utils/GeomUtils.java
b/common/src/main/java/org/apache/sedona/common/utils/GeomUtils.java
index 69f2dc04a..9ec7d4739 100644
--- a/common/src/main/java/org/apache/sedona/common/utils/GeomUtils.java
+++ b/common/src/main/java/org/apache/sedona/common/utils/GeomUtils.java
@@ -22,11 +22,11 @@ import static
org.locationtech.jts.geom.Coordinate.NULL_ORDINATE;
import java.nio.ByteOrder;
import java.util.*;
+import org.apache.sedona.common.Functions;
import org.locationtech.jts.algorithm.Angle;
import org.locationtech.jts.algorithm.distance.DiscreteFrechetDistance;
import org.locationtech.jts.algorithm.distance.DiscreteHausdorffDistance;
import org.locationtech.jts.geom.*;
-import org.locationtech.jts.geom.impl.CoordinateArraySequence;
import org.locationtech.jts.io.ByteOrderValues;
import org.locationtech.jts.io.WKBWriter;
import org.locationtech.jts.io.WKTWriter;
@@ -147,13 +147,13 @@ public class GeomUtils {
return null;
}
- Coordinate[] nthCoordinate = new Coordinate[1];
+ Coordinate coordinate;
if (n > 0) {
- nthCoordinate[0] = lineString.getCoordinates()[n - 1];
+ coordinate = lineString.getCoordinates()[n - 1];
} else {
- nthCoordinate[0] = lineString.getCoordinates()[p + n];
+ coordinate = lineString.getCoordinates()[p + n];
}
- return new Point(new CoordinateArraySequence(nthCoordinate),
lineString.getFactory());
+ return lineString.getFactory().createPoint(coordinate);
}
public static Geometry getExteriorRing(Geometry geometry) {
@@ -218,7 +218,7 @@ public class GeomUtils {
public static Geometry get2dGeom(Geometry geom) {
Coordinate[] coordinates = geom.getCoordinates();
- GeometryFactory geometryFactory = new GeometryFactory();
+ GeometryFactory geometryFactory = geom.getFactory();
CoordinateSequence sequence =
geometryFactory.getCoordinateSequenceFactory().create(coordinates);
if (sequence.getDimension() > 2) {
@@ -260,7 +260,9 @@ public class GeomUtils {
Geometry outputGeom = unaryUnionOp.union();
if (outputGeom != null) {
outputGeom.normalize();
- outputGeom.setSRID(srid);
+ if (outputGeom.getSRID() != srid) {
+ outputGeom = Functions.setSRID(outputGeom, srid);
+ }
}
return outputGeom;
}
@@ -446,19 +448,6 @@ public class GeomUtils {
return geometries;
}
- public static Geometry get3DGeom(Geometry geometry, double zValue) {
- Coordinate[] coordinates = geometry.getCoordinates();
- if (coordinates.length == 0) return geometry;
- for (int i = 0; i < coordinates.length; i++) {
- boolean is3d = !Double.isNaN(coordinates[i].z);
- if (!is3d) {
- coordinates[i].setZ(zValue);
- }
- }
- geometry.geometryChanged();
- return geometry;
- }
-
public static int getPolygonNumRings(Polygon polygon) {
LinearRing shell = polygon.getExteriorRing();
if (shell == null || shell.isEmpty()) {
diff --git
a/common/src/main/java/org/apache/sedona/common/utils/GeometryForce3DTransformer.java
b/common/src/main/java/org/apache/sedona/common/utils/GeometryForce3DTransformer.java
new file mode 100644
index 000000000..ab8b47767
--- /dev/null
+++
b/common/src/main/java/org/apache/sedona/common/utils/GeometryForce3DTransformer.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sedona.common.utils;
+
+import org.locationtech.jts.geom.*;
+import org.locationtech.jts.geom.util.GeometryTransformer;
+
+public class GeometryForce3DTransformer extends GeometryTransformer {
+
+ private final double zValue;
+
+ public GeometryForce3DTransformer(double zValue) {
+ this.zValue = zValue;
+ }
+
+ @Override
+ protected CoordinateSequence transformCoordinates(CoordinateSequence coords,
Geometry parent) {
+ Coordinate[] newCoords = new Coordinate[coords.size()];
+ for (int i = 0; i < coords.size(); i++) {
+ Coordinate coordinate = coords.getCoordinate(i);
+ double z = coordinate.getZ();
+ if (Double.isNaN(z)) {
+ z = zValue;
+ }
+ newCoords[i] = new Coordinate(coordinate.getX(), coordinate.getY(), z);
+ }
+
+ return createCoordinateSequence(newCoords);
+ }
+
+ public static Geometry transform(Geometry geometry, double zValue) {
+ if (geometry.getCoordinates().length == 0) return geometry;
+ GeometryForce3DTransformer transformer = new
GeometryForce3DTransformer(zValue);
+ return transformer.transform(geometry);
+ }
+}
diff --git a/common/src/main/java/org/apache/sedona/common/utils/H3Utils.java
b/common/src/main/java/org/apache/sedona/common/utils/H3Utils.java
index d6fe9d4de..329d9a437 100644
--- a/common/src/main/java/org/apache/sedona/common/utils/H3Utils.java
+++ b/common/src/main/java/org/apache/sedona/common/utils/H3Utils.java
@@ -32,7 +32,6 @@ import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.locationtech.jts.geom.Coordinate;
-import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.geom.Polygon;
@@ -265,7 +264,7 @@ public class H3Utils {
cells.addAll(
polygonToCells(
(Polygon)
- (new GeometryFactory()
+ (line.getFactory()
.createLineString(new Coordinate[] {cs, ce})
.getEnvelope()),
level,
diff --git
a/common/src/test/java/org/apache/sedona/common/ConstructorsTest.java
b/common/src/test/java/org/apache/sedona/common/ConstructorsTest.java
index ce8bb5c43..9619b3ba7 100644
--- a/common/src/test/java/org/apache/sedona/common/ConstructorsTest.java
+++ b/common/src/test/java/org/apache/sedona/common/ConstructorsTest.java
@@ -22,9 +22,12 @@ import static org.junit.Assert.*;
import org.apache.sedona.common.utils.GeomUtils;
import org.junit.Test;
+import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;
import org.locationtech.jts.io.ParseException;
+import org.locationtech.jts.io.WKBWriter;
public class ConstructorsTest {
@@ -66,6 +69,45 @@ public class ConstructorsTest {
assertEquals("Unknown geometry type: NOT (line 1)", invalid.getMessage());
}
+ @Test
+ public void geomFromWKB() throws ParseException {
+ GeometryFactory factory = new GeometryFactory();
+ Geometry geom = factory.createPoint(new Coordinate(1, 2));
+
+ // Test WKB without SRID
+ WKBWriter wkbWriter = new WKBWriter();
+ byte[] wkb = wkbWriter.write(geom);
+ Geometry result = Constructors.geomFromWKB(wkb);
+ assertEquals(geom, result);
+ assertEquals(0, result.getSRID());
+ assertEquals(0, result.getFactory().getSRID());
+
+ // Test specifying SRID
+ result = Constructors.geomFromWKB(wkb, 1000);
+ assertEquals(geom, result);
+ assertEquals(1000, result.getSRID());
+ assertEquals(1000, result.getFactory().getSRID());
+
+ // Test EWKB with SRID
+ wkbWriter = new WKBWriter(2, true);
+ geom.setSRID(2000);
+ wkb = wkbWriter.write(geom);
+ result = Constructors.geomFromWKB(wkb);
+ assertEquals(geom, result);
+ assertEquals(2000, result.getSRID());
+ assertEquals(2000, result.getFactory().getSRID());
+
+ // Test overriding SRID
+ result = Constructors.geomFromWKB(wkb, 3000);
+ assertEquals(geom, result);
+ assertEquals(3000, result.getSRID());
+ assertEquals(3000, result.getFactory().getSRID());
+ result = Constructors.geomFromWKB(wkb, 0);
+ assertEquals(geom, result);
+ assertEquals(0, result.getSRID());
+ assertEquals(0, result.getFactory().getSRID());
+ }
+
@Test
public void mLineFromWKT() throws ParseException {
assertNull(Constructors.mLineFromText(null, 0));
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 aaa5f6e3b..3868b4539 100644
--- a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java
+++ b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java
@@ -627,13 +627,34 @@ public class FunctionsTest extends TestBase {
return !copy.isEmpty();
}
+ @Test
+ public void convexAndConcaveHullSRID() throws ParseException {
+ Geometry geom =
+ Constructors.geomFromWKT(
+ "POLYGON ((-92.8125 37.857507, -89.362793 36.013561, -92.548828
35.782171, -92.197266 34.669359, -94.614258 36.4036, -92.834473 36.580247,
-92.8125 37.857507))",
+ 4326);
+ Geometry convex = Functions.convexHull(geom);
+ Geometry concave = Functions.concaveHull(geom, 1, false);
+ assertEquals(4326, convex.getSRID());
+ assertEquals(4326, concave.getSRID());
+ }
+
+ @Test
+ public void envelopeAndCentroidSRID() throws ParseException {
+ Geometry geom = Constructors.geomFromWKT("POLYGON ((0 0, 0 1, 1 1, 1 0, 0
0))", 3857);
+ Geometry envelope = Functions.envelope(geom);
+ assertEquals(3857, envelope.getSRID());
+ Geometry centroid = Functions.getCentroid(geom);
+ assertEquals(3857, centroid.getSRID());
+ }
+
@Test
public void getGoogleS2CellIDsPoint() {
Point point = GEOMETRY_FACTORY.createPoint(new Coordinate(1, 2));
Long[] cid = Functions.s2CellIDs(point, 30);
Polygon reversedPolygon = S2Utils.toJTSPolygon(new S2CellId(cid[0]));
// cast the cell to a rectangle, it must be able to cover the points
- assert (reversedPolygon.contains(point));
+ assertTrue(reversedPolygon.contains(point));
}
@Test
@@ -1648,9 +1669,9 @@ public class FunctionsTest extends TestBase {
@Test
public void force3DObject2DDefaultValue() {
int expectedDims = 3;
- Polygon polygon = GEOMETRY_FACTORY.createPolygon(coordArray(0, 0, 0, 90,
0, 0));
+ Polygon polygon = GEOMETRY_FACTORY.createPolygon(coordArray(0, 0, 0, 90,
90, 90, 0, 0));
Polygon expectedPolygon =
- GEOMETRY_FACTORY.createPolygon(coordArray3d(0, 0, 0, 0, 90, 0, 0, 0,
0));
+ GEOMETRY_FACTORY.createPolygon(coordArray3d(0, 0, 0, 0, 90, 0, 90, 90,
0, 0, 0, 0));
Geometry forcedPolygon = Functions.force3D(polygon);
WKTWriter wktWriter = new
WKTWriter(GeomUtils.getDimension(expectedPolygon));
assertEquals(wktWriter.write(expectedPolygon),
wktWriter.write(forcedPolygon));
@@ -1846,7 +1867,8 @@ public class FunctionsTest extends TestBase {
@Test
public void force3DObject3DDefaultValue() {
int expectedDims = 3;
- Polygon polygon = GEOMETRY_FACTORY.createPolygon(coordArray3d(0, 0, 0, 90,
0, 0, 0, 0, 0));
+ Polygon polygon =
+ GEOMETRY_FACTORY.createPolygon(coordArray3d(0, 0, 0, 90, 0, 0, 90, 90,
0, 0, 0, 0));
Geometry forcedPolygon = Functions.force3D(polygon);
WKTWriter wktWriter = new WKTWriter(GeomUtils.getDimension(polygon));
assertEquals(wktWriter.write(polygon), wktWriter.write(forcedPolygon));
@@ -1876,12 +1898,11 @@ public class FunctionsTest extends TestBase {
GEOMETRY_FACTORY.createMultiPolygon(new Polygon[] {polygon3D,
polygon});
Point point3D = GEOMETRY_FACTORY.createPoint(new Coordinate(1, 1, 1));
LineString lineString = GEOMETRY_FACTORY.createLineString(coordArray(1, 0,
1, 1, 1, 2));
- LineString emptyLineString = GEOMETRY_FACTORY.createLineString();
Geometry geomCollection =
GEOMETRY_FACTORY.createGeometryCollection(
new Geometry[] {
GEOMETRY_FACTORY.createGeometryCollection(
- new Geometry[] {multiPolygon, point3D, emptyLineString,
lineString})
+ new Geometry[] {multiPolygon, point3D, lineString})
});
Polygon expectedPolygon3D =
GEOMETRY_FACTORY.createPolygon(coordArray3d(1, 0, 2, 1, 1, 2, 2, 1, 2,
2, 0, 2, 1, 0, 2));
@@ -1900,12 +1921,9 @@ public class FunctionsTest extends TestBase {
assertEquals(
wktWriter3D.write(point3D),
wktWriter3D.write(actualGeometryCollection.getGeometryN(0).getGeometryN(1)));
- assertEquals(
- emptyLineString.toText(),
- actualGeometryCollection.getGeometryN(0).getGeometryN(2).toText());
assertEquals(
wktWriter3D.write(expectedLineString3D),
-
wktWriter3D.write(actualGeometryCollection.getGeometryN(0).getGeometryN(3)));
+
wktWriter3D.write(actualGeometryCollection.getGeometryN(0).getGeometryN(2)));
}
@Test
@@ -1917,12 +1935,11 @@ public class FunctionsTest extends TestBase {
GEOMETRY_FACTORY.createMultiPolygon(new Polygon[] {polygon3D,
polygon});
Point point3D = GEOMETRY_FACTORY.createPoint(new Coordinate(1, 1, 1));
LineString lineString = GEOMETRY_FACTORY.createLineString(coordArray(1, 0,
1, 1, 1, 2));
- LineString emptyLineString = GEOMETRY_FACTORY.createLineString();
Geometry geomCollection =
GEOMETRY_FACTORY.createGeometryCollection(
new Geometry[] {
GEOMETRY_FACTORY.createGeometryCollection(
- new Geometry[] {multiPolygon, point3D, emptyLineString,
lineString})
+ new Geometry[] {multiPolygon, point3D, lineString})
});
Polygon expectedPolygon3D =
GEOMETRY_FACTORY.createPolygon(coordArray3d(1, 0, 0, 1, 1, 0, 2, 1, 0,
2, 0, 0, 1, 0, 0));
@@ -1941,12 +1958,9 @@ public class FunctionsTest extends TestBase {
assertEquals(
wktWriter3D.write(point3D),
wktWriter3D.write(actualGeometryCollection.getGeometryN(0).getGeometryN(1)));
- assertEquals(
- emptyLineString.toText(),
- actualGeometryCollection.getGeometryN(0).getGeometryN(2).toText());
assertEquals(
wktWriter3D.write(expectedLineString3D),
-
wktWriter3D.write(actualGeometryCollection.getGeometryN(0).getGeometryN(3)));
+
wktWriter3D.write(actualGeometryCollection.getGeometryN(0).getGeometryN(2)));
}
@Test
@@ -2228,6 +2242,19 @@ public class FunctionsTest extends TestBase {
assertEquals(expected, actual);
}
+ @Test
+ public void testBufferSRID() throws ParseException {
+ Geometry geom = geomFromWKT("POINT (10 20)", 3857);
+ Geometry buffered = Functions.buffer(geom, 1);
+ assertEquals(3857, buffered.getSRID());
+ assertEquals(3857, buffered.getFactory().getSRID());
+
+ geom = geomFromWKT("POINT (10 20)", 4326);
+ buffered = Functions.buffer(geom, 1, true);
+ assertEquals(4326, buffered.getSRID());
+ assertEquals(4326, buffered.getFactory().getSRID());
+ }
+
@Test
public void testSnap() throws ParseException {
Geometry poly =
@@ -3309,11 +3336,15 @@ public class FunctionsTest extends TestBase {
Geometry geomActual = FunctionsGeoTools.transform(geomExpected,
"EPSG:4326", "EPSG:4326");
assertEquals(geomExpected.getCoordinate().x, geomActual.getCoordinate().x,
FP_TOLERANCE);
assertEquals(geomExpected.getCoordinate().y, geomActual.getCoordinate().y,
FP_TOLERANCE);
+ assertEquals(4326, geomActual.getSRID());
+ assertEquals(4326, geomActual.getFactory().getSRID());
// The source and target CRS are different
geomActual = FunctionsGeoTools.transform(geomExpected, "EPSG:4326",
"EPSG:3857");
assertEquals(1.3358338895192828E7, geomActual.getCoordinate().x,
FP_TOLERANCE);
assertEquals(8399737.889818355, geomActual.getCoordinate().y,
FP_TOLERANCE);
+ assertEquals(3857, geomActual.getSRID());
+ assertEquals(3857, geomActual.getFactory().getSRID());
// The source CRS is not specified and the geometry has no SRID
Exception e =
@@ -3334,12 +3365,16 @@ public class FunctionsTest extends TestBase {
geomActual = FunctionsGeoTools.transform(geomExpected, crsWkt,
"EPSG:3857");
assertEquals(1.3358338895192828E7, geomActual.getCoordinate().x,
FP_TOLERANCE);
assertEquals(8399737.889818355, geomActual.getCoordinate().y,
FP_TOLERANCE);
+ assertEquals(3857, geomActual.getSRID());
+ assertEquals(3857, geomActual.getFactory().getSRID());
// The source CRS is not specified but the geometry has a valid SRID
geomExpected.setSRID(4326);
geomActual = FunctionsGeoTools.transform(geomExpected, "EPSG:3857");
assertEquals(1.3358338895192828E7, geomActual.getCoordinate().x,
FP_TOLERANCE);
assertEquals(8399737.889818355, geomActual.getCoordinate().y,
FP_TOLERANCE);
+ assertEquals(3857, geomActual.getSRID());
+ assertEquals(3857, geomActual.getFactory().getSRID());
// The source and target CRS are different, and latitude is out of range
Point geometryWrong = GEOMETRY_FACTORY.createPoint(new Coordinate(60,
120));
diff --git a/docs/api/sql/Constructor.md b/docs/api/sql/Constructor.md
index c1417cf27..c69f15d19 100644
--- a/docs/api/sql/Constructor.md
+++ b/docs/api/sql/Constructor.md
@@ -303,42 +303,6 @@ Output:
POINT(40.7128 -74.006)
```
-## ST_GeomFromEWKB
-
-Introduction: Construct a Geometry from EWKB string or Binary. This function
is an alias of [ST_GeomFromWKB](#st_geomfromwkb).
-
-Format:
-
-`ST_GeomFromEWKB (Wkb: String)`
-
-`ST_GeomFromEWKB (Wkb: Binary)`
-
-Since: `v1.6.1`
-
-SQL Example
-
-```sql
-SELECT ST_GeomFromEWKB([01 02 00 00 00 02 00 00 00 00 00 00 00 84 D6 00 C0 00
00 00 00 80 B5 D6 BF 00 00 00 60 E1 EF F7 BF 00 00 00 80 07 5D E5 BF])
-```
-
-Output:
-
-```
-LINESTRING (-2.1047439575195312 -0.354827880859375, -1.49606454372406
-0.6676061153411865)
-```
-
-SQL Example
-
-```sql
-SELECT
ST_asEWKT(ST_GeomFromEWKB('01010000a0e6100000000000000000f03f000000000000f03f000000000000f03f'))
-```
-
-Output:
-
-```
-SRID=4326;POINT Z(1 1 1)
-```
-
## ST_GeomFromWKB
Introduction: Construct a Geometry from WKB string or Binary. This function
also supports EWKB format.
diff --git
a/flink/src/main/java/org/apache/sedona/flink/expressions/Constructors.java
b/flink/src/main/java/org/apache/sedona/flink/expressions/Constructors.java
index 59501155e..d7732a876 100644
--- a/flink/src/main/java/org/apache/sedona/flink/expressions/Constructors.java
+++ b/flink/src/main/java/org/apache/sedona/flink/expressions/Constructors.java
@@ -20,13 +20,13 @@ package org.apache.sedona.flink.expressions;
import org.apache.flink.table.annotation.DataTypeHint;
import org.apache.flink.table.functions.ScalarFunction;
+import org.apache.sedona.common.Functions;
import org.apache.sedona.common.enums.FileDataSplitter;
import org.apache.sedona.common.enums.GeometryType;
import org.apache.sedona.common.utils.FormatUtils;
import org.apache.sedona.common.utils.GeoHashDecoder;
import org.locationtech.jts.geom.*;
import org.locationtech.jts.io.ParseException;
-import org.locationtech.jts.io.WKBReader;
import org.locationtech.jts.io.gml2.GMLReader;
import org.locationtech.jts.io.kml.KMLReader;
@@ -279,8 +279,7 @@ public class Constructors {
@DataTypeHint(value = "RAW", bridgedTo =
org.locationtech.jts.geom.Geometry.class)
public Geometry eval(@DataTypeHint("Bytes") byte[] wkb) throws
ParseException {
- WKBReader wkbReader = new WKBReader();
- return wkbReader.read(wkb);
+ return org.apache.sedona.common.Constructors.geomFromWKB(wkb);
}
}
@@ -292,8 +291,7 @@ public class Constructors {
@DataTypeHint(value = "RAW", bridgedTo =
org.locationtech.jts.geom.Geometry.class)
public Geometry eval(@DataTypeHint("Bytes") byte[] wkb) throws
ParseException {
- WKBReader wkbReader = new WKBReader();
- return wkbReader.read(wkb);
+ return org.apache.sedona.common.Constructors.geomFromWKB(wkb);
}
}
@@ -302,7 +300,6 @@ public class Constructors {
public Geometry eval(@DataTypeHint("String") String wkbString) throws
ParseException {
Geometry geometry = getGeometryByFileData(wkbString,
FileDataSplitter.WKB);
if (geometry instanceof Point) {
- geometry.setSRID(0);
return geometry;
}
return null; // Return null if geometry is not a Point
@@ -312,7 +309,7 @@ public class Constructors {
public Geometry eval(@DataTypeHint("String") String wkbString, int srid)
throws ParseException {
Geometry geometry = getGeometryByFileData(wkbString,
FileDataSplitter.WKB);
if (geometry instanceof Point) {
- geometry.setSRID(srid);
+ geometry = Functions.setSRID(geometry, srid);
return geometry;
}
return null; // Return null if geometry is not a Point
@@ -320,7 +317,7 @@ public class Constructors {
@DataTypeHint(value = "RAW", bridgedTo = Geometry.class)
public Geometry eval(@DataTypeHint("Bytes") byte[] wkb) throws
ParseException {
- return org.apache.sedona.common.Constructors.pointFromWKB(wkb, 0);
+ return org.apache.sedona.common.Constructors.pointFromWKB(wkb);
}
@DataTypeHint(value = "RAW", bridgedTo = Geometry.class)
@@ -334,7 +331,6 @@ public class Constructors {
public Geometry eval(@DataTypeHint("String") String wkbString) throws
ParseException {
Geometry geometry = getGeometryByFileData(wkbString,
FileDataSplitter.WKB);
if (geometry instanceof LineString) {
- geometry.setSRID(0);
return geometry;
}
return null; // Return null if geometry is not a Point
@@ -344,7 +340,7 @@ public class Constructors {
public Geometry eval(@DataTypeHint("String") String wkbString, int srid)
throws ParseException {
Geometry geometry = getGeometryByFileData(wkbString,
FileDataSplitter.WKB);
if (geometry instanceof LineString) {
- geometry.setSRID(srid);
+ geometry = Functions.setSRID(geometry, srid);
return geometry;
}
return null; // Return null if geometry is not a Linestring
@@ -352,7 +348,7 @@ public class Constructors {
@DataTypeHint(value = "RAW", bridgedTo = Geometry.class)
public Geometry eval(@DataTypeHint("Bytes") byte[] wkb) throws
ParseException {
- return org.apache.sedona.common.Constructors.lineFromWKB(wkb, 0);
+ return org.apache.sedona.common.Constructors.lineFromWKB(wkb);
}
@DataTypeHint(value = "RAW", bridgedTo = Geometry.class)
@@ -366,7 +362,6 @@ public class Constructors {
public Geometry eval(@DataTypeHint("String") String wkbString) throws
ParseException {
Geometry geometry = getGeometryByFileData(wkbString,
FileDataSplitter.WKB);
if (geometry instanceof LineString) {
- geometry.setSRID(0);
return geometry;
}
return null; // Return null if geometry is not a Linestring
@@ -376,7 +371,7 @@ public class Constructors {
public Geometry eval(@DataTypeHint("String") String wkbString, int srid)
throws ParseException {
Geometry geometry = getGeometryByFileData(wkbString,
FileDataSplitter.WKB);
if (geometry instanceof LineString) {
- geometry.setSRID(srid);
+ geometry = Functions.setSRID(geometry, srid);
return geometry;
}
return null; // Return null if geometry is not a Linestring
@@ -384,7 +379,7 @@ public class Constructors {
@DataTypeHint(value = "RAW", bridgedTo = Geometry.class)
public Geometry eval(@DataTypeHint("Bytes") byte[] wkb) throws
ParseException {
- return org.apache.sedona.common.Constructors.lineFromWKB(wkb, 0);
+ return org.apache.sedona.common.Constructors.lineFromWKB(wkb);
}
@DataTypeHint(value = "RAW", bridgedTo = Geometry.class)
diff --git
a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/GeometrySerde.java
b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/GeometrySerde.java
index a395d61a1..73002d5bd 100644
---
a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/GeometrySerde.java
+++
b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/GeometrySerde.java
@@ -19,6 +19,7 @@
package org.apache.sedona.snowflake.snowsql;
import java.util.Arrays;
+import org.apache.sedona.common.Constructors;
import org.apache.sedona.common.Functions;
import org.apache.sedona.common.enums.FileDataSplitter;
import org.apache.sedona.common.utils.FormatUtils;
@@ -27,7 +28,6 @@ import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryCollection;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.io.ParseException;
-import org.locationtech.jts.io.WKBReader;
public class GeometrySerde {
@@ -51,7 +51,7 @@ public class GeometrySerde {
public static Geometry deserialize(byte[] bytes) {
try {
- return new WKBReader().read(bytes);
+ return Constructors.geomFromWKB(bytes);
} catch (ParseException e) {
String msg =
String.format(
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 601c9cc06..7985017cb 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
@@ -789,7 +789,7 @@ public class UDFs {
@UDFAnnotations.ParamMeta(argNames = {"wkb"})
public static byte[] ST_PointFromWKB(byte[] wkb) throws ParseException {
- return GeometrySerde.serialize(Constructors.pointFromWKB(wkb, 0));
+ return GeometrySerde.serialize(Constructors.pointFromWKB(wkb));
}
@UDFAnnotations.ParamMeta(argNames = {"wkb", "srid"})
@@ -799,7 +799,7 @@ public class UDFs {
@UDFAnnotations.ParamMeta(argNames = {"wkb"})
public static byte[] ST_LineFromWKB(byte[] wkb) throws ParseException {
- return GeometrySerde.serialize(Constructors.lineFromWKB(wkb, 0));
+ return GeometrySerde.serialize(Constructors.lineFromWKB(wkb));
}
@UDFAnnotations.ParamMeta(argNames = {"wkb", "srid"})
@@ -809,7 +809,7 @@ public class UDFs {
@UDFAnnotations.ParamMeta(argNames = {"wkb"})
public static byte[] ST_LinestringFromWKB(byte[] wkb) throws ParseException {
- return GeometrySerde.serialize(Constructors.lineFromWKB(wkb, 0));
+ return GeometrySerde.serialize(Constructors.lineFromWKB(wkb));
}
@UDFAnnotations.ParamMeta(argNames = {"wkb", "srid"})
diff --git
a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Constructors.scala
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Constructors.scala
index c28369754..b1a82ba10 100644
---
a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Constructors.scala
+++
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Constructors.scala
@@ -18,7 +18,7 @@
*/
package org.apache.spark.sql.sedona_sql.expressions
-import org.apache.sedona.common.Constructors
+import org.apache.sedona.common.{Constructors, Functions}
import org.apache.sedona.common.enums.FileDataSplitter
import org.apache.sedona.sql.utils.GeometrySerializer
import org.apache.spark.sql.catalyst.InternalRow
@@ -234,22 +234,23 @@ case class ST_LineFromWKB(inputExpressions:
Seq[Expression])
override def eval(inputRow: InternalRow): Any = {
val wkb = inputExpressions.head.eval(inputRow)
- val srid = if (inputExpressions.length > 1)
inputExpressions(1).eval(inputRow) else 0
+ val srid =
+ if (inputExpressions.length > 1)
inputExpressions(1).eval(inputRow).asInstanceOf[Int]
+ else -1
wkb match {
case geomString: UTF8String =>
// Parse UTF-8 encoded WKB string
val geom = Constructors.lineStringFromText(geomString.toString, "wkb")
if (geom.getGeometryType == "LineString") {
- geom.setSRID(srid.asInstanceOf[Int])
- geom.toGenericArrayData
+ (if (srid != -1) Functions.setSRID(geom, srid) else
geom).toGenericArrayData
} else {
null
}
case wkbArray: Array[Byte] =>
// Convert raw WKB byte array to geometry
- Constructors.lineFromWKB(wkbArray,
srid.asInstanceOf[Int]).toGenericArrayData
+ Constructors.lineFromWKB(wkbArray, srid).toGenericArrayData
case _ => null
}
@@ -282,22 +283,23 @@ case class ST_LinestringFromWKB(inputExpressions:
Seq[Expression])
override def eval(inputRow: InternalRow): Any = {
val wkb = inputExpressions.head.eval(inputRow)
- val srid = if (inputExpressions.length > 1)
inputExpressions(1).eval(inputRow) else 0
+ val srid =
+ if (inputExpressions.length > 1)
inputExpressions(1).eval(inputRow).asInstanceOf[Int]
+ else -1
wkb match {
case geomString: UTF8String =>
// Parse UTF-8 encoded WKB string
val geom = Constructors.lineStringFromText(geomString.toString, "wkb")
if (geom.getGeometryType == "LineString") {
- geom.setSRID(srid.asInstanceOf[Int])
- geom.toGenericArrayData
+ (if (srid != -1) Functions.setSRID(geom, srid) else
geom).toGenericArrayData
} else {
null
}
case wkbArray: Array[Byte] =>
// Convert raw WKB byte array to geometry
- Constructors.lineFromWKB(wkbArray,
srid.asInstanceOf[Int]).toGenericArrayData
+ Constructors.lineFromWKB(wkbArray, srid).toGenericArrayData
case _ => null
}
@@ -330,22 +332,23 @@ case class ST_PointFromWKB(inputExpressions:
Seq[Expression])
override def eval(inputRow: InternalRow): Any = {
val wkb = inputExpressions.head.eval(inputRow)
- val srid = if (inputExpressions.length > 1)
inputExpressions(1).eval(inputRow) else 0
+ val srid =
+ if (inputExpressions.length > 1)
inputExpressions(1).eval(inputRow).asInstanceOf[Int]
+ else -1
wkb match {
case geomString: UTF8String =>
// Parse UTF-8 encoded WKB string
val geom = Constructors.pointFromText(geomString.toString, "wkb")
if (geom.getGeometryType == "Point") {
- geom.setSRID(srid.asInstanceOf[Int])
- geom.toGenericArrayData
+ (if (srid != -1) Functions.setSRID(geom, srid) else
geom).toGenericArrayData
} else {
null
}
case wkbArray: Array[Byte] =>
// Convert raw WKB byte array to geometry
- Constructors.pointFromWKB(wkbArray,
srid.asInstanceOf[Int]).toGenericArrayData
+ Constructors.pointFromWKB(wkbArray, srid).toGenericArrayData
case _ => null
}
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 16c325714..47711716a 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
@@ -606,8 +606,6 @@ case class ST_MinimumBoundingRadius(inputExpressions:
Seq[Expression])
override def nullable: Boolean = true
- private val geometryFactory = new GeometryFactory()
-
override def eval(input: InternalRow): Any = {
val expr = inputExpressions(0)
val geometry = expr match {
@@ -623,7 +621,7 @@ case class ST_MinimumBoundingRadius(inputExpressions:
Seq[Expression])
private def getMinimumBoundingRadius(geom: Geometry): InternalRow = {
val minimumBoundingCircle = new MinimumBoundingCircle(geom)
- val centerPoint =
geometryFactory.createPoint(minimumBoundingCircle.getCentre)
+ val centerPoint =
geom.getFactory.createPoint(minimumBoundingCircle.getCentre)
InternalRow(centerPoint.toGenericArrayData,
minimumBoundingCircle.getRadius)
}
diff --git
a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_constructors.scala
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_constructors.scala
index ca22543b4..890ac1039 100644
---
a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_constructors.scala
+++
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_constructors.scala
@@ -143,22 +143,22 @@ object st_constructors extends DataFrameAPI {
def ST_PointZM(x: Double, y: Double, z: Double, m: Double, srid: Int):
Column =
wrapExpression[ST_PointZM](x, y, z, m, srid)
- def ST_PointFromWKB(wkb: Column): Column =
wrapExpression[ST_PointFromWKB](wkb, 0)
- def ST_PointFromWKB(wkb: String): Column =
wrapExpression[ST_PointFromWKB](wkb, 0)
+ def ST_PointFromWKB(wkb: Column): Column =
wrapExpression[ST_PointFromWKB](wkb)
+ def ST_PointFromWKB(wkb: String): Column =
wrapExpression[ST_PointFromWKB](wkb)
def ST_PointFromWKB(wkb: Column, srid: Column): Column =
wrapExpression[ST_PointFromWKB](wkb, srid)
def ST_PointFromWKB(wkb: String, srid: Int): Column =
wrapExpression[ST_PointFromWKB](wkb, srid)
- def ST_LineFromWKB(wkb: Column): Column =
wrapExpression[ST_LineFromWKB](wkb, 0)
- def ST_LineFromWKB(wkb: String): Column =
wrapExpression[ST_LineFromWKB](wkb, 0)
+ def ST_LineFromWKB(wkb: Column): Column = wrapExpression[ST_LineFromWKB](wkb)
+ def ST_LineFromWKB(wkb: String): Column = wrapExpression[ST_LineFromWKB](wkb)
def ST_LineFromWKB(wkb: Column, srid: Column): Column =
wrapExpression[ST_LineFromWKB](wkb, srid)
def ST_LineFromWKB(wkb: String, srid: Int): Column =
wrapExpression[ST_LineFromWKB](wkb, srid)
- def ST_LinestringFromWKB(wkb: Column): Column =
wrapExpression[ST_LinestringFromWKB](wkb, 0)
- def ST_LinestringFromWKB(wkb: String): Column =
wrapExpression[ST_LinestringFromWKB](wkb, 0)
+ def ST_LinestringFromWKB(wkb: Column): Column =
wrapExpression[ST_LinestringFromWKB](wkb)
+ def ST_LinestringFromWKB(wkb: String): Column =
wrapExpression[ST_LinestringFromWKB](wkb)
def ST_LinestringFromWKB(wkb: Column, srid: Column): Column =
wrapExpression[ST_LinestringFromWKB](wkb, srid)
def ST_LinestringFromWKB(wkb: String, srid: Int): Column =
diff --git
a/spark/common/src/test/scala/org/apache/sedona/sql/PreserveSRIDSuite.scala
b/spark/common/src/test/scala/org/apache/sedona/sql/PreserveSRIDSuite.scala
new file mode 100644
index 000000000..1d4cc9e8b
--- /dev/null
+++ b/spark/common/src/test/scala/org/apache/sedona/sql/PreserveSRIDSuite.scala
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sedona.sql
+
+import org.apache.sedona.common.Constructors
+import org.apache.spark.sql.{DataFrame, Row}
+import org.apache.spark.sql.sedona_sql.UDT.GeometryUDT
+import org.apache.spark.sql.types.{StructField, StructType}
+import org.locationtech.jts.geom.Geometry
+import org.scalatest.prop.TableDrivenPropertyChecks
+
+import scala.collection.mutable
+
+class PreserveSRIDSuite extends TestBaseScala with TableDrivenPropertyChecks {
+ private var testDf: DataFrame = _
+
+ override def beforeAll(): Unit = {
+ super.beforeAll()
+ testDf = prepareTestDataFrame()
+ }
+
+ describe("Preserve SRID") {
+ val testCases = Table(
+ "test case",
+ ("ST_ConcaveHull(geom1, 1, false)", 1000),
+ ("ST_ConcaveHull(geom1, 1, true)", 1000),
+ ("ST_ConvexHull(geom1)", 1000),
+ ("ST_Buffer(geom1, 1)", 1000),
+ ("ST_ShiftLongitude(geom1)", 1000),
+ ("ST_Envelope(geom1)", 1000),
+ ("ST_Centroid(geom1)", 1000),
+ ("ST_Transform(geom1, 'EPSG:4326', 'EPSG:3857')", 3857),
+ ("ST_Intersection(geom1, ST_Point(0, 1))", 1000),
+ ("ST_MakeValid(geom1)", 1000),
+ ("ST_ReducePrecision(geom1, 6)", 1000),
+ ("ST_SimplifyVW(geom1, 0.1)", 1000),
+ ("ST_SimplifyPolygonHull(geom1, 0.5)", 1000),
+ ("ST_SetSRID(geom1, 2000)", 2000),
+ ("ST_LineMerge(geom2)", 1000),
+ ("ST_StartPoint(geom3)", 1000),
+ ("ST_Snap(geom3, geom3, 0.1)", 1000),
+ ("ST_Boundary(geom1)", 1000),
+ ("ST_LineSubstring(geom3, 0.1, 0.9)", 1000),
+ ("ST_LineInterpolatePoint(geom3, 0.1)", 1000),
+ ("ST_EndPoint(geom3)", 1000),
+ ("ST_ExteriorRing(geom1)", 1000),
+ ("ST_GeometryN(geom2, 2)", 1000),
+ ("ST_InteriorRingN(geom4, 0)", 1000),
+ ("ST_Dump(geom2)", 1000),
+ ("ST_DumpPoints(geom2)", 1000),
+ ("ST_AddMeasure(geom3, 0, 1)", 1000),
+ ("ST_AddPoint(geom3, ST_Point(0.5, 0.5), 1)", 1000),
+ ("ST_RemovePoint(geom3, 1)", 1000),
+ ("ST_SetPoint(geom3, 1, ST_Point(0.5, 0.5))", 1000),
+ ("ST_ClosestPoint(geom1, geom2)", 1000),
+ ("ST_FlipCoordinates(geom1)", 1000),
+ ("ST_SubDivide(geom4, 4)", 1000),
+ ("ST_MakeLine(geom3, geom3)", 1000),
+ ("ST_Points(geom1)", 1000),
+ ("ST_Polygon(ST_InteriorRingN(geom4, 0), 2000)", 2000),
+ ("ST_Polygonize(geom5)", 1000),
+ ("ST_MakePolygon(ST_ExteriorRing(geom4), ARRAY(ST_InteriorRingN(geom4,
0)))", 1000),
+ ("ST_Difference(geom1, geom2)", 1000),
+ ("ST_SymDifference(geom1, geom2)", 1000),
+ ("ST_UnaryUnion(geom5)", 1000),
+ ("ST_Union(geom1, geom2)", 1000),
+ ("ST_Union(ARRAY(geom1, geom2))", 1000),
+ ("ST_Multi(geom1)", 1000),
+ ("ST_PointOnSurface(geom1)", 1000),
+ ("ST_Reverse(geom1)", 1000),
+ ("ST_PointN(geom3, 1)", 1000),
+ ("ST_Force_2D(ST_AddMeasure(geom3, 0, 1))", 1000),
+ ("ST_BuildArea(geom5)", 1000),
+ ("ST_Normalize(geom5)", 1000),
+ ("ST_LineFromMultiPoint(ST_Points(geom1))", 1000),
+ ("ST_Split(geom1, ST_MakeLine(ST_Point(0.5, -10), ST_Point(0.5, 10)))",
1000),
+ ("ST_CollectionExtract(geom5, 2)", 1000),
+ ("ST_GeometricMedian(ST_Points(geom5))", 1000),
+ ("ST_LocateAlong(ST_AddMeasure(geom3, 0, 1), 0.25)", 1000),
+ ("ST_LongestLine(geom1, geom2)", 1000),
+ ("ST_Force3D(geom1, 10)", 1000),
+ ("ST_Force3DZ(geom1, 10)", 1000),
+ ("ST_Force3DM(geom1, 10)", 1000),
+ ("ST_Force4D(geom1, 5, 10)", 1000),
+ ("ST_ForceCollection(geom1)", 1000),
+ ("ST_ForcePolygonCW(ST_ForcePolygonCCW(geom1))", 1000),
+ ("ST_ForcePolygonCCW(ST_ForcePolygonCW(geom1))", 1000),
+ ("ST_Translate(geom1, 1, 2, 3)", 1000),
+ ("ST_TriangulatePolygon(geom4)", 1000),
+ ("ST_VoronoiPolygons(geom4)", 1000),
+ ("ST_Affine(geom1, 1, 2, 1, 2, 1, 2)", 1000),
+ ("ST_BoundingDiagonal(geom1)", 1000),
+ ("ST_DelaunayTriangles(geom4)", 1000),
+ ("ST_Rotate(geom1, 10)", 1000),
+ ("ST_Collect(geom1, geom2, geom3)", 1000))
+
+ forAll(testCases) { case (expression: String, srid: Int) =>
+ it(s"$expression") {
+ testDf.selectExpr(expression).collect().foreach { row =>
+ val value = row.getAs[AnyRef](0)
+ value match {
+ case geom: Geometry => assert(geom.getSRID == srid)
+ case geoms: mutable.WrappedArray[Geometry] =>
+ geoms.foreach(geom => assert(geom.getSRID == srid))
+ case _ => fail(s"Unexpected result: $value")
+ }
+ }
+ }
+ }
+ }
+
+ private def prepareTestDataFrame(): DataFrame = {
+ import scala.collection.JavaConverters._
+
+ val schema = StructType(
+ Seq(
+ StructField("geom1", GeometryUDT),
+ StructField("geom2", GeometryUDT),
+ StructField("geom3", GeometryUDT),
+ StructField("geom4", GeometryUDT),
+ StructField("geom5", GeometryUDT)))
+ val geom1 = Constructors.geomFromWKT("POLYGON ((0 0, 1 0, 0.5 0.5, 1 1, 0
1, 0 0))", 1000)
+ val geom2 =
+ Constructors.geomFromWKT("MULTILINESTRING ((0 0, 0 1), (0 1, 1 1), (1 1,
1 0))", 1000)
+ val geom3 = Constructors.geomFromWKT("LINESTRING (0 0, 0 1, 1 1, 1 0)",
1000)
+ val geom4 = Constructors.geomFromWKT(
+ "POLYGON (( 30 10, 40 40, 20 40, 10 20, 30 10 ), ( 20 30, 35 35, 30 20,
20 30 ))",
+ 1000)
+ val geom5 = Constructors.geomFromWKT(
+ """GEOMETRYCOLLECTION (LINESTRING (2 0, 2 1, 2 2),
+ |LINESTRING (2 2, 2 3, 2 4), LINESTRING (0 2, 1 2, 2 2),
+ |LINESTRING (2 2, 3 2, 4 2), LINESTRING (0 2, 1 3, 2 4),
+ |LINESTRING (2 4, 3 3, 4 2))""".stripMargin,
+ 1000)
+ val rows = Seq(Row(geom1, geom2, geom3, geom4, geom5))
+ sparkSession.createDataFrame(rows.asJava, schema)
+ }
+}
diff --git
a/spark/common/src/test/scala/org/apache/sedona/sql/constructorTestScala.scala
b/spark/common/src/test/scala/org/apache/sedona/sql/constructorTestScala.scala
index 8f57fc4bc..8bf503927 100644
---
a/spark/common/src/test/scala/org/apache/sedona/sql/constructorTestScala.scala
+++
b/spark/common/src/test/scala/org/apache/sedona/sql/constructorTestScala.scala
@@ -177,6 +177,17 @@ class constructorTestScala extends TestBaseScala {
}
}
+ val ewkb = "0020000001000007D03FF00000000000004000000000000000"
+ var geom = sparkSession.sql(s"SELECT
ST_PointFromWKB('$ewkb')").first().getAs[Geometry](0)
+ assert(geom.toString == "POINT (1 2)")
+ assert(geom.getSRID == 2000)
+ geom = sparkSession.sql(s"SELECT ST_PointFromWKB('$ewkb',
1000)").first().getAs[Geometry](0)
+ assert(geom.toString == "POINT (1 2)")
+ assert(geom.getSRID == 1000)
+ geom = sparkSession.sql(s"SELECT ST_PointFromWKB('$ewkb',
0)").first().getAs[Geometry](0)
+ assert(geom.toString == "POINT (1 2)")
+ assert(geom.getSRID == 0)
+
intercept[Exception] {
sparkSession.sql("SELECT ST_PointFromWKB('invalid')").collect()
}
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 33054a39c..ea399f4f1 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
@@ -554,7 +554,7 @@ class functionTestScala
it("Passed ST_MMax") {
var baseDf = sparkSession.sql(
"SELECT ST_GeomFromWKT('LINESTRING ZM(1 1 1 1, 2 2 2 2, 3 3 3 3, -1 -1
-1 -1)') as line")
- val actual = baseDf.selectExpr("ST_MMax(line)").first().getDouble(0)
+ var actual = baseDf.selectExpr("ST_MMax(line)").first().getDouble(0)
assert(actual == 3.0)
baseDf =
@@ -562,11 +562,11 @@ class functionTestScala
val actualNull = baseDf.selectExpr("ST_MMax(line)").first().get(0)
assert(actualNull == null)
- print(
- sparkSession
- .sql("SELECT ST_MMax(ST_GeomFromWKT('POLYGON ZM ((30 10 5 1, 40 40
10 2, 20 40 15 3, 10 20 20 4, 30 10 5 1))'))")
- .first()
- .get(0))
+ actual = sparkSession
+ .sql("SELECT ST_MMax(ST_GeomFromWKT('POLYGON ZM ((30 10 5 1, 40 40 10
2, 20 40 15 3, 10 20 20 4, 30 10 5 1))'))")
+ .first()
+ .getDouble(0)
+ assert(actual == 4.0)
}
it("Passed ST_MakeLine") {
@@ -2827,7 +2827,7 @@ class functionTestScala
}
}
- it("Should pass ST_'Force3DZ'") {
+ it("Should pass ST_Force3DZ") {
val geomTestCases = Map(
("'LINESTRING (0 1, 1 0, 2 0)'") -> ("'LINESTRING Z(0 1 1, 1 0 1, 2 0
1)'", "'LINESTRING Z(0 1 0, 1 0 0, 2 0 0)'"),
("'LINESTRING Z(0 1 3, 1 0 3, 2 0 3)'") -> ("'LINESTRING Z(0 1 3, 1 0 3,
2 0 3)'", "'LINESTRING Z(0 1 3, 1 0 3, 2 0 3)'"),