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 8d5c76d5 [SEDONA-338] Fix `RS_MakeEmptyRaster` and make affine
transformation parameters compliant with the GDAL/PostGIS convention (#940)
8d5c76d5 is described below
commit 8d5c76d52d855f6ee7de6e0132117cfa52ab40c7
Author: Kristin Cowalcijk <[email protected]>
AuthorDate: Thu Aug 3 00:36:14 2023 +0800
[SEDONA-338] Fix `RS_MakeEmptyRaster` and make affine transformation
parameters compliant with the GDAL/PostGIS convention (#940)
---
.../apache/sedona/common/raster/MapAlgebra.java | 30 +------
.../sedona/common/raster/RasterAccessors.java | 69 +++++++--------
.../sedona/common/raster/RasterConstructors.java | 48 ++++++-----
.../apache/sedona/common/utils/RasterUtils.java | 91 ++++++++++++++++++++
.../sedona/common/raster/RasterAccessorsTest.java | 98 ++++++++++++++++------
.../common/raster/RasterConstructorsTest.java | 5 +-
docs/api/sql/Raster-loader.md | 14 ++--
docs/api/sql/Raster-operators.md | 6 +-
.../org/apache/sedona/sql/rasteralgebraTest.scala | 2 +-
9 files changed, 234 insertions(+), 129 deletions(-)
diff --git
a/common/src/main/java/org/apache/sedona/common/raster/MapAlgebra.java
b/common/src/main/java/org/apache/sedona/common/raster/MapAlgebra.java
index 67e8ad18..98a77204 100644
--- a/common/src/main/java/org/apache/sedona/common/raster/MapAlgebra.java
+++ b/common/src/main/java/org/apache/sedona/common/raster/MapAlgebra.java
@@ -18,25 +18,15 @@
*/
package org.apache.sedona.common.raster;
-import com.sun.media.imageioimpl.common.BogusColorSpace;
-import org.geotools.coverage.CoverageFactoryFinder;
+import org.apache.sedona.common.utils.RasterUtils;
import org.geotools.coverage.GridSampleDimension;
import org.geotools.coverage.grid.GridCoverage2D;
-import org.geotools.coverage.grid.GridCoverageFactory;
import javax.media.jai.RasterFactory;
-
import java.awt.Point;
-import java.awt.Transparency;
-import java.awt.color.ColorSpace;
-import java.awt.image.BufferedImage;
-import java.awt.image.ColorModel;
-import java.awt.image.ComponentColorModel;
-import java.awt.image.DataBuffer;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.awt.image.WritableRaster;
-import java.util.Arrays;
public class MapAlgebra
{
@@ -134,7 +124,7 @@ public class MapAlgebra
System.arraycopy(originalSampleDimensions, 0, sampleDimensions, 0,
originalSampleDimensions.length);
sampleDimensions[numBand - 1] = new GridSampleDimension("band" +
numBand);
// Construct a GridCoverage2D with the copied image.
- return createCompatibleGridCoverage2D(gridCoverage2D, wr,
sampleDimensions);
+ return RasterUtils.create(wr, gridCoverage2D.getGridGeometry(),
sampleDimensions);
}
private static GridCoverage2D copyRasterAndReplaceBand(GridCoverage2D
gridCoverage2D, int bandIndex, double[] bandValues) {
@@ -155,20 +145,6 @@ public class MapAlgebra
}
}
// Create a new GridCoverage2D with the copied image
- return createCompatibleGridCoverage2D(gridCoverage2D, wr,
gridCoverage2D.getSampleDimensions());
- }
-
- private static GridCoverage2D
createCompatibleGridCoverage2D(GridCoverage2D gridCoverage2D, WritableRaster
wr, GridSampleDimension[] bands) {
- int rasterDataType = wr.getDataBuffer().getDataType();
- int numBand = wr.getNumBands();
- final ColorSpace cs = new BogusColorSpace(numBand);
- final int[] nBits = new int[numBand];
- Arrays.fill(nBits, DataBuffer.getDataTypeSize(rasterDataType));
- ColorModel colorModel =
- new ComponentColorModel(cs, nBits, false, true,
Transparency.OPAQUE, rasterDataType);
- final RenderedImage image = new BufferedImage(colorModel, wr, false,
null);
- GridCoverageFactory gridCoverageFactory =
CoverageFactoryFinder.getGridCoverageFactory(null);
- return gridCoverageFactory.create(gridCoverage2D.getName(), image,
gridCoverage2D.getCoordinateReferenceSystem(),
- gridCoverage2D.getGridGeometry().getGridToCRS(), bands, null,
null);
+ return RasterUtils.create(wr, gridCoverage2D.getGridGeometry(),
gridCoverage2D.getSampleDimensions());
}
}
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 e5841680..07aaab24 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
@@ -18,9 +18,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.GridGeometry2D;
-import org.geotools.coverage.processing.operation.Affine;
+import org.geotools.coverage.grid.GridEnvelope2D;
import org.geotools.geometry.Envelope2D;
import org.geotools.referencing.CRS;
import org.geotools.referencing.crs.DefaultEngineeringCRS;
@@ -31,9 +31,7 @@ import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.PrecisionModel;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
-import org.opengis.referencing.operation.MathTransform;
-import java.awt.image.RenderedImage;
import java.util.Optional;
public class RasterAccessors
@@ -64,30 +62,21 @@ public class RasterAccessors
}
public static double getUpperLeftX(GridCoverage2D raster) {
- Envelope2D envelope2D = raster.getEnvelope2D();
- return envelope2D.getMinX();
+ AffineTransform2D affine = RasterUtils.getGDALAffineTransform(raster);
+ return affine.getTranslateX();
}
public static double getUpperLeftY(GridCoverage2D raster) {
- Envelope2D envelope2D = raster.getEnvelope2D();
- return envelope2D.getMaxY();
+ AffineTransform2D affine = RasterUtils.getGDALAffineTransform(raster);
+ return affine.getTranslateY();
}
public static double getScaleX(GridCoverage2D raster) {
- return getAffineTransform(raster).getScaleX();
+ return RasterUtils.getGDALAffineTransform(raster).getScaleX();
}
public static double getScaleY(GridCoverage2D raster) {
- return getAffineTransform(raster).getScaleY();
- }
-
- private static AffineTransform2D getAffineTransform(GridCoverage2D raster)
throws UnsupportedOperationException {
- GridGeometry2D gridGeometry2D = raster.getGridGeometry();
- MathTransform crsTransform = gridGeometry2D.getGridToCRS2D();
- if (!(crsTransform instanceof AffineTransform2D)) {
- throw new UnsupportedOperationException("Only AffineTransform2D is
supported");
- }
- return (AffineTransform2D) crsTransform;
+ return RasterUtils.getGDALAffineTransform(raster).getScaleY();
}
public static Geometry envelope(GridCoverage2D raster) throws
FactoryException {
@@ -100,16 +89,16 @@ public class RasterAccessors
/**
* Returns the metadata of a raster as an array of doubles.
- * @param raster
+ * @param raster the raster
* @return double[] with the following values:
- * 0: minX: upper left x
- * 1: maxY: upper left y
+ * 0: upperLeftX: upper left x
+ * 1: upperLeftY: upper left y
* 2: width: number of pixels on x axis
* 3: height: number of pixels on y axis
* 4: scaleX: pixel width
* 5: scaleY: pixel height
- * 6: shearX: skew on x axis
- * 7: shearY: skew on y axis
+ * 6: skewX: skew on x axis
+ * 7: skewY: skew on y axis
* 8: srid
* 9: numBands
* @throws FactoryException
@@ -117,23 +106,21 @@ public class RasterAccessors
public static double[] metadata(GridCoverage2D raster)
throws FactoryException
{
- RenderedImage image = raster.getRenderedImage();
- // Georeference metadata
- Envelope2D envelope2D = raster.getEnvelope2D();
- MathTransform gridToCRS = raster.getGridGeometry().getGridToCRS2D();
- if (gridToCRS instanceof AffineTransform2D) {
- AffineTransform2D affine = (AffineTransform2D) gridToCRS;
+ // Get Geo-reference metadata
+ GridEnvelope2D gridRange = raster.getGridGeometry().getGridRange2D();
+ AffineTransform2D affine = RasterUtils.getGDALAffineTransform(raster);
- // Get the affine parameters
- double scaleX = affine.getScaleX();
- double scaleY = affine.getScaleY();
- double shearX = affine.getShearX();
- double shearY = affine.getShearY();
- return new double[] {envelope2D.getMinX(), envelope2D.getMaxY(),
image.getWidth(), image.getHeight(), scaleX, scaleY, shearX, shearY,
srid(raster), raster.getNumSampleDimensions()};
- }
- else {
- // Handle the case where gridToCRS is not an AffineTransform2D
- throw new UnsupportedOperationException("Only AffineTransform2D is
supported");
- }
+ // Get the affine parameters
+ double upperLeftX = affine.getTranslateX();
+ double upperLeftY = affine.getTranslateY();
+ double scaleX = affine.getScaleX();
+ double scaleY = affine.getScaleY();
+ double skewX = affine.getShearX();
+ double skewY = affine.getShearY();
+ return new double[] {
+ upperLeftX, upperLeftY,
+ gridRange.getWidth(), gridRange.getHeight(),
+ scaleX, scaleY, skewX, skewY,
+ srid(raster), raster.getNumSampleDimensions()};
}
}
diff --git
a/common/src/main/java/org/apache/sedona/common/raster/RasterConstructors.java
b/common/src/main/java/org/apache/sedona/common/raster/RasterConstructors.java
index 9111cf3e..019748f3 100644
---
a/common/src/main/java/org/apache/sedona/common/raster/RasterConstructors.java
+++
b/common/src/main/java/org/apache/sedona/common/raster/RasterConstructors.java
@@ -14,19 +14,18 @@
package org.apache.sedona.common.raster;
import org.apache.sedona.common.raster.inputstream.ByteArrayImageInputStream;
-import org.geotools.coverage.CoverageFactoryFinder;
+import org.apache.sedona.common.utils.RasterUtils;
import org.geotools.coverage.grid.GridCoverage2D;
-import org.geotools.coverage.grid.GridCoverageFactory;
import org.geotools.coverage.grid.GridEnvelope2D;
import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.gce.arcgrid.ArcGridReader;
import org.geotools.gce.geotiff.GeoTiffReader;
-import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.referencing.CRS;
import org.geotools.referencing.crs.DefaultEngineeringCRS;
import org.geotools.referencing.operation.transform.AffineTransform2D;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.datum.PixelInCell;
import org.opengis.referencing.operation.MathTransform;
import javax.media.jai.RasterFactory;
@@ -47,26 +46,28 @@ public class RasterConstructors
}
/**
- * Create a new empty raster with the given number of empty bands
- * The bounding envelope is defined by the upper left corner and the scale
+ * Create a new empty raster with the given number of empty bands.
+ * The bounding envelope is defined by the upper left corner and the scale.
* The math formula of the envelope is: minX = upperLeftX = lowerLeftX,
minY (lowerLeftY) = upperLeftY - height * pixelSize
- * The raster is defined by the width and height
- * The affine transform is defined by the skewX and skewY
- * The upper left corner is defined by the upperLeftX and upperLeftY
- * The scale is defined by the scaleX and scaleY
- * SRID is default to 0 which means the default CRS (Cartesian 2D)
+ * <ul>
+ * <li>The raster is defined by the width and height
+ * <li>The upper left corner is defined by the upperLeftX and upperLeftY
+ * <li>The scale is defined by pixelSize. The scaleX is equal to
pixelSize and scaleY is equal to -pixelSize
+ * <li>skewX and skewY are zero, which means no shear or rotation.
+ * <li>SRID is default to 0 which means the default CRS (Generic 2D)
+ * </ul>
* @param numBand the number of bands
- * @param widthInPixel
- * @param heightInPixel
+ * @param widthInPixel the width of the raster, in pixel
+ * @param heightInPixel the height of the raster, in pixel
* @param upperLeftX the upper left corner of the raster. Note that: the
minX of the envelope is equal to the upperLeftX
- * @param upperLeftY the upper left corner of the raster. Note that: the
minY of the envelope is equal to the upperLeftY - height * scaleY
+ * @param upperLeftY the upper left corner of the raster. Note that: the
minY of the envelope is equal to the upperLeftY - height * pixelSize
* @param pixelSize the size of the pixel in the unit of the CRS
- * @return
+ * @return the new empty raster
*/
public static GridCoverage2D makeEmptyRaster(int numBand, int
widthInPixel, int heightInPixel, double upperLeftX, double upperLeftY, double
pixelSize)
throws FactoryException
{
- return makeEmptyRaster(numBand, widthInPixel, heightInPixel,
upperLeftX, upperLeftY, pixelSize, pixelSize, 0, 0, 0);
+ return makeEmptyRaster(numBand, widthInPixel, heightInPixel,
upperLeftX, upperLeftY, pixelSize, -pixelSize, 0, 0, 0);
}
/**
@@ -75,13 +76,13 @@ public class RasterConstructors
* @param widthInPixel the width of the raster, in pixel
* @param heightInPixel the height of the raster, in pixel
* @param upperLeftX the upper left corner of the raster, in the CRS unit.
Note that: the minX of the envelope is equal to the upperLeftX
- * @param upperLeftY the upper left corner of the raster, in the CRS unit.
Note that: the minY of the envelope is equal to the upperLeftY - height * scaleY
+ * @param upperLeftY the upper left corner of the raster, in the CRS unit.
Note that: the minY of the envelope is equal to the upperLeftY + height * scaleY
* @param scaleX the scale of the raster (pixel size on X), in the CRS unit
* @param scaleY the scale of the raster (pixel size on Y), in the CRS unit
* @param skewX the skew of the raster on X, in the CRS unit
* @param skewY the skew of the raster on Y, in the CRS unit
* @param srid the srid of the CRS. 0 means the default CRS (Cartesian 2D)
- * @return
+ * @return the new empty raster
* @throws FactoryException
*/
public static GridCoverage2D makeEmptyRaster(int numBand, int
widthInPixel, int heightInPixel, double upperLeftX, double upperLeftY, double
scaleX, double scaleY, double skewX, double skewY, int srid)
@@ -93,13 +94,14 @@ public class RasterConstructors
} else {
crs = CRS.decode("EPSG:" + srid);
}
+
// Create a new empty raster
WritableRaster raster =
RasterFactory.createBandedRaster(DataBuffer.TYPE_DOUBLE, widthInPixel,
heightInPixel, numBand, null);
- MathTransform transform = new AffineTransform2D(scaleX, skewY, skewX,
-scaleY, upperLeftX + scaleX / 2, upperLeftY - scaleY / 2);
- GridGeometry2D gridGeometry = new GridGeometry2D(new GridEnvelope2D(0,
0, widthInPixel, heightInPixel), transform, crs);
- ReferencedEnvelope referencedEnvelope = new
ReferencedEnvelope(gridGeometry.getEnvelope2D());
- // Create a new coverage
- GridCoverageFactory gridCoverageFactory =
CoverageFactoryFinder.getGridCoverageFactory(null);
- return gridCoverageFactory.create("genericCoverage", raster,
referencedEnvelope);
+ MathTransform transform = new AffineTransform2D(scaleX, skewY, skewX,
scaleY, upperLeftX, upperLeftY);
+ GridGeometry2D gridGeometry = new GridGeometry2D(
+ new GridEnvelope2D(0, 0, widthInPixel, heightInPixel),
+ PixelInCell.CELL_CORNER,
+ transform, crs, null);
+ return RasterUtils.create(raster, gridGeometry, null);
}
}
diff --git
a/common/src/main/java/org/apache/sedona/common/utils/RasterUtils.java
b/common/src/main/java/org/apache/sedona/common/utils/RasterUtils.java
new file mode 100644
index 00000000..a120c88b
--- /dev/null
+++ b/common/src/main/java/org/apache/sedona/common/utils/RasterUtils.java
@@ -0,0 +1,91 @@
+/*
+ * 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 com.sun.media.imageioimpl.common.BogusColorSpace;
+import org.geotools.coverage.CoverageFactoryFinder;
+import org.geotools.coverage.GridSampleDimension;
+import org.geotools.coverage.grid.GridCoverage2D;
+import org.geotools.coverage.grid.GridCoverageFactory;
+import org.geotools.coverage.grid.GridGeometry2D;
+import org.geotools.referencing.operation.transform.AffineTransform2D;
+import org.opengis.metadata.spatial.PixelOrientation;
+import org.opengis.referencing.operation.MathTransform;
+
+import java.awt.Transparency;
+import java.awt.color.ColorSpace;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.ComponentColorModel;
+import java.awt.image.DataBuffer;
+import java.awt.image.RenderedImage;
+import java.awt.image.WritableRaster;
+import java.util.Arrays;
+
+/**
+ * Utility functions for working with GridCoverage2D objects.
+ */
+public class RasterUtils {
+ private RasterUtils() {}
+
+ private static final GridCoverageFactory gridCoverageFactory =
CoverageFactoryFinder.getGridCoverageFactory(null);
+
+ /**
+ * Create a new empty raster from the given WritableRaster object.
+ * @param raster The raster object to be wrapped as an image.
+ * @param gridGeometry The grid geometry of the raster.
+ * @param bands The bands of the raster.
+ * @return A new GridCoverage2D object.
+ */
+ public static GridCoverage2D create(WritableRaster raster, GridGeometry2D
gridGeometry, GridSampleDimension[] bands) {
+ int numBand = raster.getNumBands();
+ int rasterDataType = raster.getDataBuffer().getDataType();
+
+ // Construct a color model for the rendered image. This color model
should be able to be serialized and
+ // deserialized. The color model object automatically constructed by
grid coverage factory may not be
+ // serializable, please refer to
https://issues.apache.org/jira/browse/SEDONA-319 for more details.
+ final ColorSpace cs = new BogusColorSpace(numBand);
+ final int[] nBits = new int[numBand];
+ Arrays.fill(nBits, DataBuffer.getDataTypeSize(rasterDataType));
+ ColorModel colorModel =
+ new ComponentColorModel(cs, nBits, false, true,
Transparency.OPAQUE, rasterDataType);
+
+ final RenderedImage image = new BufferedImage(colorModel, raster,
false, null);
+ return gridCoverageFactory.create("genericCoverage", image,
gridGeometry, bands, null, null);
+ }
+
+ /**
+ * Get a GDAL-compliant affine transform from the given raster, where the
grid coordinate indicates the upper left
+ * corner of the pixel. PostGIS also follows GDAL convention.
+ * @param raster The raster to get the affine transform from.
+ * @return The affine transform.
+ */
+ public static AffineTransform2D getGDALAffineTransform(GridCoverage2D
raster) {
+ return getAffineTransform(raster, PixelOrientation.UPPER_LEFT);
+ }
+
+ public static AffineTransform2D getAffineTransform(GridCoverage2D raster,
PixelOrientation orientation) throws UnsupportedOperationException {
+ GridGeometry2D gridGeometry2D = raster.getGridGeometry();
+ MathTransform crsTransform =
gridGeometry2D.getGridToCRS2D(orientation);
+ if (!(crsTransform instanceof AffineTransform2D)) {
+ throw new UnsupportedOperationException("Only AffineTransform2D is
supported");
+ }
+ return (AffineTransform2D) crsTransform;
+ }
+}
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 d70ad0f8..af9a487a 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,6 +20,7 @@ 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;
@@ -34,10 +35,32 @@ public class RasterAccessorsTest extends RasterTestBase
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() {
assertEquals(1, RasterAccessors.numBands(oneBandRaster));
@@ -75,18 +98,18 @@ public class RasterAccessorsTest extends RasterTestBase
gridCoverage2D = RasterConstructors.makeEmptyRaster(10, 7, 8, 5, 6, 9);
upperLeftY = RasterAccessors.getUpperLeftY(gridCoverage2D);
- assertEquals(6, upperLeftY, 0.1d);
+ assertEquals(6, upperLeftY, 0.1d);
}
@Test
public void testScaleX() throws UnsupportedOperationException,
FactoryException {
- GridCoverage2D emptyRaster = RasterConstructors.makeEmptyRaster(2, 10,
15, 0, 0, 1, 2, 0, 0, 0);
+ GridCoverage2D emptyRaster = RasterConstructors.makeEmptyRaster(2, 10,
15, 0, 0, 1, -2, 0, 0, 0);
assertEquals(1, RasterAccessors.getScaleX(emptyRaster), 1e-9);
}
@Test
public void testScaleY() throws UnsupportedOperationException,
FactoryException {
- GridCoverage2D emptyRaster = RasterConstructors.makeEmptyRaster(2, 10,
15, 0, 0, 1, 2, 0, 0, 0);
+ GridCoverage2D emptyRaster = RasterConstructors.makeEmptyRaster(2, 10,
15, 0, 0, 1, -2, 0, 0, 0);
assertEquals(-2, RasterAccessors.getScaleY(emptyRaster), 1e-9);
}
@@ -103,16 +126,16 @@ public class RasterAccessorsTest extends RasterTestBase
GridCoverage2D gridCoverage2D =
RasterConstructors.makeEmptyRaster(numBands, widthInPixel, heightInPixel,
upperLeftX, upperLeftY, pixelSize);
double[] metadata = RasterAccessors.metadata(gridCoverage2D);
- assertEquals(upperLeftX, metadata[0], 0.1d);
- assertEquals(upperLeftY, metadata[1], 0.1d);
- assertEquals(widthInPixel, metadata[2], 0.1d);
- assertEquals(heightInPixel, metadata[3], 0.1d);
- assertEquals(pixelSize, metadata[4], 0.1d);
- assertEquals(-1 * pixelSize, metadata[5], 0.1d);
- assertEquals(0, metadata[6], 0.1d);
- assertEquals(0, metadata[7], 0.1d);
- assertEquals(0, metadata[8], 0.1d);
- assertEquals(numBands, metadata[9], 0.1d);
+ assertEquals(upperLeftX, metadata[0], 1e-9);
+ assertEquals(upperLeftY, metadata[1], 1e-9);
+ assertEquals(widthInPixel, metadata[2], 1e-9);
+ assertEquals(heightInPixel, metadata[3], 1e-9);
+ assertEquals(pixelSize, metadata[4], 1e-9);
+ assertEquals(-1 * pixelSize, metadata[5], 1e-9);
+ assertEquals(0, metadata[6], 1e-9);
+ assertEquals(0, metadata[7], 1e-9);
+ assertEquals(0, metadata[8], 1e-9);
+ assertEquals(numBands, metadata[9], 1e-9);
assertEquals(10, metadata.length);
upperLeftX = 5;
@@ -126,17 +149,44 @@ public class RasterAccessorsTest extends RasterTestBase
metadata = RasterAccessors.metadata(gridCoverage2D);
- assertEquals(upperLeftX, metadata[0], 0.1d);
- assertEquals(upperLeftY, metadata[1], 0.1d);
- assertEquals(widthInPixel, metadata[2], 0.1d);
- assertEquals(heightInPixel, metadata[3], 0.1d);
- assertEquals(pixelSize, metadata[4], 0.1d);
- assertEquals(-1 * pixelSize, metadata[5], 0.1d);
- assertEquals(0, metadata[6], 0.1d);
- assertEquals(0, metadata[7], 0.1d);
- assertEquals(0, metadata[8], 0.1d);
- assertEquals(numBands, metadata[9], 0.1d);
+ assertEquals(upperLeftX, metadata[0], 1e-9);
+ assertEquals(upperLeftY, metadata[1], 1e-9);
+ assertEquals(widthInPixel, metadata[2], 1e-9);
+ assertEquals(heightInPixel, metadata[3], 1e-9);
+ assertEquals(pixelSize, metadata[4], 1e-9);
+ assertEquals(-1 * pixelSize, metadata[5], 1e-9);
+ assertEquals(0, metadata[6], 1e-9);
+ assertEquals(0, metadata[7], 1e-9);
+ assertEquals(0, metadata[8], 1e-9);
+ assertEquals(numBands, metadata[9], 1e-9);
+
+ assertEquals(10, metadata.length);
+ }
+ @Test
+ public void testMetaDataUsingSkewedRaster() throws FactoryException {
+ int widthInPixel = 3;
+ int heightInPixel = 4;
+ double upperLeftX = 100.0;
+ double upperLeftY = 200.0;
+ double scaleX = 2.0;
+ double scaleY = -3.0;
+ double skewX = 0.1;
+ double skewY = 0.2;
+ int numBands = 1;
+
+ GridCoverage2D gridCoverage2D =
RasterConstructors.makeEmptyRaster(numBands, widthInPixel, heightInPixel,
upperLeftX, upperLeftY, scaleX, scaleY, skewX, skewY, 3857);
+ double[] metadata = RasterAccessors.metadata(gridCoverage2D);
+ assertEquals(upperLeftX, metadata[0], 1e-9);
+ assertEquals(upperLeftY, metadata[1], 1e-9);
+ assertEquals(widthInPixel, metadata[2], 1e-9);
+ assertEquals(heightInPixel, metadata[3], 1e-9);
+ assertEquals(scaleX, metadata[4], 1e-9);
+ assertEquals(scaleY, metadata[5], 1e-9);
+ assertEquals(skewX, metadata[6], 1e-9);
+ assertEquals(skewY, metadata[7], 1e-9);
+ assertEquals(3857, metadata[8], 1e-9);
+ assertEquals(numBands, metadata[9], 1e-9);
assertEquals(10, metadata.length);
}
}
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 6f397e7d..5c1c2a47 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
@@ -80,12 +80,11 @@ public class RasterConstructorsTest
assertEquals(0d,
gridCoverage2D.getRenderedImage().getData().getPixel(0, 0, (double[])null)[0],
0.001);
assertEquals(1, gridCoverage2D.getNumSampleDimensions());
- gridCoverage2D = RasterConstructors.makeEmptyRaster(numBands,
widthInPixel, heightInPixel, upperLeftX, upperLeftY, pixelSize, pixelSize + 1,
0, 0, 0);
+ gridCoverage2D = RasterConstructors.makeEmptyRaster(numBands,
widthInPixel, heightInPixel, upperLeftX, upperLeftY, pixelSize, -pixelSize - 1,
0, 0, 0);
envelope = RasterAccessors.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);
assertEquals(upperLeftY, envelope.getEnvelopeInternal().getMaxY(),
0.001);
-
}
-}
\ No newline at end of file
+}
diff --git a/docs/api/sql/Raster-loader.md b/docs/api/sql/Raster-loader.md
index c0a15640..ba382fa9 100644
--- a/docs/api/sql/Raster-loader.md
+++ b/docs/api/sql/Raster-loader.md
@@ -80,7 +80,7 @@ Format: `RS_MakeEmptyRaster(numBands:Int, width: Int, height:
Int, upperleftX: D
SQL example 1 (with 2 bands):
```sql
-SELECT RS_MakeEmptyRaster(2, 10, 10, 0.0, 0.0, 1.0) as raster
+SELECT RS_MakeEmptyRaster(2, 10, 10, 0.0, 0.0, 1.0)
```
Output:
@@ -95,16 +95,16 @@ Output:
SQL example 1 (with 2 bands, scale, skew, and SRID):
```sql
-SELECT RS_MakeEmptyRaster(2, 10, 10, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 4326) as
raster
+SELECT RS_MakeEmptyRaster(2, 10, 10, 0.0, 0.0, 1.0, -1.0, 0.0, 0.0, 4326)
```
Output:
```
-+--------------------------------------------------------------+
-|rs_makeemptyraster(2, 10, 10, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0)|
-+--------------------------------------------------------------+
-| GridCoverage2D["g...|
-+--------------------------------------------------------------+
++------------------------------------------------------------------+
+|rs_makeemptyraster(2, 10, 10, 0.0, 0.0, 1.0, -1.0, 0.0, 0.0, 4326)|
++------------------------------------------------------------------+
+| GridCoverage2D["g...|
++------------------------------------------------------------------+
```
## Load GeoTiff to Array[Double] format
diff --git a/docs/api/sql/Raster-operators.md b/docs/api/sql/Raster-operators.md
index 5bead932..f2f4f650 100644
--- a/docs/api/sql/Raster-operators.md
+++ b/docs/api/sql/Raster-operators.md
@@ -164,12 +164,12 @@ true
Introduction: Returns the metadata of the raster as an array of double. The
array contains the following values:
-- 0: upper left x coordinate of the raster, in terms of CRS units (the minimum
x coordinate)
-- 1: upper left y coordinate of the raster, in terms of CRS units (the maximum
y coordinate)
+- 0: upper left x coordinate of the raster, in terms of CRS units
+- 1: upper left y coordinate of the raster, in terms of CRS units
- 2: width of the raster, in terms of pixels
- 3: height of the raster, in terms of pixels
- 4: width of a pixel, in terms of CRS units (scaleX)
-- 5: height of a pixel, in terms of CRS units (scaleY)
+- 5: height of a pixel, in terms of CRS units (scaleY), may be negative
- 6: skew in x direction (rotation x)
- 7: skew in y direction (rotation y)
- 8: srid of the raster
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 36d21b73..88d4d33d 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
@@ -445,7 +445,7 @@ class rasteralgebraTest extends TestBaseScala with
BeforeAndAfter with GivenWhen
val skewX = 0.0
val skewY = 0.0
val srid = 0
- result = sparkSession.sql(s"SELECT
RS_Metadata(RS_MakeEmptyRaster($numBands, $widthInPixel, $heightInPixel,
$upperLeftX, $upperLeftY, $cellSize, $cellSize, $skewX, $skewY,
$srid))").first().getSeq(0)
+ result = sparkSession.sql(s"SELECT
RS_Metadata(RS_MakeEmptyRaster($numBands, $widthInPixel, $heightInPixel,
$upperLeftX, $upperLeftY, $cellSize, -$cellSize, $skewX, $skewY,
$srid))").first().getSeq(0)
assertEquals(numBands, result(9), 0.001)
}