You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sedona.apache.org by ji...@apache.org on 2023/09/14 00:17:12 UTC

[sedona] branch master updated: [SEDONA-394] fix RS_Band data type bug (#1018)

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 4e2761d74 [SEDONA-394] fix RS_Band data type bug (#1018)
4e2761d74 is described below

commit 4e2761d74c8018b8de10e1dd25f77b37482fc120
Author: Furqaanahmed Khan <46...@users.noreply.github.com>
AuthorDate: Wed Sep 13 20:17:06 2023 -0400

    [SEDONA-394] fix RS_Band data type bug (#1018)
---
 .../sedona/common/raster/RasterBandAccessors.java  | 30 ++++++++++++++++++----
 .../common/raster/RasterBandAccessorsTest.java     | 18 +++++++++++++
 .../sedona/common/raster/RasterOutputTest.java     | 12 +++++++++
 docs/api/sql/Raster-writer.md                      | 14 +++++++++-
 4 files changed, 68 insertions(+), 6 deletions(-)

diff --git a/common/src/main/java/org/apache/sedona/common/raster/RasterBandAccessors.java b/common/src/main/java/org/apache/sedona/common/raster/RasterBandAccessors.java
index 3da3e469e..2ffbb0315 100644
--- a/common/src/main/java/org/apache/sedona/common/raster/RasterBandAccessors.java
+++ b/common/src/main/java/org/apache/sedona/common/raster/RasterBandAccessors.java
@@ -24,6 +24,7 @@ import org.geotools.coverage.grid.GridCoordinates2D;
 import org.geotools.coverage.grid.GridCoverage2D;
 import org.opengis.referencing.FactoryException;
 
+import javax.media.jai.RasterFactory;
 import java.awt.image.Raster;
 import java.awt.image.WritableRaster;
 import java.util.Arrays;
@@ -138,6 +139,12 @@ public class RasterBandAccessors {
 //        return getSummaryStats(raster, 1, excludeNoDataValue);
 //    }
 
+    /**
+     * @param rasterGeom The raster where the bands will be extracted from.
+     * @param bandIndexes The bands to be added to new raster.
+     *
+     * @return Raster with the specified bands.
+     **/
     public static GridCoverage2D getBand(GridCoverage2D rasterGeom, int[] bandIndexes) throws FactoryException {
         Double noDataValue;
         double[] metadata = RasterAccessors.metadata(rasterGeom);
@@ -145,22 +152,35 @@ public class RasterBandAccessors {
         GridCoverage2D resultRaster = RasterConstructors.makeEmptyRaster(bandIndexes.length, width, height,
                 metadata[0], metadata[1], metadata[4], metadata[5], metadata[6], metadata[7], (int) metadata[8]);
 
+        // Get raster data type
+        Raster raster = RasterUtils.getRaster(rasterGeom.getRenderedImage());
+        int dataTypeCode = raster.getDataBuffer().getDataType();
+        boolean isDataTypeIntegral = RasterUtils.isDataTypeIntegral(dataTypeCode);
+
         // Get band data that's required
         int[] bandsDistinct = Arrays.stream(bandIndexes).distinct().toArray();
-        HashMap<Integer, double[]> bandData = new HashMap<>();
+        HashMap<Integer, Object> bandData = new HashMap<>();
         for (int curBand: bandsDistinct) {
             RasterUtils.ensureBand(rasterGeom, curBand);
-            bandData.put(curBand - 1, MapAlgebra.bandAsArray(rasterGeom, curBand));
+            if (isDataTypeIntegral) {
+                bandData.put(curBand - 1, raster.getSamples(0, 0, width, height, curBand - 1, (int[]) null));
+            } else {
+                bandData.put(curBand - 1, raster.getSamples(0, 0, width, height, curBand - 1, (double[]) null));
+            }
         }
 
-        // Get Writable Raster from the resultRaster
-        WritableRaster wr = RasterUtils.getRaster(resultRaster.getRenderedImage()).createCompatibleWritableRaster();
+        // Create Writable Raster with the datatype of given raster
+        WritableRaster wr = RasterFactory.createBandedRaster(dataTypeCode, width, height, bandIndexes.length, null);
 
         GridSampleDimension[] sampleDimensionsOg = rasterGeom.getSampleDimensions();
         GridSampleDimension[] sampleDimensionsResult = resultRaster.getSampleDimensions();
         for (int i = 0; i < bandIndexes.length; i ++) {
             sampleDimensionsResult[i] = sampleDimensionsOg[bandIndexes[i] - 1];
-            wr.setSamples(0, 0, width, height, i, bandData.get(bandIndexes[i] - 1));
+            if (isDataTypeIntegral) {
+                wr.setSamples(0, 0, width, height, i, (int[]) bandData.get(bandIndexes[i] - 1));
+            } else {
+                wr.setSamples(0, 0, width, height, i, (double[]) bandData.get(bandIndexes[i] - 1));
+            }
             noDataValue = RasterBandAccessors.getBandNoDataValue(rasterGeom, bandIndexes[i]);
             GridSampleDimension sampleDimension = sampleDimensionsResult[i];
             if (noDataValue != null) {
diff --git a/common/src/test/java/org/apache/sedona/common/raster/RasterBandAccessorsTest.java b/common/src/test/java/org/apache/sedona/common/raster/RasterBandAccessorsTest.java
index 21a22c9d0..4b1809c5a 100644
--- a/common/src/test/java/org/apache/sedona/common/raster/RasterBandAccessorsTest.java
+++ b/common/src/test/java/org/apache/sedona/common/raster/RasterBandAccessorsTest.java
@@ -266,6 +266,24 @@ public class RasterBandAccessorsTest extends RasterTestBase {
         assertArrayEquals(expectedBandValues, actualBandValues, 0.1d);
     }
 
+    @Test
+    public void testGetBandWithDataTypes() throws FactoryException, IOException {
+        GridCoverage2D emptyRaster = RasterConstructors.makeEmptyRaster( 4, "d", 5, 5, 3, -215, 2, -2, 2, 2, 0);
+        double[] values1 = new double[] {16, 0, 24, 33, 43, 49, 64, 0, 76, 77, 79, 89, 0, 116, 118, 125, 135, 0, 157, 190, 215, 229, 241, 248, 249};
+        emptyRaster = MapAlgebra.addBandFromArray(emptyRaster, values1, 1, 0d);
+        String actual = RasterBandAccessors.getBandType(emptyRaster, 1);
+        String expected = "REAL_64BITS";
+        assertEquals(expected, actual);
+
+        GridCoverage2D raster = rasterFromGeoTiff(resourceFolder + "raster_geotiff_color/FAA_UTM18N_NAD83.tif");
+        raster = RasterBandAccessors.getBand(raster, new int[] {2,1,3});
+        for (int i = 1; i <= RasterAccessors.numBands(raster); i++) {
+            actual = RasterBandAccessors.getBandType(raster, i);
+            expected = "UNSIGNED_8BITS";
+            assertEquals(expected, actual);
+        }
+    }
+
     @Test
     public void testGetBandWithRaster() throws IOException, FactoryException {
         GridCoverage2D raster = rasterFromGeoTiff(resourceFolder + "raster_geotiff_color/FAA_UTM18N_NAD83.tif");
diff --git a/common/src/test/java/org/apache/sedona/common/raster/RasterOutputTest.java b/common/src/test/java/org/apache/sedona/common/raster/RasterOutputTest.java
index 7375b9396..ab38abe67 100644
--- a/common/src/test/java/org/apache/sedona/common/raster/RasterOutputTest.java
+++ b/common/src/test/java/org/apache/sedona/common/raster/RasterOutputTest.java
@@ -52,6 +52,18 @@ public class RasterOutputTest
         assertEquals("image/png", mimeType);
     }
 
+    @Test
+    public void testAsPNGWithBand() throws IOException, FactoryException {
+        String dirPath = System.getProperty("user.dir") + "/target/testAsPNGFunction/";
+        new File(dirPath).mkdirs();
+        GridCoverage2D raster = rasterFromGeoTiff(resourceFolder + "raster_geotiff_color/FAA_UTM18N_NAD83.tif");
+        byte[] pngData = RasterOutputs.asPNG(RasterBandAccessors.getBand(raster, new int[] {3, 1, 2}));
+        RasterOutputs.writeToDiskFile(pngData, dirPath + "test2.png");
+        File f = new File(dirPath + "test2.png");
+        String mimeType = URLConnection.guessContentTypeFromName(f.getName());
+        assertEquals("image/png", mimeType);
+    }
+
     @Test
     public void testAsGeoTiff() throws IOException {
         GridCoverage2D rasterOg = rasterFromGeoTiff(resourceFolder + "raster/test1.tiff");
diff --git a/docs/api/sql/Raster-writer.md b/docs/api/sql/Raster-writer.md
index 813fd8d1f..6a92fd6f2 100644
--- a/docs/api/sql/Raster-writer.md
+++ b/docs/api/sql/Raster-writer.md
@@ -95,7 +95,7 @@ root
 
 #### RS_AsPNG
 
-Introduction: Returns a PNG byte array, that can be written to raster files as PNGs using the [sedona function](#write-a-binary-dataframe-to-raster-files). This function can only accept pixel data type of unsigned integer.
+Introduction: Returns a PNG byte array, that can be written to raster files as PNGs using the [sedona function](#write-a-binary-dataframe-to-raster-files). This function can only accept pixel data type of unsigned integer. PNG can accept 1 or 3 bands of data from the raster, refer to [RS_Band](../Raster-operators/#rs_band) for more details.
 
 !!!Note
 	Raster having `UNSIGNED_8BITS` pixel data type will have range of `0 - 255`, whereas rasters having `UNSIGNED_16BITS` pixel data type will have range of `0 - 65535`. If provided pixel value is greater than either `255` for `UNSIGNED_8BITS` or `65535` for `UNSIGNED_16BITS`, then the extra bit will be truncated.
@@ -119,6 +119,18 @@ Output:
 [-119, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73...]
 ```
 
+Spark SQL Example:
+
+```sql
+SELECT RS_AsPNG(RS_Band(raster, Array(3, 1, 2)))
+```
+
+Output:
+
+```
+[-103, 78, 94, -26, 61, -16, -91, -103, -65, -116...]
+```
+
 ### Write a binary DataFrame to raster files
 
 Introduction: You can write a Sedona binary DataFrame to external storage using Sedona's built-in `raster` data source. Note that: `raster` data source does not support reading rasters. Please use Spark built-in `binaryFile` and Sedona RS constructors together to read rasters.