sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] 06/07: Get `SampleModel` and `ColorModel` managed by `TiledGridCoverage` base class. Add `TiledGridCoverage.selectedBands` field and clarifications in Javadoc.
Date Thu, 29 Jul 2021 13:07:21 GMT
This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git

commit 973c7c035dbdd967179e84b82fd4e7e036a1dc42
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Wed Jul 28 11:35:26 2021 +0200

    Get `SampleModel` and `ColorModel` managed by `TiledGridCoverage` base class.
    Add `TiledGridCoverage.selectedBands` field and clarifications in Javadoc.
---
 .../sis/storage/geotiff/CompressedSubset.java      |   6 +-
 .../org/apache/sis/storage/geotiff/DataCube.java   |  30 +-----
 .../org/apache/sis/storage/geotiff/DataSubset.java |  10 +-
 .../sis/storage/geotiff/ImageFileDirectory.java    |   4 +-
 .../sis/internal/storage/TiledGridCoverage.java    |  39 +++----
 .../sis/internal/storage/TiledGridResource.java    | 116 +++++++++++++++++----
 6 files changed, 131 insertions(+), 74 deletions(-)

diff --git a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/CompressedSubset.java
b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/CompressedSubset.java
index bf1b2b2..d071da6 100644
--- a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/CompressedSubset.java
+++ b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/CompressedSubset.java
@@ -91,7 +91,11 @@ final class CompressedSubset extends DataSubset {
         final int      skipY  = subsampling[1] - 1;
         final int      skipX  = numInterleaved * (subsampling[0] - 1);
         final long     head   = numInterleaved * lower[0];
-        final long     tail   = numInterleaved * (getTileSize(0) - (width*subsampling[0]
+ lower[0])) + skipX;
+        final long     tail   = numInterleaved * (getTileSize(0) - width*subsampling[0])
+ skipX - head;
+        /*
+         * `head` and `tail` are the number of sample values to skip at the beginning and
end of each row.
+         * Then `skipX` is the number of sample values to skip between each pixel.
+         */
         final Buffer[] banks  = new Buffer[numBanks];
         for (int b=0; b<numBanks; b++) {
             final Buffer   bank = RasterFactory.createBuffer(type, capacity);
diff --git a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/DataCube.java
b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/DataCube.java
index 0c2794d..8fd4870 100644
--- a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/DataCube.java
+++ b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/DataCube.java
@@ -17,7 +17,6 @@
 package org.apache.sis.storage.geotiff;
 
 import java.nio.file.Path;
-import java.awt.image.ColorModel;
 import java.awt.image.SampleModel;
 import java.awt.image.BandedSampleModel;
 import org.apache.sis.storage.DataStoreException;
@@ -76,33 +75,6 @@ abstract class DataCube extends TiledGridResource implements ResourceOnFileSyste
     }
 
     /**
-     * Returns the Java2D sample model describing pixel type and layout.
-     * The raster size is the tile size as stored in the GeoTIFF file.
-     *
-     * <h4>Multi-dimensional data cube</h4>
-     * If this resource has more than 2 dimensions, then this model is for the two first
ones (usually horizontal).
-     * The images for all levels in additional dimensions shall use the same sample model.
-     *
-     * @throws DataStoreContentException if the type is not recognized.
-     */
-    protected abstract SampleModel getSampleModel() throws DataStoreException;
-
-    /**
-     * Returns the Java2D color model for rendering images.
-     *
-     * @throws DataStoreContentException if the type is not recognized.
-     */
-    protected abstract ColorModel getColorModel() throws DataStoreException;
-
-    /**
-     * Returns the value to use for filling empty spaces in rasters,
-     * or {@code null} if none, not different than zero or not valid for the target data
type.
-     * This value is used if a tile contains less pixels than expected.
-     * The zero value is excluded because tiles are already initialized to zero by default.
-     */
-    protected abstract Number fillValue();
-
-    /**
      * Returns the total number of tiles. This is used for computing the stride between a
      * band and the next band in {@link #tileOffsets} and {@link #tileByteCounts} vectors.
      */
@@ -143,7 +115,7 @@ abstract class DataCube extends TiledGridResource implements ResourceOnFileSyste
         final GridCoverage coverage;
         try {
             synchronized (reader.store) {
-                final Subset subset = new Subset(this, domain, range);
+                final Subset subset = new Subset(domain, range, false);
                 final Compression compression = getCompression();
                 if (compression == null) {
                     throw new DataStoreContentException(reader.resources().getString(
diff --git a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/DataSubset.java
b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/DataSubset.java
index 97170c6..562af30 100644
--- a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/DataSubset.java
+++ b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/DataSubset.java
@@ -129,7 +129,7 @@ class DataSubset extends TiledGridCoverage implements Localized {
      * @throws ArithmeticException if the number of tiles overflows 32 bits integer arithmetic.
      */
     DataSubset(final DataCube source, final TiledGridResource.Subset subset) throws DataStoreException
{
-        super(subset, source.getSampleModel(), source.getColorModel(), source.fillValue());
+        super(subset);
         this.source   = source;
         this.numTiles = toIntExact(source.getNumTiles());
         final Vector[] tileArrayInfo = source.getTileArrayInfo();
@@ -290,8 +290,8 @@ class DataSubset extends TiledGridCoverage implements Localized {
             final long[] upper       = new long[BIDIMENSIONAL];   // Coordinates after the
last pixel to read relative to the tile.
             final int[]  subsampling = new int [BIDIMENSIONAL];
             final Point  origin      = new Point();
-            final long[] offsets     = new long[tileOffsets.size() / numTiles];
-            final long[] byteCounts  = new long[tileByteCounts.size() / numTiles];
+            final long[] offsets     = new long[numBanks];
+            final long[] byteCounts  = new long[numBanks];
             boolean needsCompaction  = false;
             for (int i=0; i<numMissings; i++) {
                 final Tile tile = missings[i];
@@ -303,8 +303,8 @@ class DataSubset extends TiledGridCoverage implements Localized {
                     for (int b=0; b<offsets.length; b++) {
                         offsets[b] = addExact(offsets[b], reader().origin);
                     }
-                    result[tile.indexInResultArray] = tile.cache(
-                            readSlice(offsets, byteCounts, lower, upper, subsampling, origin));
+                    WritableRaster r = readSlice(offsets, byteCounts, lower, upper, subsampling,
origin);
+                    result[tile.indexInResultArray] = tile.cache(r);
                 } else {
                     needsCompaction = true;
                 }
diff --git a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageFileDirectory.java
b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageFileDirectory.java
index a9fbe5c..cb6540b 100644
--- a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageFileDirectory.java
+++ b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageFileDirectory.java
@@ -332,7 +332,7 @@ final class ImageFileDirectory extends DataCube {
     /**
      * The "no data" or background pixel value, or NaN if undefined.
      *
-     * @see #fillValue()
+     * @see #getFillValue()
      */
     private double noData = Double.NaN;
 
@@ -1495,7 +1495,7 @@ final class ImageFileDirectory extends DataCube {
      * not different than zero or not valid for the target data type.
      */
     @Override
-    protected Number fillValue() {
+    protected Number getFillValue() {
         if (Double.isFinite(noData) && noData != 0) {
             final long min, max;
             switch (sampleFormat) {
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/TiledGridCoverage.java
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/TiledGridCoverage.java
index 3b30e63..f9abba0 100644
--- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/TiledGridCoverage.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/TiledGridCoverage.java
@@ -124,6 +124,17 @@ public abstract class TiledGridCoverage extends GridCoverage {
     private final int[] subsampling, subsamplingOffsets;
 
     /**
+     * Indices of {@link TiledGridResource} bands which have been retained for
+     * inclusion in this {@code TiledGridCoverage}, in strictly increasing order.
+     * This is {@code null} if all bands shall be included.
+     *
+     * <p>If the user specified bands out of order, the change of band order is taken
in account
+     * by the sample {@link #model}. This {@code selectedBands} array does not show any change
+     * of order for making sequential readings easier.</p>
+     */
+    protected final int[] selectedBands;
+
+    /**
      * Cache of rasters read by this {@code TiledGridCoverage}. This cache may be shared
with other coverages
      * created for the same {@link TiledGridResource} resource. For each value, the raster
{@code minX} and
      * {@code minY} values can be anything, depending which {@code TiledGridCoverage} was
first to load the tile.
@@ -136,6 +147,8 @@ public abstract class TiledGridCoverage extends GridCoverage {
     /**
      * The sample model for all rasters. The size of this sample model is the values of
      * the two first elements of {@link #tileSize} divided by subsampling after clipping.
+     * If user requested to read only a subset of the bands, this sample model is already
+     * the subset.
      */
     protected final SampleModel model;
 
@@ -152,21 +165,17 @@ public abstract class TiledGridCoverage extends GridCoverage {
     /**
      * Creates a new tiled grid coverage. All parameters should have been validated before
this call.
      *
-     * @param  subset     description of the {@link TiledGridResource} subset to cover.
-     * @param  model      description of image layout before clipping and subsampling.
-     * @param  colors     Java2D color model for images rendered from this coverage.
-     * @param  fillValue  the value to use for filling empty spaces in rasters, of {@code
null}.
+     * @param  subset  description of the {@link TiledGridResource} subset to cover.
      * @throws ArithmeticException if the number of tiles overflows 32 bits integer arithmetic.
      */
-    protected TiledGridCoverage(final TiledGridResource.Subset subset, SampleModel model,
-                                final ColorModel colors, final Number fillValue)
-    {
-        super(subset.domain,  subset.ranges);
+    protected TiledGridCoverage(final TiledGridResource.Subset subset) {
+        super(subset.domain, subset.ranges);
         final GridExtent extent = subset.domain.getExtent();
         final int dimension = subset.sourceExtent.getDimension();
         readExtent          = subset.readExtent;
         subsampling         = subset.subsampling;
         subsamplingOffsets  = subset.subsamplingOffsets;
+        selectedBands       = subset.selectedBands;
         rasters             = subset.cache;
         tileSize            = subset.tileSize;
         tmcOfFirstTile      = new long[dimension];
@@ -188,22 +197,14 @@ public abstract class TiledGridCoverage extends GridCoverage {
          * At this point, `tileStride` is the total number of tiles in source.
          * This value is not stored but its computation is still useful because
          * we want `ArithmeticException` to be thrown if the value is too high.
-         *
-         * Note: the compatible sample model will require only the memory needed for the
new size.
-         * But the subset sample model will use a `DataBuffer` that still consume all the
memory of
-         * the sample model for all bands (contrarily to the sample model for a smaller raster
size).
-         * It is okay for `PixelInterleavedSampleModel` because we will need to read all
bands anyway.
-         * It will be more complicated for the `BandedSampleModel` case.
          */
+        SampleModel model = subset.modelForBandSubset;
         if (model.getWidth() != subSize[0] || model.getHeight() != subSize[1]) {
             model = model.createCompatibleSampleModel(subSize[0], subSize[1]);
         }
-        if (subset.selectedBands != null) {
-            model = model.createSubsetSampleModel(subset.selectedBands);
-        }
         this.model     = model;
-        this.colors    = colors;
-        this.fillValue = fillValue;
+        this.colors    = subset.colorsForBandSubset;
+        this.fillValue = subset.fillValue;
     }
 
     /**
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/TiledGridResource.java
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/TiledGridResource.java
index 8a13cbd..cdd4e59 100644
--- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/TiledGridResource.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/TiledGridResource.java
@@ -18,7 +18,10 @@ package org.apache.sis.internal.storage;
 
 import java.util.List;
 import java.util.Arrays;
+import java.awt.image.ColorModel;
+import java.awt.image.SampleModel;
 import java.awt.image.WritableRaster;
+import java.awt.image.RasterFormatException;
 import org.apache.sis.coverage.SampleDimension;
 import org.apache.sis.coverage.grid.GridDerivation;
 import org.apache.sis.coverage.grid.GridExtent;
@@ -27,12 +30,13 @@ import org.apache.sis.coverage.grid.GridRoundingMode;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.event.StoreListeners;
 import org.apache.sis.util.collection.WeakValueHashMap;
+import org.apache.sis.util.ArraysExt;
 
 
 /**
  * Base class of grid coverage resource storing data in tiles.
- * Word "tile" is used for simplicity but can be understood as "chunk"
- * in a <var>n</var>-dimensional generalization.
+ * The word "tile" is used for simplicity but can be understood
+ * as "chunk" in a <var>n</var>-dimensional generalization.
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @version 1.1
@@ -66,15 +70,49 @@ public abstract class TiledGridResource extends AbstractGridResource {
      * The length of the returned array is the number of dimensions.
      *
      * @return the size of tiles in this resource.
+     * @throws DataStoreException if an error occurred while fetching tile size information.
      */
-    protected abstract int[] getTileSize();
+    protected abstract int[] getTileSize() throws DataStoreException;
+
+    /**
+     * Returns the Java2D sample model describing pixel type and layout for all bands.
+     * The raster size is the {@linkplain #getTileSize() tile size} as stored in the resource.
+     *
+     * <h4>Multi-dimensional data cube</h4>
+     * If this resource has more than 2 dimensions, then this model is for the two first
ones (usually horizontal).
+     * The images for all levels in additional dimensions shall use the same sample model.
+     *
+     * @return the sample model for tiles at full resolution with all their bands.
+     * @throws DataStoreException if an error occurred during sample model construction.
+     */
+    protected abstract SampleModel getSampleModel() throws DataStoreException;
+
+    /**
+     * Returns the Java2D color model for rendering images, or {@code null} if none.
+     * The color model shall be compatible with the sample model returned by {@link #getSampleModel()}.
+     *
+     * @return a color model compatible with {@link #getSampleModel()}, or {@code null} if
none.
+     * @throws DataStoreException if an error occurred during color model construction.
+     */
+    protected abstract ColorModel getColorModel() throws DataStoreException;
+
+    /**
+     * Returns the value to use for filling empty spaces in rasters,
+     * or {@code null} if none, not different than zero or not valid for the target data
type.
+     * This value is used if a tile contains less pixels than expected.
+     * The zero value is excluded because tiles are already initialized to zero by default.
+     *
+     * @return the value to use for filling empty spaces in rasters.
+     * @throws DataStoreException if an error occurred while fetching filling information.
+     */
+    protected abstract Number getFillValue() throws DataStoreException;
 
     /**
      * Parameters that describe the resource subset to be accepted by the {@link TiledGridCoverage}
constructor.
      * This is a temporary class used only for transferring information from {@link TiledGridResource}.
      * This class does not perform I/O operations.
      */
-    public static final class Subset {
+    public final class Subset {
         /**
          * The full size of the coverage in the enclosing {@link TiledGridResource}.
          */
@@ -100,9 +138,13 @@ public abstract class TiledGridResource extends AbstractGridResource
{
         final List<? extends SampleDimension> ranges;
 
         /**
-         * Indices of {@link TiledGridResource} bands which have been retained
-         * for inclusion in the {@link TiledGridCoverage} to construct.
+         * Indices of {@link TiledGridResource} bands which have been retained for inclusion
+         * in the {@link TiledGridCoverage} to construct, in strictly increasing order.
          * This is {@code null} if all bands shall be included.
+         *
+         * <p>If the user specified bands out of order, the change of band order is
taken in
+         * account by the {@link #modelForBandSubset}. This {@code selectedBands} array does
+         * not show any change of order for making sequential readings easier.</p>
          */
         final int[] selectedBands;
 
@@ -119,11 +161,29 @@ public abstract class TiledGridResource extends AbstractGridResource
{
         final int[] subsamplingOffsets;
 
         /**
-         * Size of tiles in each dimension.
+         * Size of tiles (or chunks) in the resource, without clipping and subsampling.
          */
         final int[] tileSize;
 
         /**
+         * The sample model for the bands to read (not the full set of bands in the resource).
+         * The width is {@code tileSize[0]} and the height it {@code tileSize[1]},
+         * i.e. subsampling is <strong>not</strong> applied.
+         */
+        final SampleModel modelForBandSubset;
+
+        /**
+         * The color model for the bands to read (not the full set of bands in the resource).
+         */
+        final ColorModel colorsForBandSubset;
+
+        /**
+         * Value to use for filling empty spaces in rasters, or {@code null} if none,
+         * not different than zero or not valid for the target data type.
+         */
+        final Number fillValue;
+
+        /**
          * Cache to use for tiles loaded by the {@link TiledGridCoverage}.
          * It is a reference to {@link TiledGridResource#rasters} if shareable.
          */
@@ -132,18 +192,25 @@ public abstract class TiledGridResource extends AbstractGridResource
{
         /**
          * Creates parameters for the given domain and range.
          *
-         * @param  caller  the resource for which a subset is created.
          * @param  domain  the domain argument specified by user in a call to {@code GridCoverageResource.read(…)}.
          * @param  range   the range argument specified by user in a call to {@code GridCoverageResource.read(…)}.
+         * @param  loadAllBands {@code true} if the reader will load all bands regardless
the {@code range} subset,
+         *         or {@code false} if only requested bands will be loaded and other bands
will be skipped. See
+         *         {@link AbstractGridResource.RangeArgument#select(SampleModel, boolean)
RangeArgument.select(…)}
+         *         for more information.
+         *
          * @throws ArithmeticException if pixel indices exceed 64 bits integer capacity.
          * @throws DataStoreException if a call to {@link TiledGridResource} method failed.
+         * @throws RasterFormatException if the sample model is not recognized.
+         * @throws IllegalArgumentException if an error occurred in an operation
+         *         such as creating the {@code SampleModel} subset for selected bands.
          */
-        public Subset(final TiledGridResource caller, GridGeometry domain, final int[] range)
throws DataStoreException {
-            List<SampleDimension> bands        = caller.getSampleDimensions();
-            final RangeArgument   rangeIndices = caller.validateRangeArgument(bands.size(),
range);
-            final GridGeometry    gridGeometry = caller.getGridGeometry();
+        public Subset(GridGeometry domain, final int[] range, final boolean loadAllBands)
throws DataStoreException {
+            List<SampleDimension> bands        = getSampleDimensions();
+            final RangeArgument   rangeIndices = validateRangeArgument(bands.size(), range);
+            final GridGeometry    gridGeometry = getGridGeometry();
             sourceExtent = gridGeometry.getExtent();
-            tileSize = caller.getTileSize();
+            tileSize = getTileSize();
             boolean sharedCache = true;
             if (domain == null) {
                 domain             = gridGeometry;
@@ -178,22 +245,35 @@ public abstract class TiledGridResource extends AbstractGridResource
{
                     }
                 }
             }
+            /*
+             * Get the bands selected by user in strictly increasing order of source band
index.
+             * If user has specified bands in a different order, that change of band order
will
+             * be handled by the `SampleModel`, not in `selectedBands` array.
+             */
             int[] selectedBands = null;
             if (!rangeIndices.isIdentity()) {
+                sharedCache = false;
                 bands = Arrays.asList(rangeIndices.select(bands));
                 selectedBands = new int[rangeIndices.getNumBands()];
                 for (int i=0; i<selectedBands.length; i++) {
-                    selectedBands[rangeIndices.getTargetIndex(i)] = rangeIndices.getSourceIndex(i);
+                    selectedBands[i] = rangeIndices.getSourceIndex(i);
+                }
+                assert ArraysExt.isSorted(selectedBands, true);
+                if (ArraysExt.isRange(0, selectedBands)) {
+                    selectedBands = null;
                 }
             }
-            this.domain        = domain;
-            this.ranges        = bands;
-            this.selectedBands = selectedBands;
+            this.domain              = domain;
+            this.ranges              = bands;
+            this.selectedBands       = selectedBands;
+            this.modelForBandSubset  = rangeIndices.select(getSampleModel(), loadAllBands);
+            this.colorsForBandSubset = rangeIndices.select(getColorModel());
+            this.fillValue           = getFillValue();
             /*
              * All `TiledGridCoverage` instances can share the same cache if they read all
tiles fully.
              * If they read only sub-regions or apply subsampling, then they will need their
own cache.
              */
-            cache = sharedCache ? caller.rasters : new WeakValueHashMap<>(Integer.class);
+            cache = sharedCache ? rasters : new WeakValueHashMap<>(Integer.class);
         }
 
         /**

Mime
View raw message