jiayuasu commented on code in PR #3312:
URL: https://github.com/apache/sedona/pull/3312#discussion_r3926861747


##########
common/src/main/java/org/apache/sedona/common/raster/RasterOutputs.java:
##########
@@ -62,6 +63,19 @@ public static byte[] asGeoTiff(
       throw new RuntimeException(e);
     }
     ParameterValueGroup defaultParams = 
writer.getFormat().getWriteParameters();
+    // Without this, GeoTiffWriter writes a default GDAL_NODATA of 0 for 
coverages that
+    // have no no-data value, so a cleared (or never set) no-data value comes 
back as 0
+    // when the written bytes are read again.
+    boolean hasNoDataValue = false;
+    for (int band = 1; band <= raster.getNumSampleDimensions(); band++) {

Review Comment:
   I may be missing a GeoTIFF constraint here, but this still looks lossy for 
mixed-band metadata. I reproduced it with a two-band GeoTIFF where both bands 
start with `-999`:
   
   - clear band 2: `(-999, null)` in memory, `(-999, -999)` after 
`RS_FromGeoTiff(RS_AsGeoTiff(...))`
   - clear band 1: `(null, -999)` in memory, `(null, null)` after the same 
round trip
   
   GeoTools writes one dataset-wide NoData value derived from band 1, so 
`hasNoDataValue` cannot encode either heterogeneous state. Would it make sense 
to reject this state or use a lossless representation, and add both cases to 
the round-trip test?



##########
common/src/main/java/org/apache/sedona/common/raster/RasterBandEditors.java:
##########
@@ -53,14 +55,22 @@ public static GridCoverage2D setBandNoDataValue(
 
     // Remove no-Data if it is null
     if (noDataValue == null) {
-      if (RasterBandAccessors.getBandNoDataValue(raster) == null) {
+      if (rasterNoData == null) {
         return raster;
       }
       GridSampleDimension[] sampleDimensions = raster.getSampleDimensions();
       sampleDimensions[bandIndex - 1] =
           RasterUtils.removeNoDataValue(sampleDimensions[bandIndex - 1]);
-      return RasterUtils.clone(
-          raster.getRenderedImage(), null, sampleDimensions, raster, null, 
true);
+      // The GC_NODATA sentinel also rides on the rendered image itself as an 
image
+      // property and survives serialization there, so cloning the image would 
let
+      // readers and writers resurrect the cleared value. Copy the pixels into 
a
+      // property-free image and drop the sentinel from the coverage 
properties too.
+      Map propertyMap = raster.getProperties();
+      if (propertyMap != null) {
+        propertyMap.remove(NoDataContainer.GC_NODATA);
+      }
+      WritableRaster pixels = raster.getRenderedImage().copyData(null);

Review Comment:
   Could we keep this on a `RenderedImage` path? I reproduced a clear on a 
valid coverage with image origin `(5, 7)` and NoData `0`. `copyData(null)` 
keeps that origin, then the `BufferedImage` constructed in `RasterUtils.clone` 
throws `IllegalArgumentException: Raster ... has minX or minY not equal to 
zero: 5 7`. The previous clone path succeeds and preserves the origin. A 
translated-raster regression test would cover it.



##########
common/src/main/java/org/apache/sedona/common/raster/RasterBandEditors.java:
##########
@@ -53,14 +55,22 @@ public static GridCoverage2D setBandNoDataValue(
 
     // Remove no-Data if it is null
     if (noDataValue == null) {
-      if (RasterBandAccessors.getBandNoDataValue(raster) == null) {
+      if (rasterNoData == null) {
         return raster;
       }
       GridSampleDimension[] sampleDimensions = raster.getSampleDimensions();
       sampleDimensions[bandIndex - 1] =
           RasterUtils.removeNoDataValue(sampleDimensions[bandIndex - 1]);
-      return RasterUtils.clone(
-          raster.getRenderedImage(), null, sampleDimensions, raster, null, 
true);
+      // The GC_NODATA sentinel also rides on the rendered image itself as an 
image
+      // property and survives serialization there, so cloning the image would 
let
+      // readers and writers resurrect the cleared value. Copy the pixels into 
a

Review Comment:
   This may also be expensive for the streaming GeoTIFF path. 
`RasterConstructors.fromGeoTiff(ImageInputStream)` returns a lazily decoded 
image, but `copyData(null)` allocates a raster for the full bounds and walks 
every tile. A 10,000 × 10,000 four-band float raster is about 1.6 GB of copied 
samples. Could the property be filtered with a delegating `RenderedImage` 
instead, so clearing metadata does not force pixel materialization?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to