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
The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
new 262e966 Move GridCoverage2D to public API and revisit method contract
/ implementation.
262e966 is described below
commit 262e96653442167e420681e5b2db0431f472aeca
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Fri Dec 20 20:23:54 2019 +0100
Move GridCoverage2D to public API and revisit method contract /
implementation.
---
.../sis/coverage/grid/DisjointExtentException.java | 17 +-
.../apache/sis/coverage/grid/GridCoverage2D.java | 517 +++++++++++++++++++++
.../org/apache/sis/coverage/grid/GridExtent.java | 61 ++-
.../grid/IllegalGridGeometryException.java | 13 +-
.../apache/sis/coverage/grid/ImageRenderer.java | 5 +-
.../org/apache/sis/coverage/grid/package-info.java | 2 +-
.../java/org/apache/sis/image/LinearIterator.java | 2 +-
.../java/org/apache/sis/image/PixelIterator.java | 28 +-
.../internal/coverage/BufferedGridCoverage.java | 26 +-
.../internal/coverage/ConvertedGridCoverage.java | 16 +-
.../sis/internal/coverage/GridCoverage2D.java | 258 ----------
.../sis/internal/coverage/ImageUtilities.java | 14 +
.../apache/sis/internal/coverage/package-info.java | 2 +-
.../org/apache/sis/internal/feature/Resources.java | 19 +-
.../sis/internal/feature/Resources.properties | 3 +
.../sis/internal/feature/Resources_fr.properties | 3 +
.../apache/sis/internal/feature/package-info.java | 2 +-
.../internal/image/TranslatedRenderedImage.java | 20 +-
.../grid}/GridCoverage2DTest.java | 20 +-
.../apache/sis/test/suite/FeatureTestSuite.java | 10 +-
20 files changed, 675 insertions(+), 363 deletions(-)
diff --git
a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/DisjointExtentException.java
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/DisjointExtentException.java
index cefd306..53719d0 100644
---
a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/DisjointExtentException.java
+++
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/DisjointExtentException.java
@@ -24,7 +24,8 @@ import org.apache.sis.internal.feature.Resources;
* does not intersect anymore the {@link GridExtent} of the {@link
GridGeometry}.
*
* @author Johann Sorel (Geomatys)
- * @version 1.0
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.1
* @since 1.0
* @module
*/
@@ -60,7 +61,7 @@ public class DisjointExtentException extends
IllegalGridGeometryException {
}
/**
- * Creates an exception with an error message built from the given
argument.
+ * Creates an exception with an error message built from the given
arguments.
* Current implementation creates the error message immediately, but we may
* change to deferred creation later if it is a performance issue.
*
@@ -73,4 +74,16 @@ public class DisjointExtentException extends
IllegalGridGeometryException {
DisjointExtentException(final Object dim, final long min, final long max,
final long lower, final long upper) {
super(Resources.format(Resources.Keys.GridEnvelopeOutsideCoverage_5,
new Object[] {dim, min, max, lower, upper}));
}
+
+ /**
+ * Creates an exception with an error message built from the given extents.
+ *
+ * @param source extent of the source.
+ * @param request extent of a slice requested by user.
+ * @param dim index of the dimension having an invalid value.
+ */
+ DisjointExtentException(final GridExtent source, final GridExtent request,
final int dim) {
+ this(source.getAxisIdentification(dim, dim), source.getLow(dim),
source.getHigh(dim),
+ request.getLow(dim), request.getHigh(dim));
+ }
}
diff --git
a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverage2D.java
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverage2D.java
new file mode 100644
index 0000000..d360e57
--- /dev/null
+++
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverage2D.java
@@ -0,0 +1,517 @@
+/*
+ * 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.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.awt.Rectangle;
+import java.awt.image.BufferedImage;
+import java.awt.image.RenderedImage;
+import java.awt.image.SampleModel;
+import org.opengis.geometry.Envelope;
+import org.opengis.metadata.spatial.DimensionNameType;
+import org.opengis.util.NameFactory;
+import org.opengis.util.InternationalString;
+import org.opengis.geometry.DirectPosition;
+import org.opengis.referencing.datum.PixelInCell;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.operation.TransformException;
+import org.opengis.coverage.CannotEvaluateException;
+import org.opengis.coverage.PointOutsideCoverageException;
+import org.apache.sis.coverage.SampleDimension;
+import org.apache.sis.internal.coverage.ImageUtilities;
+import org.apache.sis.internal.coverage.ConvertedGridCoverage;
+import org.apache.sis.internal.image.TranslatedRenderedImage;
+import org.apache.sis.internal.feature.Resources;
+import org.apache.sis.internal.system.DefaultFactories;
+import org.apache.sis.util.resources.Vocabulary;
+import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.ArraysExt;
+import org.apache.sis.util.Workaround;
+
+
+/**
+ * Basic access to grid data values backed by a two-dimensional {@link
RenderedImage}.
+ * While images are two-dimensional, the coverage <em>envelope</em> may have
more dimensions.
+ * In other words the rendered image can be a two-dimensional slice in a
<var>n</var>-dimensional space.
+ * The only restriction is that the {@linkplain GridGeometry#getExtent() grid
extent} has a
+ * {@linkplain GridExtent#getSize(int) size} equals to 1 in all dimensions
except two of them.
+ *
+ * <div class="note"><b>Example:</b>
+ * a remote sensing image may be valid only over some time range
+ * (the temporal period of the satellite passing over observed area).
+ * Envelopes for such grid coverage can have three dimensions:
+ * the two usual ones (horizontal extent along <var>x</var> and <var>y</var>),
+ * and a third dimension for start time and end time (temporal extent along
<var>t</var>).
+ * This "two-dimensional" grid coverage can have any number of columns along
<var>x</var> axis
+ * and any number of rows along <var>y</var> axis, but only one plan along
<var>t</var> axis.
+ * This single plan can have a lower bound (the start time) and an upper bound
(the end time).
+ * </div>
+ *
+ * <h2>Image size and location</h2>
+ * The {@linkplain RenderedImage#getWidth() image width} and {@linkplain
RenderedImage#getHeight() height}
+ * must be equal to the {@linkplain GridExtent#getSize(int) grid extent size}
in the two dimensions of the slice.
+ * However the image origin ({@linkplain RenderedImage#getMinX() minimal x}
and {@linkplain RenderedImage#getMinY() y}
+ * values) does not need to be equal to the {@linkplain GridExtent#getLow(int)
grid extent low values};
+ * a translation will be applied as needed.
+ *
+ * <h2>Image bands</h2>
+ * Each band in an image is represented as a {@link SampleDimension}.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @author Johann Sorel (Geomatys)
+ * @version 1.1
+ * @since 1.1
+ * @module
+ */
+public class GridCoverage2D extends GridCoverage {
+ /**
+ * Minimal number of dimension for this coverage.
+ */
+ private static final int MIN_DIMENSION = 2;
+
+ /**
+ * The sample values stored as a {@code RenderedImage}.
+ */
+ private final RenderedImage data;
+
+ /**
+ * Offsets to apply for converting grid coverage coordinates to image
pixel coordinates.
+ * This is {@link RenderedImage#getMinX()} − <code>{@linkplain
GridExtent#getLow(int)
+ * GridExtent.getLow}({@linkplain #xDimension})</code> for the
<var>x</var> offset
+ * and a similar formula for the <var>y</var> offset.
+ */
+ private final long gridToImageX, gridToImageY;
+
+ /**
+ * Indices of extent dimensions corresponding to image <var>x</var> and
<var>y</var> coordinates.
+ * Typical values are 0 for {@code xDimension} and 1 for {@code
yDimension}, but different values
+ * are allowed.
+ */
+ private final int xDimension, yDimension;
+
+ /**
+ * The two-dimensional components of the coordinate reference system and
"grid to CRS" transform.
+ * This is derived from {@link #getGridGeometry()} when first needed,
retaining only the components
+ * at dimension indices {@link #xDimension} and {@link #yDimension}.
+ *
+ * @see #getGridGeometry2D()
+ */
+ private transient GridGeometry gridGeometry2D;
+
+ /**
+ * Result of the call to {@link #forConvertedValues(boolean)} with a
boolean value opposite to
+ * {@link #isConverted}. This coverage is determined when first needed and
may be {@code this}.
+ *
+ * @see #forConvertedValues(boolean)
+ */
+ private transient GridCoverage converse;
+
+ /**
+ * Whether all sample dimensions are already representing converted values.
+ * This field has no meaning if {@link #converse} is null.
+ *
+ * @see #forConvertedValues(boolean)
+ */
+ private transient boolean isConverted;
+
+ /**
+ * Constructs a grid coverage using the specified domain, range and data.
If the given domain does not
+ * have an extent, then a default {@link GridExtent} will be computed from
given image. Otherwise the
+ * {@linkplain RenderedImage#getWidth() image width} and {@linkplain
RenderedImage#getHeight() height}
+ * must be equal to the {@linkplain GridExtent#getSize(int) grid extent
size} in the two dimensions of
+ * the slice.
+ *
+ * <p>The image origin ({@linkplain RenderedImage#getMinX() minimal x} and
{@linkplain RenderedImage#getMinY() y}
+ * values) can be anywhere; it does not need to be the same as the
{@linkplain GridExtent#getLow(int) grid extent
+ * low values}. Translations will be applied automatically when needed.</p>
+ *
+ * <p>This constructor throws an {@link IllegalGridGeometryException} if
one
+ * of the following errors is detected in the {@code domain} argument:</p>
+ * <ul>
+ * <li>The given domain has less than two dimensions.</li>
+ * <li>The given domain has more than two dimensions having an
+ * {@linkplain GridExtent#getSize(int) extent size} greater than
1.</li>
+ * <li>The extent size along <var>x</var> and <var>y</var> axes is not
equal to the image width and height.</li>
+ * </ul>
+ *
+ * @param domain the grid extent (may be absent), CRS and conversion
from cell indices.
+ * If {@code null} a default grid geometry will be created
with no CRS and identity conversion.
+ * @param range sample dimensions for each image band. The size of this
list must be equal to the number of bands.
+ * If {@code null}, default sample dimensions will be
created with no transfer function.
+ * @param data the sample values as a {@link RenderedImage}, with one
band for each sample dimension.
+ * @throws IllegalGridGeometryException if the {@code domain} does not met
the above-documented conditions.
+ * @throws IllegalArgumentException if the image number of bands is not
the same than the number of sample dimensions.
+ * @throws ArithmeticException if the distance between grid location and
image location exceeds the {@code long} capacity.
+ */
+ public GridCoverage2D(GridGeometry domain, final Collection<? extends
SampleDimension> range, final RenderedImage data) {
+ super(domain = addExtentIfAbsent(domain, data), defaultIfAbsent(range,
data));
+ this.data = data; // Non-null verified by
addExtentIfAbsent(…, data).
+ /*
+ * Find indices of the two dimensions of the slice. Those dimensions
are usually 0 for x and 1 for y,
+ * but not necessarily. A two dimensional CRS will be extracted for
those dimensions later if needed.
+ */
+ final GridExtent extent = domain.getExtent();
+ final int[] imageAxes;
+ try {
+ imageAxes = extent.getSubspaceDimensions(MIN_DIMENSION);
+ } catch (CannotEvaluateException e) {
+ throw new IllegalGridGeometryException(e.getMessage(), e);
+ }
+ xDimension = imageAxes[0];
+ yDimension = imageAxes[1];
+ gridToImageX = Math.subtractExact(data.getMinX(),
extent.getLow(xDimension));
+ gridToImageY = Math.subtractExact(data.getMinY(),
extent.getLow(yDimension));
+ /*
+ * Verifiy that the domain is consistent with image size.
+ * We do not verify image location; it can be anywhere.
+ */
+ for (int i=0; i<MIN_DIMENSION; i++) {
+ final int imageSize = (i == 0) ? data.getWidth() :
data.getHeight();
+ final long gridSize = extent.getSize(imageAxes[i]);
+ if (imageSize != gridSize) {
+ throw new
IllegalGridGeometryException(Resources.format(Resources.Keys.MismatchedImageSize_3,
i, imageSize, gridSize));
+ }
+ }
+ verifyBandCount(range, data);
+ }
+
+ /**
+ * If the given domain does not have a {@link GridExtent}, creates a new
grid geometry
+ * with an extent computed from the given image. The new grid will start
at the same
+ * location than the image and will have the same size.
+ *
+ * <p>This static method is a workaround for RFE #4093999
+ * ("Relax constraint on placement of this()/super() call in
constructors").</p>
+ */
+ @Workaround(library="JDK", version="1.8")
+ private static GridGeometry addExtentIfAbsent(GridGeometry domain, final
RenderedImage data) {
+ ArgumentChecks.ensureNonNull("data", data);
+ if (domain == null) {
+ GridExtent extent = new GridExtent(data.getMinX(), data.getMinY(),
data.getWidth(), data.getHeight());
+ domain = new GridGeometry(extent, PixelInCell.CELL_CENTER, null,
null);
+ } else if (!domain.isDefined(GridGeometry.EXTENT)) {
+ final int dimension = domain.getDimension();
+ if (dimension >= MIN_DIMENSION) {
+ CoordinateReferenceSystem crs = null;
+ if (domain.isDefined(GridGeometry.CRS)) {
+ crs = domain.getCoordinateReferenceSystem();
+ }
+ final GridExtent extent = createExtent(dimension, data, crs);
+ try {
+ domain = new GridGeometry(domain, extent, null);
+ } catch (TransformException e) {
+ throw new IllegalGridGeometryException(e);
// Should never happen.
+ }
+ }
+ }
+ return domain;
+ }
+
+ /**
+ * Constructs a grid coverage using the specified envelope, range and data.
+ * This convenience constructor computes a {@link GridGeometry} from the
given envelope and image size.
+ * This constructor assumes that all grid axes are in the same order than
CRS axes and no axis is flipped.
+ * This straightforward approach often results in the <var>y</var> axis to
be oriented toward up,
+ * not down as often expected in rendered images.
+ *
+ * <p>This constructor is generally not recommended because of the
assumptions on axis order and directions.
+ * For better control, use the constructor expecting a {@link
GridGeometry} argument instead.
+ * This constructor is provided mostly as a convenience for testing
purposes.</p>
+ *
+ * @todo Not yet public. We should provide an argument controlling whether
to flip Y axis.
+ *
+ * @param domain the envelope encompassing all images, from upper-left
corner to lower-right corner.
+ * If {@code null} a default grid geometry will be created
with no CRS and identity conversion.
+ * @param range sample dimensions for each image band. The size of this
list must be equal to the number of bands.
+ * If {@code null}, default sample dimensions will be
created with no transfer function.
+ * @param data the sample values as a {@link RenderedImage}, with one
band for each sample dimension.
+ * @throws IllegalArgumentException if the image number of bands is not
the same than the number of sample dimensions.
+ *
+ * @see GridGeometry#GridGeometry(GridExtent, Envelope)
+ */
+ GridCoverage2D(final Envelope domain, final Collection<? extends
SampleDimension> range, final RenderedImage data) {
+ super(createGridGeometry(data, domain), defaultIfAbsent(range, data));
+ this.data = data; // Non-null verified by createGridGeometry(…,
data).
+ xDimension = 0;
+ yDimension = 1;
+ gridToImageX = 0;
+ gridToImageY = 0;
+ verifyBandCount(range, data);
+ }
+
+ /**
+ * Creates a grid geometry from an envelope. The grid extent is computed
from the image size.
+ * This static method is a workaround for RFE #4093999
+ * ("Relax constraint on placement of this()/super() call in
constructors").
+ */
+ @Workaround(library="JDK", version="1.8")
+ private static GridGeometry createGridGeometry(final RenderedImage data,
final Envelope envelope) {
+ ArgumentChecks.ensureNonNull("data", data);
+ CoordinateReferenceSystem crs = null;
+ int dimension = MIN_DIMENSION;
+ if (envelope != null) {
+ dimension = envelope.getDimension();
+ if (dimension < MIN_DIMENSION) {
+ throw new IllegalGridGeometryException(Resources.format(
+ Resources.Keys.GridEnvelopeMustBeNDimensional_1,
MIN_DIMENSION));
+ }
+ crs = envelope.getCoordinateReferenceSystem();
+ }
+ return new GridGeometry(createExtent(dimension, data, crs), envelope);
+ }
+
+ /**
+ * Creates a grid extent with the low and high coordinates of the given
image.
+ * The coordinate reference system is used for extracting grid axis names,
in particular
+ * the {@link DimensionNameType#VERTICAL} and {@link
DimensionNameType#TIME} dimensions.
+ * The {@link DimensionNameType#COLUMN} and {@link DimensionNameType#ROW}
dimensions can
+ * not be inferred from CRS analysis; they are added from knowledge that
we have an image.
+ *
+ * @param dimension number of dimensions.
+ * @param data the image for which to create a grid extent.
+ * @param crs coordinate reference system, or {@code null} if none.
+ */
+ private static GridExtent createExtent(final int dimension, final
RenderedImage data, final CoordinateReferenceSystem crs) {
+ final long[] low = new long[dimension];
+ final long[] high = new long[dimension];
+ low [0] = data.getMinX();
+ low [1] = data.getMinY();
+ high[0] = data.getWidth() + low[0] - 1; // Inclusive.
+ high[1] = data.getHeight() + low[1] - 1;
+ DimensionNameType[] axisTypes = GridExtent.typeFromAxes(crs,
dimension);
+ if (axisTypes == null) {
+ axisTypes = new DimensionNameType[dimension];
+ }
+ if (!ArraysExt.contains(axisTypes, DimensionNameType.COLUMN))
axisTypes[0] = DimensionNameType.COLUMN;
+ if (!ArraysExt.contains(axisTypes, DimensionNameType.ROW))
axisTypes[1] = DimensionNameType.ROW;
+ return new GridExtent(axisTypes, low, high, true);
+ }
+
+ /**
+ * If the sample dimensions are null, creates default sample dimensions
+ * with "gray", "red, green, blue" or "cyan, magenta, yellow" names.
+ */
+ private static Collection<? extends SampleDimension> defaultIfAbsent(
+ Collection<? extends SampleDimension> range, final RenderedImage
data)
+ {
+ if (range == null) {
+ final short[] names = ImageUtilities.bandNames(data);
+ final SampleDimension[] sd = new SampleDimension[names.length];
+ final NameFactory factory =
DefaultFactories.forBuildin(NameFactory.class);
+ for (int i=0; i<names.length; i++) {
+ final InternationalString name;
+ final short k = names[0];
+ if (k != 0) {
+ name = Vocabulary.formatInternational(k);
+ } else {
+ name =
Vocabulary.formatInternational(Vocabulary.Keys.Band_1, i+1);
+ }
+ sd[i] = new SampleDimension(factory.createLocalName(null,
name), null, Collections.emptyList());
+ }
+ range = Arrays.asList(sd);
+ }
+ return range;
+ }
+
+ /**
+ * Verifies that the number of bands in the image is equal to the number
of sample dimensions.
+ * The number of bands is fetched from the sample model, which in theory
shall never be null.
+ * However this class has a little bit of tolerance to missing sample
model; it may happen
+ * when the image is used only as a matrix storage.
+ */
+ private static void verifyBandCount(final Collection<? extends
SampleDimension> range, final RenderedImage data) {
+ if (range != null) {
+ final SampleModel sm = data.getSampleModel();
+ if (sm != null) {
+ final int nb = sm.getNumBands();
+ final int ns = range.size();
+ if (nb != ns) {
+ throw new
IllegalArgumentException(Resources.format(Resources.Keys.MismatchedBandCount_2,
nb, ns));
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the two-dimensional part of this grid geometry.
+ * If the {@linkplain #getGridGeometry() complete geometry} is already
two-dimensional,
+ * then this method returns the same geometry. Otherwise it returns a
geometry for the two first
+ * axes having a {@linkplain GridExtent#getSize(int) size} greater than 1
in the grid envelope.
+ * Note that those axes are guaranteed to appear in the same order than in
the complete geometry.
+ *
+ * @return the two-dimensional part of the grid geometry.
+ *
+ * @see #getGridGeometry()
+ * @see GridGeometry#reduce(int...)
+ */
+ public synchronized GridGeometry getGridGeometry2D() {
+ if (gridGeometry2D == null) {
+ gridGeometry2D = getGridGeometry().reduce(xDimension, yDimension);
+ }
+ return gridGeometry2D;
+ }
+
+ /**
+ * Returns a grid coverage that contains real values or sample values,
+ * depending if {@code converted} is {@code true} or {@code false}
respectively.
+ *
+ * If the given value is {@code true}, then the default implementation
returns a grid coverage which produces
+ * {@link RenderedImage} views. Those views convert each sample value on
the fly. This is known to be very slow
+ * if an entire raster needs to be processed, but this is temporary until
another implementation is provided in
+ * a future SIS release.
+ *
+ * @param converted {@code true} for a coverage containing converted
values,
+ * or {@code false} for a coverage containing packed
values.
+ * @return a coverage containing converted or packed values, depending on
{@code converted} argument value.
+ */
+ @Override
+ public synchronized GridCoverage forConvertedValues(final boolean
converted) {
+ if (converse == null) {
+ isConverted = allConvertedFlagEqual(true);
+ if (isConverted) {
+ if (allConvertedFlagEqual(false)) {
+ // No conversion in any direction.
+ converse = this;
+ } else {
+ // Data are converted and user may want a packed format.
+ converse = ConvertedGridCoverage.createFromConverted(this);
+ }
+ } else {
+ // Anything that need conversion, even if "is packed" test is
also false.
+ converse = ConvertedGridCoverage.createFromPacked(this);
+ }
+ }
+ return (converted == isConverted) ? this : converse;
+ }
+
+ /**
+ * Determines whether an "is converted" or "is packed" test on all sample
dimensions returns {@code true}.
+ *
+ * @param converted {@coce true} for an "is converted" test, or {@code
false} for an "is packed" test.
+ * @return whether all sample dimensions in this coverage pass the
specified test.
+ */
+ private boolean allConvertedFlagEqual(final boolean converted) {
+ for (final SampleDimension sd : getSampleDimensions()) {
+ if (sd != sd.forConvertedValues(converted)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns a grid data region as a rendered image. The {@code sliceExtent}
argument
+ * specifies the area of interest and may be {@code null} for requesting
the whole image.
+ * The coordinates given by {@link RenderedImage#getMinX()} and {@link
RenderedImage#getMinY() getMinY()}
+ * will be the image location <em>relative to</em> the location specified
in {@code sliceExtent}
+ * {@linkplain GridExtent#getLow(int) low coordinates} (see super-class
javadoc for more discussion).
+ * The {@linkplain RenderedImage#getWidth() image width} and {@linkplain
RenderedImage#getHeight() height} will be
+ * the {@code sliceExtent} {@linkplain GridExtent#getSize(int) sizes} if
this method can honor exactly the request,
+ * but this method is free to return a smaller or larger image if doing so
reduce the amount of data to create or copy.
+ * This implementation returns a view as much as possible, without copying
sample values.
+ *
+ * @param sliceExtent area of interest, or {@code null} for the whole
image.
+ * @return the grid slice as a rendered image. Image location is relative
to {@code sliceExtent}.
+ * @throws DisjointExtentException if the given extent does not intersect
this grid coverage.
+ * @throws CannotEvaluateException if this method can not produce the
rendered image for another reason.
+ *
+ * @see BufferedImage#getSubimage(int, int, int, int)
+ */
+ @Override
+ public RenderedImage render(final GridExtent sliceExtent) throws
CannotEvaluateException {
+ if (sliceExtent == null) {
+ return data;
+ }
+ final GridExtent extent = getGridGeometry().extent;
+ if (extent != null) {
+ for (int i = Math.min(sliceExtent.getDimension(),
extent.getDimension()); --i >= 0;) {
+ if (i != xDimension && i != yDimension) {
+ if (sliceExtent.getLow(i) < extent.getLow(i) ||
sliceExtent.getHigh(i) > extent.getHigh(i)) {
+ throw new DisjointExtentException(extent, sliceExtent,
i);
+ }
+ }
+ }
+ }
+ try {
+ final Rectangle bounds = ImageUtilities.getBounds(data);
+ final long x = Math.addExact(sliceExtent.getLow(xDimension),
gridToImageX);
+ final long y = Math.addExact(sliceExtent.getLow(yDimension),
gridToImageY);
+ /*
+ * The following code clamp values to 32 bits integers without
throwing ArithmeticException
+ * because any value that overflow 32 bits are sure to be outside
the RenderedImage bounds.
+ * In such case, clamping changes nothing to the result.
+ */
+ final Rectangle request = bounds.intersection(new Rectangle(
+ (int) Math.max(Integer.MIN_VALUE,
Math.min(Integer.MAX_VALUE, x)),
+ (int) Math.max(Integer.MIN_VALUE,
Math.min(Integer.MAX_VALUE, y)),
+ (int) Math.min(Integer.MAX_VALUE,
sliceExtent.getSize(xDimension)),
+ (int) Math.min(Integer.MAX_VALUE,
sliceExtent.getSize(yDimension))));
+ /*
+ * BufferedImage.getSubimage() returns a new image with upper-left
coordinate at (0,0),
+ * which is exactly what this method contract is requesting.
+ */
+ if (data instanceof BufferedImage) {
+ final BufferedImage image = (BufferedImage) data;
+ return image.getSubimage(request.x, request.y, request.width,
request.height);
+ }
+ return new TranslatedRenderedImage(data,
+ Math.toIntExact(Math.subtractExact(bounds.x, x)),
+ Math.toIntExact(Math.subtractExact(bounds.y, y)));
+ } catch (ArithmeticException e) {
+ throw new CannotEvaluateException(e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Returns a sequence of double values for a given point in the coverage.
+ * The CRS of the given point may be any coordinate reference system,
+ * or {@code null} for the same CRS than this coverage.
+ * The returned sequence contains a value for each {@linkplain
SampleDimension sample dimension}.
+ *
+ * @param point the coordinate point where to evaluate.
+ * @param buffer an array in which to store values, or {@code null} to
create a new array.
+ * @return the {@code buffer} array, or a newly created array if {@code
buffer} was null.
+ * @throws PointOutsideCoverageException if the evaluation failed because
the input point
+ * has invalid coordinates.
+ * @throws CannotEvaluateException if the values can not be computed at
the specified coordinate
+ * for an other reason.
+ */
+ @Override
+ public double[] evaluate(final DirectPosition point, double[] buffer)
throws CannotEvaluateException {
+ try {
+ final FractionalGridCoordinates gc = toGridCoordinates(point);
+ final int x = Math.toIntExact(gc.getCoordinateValue(xDimension));
+ final int y = Math.toIntExact(gc.getCoordinateValue(yDimension));
+ final int xmin = data.getMinX();
+ final int ymin = data.getMinY();
+ if (x >= xmin && x < xmin + (long) data.getWidth() &&
+ y >= ymin && y < ymin + (long) data.getHeight())
+ {
+ final int tx = Math.floorDiv(x - data.getTileGridXOffset(),
data.getTileWidth());
+ final int ty = Math.floorDiv(y - data.getTileGridYOffset(),
data.getTileHeight());
+ return data.getTile(tx, ty).getPixel(x, y, buffer);
+ }
+ } catch (ArithmeticException | DisjointExtentException ex) {
+ throw (PointOutsideCoverageException) new
PointOutsideCoverageException(ex.getMessage(), point).initCause(ex);
+ } catch (IllegalArgumentException | TransformException ex) {
+ throw new CannotEvaluateException(ex.getMessage(), ex);
+ }
+ throw new PointOutsideCoverageException(null, point);
+ }
+}
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 51529ef..12688c5 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
@@ -241,6 +241,24 @@ public class GridExtent implements GridEnvelope,
Serializable {
}
/**
+ * Creates a new grid extent for an image of the given size and location.
This constructor
+ * is for {@link GridCoverage2D} internal usage: it does not check for
overflow (arguments
+ * are assumed small enough, which is the case when they are converted
from {@code int}s),
+ * and argument meanings differ from conventions in public constructors.
+ *
+ * @param xmin column index of the first cell.
+ * @param ymin row index of the first cell.
+ * @param width number of pixels in each row.
+ * @param height number of pixels in each column.
+ */
+ GridExtent(final long xmin, final long ymin, final long width, final long
height) {
+ this(width, height);
+ for (int i=coordinates.length; --i >= 0;) {
+ coordinates[i] += ((i & 1) == 0) ? xmin : ymin;
+ }
+ }
+
+ /**
* Constructs a new grid envelope set to the specified coordinates.
* The given arrays contain a minimum (inclusive) and maximum value for
each dimension of the grid coverage.
* The lowest valid grid coordinates are often zero, but this is not
mandatory.
@@ -295,6 +313,29 @@ public class GridExtent implements GridEnvelope,
Serializable {
}
/**
+ * Infers the axis types from the given coordinate reference system.
+ *
+ * @param crs the coordinate reference system, or {@code null}.
+ * @param dimension number of name type to infer. Shall not be greater
than the CRS dimension.
+ */
+ static DimensionNameType[] typeFromAxes(final CoordinateReferenceSystem
crs, final int dimension) {
+ DimensionNameType[] axisTypes = null;
+ if (crs != null) {
+ final CoordinateSystem cs = crs.getCoordinateSystem();
+ for (int i=0; i<dimension; i++) {
+ final DimensionNameType type =
AXIS_DIRECTIONS.get(AxisDirections.absolute(cs.getAxis(i).getDirection()));
+ if (type != null) {
+ if (axisTypes == null) {
+ axisTypes = new DimensionNameType[dimension];
+ }
+ axisTypes[i] = type;
+ }
+ }
+ }
+ return axisTypes;
+ }
+
+ /**
* Creates a new grid extent by rounding the given envelope to (usually)
nearest integers.
* The envelope coordinates shall be cell indices with lower values
inclusive and upper values exclusive.
* {@link Double#NaN} envelope coordinates will be set to the
corresponding {@code enclosing} coordinates
@@ -331,21 +372,7 @@ public class GridExtent implements GridEnvelope,
Serializable {
if (enclosing != null && enclosing.types != null) {
types = enclosing.types;
} else {
- DimensionNameType[] axisTypes = null;
- final CoordinateReferenceSystem crs =
envelope.getCoordinateReferenceSystem();
- if (crs != null) {
- final CoordinateSystem cs = crs.getCoordinateSystem();
- for (int i=0; i<dimension; i++) {
- final DimensionNameType type =
AXIS_DIRECTIONS.get(AxisDirections.absolute(cs.getAxis(i).getDirection()));
- if (type != null) {
- if (axisTypes == null) {
- axisTypes = new DimensionNameType[dimension];
- }
- axisTypes[i] = type;
- }
- }
- }
- types = validateAxisTypes(axisTypes);
+ types =
validateAxisTypes(typeFromAxes(envelope.getCoordinateReferenceSystem(),
dimension));
}
/*
* Now computes the grid extent coordinates.
@@ -693,8 +720,8 @@ public class GridExtent implements GridEnvelope,
Serializable {
* {@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.
+ * extent, until there is exactly <var>s</var> dimensions. If this grid
extent does not have at least
+ * <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>.
diff --git
a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/IllegalGridGeometryException.java
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/IllegalGridGeometryException.java
index fb0771d..c4a3ab4 100644
---
a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/IllegalGridGeometryException.java
+++
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/IllegalGridGeometryException.java
@@ -27,7 +27,7 @@ import org.apache.sis.internal.feature.Resources;
* linear (or affine) "grid to CRS" transform.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.1
* @since 1.0
* @module
*/
@@ -53,6 +53,17 @@ public class IllegalGridGeometryException extends
IllegalArgumentException {
}
/**
+ * Constructs an exception with the specified cause.
+ *
+ * @param cause the cause for this exception.
+ *
+ * @since 1.1
+ */
+ public IllegalGridGeometryException(final Throwable cause) {
+ super(cause);
+ }
+
+ /**
* Constructs an exception with the specified detail message and cause.
*
* @param message the detail message.
diff --git
a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ImageRenderer.java
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ImageRenderer.java
index f0ef651..42f0ffa 100644
---
a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ImageRenderer.java
+++
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ImageRenderer.java
@@ -79,7 +79,7 @@ import org.apache.sis.math.Vector;
* Support for tiled images will be added in a future version.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.1
*
* @see GridCoverage#render(GridExtent)
*
@@ -240,8 +240,7 @@ public class ImageRenderer {
final long ymax = Math.min(sliceExtent.getHigh(yd),
source.getHigh(yd));
if (xmax < xmin || ymax < ymin) {
// max are inclusive.
final int d = (xmax < xmin) ? xd : yd;
- throw new DisjointExtentException(source.getAxisIdentification(d,
d),
- source.getLow(d), source.getHigh(d),
sliceExtent.getLow(d), sliceExtent.getHigh(d));
+ throw new DisjointExtentException(source, sliceExtent, d);
}
width = Math.incrementExact(Math.toIntExact(xmax - xmin));
height = Math.incrementExact(Math.toIntExact(ymax - ymin));
diff --git
a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/package-info.java
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/package-info.java
index 597ff30..29fb023 100644
---
a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/package-info.java
+++
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/package-info.java
@@ -21,7 +21,7 @@
* In the three-dimensional case, the cells are voxels.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.1
* @since 1.0
* @module
*/
diff --git
a/core/sis-feature/src/main/java/org/apache/sis/image/LinearIterator.java
b/core/sis-feature/src/main/java/org/apache/sis/image/LinearIterator.java
index 7eb1688..9a7cdb7 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/image/LinearIterator.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/image/LinearIterator.java
@@ -30,7 +30,7 @@ import org.apache.sis.internal.feature.Resources;
/**
* Iterator for the {@link SequenceType#LINEAR} traversal order.
- * This iterator behaves as is the while image was a single tile.
+ * This iterator behaves as is the whole image was a single tile.
* Calls to {@link #next()} move the current position by increasing the
following values, in order:
*
* <ol>
diff --git
a/core/sis-feature/src/main/java/org/apache/sis/image/PixelIterator.java
b/core/sis-feature/src/main/java/org/apache/sis/image/PixelIterator.java
index a0be7ec..4235a5d 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/image/PixelIterator.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/image/PixelIterator.java
@@ -22,7 +22,6 @@ import java.nio.Buffer;
import java.awt.Point;
import java.awt.Dimension;
import java.awt.Rectangle;
-import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
@@ -164,31 +163,8 @@ public abstract class PixelIterator {
numBands = data.getSampleModel().getNumBands();
tileWidth = data.getTileWidth();
tileHeight = data.getTileHeight();
-
- if (data instanceof BufferedImage) {
- /**
- * BufferedImage.getSubImage produces an image which raster has a
SampleModelTranslateX
- * which concatenate the offset requested in getSubImage.
- * This means each tile raster may have a unique offset and that
- * we can't compute the real tile pixels bounds until we have read
the tile raster.
- *
- * This PixelIterator assume each tile has a deterministic
position in the image
- * which allows to fetch the exact tiles when required.
- *
- * To compensate the raster offset we cheat on the tileGridOffset
in this special case.
- * We can apply this trick only because we assume BufferedImage is
the only implementation
- * to behave this way and because a BufferedImage only has one
raster tile.
- */
- final Raster rasterTemplate = data.getTile(data.getMinTileX(),
data.getMinTileY());
- final int innerRasterTranslationX =
rasterTemplate.getSampleModelTranslateX() - data.getMinX();
- final int innerRasterTranslationY =
rasterTemplate.getSampleModelTranslateY() - data.getMinY();
- tileGridXOffset = data.getTileGridXOffset() -
innerRasterTranslationX;
- tileGridYOffset = data.getTileGridYOffset() -
innerRasterTranslationY;
- } else {
- tileGridXOffset = data.getTileGridXOffset();
- tileGridYOffset = data.getTileGridYOffset();
- }
-
+ tileGridXOffset = data.getTileGridXOffset();
+ tileGridYOffset = data.getTileGridYOffset();
bounds = intersection(data.getMinX(), data.getMinY(),
data.getWidth(), data.getHeight(), subArea, window);
lowerX = bounds.x;
lowerY = bounds.y;
diff --git
a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/BufferedGridCoverage.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/BufferedGridCoverage.java
index 0fae561..4651f7e 100644
---
a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/BufferedGridCoverage.java
+++
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/BufferedGridCoverage.java
@@ -16,6 +16,7 @@
*/
package org.apache.sis.internal.coverage;
+import java.util.Collection;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferDouble;
@@ -25,8 +26,6 @@ import java.awt.image.DataBufferShort;
import java.awt.image.DataBufferUShort;
import java.awt.image.RasterFormatException;
import java.awt.image.RenderedImage;
-import java.awt.image.SampleModel;
-import java.util.Collection;
import org.apache.sis.coverage.SampleDimension;
import org.apache.sis.coverage.grid.GridCoverage;
import org.apache.sis.coverage.grid.GridExtent;
@@ -44,7 +43,7 @@ import org.opengis.coverage.CannotEvaluateException;
* Those data can be shown as {@link RenderedImage}.
*
* @author Johann Sorel (Geomatys)
- * @version 1.0
+ * @version 1.1
* @since 1.0
* @module
*/
@@ -125,7 +124,7 @@ public class BufferedGridCoverage extends GridCoverage {
* Returns a grid coverage that contains real values or sample values,
depending if {@code converted} is {@code true}
* or {@code false} respectively.
*
- * If the given value is {@code false}, then the default implementation
returns a grid coverage which produces
+ * If the given value is {@code true}, then the default implementation
returns a grid coverage which produces
* {@link RenderedImage} views. Those views convert each sample value on
the fly. This is known to be very slow
* if an entire raster needs to be processed, but this is temporary until
another implementation is provided in
* a future SIS release.
@@ -137,28 +136,11 @@ public class BufferedGridCoverage extends GridCoverage {
if (converted) {
synchronized (this) {
if (this.converted == null) {
- this.converted = convert(this);
+ this.converted =
ConvertedGridCoverage.createFromPacked(this);
}
return this.converted;
}
}
return this;
}
-
- /**
- * Returns a coverage for converted values. If the given coverage is
already converted,
- * then this method returns the given {@code coverage} unchanged.
- *
- * <p><b>WARNING: this is a temporary implementation.</b>
- * This method uses a special {@link SampleModel} in departure with the
contract documented in JDK javadoc.
- * That sample model does not only define the sample layout (pixel stride,
scanline stride, <i>etc.</i>), but
- * also converts the sample values. This may be an issue for optimized
pipelines accessing {@link DataBuffer}
- * directly. This method may be replaced by another mechanism (creating
new tiles) in a future SIS version.</p>
- *
- * @param packed the coverage containing packed values to convert.
- * @return the converted coverage. May be {@code coverage}.
- */
- public static GridCoverage convert(final GridCoverage packed) {
- return ConvertedGridCoverage.create(packed);
- }
}
diff --git
a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/ConvertedGridCoverage.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/ConvertedGridCoverage.java
index 5fed1e8..d3c8ec8 100644
---
a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/ConvertedGridCoverage.java
+++
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/ConvertedGridCoverage.java
@@ -48,19 +48,20 @@ import org.opengis.referencing.operation.TransformException;
* directly. This class may be replaced by another mechanism (creating new
tiles) in a future SIS version.</p>
*
* @author Johann Sorel (Geomatys)
- * @version 1.0
+ * @version 1.1
* @since 1.0
* @module
*/
-final class ConvertedGridCoverage extends GridCoverage {
+public final class ConvertedGridCoverage extends GridCoverage {
/**
- * Returns a coverage for converted values. If the given coverage is
already converted,
+ * Returns a coverage for converted values computed from a coverage of
package values.
+ * If the given coverage is already converted,
* then this method returns the given {@code coverage} unchanged.
*
* @param packed the coverage containing packed values to convert.
* @return the converted coverage. May be {@code coverage}.
*/
- public static GridCoverage create(final GridCoverage packed) {
+ public static GridCoverage createFromPacked(final GridCoverage packed) {
final List<SampleDimension> sds = packed.getSampleDimensions();
final List<SampleDimension> cfs = new ArrayList<>(sds.size());
for (SampleDimension sd : sds) {
@@ -70,6 +71,13 @@ final class ConvertedGridCoverage extends GridCoverage {
}
/**
+ * @todo Placeholder for future evolution.
+ */
+ public static GridCoverage createFromConverted(final GridCoverage
converted) {
+ throw new UnsupportedOperationException("\"Converted to packed\" not
yet implemented.");
+ }
+
+ /**
* The coverage containing packed values. Sample values will be converted
from this coverage.
*/
private final GridCoverage packed;
diff --git
a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/GridCoverage2D.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/GridCoverage2D.java
deleted file mode 100644
index f0bcf3b..0000000
---
a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/GridCoverage2D.java
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * 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.internal.coverage;
-
-import java.util.Collection;
-import java.awt.image.BufferedImage;
-import java.awt.image.RenderedImage;
-import org.opengis.util.FactoryException;
-import org.opengis.geometry.DirectPosition;
-import org.opengis.referencing.crs.CoordinateReferenceSystem;
-import org.opengis.referencing.datum.PixelInCell;
-import org.opengis.referencing.operation.MathTransform;
-import org.opengis.referencing.operation.TransformException;
-import org.opengis.coverage.CannotEvaluateException;
-import org.opengis.coverage.PointOutsideCoverageException;
-import org.apache.sis.coverage.SampleDimension;
-import org.apache.sis.coverage.grid.FractionalGridCoordinates;
-import org.apache.sis.coverage.grid.GridCoverage;
-import org.apache.sis.coverage.grid.GridExtent;
-import org.apache.sis.coverage.grid.GridGeometry;
-import org.apache.sis.coverage.grid.DisjointExtentException;
-import org.apache.sis.coverage.grid.IllegalGridGeometryException;
-import org.apache.sis.coverage.grid.IncompleteGridGeometryException;
-import org.apache.sis.internal.image.TranslatedRenderedImage;
-import org.apache.sis.internal.referencing.AxisDirections;
-import org.apache.sis.internal.feature.Resources;
-import org.apache.sis.referencing.CRS;
-import org.apache.sis.referencing.operation.transform.TransformSeparator;
-import org.apache.sis.util.ArgumentChecks;
-
-
-/**
- * Basic access to grid data values backed by a two-dimensional {@link
RenderedImage}.
- * Each band in an image is represented as a {@link SampleDimension}.
- * The rendered image can be a two-dimensional slice in a
<var>n</var>-dimensional space
- * (i.e. the {@linkplain GridGeometry#getEnvelope() grid geometry envelope}
may have more
- * than two dimensions) provided that the {@linkplain GridExtent grid extent}
have a
- * {@linkplain GridExtent#getSize size} equals to 1 in all dimensions except 2.
- *
- * <div class="note"><b>Example:</b>
- * a remote sensing image may be valid only over some time range
- * (the time of satellite pass over the observed area).
- * Envelopes for such grid coverage can have three dimensions:
- * the two usual ones (horizontal extent along <var>x</var> and <var>y</var>),
- * and a third one for start time and end time (time extent along
<var>t</var>).
- * The "two-dimensional" grid coverage can have any number of columns along
<var>x</var> axis
- * and any number of rows along <var>y</var> axis, but only one plan along
<var>t</var> axis.
- * This single plan can have a lower bound (the start time) and an upper bound
(the end time).
- * </div>
- *
- * @author Martin Desruisseaux (Geomatys)
- * @author Johann Sorel (Geomatys)
- * @version 1.1
- * @since 1.1
- * @module
- */
-public final class GridCoverage2D extends GridCoverage {
- /**
- * The sample values stored as a {@code RenderedImage}.
- */
- private final RenderedImage data;
-
- /**
- * Index of extent dimensions corresponding to image <var>x</var> and
<var>y</var> coordinates.
- * Typical values are 0 for {@code xDimension} and 1 for {@code
yDimension}, but different values
- * are allowed.
- */
- private final int xDimension, yDimension;
-
- /**
- * The two-dimensional component of the coordinate reference system, or
{@code null} if unspecified.
- */
- private final CoordinateReferenceSystem crs2D;
-
- /**
- * Result of the call to {@link #forConvertedValues(boolean)}, created
when first needed.
- */
- private transient GridCoverage converted;
-
- /**
- * Constructs a grid coverage using the specified domain, range and data.
- * The given RenderedImage may not start at 0,0, so does the gridExtent of
the grid geometry.
- * Image 0/0 coordinate is expected to match grid extent lower corner.
- *
- * @param domain the grid extent, CRS and conversion from cell indices
to CRS.
- * @param range sample dimensions for each image band.
- * @param data the sample values as a RenderedImage, potentially
multi-banded in packed view.
- */
- public GridCoverage2D(final GridGeometry domain, final Collection<?
extends SampleDimension> range, final RenderedImage data) {
- super(domain, range);
- this.data = data;
- ArgumentChecks.ensureNonNull("image", data);
- /*
- * Extract the 2D components of the coordinate reference system.
- */
- final GridExtent extent = domain.getExtent();
- final int[] imageAxes = extent.getSubspaceDimensions(2);
- xDimension = imageAxes[0];
- yDimension = imageAxes[1];
- if (domain.isDefined(GridGeometry.CRS)) {
- final CoordinateReferenceSystem crs =
domain.getCoordinateReferenceSystem();
- try {
- crs2D = CRS.reduce(crs, imageAxes);
- } catch (IllegalArgumentException | FactoryException e) {
- throw new IllegalGridGeometryException("Can not create a
two-dimensional CRS from " + crs.getName(), e);
- }
- } else {
- crs2D = null;
- }
- /*
- * Check that image is coherent with grid geometry.
- */
- int actual;
- long expected;
- if ((actual = data.getWidth()) != (expected =
extent.getSize(xDimension))) {
- throw new IllegalArgumentException("Image width " + actual + "
does not match grid extent width " + expected);
- }
- if ((actual = data.getHeight()) != (expected =
extent.getSize(yDimension))) {
- throw new IllegalArgumentException("Image height " + actual + "
does not match grid extent height " + expected);
- }
- int n;
- if ((actual = data.getSampleModel().getNumBands()) != (n =
range.size())) {
- throw new IllegalArgumentException("Image sample model number of
bands " + actual + " does not match number of sample dimensions " + n);
- }
- }
-
- /**
- * Returns the two-dimensional part of this grid coverage CRS.
- * If the {@linkplain #getCoordinateReferenceSystem complete CRS} is
two-dimensional,
- * then this method returns the same CRS. Otherwise it returns a CRS for
the two first axis
- * having a {@linkplain GridExtent#getSize(int) size} greater than 1 in
the grid envelope.
- * Note that those axis are guaranteed to appear in the same order than in
the complete CRS.
- *
- * @return the two-dimensional part of the grid coverage CRS.
- * @throws IncompleteGridGeometryException if the grid geometry does not
contain a CRS.
- *
- * @see #getCoordinateReferenceSystem()
- */
- public CoordinateReferenceSystem getCoordinateReferenceSystem2D() {
- if (crs2D != null) {
- return crs2D;
- }
- throw new
IncompleteGridGeometryException(Resources.format(Resources.Keys.UnspecifiedCRS));
- }
-
- /**
- * Returns the grid to CRS 2D transform in pixel center.
- *
- * @return MathTransform grid to CRS 2D transform
- * @throws FactoryException if separating 2d transform fails.
- */
- public MathTransform getGridToCrs2D() throws FactoryException {
- TransformSeparator sep = new
TransformSeparator(getGridGeometry().getGridToCRS(PixelInCell.CELL_CENTER));
- int idx =
AxisDirections.indexOfColinear(getCoordinateReferenceSystem().getCoordinateSystem(),
crs2D.getCoordinateSystem());
- sep.addSourceDimensionRange(idx, idx+2);
- return sep.separate();
- }
-
- /**
- * Returns a grid coverage that contains real values or sample values,
depending if {@code converted} is {@code true}
- * or {@code false} respectively.
- *
- * If the given value is {@code false}, then the default implementation
returns a grid coverage which produces
- * {@link RenderedImage} views. Those views convert each sample value on
the fly. This is known to be very slow
- * if an entire raster needs to be processed, but this is temporary until
another implementation is provided in
- * a future SIS release.
- *
- * @return a coverage containing converted or packed values, depending on
{@code converted} argument value.
- */
- @Override
- public GridCoverage forConvertedValues(final boolean converted) {
- if (converted) {
- synchronized (this) {
- if (this.converted == null) {
- this.converted = BufferedGridCoverage.convert(this);
- }
- return this.converted;
- }
- }
- return this;
- }
-
- /**
- * Returns a two-dimensional slice of grid data as a rendered image.
- * This method may return a view or a copy.
- *
- * @return the grid slice as a rendered image.
- */
- @Override
- public RenderedImage render(final GridExtent sliceExtent) throws
CannotEvaluateException {
- if (sliceExtent == null ||
sliceExtent.equals(getGridGeometry().getExtent())) {
- return data;
- } else {
- final int subX = Math.toIntExact(sliceExtent.getLow(xDimension));
- final int subY = Math.toIntExact(sliceExtent.getLow(yDimension));
- final int subWidth =
Math.toIntExact(Math.round(sliceExtent.getSize(xDimension)));
- final int subHeight =
Math.toIntExact(Math.round(sliceExtent.getSize(yDimension)));
-
- if (data instanceof BufferedImage) {
- final BufferedImage bi = (BufferedImage) data;
- return bi.getSubimage(subX, subY, subWidth, subHeight);
- } else {
- return new TranslatedRenderedImage(data, subX, subY);
- }
- }
- }
-
- /**
- * Returns a sequence of double values for a given point in the coverage.
- * The CRS of the given point may be any coordinate reference system,
- * or {@code null} for the same CRS than this coverage.
- * The returned sequence contains a value for each {@linkplain
SampleDimension sample dimension}.
- *
- * @param point the coordinate point where to evaluate.
- * @param buffer an array in which to store values, or {@code null} to
create a new array.
- * @return the {@code buffer} array, or a newly created array if {@code
buffer} was null.
- * @throws PointOutsideCoverageException if the evaluation failed because
the input point
- * has invalid coordinates.
- * @throws CannotEvaluateException if the values can not be computed at
the specified coordinate
- * for an other reason.
- */
- @Override
- public double[] evaluate(final DirectPosition point, double[] buffer)
throws CannotEvaluateException {
- try {
- final FractionalGridCoordinates gc = toGridCoordinates(point);
- final int x = Math.toIntExact(gc.getCoordinateValue(xDimension));
- final int y = Math.toIntExact(gc.getCoordinateValue(yDimension));
- final int xmin = data.getMinX();
- final int ymin = data.getMinY();
- if (x >= xmin && x < xmin + (long) data.getWidth() &&
- y >= ymin && y < ymin + (long) data.getHeight())
- {
- final int tx = Math.floorDiv(x - data.getTileGridXOffset(),
data.getTileWidth());
- final int ty = Math.floorDiv(y - data.getTileGridYOffset(),
data.getTileHeight());
- return data.getTile(tx, ty).getPixel(x, y, buffer);
- }
- } catch (ArithmeticException | DisjointExtentException ex) {
- throw (PointOutsideCoverageException) new
PointOutsideCoverageException(ex.getMessage(), point).initCause(ex);
- } catch (IllegalArgumentException | TransformException ex) {
- throw new CannotEvaluateException(ex.getMessage(), ex);
- }
- throw new PointOutsideCoverageException(null, point);
- }
-}
diff --git
a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/ImageUtilities.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/ImageUtilities.java
index 73fa6aa..2dcab5a 100644
---
a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/ImageUtilities.java
+++
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/ImageUtilities.java
@@ -17,10 +17,12 @@
package org.apache.sis.internal.coverage;
import java.util.Arrays;
+import java.awt.Rectangle;
import java.awt.color.ColorSpace;
import java.awt.image.ColorModel;
import java.awt.image.PackedColorModel;
import java.awt.image.RenderedImage;
+import java.awt.image.Raster;
import java.awt.image.SampleModel;
import java.awt.image.SinglePixelPackedSampleModel;
import org.apache.sis.internal.system.Modules;
@@ -44,6 +46,18 @@ public final class ImageUtilities {
}
/**
+ * Returns the bounds of the given image as a new rectangle.
+ *
+ * @param image the image for which to get the bounds.
+ * @return the bounds of the given image.
+ *
+ * @see Raster#getBounds()
+ */
+ public static Rectangle getBounds(final RenderedImage image) {
+ return new Rectangle(image.getMinX(), image.getMinY(),
image.getWidth(), image.getHeight());
+ }
+
+ /**
* Returns names of bands based on inspection of the color model.
* The bands are identified by {@link Vocabulary.Keys} values for
* red, green, blue, cyan, magenta, yellow, black, gray, <i>etc</i>.
diff --git
a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/package-info.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/package-info.java
index b390cb7..68fe5d0 100644
---
a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/package-info.java
+++
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/package-info.java
@@ -24,7 +24,7 @@
* may change in incompatible ways in any future version without notice.
*
* @author Johann Sorel (Geomatys)
- * @version 1.0
+ * @version 1.1
* @since 1.0
* @module
*/
diff --git
a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.java
index 3e23ce6..195583c 100644
---
a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.java
+++
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.java
@@ -43,7 +43,7 @@ public final class Resources extends IndexedResourceBundle {
* pools of compiled classes.
*
* @author Martin Desruisseaux (IRD, Geomatys)
- * @since 0.8
+ * @since 1.1
* @module
*/
@Generated("org.apache.sis.util.resources.IndexedResourceCompiler")
@@ -70,6 +70,11 @@ public final class Resources extends IndexedResourceBundle {
public static final short CanNotAssignCharacteristics_1 = 2;
/**
+ * Can not create a two-dimensional reference system from the “{0}”
system.
+ */
+ public static final short CanNotCreateTwoDimensionalCRS_1 = 60;
+
+ /**
* Can not enumerate values in the {0} range.
*/
public static final short CanNotEnumerateValuesInRange_1 = 23;
@@ -199,6 +204,12 @@ public final class Resources extends IndexedResourceBundle
{
public static final short IterationNotStarted = 37;
/**
+ * Image number of bands {0,number} does not match the number of
sample dimensions
+ * ({1,number}).
+ */
+ public static final short MismatchedBandCount_2 = 61;
+
+ /**
* The bands have different number of sample values.
*/
public static final short MismatchedBandSize = 38;
@@ -214,6 +225,12 @@ public final class Resources extends IndexedResourceBundle
{
public static final short MismatchedImageLocation = 40;
/**
+ * Image {0,choice,0#width|1#height} ({1,number} pixels) does not
match the grid extent size
+ * ({2,number} cells).
+ */
+ public static final short MismatchedImageSize_3 = 62;
+
+ /**
* Mismatched type for “{0}” property.
*/
public static final short MismatchedPropertyType_1 = 12;
diff --git
a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.properties
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.properties
index f1153af..e950146 100644
---
a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.properties
+++
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.properties
@@ -21,6 +21,7 @@
#
AbstractFeatureType_1 = Feature type \u2018{0}\u2019 is abstract.
CanNotAssignCharacteristics_1 = Can not assign characteristics to the
\u201c{0}\u201d property.
+CanNotCreateTwoDimensionalCRS_1 = Can not create a two-dimensional reference
system from the \u201c{0}\u201d system.
CanNotEnumerateValuesInRange_1 = Can not enumerate values in the {0} range.
CanNotInstantiateProperty_1 = Property \u201c{0}\u201d is not a type
that can be instantiated.
CanNotMapToGridDimensions = Some envelope dimensions can not be mapped
to grid dimensions.
@@ -46,9 +47,11 @@ IncompatibleTile_2 = The ({0}, {1}) tile has
an unexpected size,
InvalidExpression_2 = Invalid or unsupported \u201c{1}\u201d
expression at index {0}.
IterationIsFinished = Iteration is finished.
IterationNotStarted = Iteration did not started.
+MismatchedBandCount_2 = Image number of bands {0,number} does not
match the number of sample dimensions ({1,number}).
MismatchedBandSize = The bands have different number of sample
values.
MismatchedDataType = The bands store sample values using
different data types.
MismatchedImageLocation = The two images have different size or
pixel coordinates.
+MismatchedImageSize_3 = Image {0,choice,0#width|1#height}
({1,number} pixels) does not match the grid extent size ({2,number} cells).
MismatchedPropertyType_1 = Mismatched type for \u201c{0}\u201d
property.
MismatchedSampleModel = The two images use different sample models.
MismatchedTileGrid = The two images have different tile grid.
diff --git
a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources_fr.properties
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources_fr.properties
index a0f13a6..1b0618b 100644
---
a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources_fr.properties
+++
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources_fr.properties
@@ -26,6 +26,7 @@
#
AbstractFeatureType_1 = Le type d\u2019entit\u00e9 \u2018{0}\u2019
est abstrait.
CanNotAssignCharacteristics_1 = Ne peut pas assigner des
caract\u00e9ristiques \u00e0 la propri\u00e9t\u00e9 \u00ab\u202f{0}\u202f\u00bb.
+CanNotCreateTwoDimensionalCRS_1 = Ne peut pas cr\u00e9er un syst\u00e8me de
r\u00e9f\u00e9rence bidimensionnel \u00e0 partir du syst\u00e8me
\u00ab\u202f{0}\u202f\u00bb.
CanNotEnumerateValuesInRange_1 = Ne peut pas \u00e9num\u00e9rer les valeurs
dans la plage {0}.
CanNotInstantiateProperty_1 = La propri\u00e9t\u00e9
\u00ab\u202f{0}\u202f\u00bb n\u2019est pas d\u2019un type qui peut \u00eatre
instanci\u00e9.
CanNotMapToGridDimensions = Certaines dimensions de l\u2019enveloppe
ne correspondent pas \u00e0 des dimensions de la grille.
@@ -51,9 +52,11 @@ IncompatibleTile_2 = La tuile ({0}, {1}) a
une taille, un nombre
InvalidExpression_2 = Expression \u00ab\u202f{1}\u202f\u00bb
invalide ou non-support\u00e9e \u00e0 l\u2019index {0}.
IterationIsFinished = L\u2019it\u00e9ration est termin\u00e9e.
IterationNotStarted = L\u2019it\u00e9ration n\u2019a pas
commenc\u00e9e.
+MismatchedBandCount_2 = Le nombre de bandes de l\u2019image
({0,number}) ne correspond pas au nombre de dimensions
d\u2019\u00e9chantillonnage ({1,number}).
MismatchedBandSize = Les bandes ont un nombre diff\u00e9rent de
valeurs.
MismatchedDataType = Les bandes stockent leurs valeurs en
utilisant des types de donn\u00e9es diff\u00e9rents.
MismatchedImageLocation = Les deux images ont une taille ou des
coordonn\u00e9es pixels diff\u00e9rentes.
+MismatchedImageSize_3 = La {0,choice,0#largeur|1#hauteur} de
l\u2019image ({1,number} pixels) ne correspond pas \u00e0 l\u2019\u00e9tendue
de la grille ({2,number} cellules).
MismatchedPropertyType_1 = Le type de la propri\u00e9t\u00e9
\u00ab\u202f{0}\u202f\u00bb ne correspond pas.
MismatchedSampleModel = Les deux images disposent les pixels
diff\u00e9remment.
MismatchedTileGrid = Les deux images utilisent des grilles de
tuiles diff\u00e9rentes.
diff --git
a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/package-info.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/package-info.java
index 9db59f4..dfd97c4 100644
---
a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/package-info.java
+++
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/package-info.java
@@ -27,7 +27,7 @@
*
* @author Johann Sorel (Geomatys)
* @author Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.1
* @since 0.7
* @module
*/
diff --git
a/core/sis-feature/src/main/java/org/apache/sis/internal/image/TranslatedRenderedImage.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/image/TranslatedRenderedImage.java
index 2d08aec..dd21d59 100644
---
a/core/sis-feature/src/main/java/org/apache/sis/internal/image/TranslatedRenderedImage.java
+++
b/core/sis-feature/src/main/java/org/apache/sis/internal/image/TranslatedRenderedImage.java
@@ -22,12 +22,13 @@ import java.awt.image.RenderedImage;
import java.awt.image.SampleModel;
import java.util.Vector;
+
/**
* Translated RenderedImage implementation.
*
- * @author Johann Sorel (Geomatys)
- * @version 2.0
- * @since 2.0
+ * @author Johann Sorel (Geomatys)
+ * @version 1.1
+ * @since 1.1
* @module
*/
public final class TranslatedRenderedImage extends AbstractRenderedImage {
@@ -81,12 +82,12 @@ public final class TranslatedRenderedImage extends
AbstractRenderedImage {
@Override
public int getMinX() {
- return image.getMinX() + offsetX;
+ return Math.addExact(image.getMinX(), offsetX);
}
@Override
public int getMinY() {
- return image.getMinY() + offsetY;
+ return Math.addExact(image.getMinY(), offsetY);
}
@Override
@@ -121,18 +122,19 @@ public final class TranslatedRenderedImage extends
AbstractRenderedImage {
@Override
public int getTileGridXOffset() {
- return image.getTileGridXOffset() + offsetX;
+ return Math.addExact(image.getTileGridXOffset(), offsetX);
}
@Override
public int getTileGridYOffset() {
- return image.getTileGridYOffset() + offsetY;
+ return Math.addExact(image.getTileGridYOffset(), offsetY);
}
@Override
public Raster getTile(int tileX, int tileY) {
final Raster tile = image.getTile(tileX, tileY);
- return tile.createTranslatedChild(offsetX, offsetY);
+ return tile.createTranslatedChild(
+ Math.addExact(tile.getMinX(), offsetX),
+ Math.addExact(tile.getMinY(), offsetY));
}
-
}
diff --git
a/core/sis-feature/src/test/java/org/apache/sis/internal/coverage/GridCoverage2DTest.java
b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridCoverage2DTest.java
similarity index 95%
rename from
core/sis-feature/src/test/java/org/apache/sis/internal/coverage/GridCoverage2DTest.java
rename to
core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridCoverage2DTest.java
index 0bc953b..cefd2cf 100644
---
a/core/sis-feature/src/test/java/org/apache/sis/internal/coverage/GridCoverage2DTest.java
+++
b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridCoverage2DTest.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.sis.internal.coverage;
+package org.apache.sis.coverage.grid;
import java.awt.Point;
import java.awt.Transparency;
@@ -27,11 +27,13 @@ import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.util.Arrays;
import java.util.Hashtable;
+import org.opengis.util.FactoryException;
+import org.opengis.coverage.PointOutsideCoverageException;
+import org.opengis.referencing.operation.MathTransform1D;
+import org.opengis.referencing.datum.PixelInCell;
import org.apache.sis.coverage.SampleDimension;
-import org.apache.sis.coverage.grid.GridCoverage;
-import org.apache.sis.coverage.grid.GridExtent;
-import org.apache.sis.coverage.grid.GridGeometry;
import org.apache.sis.geometry.DirectPosition2D;
+import org.apache.sis.internal.coverage.ColorModelFactory;
import org.apache.sis.measure.NumberRange;
import org.apache.sis.measure.Units;
import org.apache.sis.referencing.crs.HardCodedCRS;
@@ -39,18 +41,14 @@ import
org.apache.sis.referencing.operation.transform.MathTransforms;
import org.apache.sis.test.TestCase;
import org.junit.Assert;
import org.junit.Test;
-import org.opengis.coverage.PointOutsideCoverageException;
-import org.opengis.referencing.datum.PixelInCell;
-import org.opengis.referencing.operation.MathTransform1D;
-import org.opengis.util.FactoryException;
/**
* Tests the {@link GridCoverage2D} implementation.
*
* @author Johann Sorel (Geomatys)
- * @version 2.0
- * @since 2.0
+ * @version 1.1
+ * @since 1.1
* @module
*/
public class GridCoverage2DTest extends TestCase {
@@ -77,7 +75,7 @@ public class GridCoverage2DTest extends TestCase {
final ColorSpace colors = ColorModelFactory.createColorSpace(1, 0,
-10, 10);
final ColorModel cm = new ComponentColorModel(colors, false, false,
Transparency.OPAQUE, DataBuffer.TYPE_INT);
BufferedImage image = new BufferedImage(cm, raster, false, new
Hashtable<>());
- GridCoverage coverage = new GridCoverage2D(grid, Arrays.asList(sd),
image);
+ GridCoverage coverage = new GridCoverage2D(grid, Arrays.asList(sd),
image);
raster.setSample(0, 0, 0, 0);
raster.setSample(1, 0, 0, 5);
raster.setSample(0, 1, 0, -5);
diff --git
a/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
b/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
index edd4256..ec05519 100644
---
a/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
+++
b/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
@@ -76,19 +76,19 @@ import org.junit.runners.Suite;
// Rasters
org.apache.sis.image.DefaultIteratorTest.class,
org.apache.sis.image.LinearIteratorTest.class,
+ org.apache.sis.coverage.CategoryTest.class,
+ org.apache.sis.coverage.CategoryListTest.class,
+ org.apache.sis.coverage.SampleDimensionTest.class,
+ org.apache.sis.coverage.SampleRangeFormatTest.class,
org.apache.sis.coverage.grid.PixelTranslationTest.class,
org.apache.sis.coverage.grid.GridExtentTest.class,
org.apache.sis.coverage.grid.GridGeometryTest.class,
org.apache.sis.coverage.grid.GridDerivationTest.class,
org.apache.sis.coverage.grid.FractionalGridCoordinates.class,
- org.apache.sis.coverage.CategoryTest.class,
- org.apache.sis.coverage.CategoryListTest.class,
- org.apache.sis.coverage.SampleDimensionTest.class,
- org.apache.sis.coverage.SampleRangeFormatTest.class,
+ org.apache.sis.coverage.grid.GridCoverage2DTest.class,
org.apache.sis.internal.coverage.ImageUtilitiesTest.class,
org.apache.sis.internal.coverage.ScaledColorSpaceTest.class,
org.apache.sis.internal.coverage.BufferedGridCoverageTest.class,
- org.apache.sis.internal.coverage.GridCoverage2DTest.class,
org.apache.sis.internal.image.TranslatedRenderedImageTest.class
})
public final strictfp class FeatureTestSuite extends TestSuite {