Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SEDONA-193] Refactor MakeEmptyRaster to allow setting custom datatype #957

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public static GridCoverage2D fromGeoTiff(byte[] bytes) throws IOException {
}

/**
* Convenience function setting DOUBLE as datatype for the bands
* 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
Expand All @@ -70,9 +71,50 @@ public static GridCoverage2D makeEmptyRaster(int numBand, int widthInPixel, int
return makeEmptyRaster(numBand, widthInPixel, heightInPixel, upperLeftX, upperLeftY, pixelSize, -pixelSize, 0, 0, 0);
}

/**
* Convenience function allowing explicitly setting the datatype for all the bands
* @param numBand
* @param dataType
* @param widthInPixel
* @param heightInPixel
* @param upperLeftX
* @param upperLeftY
* @param pixelSize
* @return
* @throws FactoryException
*/
public static GridCoverage2D makeEmptyRaster(int numBand, String dataType, int widthInPixel, int heightInPixel, double upperLeftX, double upperLeftY, double pixelSize)
throws FactoryException
{
return makeEmptyRaster(numBand, dataType, widthInPixel, heightInPixel, upperLeftX, upperLeftY, pixelSize, -pixelSize, 0, 0, 0);
}


/**
* Convenience function for creating a raster with data type DOUBLE for all the bands
* @param numBand
* @param widthInPixel
* @param heightInPixel
* @param upperLeftX
* @param upperLeftY
* @param scaleX
* @param scaleY
* @param skewX
* @param skewY
* @param srid
* @return
* @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)
throws FactoryException
{
return makeEmptyRaster(numBand, "d", widthInPixel, heightInPixel, upperLeftX, upperLeftY, scaleX, scaleY, skewX, skewY, srid);
}

/**
* Create a new empty raster with the given number of empty bands
* @param numBand the number of bands
* @param bandDataType the data type of the raster, one of D | B | I | F | S | US
* @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
Expand All @@ -85,7 +127,7 @@ public static GridCoverage2D makeEmptyRaster(int numBand, int widthInPixel, int
* @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)
public static GridCoverage2D makeEmptyRaster(int numBand, String bandDataType, int widthInPixel, int heightInPixel, double upperLeftX, double upperLeftY, double scaleX, double scaleY, double skewX, double skewY, int srid)
throws FactoryException
{
CoordinateReferenceSystem crs;
Expand All @@ -96,12 +138,30 @@ public static GridCoverage2D makeEmptyRaster(int numBand, int widthInPixel, int
}

// Create a new empty raster
WritableRaster raster = RasterFactory.createBandedRaster(DataBuffer.TYPE_DOUBLE, widthInPixel, heightInPixel, numBand, null);
WritableRaster raster = RasterFactory.createBandedRaster(getDataTypeCode(bandDataType), widthInPixel, heightInPixel, numBand, null);
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);
}
}

private static int getDataTypeCode(String s) {
switch (s.toLowerCase()) {
case "d":
return 5;
case "i":
return 3;
case "b":
return 0;
case "f":
return 4;
case "u":
return 2;
case "us":
return 1;
}
return 5; // defaulting to double
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,23 @@ public void makeEmptyRaster() throws FactoryException {
int heightInPixel = 2;
double pixelSize = 2;
int numBands = 1;
String dataType = "I";

GridCoverage2D gridCoverage2D = RasterConstructors.makeEmptyRaster(numBands, widthInPixel, heightInPixel, upperLeftX, upperLeftY, pixelSize);
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);
assertEquals(upperLeftY, envelope.getEnvelopeInternal().getMaxY(), 0.001);
assertEquals("REAL_64BITS", gridCoverage2D.getSampleDimension(0).getSampleDimensionType().name());

gridCoverage2D = RasterConstructors.makeEmptyRaster(numBands, dataType, widthInPixel, heightInPixel, upperLeftX, upperLeftY, pixelSize);
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);
assertEquals(upperLeftY, envelope.getEnvelopeInternal().getMaxY(), 0.001);
assertEquals("SIGNED_32BITS", gridCoverage2D.getSampleDimension(0).getSampleDimensionType().name());

assertEquals("POLYGON ((0 -4, 0 0, 2 0, 2 -4, 0 -4))", envelope.toString());
double expectedWidthInDegree = pixelSize * widthInPixel;
Expand All @@ -86,5 +96,15 @@ public void makeEmptyRaster() throws FactoryException {
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);
assertEquals("REAL_64BITS", gridCoverage2D.getSampleDimension(0).getSampleDimensionType().name());

gridCoverage2D = RasterConstructors.makeEmptyRaster(numBands, dataType, widthInPixel, heightInPixel, upperLeftX, upperLeftY, pixelSize, -pixelSize - 1, 0, 0, 0);
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);
assertEquals(upperLeftY, envelope.getEnvelopeInternal().getMaxY(), 0.001);
assertEquals("SIGNED_32BITS", gridCoverage2D.getSampleDimension(0).getSampleDimensionType().name());
}

}
60 changes: 56 additions & 4 deletions docs/api/sql/Raster-loader.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,19 @@ df = df.withColumn("raster", f.expr("RS_FromGeoTiff(content)"))

Introduction: Returns an empty raster geometry. Every band in the raster is initialized to `0.0`.

Since: `v1.4.1`
Since: `v1.5.0`

Format: `RS_MakeEmptyRaster(numBands:Int, width: Int, height: Int, upperleftX: Double, upperleftY: Double, cellSize:Double)`
Format: `RS_MakeEmptyRaster(numBands:Int, bandDataType:String = 'D', width: Int, height: Int, upperleftX: Double, upperleftY: Double, cellSize:Double)`

* NumBands: The number of bands in the raster. If not specified, the raster will have a single band.
* BandDataType: Optional parameter specifying the data types of all the bands in the created raster.
Accepts one of:
1. "D" - 64 bits Double
2. "F" - 32 bits Float
3. "I" - 32 bits signed Integer
4. "S" - 16 bits signed Short
5. "US" - 16 bits unsigned Short
6. "B" - 8 bits Byte
* Width: The width of the raster in pixels.
* Height: The height of the raster in pixels.
* UpperleftX: The X coordinate of the upper left corner of the raster, in terms of the CRS units.
Expand All @@ -64,9 +72,17 @@ Format: `RS_MakeEmptyRaster(numBands:Int, width: Int, height: Int, upperleftX: D

It uses the default Cartesian coordinate system.

Format: `RS_MakeEmptyRaster(numBands:Int, width: Int, height: Int, upperleftX: Double, upperleftY: Double, scaleX:Double, scaleY:Double, skewX:Double, skewY:Double, srid: Int)`
Format: `RS_MakeEmptyRaster(numBands:Int, bandDataType = 'D', width: Int, height: Int, upperleftX: Double, upperleftY: Double, scaleX:Double, scaleY:Double, skewX:Double, skewY:Double, srid: Int)`

* NumBands: The number of bands in the raster. If not specified, the raster will have a single band.
* BandDataType: Optional parameter specifying the data types of all the bands in the created raster.
Accepts one of:
1. "D" - 64 bits Double
2. "F" - 32 bits Float
3. "I" - 32 bits signed Integer
4. "S" - 16 bits signed Short
5. "US" - 16 bits unsigned Short
6. "B" - 8 bits Byte
* Width: The width of the raster in pixels.
* Height: The height of the raster in pixels.
* UpperleftX: The X coordinate of the upper left corner of the raster, in terms of the CRS units.
Expand All @@ -77,6 +93,10 @@ Format: `RS_MakeEmptyRaster(numBands:Int, width: Int, height: Int, upperleftX: D
* SkewY: The skew of the raster on the Y axis, in terms of the CRS units.
* SRID: The SRID of the raster. Use 0 if you want to use the default Cartesian coordinate system. Use 4326 if you want to use WGS84.


!!!Note
If any other value than the accepted values for the bandDataType is provided, RS_MakeEmptyRaster defaults to double as the data type for the raster.

SQL example 1 (with 2 bands):

```sql
Expand All @@ -92,7 +112,23 @@ Output:
+--------------------------------------------+
```

SQL example 1 (with 2 bands, scale, skew, and SRID):
SQL example 2 (with 2 bands and dataType):

```sql
SELECT RS_MakeEmptyRaster(2, 'I', 10, 10, 0.0, 0.0, 1.0) - Create a raster with integer datatype
```

Output:
```
+--------------------------------------------+
|rs_makeemptyraster(2, 10, 10, 0.0, 0.0, 1.0)|
+--------------------------------------------+
| GridCoverage2D["g...|
+--------------------------------------------+
```


SQL example 3 (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)
Expand All @@ -107,6 +143,22 @@ Output:
+------------------------------------------------------------------+
```


SQL example 4 (with 2 bands, scale, skew, and SRID):

```sql
SELECT RS_MakeEmptyRaster(2, 'F', 10, 10, 0.0, 0.0, 1.0, -1.0, 0.0, 0.0, 4326) - Create a raster with float datatype
```

Output:
```
+------------------------------------------------------------------+
|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

!!!warning
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ case class RS_FromGeoTiff(inputExpressions: Seq[Expression])

case class RS_MakeEmptyRaster(inputExpressions: Seq[Expression])
extends InferredExpression(
inferrableFunction6(RasterConstructors.makeEmptyRaster),
inferrableFunction10(RasterConstructors.makeEmptyRaster)) {
inferrableFunction6(RasterConstructors.makeEmptyRaster), inferrableFunction7(RasterConstructors.makeEmptyRaster),
inferrableFunction10(RasterConstructors.makeEmptyRaster), inferrableFunction11(RasterConstructors.makeEmptyRaster)) {

override def foldable: Boolean = false

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
package org.apache.sedona.sql

import org.apache.spark.sql.connector.catalog.TableChange.ColumnPosition.first
import org.apache.spark.sql.functions.{collect_list, expr}
import org.geotools.coverage.grid.GridCoverage2D
import org.junit.Assert.assertEquals
Expand Down Expand Up @@ -447,20 +448,28 @@ class rasteralgebraTest extends TestBaseScala with BeforeAndAfter with GivenWhen
val upperLeftY = 0.0
val cellSize = 1.0
val numBands = 2
// Test without skewX, skewY, srid
// Test without skewX, skewY, srid and datatype
var result = sparkSession.sql(s"SELECT RS_Metadata(RS_MakeEmptyRaster($numBands, $widthInPixel, $heightInPixel, $upperLeftX, $upperLeftY, $cellSize))").first().getSeq(0)
assertEquals(numBands, result(9), 0.001)

//Test without skewX, skewY, srid
result = sparkSession.sql(s"SELECT RS_Metadata(RS_MakeEmptyRaster($numBands, 'I', $widthInPixel, $heightInPixel, $upperLeftX, $upperLeftY, $cellSize))").first().getSeq(0)
assertEquals(numBands, result(9), 0.001)

// Test with integer type input
result = sparkSession.sql(s"SELECT RS_Metadata(RS_MakeEmptyRaster($numBands, $widthInPixel, $heightInPixel, ${upperLeftX.toInt}, ${upperLeftY.toInt}, ${cellSize.toInt}))").first().getSeq(0)
assertEquals(numBands, result(9), 0.001)

// Test with skewX, skewY, srid
// Test with skewX, skewY, srid but WITHOUT datatype
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)
assertEquals(numBands, result(9), 0.001)

//Test with skewX, skewY, srid and datatype
result = sparkSession.sql(s"SELECT RS_Metadata(RS_MakeEmptyRaster($numBands, 'I', $widthInPixel, $heightInPixel, $upperLeftX, $upperLeftY, $cellSize, -$cellSize, $skewX, $skewY, $srid))").first().getSeq(0)
assertEquals(numBands, result(9), 0.001)
}

it("Passed RS_BandAsArray") {
Expand Down
Loading