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. */
