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 2022/08/16 07:59:09 UTC
[incubator-sedona] branch master updated: [SEDONA-132] Move some functions to a common module (#647)
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/incubator-sedona.git
The following commit(s) were added to refs/heads/master by this push:
new f2e61b85 [SEDONA-132] Move some functions to a common module (#647)
f2e61b85 is described below
commit f2e61b85dc95235bffd5aa49a8db35ad0735f1a7
Author: Adam Binford <ad...@gmail.com>
AuthorDate: Tue Aug 16 01:59:04 2022 -0600
[SEDONA-132] Move some functions to a common module (#647)
---
.../scripts/prepare_sparklyr_sedona_test_env.sh | 2 +-
R/tests/testthat/helper-initialize.R | 2 +-
common/.gitignore | 9 +
{core => common}/pom.xml | 35 +--
common/src/.gitignore | 1 +
common/src/main/.gitignore | 1 +
.../java/org/apache/sedona/common/Functions.java | 132 ++++++++++
.../java/org/apache/sedona/common/utils/BBox.java | 60 +++++
.../org/apache/sedona/common}/utils/GeomUtils.java | 5 +-
.../common/utils/GeometryGeoHashEncoder.java | 30 ++-
.../sedona/common/utils/PointGeoHashEncoder.java | 94 +++++++
core/pom.xml | 6 +
.../apache/sedona/core/geometryObjects/Circle.java | 2 +-
.../sedona/core/spatialOperator/JoinQuery.java | 2 +-
.../apache/sedona/core/spatialRDD/SpatialRDD.java | 2 +-
.../shapefileParser/shapes/GeometrySerdeTest.java | 2 +-
.../shapefileParser/shapes/ShapefileRDDTest.java | 2 +-
.../shapes/ShapefileReaderTest.java | 2 +-
.../sedona/core/geometryObjects/CircleTest.java | 2 +-
.../core/spatialRDD/SpatialRDDWriterTest.java | 2 +-
flink/pom.xml | 5 +
.../main/java/org/apache/sedona/flink/Catalog.java | 1 -
.../apache/sedona/flink/expressions/Functions.java | 90 ++-----
pom.xml | 3 +
sql/pom.xml | 5 +
.../sql/sedona_sql/expressions/Functions.scala | 284 +++------------------
.../expressions/NullSafeExpressions.scala | 148 ++++++++++-
.../expressions/geohash/GeoHashDecoder.scala | 11 +-
.../expressions/geohash/PointGeoHashEncoder.scala | 101 --------
.../sedona/sql/functions/geohash/Fixtures.scala | 5 +-
viz/pom.xml | 5 +
31 files changed, 561 insertions(+), 490 deletions(-)
diff --git a/.github/workflows/scripts/prepare_sparklyr_sedona_test_env.sh b/.github/workflows/scripts/prepare_sparklyr_sedona_test_env.sh
index 7a25f0e5..dc72029c 100644
--- a/.github/workflows/scripts/prepare_sparklyr_sedona_test_env.sh
+++ b/.github/workflows/scripts/prepare_sparklyr_sedona_test_env.sh
@@ -19,7 +19,7 @@
sedona_jar_files () {
local subdir
- for subdir in 'core' 'sql' 'viz'; do
+ for subdir in 'common' 'core' 'sql' 'viz'; do
local artifact_id="$(
mvn \
org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate \
diff --git a/R/tests/testthat/helper-initialize.R b/R/tests/testthat/helper-initialize.R
index 4ca1eefe..521d800a 100644
--- a/R/tests/testthat/helper-initialize.R
+++ b/R/tests/testthat/helper-initialize.R
@@ -114,7 +114,7 @@ expect_geom_equal <- function(sc, lhs, rhs) {
testthat::expect_true(
invoke_static(
sc,
- "org.apache.sedona.core.utils.GeomUtils",
+ "org.apache.sedona.common.utils.GeomUtils",
"equalsExactGeom",
lhs[[i]],
rhs[[i]]
diff --git a/common/.gitignore b/common/.gitignore
new file mode 100644
index 00000000..79869dd0
--- /dev/null
+++ b/common/.gitignore
@@ -0,0 +1,9 @@
+/target/
+/.settings/
+/.classpath
+/.project
+/dependency-reduced-pom.xml
+/doc/
+/.idea/
+*.iml
+/latest/
diff --git a/core/pom.xml b/common/pom.xml
similarity index 52%
copy from core/pom.xml
copy to common/pom.xml
index 336eb293..ffd5de3e 100644
--- a/core/pom.xml
+++ b/common/pom.xml
@@ -22,13 +22,13 @@
<parent>
<groupId>org.apache.sedona</groupId>
<artifactId>sedona-parent</artifactId>
- <version>1.3.0-incubating-SNAPSHOT</version>
+ <version>1.2.1-incubating-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
- <artifactId>sedona-core-${spark.compat.version}_${scala.compat.version}</artifactId>
+ <artifactId>sedona-common</artifactId>
<name>${project.groupId}:${project.artifactId}</name>
- <description>A cluster computing system for processing large-scale spatial data: RDD API. Apache Sedona is an effort undergoing incubation at The Apache Software Foundation (ASF), sponsored by the Apache Incubator. Incubation is required of all newly accepted projects until a further review indicates that the infrastructure, communications, and decision making process have stabilized in a manner consistent with other successful ASF projects. While incubation status is not necessarily [...]
+ <description>A cluster computing system for processing large-scale spatial data: Common API. Apache Sedona is an effort undergoing incubation at The Apache Software Foundation (ASF), sponsored by the Apache Incubator. Incubation is required of all newly accepted projects until a further review indicates that the infrastructure, communications, and decision making process have stabilized in a manner consistent with other successful ASF projects. While incubation status is not necessar [...]
<url>http://sedona.apache.org/</url>
<packaging>jar</packaging>
@@ -37,35 +37,6 @@
</properties>
<dependencies>
- <!-- Test -->
- <dependency>
- <groupId>org.apache.hadoop</groupId>
- <artifactId>hadoop-minicluster</artifactId>
- <version>${hadoop.version}</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-all</artifactId>
- <version>1.8.5</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.geotools</groupId>
- <artifactId>gt-shapefile</artifactId>
- <version>${geotools.version}</version>
- <scope>test</scope>
- <exclusions>
- <exclusion>
- <groupId>org.locationtech.jts</groupId>
- <artifactId>jts-core</artifactId>
- </exclusion>
- <exclusion>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>*</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
</dependencies>
<build>
<sourceDirectory>src/main/java</sourceDirectory>
diff --git a/common/src/.gitignore b/common/src/.gitignore
new file mode 100644
index 00000000..5509140f
--- /dev/null
+++ b/common/src/.gitignore
@@ -0,0 +1 @@
+*.DS_Store
diff --git a/common/src/main/.gitignore b/common/src/main/.gitignore
new file mode 100644
index 00000000..5509140f
--- /dev/null
+++ b/common/src/main/.gitignore
@@ -0,0 +1 @@
+*.DS_Store
diff --git a/common/src/main/java/org/apache/sedona/common/Functions.java b/common/src/main/java/org/apache/sedona/common/Functions.java
new file mode 100644
index 00000000..7ce2dc30
--- /dev/null
+++ b/common/src/main/java/org/apache/sedona/common/Functions.java
@@ -0,0 +1,132 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sedona.common;
+
+import java.util.Optional;
+import org.apache.sedona.common.utils.GeomUtils;
+import org.apache.sedona.common.utils.GeometryGeoHashEncoder;
+import org.geotools.geometry.jts.JTS;
+import org.geotools.referencing.CRS;
+import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.geom.LineString;
+import org.opengis.referencing.FactoryException;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.TransformException;
+
+
+
+public class Functions {
+ public static Geometry buffer(Geometry geometry, double radius) {
+ return geometry.buffer(radius);
+ }
+
+ public static double distance(Geometry left, Geometry right) {
+ return left.distance(right);
+ }
+
+ public static double xMin(Geometry geometry) {
+ Coordinate[] points = geometry.getCoordinates();
+ double min = Double.MAX_VALUE;
+ for(int i=0; i < points.length; i++){
+ min = Math.min(points[i].getX(), min);
+ }
+ return min;
+ }
+
+ public static double xMax(Geometry geometry) {
+ Coordinate[] points = geometry.getCoordinates();
+ double max = Double.MIN_VALUE;
+ for (int i=0; i < points.length; i++) {
+ max = Math.max(points[i].getX(), max);
+ }
+ return max;
+ }
+
+ public static double yMin(Geometry geometry) {
+ Coordinate[] points = geometry.getCoordinates();
+ double min = Double.MAX_VALUE;
+ for(int i=0; i < points.length; i++){
+ min = Math.min(points[i].getY(), min);
+ }
+ return min;
+ }
+
+ public static double yMax(Geometry geometry) {
+ Coordinate[] points = geometry.getCoordinates();
+ double max = Double.MIN_VALUE;
+ for (int i=0; i < points.length; i++) {
+ max = Math.max(points[i].getY(), max);
+ }
+ return max;
+ }
+
+ public static Geometry transform(Geometry geometry, String sourceCRS, String targetCRS)
+ throws FactoryException, TransformException {
+ return transform(geometry, sourceCRS, targetCRS, false);
+ }
+
+ public static Geometry transform(Geometry geometry, String sourceCRS, String targetCRS, boolean lenient)
+ throws FactoryException, TransformException {
+ CoordinateReferenceSystem sourceCRScode = CRS.decode(sourceCRS);
+ CoordinateReferenceSystem targetCRScode = CRS.decode(targetCRS);
+ MathTransform transform = CRS.findMathTransform(sourceCRScode, targetCRScode, lenient);
+ return JTS.transform(geometry, transform);
+ }
+
+ public static Geometry flipCoordinates(Geometry geometry) {
+ GeomUtils.flipCoordinates(geometry);
+ return geometry;
+ }
+
+ public static String geohash(Geometry geometry, int precision) {
+ return GeometryGeoHashEncoder.calculate(geometry, precision);
+ }
+
+ public static Geometry pointOnSurface(Geometry geometry) {
+ return GeomUtils.getInteriorPoint(geometry);
+ }
+
+ public static Geometry reverse(Geometry geometry) {
+ return geometry.reverse();
+ }
+
+ public static Geometry pointN(Geometry geometry, int n) {
+ if(!(geometry instanceof LineString)) {
+ return null;
+ }
+ return GeomUtils.getNthPoint((LineString)geometry, n);
+ }
+
+ public static Geometry exteriorRing(Geometry geometry) {
+ return GeomUtils.getExteriorRing(geometry);
+ }
+
+ public static String asEWKT(Geometry geometry) {
+ return GeomUtils.getEWKT(geometry);
+ }
+
+ public static Geometry force2D(Geometry geometry) {
+ return GeomUtils.get2dGeom(geometry);
+ }
+
+ public static boolean isEmpty(Geometry geometry) {
+ return geometry.isEmpty();
+ }
+
+ public static Geometry buildArea(Geometry geometry) {
+ return GeomUtils.buildArea(geometry);
+ }
+}
diff --git a/common/src/main/java/org/apache/sedona/common/utils/BBox.java b/common/src/main/java/org/apache/sedona/common/utils/BBox.java
new file mode 100644
index 00000000..898ccee5
--- /dev/null
+++ b/common/src/main/java/org/apache/sedona/common/utils/BBox.java
@@ -0,0 +1,60 @@
+/*
+ * 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.Coordinate;
+import org.locationtech.jts.geom.GeometryFactory;
+import org.locationtech.jts.geom.Point;
+import org.locationtech.jts.geom.Polygon;
+
+public class BBox {
+ double startLon;
+ double endLon;
+ double startLat;
+ double endLat;
+
+ static GeometryFactory geometryFactory = new GeometryFactory();
+
+ public BBox(double startLon, double endLon, double startLat, double endLat) {
+ this.startLon = startLon;
+ this.endLon = endLon;
+ this.startLat = startLat;
+ this.endLat = endLat;
+ }
+
+ public BBox(BBox other) {
+ this(other.startLon, other.endLon, other.startLat, other.endLat);
+ }
+
+ public Point getCentroid() {
+ double lon = this.startLon + ((this.startLon + this.endLon)/2);
+ double lat = this.startLat + ((this.startLat + this.endLat)/2);
+ return geometryFactory.createPoint(new Coordinate(lon, lat));
+ }
+
+ public Polygon toPolygon() {
+ return geometryFactory.createPolygon(new Coordinate[] {
+ new Coordinate(this.startLon, this.startLat),
+ new Coordinate(this.startLon, this.endLat),
+ new Coordinate(this.endLon, this.endLat),
+ new Coordinate(this.endLon, this.startLat),
+ new Coordinate(this.startLon, this.startLat)
+ });
+ }
+}
\ No newline at end of file
diff --git a/core/src/main/java/org/apache/sedona/core/utils/GeomUtils.java b/common/src/main/java/org/apache/sedona/common/utils/GeomUtils.java
similarity index 99%
rename from core/src/main/java/org/apache/sedona/core/utils/GeomUtils.java
rename to common/src/main/java/org/apache/sedona/common/utils/GeomUtils.java
index f5b9b119..857ca1d2 100644
--- a/core/src/main/java/org/apache/sedona/core/utils/GeomUtils.java
+++ b/common/src/main/java/org/apache/sedona/common/utils/GeomUtils.java
@@ -11,7 +11,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.sedona.core.utils;
+package org.apache.sedona.common.utils;
import org.locationtech.jts.geom.*;
import org.locationtech.jts.geom.impl.CoordinateArraySequence;
@@ -27,8 +27,7 @@ import java.util.*;
import static org.locationtech.jts.geom.Coordinate.NULL_ORDINATE;
-public class GeomUtils
-{
+public class GeomUtils {
public static String printGeom(Geometry geom) {
if(geom.getUserData()!=null) return geom.toText() + "\t" + geom.getUserData();
else return geom.toText();
diff --git a/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/geohash/GeometryGeoHashEncoder.scala b/common/src/main/java/org/apache/sedona/common/utils/GeometryGeoHashEncoder.java
similarity index 51%
rename from sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/geohash/GeometryGeoHashEncoder.scala
rename to common/src/main/java/org/apache/sedona/common/utils/GeometryGeoHashEncoder.java
index f1ecbc19..d1e8e1a7 100644
--- a/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/geohash/GeometryGeoHashEncoder.scala
+++ b/common/src/main/java/org/apache/sedona/common/utils/GeometryGeoHashEncoder.java
@@ -16,22 +16,28 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.spark.sql.sedona_sql.expressions.geohash
+package org.apache.sedona.common.utils;
-import org.locationtech.jts.geom.{Coordinate, Geometry, GeometryFactory}
+import java.util.Optional;
+import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.Envelope;
+import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.geom.GeometryFactory;
-object GeometryGeoHashEncoder {
- private val geometryFactory = new GeometryFactory()
- def calculate(geom: Geometry, precision: Int): Option[String] = {
- val gbox = geom.getEnvelope.getEnvelopeInternal
+public class GeometryGeoHashEncoder {
+ private static GeometryFactory geometryFactory = new GeometryFactory();
+
+ public static String calculate(Geometry geom, int precision) {
+ Envelope gbox = geom.getEnvelope().getEnvelopeInternal();
// Latitude can take values in [-90, 90]
// Longitude can take values in [-180, 180]
- if (gbox.getMinX < -180 || gbox.getMinY < -90 || gbox.getMaxX > 180 || gbox.getMaxY > 90) None
- else {
- val lon = gbox.getMinX + (gbox.getMaxX - gbox.getMinX) / 2
- val lat = gbox.getMinY + (gbox.getMaxY - gbox.getMinY) / 2
-
- Some(PointGeoHashEncoder.calculateGeoHash(geometryFactory.createPoint(new Coordinate(lon, lat)), precision))
+ if (gbox.getMinX() < -180 || gbox.getMinY() < -90 || gbox.getMaxX() > 180 || gbox.getMaxY() > 90) {
+ return null;
}
+
+ double lon = gbox.getMinX() + (gbox.getMaxX() - gbox.getMinX()) / 2;
+ double lat = gbox.getMinY() + (gbox.getMaxY() - gbox.getMinY()) / 2;
+
+ return PointGeoHashEncoder.calculateGeoHash(geometryFactory.createPoint(new Coordinate(lon, lat)), precision);
}
}
diff --git a/common/src/main/java/org/apache/sedona/common/utils/PointGeoHashEncoder.java b/common/src/main/java/org/apache/sedona/common/utils/PointGeoHashEncoder.java
new file mode 100644
index 00000000..2305f2ea
--- /dev/null
+++ b/common/src/main/java/org/apache/sedona/common/utils/PointGeoHashEncoder.java
@@ -0,0 +1,94 @@
+/*
+ * 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.Point;
+
+
+public class PointGeoHashEncoder {
+ private static String base32 = "0123456789bcdefghjkmnpqrstuvwxyz";
+ private static int[] bits = new int[] { 16, 8, 4, 2, 1 };
+
+ public static String calculateGeoHash(Point geom, long precision) {
+ BBox bbox = new BBox(-180, 180, -90, 90);
+ long precisionUpdated = Math.min(precision, 20);
+ if (precision <= 0) {
+ return "";
+ }
+ return geoHashAggregate(geom, precisionUpdated, 0, "", true, bbox, 0, 0);
+
+ }
+
+ private static String geoHashAggregate(Point point, long precision, long currentPrecision,
+ String geoHash, boolean isEven, BBox bbox, int bit, int ch) {
+ if (currentPrecision >= precision) {
+ return geoHash;
+ }
+
+ BBox updatedBbox = null;
+ int updatedCh = -1;
+ if (isEven) {
+ double mid = (bbox.startLon + bbox.endLon) / 2.0;
+ if (point.getX() >= mid) {
+ updatedBbox = new BBox(bbox);
+ updatedBbox.startLon = mid;
+ updatedCh = ch | bits[bit];
+ } else {
+ updatedBbox = new BBox(bbox);
+ updatedBbox.endLon = mid;
+ updatedCh = ch;
+ }
+ } else {
+ double mid = (bbox.startLat + bbox.endLat) / 2.0;
+ if (point.getY() >= mid) {
+ updatedBbox = new BBox(bbox);
+ updatedBbox.startLat = mid;
+ updatedCh = ch | bits[bit];
+ } else {
+ updatedBbox = new BBox(bbox);
+ updatedBbox.endLat = mid;
+ updatedCh = ch;
+ }
+ }
+ if (bit < 4) {
+ return geoHashAggregate(
+ point,
+ precision,
+ currentPrecision,
+ geoHash,
+ !isEven,
+ updatedBbox,
+ bit + 1,
+ updatedCh
+ );
+ } else {
+ String geoHashUpdated = geoHash + base32.charAt(updatedCh);
+ return geoHashAggregate(
+ point,
+ precision,
+ currentPrecision + 1,
+ geoHashUpdated,
+ !isEven,
+ updatedBbox,
+ 0,
+ 0
+ );
+ }
+ }
+}
diff --git a/core/pom.xml b/core/pom.xml
index 336eb293..5908d3f1 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -37,6 +37,12 @@
</properties>
<dependencies>
+ <dependency>
+ <groupId>org.apache.sedona</groupId>
+ <artifactId>sedona-common</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
<!-- Test -->
<dependency>
<groupId>org.apache.hadoop</groupId>
diff --git a/core/src/main/java/org/apache/sedona/core/geometryObjects/Circle.java b/core/src/main/java/org/apache/sedona/core/geometryObjects/Circle.java
index 6e26c36e..b0f94cd6 100644
--- a/core/src/main/java/org/apache/sedona/core/geometryObjects/Circle.java
+++ b/core/src/main/java/org/apache/sedona/core/geometryObjects/Circle.java
@@ -19,7 +19,7 @@
package org.apache.sedona.core.geometryObjects;
-import org.apache.sedona.core.utils.GeomUtils;
+import org.apache.sedona.common.utils.GeomUtils;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.CoordinateFilter;
import org.locationtech.jts.geom.CoordinateSequenceComparator;
diff --git a/core/src/main/java/org/apache/sedona/core/spatialOperator/JoinQuery.java b/core/src/main/java/org/apache/sedona/core/spatialOperator/JoinQuery.java
index 78aa017d..46be5284 100644
--- a/core/src/main/java/org/apache/sedona/core/spatialOperator/JoinQuery.java
+++ b/core/src/main/java/org/apache/sedona/core/spatialOperator/JoinQuery.java
@@ -22,6 +22,7 @@ package org.apache.sedona.core.spatialOperator;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
+import org.apache.sedona.common.utils.GeomUtils;
import org.apache.sedona.core.enums.IndexType;
import org.apache.sedona.core.enums.JoinBuildSide;
import org.apache.sedona.core.geometryObjects.Circle;
@@ -34,7 +35,6 @@ import org.apache.sedona.core.monitoring.Metric;
import org.apache.sedona.core.spatialPartitioning.SpatialPartitioner;
import org.apache.sedona.core.spatialRDD.CircleRDD;
import org.apache.sedona.core.spatialRDD.SpatialRDD;
-import org.apache.sedona.core.utils.GeomUtils;
import org.apache.spark.SparkContext;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaRDD;
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 0044e3b9..4f1123d5 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,13 +21,13 @@ package org.apache.sedona.core.spatialRDD;
import org.apache.commons.lang.NullArgumentException;
import org.apache.log4j.Logger;
+import org.apache.sedona.common.utils.GeomUtils;
import org.apache.sedona.core.enums.GridType;
import org.apache.sedona.core.enums.IndexType;
import org.apache.sedona.core.spatialPartitioning.*;
import org.apache.sedona.core.spatialPartitioning.quadtree.StandardQuadTree;
import org.apache.sedona.core.spatialRddTool.IndexBuilder;
import org.apache.sedona.core.spatialRddTool.StatCalculator;
-import org.apache.sedona.core.utils.GeomUtils;
import org.apache.sedona.core.utils.RDDSampleUtils;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.function.FlatMapFunction;
diff --git a/core/src/test/java/org/apache/sedona/core/formatMapper/shapefileParser/shapes/GeometrySerdeTest.java b/core/src/test/java/org/apache/sedona/core/formatMapper/shapefileParser/shapes/GeometrySerdeTest.java
index 3bb9af4a..5fdb60ee 100644
--- a/core/src/test/java/org/apache/sedona/core/formatMapper/shapefileParser/shapes/GeometrySerdeTest.java
+++ b/core/src/test/java/org/apache/sedona/core/formatMapper/shapefileParser/shapes/GeometrySerdeTest.java
@@ -22,9 +22,9 @@ package org.apache.sedona.core.formatMapper.shapefileParser.shapes;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
+import org.apache.sedona.common.utils.GeomUtils;
import org.apache.sedona.core.geometryObjects.Circle;
import org.apache.sedona.core.geometryObjects.GeometrySerde;
-import org.apache.sedona.core.utils.GeomUtils;
import org.junit.Test;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryCollection;
diff --git a/core/src/test/java/org/apache/sedona/core/formatMapper/shapefileParser/shapes/ShapefileRDDTest.java b/core/src/test/java/org/apache/sedona/core/formatMapper/shapefileParser/shapes/ShapefileRDDTest.java
index c0bf3c94..488ab8df 100644
--- a/core/src/test/java/org/apache/sedona/core/formatMapper/shapefileParser/shapes/ShapefileRDDTest.java
+++ b/core/src/test/java/org/apache/sedona/core/formatMapper/shapefileParser/shapes/ShapefileRDDTest.java
@@ -20,13 +20,13 @@ package org.apache.sedona.core.formatMapper.shapefileParser.shapes;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
+import org.apache.sedona.common.utils.GeomUtils;
import org.apache.sedona.core.formatMapper.shapefileParser.ShapefileRDD;
import org.apache.sedona.core.formatMapper.shapefileParser.boundary.BoundBox;
import org.apache.sedona.core.spatialOperator.RangeQuery;
import org.apache.sedona.core.spatialRDD.LineStringRDD;
import org.apache.sedona.core.spatialRDD.PointRDD;
import org.apache.sedona.core.spatialRDD.PolygonRDD;
-import org.apache.sedona.core.utils.GeomUtils;
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaSparkContext;
import org.geotools.data.DataStore;
diff --git a/core/src/test/java/org/apache/sedona/core/formatMapper/shapefileParser/shapes/ShapefileReaderTest.java b/core/src/test/java/org/apache/sedona/core/formatMapper/shapefileParser/shapes/ShapefileReaderTest.java
index 6663e689..904381bf 100644
--- a/core/src/test/java/org/apache/sedona/core/formatMapper/shapefileParser/shapes/ShapefileReaderTest.java
+++ b/core/src/test/java/org/apache/sedona/core/formatMapper/shapefileParser/shapes/ShapefileReaderTest.java
@@ -26,6 +26,7 @@ import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.RemoteIterator;
import org.apache.hadoop.hdfs.HdfsConfiguration;
import org.apache.hadoop.hdfs.MiniDFSCluster;
+import org.apache.sedona.common.utils.GeomUtils;
import org.apache.sedona.core.TestBase;
import org.apache.sedona.core.formatMapper.shapefileParser.ShapefileReader;
import org.apache.sedona.core.formatMapper.shapefileParser.boundary.BoundBox;
@@ -34,7 +35,6 @@ import org.apache.sedona.core.spatialRDD.LineStringRDD;
import org.apache.sedona.core.spatialRDD.PointRDD;
import org.apache.sedona.core.spatialRDD.PolygonRDD;
import org.apache.sedona.core.spatialRDD.SpatialRDD;
-import org.apache.sedona.core.utils.GeomUtils;
import org.geotools.data.DataStore;
import org.geotools.data.DataStoreFinder;
import org.geotools.data.FeatureSource;
diff --git a/core/src/test/java/org/apache/sedona/core/geometryObjects/CircleTest.java b/core/src/test/java/org/apache/sedona/core/geometryObjects/CircleTest.java
index 7ef7c3cd..bd5fd416 100644
--- a/core/src/test/java/org/apache/sedona/core/geometryObjects/CircleTest.java
+++ b/core/src/test/java/org/apache/sedona/core/geometryObjects/CircleTest.java
@@ -18,7 +18,7 @@
*/
package org.apache.sedona.core.geometryObjects;
-import org.apache.sedona.core.utils.GeomUtils;
+import org.apache.sedona.common.utils.GeomUtils;
import org.junit.Test;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Envelope;
diff --git a/core/src/test/java/org/apache/sedona/core/spatialRDD/SpatialRDDWriterTest.java b/core/src/test/java/org/apache/sedona/core/spatialRDD/SpatialRDDWriterTest.java
index 8931f0c9..0e22c3f7 100644
--- a/core/src/test/java/org/apache/sedona/core/spatialRDD/SpatialRDDWriterTest.java
+++ b/core/src/test/java/org/apache/sedona/core/spatialRDD/SpatialRDDWriterTest.java
@@ -21,8 +21,8 @@ package org.apache.sedona.core.spatialRDD;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.NullArgumentException;
+import org.apache.sedona.common.utils.GeomUtils;
import org.apache.sedona.core.enums.FileDataSplitter;
-import org.apache.sedona.core.utils.GeomUtils;
import org.apache.spark.storage.StorageLevel;
import org.junit.AfterClass;
import org.junit.BeforeClass;
diff --git a/flink/pom.xml b/flink/pom.xml
index abd838b1..7bdbd409 100644
--- a/flink/pom.xml
+++ b/flink/pom.xml
@@ -38,6 +38,11 @@
</properties>
<dependencies>
+ <dependency>
+ <groupId>org.apache.sedona</groupId>
+ <artifactId>sedona-common</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>org.apache.sedona</groupId>
<artifactId>sedona-core-${spark.compat.version}_${scala.compat.version}</artifactId>
diff --git a/flink/src/main/java/org/apache/sedona/flink/Catalog.java b/flink/src/main/java/org/apache/sedona/flink/Catalog.java
index 81c2a457..3d8dd88a 100644
--- a/flink/src/main/java/org/apache/sedona/flink/Catalog.java
+++ b/flink/src/main/java/org/apache/sedona/flink/Catalog.java
@@ -14,7 +14,6 @@
package org.apache.sedona.flink;
import org.apache.flink.table.functions.UserDefinedFunction;
-import org.apache.sedona.core.spatialPartitioning.PartitioningUtils;
import org.apache.sedona.flink.expressions.*;
public class Catalog {
diff --git a/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java b/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java
index c0542afa..3ca77b8b 100644
--- a/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java
+++ b/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java
@@ -15,15 +15,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.core.utils.GeomUtils;
-import org.locationtech.jts.io.WKTWriter;
-import org.apache.spark.sql.sedona_sql.expressions.geohash.GeometryGeoHashEncoder;
-import org.apache.spark.sql.sedona_sql.expressions.geohash.PointGeoHashEncoder;
+import org.apache.sedona.common.utils.GeomUtils;
import org.geotools.geometry.jts.JTS;
import org.geotools.referencing.CRS;
+import org.locationtech.jts.io.WKTWriter;
+import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.LineString;
-import org.locationtech.jts.geom.Coordinate;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
@@ -39,7 +37,7 @@ public class Functions {
@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class)
public Geometry eval(Object o, @DataTypeHint("Double") Double radius) {
Geometry geom = (Geometry) o;
- return geom.buffer(radius);
+ return org.apache.sedona.common.Functions.buffer(geom, radius);
}
}
@@ -49,7 +47,7 @@ public class Functions {
@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) Object o2) {
Geometry geom1 = (Geometry) o1;
Geometry geom2 = (Geometry) o2;
- return geom1.distance(geom2);
+ return org.apache.sedona.common.Functions.distance(geom1, geom2);
}
}
@@ -57,12 +55,7 @@ public class Functions {
@DataTypeHint("Double")
public Double eval(@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) Object o){
Geometry geom = (Geometry) o;
- Coordinate[] points= geom.getCoordinates();
- double min=Double.MAX_VALUE;
- for(int i=0;i<points.length;i++){
- min=Math.min(points[i].getY(),min);
- }
- return min;
+ return org.apache.sedona.common.Functions.yMin(geom);
}
}
@@ -70,29 +63,17 @@ public class Functions {
@DataTypeHint("Double")
public Double eval(@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) Object o){
Geometry geom = (Geometry) o;
- Coordinate[] points= geom.getCoordinates();
- double max=Double.MIN_VALUE;
- for(int i=0;i<points.length;i++){
- max=Math.max(points[i].getY(),max);
- }
- return max;
+ return org.apache.sedona.common.Functions.yMax(geom);
}
}
public static class ST_Transform extends ScalarFunction {
@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class)
- public Geometry eval(@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) Object o, @DataTypeHint("String") String sourceCRS, @DataTypeHint("String") String targetCRS) {
+ public Geometry eval(@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) Object o, @DataTypeHint("String") String sourceCRS, @DataTypeHint("String") String targetCRS)
+ throws FactoryException, TransformException {
Geometry geom = (Geometry) o;
- try {
- CoordinateReferenceSystem sourceCRScode = CRS.decode(sourceCRS);
- CoordinateReferenceSystem targetCRScode = CRS.decode(targetCRS);
- MathTransform transform = CRS.findMathTransform(sourceCRScode, targetCRScode);
- geom = JTS.transform(geom, transform);
- } catch (FactoryException | TransformException e) {
- e.printStackTrace();
- }
- return geom;
+ return org.apache.sedona.common.Functions.transform(geom, sourceCRS, targetCRS);
}
}
@@ -100,8 +81,7 @@ public class Functions {
@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class)
public Geometry eval(@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) Object o) {
Geometry geom = (Geometry) o;
- GeomUtils.flipCoordinates(geom);
- return geom;
+ return org.apache.sedona.common.Functions.flipCoordinates(geom);
}
}
@@ -109,11 +89,7 @@ public class Functions {
@DataTypeHint("RAW")
public Optional<String> eval(@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) Object geometry, Integer precision) {
Geometry geom = (Geometry) geometry;
- Option<String> geoHash = GeometryGeoHashEncoder.calculate(geom, precision);
- if (geoHash.isDefined()){
- return Optional.of(geoHash.get());
- }
- return Optional.empty();
+ return Optional.ofNullable(org.apache.sedona.common.Functions.geohash(geom, precision));
}
}
@@ -121,8 +97,7 @@ public class Functions {
@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class)
public Geometry eval(@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) Object o) {
Geometry geom = (Geometry) o;
- GeomUtils.getInteriorPoint(geom);
- return geom;
+ return org.apache.sedona.common.Functions.pointOnSurface(geom);
}
}
@@ -130,26 +105,23 @@ public class Functions {
@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class)
public Geometry eval(@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) Object o) {
Geometry geom = (Geometry) o;
- return geom.reverse();
+ return org.apache.sedona.common.Functions.reverse(geom);
}
}
public static class ST_PointN extends ScalarFunction {
@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class)
public Geometry eval(@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) Object o, int n) {
- if(!(o instanceof LineString)) {
- return null;
- }
- LineString lineString = (LineString) o;
- return GeomUtils.getNthPoint(lineString, n);
+ Geometry geom = (Geometry) o;
+ return org.apache.sedona.common.Functions.pointN(geom, n);
}
}
public static class ST_ExteriorRing extends ScalarFunction {
@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class)
public Geometry eval(@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) Object o) {
- Geometry geometry = (Geometry) o;
- return GeomUtils.getExteriorRing(geometry);
+ Geometry geom = (Geometry) o;
+ return org.apache.sedona.common.Functions.exteriorRing(geom);
}
}
@@ -157,7 +129,7 @@ public class Functions {
@DataTypeHint("String")
public String eval(@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) Object o) {
Geometry geom = (Geometry) o;
- return GeomUtils.getEWKT(geom);
+ return org.apache.sedona.common.Functions.asEWKT(geom);
}
}
@@ -165,7 +137,7 @@ public class Functions {
@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class)
public Geometry eval(@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) Object o) {
Geometry geom = (Geometry) o;
- return GeomUtils.get2dGeom(geom);
+ return org.apache.sedona.common.Functions.force2D(geom);
}
}
@@ -173,7 +145,7 @@ public class Functions {
@DataTypeHint("Boolean")
public boolean eval(@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) Object o) {
Geometry geom = (Geometry) o;
- return geom.isEmpty();
+ return org.apache.sedona.common.Functions.isEmpty(geom);
}
}
@@ -181,14 +153,7 @@ public class Functions {
@DataTypeHint("Double")
public Double eval(@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) Object o) {
Geometry geom = (Geometry) o;
- Coordinate[] coord = geom.getCoordinates();
- double max = Double.MIN_VALUE;
- for (int i = 0; i < coord.length; i++) {
- if (coord[i].getX() > max) {
- max = coord[i].getX();
- }
- }
- return max;
+ return org.apache.sedona.common.Functions.xMax(geom);
}
}
@@ -196,14 +161,7 @@ public class Functions {
@DataTypeHint("Double")
public Double eval(@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) Object o) {
Geometry geom = (Geometry) o;
- Coordinate[] coord = geom.getCoordinates();
- double min = Double.MAX_VALUE;
- for(int i=0;i< coord.length;i++){
- if(coord[i].getX()<min){
- min = coord[i].getX();
- }
- }
- return min;
+ return org.apache.sedona.common.Functions.xMin(geom);
}
}
@@ -211,7 +169,7 @@ public class Functions {
@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class)
public Geometry eval(@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) Object o) {
Geometry geom = (Geometry) o;
- return GeomUtils.buildArea(geom);
+ return org.apache.sedona.common.Functions.buildArea(geom);
}
}
}
diff --git a/pom.xml b/pom.xml
index d92b7018..aae83e16 100644
--- a/pom.xml
+++ b/pom.xml
@@ -558,6 +558,7 @@
<scaladoc.arg>-no-java-comments</scaladoc.arg>
</properties>
<modules>
+ <module>common</module>
<module>core</module>
<module>sql</module>
<module>viz</module>
@@ -579,6 +580,7 @@
<scaladoc.arg>-no-java-comments</scaladoc.arg>
</properties>
<modules>
+ <module>common</module>
<module>core</module>
<module>sql</module>
<module>viz</module>
@@ -601,6 +603,7 @@
<scaladoc.arg />
</properties>
<modules>
+ <module>common</module>
<module>core</module>
<module>sql</module>
<module>viz</module>
diff --git a/sql/pom.xml b/sql/pom.xml
index 781939d6..c3de7f99 100644
--- a/sql/pom.xml
+++ b/sql/pom.xml
@@ -37,6 +37,11 @@
</properties>
<dependencies>
+ <dependency>
+ <groupId>org.apache.sedona</groupId>
+ <artifactId>sedona-common</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>org.apache.sedona</groupId>
<artifactId>sedona-core-${spark.compat.version}_${scala.compat.version}</artifactId>
diff --git a/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala b/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala
index 738e8001..a4097f18 100644
--- a/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala
+++ b/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala
@@ -18,17 +18,18 @@
*/
package org.apache.spark.sql.sedona_sql.expressions
+import org.apache.sedona.common.Functions
+import org.apache.sedona.common.utils.GeomUtils
import org.apache.sedona.core.geometryObjects.Circle
-import org.apache.sedona.core.utils.GeomUtils
import org.apache.sedona.sql.utils.GeometrySerializer
import org.apache.spark.internal.Logging
import org.apache.spark.sql.catalyst.InternalRow
import org.apache.spark.sql.catalyst.expressions.codegen.{CodegenContext, CodegenFallback, ExprCode}
-import org.apache.spark.sql.catalyst.expressions.{BoundReference, Expression, Generator}
+import org.apache.spark.sql.catalyst.expressions._
import org.apache.spark.sql.catalyst.util.{ArrayData, GenericArrayData}
import org.apache.spark.sql.sedona_sql.UDT.GeometryUDT
import org.apache.spark.sql.sedona_sql.expressions.collect.Collect
-import org.apache.spark.sql.sedona_sql.expressions.geohash.{GeoHashDecoder, GeometryGeoHashEncoder, InvalidGeoHashException}
+import org.apache.spark.sql.sedona_sql.expressions.geohash.{GeoHashDecoder, InvalidGeoHashException}
import org.apache.spark.sql.sedona_sql.expressions.implicits._
import org.apache.spark.sql.sedona_sql.expressions.subdivide.GeometrySubDivider
import org.apache.spark.sql.types.{ArrayType, _}
@@ -61,18 +62,7 @@ import scala.util.{Failure, Success, Try}
* @param inputExpressions This function takes two geometries and calculates the distance between two objects.
*/
case class ST_Distance(inputExpressions: Seq[Expression])
- extends BinaryGeometryExpression with CodegenFallback {
- assert(inputExpressions.length == 2)
-
- override def toString: String = s" **${ST_Distance.getClass.getName}** "
-
- override def nullSafeEval(leftGeometry: Geometry, rightGeometry: Geometry): Any = {
- leftGeometry.distance(rightGeometry)
- }
-
- override def dataType = DoubleType
-
- override def children: Seq[Expression] = inputExpressions
+ extends InferredBinaryExpression(Functions.distance) {
protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = {
copy(inputExpressions = newChildren)
@@ -81,21 +71,7 @@ case class ST_Distance(inputExpressions: Seq[Expression])
case class ST_YMax(inputExpressions: Seq[Expression])
- extends UnaryGeometryExpression with CodegenFallback {
- assert(inputExpressions.length == 1)
-
- override protected def nullSafeEval(geometry: Geometry): Any = {
- val seqRev : Array[Coordinate] = geometry.getCoordinates()
- var maxVal:Double = Double.MinValue
- for(x <- seqRev ){
- maxVal=Math.max(maxVal,x.getY())
- }
- maxVal
- }
-
- override def dataType: DataType = DoubleType
-
- override def children: Seq[Expression] = inputExpressions
+ extends InferredUnaryExpression(Functions.yMax) {
protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = {
copy(inputExpressions = newChildren)
@@ -103,26 +79,13 @@ case class ST_YMax(inputExpressions: Seq[Expression])
}
case class ST_YMin(inputExpressions: Seq[Expression])
- extends UnaryGeometryExpression with CodegenFallback {
- assert(inputExpressions.length == 1)
-
- override protected def nullSafeEval(geometry: Geometry): Any = {
- val seqRev : Array[Coordinate] = geometry.getCoordinates()
- var minVal:Double = Double.MaxValue
- for(x <- seqRev){
- minVal=Math.min(minVal,x.getY())
- }
- minVal
- }
-
- override def dataType: DataType = DoubleType
-
- override def children: Seq[Expression] = inputExpressions
+ extends InferredUnaryExpression(Functions.yMin) {
protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = {
copy(inputExpressions = newChildren)
}
}
+
case class ST_3DDistance(inputExpressions: Seq[Expression])
extends BinaryGeometryExpression with CodegenFallback {
assert(inputExpressions.length == 2)
@@ -191,26 +154,7 @@ case class ST_NPoints(inputExpressions: Seq[Expression])
* @param inputExpressions
*/
case class ST_Buffer(inputExpressions: Seq[Expression])
- extends Expression with CodegenFallback {
- assert(inputExpressions.length == 2)
-
- override def nullable: Boolean = true
-
- override def eval(input: InternalRow): Any = {
- val buffer: Double = inputExpressions(1).eval(input) match {
- case a: Decimal => a.toDouble
- case a: Double => a
- case a: Int => a
- }
- inputExpressions(0).toGeometry(input) match {
- case geometry: Geometry => geometry.buffer(buffer).toGenericArrayData
- case _ => null
- }
- }
-
- override def dataType: DataType = GeometryUDT
-
- override def children: Seq[Expression] = inputExpressions
+ extends InferredBinaryExpression(Functions.buffer) with CodegenFallback {
protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = {
copy(inputExpressions = newChildren)
@@ -318,22 +262,18 @@ case class ST_Transform(inputExpressions: Seq[Expression])
override def nullable: Boolean = true
override def eval(input: InternalRow): Any = {
- val originalGeometry = inputExpressions(0).toGeometry(input)
+ val geometry = inputExpressions(0).toGeometry(input)
val sourceCRS = inputExpressions(1).asString(input)
val targetCRS = inputExpressions(2).asString(input)
- (originalGeometry, sourceCRS, targetCRS) match {
- case (originalGeometry: Geometry, sourceCRS: String, targetCRS: String) =>
- val sourceCRScode = CRS.decode(sourceCRS)
- val targetCRScode = CRS.decode(targetCRS)
- var transform: MathTransform = null
- if (inputExpressions.length == 4) {
- transform = CRS.findMathTransform(sourceCRScode, targetCRScode, inputExpressions(3).eval(input).asInstanceOf[Boolean])
- }
- else {
- transform = CRS.findMathTransform(sourceCRScode, targetCRScode, false)
+ (geometry, sourceCRS, targetCRS) match {
+ case (geometry: Geometry, sourceCRS: String, targetCRS: String) =>
+ val lenient = if (inputExpressions.length == 4) {
+ inputExpressions(3).eval(input).asInstanceOf[Boolean]
+ } else {
+ false
}
- JTS.transform(originalGeometry, transform).toGenericArrayData
+ Functions.transform(geometry, sourceCRS, targetCRS, lenient).toGenericArrayData
case (_, _, _) => null
}
}
@@ -1027,19 +967,7 @@ case class ST_EndPoint(inputExpressions: Seq[Expression])
}
case class ST_ExteriorRing(inputExpressions: Seq[Expression])
- extends UnaryGeometryExpression with CodegenFallback {
-
- override protected def nullSafeEval(geometry: Geometry): Any = {
- geometry match {
- case polygon: Polygon => polygon.getExteriorRing.toGenericArrayData
- case _ => null
- }
-
- }
-
- override def dataType: DataType = GeometryUDT
-
- override def children: Seq[Expression] = inputExpressions
+ extends InferredUnaryExpression(Functions.exteriorRing) {
protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = {
copy(inputExpressions = newChildren)
@@ -1333,17 +1261,7 @@ case class ST_NumGeometries(inputExpressions: Seq[Expression])
* @param inputExpressions Geometry
*/
case class ST_FlipCoordinates(inputExpressions: Seq[Expression])
- extends UnaryGeometryExpression with CodegenFallback {
- assert(inputExpressions.length == 1)
-
- override protected def nullSafeEval(geometry: Geometry): Any = {
- GeomUtils.flipCoordinates(geometry)
- geometry.toGenericArrayData
- }
-
- override def dataType: DataType = GeometryUDT
-
- override def children: Seq[Expression] = inputExpressions
+ extends InferredUnaryExpression(Functions.flipCoordinates) {
protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = {
copy(inputExpressions = newChildren)
@@ -1449,32 +1367,7 @@ case class ST_MakePolygon(inputExpressions: Seq[Expression])
}
case class ST_GeoHash(inputExpressions: Seq[Expression])
- extends Expression with CodegenFallback {
- assert(inputExpressions.length == 2)
-
- override def nullable: Boolean = true
-
- override def eval(input: InternalRow): Any = {
- val geometry = inputExpressions(0).toGeometry(input)
-
- val precision = inputExpressions(1).toInt(input)
-
- geometry match {
- case geom: Geometry =>
- val geoHash = GeometryGeoHashEncoder.calculate(geom, precision)
- geoHash match {
- case Some(value) => UTF8String.fromString(value)
- case None => null
- }
-
- case _ => null
- }
-
- }
-
- override def dataType: DataType = StringType
-
- override def children: Seq[Expression] = inputExpressions
+ extends InferredBinaryExpression(Functions.geohash) with CodegenFallback {
protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = {
copy(inputExpressions = newChildren)
@@ -1581,16 +1474,7 @@ case class ST_Multi(inputExpressions: Seq[Expression]) extends UnaryGeometryExpr
* @param inputExpressions Geometry
*/
case class ST_PointOnSurface(inputExpressions: Seq[Expression])
- extends UnaryGeometryExpression with CodegenFallback {
- assert(inputExpressions.length == 1)
-
- override protected def nullSafeEval(geometry: Geometry): Any = {
- new GenericArrayData(GeometrySerializer.serialize(GeomUtils.getInteriorPoint(geometry)))
- }
-
- override def dataType: DataType = GeometryUDT
-
- override def children: Seq[Expression] = inputExpressions
+ extends InferredUnaryExpression(Functions.pointOnSurface) {
protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = {
copy(inputExpressions = newChildren)
@@ -1603,16 +1487,7 @@ case class ST_PointOnSurface(inputExpressions: Seq[Expression])
* @param inputExpressions
*/
case class ST_Reverse(inputExpressions: Seq[Expression])
- extends UnaryGeometryExpression with CodegenFallback {
- assert(inputExpressions.length == 1)
-
- override protected def nullSafeEval(geometry: Geometry): Any = {
- new GenericArrayData(GeometrySerializer.serialize(geometry.reverse()))
- }
-
- override def dataType: DataType = GeometryUDT
-
- override def children: Seq[Expression] = inputExpressions
+ extends InferredUnaryExpression(Functions.reverse) {
protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = {
copy(inputExpressions = newChildren)
@@ -1625,30 +1500,7 @@ case class ST_Reverse(inputExpressions: Seq[Expression])
* @param inputExpressions sequence of 2 input arguments, a geometry and a value 'n'
*/
case class ST_PointN(inputExpressions: Seq[Expression])
- extends Expression with CodegenFallback {
- inputExpressions.validateLength(2)
-
- override def nullable: Boolean = true
-
- override def eval(input: InternalRow): Any = {
- val geometry = inputExpressions.head.toGeometry(input)
- val n = inputExpressions(1).toInt(input)
- getNthPoint(geometry, n)
- }
-
- private def getNthPoint(geometry: Geometry, n: Int): GenericArrayData = {
- geometry match {
- case linestring: LineString => val point = GeomUtils.getNthPoint(linestring, n)
- point match {
- case geometry: Geometry => new GenericArrayData(GeometrySerializer.serialize(geometry))
- case _ => null
- }
- case _ => null
- }
- }
- override def dataType: DataType = GeometryUDT
-
- override def children: Seq[Expression] = inputExpressions
+ extends InferredBinaryExpression(Functions.pointN) {
protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = {
copy(inputExpressions = newChildren)
@@ -1661,16 +1513,7 @@ case class ST_PointN(inputExpressions: Seq[Expression])
* @param inputExpressions
*/
case class ST_Force_2D(inputExpressions: Seq[Expression])
- extends UnaryGeometryExpression with CodegenFallback {
- assert(inputExpressions.length == 1)
-
- override protected def nullSafeEval(geometry: Geometry): Any = {
- new GenericArrayData(GeometrySerializer.serialize(GeomUtils.get2dGeom(geometry)))
- }
-
- override def dataType: DataType = GeometryUDT
-
- override def children: Seq[Expression] = inputExpressions
+ extends InferredUnaryExpression(Functions.force2D) {
protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = {
copy(inputExpressions = newChildren)
@@ -1683,16 +1526,7 @@ case class ST_Force_2D(inputExpressions: Seq[Expression])
* @param inputExpressions
*/
case class ST_AsEWKT(inputExpressions: Seq[Expression])
- extends UnaryGeometryExpression with CodegenFallback {
- assert(inputExpressions.length == 1)
-
- override protected def nullSafeEval(geometry: Geometry): Any = {
- UTF8String.fromString(GeomUtils.getEWKT(geometry))
- }
-
- override def dataType: DataType = StringType
-
- override def children: Seq[Expression] = inputExpressions
+ extends InferredUnaryExpression(Functions.asEWKT) {
protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = {
copy(inputExpressions = newChildren)
@@ -1705,16 +1539,7 @@ case class ST_AsEWKT(inputExpressions: Seq[Expression])
* @param inputExpressions
*/
case class ST_IsEmpty(inputExpressions: Seq[Expression])
- extends UnaryGeometryExpression with CodegenFallback {
- assert(inputExpressions.length == 1)
-
- override protected def nullSafeEval(geometry: Geometry): Any = {
- geometry.isEmpty()
- }
-
- override def dataType: DataType = BooleanType
-
- override def children: Seq[Expression] = inputExpressions
+ extends InferredUnaryExpression(Functions.isEmpty) {
protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = {
copy(inputExpressions = newChildren)
@@ -1727,25 +1552,7 @@ case class ST_IsEmpty(inputExpressions: Seq[Expression])
* @param inputExpressions
*/
case class ST_XMax(inputExpressions: Seq[Expression])
- extends UnaryGeometryExpression with CodegenFallback {
- assert(inputExpressions.length == 1)
-
-
- override protected def nullSafeEval(geometry: Geometry): Any = {
- var coord:Array[Coordinate] = geometry.getCoordinates()
- var maxval = Double.MinValue
- for (point<-coord) {
- if(point.getX()>maxval){
- maxval = point.getX()
- }
- }
- maxval
-
- }
-
- override def dataType: DataType = DoubleType
-
- override def children: Seq[Expression] = inputExpressions
+ extends InferredUnaryExpression(Functions.xMax) {
protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = {
copy(inputExpressions = newChildren)
@@ -1758,25 +1565,7 @@ case class ST_XMax(inputExpressions: Seq[Expression])
* @param inputExpressions
*/
case class ST_XMin(inputExpressions: Seq[Expression])
- extends UnaryGeometryExpression with CodegenFallback {
- assert(inputExpressions.length == 1)
-
-
- override protected def nullSafeEval(geometry: Geometry): Any = {
- var coord: Array[Coordinate] = geometry.getCoordinates()
- var minval = Double.MaxValue
- for (point <- coord) {
- if (point.getX() < minval) {
- minval = point.getX()
- }
- }
- minval
-
- }
-
- override def dataType: DataType = DoubleType
-
- override def children: Seq[Expression] = inputExpressions
+ extends InferredUnaryExpression(Functions.xMin) {
protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = {
copy(inputExpressions = newChildren)
@@ -1790,22 +1579,7 @@ case class ST_XMin(inputExpressions: Seq[Expression])
* @param inputExpressions
*/
case class ST_BuildArea(inputExpressions: Seq[Expression])
- extends Expression with CodegenFallback {
- assert(inputExpressions.length == 1)
-
- override def nullable: Boolean = true
-
- override def eval(input: InternalRow): Any = {
- val geometry = inputExpressions.head.toGeometry(input)
- geometry match {
- case geom: Geometry => new GenericArrayData(GeometrySerializer.serialize(GeomUtils.buildArea(geom)))
- case _ => null
- }
- }
-
- override def dataType: DataType = GeometryUDT
-
- override def children: Seq[Expression] = inputExpressions
+ extends InferredUnaryExpression(Functions.buildArea) {
protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]): Expression = {
copy(inputExpressions = newChildren)
diff --git a/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/NullSafeExpressions.scala b/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/NullSafeExpressions.scala
index 8b7af2b0..87ed9ce1 100644
--- a/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/NullSafeExpressions.scala
+++ b/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/NullSafeExpressions.scala
@@ -18,10 +18,19 @@
*/
package org.apache.spark.sql.sedona_sql.expressions
+import org.apache.sedona.sql.utils.GeometrySerializer
import org.apache.spark.sql.catalyst.InternalRow
-import org.apache.spark.sql.catalyst.expressions.Expression
-import org.locationtech.jts.geom.Geometry
+import org.apache.spark.sql.catalyst.expressions._
+import org.apache.spark.sql.catalyst.expressions.codegen.CodegenFallback
+import org.apache.spark.sql.catalyst.util.GenericArrayData
import org.apache.spark.sql.sedona_sql.expressions.implicits._
+import org.apache.spark.sql.sedona_sql.UDT.GeometryUDT
+import org.apache.spark.sql.types._
+import org.apache.spark.unsafe.types.UTF8String
+import org.locationtech.jts.geom.Geometry
+
+import scala.reflect.runtime.universe._
+
abstract class UnaryGeometryExpression extends Expression {
def inputExpressions: Seq[Expression]
@@ -54,4 +63,137 @@ abstract class BinaryGeometryExpression extends Expression {
}
protected def nullSafeEval(leftGeometry: Geometry, rightGeometry: Geometry): Any
-}
\ No newline at end of file
+}
+
+// This is a compile time type shield for the types we are able to infer. Anything
+// other than these types will cause a compilation error. This is the Scala
+// 2 way of making a union type.
+sealed class InferrableType[T: TypeTag]
+object InferrableType {
+ implicit val geometryInstance: InferrableType[Geometry] =
+ new InferrableType[Geometry] {}
+ implicit val doubleInstance: InferrableType[Double] =
+ new InferrableType[Double] {}
+ implicit val booleanInstance: InferrableType[Boolean] =
+ new InferrableType[Boolean] {}
+ implicit val intInstance: InferrableType[Int] =
+ new InferrableType[Int] {}
+ implicit val stringInstance: InferrableType[String] =
+ new InferrableType[String] {}
+}
+
+object InferredTypes {
+ def buildExtractor[T: TypeTag](expr: Expression): InternalRow => T = {
+ if (typeOf[T] =:= typeOf[Geometry]) {
+ input: InternalRow => expr.toGeometry(input).asInstanceOf[T]
+ } else if (typeOf[T] =:= typeOf[String]) {
+ input: InternalRow => expr.asString(input).asInstanceOf[T]
+ } else {
+ input: InternalRow => expr.eval(input).asInstanceOf[T]
+ }
+ }
+
+ def buildSerializer[T: TypeTag]: T => Any = {
+ if (typeOf[T] =:= typeOf[Geometry]) {
+ output: T => if (output != null) {
+ output.asInstanceOf[Geometry].toGenericArrayData
+ } else {
+ null
+ }
+ } else if (typeOf[T] =:= typeOf[String]) {
+ output: T => if (output != null) {
+ UTF8String.fromString(output.asInstanceOf[String])
+ } else {
+ null
+ }
+ } else {
+ output: T => output
+ }
+ }
+
+ def inferSparkType[T: TypeTag]: DataType = {
+ if (typeOf[T] =:= typeOf[Geometry]) {
+ GeometryUDT
+ } else if (typeOf[T] =:= typeOf[Double]) {
+ DoubleType
+ } else if (typeOf[T] =:= typeOf[Int]) {
+ IntegerType
+ } else if (typeOf[T] =:= typeOf[String]) {
+ StringType
+ } else {
+ BooleanType
+ }
+ }
+}
+
+/**
+ * The implicit TypeTag's tell Scala to maintain generic type info at runtime. Normally type
+ * erasure would remove any knowledge of what the passed in generic type is.
+ */
+abstract class InferredUnaryExpression[A1: InferrableType, R: InferrableType]
+ (f: (A1) => R)
+ (implicit val a1Tag: TypeTag[A1], implicit val rTag: TypeTag[R])
+ extends Expression with ImplicitCastInputTypes with CodegenFallback with Serializable {
+ import InferredTypes._
+
+ def inputExpressions: Seq[Expression]
+ assert(inputExpressions.length == 1)
+
+ override def children: Seq[Expression] = inputExpressions
+
+ override def toString: String = s" **${getClass.getName}** "
+
+ override def inputTypes: Seq[AbstractDataType] = Seq(inferSparkType[A1])
+
+ override def nullable: Boolean = true
+
+ override def dataType = inferSparkType[R]
+
+ lazy val extract = buildExtractor[A1](inputExpressions(0))
+
+ lazy val serialize = buildSerializer[R]
+
+ override def eval(input: InternalRow): Any = {
+ val value = extract(input)
+ if (value != null) {
+ serialize(f(value))
+ } else {
+ null
+ }
+ }
+}
+
+abstract class InferredBinaryExpression[A1: InferrableType, A2: InferrableType, R: InferrableType]
+ (f: (A1, A2) => R)
+ (implicit val a1Tag: TypeTag[A1], implicit val a2Tag: TypeTag[A2], implicit val rTag: TypeTag[R])
+ extends Expression with ImplicitCastInputTypes with CodegenFallback with Serializable {
+ import InferredTypes._
+
+ def inputExpressions: Seq[Expression]
+ assert(inputExpressions.length == 2)
+
+ override def children: Seq[Expression] = inputExpressions
+
+ override def toString: String = s" **${getClass.getName}** "
+
+ override def inputTypes: Seq[AbstractDataType] = Seq(inferSparkType[A1], inferSparkType[A2])
+
+ override def nullable: Boolean = true
+
+ override def dataType = inferSparkType[R]
+
+ lazy val extractLeft = buildExtractor[A1](inputExpressions(0))
+ lazy val extractRight = buildExtractor[A2](inputExpressions(1))
+
+ lazy val serialize = buildSerializer[R]
+
+ override def eval(input: InternalRow): Any = {
+ val left = extractLeft(input)
+ val right = extractRight(input)
+ if (left != null && right != null) {
+ serialize(f(left, right))
+ } else {
+ null
+ }
+ }
+}
diff --git a/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/geohash/GeoHashDecoder.scala b/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/geohash/GeoHashDecoder.scala
index ae21b841..9e510cd0 100644
--- a/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/geohash/GeoHashDecoder.scala
+++ b/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/geohash/GeoHashDecoder.scala
@@ -18,6 +18,7 @@
*/
package org.apache.spark.sql.sedona_sql.expressions.geohash
+import org.apache.sedona.common.utils.BBox
import org.locationtech.jts.geom.{Geometry, GeometryFactory}
import scala.collection.mutable
@@ -68,10 +69,10 @@ object GeoHashDecoder {
}
private case class LatLon(var lons: mutable.Seq[Double], var lats: mutable.Seq[Double]){
- def getBbox(): BBox = BBox(
- startLat = lats.head,
- endLat = lats(1),
- startLon = lons.head,
- endLon = lons(1)
+ def getBbox(): BBox = new BBox(
+ lons.head,
+ lons(1),
+ lats.head,
+ lats(1)
)
}
diff --git a/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/geohash/PointGeoHashEncoder.scala b/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/geohash/PointGeoHashEncoder.scala
deleted file mode 100644
index eaacd123..00000000
--- a/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/geohash/PointGeoHashEncoder.scala
+++ /dev/null
@@ -1,101 +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.spark.sql.sedona_sql.expressions.geohash
-import org.locationtech.jts.geom.{Coordinate, GeometryFactory, Point, Polygon}
-
-
-object PointGeoHashEncoder {
- private val base32 = "0123456789bcdefghjkmnpqrstuvwxyz"
- private val bits = Seq(16, 8, 4, 2, 1)
-
- def calculateGeoHash(geom: Point, precision: Long): String = {
- val bbox = BBox(-180, 180, -90, 90)
- val precisionUpdated = math.min(precision, 20)
- if (precision <= 0) ""
- else geoHashAggregate(
- geom, precisionUpdated, 0, "", true, bbox, 0, 0
- )
-
- }
-
- private def geoHashAggregate(point: Point, precision: Long, currentPrecision: Long,
- geoHash: String, isEven: Boolean, bbox: BBox, bit: Int, ch: Int): String = {
- if (currentPrecision >= precision) geoHash
- else {
- val geoHashUpdate = if (isEven){
- val mid = (bbox.startLon + bbox.endLon) / 2.0
- if (point.getX >= mid) GeoHashUpdate(bbox.copy(startLon = mid), ch | bits(bit))
- else GeoHashUpdate(bbox.copy(endLon = mid), ch)
- }else {
- val mid = (bbox.startLat + bbox.endLat) / 2.0
- if (point.getY >= mid) GeoHashUpdate(bbox.copy(startLat = mid), ch | bits(bit))
- else GeoHashUpdate(bbox.copy(endLat = mid), ch)
- }
- if (bit < 4) {
-
- geoHashAggregate(
- point,
- precision,
- currentPrecision,
- geoHash,
- !isEven,
- geoHashUpdate.bbox,
- bit + 1,
- geoHashUpdate.ch
- )
- }
- else {
- val geoHashUpdated = geoHash + base32(geoHashUpdate.ch)
- geoHashAggregate(
- point,
- precision,
- currentPrecision + 1,
- geoHashUpdated,
- !isEven,
- geoHashUpdate.bbox,
- 0,
- 0
- )
- }
- }
- }
-}
-
-private[geohash] sealed case class BBox(startLon: Double, endLon: Double, startLat: Double, endLat: Double){
- private val geometryFactory = new GeometryFactory()
- def getCentroid(): Point = {
- val lon = startLon + ((startLon + endLon)/2)
- val lat = startLat + ((startLat + endLat)/2)
- geometryFactory.createPoint(new Coordinate(lon, lat))
- }
-
- def toPolygon(): Polygon = {
- geometryFactory.createPolygon(
- Array(
- new Coordinate(startLon, startLat),
- new Coordinate(startLon, endLat),
- new Coordinate(endLon, endLat),
- new Coordinate(endLon, startLat),
- new Coordinate(startLon, startLat)
- )
- )
- }
-}
-
-private[geohash] sealed case class GeoHashUpdate(bbox: BBox, ch: Int)
\ No newline at end of file
diff --git a/sql/src/test/scala/org/apache/sedona/sql/functions/geohash/Fixtures.scala b/sql/src/test/scala/org/apache/sedona/sql/functions/geohash/Fixtures.scala
index b98acdde..1d729286 100644
--- a/sql/src/test/scala/org/apache/sedona/sql/functions/geohash/Fixtures.scala
+++ b/sql/src/test/scala/org/apache/sedona/sql/functions/geohash/Fixtures.scala
@@ -18,7 +18,8 @@
*/
package org.apache.sedona.sql.functions.geohash
-import org.apache.spark.sql.sedona_sql.expressions.geohash.{GeoHashDecoder, GeometryGeoHashEncoder}
+import org.apache.sedona.common.utils.GeometryGeoHashEncoder
+import org.apache.spark.sql.sedona_sql.expressions.geohash.GeoHashDecoder
import org.locationtech.jts.geom.Geometry
import org.scalatest.prop.{TableDrivenPropertyChecks, TableFor2, TableFor3, TableFor4}
@@ -63,7 +64,7 @@ object Fixtures extends TableDrivenPropertyChecks {
)
def calculateGeoHash(geom: Geometry, precision: Int): Option[String] = {
- GeometryGeoHashEncoder.calculate(geom, precision)
+ Option(GeometryGeoHashEncoder.calculate(geom, precision))
}
def decodeGeoHash(geohash: String, precision: Option[Int]): Geometry =
diff --git a/viz/pom.xml b/viz/pom.xml
index b427a8c4..4a4530f8 100644
--- a/viz/pom.xml
+++ b/viz/pom.xml
@@ -37,6 +37,11 @@
</properties>
<dependencies>
+ <dependency>
+ <groupId>org.apache.sedona</groupId>
+ <artifactId>sedona-common</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>org.apache.sedona</groupId>
<artifactId>sedona-core-${spark.compat.version}_${scala.compat.version}</artifactId>