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 8ba711cc91f1d09df0e6e7d25837578c82fa0c01 Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Fri Nov 24 19:00:26 2023 +0100 Add a `GridCoverageProcessor.appendDimension(…)` method. --- .../sis/coverage/grid/DimensionAppender.java | 152 +++++++++++++++++++++ .../sis/coverage/grid/GridCoverageProcessor.java | 28 +++- .../org/apache/sis/feature/internal/Resources.java | 5 + .../sis/feature/internal/Resources.properties | 1 + .../sis/feature/internal/Resources_fr.properties | 1 + 5 files changed, 186 insertions(+), 1 deletion(-) diff --git a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/DimensionAppender.java b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/DimensionAppender.java new file mode 100644 index 0000000000..4deb4f4a1c --- /dev/null +++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/DimensionAppender.java @@ -0,0 +1,152 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sis.coverage.grid; + +import java.awt.image.RenderedImage; +import org.opengis.util.FactoryException; +import org.apache.sis.image.DataType; +import org.apache.sis.util.ArraysExt; +import org.apache.sis.util.ArgumentChecks; +import org.apache.sis.util.Workaround; +import org.apache.sis.util.internal.Numerics; +import org.apache.sis.feature.internal.Resources; +import org.apache.sis.coverage.SubspaceNotSpecifiedException; + +// Specific to the geoapi-3.1 and geoapi-4.0 branches: +import org.opengis.coverage.CannotEvaluateException; + + +/** + * A grid coverage with extra dimensions appended. + * All extra dimensions have a grid size of one cell. + * + * @author Martin Desruisseaux (Geomatys) + */ +final class DimensionAppender extends GridCoverage { + /** + * The source grid coverage for which to append extra dimensions. + */ + private final GridCoverage source; + + /** + * The dimensions added to the source grid coverage. + * Should have a grid size of one cell in all dimensions. + */ + private final GridGeometry dimToAdd; + + /** + * Creates a new dimension appender for the given grid coverage. + * The grid extent of {@code dimToAdd} shall have a grid size of one cell in all dimensions. + * + * @param source the source grid coverage for which to append extra dimensions. + * @param dimToAdd the dimensions to add to the source grid coverage. + * @throws FactoryException if the compound CRS cannot be created. + * @throws IllegalGridGeometryException if a dimension has more than one grid cell. + * @throws IllegalArgumentException if the concatenation results in duplicated + * {@linkplain GridExtent#getAxisType(int) grid axis types}. + */ + private DimensionAppender(final GridCoverage source, final GridGeometry dimToAdd) throws FactoryException { + super(source, new GridGeometry(source.getGridGeometry(), dimToAdd)); + this.source = source; + this.dimToAdd = dimToAdd; + final GridExtent extent = dimToAdd.getExtent(); + for (int i = extent.getDimension(); --i >= 0;) { + final long size = extent.getSize(i); + if (size != 1) { + throw new IllegalGridGeometryException(Resources.format(Resources.Keys.NotASlice_2, + extent.getAxisIdentification(i,i), size)); + } + } + } + + /** + * Work around for RFE #4093999 in Sun's bug database + * ("Relax constraint on placement of this()/super() call in constructors"). + */ + @Workaround(library="JDK", version="1.7") + static DimensionAppender create(GridCoverage source, GridGeometry dimToAdd) throws FactoryException { + if (source instanceof DimensionAppender) { + final var a = (DimensionAppender) source; + dimToAdd = new GridGeometry(a.dimToAdd, dimToAdd); + source = a.source; + } + return new DimensionAppender(source, dimToAdd); + } + + /** + * Returns the data type identifying the primitive type used for storing sample values in each band. + */ + @Override + final DataType getBandType() { + return source.getBandType(); + } + + /** + * Creates the grid coverage instance for the converted or packed values. + * This method is invoked only when first needed, and the result is cached by the caller. + */ + @Override + protected GridCoverage createConvertedValues(final boolean converted) { + try { + return new DimensionAppender(source.forConvertedValues(converted), dimToAdd); + } catch (FactoryException e) { + throw new CannotEvaluateException(e.getMessage(), e); + } + } + + /** + * Creates a new function for computing or interpolating sample values at given locations. + * This implementation returns the evaluator of the source coverage on the assumption that + * it should be able to do dimensionality reduction of the coordinates given to it. + */ + @Override + public Evaluator evaluator() { + return source.evaluator(); + } + + /** + * Returns a two-dimensional slice of grid data as a rendered image. + * + * @param sliceExtent a subspace of this grid coverage where all dimensions except two have a size of 1 cell. + * May be {@code null} if this grid coverage has only two dimensions with a size greater than 1 cell. + * @return the grid slice as a rendered image. Image location is relative to {@code sliceExtent}. + */ + @Override + public RenderedImage render(GridExtent sliceExtent) { + if (sliceExtent != null) { + final int sourceDim = source.getGridGeometry().getDimension(); + final int dimension = dimToAdd.getDimension() + sourceDim; + ArgumentChecks.ensureDimensionMatches("sliceExtent", dimension, sliceExtent); + for (int i=sourceDim; i<dimension; i++) { + final long size = sliceExtent.getSize(i); + if (size != 1) { + throw new SubspaceNotSpecifiedException(Resources.format(Resources.Keys.NoNDimensionalSlice_3, + sourceDim, sliceExtent.getAxisIdentification(i,i), Numerics.toUnsignedDouble(size))); + } + if (dimToAdd.extent != null) { + final long actual = sliceExtent.getLow(i); + final long expected = dimToAdd.extent.getLow(i - sourceDim); + if (actual != expected) { + throw new DisjointExtentException(sliceExtent.getAxisIdentification(i,i), expected, expected, actual, actual); + } + } + } + sliceExtent = sliceExtent.selectDimensions(ArraysExt.range(0, sourceDim)); + } + return source.render(sliceExtent); + } +} diff --git a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCoverageProcessor.java b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCoverageProcessor.java index 1fe3f23034..d61f6a4b18 100644 --- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCoverageProcessor.java +++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCoverageProcessor.java @@ -591,7 +591,7 @@ public class GridCoverageProcessor implements Cloneable { throw e; } } catch (FactoryException e) { - throw new TransformException(e); + throw new TransformException(e.getMessage(), e); } return resampled.forConvertedValues(isConverted); } @@ -619,6 +619,32 @@ public class GridCoverageProcessor implements Cloneable { return resample(source, new GridGeometry(null, PixelInCell.CELL_CENTER, null, target)); } + /** + * Appends the specified grid dimensions after the dimensions of the given source coverage. + * This method is typically invoked for adding a vertical or temporal axis to a two-dimensional coverage. + * The grid extent must have a size of one cell in all the specified additional dimensions. + * + * @param source the source on which to append dimensions. + * @param dimToAdd the dimensions to append. The grid extent size must be 1 cell in all dimensions. + * @return a coverage with the specified dimensions added. + * @throws IllegalGridGeometryException if a dimension has more than one grid cell, or concatenation + * would result in duplicated {@linkplain GridExtent#getAxisType(int) grid axis types}, + * or the compound CRS cannot be created. + * + * @since 1.5 + */ + public GridCoverage appendDimensions(final GridCoverage source, final GridGeometry dimToAdd) { + ArgumentChecks.ensureNonNull("source", source); + ArgumentChecks.ensureNonNull("dimToAdd", dimToAdd); + try { + return DimensionAppender.create(source, dimToAdd); + } catch (IllegalGridGeometryException e) { + throw e; + } catch (FactoryException | IllegalArgumentException e) { + throw new IllegalGridGeometryException(e.getMessage(), e); + } + } + /** * Automatically reduces a grid coverage dimensionality by removing all grid axes with an extent size of 1. * Axes in the reduced grid coverage will be in the same order than in the source coverage. diff --git a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources.java b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources.java index 769fa14109..7d934b4d39 100644 --- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources.java +++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources.java @@ -368,6 +368,11 @@ public class Resources extends IndexedResourceBundle { */ public static final short NotASingleton_1 = 53; + /** + * Not a slice. Dimension “{0}” has {1} cells. + */ + public static final short NotASlice_2 = 90; + /** * The specified dimensions are not in strictly ascending order. */ diff --git a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources.properties b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources.properties index 6f1591f2f9..897c5809d8 100644 --- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources.properties +++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources.properties @@ -80,6 +80,7 @@ NonLinearInDimensions_1 = non-linear in {0} dimension{0,choice,1#|2#s} NonSeparableReducedDimensions = The dimensions to reduce cannot be separated. NotAGeometryAtFirstExpression = Value provided by first expression is not a geometry. NotASingleton_1 = Property \u201c{0}\u201d contains more than one value. +NotASlice_2 = Not a slice. Dimension \u201c{0}\u201d has {1} cells. NotStrictlyOrderedDimensions = The specified dimensions are not in strictly ascending order. OperationRequiresSingleBand = This operation requires an image with only one band. OptionalLibraryNotFound_2 = The {0} optional library is not available. Geometric operations will ignore that library.\nCause is {1}. diff --git a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources_fr.properties b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources_fr.properties index cdfb25398e..3d685e17ac 100644 --- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources_fr.properties +++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources_fr.properties @@ -85,6 +85,7 @@ NonLinearInDimensions_1 = non-lin\u00e9aire dans {0} dimension{0,choic NonSeparableReducedDimensions = Les dimensions \u00e0 r\u00e9duire ne peuvent pas \u00eatre s\u00e9par\u00e9es. NotAGeometryAtFirstExpression = La valeur fournie par la premi\u00e8re expression n\u2019est pas une g\u00e9om\u00e9trie. NotASingleton_1 = La propri\u00e9t\u00e9 \u00ab\u202f{0}\u202f\u00bb contient plus de une valeur. +NotASlice_2 = Ce n\u2019est pas une tranche. La dimension \u00ab\u202f{0}\u202f\u00bb a {1} cellules. NotStrictlyOrderedDimensions = Les dimensions sp\u00e9cifi\u00e9es ne sont pas en ordre strictement croissant. OperationRequiresSingleBand = Cette op\u00e9ration n\u00e9cessite une image avec une seule bande. OptionalLibraryNotFound_2 = La biblioth\u00e8que optionnelle {0} n\u2019est pas disponible. Les op\u00e9rations g\u00e9om\u00e9triques ignoreront cette biblioth\u00e8que.\nLa cause est {1}.