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 a2b029fb19a3272b9dc7e6a597d1d7920628c465
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Sun Jul 10 19:15:41 2022 +0200

    Prevent the replacement by source coverage in a situation where it should 
not be done.
    Report the existence of a source coverage in `toString()` output.
    Clarifications in documentation.
---
 .../sis/coverage/grid/ConvertedGridCoverage.java   | 51 ++++++++++++++------
 .../sis/coverage/grid/DerivedGridCoverage.java     | 56 ++++++++++++++++++++++
 .../coverage/grid/FractionalGridCoordinates.java   |  4 +-
 .../sis/coverage/grid/GridCoverageProcessor.java   |  7 ++-
 .../apache/sis/coverage/grid/GridEvaluator.java    |  6 +++
 .../org/apache/sis/coverage/grid/GridExtent.java   | 10 +++-
 .../sis/coverage/grid/ResampledGridCoverage.java   |  4 +-
 .../sis/coverage/grid/TranslatedGridCoverage.java  |  5 +-
 8 files changed, 119 insertions(+), 24 deletions(-)

diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ConvertedGridCoverage.java
 
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ConvertedGridCoverage.java
index 5a4726bfa5..582912140d 100644
--- 
a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ConvertedGridCoverage.java
+++ 
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ConvertedGridCoverage.java
@@ -55,7 +55,7 @@ import org.apache.sis.image.ImageProcessor;
  */
 final class ConvertedGridCoverage extends DerivedGridCoverage {
     /**
-     * Conversions from {@link #source} values to converted values.
+     * Conversions from {@linkplain #source source} values to converted values.
      * The length of this array shall be equal to the number of bands.
      */
     private final MathTransform1D[] converters;
@@ -79,6 +79,12 @@ final class ConvertedGridCoverage extends 
DerivedGridCoverage {
      */
     private final ImageProcessor processor;
 
+    /**
+     * {@code true} if the conversion was defined by user, or {@code false} if 
this instance
+     * has been created by {@link #forConvertedValues(boolean)} implementation.
+     */
+    private final boolean isUSerDefined;
+
     /**
      * Creates a new coverage with the same grid geometry than the given 
coverage but converted sample dimensions.
      *
@@ -90,13 +96,14 @@ final class ConvertedGridCoverage extends 
DerivedGridCoverage {
      */
     ConvertedGridCoverage(final GridCoverage source, final 
List<SampleDimension> range,
                           final MathTransform1D[] converters, final boolean 
isConverted,
-                          final ImageProcessor processor)
+                          final ImageProcessor processor, final boolean 
isUSerDefined)
     {
         super(source, range);
-        this.converters  = converters;
-        this.isConverted = isConverted;
-        this.bandType    = getBandType(range, isConverted, source);
-        this.processor   = processor;
+        this.converters    = converters;
+        this.isConverted   = isConverted;
+        this.bandType      = getBandType(range, isConverted, source);
+        this.processor     = processor;
+        this.isUSerDefined = isUSerDefined;
     }
 
     /**
@@ -117,7 +124,7 @@ final class ConvertedGridCoverage extends 
DerivedGridCoverage {
         if (converters == null) {
             return source;
         }
-        return new ConvertedGridCoverage(source, targets, converters, 
converted, Lazy.PROCESSOR);
+        return new ConvertedGridCoverage(source, targets, converters, 
converted, Lazy.PROCESSOR, false);
     }
 
     /**
@@ -211,35 +218,49 @@ final class ConvertedGridCoverage extends 
DerivedGridCoverage {
         return bandType;
     }
 
+    /**
+     * Returns {@code true} if this coverage should not be replaced by its 
source.
+     *
+     * @see GridCoverageProcessor.Optimization#REPLACE_SOURCE
+     */
+    @Override
+    final boolean IsNotRepleacable() {
+        return isUSerDefined;
+    }
+
     /**
      * Creates a new function for computing or interpolating sample values at 
given locations.
      *
      * <h4>Multi-threading</h4>
      * {@code GridEvaluator}s are not thread-safe. For computing sample values 
concurrently,
      * a new {@link GridEvaluator} instance should be created for each thread.
-     *
-     * @since 1.1
      */
     @Override
     public GridEvaluator evaluator() {
-        return new SampleConverter();
+        return new SampleConverter(this);
     }
 
     /**
      * Implementation of evaluator returned by {@link #evaluator()}.
      */
-    private final class SampleConverter extends GridEvaluator {
+    private static final class SampleConverter extends GridEvaluator {
         /**
          * The evaluator provided by source coverage.
          */
         private final GridEvaluator evaluator;
 
+        /**
+         * Conversions from {@linkplain #source source} values to converted 
values.
+         */
+        private final MathTransform1D[] converters;
+
         /**
          * Creates a new evaluator for the enclosing coverage.
          */
-        SampleConverter() {
-            super(ConvertedGridCoverage.this);
-            evaluator = source.evaluator();
+        SampleConverter(final ConvertedGridCoverage coverage) {
+            super(coverage);
+            evaluator  = coverage.source.evaluator();
+            converters = coverage.converters;
         }
 
         /**
@@ -313,7 +334,7 @@ final class ConvertedGridCoverage extends 
DerivedGridCoverage {
     }
 
     /**
-     * Creates a converted view over {@link #source} data for the given extent.
+     * Creates a converted view over {@linkplain #source source} data for the 
given extent.
      * Values will be converted when first requested on a tile-by-tile basis.
      * Note that if the returned image is discarded, then the cache of 
converted
      * tiles will be discarded too.
diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/DerivedGridCoverage.java
 
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/DerivedGridCoverage.java
index a6ec5b4552..67c0f3ce60 100644
--- 
a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/DerivedGridCoverage.java
+++ 
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/DerivedGridCoverage.java
@@ -17,8 +17,15 @@
 package org.apache.sis.coverage.grid;
 
 import java.util.List;
+import java.util.Locale;
+import org.opengis.geometry.DirectPosition;
 import org.apache.sis.image.DataType;
 import org.apache.sis.coverage.SampleDimension;
+import org.apache.sis.util.collection.TableColumn;
+import org.apache.sis.util.collection.TreeTable;
+import org.apache.sis.util.resources.Vocabulary;
+import org.apache.sis.util.Classes;
+import org.apache.sis.util.Debug;
 
 
 /**
@@ -64,6 +71,15 @@ abstract class DerivedGridCoverage extends GridCoverage {
         this.source = source;
     }
 
+    /**
+     * Returns {@code true} if this coverage should not be replaced by its 
source.
+     *
+     * @see GridCoverageProcessor.Optimization#REPLACE_SOURCE
+     */
+    boolean IsNotRepleacable() {
+        return false;
+    }
+
     /**
      * Returns the data type identifying the primitive type used for storing 
sample values in each band.
      * The default implementation returns the type of the source.
@@ -87,4 +103,44 @@ abstract class DerivedGridCoverage extends GridCoverage {
     public GridEvaluator evaluator() {
         return source.evaluator();
     }
+
+    /**
+     * Returns a tree representation of some elements of this grid coverage.
+     * This method create the tree documented in parent class,
+     * augmented with a short summary of the source.
+     *
+     * @param  locale   the locale to use for textual labels.
+     * @param  bitmask  combination of {@link GridGeometry} flags.
+     * @return a tree representation of the specified elements.
+     */
+    @Debug
+    public TreeTable toTree(final Locale locale, final int bitmask) {
+        final TreeTable tree = super.toTree(locale, bitmask);
+        final TreeTable.Node branch = tree.getRoot().newChild();
+        final Vocabulary vocabulary = Vocabulary.getResources(locale);
+        final TableColumn<CharSequence> column = TableColumn.VALUE_AS_TEXT;
+        branch.setValue(column, vocabulary.getString(Vocabulary.Keys.Source));
+        branch.newChild().setValue(column, summary(source));
+        return tree;
+    }
+
+    /**
+     * Returns a short (single-line) string representation of the given 
coverage.
+     * This is used for listing sources.
+     */
+    private static String summary(final GridCoverage source) {
+        final StringBuilder b = new 
StringBuilder(Classes.getShortClassName(source));
+        final GridExtent extent = source.gridGeometry.extent;
+        if (extent != null) {
+            b.append('[');
+            final int dimension = extent.getDimension();
+            for (int i=0; i<dimension; i++) {
+                if (i != 0) b.append(" × ");
+                // Do not use `extent.getSize(i)` for avoiding potential 
ArithmeticException.
+                b.append(GridExtent.toSizeString(extent.getHigh(i) - 
extent.getLow(i) + 1));
+            }
+            b.append(']');
+        }
+        return b.toString();
+    }
 }
diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/FractionalGridCoordinates.java
 
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/FractionalGridCoordinates.java
index f243986e72..d91504dd87 100644
--- 
a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/FractionalGridCoordinates.java
+++ 
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/FractionalGridCoordinates.java
@@ -160,8 +160,8 @@ public class FractionalGridCoordinates implements 
GridCoordinates, Serializable
     }
 
     /**
-     * Sets the coordinate value at the specified dimension. The given value 
can not be
-     * NaN or infinite and shall be convertible to {@code long} without 
precision lost.
+     * Sets the coordinate value at the specified dimension.
+     * The given value shall be convertible to {@code double} without 
precision lost.
      *
      * @param  dimension  the dimension for which to set the coordinate value.
      * @param  value      the new value.
diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverageProcessor.java
 
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverageProcessor.java
index 6b1b75675a..031942f67f 100644
--- 
a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverageProcessor.java
+++ 
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverageProcessor.java
@@ -338,7 +338,8 @@ public class GridCoverageProcessor implements Cloneable {
             targetBands[i] = 
sampleDimensionModifier.apply(builder.setName(band.getName())).forConvertedValues(true);
             builder.clear();
         }
-        return new ConvertedGridCoverage(source, 
UnmodifiableArrayList.wrap(targetBands), converters, true, 
unique(imageProcessor));
+        return new ConvertedGridCoverage(source, 
UnmodifiableArrayList.wrap(targetBands),
+                                         converters, true, 
unique(imageProcessor), true);
     }
 
     /**
@@ -444,7 +445,9 @@ public class GridCoverageProcessor implements Cloneable {
             if (ResampledGridCoverage.equivalent(source.getGridGeometry(), 
target)) {
                 return source;
             } else if (allowSourceReplacement && source instanceof 
DerivedGridCoverage) {
-                source = ((DerivedGridCoverage) source).source;
+                final DerivedGridCoverage derived = (DerivedGridCoverage) 
source;
+                if (derived.IsNotRepleacable()) break;
+                source = derived.source;
             } else {
                 break;
             }
diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridEvaluator.java
 
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridEvaluator.java
index 6a40742dbb..5e22a9d539 100644
--- 
a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridEvaluator.java
+++ 
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridEvaluator.java
@@ -170,6 +170,8 @@ public class GridEvaluator implements 
GridCoverage.Evaluator {
 
     /**
      * Returns the coverage from which this evaluator is fetching sample 
values.
+     * This is usually the coverage on which the {@link 
GridCoverage#evaluator()} method has been invoked,
+     * but not necessarily. Implementations are allowed to use a different 
coverage for efficiency.
      *
      * @return the source of sample values for this evaluator.
      */
@@ -416,6 +418,10 @@ public class GridEvaluator implements 
GridCoverage.Evaluator {
      * The result may be outside the {@linkplain GridGeometry#getExtent() grid 
extent}
      * if the {@linkplain GridGeometry#getGridToCRS(PixelInCell) grid to CRS} 
transform allows it.</p>
      *
+     * <p>The grid coordinates are relative to the grid of the coverage 
returned by {@link #getCoverage()}.
+     * This is usually the coverage on which the {@link 
GridCoverage#evaluator()} method has been invoked,
+     * but not necessarily. Implementations are allowed to use a different 
coverage for efficiency.</p>
+     *
      * @param  point  geospatial coordinates (in arbitrary CRS) to transform 
to grid coordinates.
      * @return the grid coordinates for the given geospatial coordinates.
      * @throws IncompleteGridGeometryException if the {@linkplain 
GridCoverage#getGridGeometry() grid geometry}
diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridExtent.java 
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
index 6b40ce9100..c875ec3ed5 100644
--- 
a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
+++ 
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
@@ -1800,8 +1800,16 @@ public class GridExtent implements GridEnvelope, 
LenientComparable, Serializable
             table.append(Long.toString(lower)).append(" … ").nextColumn();
             table.append(Long.toString(upper)).append("] ") .nextColumn();
             
table.append('(').append(vocabulary.getString(Vocabulary.Keys.CellCount_1,
-                    Long.toUnsignedString(upper - lower + 
1))).append(')').nextLine();
+                    toSizeString(upper - lower + 1))).append(')').nextLine();
         }
         table.flush();
     }
+
+    /**
+     * Returns a string representation of the given size, assumed computed by 
{@code high - low + 1}.
+     * A value of 0 means that there is an overflow and that the true value os 
2<sup>64</sup>.
+     */
+    static String toSizeString(final long size) {
+        return (size != 0) ? Long.toUnsignedString(size) : "2⁶⁴";
+    }
 }
diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ResampledGridCoverage.java
 
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ResampledGridCoverage.java
index adbf27f297..92ffd45d8a 100644
--- 
a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ResampledGridCoverage.java
+++ 
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ResampledGridCoverage.java
@@ -62,14 +62,14 @@ final class ResampledGridCoverage extends 
DerivedGridCoverage {
     private static final int BIDIMENSIONAL = 2;
 
     /**
-     * The transform from cell coordinates in this coverage to cell 
coordinates in {@linkplain #source} coverage.
+     * The transform from cell coordinates in this coverage to cell 
coordinates in {@linkplain #source source} coverage.
      * Note that an offset may exist between cell coordinates and pixel 
coordinates, so some translations may need
      * to be concatenated with this transform on an image-by-image basis.
      */
     private final MathTransform toSourceCorner, toSourceCenter;
 
     /**
-     * Mapping from dimensions in this {@code ResampledGridCoverage} to 
dimensions in the {@link #source} coverage.
+     * Mapping from dimensions in this {@code ResampledGridCoverage} to 
dimensions in the {@linkplain #source source} coverage.
      * The mapping is represented by a bitmask. For a target dimension 
<var>i</var>, {@code toSourceDimensions[i]}
      * has a bit set to 1 for all source dimensions used in the computation of 
that target dimension.
      * This array may be {@code null} if the mapping can not be computed or if 
it is not needed.
diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/TranslatedGridCoverage.java
 
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/TranslatedGridCoverage.java
index a6d2a13a09..81bb354bbe 100644
--- 
a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/TranslatedGridCoverage.java
+++ 
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/TranslatedGridCoverage.java
@@ -34,7 +34,8 @@ import org.opengis.coverage.CannotEvaluateException;
 final class TranslatedGridCoverage extends DerivedGridCoverage {
     /**
      * The translation to apply on the argument given to {@link 
#render(GridExtent)}
-     * before to delegate to the source.
+     * before to delegate to the source. This is the conversion from this 
coverage to
+     * the source coverage.
      */
     private final long[] translation;
 
@@ -109,7 +110,7 @@ final class TranslatedGridCoverage extends 
DerivedGridCoverage {
 
     /**
      * Returns a two-dimensional slice of grid data as a rendered image.
-     * This method translates the {@code sliceExtent} argument, then delegates 
to the {@linkplain #source}.
+     * This method translates the {@code sliceExtent} argument, then delegates 
to the {@linkplain #source source}.
      * It is okay to use the source result as-is because image coordinates are 
relative to the request;
      * the rendered image shall not be translated.
      */

Reply via email to