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 ff45593df165f4b1d30028535a6ab90f2728a907 Author: Martin Desruisseaux <[email protected]> AuthorDate: Sat Jan 5 17:56:26 2019 +0100 First implementation of GridCoverage.render(...) taking in account the slice point. --- .../coverage/SubspaceNotSpecifiedException.java | 67 ++++++++++ .../org/apache/sis/coverage/grid/GridChange.java | 42 +++--- .../org/apache/sis/coverage/grid/GridCoverage.java | 4 +- .../org/apache/sis/coverage/grid/GridExtent.java | 99 ++++++++++++-- .../org/apache/sis/coverage/grid/GridGeometry.java | 3 +- .../apache/sis/internal/raster/RasterFactory.java | 142 ++++++++++++++++++--- .../org/apache/sis/internal/raster/Resources.java | 19 ++- .../sis/internal/raster/Resources.properties | 8 +- .../sis/internal/raster/Resources_fr.properties | 8 +- .../apache/sis/coverage/grid/GridChangeTest.java | 8 +- .../apache/sis/coverage/grid/GridExtentTest.java | 18 +++ .../org/apache/sis/internal/netcdf/Resources.java | 5 + .../sis/internal/netcdf/Resources.properties | 1 + .../sis/internal/netcdf/Resources_fr.properties | 1 + .../apache/sis/storage/netcdf/GridResource.java | 31 ++++- .../java/org/apache/sis/storage/netcdf/Image.java | 20 +-- 16 files changed, 397 insertions(+), 79 deletions(-) diff --git a/core/sis-raster/src/main/java/org/apache/sis/coverage/SubspaceNotSpecifiedException.java b/core/sis-raster/src/main/java/org/apache/sis/coverage/SubspaceNotSpecifiedException.java new file mode 100644 index 0000000..df8037e --- /dev/null +++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/SubspaceNotSpecifiedException.java @@ -0,0 +1,67 @@ +/* + * 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; + +import org.opengis.coverage.CannotEvaluateException; + + +/** + * Thrown when an operation can only be applied on a subspace of a multi-dimensional coverage, + * but not such subspace has been specified. + * For example if a {@link org.apache.sis.coverage.grid.GridCoverage} has three or more dimensions, + * then a two-dimensional slice must be specified in order to produce a {@link java.awt.image.RenderedImage} + * from that grid coverage. + * + * @author Martin Desruisseaux (Geomatys) + * @version 1.0 + * + * @see <a href="https://en.wikipedia.org/wiki/Linear_subspace">Linear subspace on Wikipedia</a> + * + * @since 1.0 + * @module + */ +public class SubspaceNotSpecifiedException extends CannotEvaluateException { + /** + * Serial number for inter-operability with different versions. + */ + private static final long serialVersionUID = -8993517725073815199L; + + /** + * Constructs an exception with no detail message. + */ + public SubspaceNotSpecifiedException() { + } + + /** + * Constructs an exception with the specified detail message. + * + * @param message the detail message. + */ + public SubspaceNotSpecifiedException(final String message) { + super(message); + } + + /** + * Constructs an exception with the specified detail message and cause. + * + * @param message the detail message. + * @param cause the cause for this exception. + */ + public SubspaceNotSpecifiedException(final String message, final Throwable cause) { + super(message, cause); + } +} diff --git a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridChange.java b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridChange.java index 70407d9..ab2bd73 100644 --- a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridChange.java +++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridChange.java @@ -57,7 +57,7 @@ import org.apache.sis.util.Debug; * public GridCoverage read(GridGeometry domain, int... range) throws DataStoreException { * GridChange change = new GridChange(domain, getGridGeometry()); * GridExtent toRead = change.getTargetExtent(); - * int[] subsampling = change.getTargetStrides()); + * int[] subsampling = change.getTargetSubsamplings()); * // Do reading here. * } * } @@ -102,7 +102,7 @@ public class GridChange implements Serializable { * grid to target grid. Those factors appear in the order of <em>target</em> grid axes. * May be {@code null} if the conversion is identity. * - * @see #getTargetStrides() + * @see #getTargetSubsamplings() */ private final double[] scales; @@ -272,7 +272,7 @@ public class GridChange implements Serializable { /** * Returns the intersection of the two grid geometry extents, in units of the target grid cells. * This is the expected ranges of grid coordinates after conversions from source to target grid, - * clipped to target grid extent and ignoring {@linkplain #getTargetStrides() strides}. + * clipped to target grid extent and ignoring {@linkplain #getTargetSubsamplings() sub-samplings}. * * @return intersection of grid geometry extents in units of target cells. */ @@ -305,7 +305,7 @@ public class GridChange implements Serializable { * * @return an <em>estimation</em> of the steps for accessing cells along each axis of target range. */ - public int[] getTargetStrides() { + public int[] getTargetSubsamplings() { final int[] subsamplings; if (scales == null) { subsamplings = new int[targetExtent.getDimension()]; @@ -320,41 +320,41 @@ public class GridChange implements Serializable { } /** - * Returns the grid geometry resulting from sub-sampling the target grid with the given strides. - * The {@code strides} argument is usually the array returned by {@link #getTargetStrides()}, but not necessarily. - * The {@linkplain GridGeometry#getExtent() extent} of the returned grid geometry will be derived from - * {@link #getTargetExtent()} as below for each dimension <var>i</var>: + * Returns the grid geometry resulting from sub-sampling the target grid with the given periods. + * The {@code periods} argument is usually the array returned by {@link #getTargetSubsamplings()}, but + * not necessarily. The {@linkplain GridGeometry#getExtent() extent} of the returned grid geometry will + * be derived from {@link #getTargetExtent()} as below for each dimension <var>i</var>: * * <ul> - * <li>The {@linkplain GridExtent#getLow(int) low} is divided by {@code strides[i]}, rounded toward zero.</li> - * <li>The {@linkplain GridExtent#getSize(int) size} is divided by {@code strides[i]}, rounded toward zero.</li> + * <li>The {@linkplain GridExtent#getLow(int) low} is divided by {@code periods[i]}, rounded toward zero.</li> + * <li>The {@linkplain GridExtent#getSize(int) size} is divided by {@code periods[i]}, rounded toward zero.</li> * <li>The {@linkplain GridExtent#getHigh(int) high} is recomputed from above low and size.</li> * </ul> * * The {@linkplain GridGeometry#getGridToCRS(PixelInCell) grid to CRS} transform is scaled accordingly * in order to map approximately to the same {@linkplain GridGeometry#getEnvelope() envelope}. * - * @param strides the sub-sampling to apply on each grid dimension. All values shall be greater than zero. + * @param periods the sub-sampling to apply on each grid dimension. All values shall be greater than zero. * If the array length is shorter than the number of dimensions, missing values are assumed to be 1. * @return a grid geometry derived from the target geometry with the given sub-sampling. * @throws TransformException if an error occurred during computation of target grid geometry. */ - public GridGeometry getTargetGeometry(final int... strides) throws TransformException { + public GridGeometry getTargetGeometry(final int... periods) throws TransformException { GridExtent extent = getTargetExtent(); double[] factors = null; Matrix toGiven = null; - if (strides != null) { - // Validity of the strides values will be verified by GridExtent.subsampling(…) invoked below. + if (periods != null) { + // Validity of the periods values will be verified by GridExtent.subsampling(…) invoked below. final GridExtent unscaled = extent; final int dimension = extent.getDimension(); - for (int i = Math.min(dimension, strides.length); --i >= 0;) { - final int s = strides[i]; + for (int i = Math.min(dimension, periods.length); --i >= 0;) { + final int s = periods[i]; if (s != 1) { if (factors == null) { - extent = extent.subsample(strides); + extent = extent.subsample(periods); factors = new double[dimension]; Arrays.fill(factors, 1); - if (!extent.startsWithZero()) { + if (!extent.startsAtZero()) { toGiven = Matrices.createIdentity(dimension + 1); } } @@ -468,18 +468,18 @@ public class GridChange implements Serializable { } /* * GridChange (example) - * └─Target strides + * └─Target subsamplings * ├─{50, 300} * └─Global ≈ 175.0 */ buffer.setLength(0); buffer.append('{'); - for (int s : getTargetStrides()) { + for (int s : getTargetSubsamplings()) { if (buffer.length() > 1) buffer.append(", "); buffer.append(s); } section = root.newChild(); - section.setValue(column, "Target strides"); + section.setValue(column, "Target subsamplings"); section.newChild().setValue(column, buffer.append('}').toString()); buffer.setLength(0); section.newChild().setValue(column, buffer.append("Global ≈ ").append((float) getGlobalScale()).toString()); return tree; diff --git a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridCoverage.java b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridCoverage.java index 78ab4b5..dea57aa 100644 --- a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridCoverage.java +++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridCoverage.java @@ -24,6 +24,7 @@ import org.opengis.geometry.DirectPosition; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.apache.sis.internal.util.UnmodifiableArrayList; import org.apache.sis.coverage.SampleDimension; +import org.apache.sis.coverage.SubspaceNotSpecifiedException; import org.apache.sis.util.collection.DefaultTreeTable; import org.apache.sis.util.collection.TableColumn; import org.apache.sis.util.collection.TreeTable; @@ -139,7 +140,8 @@ public abstract class GridCoverage { * May be {@code null} if this coverage can render only one image, for example because its CRS is two-dimensional. * @return the grid slice as a rendered image. * @throws PointOutsideCoverageException if the given slice point is illegal. - * @throws CannotEvaluateException if this method can not produce the render image for another reason. + * @throws SubspaceNotSpecifiedException if the given argument is not sufficient for reducing the grid to a two-dimensional slice. + * @throws CannotEvaluateException if this method can not produce the rendered image for another reason. */ public abstract RenderedImage render(DirectPosition slicePoint) throws CannotEvaluateException; diff --git a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java index c0b130d..3b8f41e 100644 --- a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java +++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java @@ -44,6 +44,7 @@ import org.apache.sis.internal.util.Strings; import org.apache.sis.geometry.AbstractEnvelope; import org.apache.sis.geometry.GeneralEnvelope; import org.apache.sis.geometry.Envelopes; +import org.apache.sis.coverage.SubspaceNotSpecifiedException; import org.apache.sis.referencing.operation.transform.MathTransforms; import org.apache.sis.referencing.operation.transform.TransformSeparator; import org.apache.sis.io.TableAppender; @@ -52,6 +53,7 @@ import org.apache.sis.util.iso.Types; // Branch-dependent imports import org.opengis.coverage.grid.GridEnvelope; +import org.opengis.coverage.CannotEvaluateException; import org.opengis.coverage.PointOutsideCoverageException; @@ -490,7 +492,7 @@ public class GridExtent implements Serializable { * * @return whether all low coordinates are zero. */ - public boolean startsWithZero() { + public boolean startsAtZero() { for (int i = getDimension(); --i >= 0;) { if (coordinates[i] != 0) { return false; @@ -595,6 +597,83 @@ public class GridExtent implements Serializable { } /** + * Returns indices of all dimensions where this grid extent has a size greater than 1. + * This method can be used for getting the grid extent of a <var>s</var>-dimensional slice + * in a <var>n</var>-dimensional cube where <var>s</var> ≦ <var>n</var>. + * + * <div class="note"><b>Example:</b> + * suppose that we want to get a two-dimensional slice <var>(y,z)</var> in a four-dimensional data cube <var>(x,y,z,t)</var>. + * The first step is to specify the <var>x</var> and <var>t</var> coordinates of the slice. + * In this example we set <var>x</var> to 5 and <var>t</var> to 8. + * + * {@preformat java + * GridGeometry grid = ...; // Geometry of the (x,y,z,t) grid. + * GridGeometry slice4D = grid.slice(new GeneralDirectPosition(5, NaN, NaN, 8)); + * } + * + * Above code created a slice at the requested position, but that slice still have 4 dimensions. + * It is a "slice" because the <var>x</var> and <var>t</var> dimensions of {@code slice4D} have only one cell. + * If a two-dimensional slice is desired, then above operations can be completed as below. + * In this example, the result of {@code getSubspaceDimensions(2)} call will be {1,2}. + * + * {@preformat java + * int[] subDimensions = slice4D.getExtent().getSubspaceDimensions(2); + * GridGeometry slice2D = slice4D.reduce(subDimensions); + * } + * + * Note that in this particular example, it would have been more efficient to execute {@code grid.reduce(1,2)} directly. + * This {@code getSubspaceDimensions(int)} method is more useful for inferring a {@code slice2D} from a {@code slice4D} + * which has been created elsewhere, or when we do not really want the {@code slice2D} but only its dimension indices. + * </div> + * + * This method returns exactly <var>s</var> indices. If there is more than <var>s</var> dimensions having a + * {@linkplain #getSize(int) size} greater than 1, then a {@link SubspaceNotSpecifiedException} is thrown. + * If there is less than <var>s</var> dimensions having a size greater than 1, then the returned list of + * dimensions is completed with some dimensions of size 1, starting with the first dimensions in this grid + * extent, until there is exactly <var>s</var> dimensions. This this grid extent does not have <var>s</var> + * dimensions, then a {@link CannotEvaluateException} is thrown. + * + * @param s number of dimensions of the sub-space. + * @return indices of sub-space dimensions, in increasing order in an array of length <var>s</var>. + * @throws SubspaceNotSpecifiedException if there is more than <var>s</var> dimensions having a size greater than 1. + * @throws CannotEvaluateException if this grid extent does not have at least <var>s</var> dimensions. + */ + public int[] getSubspaceDimensions(final int s) { + ArgumentChecks.ensurePositive("s", s); + final int m = getDimension(); + if (s > m) { + throw new CannotEvaluateException(Resources.format(Resources.Keys.GridEnvelopeMustBeNDimensional_1, s)); + } + final int[] selected = new int[s]; + int count = 0; + for (int i=0; i<m; i++) { + final long low = coordinates[i]; + final long high = coordinates[i+m]; + if (low != high) { + if (count < s) { + selected[count++] = i; + } else { + throw new SubspaceNotSpecifiedException(Resources.format(Resources.Keys.NoNDimensionalSlice_3, + s, getAxisIdentification(i,i), Numerics.toUnsignedDouble(high - low))); + } + } + } + final int missing = s - count; + if (missing != 0) { + System.arraycopy(selected, 0, selected, missing, count); + count = 0; + for (int i=0; ; i++) { // An IndexOutOfBoundsException would be a bug in our algorithm. + if (coordinates[i] == coordinates[i+m]) { + selected[count++] = i; + if (count == missing) break; + } + } + Arrays.sort(selected); + } + return selected; + } + + /** * Returns the type (vertical, temporal, …) of grid axis at given dimension. * This information is provided because the grid axis type can not always be inferred from the context. * Some examples are: @@ -820,8 +899,8 @@ public class GridExtent implements Serializable { /** * Creates a new grid extent sub-sampled by the given amount of cells along each grid dimensions. * This method divides {@linkplain #getLow(int) low coordinates} and {@linkplain #getSize(int) grid sizes} - * by the given strides, rounding toward zero. The {@linkplain #getHigh(int) high coordinates} are adjusted - * accordingly (this is often equivalent to dividing high coordinates by the strides too, but a difference + * by the given periods, rounding toward zero. The {@linkplain #getHigh(int) high coordinates} are adjusted + * accordingly (this is often equivalent to dividing high coordinates by the periods too, but a difference * of one cell may exist). * * <div class="note"><b>Note:</b> @@ -833,19 +912,19 @@ public class GridExtent implements Serializable { * This method does not reduce the number of dimensions of the grid extent. * For dimensionality reduction, see {@link #reduce(int...)}. * - * @param strides the strides. Length shall be equal to the number of dimension and all values shall be greater than zero. + * @param periods the sub-samplings. Length shall be equal to the number of dimension and all values shall be greater than zero. * @return the sub-sampled extent, or {@code this} is sub-sampling results in the same extent. - * @throws IllegalArgumentException if a stride is not greater than zero. + * @throws IllegalArgumentException if a period is not greater than zero. * * @see GridGeometry#subgrid(Envelope, double...) */ - public GridExtent subsample(final int... strides) { - ArgumentChecks.ensureNonNull("strides", strides); + public GridExtent subsample(final int... periods) { + ArgumentChecks.ensureNonNull("periods", periods); final int m = getDimension(); - ArgumentChecks.ensureDimensionMatches("strides", m, strides); + ArgumentChecks.ensureDimensionMatches("periods", m, periods); final GridExtent sub = new GridExtent(this); for (int i=0; i<m; i++) { - final int s = strides[i]; + final int s = periods[i]; if (s > 1) { final int j = i + m; long low = coordinates[i]; @@ -856,7 +935,7 @@ public class GridExtent implements Serializable { sub.coordinates[i] = low /= s; sub.coordinates[j] = low + r; } else if (s <= 0) { - throw new IllegalArgumentException(Errors.format(Errors.Keys.ValueNotGreaterThanZero_2, Strings.toIndexed("strides", i), s)); + throw new IllegalArgumentException(Errors.format(Errors.Keys.ValueNotGreaterThanZero_2, Strings.toIndexed("periods", i), s)); } } return Arrays.equals(coordinates, sub.coordinates) ? this : sub; diff --git a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java index c93cb85..cddfa57 100644 --- a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java +++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java @@ -1126,10 +1126,11 @@ public class GridGeometry implements Serializable { * <p>This method performs a <cite>dimensionality reduction</cite>. * This method can not be used for changing dimension order.</p> * - * @param dimensions the dimensions to select, in strictly increasing order. + * @param dimensions the grid (not CRS) dimensions to select, in strictly increasing order. * @return the sub grid geometry, or {@code this} if the given array contains all dimensions of this grid grid geometry. * @throws IndexOutOfBoundsException if an index is out of bounds. * + * @see GridExtent#getSubspaceDimensions(int) * @see GridExtent#reduce(int...) * @see org.apache.sis.referencing.CRS#reduce(CoordinateReferenceSystem, int...) */ diff --git a/core/sis-raster/src/main/java/org/apache/sis/internal/raster/RasterFactory.java b/core/sis-raster/src/main/java/org/apache/sis/internal/raster/RasterFactory.java index 002ec85..9619ca6 100644 --- a/core/sis-raster/src/main/java/org/apache/sis/internal/raster/RasterFactory.java +++ b/core/sis-raster/src/main/java/org/apache/sis/internal/raster/RasterFactory.java @@ -28,9 +28,16 @@ import java.awt.image.DataBufferFloat; import java.awt.image.DataBufferDouble; import java.awt.image.SampleModel; import java.awt.image.BandedSampleModel; +import java.awt.image.ComponentSampleModel; +import java.awt.image.PixelInterleavedSampleModel; import java.awt.image.WritableRaster; +import org.opengis.geometry.MismatchedDimensionException; +import org.apache.sis.coverage.SubspaceNotSpecifiedException; +import org.apache.sis.coverage.grid.GridExtent; import org.apache.sis.util.ArraysExt; import org.apache.sis.util.Static; +import org.apache.sis.util.Workaround; +import org.apache.sis.util.ArgumentChecks; import org.apache.sis.util.resources.Errors; @@ -50,42 +57,143 @@ public final class RasterFactory extends Static { } /** + * Wraps a sub-region of the given data buffer in a raster. + * The raster width, raster height, pixel stride and scanline stride are inferred from the grid extents. + * The sample model type is selected according the number of bands and the pixel stride. + * + * @param buffer buffer that contains the sample values. + * @param bankIndices bank indices for each band, or {@code null} for 0, 1, 2, 3…. + * @param bandOffsets number of data elements from the first element of the bank to the first sample of the band, or {@code null} for all 0. + * @param source extent of the data wrapped by the given buffer. May have any number of dimensions. + * @param target extent of the subspace to wrap in a raster. + * @param startAtZero whether to force the raster to start at (0,0) instead than the target extent low coordinates. + * @return a raster built from given properties. + * @throws ArithmeticException if a stride calculation overflows the 32 bits integer capacity. + * @throws SubspaceNotSpecifiedException if this method can not infer a two-dimensional slice from {@code target}. + */ + public static WritableRaster createRaster(final DataBuffer buffer, final int[] bankIndices, final int[] bandOffsets, + final GridExtent source, final GridExtent target, final boolean startAtZero) + { + int dimension = target.getDimension(); + if (source.getDimension() != dimension) { + throw new MismatchedDimensionException(Errors.format( + Errors.Keys.MismatchedDimension_3, "target", source.getDimension(), dimension)); + } + final int[] dimensions = target.getSubspaceDimensions(2); + int xd = dimensions[0]; + int yd = dimensions[1]; + final Point location; + if (startAtZero) { + location = null; + } else { + location = new Point(Math.toIntExact(target.getLow(xd)), + Math.toIntExact(target.getLow(yd))); + } + final int width = Math.toIntExact(target.getSize(xd)); + final int height = Math.toIntExact(target.getSize(yd)); + /* + * After this point, xd and yd should be indices relative to source extent. + * For now we keep them unchanged on the assumption that the two grid extents have the same dimensions. + */ + long pixelStride = 1; + for (int i=0; i<xd; i++) { + pixelStride = Math.multiplyExact(pixelStride, target.getSize(i)); + } + long scanlineStride = pixelStride; + for (int i=xd; i<yd; i++) { + scanlineStride = Math.multiplyExact(scanlineStride, target.getSize(i)); + } + return createRaster(buffer, width, height, + Math.toIntExact(pixelStride), Math.toIntExact(scanlineStride), bankIndices, bandOffsets, location); + } + + /** * Wraps the given data buffer in a raster. + * The sample model type is selected according the number of bands and the pixel stride. * * @param buffer buffer that contains the sample values. * @param width raster width in pixels. * @param height raster height in pixels. - * @param scanlineStride line stride of raster data. + * @param pixelStride number of data elements between two samples for the same band on the same line. + * @param scanlineStride number of data elements between a given sample and the corresponding sample in the same column of the next line. * @param bankIndices bank indices for each band, or {@code null} for 0, 1, 2, 3…. - * @param bandOffsets offsets of all bands, or {@code null} for all 0. + * @param bandOffsets number of data elements from the first element of the bank to the first sample of the band, or {@code null} for all 0. * @param location the upper-left corner of the raster, or {@code null} for (0,0). * @return a raster built from given properties. * + * @see WritableRaster#createInterleavedRaster(DataBuffer, int, int, int, int, int[], Point) * @see WritableRaster#createBandedRaster(DataBuffer, int, int, int, int[], int[], Point) */ - public static WritableRaster createBandedRaster(final DataBuffer buffer, - final int width, final int height, final int scanlineStride, + @SuppressWarnings("fallthrough") + public static WritableRaster createRaster(final DataBuffer buffer, + final int width, final int height, final int pixelStride, final int scanlineStride, int[] bankIndices, int[] bandOffsets, final Point location) { - if (bankIndices == null) { - bankIndices = ArraysExt.sequence(0, buffer.getNumBanks()); - } + ArgumentChecks.ensureStrictlyPositive("width", width); + ArgumentChecks.ensureStrictlyPositive("height", height); + ArgumentChecks.ensureStrictlyPositive("pixelStride", pixelStride); + ArgumentChecks.ensureStrictlyPositive("scanlineStride", scanlineStride); if (bandOffsets == null) { - bandOffsets = new int[bankIndices.length]; + bandOffsets = new int[buffer.getNumBanks()]; } final int dataType = buffer.getDataType(); - switch (dataType) { - case DataBuffer.TYPE_BYTE: - case DataBuffer.TYPE_USHORT: - case DataBuffer.TYPE_INT: { - // This constructor supports only above-cited types. - return WritableRaster.createBandedRaster(buffer, width, width, scanlineStride, bankIndices, bandOffsets, location); + /* + * This SampleModel variable is a workaround for WritableRaster static methods not supporting all data types. + * If 'dataType' is unsupported, then we create a SampleModel ourselves in the 'switch' statements below and + * use it for creating a WritableRaster at the end of this method. This variable, together with the 'switch' + * statements, may be removed in a future SIS version if all types become supported by the JDK. + */ + @Workaround(library = "JDK", version = "10") + final SampleModel model; + if (buffer.getNumBanks() == 1 && (bankIndices == null || bankIndices[0] == 0)) { + /* + * Sample data are stored for all bands in a single bank of the DataBuffer. + * Each sample of a pixel occupies one data element of the DataBuffer. + * The number of bands is inferred from bandOffsets.length. + */ + switch (dataType) { + case DataBuffer.TYPE_BYTE: + case DataBuffer.TYPE_USHORT: { + // 'scanlineStride' and 'pixelStride' really interchanged in that method signature. + return WritableRaster.createInterleavedRaster(buffer, width, height, scanlineStride, pixelStride, bandOffsets, location); + } + case DataBuffer.TYPE_INT: { + if (bandOffsets.length == 1) { + // From JDK javadoc: "To create a 1-band Raster of type TYPE_INT, use createPackedRaster()". + return WritableRaster.createPackedRaster(buffer, width, height, Integer.SIZE, location); + } + // else fallthrough. + } + default: { + model = new PixelInterleavedSampleModel(dataType, width, height, pixelStride, scanlineStride, bandOffsets); + break; + } + } + } else { + if (bankIndices == null) { + bankIndices = ArraysExt.sequence(0, bandOffsets.length); } - default: { - SampleModel model = new BandedSampleModel(dataType, width, height, scanlineStride, bankIndices, bandOffsets); - return WritableRaster.createWritableRaster(model, buffer, location); + if (pixelStride == 1) { + /* + * All pixels are consecutive (pixelStride = 1) but may be on many bands. + */ + switch (dataType) { + case DataBuffer.TYPE_BYTE: + case DataBuffer.TYPE_USHORT: + case DataBuffer.TYPE_INT: { + // This constructor supports only above-cited types. + return WritableRaster.createBandedRaster(buffer, width, width, scanlineStride, bankIndices, bandOffsets, location); + } + default: { + model = new BandedSampleModel(dataType, width, height, scanlineStride, bankIndices, bandOffsets); + break; + } + } + } else { + model = new ComponentSampleModel(dataType, width, height, pixelStride, scanlineStride, bankIndices, bandOffsets); } } + return WritableRaster.createWritableRaster(model, buffer, location); } /** diff --git a/core/sis-raster/src/main/java/org/apache/sis/internal/raster/Resources.java b/core/sis-raster/src/main/java/org/apache/sis/internal/raster/Resources.java index 865cd1d..2728aab 100644 --- a/core/sis-raster/src/main/java/org/apache/sis/internal/raster/Resources.java +++ b/core/sis-raster/src/main/java/org/apache/sis/internal/raster/Resources.java @@ -79,18 +79,23 @@ public final class Resources extends IndexedResourceBundle { public static final short CategoryRangeOverlap_4 = 13; /** - * Indices ({3}) are outside grid coverage. The value at dimension {0} shall be between {1} and - * {2} inclusive. + * Indices ({3}) are outside grid coverage. The value at dimension {0} shall be between + * {1,number} and {2,number} inclusive. */ public static final short GridCoordinateOutsideCoverage_4 = 21; /** + * The grid envelope must have at least {0} dimensions. + */ + public static final short GridEnvelopeMustBeNDimensional_1 = 25; + + /** * Sample value range {1} for “{0}” category is illegal. */ public static final short IllegalCategoryRange_2 = 15; /** - * Illegal grid envelope [{1} … {2}] for dimension {0}. + * Illegal grid envelope [{1,number} … {2,number}] for dimension {0}. */ public static final short IllegalGridEnvelope_3 = 8; @@ -140,6 +145,12 @@ public final class Resources extends IndexedResourceBundle { public static final short NoCategoryForValue_1 = 14; /** + * Can not infer a {0}-dimensional slice from the grid envelope. Dimension {1} has {2,number} + * cells. + */ + public static final short NoNDimensionalSlice_3 = 26; + + /** * non-linear in {0} dimension{0,choice,1#|2#s}: */ public static final short NonLinearInDimensions_1 = 20; @@ -150,7 +161,7 @@ public final class Resources extends IndexedResourceBundle { public static final short NotStrictlyOrderedDimensions = 24; /** - * The ({0}, {1}) pixel coordinate is outside iterator domain. + * The ({0,number}, {1,number}) pixel coordinate is outside iterator domain. */ public static final short OutOfIteratorDomain_2 = 1; diff --git a/core/sis-raster/src/main/java/org/apache/sis/internal/raster/Resources.properties b/core/sis-raster/src/main/java/org/apache/sis/internal/raster/Resources.properties index fd2bd6f..90419df 100644 --- a/core/sis-raster/src/main/java/org/apache/sis/internal/raster/Resources.properties +++ b/core/sis-raster/src/main/java/org/apache/sis/internal/raster/Resources.properties @@ -23,9 +23,10 @@ CanNotEnumerateValuesInRange_1 = Can not enumerate values in the {0} range. CanNotMapToGridDimensions = Some envelope dimensions can not be mapped to grid dimensions. CanNotSimplifyTransferFunction_1 = Can not simplify transfer function of sample dimension \u201c{0}\u201d. CategoryRangeOverlap_4 = The two categories \u201c{0}\u201d and \u201c{2}\u201d have overlapping ranges: {1} and {3} respectively. -GridCoordinateOutsideCoverage_4 = Indices ({3}) are outside grid coverage. The value at dimension {0} shall be between {1} and {2} inclusive. +GridCoordinateOutsideCoverage_4 = Indices ({3}) are outside grid coverage. The value at dimension {0} shall be between {1,number} and {2,number} inclusive. +GridEnvelopeMustBeNDimensional_1 = The grid envelope must have at least {0} dimensions. IllegalCategoryRange_2 = Sample value range {1} for \u201c{0}\u201d category is illegal. -IllegalGridEnvelope_3 = Illegal grid envelope [{1} \u2026 {2}] for dimension {0}. +IllegalGridEnvelope_3 = Illegal grid envelope [{1,number} \u2026 {2,number}] for dimension {0}. IllegalGridGeometryComponent_1 = Can not create a grid geometry with the given \u201c{0}\u201d component. IllegalTransferFunction_1 = Illegal transfer function for \u201c{0}\u201d category. IncompatibleTile_2 = The ({0}, {1}) tile has an unexpected size, number of bands or sample layout. @@ -35,9 +36,10 @@ MismatchedImageLocation = The two images have different size or pixel MismatchedSampleModel = The two images use different sample models. MismatchedTileGrid = The two images have different tile grid. NoCategoryForValue_1 = No category for value {0}. +NoNDimensionalSlice_3 = Can not infer a {0}-dimensional slice from the grid envelope. Dimension {1} has {2,number} cells. NonLinearInDimensions_1 = non-linear in {0} dimension{0,choice,1#|2#s}: NotStrictlyOrderedDimensions = The specified dimensions are not in strictly ascending order. -OutOfIteratorDomain_2 = The ({0}, {1}) pixel coordinate is outside iterator domain. +OutOfIteratorDomain_2 = The ({0,number}, {1,number}) pixel coordinate is outside iterator domain. PointOutsideCoverageDomain_1 = Point ({0}) is outside the coverage domain. TooManyQualitatives = Too many qualitative categories. UnspecifiedCRS = Coordinate reference system is unspecified. diff --git a/core/sis-raster/src/main/java/org/apache/sis/internal/raster/Resources_fr.properties b/core/sis-raster/src/main/java/org/apache/sis/internal/raster/Resources_fr.properties index 4dcd1d7..f2a6c2d 100644 --- a/core/sis-raster/src/main/java/org/apache/sis/internal/raster/Resources_fr.properties +++ b/core/sis-raster/src/main/java/org/apache/sis/internal/raster/Resources_fr.properties @@ -28,9 +28,10 @@ CanNotEnumerateValuesInRange_1 = Ne peut pas \u00e9num\u00e9rer les valeurs d CanNotMapToGridDimensions = Certaines dimensions de l\u2019enveloppe ne correspondent pas \u00e0 des dimensions de la grille. CanNotSimplifyTransferFunction_1 = Ne peut pas simplifier la fonction de transfert de la dimension d\u2019\u00e9chantillonnage \u00ab\u202f{0}\u202f\u00bb. CategoryRangeOverlap_4 = Les deux cat\u00e9gories \u00ab\u202f{0}\u202f\u00bb et \u00ab\u202f{2}\u202f\u00bb ont des plages de valeurs qui se chevauchent\u2008: {1} et {3} respectivement. -GridCoordinateOutsideCoverage_4 = Les indices ({3}) sont en dehors du domaine de la grille. La valeur \u00e0 la dimension {0} doit \u00eatre entre {1} et {2} inclusivement. +GridCoordinateOutsideCoverage_4 = Les indices ({3}) sont en dehors du domaine de la grille. La valeur \u00e0 la dimension {0} doit \u00eatre entre {1,number} et {2,number} inclusivement. +GridEnvelopeMustBeNDimensional_1 = L\u2019enveloppe de la grille doit avoir au moins {0} dimensions. IllegalCategoryRange_2 = La plage de valeurs {1} pour la cat\u00e9gorie \u00ab\u202f{0}\u202f\u00bb est ill\u00e9gale. -IllegalGridEnvelope_3 = La plage d\u2019index [{1} \u2026 {2}] de la dimension {0} n\u2019est pas valide. +IllegalGridEnvelope_3 = La plage d\u2019index [{1,number} \u2026 {2,number}] de la dimension {0} n\u2019est pas valide. IllegalGridGeometryComponent_1 = Ne peut pas construire une g\u00e9om\u00e9trie de grille avec la composante \u00ab\u202f{0}\u202f\u00bb donn\u00e9e. IllegalTransferFunction_1 = Fonction de transfert ill\u00e9gale pour la cat\u00e9gorie \u00ab\u202f{0}\u202f\u00bb. IncompatibleTile_2 = La tuile ({0}, {1}) a une taille, un nombre de bandes ou une disposition des valeurs inattendu. @@ -40,9 +41,10 @@ MismatchedImageLocation = Les deux images ont une taille ou des coordo MismatchedSampleModel = Les deux images disposent les pixels diff\u00e9remment. MismatchedTileGrid = Les deux images utilisent des grilles de tuiles diff\u00e9rentes. NoCategoryForValue_1 = Aucune cat\u00e9gorie n\u2019est d\u00e9finie pour la valeur {0}. +NoNDimensionalSlice_3 = Ne peut pas inf\u00e9rer une tranche \u00e0 {0} dimensions \u00e0 partir de l\u2019enveloppe de la grille. La dimension {1} a {2,number} cellules. NonLinearInDimensions_1 = non-lin\u00e9aire dans {0} dimension{0,choice,1#|2#s}\u2008: NotStrictlyOrderedDimensions = Les dimensions sp\u00e9cifi\u00e9es ne sont pas en ordre strictement croissant. -OutOfIteratorDomain_2 = La coordonn\u00e9e pixel ({0}, {1}) est en dehors du domaine de l\u2019it\u00e9rateur. +OutOfIteratorDomain_2 = La coordonn\u00e9e pixel ({0,number}, {1,number}) est en dehors du domaine de l\u2019it\u00e9rateur. PointOutsideCoverageDomain_1 = Le point ({0}) est en dehors du domaine de la couverture de donn\u00e9es. TooManyQualitatives = Trop de cat\u00e9gories qualitatives. UnspecifiedCRS = Le syst\u00e8me de r\u00e9f\u00e9rence des coordonn\u00e9es n\u2019a pas \u00e9t\u00e9 sp\u00e9cifi\u00e9. diff --git a/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridChangeTest.java b/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridChangeTest.java index 00cc475..555016a 100644 --- a/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridChangeTest.java +++ b/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridChangeTest.java @@ -66,9 +66,9 @@ public final strictfp class GridChangeTest extends TestCase { GridExtent extent = change.getTargetExtent(); assertExtentEquals(extent, 0, 2000, 5549); // Subrange of target extent. assertExtentEquals(extent, 1, -1000, 8000); - assertArrayEquals("strides", new int[] {50, 300}, change.getTargetStrides()); // s = scaleSource / scaleTarget + assertArrayEquals("subsamplings", new int[] {50, 300}, change.getTargetSubsamplings()); // s = scaleSource / scaleTarget /* - * Scale factors in following matrix shall be the same than above strides. + * Scale factors in following matrix shall be the same than above sub-samplings. * Translation appears only with PixelInCell different than the one used at construction. */ Matrix3 c = new Matrix3(); @@ -79,7 +79,7 @@ public final strictfp class GridChangeTest extends TestCase { c.m12 = 149.5; assertMatrixEquals("CELL_CENTER", c, MathTransforms.getMatrix(change.getConversion(PixelInCell.CELL_CENTER)), STRICT); /* - * If we do not ask for strides, the 'gridToCRS' transforms shall be the same than the 'target' geometry. + * If we do not ask for sub-samplings, the 'gridToCRS' transforms shall be the same than the 'target' geometry. * The envelope is the intersection of the envelopes of 'source' and 'target' geometries, documented above. */ GridGeometry tg = change.getTargetGeometry(); @@ -91,7 +91,7 @@ public final strictfp class GridChangeTest extends TestCase { expected.setRange(1, -7501, 1500); assertEnvelopeEquals(expected, tg.getEnvelope(), STRICT); /* - * If we ask for strides, then the envelope should be approximately the same or smaller. Note that without + * If we ask for sub-samplings, then the envelope should be approximately the same or smaller. Note that without * the clipping documented in GridExtent(GridExtent, int...) constructor, the envelope could be larger. */ tg = change.getTargetGeometry(50, 300); diff --git a/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridExtentTest.java b/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridExtentTest.java index c6ca296..d70678d 100644 --- a/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridExtentTest.java +++ b/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridExtentTest.java @@ -23,6 +23,7 @@ import org.opengis.coverage.PointOutsideCoverageException; import org.apache.sis.geometry.AbstractEnvelope; import org.apache.sis.geometry.GeneralEnvelope; import org.apache.sis.geometry.GeneralDirectPosition; +import org.apache.sis.coverage.SubspaceNotSpecifiedException; import org.apache.sis.referencing.crs.HardCodedCRS; import org.apache.sis.util.resources.Vocabulary; import org.apache.sis.test.TestCase; @@ -169,6 +170,23 @@ public final strictfp class GridExtentTest extends TestCase { } /** + * Tests {@link GridExtent#getSubspaceDimensions(int)}. + */ + @Test + public void testGetSubspaceDimensions() { + final GridExtent extent = new GridExtent(null, new long[] {100, 5, 200, 40}, new long[] {500, 5, 800, 40}, true); + assertArrayEquals(new int[] {0, 2 }, extent.getSubspaceDimensions(2)); + assertArrayEquals(new int[] {0,1,2 }, extent.getSubspaceDimensions(3)); + assertArrayEquals(new int[] {0,1,2,3}, extent.getSubspaceDimensions(4)); + try { + extent.getSubspaceDimensions(1); + fail("Should not reduce to 1 dimension."); + } catch (SubspaceNotSpecifiedException e) { + assertNotNull(e.getMessage()); + } + } + + /** * Tests {@link GridExtent#toString()}. * Note that the string representation may change in any future SIS version. */ diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.java index 15d2346..5ce117b 100644 --- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.java +++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.java @@ -101,6 +101,11 @@ public final class Resources extends IndexedResourceBundle { public static final short MismatchedVariableSize_3 = 8; /** + * Variables “{1}” and “{2}” in netCDF file “{0}” does not have the same type. + */ + public static final short MismatchedVariableType_3 = 13; + + /** * Reference system of type ‘{1}’ can not have {2} axes. The axes found in the “{0}” netCDF * file are: {3}. */ diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.properties b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.properties index bec3630..c2f0084 100644 --- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.properties +++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.properties @@ -27,6 +27,7 @@ CanNotUseUCAR = Can not use UCAR library for netCDF format. DimensionNotFound_3 = Dimension \u201c{2}\u201d declared by attribute \u201c{1}\u201d is not found in the \u201c{0}\u201d file. DuplicatedReference_2 = Duplicated reference to \u201c{1}\u201d in netCDF file \u201c{0}\u201d. MismatchedVariableSize_3 = The declared size of variable \u201c{1}\u201d in netCDF file \u201c{0}\u201d is {2} bytes greater than expected. +MismatchedVariableType_3 = Variables \u201c{1}\u201d and \u201c{2}\u201d in netCDF file \u201c{0}\u201d does not have the same type. UnexpectedAxisCount_4 = Reference system of type \u2018{1}\u2019 can not have {2}\u00a0axes. The axes found in the \u201c{0}\u201d netCDF file are: {3}. UnexpectedDimensionForVariable_4 = Variable \u201c{1}\u201d in file \u201c{0}\u201d has a dimension \u201c{3}\u201d while we expected \u201c{2}\u201d. UnsupportedDataType_3 = NetCDF file \u201c{0}\u201d uses unsupported data type {2} for variable \u201c{1}\u201d. diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources_fr.properties b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources_fr.properties index 6bf855a..8a8a578 100644 --- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources_fr.properties +++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources_fr.properties @@ -32,6 +32,7 @@ CanNotUseUCAR = Ne peut pas utiliser la biblioth\u00e8que de DimensionNotFound_3 = La dimension \u00ab\u202f{2}\u202f\u00bb d\u00e9clar\u00e9e par l\u2019attribut \u00ab\u202f{1}\u202f\u00bb n\u2019a pas \u00e9t\u00e9 trouv\u00e9e dans le fichier \u00ab\u202f{0}\u202f\u00bb. DuplicatedReference_2 = R\u00e9f\u00e9rence vers \u00ab\u202f{1}\u202f\u00bb dupliqu\u00e9e dans le fichier netCDF \u00ab\u202f{0}\u202f\u00bb. MismatchedVariableSize_3 = La longueur d\u00e9clar\u00e9e de la variable \u00ab\u202f{1}\u202f\u00bb dans le fichier netCDF \u00ab\u202f{0}\u202f\u00bb d\u00e9passe de {2} octets la valeur attendue. +MismatchedVariableType_3 = Les variables \u00ab\u202f{1}\u202f\u00bb et \u00ab\u202f{2}\u202f\u00bb dans le fichier netCDF \u00ab\u202f{0}\u202f\u00bb ne sont pas du m\u00eame type. UnexpectedAxisCount_4 = Les syst\u00e8mes de r\u00e9f\u00e9rence de type \u2018{1}\u2019 ne peuvent pas avoir {2}\u00a0axes. Les axes trouv\u00e9s dans le fichier netCDF \u00ab\u202f{0}\u202f\u00bb sont\u2008: {3}. UnexpectedDimensionForVariable_4 = La variable \u00ab\u202f{1}\u202f\u00bb dans le fichier \u00ab\u202f{0}\u202f\u00bb a une dimension \u00ab\u202f{3}\u202f\u00bb alors qu\u2019on attendait \u00ab\u202f{2}\u202f\u00bb. UnsupportedDataType_3 = Le fichier netCDF \u00ab\u202f{0}\u202f\u00bb utilise un type de donn\u00e9es non-support\u00e9 {2} pour la variable \u00ab\u202f{1}\u202f\u00bb. diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/GridResource.java b/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/GridResource.java index 6e94e98..1d40374 100644 --- a/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/GridResource.java +++ b/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/GridResource.java @@ -33,6 +33,7 @@ import org.apache.sis.internal.netcdf.Decoder; import org.apache.sis.internal.netcdf.Grid; import org.apache.sis.internal.netcdf.DataType; import org.apache.sis.internal.netcdf.Variable; +import org.apache.sis.internal.netcdf.Resources; import org.apache.sis.internal.storage.AbstractGridResource; import org.apache.sis.internal.storage.ResourceOnFileSystem; import org.apache.sis.coverage.SampleDimension; @@ -353,13 +354,21 @@ final class GridResource extends AbstractGridResource implements ResourceOnFileS if (domain == null) { domain = gridGeometry; } - final DataType dataType; - final DataBuffer imageBuffer; + final Variable first = data[range[0]]; + final DataType dataType = first.getDataType(); + for (int i=1; i<data.length; i++) { + final Variable variable = data[range[i]]; + if (!dataType.equals(variable.getDataType())) { + throw new DataStoreContentException(Resources.forLocale(getLocale()).getString( + Resources.Keys.MismatchedVariableType_3, getFilename(), first.getName(), variable.getName())); + } + } + final DataBuffer imageBuffer; final SampleDimension[] selected = new SampleDimension[range.length]; try { final Buffer[] samples = new Buffer[range.length]; final GridChange change = new GridChange(domain, gridGeometry); - final int[] strides = change.getTargetStrides(); + final int[] subsamplings = change.getTargetSubsamplings(); SampleDimension.Builder builder = null; /* * Iterate over netCDF variables in the order they appear in the file, not in the order requested @@ -383,16 +392,15 @@ final class GridResource extends AbstractGridResource implements ResourceOnFileS } if (values == null) { // Optional.orElseThrow() below should never fail since Variable.read(…) wraps primitive array. - values = variable.read(change.getTargetExtent(), strides).buffer().get(); + values = variable.read(change.getTargetExtent(), subsamplings).buffer().get(); } selected[j] = def; samples[j] = values; } } } - dataType = data[range[0]].getDataType(); + domain = change.getTargetGeometry(subsamplings); imageBuffer = RasterFactory.wrap(dataType.rasterDataType, samples); - domain = change.getTargetGeometry(strides); } catch (TransformException e) { throw new DataStoreReferencingException(e); } catch (IOException e) { @@ -407,6 +415,17 @@ final class GridResource extends AbstractGridResource implements ResourceOnFileS } /** + * Returns the name of the netCDF file. This is used for error messages. + */ + private String getFilename() { + if (location != null) { + return location.getFileName().toString(); + } else { + return Vocabulary.getResources(getLocale()).getString(Vocabulary.Keys.Unnamed); + } + } + + /** * Gets the paths to files used by this resource, or an empty array if unknown. */ @Override diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/Image.java b/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/Image.java index ff75602..ba322ce 100644 --- a/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/Image.java +++ b/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/Image.java @@ -23,10 +23,10 @@ import java.awt.image.BufferedImage; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import org.opengis.geometry.DirectPosition; +import org.opengis.coverage.CannotEvaluateException; import org.apache.sis.coverage.SampleDimension; import org.apache.sis.coverage.grid.GridCoverage; import org.apache.sis.coverage.grid.GridGeometry; -import org.apache.sis.coverage.grid.GridExtent; import org.apache.sis.internal.raster.RasterFactory; import org.apache.sis.internal.raster.ColorModelFactory; @@ -64,13 +64,15 @@ final class Image extends GridCoverage { */ @Override public RenderedImage render(final DirectPosition slicePoint) { - // TODO: use slicePoint. - final GridExtent extent = getGridGeometry().getExtent(); - final int width = Math.toIntExact(extent.getSize(0)); - final int height = Math.toIntExact(extent.getSize(1)); - final WritableRaster raster = RasterFactory.createBandedRaster(data, width, height, width, null, null, null); - final ColorModel colors = ColorModelFactory.createColorModel(getSampleDimensions(), VISIBLE_BAND, data.getDataType(), - ColorModelFactory.GRAYSCALE); - return new BufferedImage(colors, raster, false, null); + GridGeometry source = getGridGeometry(); + GridGeometry target = source; + if (slicePoint != null) target = target.slice(slicePoint); + try { + WritableRaster raster = RasterFactory.createRaster(data, null, null, source.getExtent(), target.getExtent(), true); + ColorModel colors = ColorModelFactory.createColorModel(getSampleDimensions(), VISIBLE_BAND, data.getDataType(), ColorModelFactory.GRAYSCALE); + return new BufferedImage(colors, raster, false, null); + } catch (ArithmeticException | IllegalArgumentException e) { + throw new CannotEvaluateException(null, e); + } } }
