You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sedona.apache.org by ji...@apache.org on 2023/08/14 08:02:45 UTC
[sedona] branch use-lon-lat created (now e44d4c339)
This is an automated email from the ASF dual-hosted git repository.
jiayu pushed a change to branch use-lon-lat
in repository https://gitbox.apache.org/repos/asf/sedona.git
at e44d4c339 Use lon lat order
This branch includes the following new commits:
new e44d4c339 Use lon lat order
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
[sedona] 01/01: Use lon lat order
Posted by ji...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
jiayu pushed a commit to branch use-lon-lat
in repository https://gitbox.apache.org/repos/asf/sedona.git
commit e44d4c33999d8e15f667f15bd5978c461c4f537d
Author: Jia Yu <ji...@apache.org>
AuthorDate: Mon Aug 14 01:02:36 2023 -0700
Use lon lat order
---
R/R/spatial_rdd.R | 2 +-
R/tests/testthat/test-crs-transform.R | 10 +-
R/vignettes/articles/apache-sedona.Rmd | 8 +-
.../java/org/apache/sedona/common/Functions.java | 78 ++++++++--
.../sedona/common/raster/RasterConstructors.java | 9 +-
.../apache/sedona/common/raster/RasterEditors.java | 4 +-
.../apache/sedona/common/utils/RasterUtils.java | 16 +-
.../org/apache/sedona/common/FunctionsTest.java | 44 ++++++
.../sedona/common/raster/RasterAccessorsTest.java | 2 +-
.../sedona/common/raster/RasterPredicatesTest.java | 6 +-
.../sedona/common/raster/RasterTestBase.java | 2 +-
.../sedona/core/spatialOperator/KNNQuery.java | 11 +-
.../sedona/core/spatialOperator/RangeQuery.java | 4 +-
.../apache/sedona/core/spatialRDD/SpatialRDD.java | 29 +---
.../sedona/core/utils/CRSTransformation.java | 165 ---------------------
.../sedona/core/utils/CRSTransformationTest.java | 3 +-
docs/api/flink/Function.md | 10 +-
docs/api/sql/Function.md | 10 +-
docs/api/sql/Raster-loader.md | 2 +
docs/tutorial/rdd.md | 2 +
docs/tutorial/sql.md | 2 +
.../java/org/apache/sedona/flink/FunctionTest.java | 4 +-
python/sedona/core/SpatialRDD/spatial_rdd.py | 3 +
python/tests/properties/crs_transform.py | 2 +-
python/tests/sql/test_dataframe_api.py | 2 +-
python/tests/sql/test_function.py | 2 +-
.../streaming/spark/test_constructor_functions.py | 4 +-
python/tests/utils/test_crs_transformation.py | 1 +
.../scala/org/apache/sedona/sql/UDF/Catalog.scala | 2 +-
.../org/apache/sedona/sql/functionTestScala.scala | 61 ++++----
.../org/apache/sedona/sql/rasteralgebraTest.scala | 4 +-
.../sedona/viz/sql/standardVizOperatorTest.scala | 4 +-
32 files changed, 222 insertions(+), 286 deletions(-)
diff --git a/R/R/spatial_rdd.R b/R/R/spatial_rdd.R
index 7c26ddb82..bd0f2eed3 100644
--- a/R/R/spatial_rdd.R
+++ b/R/R/spatial_rdd.R
@@ -135,7 +135,7 @@ approx_count <- function(x) {
#' Perform a CRS transformation.
#'
#' Transform data within a spatial RDD from one coordinate reference system to
-#' another.
+#' another. This uses the lon/lat order since v1.5.0. Before, it used lat/lon
#'
#' @param x The spatial RDD to be processed.
#' @param src_epsg_crs_code Coordinate reference system to transform from
diff --git a/R/tests/testthat/test-crs-transform.R b/R/tests/testthat/test-crs-transform.R
index 1c65e12ab..c0c03c626 100644
--- a/R/tests/testthat/test-crs-transform.R
+++ b/R/tests/testthat/test-crs-transform.R
@@ -36,11 +36,11 @@ test_that("crs_transform() works as expected", {
collect(),
tibble::tribble(
~x, ~y,
- -9833016.710450118, 3805934.914254189,
- -9815699.961781807, 3810760.096874278,
- -9839413.351030082, 3810273.8140140832,
- -9820728.151861448, 3809444.5432437807,
- -9832182.148227641, 3888758.1926142,
+ 3805934.914254189,-9833016.710450118
+ 3810760.096874278,-9815699.961781807
+ 3810273.8140140832,-9839413.351030082
+ 3809444.5432437807,-9820728.151861448
+ 3888758.1926142,-9832182.148227641
)
)
})
diff --git a/R/vignettes/articles/apache-sedona.Rmd b/R/vignettes/articles/apache-sedona.Rmd
index db03b7c09..350f153c3 100644
--- a/R/vignettes/articles/apache-sedona.Rmd
+++ b/R/vignettes/articles/apache-sedona.Rmd
@@ -190,13 +190,13 @@ The dbplyr interface transparently translates dbplyr workflows into SQL, and giv
Results are then collected back into R with `collect`.
```{r}
-## ! ST_FlipCoordinates needs to be called before st_transform as by default, this function uses lat/lon order
+## ! ST_transform uses lon/lat order since v1.5.0. Before, it used lat/lon order.
data_tbl %>%
mutate(
ALAND = ALAND %>% as.numeric(),
AWATER = AWATER %>% as.numeric(),
area = ALAND + AWATER,
- geometry_proj = st_transform(ST_FlipCoordinates(geometry), "epsg:4326", "epsg:5070", TRUE),
+ geometry_proj = st_transform(geometry, "epsg:4326", "epsg:5070", TRUE),
area_geom = st_area(geometry_proj)
) %>%
select(STATEFP, COUNTYFP, area, area_geom) %>%
@@ -209,7 +209,7 @@ Geometries need to be converted to a serializable (text or binary) format before
## Setting the CRS in R post-collect
data_tbl %>%
mutate(
- area = st_area(st_transform(ST_FlipCoordinates(geometry), "epsg:4326", "epsg:5070", TRUE)),
+ area = st_area(st_transform(geometry, "epsg:4326", "epsg:5070", TRUE)),
geometry_wkb = geometry %>% st_asBinary()
) %>%
select(COUNTYFP, geometry_wkb) %>%
@@ -223,7 +223,7 @@ data_tbl %>%
## Setting the CRS in Spark (and using EWKT to keep it)
data_tbl %>%
mutate(
- area = st_area(st_transform(ST_FlipCoordinates(geometry), "epsg:4326", "epsg:5070", TRUE)),
+ area = st_area(st_transform(geometry, "epsg:4326", "epsg:5070", TRUE)),
geometry_ewkt = geometry %>% st_setsrid(4326) %>% st_asewkt()
) %>%
select(COUNTYFP, geometry_ewkt) %>%
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 71101ffa9..287296f00 100644
--- a/common/src/main/java/org/apache/sedona/common/Functions.java
+++ b/common/src/main/java/org/apache/sedona/common/Functions.java
@@ -14,7 +14,6 @@
package org.apache.sedona.common;
import com.google.common.geometry.S2CellId;
-
import org.apache.commons.lang3.tuple.Pair;
import org.apache.sedona.common.geometryObjects.Circle;
import org.apache.sedona.common.subDivide.GeometrySubDivider;
@@ -24,9 +23,22 @@ import org.apache.sedona.common.utils.GeometrySplitter;
import org.apache.sedona.common.utils.S2Utils;
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.algorithm.MinimumBoundingCircle;
import org.locationtech.jts.algorithm.hull.ConcaveHull;
-import org.locationtech.jts.geom.*;
+import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.geom.GeometryCollection;
+import org.locationtech.jts.geom.GeometryFactory;
+import org.locationtech.jts.geom.LineString;
+import org.locationtech.jts.geom.LinearRing;
+import org.locationtech.jts.geom.MultiLineString;
+import org.locationtech.jts.geom.MultiPoint;
+import org.locationtech.jts.geom.MultiPolygon;
+import org.locationtech.jts.geom.Point;
+import org.locationtech.jts.geom.Polygon;
+import org.locationtech.jts.geom.PrecisionModel;
import org.locationtech.jts.geom.impl.CoordinateArraySequence;
import org.locationtech.jts.geom.util.GeometryFixer;
import org.locationtech.jts.io.gml2.GMLWriter;
@@ -46,7 +58,10 @@ import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
import org.wololo.jts2geojson.GeoJSONWriter;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
import java.util.stream.Collectors;
import static com.google.common.geometry.S2.DBL_EPSILON;
@@ -184,29 +199,70 @@ public class Functions {
return min == Double.MAX_VALUE ? null : min;
}
+ public static Geometry transform(Geometry geometry, String targetCRS)
+ throws FactoryException, TransformException {
+ return transform(geometry, null, targetCRS, true);
+ }
+
public static Geometry transform(Geometry geometry, String sourceCRS, String targetCRS)
throws FactoryException, TransformException {
- return transform(geometry, sourceCRS, targetCRS, false);
+ return transform(geometry, sourceCRS, targetCRS, true);
}
-
- public static Geometry transform(Geometry geometry, String sourceCRS, String targetCRS, boolean lenient)
+ public static Geometry transform(Geometry geometry, String sourceCRScode, String targetCRScode, boolean lenient)
throws FactoryException, TransformException {
- CoordinateReferenceSystem sourceCRSCode = parseCRSString(sourceCRS);
- CoordinateReferenceSystem targetCRScode = parseCRSString(targetCRS);
- return GeomUtils.transform(geometry, sourceCRSCode, targetCRScode, lenient);
+ CoordinateReferenceSystem targetCRS = parseCRSString(targetCRScode);
+ return transformToGivenTarget(geometry, sourceCRScode, targetCRS, lenient);
}
+ /**
+ * Transform a geometry from one CRS to another. If sourceCRS is not specified, it will be
+ * extracted from the geometry. If lenient is true, the transformation will be lenient.
+ * This function is used by the implicit CRS transformation in Sedona rasters.
+ * @param geometry
+ * @param sourceCRScode
+ * @param targetCRS
+ * @param lenient
+ * @return
+ * @throws FactoryException
+ * @throws TransformException
+ */
+ public static Geometry transformToGivenTarget(Geometry geometry, String sourceCRScode, CoordinateReferenceSystem targetCRS, boolean lenient)
+ 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;
+ }
+ else {
+ // If SRID is not set, throw an exception
+ throw new IllegalArgumentException("Source CRS must be specified. No SRID found on geometry.");
+ }
+ }
+ CoordinateReferenceSystem sourceCRS = parseCRSString(sourceCRScode);
+ // 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;
+ }
private static CoordinateReferenceSystem parseCRSString(String CRSString)
throws FactoryException
{
try {
- return CRS.decode(CRSString);
+ // Try to parse as a well-known CRS code
+ // Longitude first, then latitude
+ return CRS.decode(CRSString, true);
}
catch (NoSuchAuthorityCodeException e) {
try {
- return CRS.parseWKT(CRSString);
+ // Try to parse as a WKT CRS string, longitude first
+ Hints hints = new Hints(Hints.FORCE_LONGITUDE_FIRST_AXIS_ORDER, Boolean.TRUE);
+ return ReferencingFactoryFinder.getCRSFactory(hints).createFromWKT(CRSString);
}
catch (FactoryException ex) {
throw new FactoryException("First failed to read as a well-known CRS code: \n" + e.getMessage() + "\nThen failed to read as a WKT CRS string: \n" + ex.getMessage());
diff --git a/common/src/main/java/org/apache/sedona/common/raster/RasterConstructors.java b/common/src/main/java/org/apache/sedona/common/raster/RasterConstructors.java
index 0d6f5405f..1bfb48db7 100644
--- a/common/src/main/java/org/apache/sedona/common/raster/RasterConstructors.java
+++ b/common/src/main/java/org/apache/sedona/common/raster/RasterConstructors.java
@@ -23,6 +23,7 @@ import org.geotools.gce.geotiff.GeoTiffReader;
import org.geotools.referencing.CRS;
import org.geotools.referencing.crs.DefaultEngineeringCRS;
import org.geotools.referencing.operation.transform.AffineTransform2D;
+import org.geotools.util.factory.Hints;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.datum.PixelInCell;
@@ -36,12 +37,12 @@ import java.io.IOException;
public class RasterConstructors
{
public static GridCoverage2D fromArcInfoAsciiGrid(byte[] bytes) throws IOException {
- ArcGridReader reader = new ArcGridReader(new ByteArrayImageInputStream(bytes));
+ ArcGridReader reader = new ArcGridReader(new ByteArrayImageInputStream(bytes), new Hints(Hints.FORCE_LONGITUDE_FIRST_AXIS_ORDER, Boolean.TRUE));
return reader.read(null);
}
public static GridCoverage2D fromGeoTiff(byte[] bytes) throws IOException {
- GeoTiffReader geoTiffReader = new GeoTiffReader(new ByteArrayImageInputStream(bytes));
+ GeoTiffReader geoTiffReader = new GeoTiffReader(new ByteArrayImageInputStream(bytes), new Hints(Hints.FORCE_LONGITUDE_FIRST_AXIS_ORDER, Boolean.TRUE));
return geoTiffReader.read(null);
}
@@ -134,7 +135,9 @@ public class RasterConstructors
if (srid == 0) {
crs = DefaultEngineeringCRS.GENERIC_2D;
} else {
- crs = CRS.decode("EPSG:" + srid);
+ // Create the CRS from the srid
+ // Longitude first, Latitude second
+ crs = CRS.decode("EPSG:" + srid, true);
}
// Create a new empty raster
diff --git a/common/src/main/java/org/apache/sedona/common/raster/RasterEditors.java b/common/src/main/java/org/apache/sedona/common/raster/RasterEditors.java
index 4093973b9..bdbc20d85 100644
--- a/common/src/main/java/org/apache/sedona/common/raster/RasterEditors.java
+++ b/common/src/main/java/org/apache/sedona/common/raster/RasterEditors.java
@@ -36,9 +36,9 @@ public class RasterEditors
{
CoordinateReferenceSystem crs;
if (srid == 0) {
- crs = DefaultEngineeringCRS.CARTESIAN_2D;
+ crs = DefaultEngineeringCRS.GENERIC_2D;
} else {
- crs = CRS.decode("EPSG:" + srid);
+ crs = CRS.decode("EPSG:" + srid, true);
}
GridCoverageFactory gridCoverageFactory = CoverageFactoryFinder.getGridCoverageFactory(null);
diff --git a/common/src/main/java/org/apache/sedona/common/utils/RasterUtils.java b/common/src/main/java/org/apache/sedona/common/utils/RasterUtils.java
index cc24f1e79..e829b62a4 100644
--- a/common/src/main/java/org/apache/sedona/common/utils/RasterUtils.java
+++ b/common/src/main/java/org/apache/sedona/common/utils/RasterUtils.java
@@ -27,8 +27,6 @@ import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridCoverageFactory;
import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.geometry.DirectPosition2D;
-import org.geotools.geometry.jts.JTS;
-import org.geotools.referencing.CRS;
import org.geotools.referencing.crs.DefaultEngineeringCRS;
import org.geotools.referencing.operation.transform.AffineTransform2D;
import org.locationtech.jts.geom.Geometry;
@@ -127,14 +125,16 @@ public class RasterUtils {
return gridCoords;
}
- public static Geometry convertCRSIfNeeded(Geometry geometry, CoordinateReferenceSystem rasterCRS) {
+ public static Geometry convertCRSIfNeeded(Geometry geometry, CoordinateReferenceSystem targetCRS) {
int geomSRID = geometry.getSRID();
- if (rasterCRS != null && !(rasterCRS instanceof DefaultEngineeringCRS) && geomSRID > 0) {
+ // If the geometry has a SRID and it is not the same as the raster CRS, we need to transform the geometry
+ // to the raster CRS.
+ // Note that:
+ // In Sedona vector, we do not perform implicit CRS transform. Everything must be done explicitly via ST_Transform
+ // In Sedona raster, we do implicit CRS transform if the geometry has a SRID and the raster has a CRS
+ if (targetCRS != null && !(targetCRS instanceof DefaultEngineeringCRS) && geomSRID > 0) {
try {
- CoordinateReferenceSystem queryWindowCRS = CRS.decode("EPSG:" + geomSRID);
- if (!CRS.equalsIgnoreMetadata(rasterCRS, queryWindowCRS)) {
- geometry = GeomUtils.transform(geometry, queryWindowCRS, rasterCRS, true);
- }
+ geometry = Functions.transformToGivenTarget(geometry, null, targetCRS, true);
} catch (FactoryException | TransformException e) {
throw new RuntimeException("Cannot transform CRS of query window", e);
}
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 dad8f9771..ce7667462 100644
--- a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java
+++ b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java
@@ -19,10 +19,14 @@ import org.apache.sedona.common.sphere.Haversine;
import org.apache.sedona.common.sphere.Spheroid;
import org.apache.sedona.common.utils.GeomUtils;
import org.apache.sedona.common.utils.S2Utils;
+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.io.WKTReader;
import org.locationtech.jts.io.WKTWriter;
+import org.opengis.referencing.FactoryException;
+import org.opengis.referencing.operation.TransformException;
import java.util.Arrays;
import java.util.HashSet;
@@ -1622,4 +1626,44 @@ public class FunctionsTest {
Double actual = Functions.hausdorffDistance(polygon, emptyLineString);
assertEquals(expected, actual);
}
+
+ @Test
+ public void transform()
+ throws FactoryException, TransformException
+ {
+ // The source and target CRS are the same
+ Point geomExpected = GEOMETRY_FACTORY.createPoint(new Coordinate(120, 60));
+ Geometry geomActual = Functions.transform(geomExpected, "EPSG:4326", "EPSG:4326");
+ assertEquals(geomExpected.getCoordinate().x, geomActual.getCoordinate().x, FP_TOLERANCE);
+ assertEquals(geomExpected.getCoordinate().y, geomActual.getCoordinate().y, FP_TOLERANCE);
+
+ // The source and target CRS are different
+ geomActual = Functions.transform(geomExpected, "EPSG:4326", "EPSG:3857");
+ assertEquals(1.3358338895192828E7, geomActual.getCoordinate().x, FP_TOLERANCE);
+ assertEquals(8399737.889818355, geomActual.getCoordinate().y, FP_TOLERANCE);
+
+ // The source CRS is not specified and the geometry has no SRID
+ Exception e = assertThrows(IllegalArgumentException.class, () -> Functions.transform(geomExpected,"EPSG:3857"));
+ assertEquals("Source CRS must be specified. No SRID found on geometry.", e.getMessage());
+
+ // The source CRS is an invalid SRID
+ e = assertThrows(FactoryException.class, () -> Functions.transform(geomExpected, "abcde", "EPSG:3857"));
+ 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 = Functions.transform(geomExpected, crsWkt, "EPSG:3857");
+ assertEquals(1.3358338895192828E7, geomActual.getCoordinate().x, FP_TOLERANCE);
+ assertEquals(8399737.889818355, geomActual.getCoordinate().y, FP_TOLERANCE);
+
+ // The source CRS is not specified but the geometry has a valid SRID
+ geomExpected.setSRID(4326);
+ geomActual = Functions.transform(geomExpected, "EPSG:3857");
+ assertEquals(1.3358338895192828E7, geomActual.getCoordinate().x, FP_TOLERANCE);
+ assertEquals(8399737.889818355, geomActual.getCoordinate().y, FP_TOLERANCE);
+
+ // 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, () -> Functions.transform(geometryWrong, "EPSG:4326", "EPSG:3857"));
+ }
}
diff --git a/common/src/test/java/org/apache/sedona/common/raster/RasterAccessorsTest.java b/common/src/test/java/org/apache/sedona/common/raster/RasterAccessorsTest.java
index 3a2a677b5..f07115e9b 100644
--- a/common/src/test/java/org/apache/sedona/common/raster/RasterAccessorsTest.java
+++ b/common/src/test/java/org/apache/sedona/common/raster/RasterAccessorsTest.java
@@ -174,7 +174,7 @@ public class RasterAccessorsTest extends RasterTestBase
GridCoverage2D emptyRaster = RasterConstructors.makeEmptyRaster(1, 5, 5, -53, 51, 1, -1, 0, 0, srid);
GeometryFactory geometryFactory = new GeometryFactory(new PrecisionModel(), srid);
Geometry point = geometryFactory.createPoint(new Coordinate(longitude, latitude));
- point = JTS.transform(point, CRS.findMathTransform(emptyRaster.getCoordinateReferenceSystem(), CRS.decode("EPSG:3857")));
+ point = JTS.transform(point, CRS.findMathTransform(emptyRaster.getCoordinateReferenceSystem(), CRS.decode("EPSG:3857", true)));
point.setSRID(3857);
Coordinate coords = RasterAccessors.getGridCoord(emptyRaster, point).getCoordinate();
assertEquals(7, coords.getX(), 1e-9);
diff --git a/common/src/test/java/org/apache/sedona/common/raster/RasterPredicatesTest.java b/common/src/test/java/org/apache/sedona/common/raster/RasterPredicatesTest.java
index 22ecde400..d77e3c423 100644
--- a/common/src/test/java/org/apache/sedona/common/raster/RasterPredicatesTest.java
+++ b/common/src/test/java/org/apache/sedona/common/raster/RasterPredicatesTest.java
@@ -181,14 +181,12 @@ public class RasterPredicatesTest extends RasterTestBase {
GeometryFactory geometryFactory = new GeometryFactory(new PrecisionModel(), 3857);
Geometry geometry = geometryFactory.toGeometry(new Envelope(5, 10, 5, 10));
GridCoverage2D raster = RasterConstructors.makeEmptyRaster(1, 20, 20, 2, 22, 1, -1, 0, 0, 3857);
- geometry = JTS.transform(geometry, CRS.findMathTransform(raster.getCoordinateReferenceSystem(), CRS.decode("EPSG:4326")));
- geometry.setSRID(4326);
boolean result = RasterPredicates.rsContains(raster, geometry);
Assert.assertTrue(result);
//geometry protruding out of the raster envelope
geometry = geometryFactory.toGeometry(new Envelope(2, 20, 2, 25));
- geometry = JTS.transform(geometry, CRS.findMathTransform(raster.getCoordinateReferenceSystem(), CRS.decode("EPSG:4326")));
+ geometry = JTS.transform(geometry, CRS.findMathTransform(raster.getCoordinateReferenceSystem(), CRS.decode("EPSG:4326", true)));
geometry.setSRID(4326);
result = RasterPredicates.rsContains(raster, geometry);
Assert.assertFalse(result);
@@ -274,7 +272,7 @@ public class RasterPredicatesTest extends RasterTestBase {
GeometryFactory geometryFactory = new GeometryFactory(new PrecisionModel(), 4326);
Geometry geometry = geometryFactory.toGeometry(new Envelope(30, 60, 10, 50));
GridCoverage2D raster = RasterConstructors.makeEmptyRaster(1, 20, 20, 32, 35, 1, -1, 0, 0, 4326);
- geometry = JTS.transform(geometry, CRS.findMathTransform(raster.getCoordinateReferenceSystem(), CRS.decode("EPSG:3857")));
+ geometry = JTS.transform(geometry, CRS.findMathTransform(raster.getCoordinateReferenceSystem(), CRS.decode("EPSG:3857", true)));
geometry.setSRID(3857);
boolean result = RasterPredicates.rsWithin(raster, geometry);
Assert.assertTrue(result);
diff --git a/common/src/test/java/org/apache/sedona/common/raster/RasterTestBase.java b/common/src/test/java/org/apache/sedona/common/raster/RasterTestBase.java
index a8a49dbde..fed723bca 100644
--- a/common/src/test/java/org/apache/sedona/common/raster/RasterTestBase.java
+++ b/common/src/test/java/org/apache/sedona/common/raster/RasterTestBase.java
@@ -81,7 +81,7 @@ public class RasterTestBase {
CoordinateReferenceSystem crs = null;
if (crsCode != null && !crsCode.isEmpty()) {
try {
- crs = CRS.decode(crsCode);
+ crs = CRS.decode(crsCode, true);
} catch (FactoryException e) {
throw new RuntimeException(e);
}
diff --git a/core/src/main/java/org/apache/sedona/core/spatialOperator/KNNQuery.java b/core/src/main/java/org/apache/sedona/core/spatialOperator/KNNQuery.java
index 68948ab09..8e986f4ce 100644
--- a/core/src/main/java/org/apache/sedona/core/spatialOperator/KNNQuery.java
+++ b/core/src/main/java/org/apache/sedona/core/spatialOperator/KNNQuery.java
@@ -19,13 +19,15 @@
package org.apache.sedona.core.spatialOperator;
+import org.apache.sedona.common.Functions;
import org.apache.sedona.core.knnJudgement.GeometryDistanceComparator;
import org.apache.sedona.core.knnJudgement.KnnJudgement;
import org.apache.sedona.core.knnJudgement.KnnJudgementUsingIndex;
import org.apache.sedona.core.spatialRDD.SpatialRDD;
-import org.apache.sedona.core.utils.CRSTransformation;
import org.apache.spark.api.java.JavaRDD;
import org.locationtech.jts.geom.Geometry;
+import org.opengis.referencing.FactoryException;
+import org.opengis.referencing.operation.TransformException;
import java.io.Serializable;
import java.util.List;
@@ -52,7 +54,12 @@ public class KNNQuery
{
U queryCenter = originalQueryPoint;
if (spatialRDD.getCRStransformation()) {
- queryCenter = CRSTransformation.Transform(spatialRDD.getSourceEpsgCode(), spatialRDD.getTargetEpgsgCode(), originalQueryPoint);
+ try {
+ queryCenter = (U) Functions.transform(originalQueryPoint, spatialRDD.getSourceEpsgCode(), spatialRDD.getTargetEpgsgCode());
+ }
+ catch (FactoryException | TransformException e) {
+ throw new RuntimeException(e);
+ }
}
if (useIndex) {
diff --git a/core/src/main/java/org/apache/sedona/core/spatialOperator/RangeQuery.java b/core/src/main/java/org/apache/sedona/core/spatialOperator/RangeQuery.java
index 746d24099..4e8d17e9d 100644
--- a/core/src/main/java/org/apache/sedona/core/spatialOperator/RangeQuery.java
+++ b/core/src/main/java/org/apache/sedona/core/spatialOperator/RangeQuery.java
@@ -19,10 +19,10 @@
package org.apache.sedona.core.spatialOperator;
+import org.apache.sedona.common.Functions;
import org.apache.sedona.core.rangeJudgement.RangeFilter;
import org.apache.sedona.core.rangeJudgement.RangeFilterUsingIndex;
import org.apache.sedona.core.spatialRDD.SpatialRDD;
-import org.apache.sedona.core.utils.CRSTransformation;
import org.apache.spark.api.java.JavaRDD;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Envelope;
@@ -55,7 +55,7 @@ public class RangeQuery
{
U queryGeometry = originalQueryGeometry;
if (spatialRDD.getCRStransformation()) {
- queryGeometry = CRSTransformation.Transform(spatialRDD.getSourceEpsgCode(), spatialRDD.getTargetEpgsgCode(), originalQueryGeometry);
+ queryGeometry = (U) Functions.transform(originalQueryGeometry, spatialRDD.getSourceEpsgCode(), spatialRDD.getTargetEpgsgCode());
}
if (useIndex == true) {
diff --git a/core/src/main/java/org/apache/sedona/core/spatialRDD/SpatialRDD.java b/core/src/main/java/org/apache/sedona/core/spatialRDD/SpatialRDD.java
index 06e82d565..c3b4bfa25 100644
--- a/core/src/main/java/org/apache/sedona/core/spatialRDD/SpatialRDD.java
+++ b/core/src/main/java/org/apache/sedona/core/spatialRDD/SpatialRDD.java
@@ -21,6 +21,7 @@ package org.apache.sedona.core.spatialRDD;
import org.apache.commons.lang.NullArgumentException;
import org.apache.log4j.Logger;
+import org.apache.sedona.common.Functions;
import org.apache.sedona.common.utils.GeomUtils;
import org.apache.sedona.core.enums.GridType;
import org.apache.sedona.core.enums.IndexType;
@@ -153,29 +154,11 @@ public class SpatialRDD<T extends Geometry>
*/
public boolean CRSTransform(String sourceEpsgCRSCode, String targetEpsgCRSCode, boolean lenient)
{
- try {
- CoordinateReferenceSystem sourceCRS = CRS.decode(sourceEpsgCRSCode);
- CoordinateReferenceSystem targetCRS = CRS.decode(targetEpsgCRSCode);
- final MathTransform transform = CRS.findMathTransform(sourceCRS, targetCRS, lenient);
- this.CRStransformation = true;
- this.sourceEpsgCode = sourceEpsgCRSCode;
- this.targetEpgsgCode = targetEpsgCRSCode;
- this.rawSpatialRDD = this.rawSpatialRDD.map(new Function<T, T>()
- {
- @Override
- public T call(T originalObject)
- throws Exception
- {
- return (T) JTS.transform(originalObject, transform);
- }
- });
- return true;
- }
- catch (FactoryException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- return false;
- }
+ this.CRStransformation = true;
+ this.sourceEpsgCode = sourceEpsgCRSCode;
+ this.targetEpgsgCode = targetEpsgCRSCode;
+ this.rawSpatialRDD = this.rawSpatialRDD.map((geom) -> (T) Functions.transform(geom, sourceEpsgCRSCode, targetEpgsgCode, lenient));
+ return true;
}
/**
diff --git a/core/src/main/java/org/apache/sedona/core/utils/CRSTransformation.java b/core/src/main/java/org/apache/sedona/core/utils/CRSTransformation.java
deleted file mode 100644
index c24417d13..000000000
--- a/core/src/main/java/org/apache/sedona/core/utils/CRSTransformation.java
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * 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.core.utils;
-
-import org.geotools.geometry.jts.JTS;
-import org.geotools.referencing.CRS;
-import org.locationtech.jts.geom.Envelope;
-import org.locationtech.jts.geom.Geometry;
-import org.locationtech.jts.geom.Point;
-import org.locationtech.jts.geom.Polygon;
-import org.opengis.geometry.MismatchedDimensionException;
-import org.opengis.referencing.FactoryException;
-import org.opengis.referencing.crs.CoordinateReferenceSystem;
-import org.opengis.referencing.operation.MathTransform;
-import org.opengis.referencing.operation.TransformException;
-
-// TODO: Auto-generated Javadoc
-
-/**
- * The Class CRSTransformation.
- */
-public class CRSTransformation
-{
-
- /**
- * Transform.
- *
- * @param sourceEpsgCRSCode the source epsg CRS code
- * @param targetEpsgCRSCode the target epsg CRS code
- * @param sourceObject the source object
- * @return the point
- */
- public static Point Transform(String sourceEpsgCRSCode, String targetEpsgCRSCode, Point sourceObject)
- {
- try {
- CoordinateReferenceSystem sourceCRS = CRS.decode(sourceEpsgCRSCode);
- CoordinateReferenceSystem targetCRS = CRS.decode(targetEpsgCRSCode);
- final MathTransform transform = CRS.findMathTransform(sourceCRS, targetCRS, false);
- return (Point) JTS.transform(sourceObject, transform);
- }
- catch (FactoryException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- return null;
- }
- catch (MismatchedDimensionException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- return null;
- }
- catch (TransformException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- return null;
- }
- }
-
- /**
- * Transform.
- *
- * @param sourceEpsgCRSCode the source epsg CRS code
- * @param targetEpsgCRSCode the target epsg CRS code
- * @param sourceObject the source object
- * @return the polygon
- */
- public static Polygon Transform(String sourceEpsgCRSCode, String targetEpsgCRSCode, Polygon sourceObject)
- {
- try {
- CoordinateReferenceSystem sourceCRS = CRS.decode(sourceEpsgCRSCode);
- CoordinateReferenceSystem targetCRS = CRS.decode(targetEpsgCRSCode);
- final MathTransform transform = CRS.findMathTransform(sourceCRS, targetCRS, false);
- return (Polygon) JTS.transform(sourceObject, transform);
- }
- catch (FactoryException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- return null;
- }
- catch (MismatchedDimensionException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- return null;
- }
- catch (TransformException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- return null;
- }
- }
-
- /**
- * Transform.
- *
- * @param sourceEpsgCRSCode the source epsg CRS code
- * @param targetEpsgCRSCode the target epsg CRS code
- * @param sourceObject the source object
- * @return the envelope
- */
- public static Envelope Transform(String sourceEpsgCRSCode, String targetEpsgCRSCode, Envelope sourceObject)
- {
- try {
- CoordinateReferenceSystem sourceCRS = CRS.decode(sourceEpsgCRSCode);
- CoordinateReferenceSystem targetCRS = CRS.decode(targetEpsgCRSCode);
- final MathTransform transform = CRS.findMathTransform(sourceCRS, targetCRS, false);
- return JTS.transform(sourceObject, transform);
- }
- catch (FactoryException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- return null;
- }
- catch (MismatchedDimensionException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- return null;
- }
- catch (TransformException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- return null;
- }
- }
-
- public static <T extends Geometry> T Transform(String sourceEpsgCRSCode, String targetEpsgCRSCode, T sourceObject)
- {
- try {
- CoordinateReferenceSystem sourceCRS = CRS.decode(sourceEpsgCRSCode);
- CoordinateReferenceSystem targetCRS = CRS.decode(targetEpsgCRSCode);
- final MathTransform transform = CRS.findMathTransform(sourceCRS, targetCRS, false);
- return (T) JTS.transform(sourceObject, transform);
- }
- catch (FactoryException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- return null;
- }
- catch (MismatchedDimensionException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- return null;
- }
- catch (TransformException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- return null;
- }
- }
-}
diff --git a/core/src/test/java/org/apache/sedona/core/utils/CRSTransformationTest.java b/core/src/test/java/org/apache/sedona/core/utils/CRSTransformationTest.java
index e051d8337..c501ebade 100644
--- a/core/src/test/java/org/apache/sedona/core/utils/CRSTransformationTest.java
+++ b/core/src/test/java/org/apache/sedona/core/utils/CRSTransformationTest.java
@@ -160,7 +160,7 @@ public class CRSTransformationTest
gridType = GridType.getGridType(prop.getProperty("gridType"));
indexType = IndexType.getIndexType(prop.getProperty("indexType"));
numPartitions = Integer.parseInt(prop.getProperty("numPartitions"));
- queryEnvelope = new Envelope(30.01, 40.01, -90.01, -80.01);
+ queryEnvelope = new Envelope(-90.01, -80.01, 30.01, 40.01);
loopTimes = 1;
}
catch (IOException ex) {
@@ -203,6 +203,7 @@ public class CRSTransformationTest
throws Exception
{
PointRDD spatialRDD = new PointRDD(sc, InputLocation, offset, splitter, true);
+ spatialRDD.flipCoordinates();
spatialRDD.CRSTransform( "epsg:4326", "epsg:3005");
long resultSize = RangeQuery.SpatialRangeQuery(spatialRDD, queryEnvelope, false, false).count();
assert resultSize == 3127;
diff --git a/docs/api/flink/Function.md b/docs/api/flink/Function.md
index 63c895061..a491e221d 100644
--- a/docs/api/flink/Function.md
+++ b/docs/api/flink/Function.md
@@ -2223,17 +2223,21 @@ Transform the Spatial Reference System / Coordinate Reference System of A, from
For SourceCRS and TargetCRS, WKT format is also available since v1.3.1.
!!!note
- By default, this function uses lat/lon order. You can use ==ST_FlipCoordinates== to swap X and Y.
+ By default, this function uses lon/lat order since `v1.5.0`. Before, it used lat/lon order. You can use ==ST_FlipCoordinates== to swap X and Y.
!!!note
- If ==ST_Transform== throws an Exception called "Bursa wolf parameters required", you need to disable the error notification in ST_Transform. You can append a boolean value at the end.
+ By default, ==ST_Transform== follows the `lenient` mode which tries to fix issues by itself. You can append a boolean value at the end to enable the `strict` mode. In `strict` mode, ==ST_Transform== will throw an error if it finds any issue.
-Format: `ST_Transform (A:geometry, SourceCRS:string, TargetCRS:string ,[Optional] DisableError)`
+Format: `ST_Transform (A:geometry, SourceCRS:string, TargetCRS:string ,[Optional] lenientMode:bool)`
Since: `v1.2.0`
Example:
+```sql
+SELECT ST_AsText(ST_Transform(ST_GeomFromText('POLYGON((170 50,170 72,-130 72,-130 50,170 50))'),'EPSG:4326', 'EPSG:32649'))
+```
+
```sql
SELECT ST_AsText(ST_Transform(ST_GeomFromText('POLYGON((170 50,170 72,-130 72,-130 50,170 50))'),'EPSG:4326', 'EPSG:32649', false))
```
diff --git a/docs/api/sql/Function.md b/docs/api/sql/Function.md
index 267145326..3f8fcda0c 100644
--- a/docs/api/sql/Function.md
+++ b/docs/api/sql/Function.md
@@ -2315,17 +2315,21 @@ Transform the Spatial Reference System / Coordinate Reference System of A, from
For SourceCRS and TargetCRS, WKT format is also available since v1.3.1.
!!!note
- By default, this function uses lat/lon order. You can use ==ST_FlipCoordinates== to swap X and Y.
+ By default, this function uses lon/lat order since `v1.5.0`. Before, it used lat/lon order. You can use ==ST_FlipCoordinates== to swap X and Y.
!!!note
- If ==ST_Transform== throws an Exception called "Bursa wolf parameters required", you need to disable the error notification in ST_Transform. You can append a boolean value at the end.
+ By default, ==ST_Transform== follows the `lenient` mode which tries to fix issues by itself. You can append a boolean value at the end to enable the `strict` mode. In `strict` mode, ==ST_Transform== will throw an error if it finds any issue.
-Format: `ST_Transform (A:geometry, SourceCRS:string, TargetCRS:string ,[Optional] DisableError)`
+Format: `ST_Transform (A:geometry, SourceCRS:string, TargetCRS:string ,[Optional] lenientMode:bool)`
Since: `v1.0.0`
Spark SQL example:
+```sql
+SELECT ST_AsText(ST_Transform(ST_GeomFromText('POLYGON((170 50,170 72,-130 72,-130 50,170 50))'),'EPSG:4326', 'EPSG:32649'))
+```
+
```sql
SELECT ST_AsText(ST_Transform(ST_GeomFromText('POLYGON((170 50,170 72,-130 72,-130 50,170 50))'),'EPSG:4326', 'EPSG:32649', false))
```
diff --git a/docs/api/sql/Raster-loader.md b/docs/api/sql/Raster-loader.md
index fe00d7fc6..129b432ff 100644
--- a/docs/api/sql/Raster-loader.md
+++ b/docs/api/sql/Raster-loader.md
@@ -7,6 +7,8 @@ Sedona provides two types of raster DataFrame loaders. They both use Sedona buil
The raster loader of Sedona leverages Spark built-in binary data source and works with several RS RasterUDT constructors to produce RasterUDT type. Each raster is a row in the resulting DataFrame and stored in a `RasterUDT` format.
+By default, these functions uses lon/lat order since `v1.5.0`. Before, it used lat/lon order.
+
### Load raster to a binary DataFrame
You can load any type of raster data using the code below. Then use the RS constructors below to create RasterUDT.
diff --git a/docs/tutorial/rdd.md b/docs/tutorial/rdd.md
index 584d8053d..f2fdecdec 100644
--- a/docs/tutorial/rdd.md
+++ b/docs/tutorial/rdd.md
@@ -203,6 +203,8 @@ For WKT/WKB/GeoJSON data, please use ==ST_GeomFromWKT / ST_GeomFromWKB / ST_Geom
Sedona doesn't control the coordinate unit (degree-based or meter-based) of all geometries in an SpatialRDD. The unit of all related distances in Sedona is same as the unit of all geometries in an SpatialRDD.
+By default, this function uses lon/lat order since `v1.5.0`. Before, it used lat/lon order. You can use ==spatialRDD.flipCoordinates== to swap X and Y.
+
To convert Coordinate Reference System of an SpatialRDD, use the following code:
=== "Scala"
diff --git a/docs/tutorial/sql.md b/docs/tutorial/sql.md
index 1f87c7283..7524c2fa8 100644
--- a/docs/tutorial/sql.md
+++ b/docs/tutorial/sql.md
@@ -437,6 +437,8 @@ For Postgis there is no need to add a query to convert geometry types since it's
Sedona doesn't control the coordinate unit (degree-based or meter-based) of all geometries in a Geometry column. The unit of all related distances in SedonaSQL is same as the unit of all geometries in a Geometry column.
+By default, this function uses lon/lat order since `v1.5.0`. Before, it used lat/lon order. You can use ==ST_FlipCoordinates== to swap X and Y.
+
To convert Coordinate Reference System of the Geometry column created before, use the following code:
```sql
diff --git a/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java b/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
index 8e3caf329..d49449484 100644
--- a/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
+++ b/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
@@ -225,8 +225,8 @@ public class FunctionTest extends TestBase{
public void testTransformWKT() throws FactoryException {
Table pointTable = createPointTable_real(testDataSize);
- CoordinateReferenceSystem CRS_SRC = CRS.decode("epsg:4326");
- CoordinateReferenceSystem CRS_TGT = CRS.decode("epsg:3857");
+ CoordinateReferenceSystem CRS_SRC = CRS.decode("epsg:4326", true);
+ CoordinateReferenceSystem CRS_TGT = CRS.decode("epsg:3857", true);
String SRC_WKT = CRS_SRC.toWKT();
String TGT_WKT = CRS_TGT.toWKT();
diff --git a/python/sedona/core/SpatialRDD/spatial_rdd.py b/python/sedona/core/SpatialRDD/spatial_rdd.py
index 429d1000d..1522c0ee4 100644
--- a/python/sedona/core/SpatialRDD/spatial_rdd.py
+++ b/python/sedona/core/SpatialRDD/spatial_rdd.py
@@ -120,6 +120,9 @@ class SpatialRDD:
"""
return self._srdd.CRSTransform(sourceEpsgCRSCode, targetEpsgCRSCode)
+ def flipCoordinates(self):
+ return self._srdd.flipCoordinates()
+
def MinimumBoundingRectangle(self):
raise NotImplementedError()
diff --git a/python/tests/properties/crs_transform.py b/python/tests/properties/crs_transform.py
index 548ceed58..f04c93970 100644
--- a/python/tests/properties/crs_transform.py
+++ b/python/tests/properties/crs_transform.py
@@ -31,6 +31,6 @@ num_partitions = 11
distance = 0.01
input_location_query_polygon = os.path.join(tests_resource, "crs-test-polygon.csv")
loop_times = 1
-query_envelope = Envelope(30.01, 40.01, -90.01, -80.01)
+query_envelope = Envelope(-90.01, -80.01, 30.01, 40.01)
query_point = Point(34.01, -84.01)
top_k = 100
\ No newline at end of file
diff --git a/python/tests/sql/test_dataframe_api.py b/python/tests/sql/test_dataframe_api.py
index 103334406..ce51f7631 100644
--- a/python/tests/sql/test_dataframe_api.py
+++ b/python/tests/sql/test_dataframe_api.py
@@ -147,7 +147,7 @@ test_configurations = [
(stf.ST_SubDivide, ("line", 5), "linestring_geom", "", ["LINESTRING (0 0, 2.5 0)", "LINESTRING (2.5 0, 5 0)"]),
(stf.ST_SubDivideExplode, ("line", 5), "linestring_geom", "collect_list(geom)", ["LINESTRING (0 0, 2.5 0)", "LINESTRING (2.5 0, 5 0)"]),
(stf.ST_SymDifference, ("a", "b"), "overlapping_polys", "", "MULTIPOLYGON (((1 0, 0 0, 0 1, 1 1, 1 0)), ((2 0, 2 1, 3 1, 3 0, 2 0)))"),
- (stf.ST_Transform, ("point", lambda: f.lit("EPSG:4326"), lambda: f.lit("EPSG:32649")), "point_geom", "ST_ReducePrecision(geom, 2)", "POINT (-33788209.77 0)"),
+ (stf.ST_Transform, ("point", lambda: f.lit("EPSG:4326"), lambda: f.lit("EPSG:32649")), "point_geom", "ST_ReducePrecision(geom, 2)", "POINT (-34870890.91 1919456.06)"),
(stf.ST_Translate, ("geom", 1.0, 1.0,), "square_geom", "", "POLYGON ((2 1, 2 2, 3 2, 3 1, 2 1))"),
(stf.ST_Union, ("a", "b"), "overlapping_polys", "", "POLYGON ((1 0, 0 0, 0 1, 1 1, 2 1, 3 1, 3 0, 2 0, 1 0))"),
(stf.ST_X, ("b",), "two_points", "", 3.0),
diff --git a/python/tests/sql/test_function.py b/python/tests/sql/test_function.py
index 659541333..17d7fd338 100644
--- a/python/tests/sql/test_function.py
+++ b/python/tests/sql/test_function.py
@@ -184,7 +184,7 @@ class TestPredicateJoin(TestBase):
polygon_df.createOrReplaceTempView("polygondf")
polygon_df.show()
function_df = self.spark.sql(
- "select ST_Transform(ST_FlipCoordinates(polygondf.countyshape), 'epsg:4326','epsg:3857', false) from polygondf")
+ "select ST_Transform(polygondf.countyshape, 'epsg:4326','epsg:3857', false) from polygondf")
function_df.show()
def test_st_intersection_intersects_but_not_contains(self):
diff --git a/python/tests/streaming/spark/test_constructor_functions.py b/python/tests/streaming/spark/test_constructor_functions.py
index 26668ba8e..e197cd2cd 100644
--- a/python/tests/streaming/spark/test_constructor_functions.py
+++ b/python/tests/streaming/spark/test_constructor_functions.py
@@ -80,10 +80,10 @@ SEDONA_LISTED_SQL_FUNCTIONS = [
.with_transform("ST_ASText")),
(SuiteContainer.empty()
.with_function_name("ST_Transform")
- .with_arguments(["ST_GeomFromText('POINT(21.5 52.5)')", "'epsg:4326'", "'epsg:2180'"])
+ .with_arguments(["ST_GeomFromText('POINT(52.5 21.5)')", "'epsg:4326'", "'epsg:2180'"])
.with_expected_result(-2501415.806893427)
#.with_expected_result("POINT (-2501415.806893427 4119952.52325666)")
- .with_transform("ST_X")),
+ .with_transform("ST_Y")),
#.with_transform("ST_ASText")),
(SuiteContainer.empty()
.with_function_name("ST_Intersection")
diff --git a/python/tests/utils/test_crs_transformation.py b/python/tests/utils/test_crs_transformation.py
index d43cc0efe..46d63463b 100644
--- a/python/tests/utils/test_crs_transformation.py
+++ b/python/tests/utils/test_crs_transformation.py
@@ -35,6 +35,7 @@ class TestCrsTransformation(TestBase):
splitter,
True
)
+ spatial_rdd.flipCoordinates()
spatial_rdd.CRSTransform("epsg:4326", "epsg:3005")
for i in range(loop_times):
diff --git a/sql/common/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala b/sql/common/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala
index d6df59a88..d662f9508 100644
--- a/sql/common/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala
+++ b/sql/common/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala
@@ -71,7 +71,7 @@ object Catalog {
function[ST_Length](),
function[ST_Area](),
function[ST_Centroid](),
- function[ST_Transform](false),
+ function[ST_Transform](true),
function[ST_Intersection](),
function[ST_Difference](),
function[ST_SymDifference](),
diff --git a/sql/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala b/sql/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala
index 932164817..95bc493eb 100644
--- a/sql/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala
+++ b/sql/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala
@@ -21,14 +21,13 @@ package org.apache.sedona.sql
import org.apache.commons.codec.binary.Hex
import org.apache.sedona.sql.implicits._
-import org.apache.spark.sql.catalyst.expressions.{GenericRow, GenericRowWithSchema}
+import org.apache.spark.sql.catalyst.expressions.GenericRowWithSchema
import org.apache.spark.sql.functions._
-import org.apache.spark.sql.sedona_sql.expressions.ST_Degrees
import org.apache.spark.sql.{DataFrame, Row}
import org.geotools.referencing.CRS
import org.junit.Assert.assertEquals
import org.locationtech.jts.algorithm.MinimumBoundingCircle
-import org.locationtech.jts.geom.{Geometry, Polygon}
+import org.locationtech.jts.geom.{Coordinate, Geometry, GeometryFactory, Polygon}
import org.locationtech.jts.io.WKTWriter
import org.locationtech.jts.linearref.LengthIndexedLine
import org.locationtech.jts.operation.distance3d.Distance3DOp
@@ -189,52 +188,44 @@ class functionTestScala extends TestBaseScala with Matchers with GeometrySample
}
it("Passed ST_Transform") {
- var polygonWktDf = sparkSession.read.format("csv").option("delimiter", "\t").option("header", "false").load(mixedWktGeometryInputLocation)
- polygonWktDf.createOrReplaceTempView("polygontable")
- var polygonDf = sparkSession.sql("select ST_GeomFromWKT(polygontable._c0) as countyshape from polygontable")
- polygonDf.createOrReplaceTempView("polygondf")
- val polygon = "POLYGON ((110.54671 55.818002, 110.54671 55.143743, 110.940494 55.143743, 110.940494 55.818002, 110.54671 55.818002))"
- // Floats don't have an exact decimal representation. String format varies across jvm:s. Do an approximate match.
- val forceXYExpect = "POLYGON \\(\\(471596.69167460\\d* 6185916.95119\\d*, 471107.562364\\d* 6110880.97422\\d*, 496207.10915\\d* 6110788.80471\\d*, 496271.3193704\\d* 6185825.6056\\d*, 471596.6916746\\d* 6185916.95119\\d*\\)\\)"
-
- sparkSession.createDataset(Seq(polygon))
- .withColumn("geom", expr("ST_GeomFromWKT(value)"))
- .createOrReplaceTempView("df")
-
- val forceXYResult = sparkSession.sql(s"""select ST_Transform(ST_FlipCoordinates(ST_geomFromWKT('$polygon')),'EPSG:4326', 'EPSG:32649', false)""").rdd.map(row => row.getAs[Geometry](0).toString).collect()(0)
- forceXYResult should fullyMatch regex(forceXYExpect)
+ val point = "POINT (120 60)"
+ val transformedResult = sparkSession.sql(s"""select ST_Transform(ST_geomFromWKT('$point'),'EPSG:4326', 'EPSG:3857', false)""").rdd.map(row => row.getAs[Geometry](0)).collect()(0)
+ println(transformedResult)
+ assertEquals(1.3358338895192828E7, transformedResult.getCoordinate.x, FP_TOLERANCE)
+ assertEquals(8399737.889818355, transformedResult.getCoordinate.y, FP_TOLERANCE)
}
it("Passed ST_transform WKT version"){
- var polygonWktDf = sparkSession.read.format("csv").option("delimiter", "\t").option("header", "false").load(mixedWktGeometryInputLocation)
- polygonWktDf.createOrReplaceTempView("polygontable")
- var polygonDf = sparkSession.sql("select ST_GeomFromWKT(polygontable._c0) as countyshape from polygontable")
- polygonDf.createOrReplaceTempView("polygondf")
- val polygon = "POLYGON ((110.54671 55.818002, 110.54671 55.143743, 110.940494 55.143743, 110.940494 55.818002, 110.54671 55.818002))"
- val forceXYExpect = "POLYGON \\(\\(471596.6916746\\d* 6185916.95119\\d*, 471107.562364\\d* 6110880.97422\\d*, 496207.10915\\d* 6110788.80471\\d*, 496271.319370\\d* 6185825.6056\\d*, 471596.6916746\\d* 6185916.95119\\d*\\)\\)"
-
- val EPSG_TGT_CRS = CRS.decode("EPSG:32649")
+ val polygon = "POLYGON ((120 60, 121 61, 122 62, 123 63, 124 64, 120 60))"
+ val geometryFactory = new GeometryFactory
+ val coords = new Array[Coordinate](6)
+ coords(0) = new Coordinate(1000961.4042164611, 6685590.893548286)
+ coords(1) = new Coordinate(1039394.2790044537, 6804110.988854166)
+ coords(2) = new Coordinate(1074157.4382441062, 6923060.447921266)
+ coords(3) = new Coordinate(1105199.4259604653, 7042351.1239674715)
+ coords(4) = new Coordinate(1132473.1932022288, 7161889.652860963)
+ coords(5) = coords(0)
+ val polygonExpected = geometryFactory.createPolygon(coords)
+ val EPSG_TGT_CRS = CRS.decode("EPSG:32649", true)
val EPSG_TGT_WKT = EPSG_TGT_CRS.toWKT()
- val EPSG_SRC_CRS = CRS.decode("EPSG:4326")
+ val EPSG_SRC_CRS = CRS.decode("EPSG:4326", true)
val EPSG_SRC_WKT = EPSG_SRC_CRS.toWKT()
sparkSession.createDataset(Seq(polygon))
.withColumn("geom", expr("ST_GeomFromWKT(value)"))
.createOrReplaceTempView("df")
+ val result_TGT_WKT = sparkSession.sql(s"""select ST_Transform(ST_geomFromWKT('$polygon'),'EPSG:4326', '$EPSG_TGT_WKT', false)""").rdd.map(row => row.getAs[Geometry](0)).collect()(0)
+ assertEquals(0, result_TGT_WKT.compareTo(polygonExpected, COORDINATE_SEQUENCE_COMPARATOR))
- val forceXYResult_TGT_WKT = sparkSession.sql(s"""select ST_Transform(ST_FlipCoordinates(ST_geomFromWKT('$polygon')),'EPSG:4326', '$EPSG_TGT_WKT', false)""").rdd.map(row => row.getAs[Geometry](0).toString).collect()(0)
- forceXYResult_TGT_WKT should fullyMatch regex(forceXYExpect)
-
- val forceXYResult_SRC_WKT = sparkSession.sql(s"""select ST_Transform(ST_FlipCoordinates(ST_geomFromWKT('$polygon')),'$EPSG_SRC_WKT', 'EPSG:32649', false)""").rdd.map(row => row.getAs[Geometry](0).toString).collect()(0)
- forceXYResult_SRC_WKT should fullyMatch regex(forceXYExpect)
-
- val forceXYResult_SRC_TGT_WKT = sparkSession.sql(s"""select ST_Transform(ST_FlipCoordinates(ST_geomFromWKT('$polygon')),'$EPSG_SRC_WKT', '$EPSG_TGT_WKT', false)""").rdd.map(row => row.getAs[Geometry](0).toString).collect()(0)
- forceXYResult_SRC_TGT_WKT should fullyMatch regex(forceXYExpect)
+ val result_SRC_WKT = sparkSession.sql(s"""select ST_Transform(ST_geomFromWKT('$polygon'),'$EPSG_SRC_WKT', 'EPSG:32649', false)""").rdd.map(row => row.getAs[Geometry](0)).collect()(0)
+ assertEquals(0, result_SRC_WKT.compareTo(polygonExpected, COORDINATE_SEQUENCE_COMPARATOR))
+ val result_SRC_TGT_WKT = sparkSession.sql(s"""select ST_Transform(ST_geomFromWKT('$polygon'),'$EPSG_SRC_WKT', '$EPSG_TGT_WKT', false)""").rdd.map(row => row.getAs[Geometry](0)).collect()(0)
+ assertEquals(0, result_SRC_TGT_WKT.compareTo(polygonExpected, COORDINATE_SEQUENCE_COMPARATOR))
}
it("Passed Function exception check"){
- val EPSG_TGT_CRS = CRS.decode("EPSG:32649")
+ val EPSG_TGT_CRS = CRS.decode("EPSG:32649", true)
val EPSG_TGT_WKT = EPSG_TGT_CRS.toWKT()
val epsgFactoryErrorString = EPSG_TGT_WKT.substring(0,EPSG_TGT_WKT.length() - 1)
val epsgString = "EPSG:4326"
diff --git a/sql/common/src/test/scala/org/apache/sedona/sql/rasteralgebraTest.scala b/sql/common/src/test/scala/org/apache/sedona/sql/rasteralgebraTest.scala
index 976cce6cc..60d7d0c71 100644
--- a/sql/common/src/test/scala/org/apache/sedona/sql/rasteralgebraTest.scala
+++ b/sql/common/src/test/scala/org/apache/sedona/sql/rasteralgebraTest.scala
@@ -542,8 +542,8 @@ class rasteralgebraTest extends TestBaseScala with BeforeAndAfter with GivenWhen
assert(!df.selectExpr("RS_Intersects(raster, ST_SetSRID(ST_Point(-13057457,4027023), 3857))").first().getBoolean(0))
// query window and raster not in the same CRS
- assert(df.selectExpr("RS_Intersects(raster, ST_SetSRID(ST_Point(33.81798,-117.47993), 4326))").first().getBoolean(0))
- assert(!df.selectExpr("RS_Intersects(raster, ST_SetSRID(ST_Point(33.97896,-117.27868), 4326))").first().getBoolean(0))
+ assert(df.selectExpr("RS_Intersects(raster, ST_SetSRID(ST_Point(-117.47993, 33.81798), 4326))").first().getBoolean(0))
+ assert(!df.selectExpr("RS_Intersects(raster, ST_SetSRID(ST_Point(-117.27868, 33.97896), 4326))").first().getBoolean(0))
}
it("Passed RS_AddBandFromArray collect generated raster") {
diff --git a/viz/src/test/scala/org/apache/sedona/viz/sql/standardVizOperatorTest.scala b/viz/src/test/scala/org/apache/sedona/viz/sql/standardVizOperatorTest.scala
index a5346739e..9d020c89b 100644
--- a/viz/src/test/scala/org/apache/sedona/viz/sql/standardVizOperatorTest.scala
+++ b/viz/src/test/scala/org/apache/sedona/viz/sql/standardVizOperatorTest.scala
@@ -65,10 +65,10 @@ class standardVizOperatorTest extends TestBaseScala {
|LATERAL VIEW
| EXPLODE(
| ST_Pixelize(
- | ST_Transform(ST_FlipCoordinates(shape), 'epsg:4326','epsg:3857'),
+ | ST_Transform(shape, 'epsg:4326','epsg:3857'),
| 256,
| 256,
- | (SELECT ST_Transform(ST_FlipCoordinates(bound), 'epsg:4326','epsg:3857') FROM boundtable)
+ | (SELECT ST_Transform(bound, 'epsg:4326','epsg:3857') FROM boundtable)
| )) AS pixel
""".stripMargin).createOrReplaceTempView("pixels")
spark.sql(