This is an automated email from the ASF dual-hosted git repository.
jiayu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sedona.git
The following commit(s) were added to refs/heads/master by this push:
new 6eedd19f [SEDONA-340] Add RS_ConvexHull | [SEDONA-341] Move
RS_Envelope to Geometry Functions (#942)
6eedd19f is described below
commit 6eedd19fb01412c18d9cdb1fb6b96b13710e53ef
Author: Nilesh Gajwani <[email protected]>
AuthorDate: Wed Aug 2 17:53:51 2023 -0700
[SEDONA-340] Add RS_ConvexHull | [SEDONA-341] Move RS_Envelope to Geometry
Functions (#942)
---
.../sedona/common/raster/GeometryFunctions.java | 71 +++++++++++++
.../sedona/common/raster/RasterAccessors.java | 13 ---
.../apache/sedona/common/utils/RasterUtils.java | 8 ++
.../apache/sedona/common/raster/FunctionsTest.java | 4 +-
.../common/raster/GeometryFunctionsTest.java | 112 +++++++++++++++++++++
.../sedona/common/raster/RasterAccessorsTest.java | 34 -------
.../common/raster/RasterConstructorsTest.java | 8 +-
docs/api/sql/Raster-operators.md | 55 ++++++----
.../scala/org/apache/sedona/sql/UDF/Catalog.scala | 3 +-
.../expressions/raster/GeometryFunctions.scala | 37 +++++++
.../expressions/raster/RasterAccessors.scala | 8 +-
.../org/apache/sedona/sql/rasteralgebraTest.scala | 29 +++++-
12 files changed, 303 insertions(+), 79 deletions(-)
diff --git
a/common/src/main/java/org/apache/sedona/common/raster/GeometryFunctions.java
b/common/src/main/java/org/apache/sedona/common/raster/GeometryFunctions.java
new file mode 100644
index 00000000..6e7f2817
--- /dev/null
+++
b/common/src/main/java/org/apache/sedona/common/raster/GeometryFunctions.java
@@ -0,0 +1,71 @@
+/*
+ * 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.raster;
+
+import org.apache.sedona.common.utils.RasterUtils;
+import org.geotools.coverage.grid.GridCoverage2D;
+import org.geotools.geometry.Envelope2D;
+import org.locationtech.jts.geom.*;
+import org.opengis.referencing.FactoryException;
+import org.opengis.referencing.operation.TransformException;
+
+import java.awt.geom.Point2D;
+
+public class GeometryFunctions {
+
+ /***
+ * Returns the convex hull of the input raster
+ * @param raster
+ * @return Geometry: convex hull of the input raster
+ * @throws FactoryException
+ * @throws TransformException
+ */
+ public static Geometry convexHull(GridCoverage2D raster) throws
FactoryException, TransformException {
+ int width = RasterAccessors.getWidth(raster), height =
RasterAccessors.getHeight(raster);
+ GeometryFactory geometryFactory = new GeometryFactory(new
PrecisionModel(), RasterAccessors.srid(raster));
+ double upperLeftX = RasterAccessors.getUpperLeftX(raster), upperLeftY
= RasterAccessors.getUpperLeftY(raster);
+ //First and last coord (Upper left)
+ Coordinate coordOne = new Coordinate(upperLeftX, upperLeftY);
+
+ //start clockwise rotation
+
+ //upper right
+ Point2D point = RasterUtils.getCornerCoordinates(raster, width + 1, 1);
+ Coordinate coordTwo = new Coordinate(point.getX(), point.getY());
+
+ //lower right
+ point = RasterUtils.getCornerCoordinates(raster, width + 1, height +
1);
+ Coordinate coordThree = new Coordinate(point.getX(), point.getY());
+
+ //lower left
+ point = RasterUtils.getCornerCoordinates(raster, 1, height + 1);
+ Coordinate coordFour = new Coordinate(point.getX(), point.getY());
+
+ return geometryFactory.createPolygon(new Coordinate[] {coordOne,
coordTwo, coordThree, coordFour, coordOne});
+ }
+
+ public static Geometry envelope(GridCoverage2D raster) throws
FactoryException {
+ Envelope2D envelope2D = raster.getEnvelope2D();
+
+ Envelope envelope = new Envelope(envelope2D.getMinX(),
envelope2D.getMaxX(), envelope2D.getMinY(), envelope2D.getMaxY());
+ int srid = RasterAccessors.srid(raster);
+ return new GeometryFactory(new PrecisionModel(),
srid).toGeometry(envelope);
+ }
+}
\ No newline at end of file
diff --git
a/common/src/main/java/org/apache/sedona/common/raster/RasterAccessors.java
b/common/src/main/java/org/apache/sedona/common/raster/RasterAccessors.java
index 07aaab24..3ab1d27f 100644
--- a/common/src/main/java/org/apache/sedona/common/raster/RasterAccessors.java
+++ b/common/src/main/java/org/apache/sedona/common/raster/RasterAccessors.java
@@ -21,14 +21,9 @@ package org.apache.sedona.common.raster;
import org.apache.sedona.common.utils.RasterUtils;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridEnvelope2D;
-import org.geotools.geometry.Envelope2D;
import org.geotools.referencing.CRS;
import org.geotools.referencing.crs.DefaultEngineeringCRS;
import org.geotools.referencing.operation.transform.AffineTransform2D;
-import org.locationtech.jts.geom.Envelope;
-import org.locationtech.jts.geom.Geometry;
-import org.locationtech.jts.geom.GeometryFactory;
-import org.locationtech.jts.geom.PrecisionModel;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
@@ -79,14 +74,6 @@ public class RasterAccessors
return RasterUtils.getGDALAffineTransform(raster).getScaleY();
}
- public static Geometry envelope(GridCoverage2D raster) throws
FactoryException {
- Envelope2D envelope2D = raster.getEnvelope2D();
-
- Envelope envelope = new Envelope(envelope2D.getMinX(),
envelope2D.getMaxX(), envelope2D.getMinY(), envelope2D.getMaxY());
- int srid = srid(raster);
- return new GeometryFactory(new PrecisionModel(),
srid).toGeometry(envelope);
- }
-
/**
* Returns the metadata of a raster as an array of doubles.
* @param raster the raster
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 a120c88b..b60b3cd5 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
@@ -21,15 +21,19 @@ package org.apache.sedona.common.utils;
import com.sun.media.imageioimpl.common.BogusColorSpace;
import org.geotools.coverage.CoverageFactoryFinder;
import org.geotools.coverage.GridSampleDimension;
+import org.geotools.coverage.grid.GridCoordinates2D;
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.referencing.operation.transform.AffineTransform2D;
import org.opengis.metadata.spatial.PixelOrientation;
import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.TransformException;
import java.awt.Transparency;
import java.awt.color.ColorSpace;
+import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
@@ -88,4 +92,8 @@ public class RasterUtils {
}
return (AffineTransform2D) crsTransform;
}
+
+ public static Point2D getCornerCoordinates(GridCoverage2D raster, int
colX, int rowY) throws TransformException {
+ return
raster.getGridGeometry().getGridToCRS2D(PixelOrientation.UPPER_LEFT).transform(new
GridCoordinates2D(colX - 1, rowY - 1), null);
+ }
}
diff --git
a/common/src/test/java/org/apache/sedona/common/raster/FunctionsTest.java
b/common/src/test/java/org/apache/sedona/common/raster/FunctionsTest.java
index d98441f5..9a1f2033 100644
--- a/common/src/test/java/org/apache/sedona/common/raster/FunctionsTest.java
+++ b/common/src/test/java/org/apache/sedona/common/raster/FunctionsTest.java
@@ -38,8 +38,8 @@ public class FunctionsTest extends RasterTestBase {
GridCoverage2D oneBandRasterWithUpdatedSrid =
RasterEditors.setSrid(oneBandRaster, 4326);
assertEquals(4326, RasterAccessors.srid(oneBandRasterWithUpdatedSrid));
- assertEquals(4326,
RasterAccessors.envelope(oneBandRasterWithUpdatedSrid).getSRID());
-
assertTrue(RasterAccessors.envelope(oneBandRasterWithUpdatedSrid).equalsTopo(RasterAccessors.envelope(oneBandRaster)));
+ assertEquals(4326,
GeometryFunctions.envelope(oneBandRasterWithUpdatedSrid).getSRID());
+
assertTrue(GeometryFunctions.envelope(oneBandRasterWithUpdatedSrid).equalsTopo(GeometryFunctions.envelope(oneBandRaster)));
GridCoverage2D multiBandRasterWithUpdatedSrid =
RasterEditors.setSrid(multiBandRaster, 0);
assertEquals(0 , RasterAccessors.srid(multiBandRasterWithUpdatedSrid));
diff --git
a/common/src/test/java/org/apache/sedona/common/raster/GeometryFunctionsTest.java
b/common/src/test/java/org/apache/sedona/common/raster/GeometryFunctionsTest.java
new file mode 100644
index 00000000..56f37ac9
--- /dev/null
+++
b/common/src/test/java/org/apache/sedona/common/raster/GeometryFunctionsTest.java
@@ -0,0 +1,112 @@
+/*
+ * 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.raster;
+
+import org.apache.sedona.common.Functions;
+import org.geotools.coverage.grid.GridCoverage2D;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.Envelope;
+import org.locationtech.jts.geom.Geometry;
+import org.opengis.referencing.FactoryException;
+import org.opengis.referencing.operation.TransformException;
+
+import java.io.IOException;
+
+public class GeometryFunctionsTest extends RasterTestBase {
+
+ @Test
+ public void testConvexHull() throws FactoryException, TransformException {
+ GridCoverage2D emptyRaster = RasterConstructors.makeEmptyRaster(1, 5,
10, 156, -132, 5);
+ Geometry convexHull = GeometryFunctions.convexHull(emptyRaster);
+ String expected = "POLYGON ((156 -132, 181 -132, 181 -182, 156 -182,
156 -132))";
+ String actual = Functions.asWKT(convexHull);
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testConvexHullSkewed() throws FactoryException,
TransformException {
+ GridCoverage2D emptyRaster = RasterConstructors.makeEmptyRaster(1, 5,
10, 156, -132, 5, 10, 3, 5, 0);
+ Geometry convexHull = GeometryFunctions.convexHull(emptyRaster);
+ String expected = "POLYGON ((156 -132, 181 -107, 211 -7, 186 -32, 156
-132))";
+ String actual = Functions.asWKT(convexHull);
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testConvexHullFromRasterFile() throws FactoryException,
TransformException, IOException {
+ GridCoverage2D raster = rasterFromGeoTiff(resourceFolder +
"raster/test1.tiff");
+ Geometry convexHull = GeometryFunctions.convexHull(raster);
+ Coordinate[] coordinates = convexHull.getCoordinates();
+ Coordinate expectedCoordOne = new Coordinate(-13095817.809482181,
4021262.7487925636);
+ Coordinate expectedCoordTwo = new Coordinate(-13058785.559768861,
4021262.7487925636);
+ Coordinate expectedCoordThree = new Coordinate(-13058785.559768861,
3983868.8560156375);
+ Coordinate expectedCoordFour = new Coordinate(-13095817.809482181,
3983868.8560156375);
+ assertEquals(expectedCoordOne.getX(), coordinates[0].getX(), 0.5d);
+ assertEquals(expectedCoordOne.getY(), coordinates[0].getY(), 0.5d);
+
+ assertEquals(expectedCoordTwo.getX(), coordinates[1].getX(), 0.5d);
+ assertEquals(expectedCoordTwo.getY(), coordinates[1].getY(), 0.5d);
+
+ assertEquals(expectedCoordThree.getX(), coordinates[2].getX(), 0.5d);
+ assertEquals(expectedCoordThree.getY(), coordinates[2].getY(), 0.5d);
+
+ assertEquals(expectedCoordFour.getX(), coordinates[3].getX(), 0.5d);
+ assertEquals(expectedCoordFour.getY(), coordinates[3].getY(), 0.5d);
+
+ assertEquals(expectedCoordOne.getX(), coordinates[4].getX(), 0.5d);
+ assertEquals(expectedCoordOne.getY(), coordinates[4].getY(), 0.5d);
+ }
+
+ @Test
+ public void envelope() throws FactoryException
+ {
+ Geometry envelope = GeometryFunctions.envelope(oneBandRaster);
+ assertEquals(3600.0d, envelope.getArea(), 0.1d);
+ assertEquals(378922.0d + 30.0d, envelope.getCentroid().getX(), 0.1d);
+ assertEquals(4072345.0d + 30.0d, envelope.getCentroid().getY(), 0.1d);
+ assertEquals(4326,
GeometryFunctions.envelope(multiBandRaster).getSRID());
+ }
+
+ @Test
+ public void testEnvelopeUsingSkewedRaster() throws FactoryException {
+ GridCoverage2D raster = RasterConstructors.makeEmptyRaster(1, 100,
100, 5, 4, 3, -2, 0.1, 0.15, 3857);
+ Geometry envelope = GeometryFunctions.envelope(raster);
+ Envelope env = envelope.getEnvelopeInternal();
+ // The expected values were obtained by running the following query in
PostGIS:
+ // SELECT ST_AsText(ST_Envelope(ST_MakeEmptyRaster(100, 100, 5, 4, 3,
-2, 0.1, 0.15, 3857)));
+ assertEquals(5, env.getMinX(), 1e-9);
+ assertEquals(315, env.getMaxX(), 1e-9);
+ assertEquals(-196, env.getMinY(), 1e-9);
+ assertEquals(19, env.getMaxY(), 1e-9);
+
+ raster = RasterConstructors.makeEmptyRaster(1, 800, 700, 5, 4, 0.3,
-0.2, -0.1, -0.15, 3857);
+ envelope = GeometryFunctions.envelope(raster);
+ env = envelope.getEnvelopeInternal();
+ // The expected values were obtained by running the following query in
PostGIS:
+ // SELECT ST_AsText(ST_Envelope(ST_MakeEmptyRaster(800, 700, 5, 4,
0.3, -0.2, -0.1, -0.15, 3857)));
+ assertEquals(-65, env.getMinX(), 1e-9);
+ assertEquals(245, env.getMaxX(), 1e-9);
+ assertEquals(-256, env.getMinY(), 1e-9);
+ assertEquals(4, env.getMaxY(), 1e-9);
+ }
+
+}
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 c6557c02..060c4838 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
@@ -20,8 +20,6 @@ package org.apache.sedona.common.raster;
import org.geotools.coverage.grid.GridCoverage2D;
import org.junit.Test;
-import org.locationtech.jts.geom.Envelope;
-import org.locationtech.jts.geom.Geometry;
import org.opengis.referencing.FactoryException;
import java.io.IOException;
@@ -30,38 +28,6 @@ import static org.junit.Assert.assertEquals;
public class RasterAccessorsTest extends RasterTestBase
{
- @Test
- public void envelope() throws FactoryException
- {
- Geometry envelope = RasterAccessors.envelope(oneBandRaster);
- assertEquals(3600.0d, envelope.getArea(), 0.1d);
- assertEquals(378922.0d + 30.0d, envelope.getCentroid().getX(), 0.1d);
- assertEquals(4072345.0d + 30.0d, envelope.getCentroid().getY(), 0.1d);
- assertEquals(4326,
RasterAccessors.envelope(multiBandRaster).getSRID());
- }
-
- @Test
- public void testEnvelopeUsingSkewedRaster() throws FactoryException {
- GridCoverage2D raster = RasterConstructors.makeEmptyRaster(1, 100,
100, 5, 4, 3, -2, 0.1, 0.15, 3857);
- Geometry envelope = RasterAccessors.envelope(raster);
- Envelope env = envelope.getEnvelopeInternal();
- // The expected values were obtained by running the following query in
PostGIS:
- // SELECT ST_AsText(ST_Envelope(ST_MakeEmptyRaster(100, 100, 5, 4, 3,
-2, 0.1, 0.15, 3857)));
- assertEquals(5, env.getMinX(), 1e-9);
- assertEquals(315, env.getMaxX(), 1e-9);
- assertEquals(-196, env.getMinY(), 1e-9);
- assertEquals(19, env.getMaxY(), 1e-9);
-
- raster = RasterConstructors.makeEmptyRaster(1, 800, 700, 5, 4, 0.3,
-0.2, -0.1, -0.15, 3857);
- envelope = RasterAccessors.envelope(raster);
- env = envelope.getEnvelopeInternal();
- // The expected values were obtained by running the following query in
PostGIS:
- // SELECT ST_AsText(ST_Envelope(ST_MakeEmptyRaster(800, 700, 5, 4,
0.3, -0.2, -0.1, -0.15, 3857)));
- assertEquals(-65, env.getMinX(), 1e-9);
- assertEquals(245, env.getMaxX(), 1e-9);
- assertEquals(-256, env.getMinY(), 1e-9);
- assertEquals(4, env.getMaxY(), 1e-9);
- }
@Test
public void testNumBands() {
diff --git
a/common/src/test/java/org/apache/sedona/common/raster/RasterConstructorsTest.java
b/common/src/test/java/org/apache/sedona/common/raster/RasterConstructorsTest.java
index 5c1c2a47..754810c9 100644
---
a/common/src/test/java/org/apache/sedona/common/raster/RasterConstructorsTest.java
+++
b/common/src/test/java/org/apache/sedona/common/raster/RasterConstructorsTest.java
@@ -30,7 +30,7 @@ public class RasterConstructorsTest
public void fromArcInfoAsciiGrid() throws IOException, FactoryException {
GridCoverage2D gridCoverage2D =
RasterConstructors.fromArcInfoAsciiGrid(arc.getBytes(StandardCharsets.UTF_8));
- Geometry envelope = RasterAccessors.envelope(gridCoverage2D);
+ Geometry envelope = GeometryFunctions.envelope(gridCoverage2D);
assertEquals(3600, envelope.getArea(), 0.1);
assertEquals(378922d + 30, envelope.getCentroid().getX(), 0.1);
assertEquals(4072345d + 30, envelope.getCentroid().getY(), 0.1);
@@ -44,7 +44,7 @@ public class RasterConstructorsTest
public void fromGeoTiff() throws IOException, FactoryException {
GridCoverage2D gridCoverage2D =
RasterConstructors.fromGeoTiff(geoTiff);
- Geometry envelope = RasterAccessors.envelope(gridCoverage2D);
+ Geometry envelope = GeometryFunctions.envelope(gridCoverage2D);
assertEquals(100, envelope.getArea(), 0.1);
assertEquals(5, envelope.getCentroid().getX(), 0.1);
assertEquals(5, envelope.getCentroid().getY(), 0.1);
@@ -64,7 +64,7 @@ public class RasterConstructorsTest
int numBands = 1;
GridCoverage2D gridCoverage2D =
RasterConstructors.makeEmptyRaster(numBands, widthInPixel, heightInPixel,
upperLeftX, upperLeftY, pixelSize);
- Geometry envelope = RasterAccessors.envelope(gridCoverage2D);
+ Geometry envelope = GeometryFunctions.envelope(gridCoverage2D);
assertEquals(upperLeftX, envelope.getEnvelopeInternal().getMinX(),
0.001);
assertEquals(upperLeftX + widthInPixel * pixelSize,
envelope.getEnvelopeInternal().getMaxX(), 0.001);
assertEquals(upperLeftY - heightInPixel * pixelSize,
envelope.getEnvelopeInternal().getMinY(), 0.001);
@@ -81,7 +81,7 @@ public class RasterConstructorsTest
assertEquals(1, gridCoverage2D.getNumSampleDimensions());
gridCoverage2D = RasterConstructors.makeEmptyRaster(numBands,
widthInPixel, heightInPixel, upperLeftX, upperLeftY, pixelSize, -pixelSize - 1,
0, 0, 0);
- envelope = RasterAccessors.envelope(gridCoverage2D);
+ envelope = GeometryFunctions.envelope(gridCoverage2D);
assertEquals(upperLeftX, envelope.getEnvelopeInternal().getMinX(),
0.001);
assertEquals(upperLeftX + widthInPixel * pixelSize,
envelope.getEnvelopeInternal().getMaxX(), 0.001);
assertEquals(upperLeftY - heightInPixel * (pixelSize + 1),
envelope.getEnvelopeInternal().getMinY(), 0.001);
diff --git a/docs/api/sql/Raster-operators.md b/docs/api/sql/Raster-operators.md
index 43528767..eae7ae12 100644
--- a/docs/api/sql/Raster-operators.md
+++ b/docs/api/sql/Raster-operators.md
@@ -32,6 +32,44 @@ Output:
IndexOutOfBoundsException: Specified pixel coordinates (6, 2) do not lie in
the raster
```
+## Geometry Functions
+
+### RS_Envelope
+
+Introduction: Returns the envelope of the raster as a Geometry.
+
+Format: `RS_Envelope (raster: Raster)`
+
+Since: `v1.4.0`
+
+Spark SQL example:
+```sql
+SELECT RS_Envelope(raster) FROM raster_table
+```
+Output:
+```
+POLYGON ((0 0,20 0,20 60,0 60,0 0))
+```
+
+### RS_ConvexHull
+
+Introduction: Return the convex hull geometry of the raster including the
NoDataBandValue band pixels.
+For regular shaped and non-skewed rasters, this gives more or less the same
result as RS_ConvexHull and hence is only useful for irregularly shaped or
skewed rasters.
+
+Format: `RS_ConvexHull(raster: Raster)`
+
+Since: `1.5.0`
+
+Spark SQL example:
+```sql
+SELECT RS_ConvexHull(SELECT RS_PixelAsPoint(RS_MakeEmptyRaster(1, 5, 10, 156,
-132, 5, 10, 3, 5, 0)));
+```
+
+Output:
+```
+POLYGON ((156 -132, 181 -107, 211 -7, 186 -32, 156 -132))
+```
+
## Raster Accessors
### RS_Height
@@ -158,23 +196,6 @@ Output:
## Raster based operators
-### RS_Envelope
-
-Introduction: Returns the envelope of the raster as a Geometry.
-
-Format: `RS_Envelope (raster: Raster)`
-
-Since: `v1.4.0`
-
-Spark SQL example:
-```sql
-SELECT RS_Envelope(raster) FROM raster_table
-```
-Output:
-```
-POLYGON((0 0,20 0,20 60,0 60,0 0))
-```
-
### RS_Intersects
Introduction: Returns true if the envelope of the raster intersects the given
geometry. If the geometry does not have a
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 f9259db1..2886d93d 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
@@ -211,7 +211,8 @@ object Catalog {
function[RS_UpperLeftY](),
function[RS_ScaleX](),
function[RS_ScaleY](),
- function[RS_PixelAsPoint]()
+ function[RS_PixelAsPoint](),
+ function[RS_ConvexHull]()
)
val aggregateExpressions: Seq[Aggregator[Geometry, Geometry, Geometry]] =
Seq(
diff --git
a/sql/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/raster/GeometryFunctions.scala
b/sql/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/raster/GeometryFunctions.scala
new file mode 100644
index 00000000..d503c014
--- /dev/null
+++
b/sql/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/raster/GeometryFunctions.scala
@@ -0,0 +1,37 @@
+/*
+ * 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.raster
+
+import org.apache.sedona.common.raster.GeometryFunctions
+import org.apache.spark.sql.catalyst.expressions.Expression
+import org.apache.spark.sql.sedona_sql.expressions.InferredExpression
+import
org.apache.spark.sql.sedona_sql.expressions.InferrableFunctionConverter._
+
+case class RS_ConvexHull(inputExpressions: Seq[Expression]) extends
InferredExpression(GeometryFunctions.convexHull _) {
+ protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) =
{
+ copy(inputExpressions = newChildren)
+ }
+}
+
+case class RS_Envelope(inputExpressions: Seq[Expression]) extends
InferredExpression(GeometryFunctions.envelope _) {
+ protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) =
{
+ copy(inputExpressions = newChildren)
+ }
+}
+
diff --git
a/sql/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/raster/RasterAccessors.scala
b/sql/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/raster/RasterAccessors.scala
index 39c45d8e..e1e9bd11 100644
---
a/sql/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/raster/RasterAccessors.scala
+++
b/sql/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/raster/RasterAccessors.scala
@@ -18,17 +18,11 @@
*/
package org.apache.spark.sql.sedona_sql.expressions.raster
-import org.apache.sedona.common.raster.RasterAccessors
+import org.apache.sedona.common.raster.{GeometryFunctions, RasterAccessors}
import org.apache.spark.sql.catalyst.expressions.Expression
import
org.apache.spark.sql.sedona_sql.expressions.InferrableFunctionConverter._
import org.apache.spark.sql.sedona_sql.expressions.InferredExpression
-case class RS_Envelope(inputExpressions: Seq[Expression]) extends
InferredExpression(RasterAccessors.envelope _) {
- protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) =
{
- copy(inputExpressions = newChildren)
- }
-}
-
case class RS_NumBands(inputExpressions: Seq[Expression]) extends
InferredExpression(RasterAccessors.numBands _) {
protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) =
{
copy(inputExpressions = newChildren)
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 5a80a2c0..7dd4e16e 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
@@ -21,7 +21,7 @@ package org.apache.sedona.sql
import org.apache.spark.sql.functions.{collect_list, expr}
import org.geotools.coverage.grid.GridCoverage2D
import org.junit.Assert.assertEquals
-import org.locationtech.jts.geom.Geometry
+import org.locationtech.jts.geom.{Coordinate, Geometry}
import org.scalatest.{BeforeAndAfter, GivenWhenThen}
import scala.collection.mutable
@@ -517,6 +517,33 @@ class rasteralgebraTest extends TestBaseScala with
BeforeAndAfter with GivenWhen
assertEquals(expected, result, 1e-9)
}
+ it("Passed RS_ConvexHull with raster") {
+ val df = sparkSession.read.format("binaryFile").load(resourceFolder +
"raster/test1.tiff")
+ val result =
df.selectExpr("RS_ConvexHull(RS_FromGeoTiff(content))").first().getAs[Geometry](0);
+ val coordinates = result.getCoordinates;
+
+ val expectedCoordOne = new Coordinate(-13095817.809482181,
4021262.7487925636)
+ val expectedCoordTwo = new Coordinate(-13058785.559768861,
4021262.7487925636)
+ val expectedCoordThree = new Coordinate(-13058785.559768861,
3983868.8560156375)
+ val expectedCoordFour = new Coordinate(-13095817.809482181,
3983868.8560156375)
+
+ assertEquals(expectedCoordOne.getX, coordinates(0).getX, 0.5d)
+ assertEquals(expectedCoordOne.getY, coordinates(0).getY, 0.5d)
+
+ assertEquals(expectedCoordTwo.getX, coordinates(1).getX, 0.5d)
+ assertEquals(expectedCoordTwo.getY, coordinates(1).getY, 0.5d)
+
+ assertEquals(expectedCoordThree.getX, coordinates(2).getX, 0.5d)
+ assertEquals(expectedCoordThree.getY, coordinates(2).getY, 0.5d)
+
+ assertEquals(expectedCoordFour.getX, coordinates(3).getX, 0.5d)
+ assertEquals(expectedCoordFour.getY, coordinates(3).getY, 0.5d)
+
+ assertEquals(expectedCoordOne.getX, coordinates(4).getX, 0.5d)
+ assertEquals(expectedCoordOne.getY, coordinates(4).getY, 0.5d)
+
+ }
+
it("Passed RS_PixelAsPoint with raster") {
val widthInPixel = 5
val heightInPixel = 10