Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions common/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@
<groupId>edu.ucar</groupId>
<artifactId>cdm-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.sis.core</groupId>
<artifactId>sis-referencing</artifactId>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/java</sourceDirectory>
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ public static Geometry transformToGivenTarget(
throws FactoryException, TransformException {
// If sourceCRS is not specified, try to get it from the geometry
if (sourceCRScode == null) {

int srid = geometry.getSRID();
if (srid != 0) {
sourceCRScode = "epsg:" + srid;
Expand Down
155 changes: 111 additions & 44 deletions common/src/test/java/org/apache/sedona/common/FunctionsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,7 @@
import org.apache.sedona.common.sphere.Haversine;
import org.apache.sedona.common.sphere.Spheroid;
import org.apache.sedona.common.utils.*;
import org.geotools.api.referencing.FactoryException;
import org.geotools.api.referencing.operation.TransformException;
import org.geotools.referencing.CRS;
import org.geotools.referencing.operation.projection.ProjectionException;
import org.junit.Test;
import org.locationtech.jts.geom.*;
import org.locationtech.jts.geom.prep.PreparedGeometry;
Expand Down Expand Up @@ -749,11 +746,11 @@ public void convexAndConcaveHullSRID() throws ParseException {

@Test
public void envelopeAndCentroidSRID() throws ParseException {
Geometry geom = Constructors.geomFromWKT("POLYGON ((0 0, 0 1, 1 1, 1 0, 0 0))", 3857);
Geometry geom = Constructors.geomFromWKT("POLYGON ((0 0, 0 1, 1 1, 1 0, 0 0))", 4979);
Geometry envelope = Functions.envelope(geom);
assertEquals(3857, envelope.getSRID());
assertEquals(4979, envelope.getSRID());
Geometry centroid = Functions.getCentroid(geom);
assertEquals(3857, centroid.getSRID());
assertEquals(4979, centroid.getSRID());
}

@Test
Expand Down Expand Up @@ -2867,10 +2864,10 @@ public void testBuffer() {

@Test
public void testBufferSRID() throws ParseException {
Geometry geom = geomFromWKT("POINT (10 20)", 3857);
Geometry geom = geomFromWKT("POINT (10 20)", 4979);
Geometry buffered = Functions.buffer(geom, 1);
assertEquals(3857, buffered.getSRID());
assertEquals(3857, buffered.getFactory().getSRID());
assertEquals(4979, buffered.getSRID());
assertEquals(4979, buffered.getFactory().getSRID());

geom = geomFromWKT("POINT (10 20)", 4326);
buffered = Functions.buffer(geom, 1, true);
Expand Down Expand Up @@ -3936,57 +3933,127 @@ public void hausdorffDistanceDefaultEmptyGeom() throws Exception {
}

@Test
public void transform() throws FactoryException, TransformException {
public void transform() throws org.opengis.util.FactoryException, TransformException {
/**
* Validating without SIS EPSG dataset. The following CRS codes are currently available as
* Apache SIS Common codes: WGS84 (EPSG:4326) WGS72 (EPSG:4322) NAD83 (EPSG:4269) NAD27
* (EPSG:4267) ETRS89 (EPSG:4258) ED50 (EPSG:4230) GRS1980 (EPSG:4019) SPHERE (EPSG:4047)
*/
final String WGS84 = "EPSG:4326";
final String NAD27 = "EPSG:4267";
final String NAD83 = "EPSG:4269";
final String ED50 = "EPSG:4230";
final int nad27Srid = 4267;
final int nad83Srid = 4269;
// The source and target CRS are the same
Point geomExpected = GEOMETRY_FACTORY.createPoint(new Coordinate(120, 60));
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());
final Point geomExpected = GEOMETRY_FACTORY.createPoint(new Coordinate(120, 60));
Geometry geomActual = FunctionsApacheSIS.transform(geomExpected, NAD27, NAD27);
// Zero delta on these coordinate checks since the source and target CRS are the same
assertEquals(geomExpected.getCoordinate().x, geomActual.getCoordinate().x, 0);
assertEquals(geomExpected.getCoordinate().y, geomActual.getCoordinate().y, 0);
assertEquals(nad27Srid, geomActual.getSRID());
assertEquals(nad27Srid, geomActual.getFactory().getSRID());

// The source and target CRS are different.
// Using verification points from NOAA's EPSG transformation service but
Point geomInput = GEOMETRY_FACTORY.createPoint(new Coordinate(-120, 48));
geomActual = FunctionsApacheSIS.transform(geomInput, NAD83, NAD27);
// large delta on these coordinate checks since validating without EPSG dataset
assertEquals(-119.9988138773, geomActual.getCoordinate().x, 1e-2);
assertEquals(48.0001387082, geomActual.getCoordinate().y, 1e-2);
assertEquals(nad27Srid, geomActual.getSRID());
assertEquals(nad27Srid, geomActual.getFactory().getSRID());

// The source CRS is not specified and the geometry has no SRID
Exception e =
assertThrows(
IllegalArgumentException.class,
() -> FunctionsGeoTools.transform(geomExpected, "EPSG:3857"));
() -> FunctionsApacheSIS.transform(geomExpected, NAD27));
assertEquals("Source CRS must be specified. No SRID found on geometry.", e.getMessage());

// The source CRS is an invalid SRID
e =
assertThrows(
FactoryException.class,
() -> FunctionsGeoTools.transform(geomExpected, "abcde", "EPSG:3857"));
IllegalArgumentException.class,
() -> FunctionsApacheSIS.transform(geomExpected, "abcde", NAD83));
assertTrue(e.getMessage().contains("First failed to read as a well-known CRS code"));

// The source CRS is a WKT CRS string
String crsWkt = CRS.decode("EPSG:4326", true).toWKT();
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());
String crsWkt = FunctionsApacheSIS.parseCRSString(NAD27).toWKT();
geomInput = GEOMETRY_FACTORY.createPoint(new Coordinate(-110, 45));
geomActual = FunctionsApacheSIS.transform(geomInput, crsWkt, NAD83);
assertEquals(-110.0007450884, geomActual.getCoordinate().x, 1E-2);
assertEquals(44.9999435027, geomActual.getCoordinate().y, 1E-2);
assertEquals(nad83Srid, geomActual.getSRID());
assertEquals(nad83Srid, 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));
assertThrows(
ProjectionException.class,
() -> FunctionsGeoTools.transform(geometryWrong, "EPSG:4326", "EPSG:3857"));
geomInput.setSRID(nad27Srid);
geomActual = FunctionsApacheSIS.transform(geomInput, NAD83);
assertEquals(-110.0007450884, geomActual.getCoordinate().x, 1E-2);
assertEquals(44.99994350267, geomActual.getCoordinate().y, 1E-2);
assertEquals(nad83Srid, geomActual.getSRID());
assertEquals(nad83Srid, geomActual.getFactory().getSRID());
}

@Test
public void transformGeometries() {
// Basic validation that the transformed geometries have the same shape as the original
Point oP = GEOMETRY_FACTORY.createPoint(new Coordinate(0, 0));
Geometry tranP = FunctionsApacheSIS.transform(oP, "EPSG:4326", "EPSG:4979");
assert (tranP instanceof Point);

MultiPoint origMP = GEOMETRY_FACTORY.createMultiPointFromCoords(coordArray(0, 0, 2, 2));
Geometry tranMP = FunctionsApacheSIS.transform(origMP, "EPSG:4326", "EPSG:4979");
assert (tranMP instanceof MultiPoint);

LinearRing origLR = GEOMETRY_FACTORY.createLinearRing(coordArray(0, 0, 2, 2, 0, 0));
Geometry tranLR = FunctionsApacheSIS.transform(origLR, "EPSG:4326", "EPSG:4979");
assert (tranLR instanceof LinearRing);

LineString origLS = GEOMETRY_FACTORY.createLineString(coordArray(0, 0, 2, 2));
Geometry tranLS = FunctionsApacheSIS.transform(origLS, "EPSG:4326", "EPSG:4979");
assert (tranLS instanceof LineString);

MultiLineString origMLS =
GEOMETRY_FACTORY.createMultiLineString(
new LineString[] {origLS, GEOMETRY_FACTORY.createLineString(coordArray(3, 3, 5, 5))});
Geometry tranMLS = FunctionsApacheSIS.transform(origMLS, "EPSG:4326", "EPSG:4979");
assert (tranMLS instanceof MultiLineString);

Polygon origPoly = GEOMETRY_FACTORY.createPolygon(coordArray(0, 0, 2, 2, 2, 0, 0, 0));
Geometry tranPoly = FunctionsApacheSIS.transform(origPoly, "EPSG:4326", "EPSG:4979");
assert (tranPoly instanceof Polygon);

MultiPolygon origMPoly =
GEOMETRY_FACTORY.createMultiPolygon(
new Polygon[] {
GEOMETRY_FACTORY.createPolygon(coordArray(0, 0, 2, 2, 2, 0, 0, 0)),
GEOMETRY_FACTORY.createPolygon(coordArray(3, 3, 5, 5, 5, 3, 3, 3))
});
Geometry tranMPoly = FunctionsApacheSIS.transform(origMPoly, "EPSG:4326", "EPSG:4979");
assert (tranMPoly instanceof MultiPolygon);
assert (tranMPoly.getNumGeometries() == origMPoly.getNumGeometries());
assert (tranMPoly.getGeometryN(0) instanceof Polygon);
assert (tranMPoly.getGeometryN(1) instanceof Polygon);

GeometryCollection origGC =
GEOMETRY_FACTORY.createGeometryCollection(
new Geometry[] {origMP, origLR, origLS, origPoly, origMPoly});
Geometry tranGC = FunctionsApacheSIS.transform(origGC, "EPSG:4326", "EPSG:4979");
assert (tranGC instanceof GeometryCollection);
assert (tranGC.getNumGeometries() == origGC.getNumGeometries());
assert (tranGC.getGeometryN(0) instanceof MultiPoint);
assert (tranGC.getGeometryN(0).getNumGeometries() == origMP.getNumGeometries());
assert (tranGC.getGeometryN(1) instanceof LinearRing);
assert (tranGC.getGeometryN(2) instanceof LineString);
assert (tranGC.getGeometryN(3) instanceof Polygon);
assert (tranGC.getGeometryN(4) instanceof MultiPolygon);
assert (tranGC.getGeometryN(4).getGeometryN(0) instanceof Polygon);
assert (tranGC.getGeometryN(4).getGeometryN(1) instanceof Polygon);
for (var i = 0; i < tranGC.getNumGeometries(); i++) {
assertEquals(4979, tranGC.getGeometryN(i).getSRID());
}
}

@Test
Expand Down
32 changes: 15 additions & 17 deletions docs/setup/maven-coordinates.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,25 +33,11 @@ Please use the artifact with Spark major.minor version in the artifact name. For

If you are using the Scala 2.13 builds of Spark, please use the corresponding packages for Scala 2.13, which are suffixed by `_2.13`.

The optional GeoTools library is required if you want to use CRS transformation, ShapefileReader or GeoTiff reader. This wrapper library is a re-distribution of GeoTools official jars. The only purpose of this library is to bring GeoTools jars from OSGEO repository to Maven Central. This library is under GNU Lesser General Public License (LGPL) license so we cannot package it in Sedona official release.
The optional GeoTools library is required if you want to use ShapefileReader or GeoTiff reader. This wrapper library is a re-distribution of GeoTools official jars. The only purpose of this library is to bring GeoTools jars from OSGEO repository to Maven Central. This library is under GNU Lesser General Public License (LGPL) license so we cannot package it in Sedona official release.

!!! abstract "Sedona with Apache Spark and Scala 2.12"

=== "Spark 3.3 and Scala 2.12"
The optional EPSG database is required if you want to use CRS transformation. This library is a re-distribution of EPSG official jars. The only purpose of this library is to bring EPSG jars from OSGEO repository to Maven Central. This library is under GNU Lesser General Public License (LGPL) license so we cannot package it in Sedona official release.

```xml
<dependency>
<groupId>org.apache.sedona</groupId>
<artifactId>sedona-spark-shaded-3.3_2.12</artifactId>
<version>{{ sedona.current_version }}</version>
</dependency>
<!-- Optional: https://mvnrepository.com/artifact/org.datasyslab/geotools-wrapper -->
<dependency>
<groupId>org.datasyslab</groupId>
<artifactId>geotools-wrapper</artifactId>
<version>{{ sedona.current_geotools }}</version>
</dependency>
```
!!! abstract "Sedona with Apache Spark and Scala 2.12"

=== "Spark 3.4 and Scala 2.12"

Expand Down Expand Up @@ -169,6 +155,18 @@ The optional GeoTools library is required if you want to use CRS transformation,
</dependency>
```

!!! abstract "Sedona with EPSG database"

=== "EPSG database 9.8.1+"

```xml
<dependency>
<groupId>org.apache.sedona</groupId>
<artifactId>sis-embedded-data</artifactId>
<version>{{ sedona.apachesis_version }}</version>
</dependency>
```

## Use Sedona unshaded jars

!!!warning
Expand Down
2 changes: 1 addition & 1 deletion flink/src/main/java/org/apache/sedona/flink/Catalog.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public static UserDefinedFunction[] getFuncs() {
new Functions.ST_LineLocatePoint(),
new Functions.ST_LocateAlong(),
new Functions.ST_LongestLine(),
new FunctionsGeoTools.ST_Transform(),
new FunctionsApacheSIS.ST_Transform(),
new Functions.ST_FlipCoordinates(),
new Functions.ST_GeoHash(),
new Functions.ST_Perimeter(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@
import org.geotools.api.referencing.operation.TransformException;
import org.locationtech.jts.geom.Geometry;

public class FunctionsGeoTools {
public class FunctionsApacheSIS {
public static class ST_Transform extends ScalarFunction {
@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class)
public Geometry eval(
@DataTypeHint(value = "RAW", bridgedTo = Geometry.class) Object o,
@DataTypeHint("String") String targetCRS)
throws FactoryException, TransformException {
Geometry geom = (Geometry) o;
return org.apache.sedona.common.FunctionsGeoTools.transform(geom, targetCRS);
return org.apache.sedona.common.FunctionsApacheSIS.transform(geom, targetCRS);
}

@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class)
Expand All @@ -42,7 +42,7 @@ public Geometry eval(
@DataTypeHint("String") String targetCRS)
throws FactoryException, TransformException {
Geometry geom = (Geometry) o;
return org.apache.sedona.common.FunctionsGeoTools.transform(geom, sourceCRS, targetCRS);
return org.apache.sedona.common.FunctionsApacheSIS.transform(geom, sourceCRS, targetCRS);
}

@DataTypeHint(value = "RAW", bridgedTo = Geometry.class)
Expand All @@ -53,7 +53,7 @@ public Geometry eval(
@DataTypeHint("Boolean") Boolean lenient)
throws FactoryException, TransformException {
Geometry geom = (Geometry) o;
return org.apache.sedona.common.FunctionsGeoTools.transform(
return org.apache.sedona.common.FunctionsApacheSIS.transform(
geom, sourceCRS, targetCRS, lenient);
}
}
Expand Down
14 changes: 7 additions & 7 deletions flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
import org.apache.commons.lang3.tuple.Pair;
import org.apache.flink.table.api.Table;
import org.apache.sedona.flink.expressions.Functions;
import org.apache.sedona.flink.expressions.FunctionsGeoTools;
import org.apache.sedona.flink.expressions.FunctionsApacheSIS;
import org.geotools.api.referencing.FactoryException;
import org.geotools.api.referencing.crs.CoordinateReferenceSystem;
import org.geotools.referencing.CRS;
Expand Down Expand Up @@ -472,7 +472,7 @@ public void testTransform() {
Table transformedTable =
pointTable.select(
call(
FunctionsGeoTools.ST_Transform.class.getSimpleName(),
FunctionsApacheSIS.ST_Transform.class.getSimpleName(),
$(pointColNames[0]),
"epsg:4326",
"epsg:3857"));
Expand All @@ -487,7 +487,7 @@ public void testTransform() {
pointTable
.select(
call(
FunctionsGeoTools.ST_Transform.class.getSimpleName(),
FunctionsApacheSIS.ST_Transform.class.getSimpleName(),
$(pointColNames[0]),
"epsg:3857"))
.as(pointColNames[0])
Expand Down Expand Up @@ -549,7 +549,7 @@ public void testTransformWKT() throws FactoryException {
Table transformedTable_SRC =
pointTable.select(
call(
FunctionsGeoTools.ST_Transform.class.getSimpleName(),
FunctionsApacheSIS.ST_Transform.class.getSimpleName(),
$(pointColNames[0]),
SRC_WKT,
"epsg:3857"));
Expand All @@ -559,7 +559,7 @@ public void testTransformWKT() throws FactoryException {
Table transformedTable_TGT =
pointTable.select(
call(
FunctionsGeoTools.ST_Transform.class.getSimpleName(),
FunctionsApacheSIS.ST_Transform.class.getSimpleName(),
$(pointColNames[0]),
"epsg:4326",
TGT_WKT));
Expand All @@ -569,7 +569,7 @@ public void testTransformWKT() throws FactoryException {
Table transformedTable_SRC_TGT =
pointTable.select(
call(
FunctionsGeoTools.ST_Transform.class.getSimpleName(),
FunctionsApacheSIS.ST_Transform.class.getSimpleName(),
$(pointColNames[0]),
SRC_WKT,
TGT_WKT));
Expand All @@ -579,7 +579,7 @@ public void testTransformWKT() throws FactoryException {
Table transformedTable_SRC_TGT_lenient =
pointTable.select(
call(
FunctionsGeoTools.ST_Transform.class.getSimpleName(),
FunctionsApacheSIS.ST_Transform.class.getSimpleName(),
$(pointColNames[0]),
SRC_WKT,
TGT_WKT,
Expand Down
5 changes: 3 additions & 2 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,9 @@ extra:
- icon: fontawesome/brands/discord
link: 'https://discord.gg/9A3k5dEBsY'
sedona:
current_version: 1.7.2
current_geotools: 1.7.2-28.5
current_version: 1.7.1
current_geotools: 1.8.0-33.1-rc1
current_apachesis: 1.4
sedona_create_release:
current_version: 1.7.1
current_git_tag: sedona-1.7.1-rc1
Expand Down
Loading