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 f3df556dfe GeoTIFF `getTileMatrixSets()` method should return an empty
set if the image is not tiled.
f3df556dfe is described below
commit f3df556dfecb10efbd3ba3b9ece50115db7437d3
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Wed Apr 15 01:26:57 2026 +0200
GeoTIFF `getTileMatrixSets()` method should return an empty set if the
image is not tiled.
---
.../sis/coverage/grid/GridCoverageBuilder.java | 302 +++++++++++++++------
.../sis/image/internal/shared/ObservableImage.java | 2 +-
.../sis/image/internal/shared/TiledImage.java | 2 +-
.../image/internal/shared/WritableTiledImage.java | 5 +-
.../sis/coverage/grid/GridCoverageBuilderTest.java | 25 +-
.../coverage/grid/ResampledGridCoverageTest.java | 2 +-
.../apache/sis/image/BandAggregateImageTest.java | 1 +
.../org/apache/sis/image/BandSelectImageTest.java | 1 +
.../sis/image/BandedSampleConverterTest.java | 2 +-
.../org/apache/sis/image/ImageCombinerTest.java | 5 +-
.../org/apache/sis/image/PixelIteratorTest.java | 5 +-
.../test/org/apache/sis/image/PlanarImageTest.java | 3 +-
.../org/apache/sis/image/ResampledImageTest.java | 3 +-
.../apache/sis/image/StatisticsCalculatorTest.java | 3 +-
.../test/org/apache/sis/image/TiledImageMock.java | 2 +
.../sis/storage/geotiff/GeoTiffStoreTest.java | 46 +++-
.../org/apache/sis/storage/geotiff/ReaderTest.java | 43 ++-
.../test/org/apache/sis/storage/geotiff/tiled.tiff | Bin 0 -> 3564 bytes
.../storage/tiling/TiledGridCoverageResource.java | 52 +++-
.../apache/sis/storage/tiling/TiledResource.java | 2 +
20 files changed, 362 insertions(+), 144 deletions(-)
diff --git
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCoverageBuilder.java
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCoverageBuilder.java
index 8c08c74153..1740406167 100644
---
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCoverageBuilder.java
+++
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCoverageBuilder.java
@@ -26,6 +26,7 @@ import java.util.function.IntBinaryOperator;
import java.awt.Point;
import java.awt.Dimension;
import java.awt.Rectangle;
+import java.awt.image.BandedSampleModel;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
@@ -33,6 +34,7 @@ import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
+import java.awt.image.WritableRenderedImage;
import org.opengis.geometry.Envelope;
import org.opengis.referencing.operation.TransformException;
import org.apache.sis.image.DataType;
@@ -49,6 +51,7 @@ import org.apache.sis.referencing.operation.matrix.MatrixSIS;
import org.apache.sis.referencing.operation.transform.MathTransforms;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.util.resources.Errors;
+import org.apache.sis.pending.jdk.JDK18;
/**
@@ -132,7 +135,7 @@ public class GridCoverageBuilder {
/**
* The raster containing the coverage values.
- * Exactly one of {@code image}, {@link #raster} and {@link #buffer} shall
be non-null.
+ * Exactly one of {@code image}, {@link #raster}, {@link #buffer} and
{@code calc*} fields shall be non-null.
*
* @see #setValues(RenderedImage)
*/
@@ -141,7 +144,7 @@ public class GridCoverageBuilder {
/**
* The raster containing the coverage values.
* May be a {@link WritableRaster}, in which case a {@link BufferedImage}
may be created.
- * Exactly one of {@link #image}, {@code raster} and {@link #buffer} shall
be non-null.
+ * Exactly one of {@code image}, {@link #raster}, {@link #buffer} and
{@code calc*} fields shall be non-null.
*
* @see #setValues(Raster)
*/
@@ -149,19 +152,44 @@ public class GridCoverageBuilder {
/**
* The data buffer containing the coverage values.
- * Exactly one of {@link #image}, {@link #raster} and {@code buffer} shall
be non-null.
+ * Exactly one of {@code image}, {@link #raster}, {@link #buffer} and
{@code calc*} fields shall be non-null.
*
* @see #setValues(DataBuffer, Dimension)
*/
private DataBuffer buffer;
/**
- * The image size, or {@code null} if unspecified. It needs to be
specified only
- * if values were specified as a buffer without information about the grid
size.
+ * Providers of sample values as integers, one provider per band.
+ * Exactly one of {@code image}, {@link #raster}, {@link #buffer} and
{@code calc*} fields shall be non-null.
+ *
+ * @see #setValues(DataType, Dimension, IntBinaryOperator...)
+ */
+ private IntBinaryOperator[] calcAsIntegers;
+
+ /**
+ * The desired data type, or {@code null} if unspecified.
+ * This type is used only if it can be used without copying the data.
+ * For example if the user specified a raster, it will not be reformatted.
+ */
+ private DataType dataType;
+
+ /**
+ * The preferred tile size, or {@code null} if unspecified.
+ * This dimension is used only if it can be used without copying the data.
+ * For example if the user specified a fully constructed image, it will
not be re-tiled.
+ *
+ * @see #setPreferredTileSize(Dimension)
+ */
+ private Dimension tileSize;
+
+ /**
+ * The image size, or {@code null} if unspecified. This size needs to be
specified only if sample
+ * values were specified as a buffer or as a function without information
about the grid extent.
*
* @see #setValues(DataBuffer, Dimension)
+ * @see #setValues(DataType, Dimension, IntBinaryOperator...)
*/
- private Dimension size;
+ private Dimension imageSize;
/**
* Set of grid axes to reverse, as a bit mask. For any dimension
<var>i</var>, the bit
@@ -177,7 +205,7 @@ public class GridCoverageBuilder {
* @see #addImageProperty(String, Object)
*/
@SuppressWarnings("UseOfObsoleteCollectionType")
- private final Hashtable<String,Object> properties;
+ private final Hashtable<String, Object> properties;
/**
* Creates an initially empty builder.
@@ -312,6 +340,39 @@ public class GridCoverageBuilder {
return this;
}
+ /**
+ * Sets the preferred tile size, or {@code null} if no preference.
+ * This values is used only if it can be used without copying the data.
+ * For example if the user specified a fully constructed image, that image
will not be re-tiled.
+ *
+ * @param tileSize the desired tile size, or {@code null} if no
preference.
+ * @return {@code this} for method invocation chaining.
+ *
+ * @see #setValues(DataType, Dimension, IntBinaryOperator...)
+ *
+ * @since 1.7
+ */
+ public GridCoverageBuilder setPreferredTileSize(Dimension tileSize) {
+ if (tileSize != null) {
+ tileSize = new Dimension(tileSize);
+ }
+ this.tileSize = tileSize;
+ return this;
+ }
+
+ /**
+ * Clears all the ways to specify sample values, together with
dependencies such as image size.
+ */
+ private void clearValues() {
+ image = null;
+ raster = null;
+ buffer = null;
+ dataType = null;
+ tileSize = null;
+ imageSize = null;
+ calcAsIntegers = null;
+ }
+
/**
* Sets a two-dimensional slice of sample values as a rendered image.
* If {@linkplain #setRanges(SampleDimension...) sample dimensions are
specified},
@@ -328,10 +389,8 @@ public class GridCoverageBuilder {
* @see BufferedImage
*/
public GridCoverageBuilder setValues(final RenderedImage data) {
- image = Objects.requireNonNull(data);
- raster = null;
- buffer = null;
- size = null;
+ clearValues();
+ image = Objects.requireNonNull(data);
return this;
}
@@ -351,10 +410,8 @@ public class GridCoverageBuilder {
* @see Raster#createBandedRaster(int, int, int, int, Point)
*/
public GridCoverageBuilder setValues(final Raster data) {
+ clearValues();
raster = Objects.requireNonNull(data);
- image = null;
- buffer = null;
- size = null;
return this;
}
@@ -372,6 +429,7 @@ public class GridCoverageBuilder {
*/
public GridCoverageBuilder setValues(final DataBuffer data, Dimension
size) {
ArgumentChecks.ensureNonNull("data", data);
+ clearValues();
if (size != null) {
size = new Dimension(size);
ArgumentChecks.ensureStrictlyPositive("width", size.width);
@@ -382,10 +440,8 @@ public class GridCoverageBuilder {
throw new
IllegalArgumentException(Errors.format(Errors.Keys.UnexpectedArrayLength_2,
length, capacity));
}
}
- this.size = size;
- buffer = data;
- image = null;
- raster = null;
+ imageSize = size;
+ buffer = data;
return this;
}
@@ -401,21 +457,28 @@ public class GridCoverageBuilder {
* The functions may be invoked with pixel coordinates in any order.</p>
*
* @param type the type of values to store in the image.
- * @param size the image size in pixels.
+ * @param size the image size in pixels, or {@code null} if
unspecified. If null, then the image
+ * size will be taken from the {@linkplain
GridGeometry#getExtent() grid extent}.
* @param bands functions providing sample values in each band, in order.
* @return {@code this} for method invocation chaining.
* @throws IllegalArgumentException if {@code width} or {@code height} is
negative or equals to zero.
*
+ * @see #setPreferredTileSize(Dimension)
* @see WritablePixelIterator#setRemainingPixels(IntBinaryOperator...)
*
* @since 1.7
*/
- public GridCoverageBuilder setValues(final DataType type, final Dimension
size, final IntBinaryOperator... bands) {
- // Number of bands, width and height are verified by
`Raster.createBandedRaster(…)`
- final WritableRaster data =
Raster.createBandedRaster(type.toDataBufferType(), size.width, size.height,
bands.length, null);
- final WritablePixelIterator i = new
WritablePixelIterator.Builder().createWritable(data);
- i.setRemainingPixels(bands);
- return setValues(data);
+ public GridCoverageBuilder setValues(final DataType type, Dimension size,
IntBinaryOperator... bands) {
+ ArgumentChecks.ensureNonNull ("type", type);
+ ArgumentChecks.ensureNonEmpty("bands", bands);
+ clearValues();
+ if (size != null) {
+ size = new Dimension(size);
+ }
+ dataType = type;
+ imageSize = size;
+ calcAsIntegers = bands.clone();
+ return this;
}
/**
@@ -464,6 +527,95 @@ public class GridCoverageBuilder {
return this;
}
+ /**
+ * Creates a color model for the given sample model.
+ *
+ * @param sm the sample model.
+ * @param bands {@link #ranges} if non-null, or a default non-null value
otherwise.
+ * @return the color model.
+ */
+ private ColorModel createColorModel(final SampleModel sm, final List<?
extends SampleDimension> bands) {
+ final var colorizer = new
ColorScaleBuilder(ColorScaleBuilder.GRAYSCALE, null, false);
+ if (colorizer.initialize(sm, bands.get(visibleBand)) ||
colorizer.initialize(sm, visibleBand)) {
+ return colorizer.createColorModel(sm, bands.size(), visibleBand);
+ } else {
+ return ColorScaleBuilder.NULL_COLOR_MODEL;
+ }
+ }
+
+ /**
+ * Creates an image from the given rasters.
+ * The first tile in the given array must be the one located at the
minimal tile indices.
+ * All tiles must have the same size and the same sample model and must be
sorted in row-major fashion.
+ *
+ * <p>This method tries to create the most standard objects when possible:
preferably a
+ * {@link BufferedImage} from Java2D, otherwise a {@link TiledImage} from
<abbr>SIS</abbr>.</p>
+ *
+ * @param colors the color model, or {@code null}.
+ * @param width the image width in pixels.
+ * @param height the image height in pixels.
+ * @param tiles the image tiles.
+ * @return an image for the given rasters.
+ */
+ private RenderedImage createImage(final ColorModel colors, final int
width, final int height, final Raster... tiles) {
+ if (colors != null && tiles.length == 1) {
+ final Raster tile = tiles[0];
+ if (tile instanceof WritableRaster && (tile.getMinX() |
tile.getMinY()) == 0) {
+ return new ObservableImage(colors, (WritableRaster) tile,
false, properties);
+ }
+ }
+ if (tiles instanceof WritableRaster[]) {
+ return new WritableTiledImage(properties, colors, width, height,
0, 0, (WritableRaster[]) tiles);
+ } else {
+ return new TiledImage(properties, colors, width, height, 0, 0,
tiles);
+ }
+ }
+
+ /**
+ * Creates an image with values computed by the {@link #calcAsIntegers}
function.
+ *
+ * @param grid {@link #domain} if non-null, or a default non-null value
otherwise.
+ * @param bands {@link #ranges} if non-null, or a default non-null value
otherwise.
+ * @return an image computed from the {@link #calcAsIntegers} function.
+ */
+ private RenderedImage computeImage(final GridGeometry grid, final List<?
extends SampleDimension> bands) {
+ final int width, height;
+ if (imageSize != null) {
+ width = imageSize.width;
+ height = imageSize.height;
+ } else {
+ final GridExtent extent = grid.getExtent();
+ final int[] imageAxes =
extent.getSubspaceDimensions(GridCoverage.BIDIMENSIONAL);
+ width = Math.toIntExact(extent.getSize(imageAxes[0]));
+ height = Math.toIntExact(extent.getSize(imageAxes[1]));
+ }
+ final int tileWidth, tileHeight;
+ if (tileSize != null) {
+ tileWidth = tileSize.width;
+ tileHeight = tileSize.height;
+ } else {
+ tileWidth = width;
+ tileHeight = height;
+ }
+ final int numXTiles = JDK18.ceilDiv(width, tileWidth);
+ final int numYTiles = JDK18.ceilDiv(height, tileHeight);
+ final var sm = new BandedSampleModel(
+ (dataType != null) ? dataType.toDataBufferType() :
DataBuffer.TYPE_INT,
+ tileWidth, tileHeight, calcAsIntegers.length);
+
+ final var location = new Point();
+ final var tiles = new WritableRaster[Math.multiplyExact(numXTiles,
numYTiles)];
+ for (int i=0; i<tiles.length; i++) {
+ location.x = (i % numXTiles) * tileWidth;
+ location.y = (i / numXTiles) * tileHeight;
+ tiles[i] = WritableRaster.createWritableRaster(sm, location);
+ }
+ final var data = (WritableRenderedImage)
createImage(createColorModel(sm, bands), width, height, tiles);
+ final WritablePixelIterator i = new
WritablePixelIterator.Builder().createWritable(data);
+ i.setRemainingPixels(calcAsIntegers);
+ return data;
+ }
+
/**
* Creates the grid coverage from the domain, ranges and values given to
setter methods.
* The returned coverage is often a {@link GridCoverage2D} instance, but
not necessarily.
@@ -476,78 +628,62 @@ public class GridCoverageBuilder {
* {@link IllegalArgumentException} or {@link
NullPointerException}.
*/
public GridCoverage build() throws IllegalStateException {
- GridGeometry grid = domain; // May be
replaced by an instance with extent.
- List<? extends SampleDimension> bands = ranges; // May be
replaced by a non-null value.
- /*
- * If not already done, create the image from the raster. We try to
create the most standard objects
- * when possible: a BufferedImage (from Java2D), then later a
GridCoverage2D (from SIS public API).
- * An exception to this rule is the DataBuffer case: we use a
dedicated BufferedGridCoverage class
- * instead.
- */
+ List<? extends SampleDimension> bands = ranges; // May be replaced
by a non-null value.
+ GridGeometry grid = domain; // May be replaced
by an instance with extent.
+ RenderedImage data = image; // If null, will be
replaced by a non-null value.
try {
- if (image == null) {
- if (raster == null) {
- if (buffer == null) {
+ if (data == null) {
+ @SuppressWarnings("LocalVariableHidesMemberVariable")
+ final Raster raster = this.raster; // Prevent
accidental change.
+ if (raster != null) {
+ /*
+ * Create the image from the raster. If the band list is
null, create a default list
+ * because we need bands for creating a default color
model. Note that we should not
+ * do that when a `RenderedImage` has been specified
because the `GridCoverage2D`
+ * constructor will infer better sample dimension names.
+ */
+ grid = GridCoverage2D.addExtentIfAbsent(grid,
raster.getBounds());
+ bands = GridCoverage2D.defaultIfAbsent(bands, null,
raster.getNumBands());
+ final SampleModel sm = raster.getSampleModel();
+ /*
+ * Create an image from the raster. We favor BufferedImage
instance when possible,
+ * and fallback on TiledImage only if the BufferedImage
cannot be created.
+ */
+ properties.put(PlanarImage.SAMPLE_DIMENSIONS_KEY,
bands.toArray(SampleDimension[]::new));
+ data = createImage(createColorModel(sm, bands),
raster.getWidth(), raster.getHeight(), raster);
+ properties.remove(PlanarImage.SAMPLE_DIMENSIONS_KEY);
+ } else {
+ /*
+ * Case of data specified as an array (wrapped in a
buffer) or as functions.
+ */
+ if (buffer == null && calcAsIntegers == null) {
throw new
IllegalStateException(missingProperty("values"));
}
- if (size != null) {
- grid = GridCoverage2D.addExtentIfAbsent(grid, new
Rectangle(size));
- verifyGridExtent(grid.getExtent(), size.width,
size.height);
+ if (imageSize != null) {
+ grid = GridCoverage2D.addExtentIfAbsent(grid, new
Rectangle(imageSize));
+ verifyGridExtent(grid.getExtent(), imageSize.width,
imageSize.height);
} else if (grid == null) {
- throw new
IncompleteGridGeometryException(missingProperty("size"));
+ throw new
IncompleteGridGeometryException(missingProperty("imageSize"));
}
- bands = GridCoverage2D.defaultIfAbsent(bands, null,
buffer.getNumBanks());
- return new BufferedGridCoverage(domainWithAxisFlips(grid),
bands, buffer);
- }
- /*
- * If the band list is null, create a default list of bands
because we need
- * them for creating the color model. Note that we shall not
do that when a
- * RenderedImage has been specified because GridCoverage2D
constructor will
- * will infer better names.
- */
- bands = GridCoverage2D.defaultIfAbsent(bands, null,
raster.getNumBands());
- final SampleModel sm = raster.getSampleModel();
- final var colorizer = new
ColorScaleBuilder(ColorScaleBuilder.GRAYSCALE, null, false);
- final ColorModel colors;
- if (colorizer.initialize(sm, bands.get(visibleBand)) ||
colorizer.initialize(sm, visibleBand)) {
- colors = colorizer.createColorModel(sm, bands.size(),
visibleBand);
- } else {
- colors = ColorScaleBuilder.NULL_COLOR_MODEL;
- }
- /*
- * Create an image from the raster. We favor BufferedImage
instance when possible,
- * and fallback on TiledImage only if the BufferedImage cannot
be created.
- */
- if (bands != null) {
- properties.put(PlanarImage.SAMPLE_DIMENSIONS_KEY,
bands.toArray(SampleDimension[]::new));
- } else {
- properties.remove(PlanarImage.SAMPLE_DIMENSIONS_KEY);
- }
- if (raster instanceof WritableRaster) {
- final WritableRaster wr = (WritableRaster) raster;
- if (colors != null && (wr.getMinX() | wr.getMinY()) == 0) {
- image = new ObservableImage(colors, wr, false,
properties);
+ if (buffer != null) {
+ bands = GridCoverage2D.defaultIfAbsent(bands, null,
buffer.getNumBanks());
+ return new
BufferedGridCoverage(domainWithAxisFlips(grid), bands, buffer);
} else {
- image = new WritableTiledImage(properties, colors,
wr.getWidth(), wr.getHeight(), 0, 0, wr);
+ bands = GridCoverage2D.defaultIfAbsent(bands, null,
calcAsIntegers.length);
+ data = computeImage(grid, bands);
}
- } else {
- image = new TiledImage(properties, colors,
raster.getWidth(), raster.getHeight(), 0, 0, raster);
}
- properties.remove(PlanarImage.SAMPLE_DIMENSIONS_KEY);
}
- /*
- * At this point `image` shall be non-null but `bands` may still
be null (it is okay).
- */
- return new GridCoverage2D(domainWithAxisFlips(grid), bands, image);
+ return new GridCoverage2D(domainWithAxisFlips(grid), bands, data);
} catch (TransformException | NullPointerException |
IllegalArgumentException | ArithmeticException e) {
throw new
IllegalStateException(Resources.format(Resources.Keys.CanNotBuildGridCoverage),
e);
}
}
/**
- * Returns the {@linkplain #domain} with axis flips applied. If there is
no axis to flip,
- * {@link #domain} is returned unchanged (without completion for missing
extent; we leave
- * that to {@link GridCoverage2D} constructor).
+ * Returns the {@linkplain #domain} with <abbr>CRS</abbr> axis flips
applied.
+ * If there is no axis to flip, {@link #domain} is returned unchanged
+ * (without completion for missing extent, we leave that to {@link
GridCoverage2D} constructor).
*
* @see GridCoverage2D#addExtentIfAbsent(GridGeometry, Rectangle)
*/
@@ -574,7 +710,7 @@ public class GridCoverageBuilder {
/**
* Verifies that the grid extent has the expected size. This method does
not verify grid location
* (low coordinates) because it is okay to have it anywhere. The {@code
expectedSize} array can be
- * shorter than the number of dimensions (i.e. it may be a slice in a data
cube); this method uses
+ * shorter than the number of dimensions (i.e. it may be a slice in a data
cube). This method uses
* {@link GridExtent#getSubspaceDimensions(int)} for determining which
dimensions to check.
*
* <p>This verification can be useful because {@link DataBuffer} does not
contain any information
diff --git
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/internal/shared/ObservableImage.java
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/internal/shared/ObservableImage.java
index 9c05fb389a..13192e2b3f 100644
---
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/internal/shared/ObservableImage.java
+++
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/internal/shared/ObservableImage.java
@@ -30,7 +30,7 @@ import org.apache.sis.util.ArraysExt;
/**
- * A buffered image which cannotify tile observers when tile are acquired for
write operations.
+ * A buffered image which can notify tile observers when tile are acquired for
write operations.
* Provides also helper methods for {@link WritableRenderedImage}
implementations.
*
* <p>This class should be used in preference to {@link BufferedImage} when
the image may be the
diff --git
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/internal/shared/TiledImage.java
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/internal/shared/TiledImage.java
index b7807c8a59..80fbe92537 100644
---
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/internal/shared/TiledImage.java
+++
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/internal/shared/TiledImage.java
@@ -77,7 +77,7 @@ public class TiledImage extends PlanarImage {
* @param minTileY minimum tile index in the Y direction.
* @param tiles the tiles. Must contains at least one element. This
array is not cloned.
*/
- public TiledImage(final Map<String,Object> properties, final ColorModel
colorModel,
+ public TiledImage(final Map<String, Object> properties, final ColorModel
colorModel,
final int width, final int height, final int minTileX,
final int minTileY,
final Raster... tiles)
{
diff --git
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/internal/shared/WritableTiledImage.java
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/internal/shared/WritableTiledImage.java
index 3353d882bd..50e2967429 100644
---
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/internal/shared/WritableTiledImage.java
+++
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/internal/shared/WritableTiledImage.java
@@ -54,8 +54,9 @@ public class WritableTiledImage extends TiledImage implements
WritableRenderedIm
private final Map<Point,Integer> writables;
/**
- * Creates a new tiled image. The first tile in the given array must be
the one located at the minimal tile
- * indices. All tiles must have the same size and the same sample model
and must be sorted in row-major fashion.
+ * Creates a new tiled image.
+ * The first tile in the given array must be the one located at the
minimal tile indices.
+ * All tiles must have the same size and the same sample model and must be
sorted in row-major fashion.
*
* @param properties image properties, or {@code null} if none.
* @param colorModel the color model, or {@code null} if none.
diff --git
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridCoverageBuilderTest.java
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridCoverageBuilderTest.java
index 8c0bd2a2ad..525a758e7f 100644
---
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridCoverageBuilderTest.java
+++
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridCoverageBuilderTest.java
@@ -21,7 +21,6 @@ import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.RenderedImage;
-import java.awt.image.WritableRaster;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.Matrix;
import org.apache.sis.coverage.SampleDimension;
@@ -55,8 +54,8 @@ public final class GridCoverageBuilderTest extends TestCase {
*/
@Test
public void testBuildFromImage() {
- final RenderedImage image = new BufferedImage(5, 8,
BufferedImage.TYPE_INT_ARGB);
- final GridCoverageBuilder builder = new GridCoverageBuilder();
+ final var image = new BufferedImage(5, 8, BufferedImage.TYPE_INT_ARGB);
+ final var builder = new GridCoverageBuilder();
assertSame(builder, builder.setValues(image));
final GridCoverage coverage = testBuilder(builder, 4);
assertSame(image, coverage.render(null));
@@ -67,8 +66,8 @@ public final class GridCoverageBuilderTest extends TestCase {
*/
@Test
public void testBuildFromRaster() {
- final WritableRaster raster = new BufferedImage(5, 8,
BufferedImage.TYPE_3BYTE_BGR).getRaster();
- final GridCoverageBuilder builder = new GridCoverageBuilder();
+ final var raster = new BufferedImage(5, 8,
BufferedImage.TYPE_3BYTE_BGR).getRaster();
+ final var builder = new GridCoverageBuilder();
assertSame(builder, builder.setValues(raster));
final GridCoverage coverage = testBuilder(builder, 3);
assertSame(raster, coverage.render(null).getTile(0,0));
@@ -139,7 +138,7 @@ public final class GridCoverageBuilderTest extends TestCase
{
* @return the grid coverage created by the given builder.
*/
private static GridCoverage testSetDomain(final GridCoverageBuilder
builder, final int width, final int height) {
- final GeneralEnvelope env = new GeneralEnvelope(HardCodedCRS.WGS84);
+ final var env = new GeneralEnvelope(HardCodedCRS.WGS84);
env.setRange(0, 0, 10); // Scale factor of 2 for grid size of 10.
env.setRange(1, 0, 4); // Scale factor of ½ for grid size of 8.
GridGeometry grid = new GridGeometry(new GridExtent(8, 5), env,
GridOrientation.HOMOTHETY);
@@ -160,12 +159,12 @@ public final class GridCoverageBuilderTest extends
TestCase {
*/
@Test
public void testCreateFromBuffer() {
- final DataBuffer buffer = new DataBufferByte(new byte[] {1,2,3,4,5,6},
6);
- final GridCoverageBuilder builder = new GridCoverageBuilder();
+ final var buffer = new DataBufferByte(new byte[] {1,2,3,4,5,6}, 6);
+ final var builder = new GridCoverageBuilder();
assertSame(builder, builder.setValues(buffer, null));
var e = assertThrows(IncompleteGridGeometryException.class, () ->
builder.build(),
"Extent is undefined, build() should fail.");
- assertMessageContains(e, "size");
+ assertMessageContains(e, "imageSize");
final GridCoverage coverage = testSetDomain(builder, 3, 2);
assertSame(buffer, coverage.render(null).getTile(0,0).getDataBuffer());
}
@@ -175,12 +174,12 @@ public final class GridCoverageBuilderTest extends
TestCase {
*/
@Test
public void testFlipGridAxis() {
- final RenderedImage image = new BufferedImage(36, 18,
BufferedImage.TYPE_INT_ARGB);
- final GeneralEnvelope domain = new GeneralEnvelope(HardCodedCRS.WGS84);
+ final var image = new BufferedImage(36, 18,
BufferedImage.TYPE_INT_ARGB);
+ final var domain = new GeneralEnvelope(HardCodedCRS.WGS84);
domain.setRange(0, -180, +180);
domain.setRange(1, -90, +90);
- final GridCoverageBuilder builder = new GridCoverageBuilder();
+ final var builder = new GridCoverageBuilder();
assertSame(builder, builder.setValues(image));
assertSame(builder, builder.setDomain(domain));
/*
@@ -214,7 +213,7 @@ public final class GridCoverageBuilderTest extends TestCase
{
*/
@Test
public void testUndefinedDomain() {
- final GridCoverageBuilder builder = new GridCoverageBuilder();
+ final var builder = new GridCoverageBuilder();
assertSame(builder, builder.setDomain(GridGeometry.UNDEFINED));
assertSame(builder, builder.setValues(new BufferedImage(3, 4,
BufferedImage.TYPE_BYTE_GRAY)));
final GridCoverage coverage = builder.build();
diff --git
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/ResampledGridCoverageTest.java
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/ResampledGridCoverageTest.java
index 8771f50569..403438369e 100644
---
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/ResampledGridCoverageTest.java
+++
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/ResampledGridCoverageTest.java
@@ -103,7 +103,7 @@ public final class ResampledGridCoverageTest extends
TestCase {
random = TestUtilities.createRandomNumberGenerator();
final int width = random.nextInt(8) + 3;
final int height = random.nextInt(8) + 3;
- final TiledImageMock image = new TiledImageMock(
+ final var image = new TiledImageMock(
DataBuffer.TYPE_USHORT, 2, // dataType and numBands
random.nextInt(32) - 10, // minX (no effect on tests)
random.nextInt(32) - 10, // minY (no effect on tests)
diff --git
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/BandAggregateImageTest.java
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/BandAggregateImageTest.java
index cd5cae30f5..ffd554ed1d 100644
---
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/BandAggregateImageTest.java
+++
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/BandAggregateImageTest.java
@@ -46,6 +46,7 @@ import org.apache.sis.test.TestCase;
* @author Alexis Manin (Geomatys)
* @author Martin Desruisseaux (Geomatys)
*/
+@SuppressWarnings("exports")
public final class BandAggregateImageTest extends TestCase {
/**
* Whether to test write operations.
diff --git
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/BandSelectImageTest.java
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/BandSelectImageTest.java
index 32391850f6..7a342717ff 100644
---
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/BandSelectImageTest.java
+++
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/BandSelectImageTest.java
@@ -42,6 +42,7 @@ import static
org.apache.sis.feature.Assertions.assertValuesEqual;
*
* @author Martin Desruisseaux (Geomatys)
*/
+@SuppressWarnings("exports")
public final class BandSelectImageTest extends TestCase {
/**
* Arbitrary size for the test image.
diff --git
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/BandedSampleConverterTest.java
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/BandedSampleConverterTest.java
index cca05744ac..758d3fb50b 100644
---
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/BandedSampleConverterTest.java
+++
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/BandedSampleConverterTest.java
@@ -56,7 +56,7 @@ public final class BandedSampleConverterTest extends
ImageTestCase {
*/
private void createImage(final int sourceType, final DataType targetType,
final double scale) {
final Random random = TestUtilities.createRandomNumberGenerator();
- final TiledImageMock source = new TiledImageMock(
+ final var source = new TiledImageMock(
sourceType, 1,
random.nextInt(20) - 10, // minX
random.nextInt(20) - 10, // minY
diff --git
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/ImageCombinerTest.java
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/ImageCombinerTest.java
index eb3f98fc04..d886c98a20 100644
---
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/ImageCombinerTest.java
+++
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/ImageCombinerTest.java
@@ -36,6 +36,7 @@ import static
org.apache.sis.feature.Assertions.assertValuesEqual;
* @author Martin Desruisseaux (Geomatys)
* @author Johann Sorel (Geomatys)
*/
+@SuppressWarnings("exports")
public final class ImageCombinerTest extends ImageTestCase {
/**
* The image to add to the {@link ImageCombiner}.
@@ -52,7 +53,7 @@ public final class ImageCombinerTest extends ImageTestCase {
* Creates a rendered image with arbitrary tiles.
*/
private ImageCombiner initialize() {
- final TiledImageMock destination = new TiledImageMock(
+ final var destination = new TiledImageMock(
DataBuffer.TYPE_USHORT, 1, // dataType, numBands
3, 4, // minX, minY
12, 8, // width, height
@@ -63,7 +64,7 @@ public final class ImageCombinerTest extends ImageTestCase {
* An image intersecting the destination, with a small part outside.
* Intentionally use a different data type and different tile layout.
*/
- final TiledImageMock source = new TiledImageMock(
+ final var source = new TiledImageMock(
DataBuffer.TYPE_FLOAT, 1, // dataType, numBands
5, 3, // minX, minY
9, 6, // width, height
diff --git
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/PixelIteratorTest.java
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/PixelIteratorTest.java
index b4610065e0..b1ef255c2f 100644
---
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/PixelIteratorTest.java
+++
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/PixelIteratorTest.java
@@ -54,6 +54,7 @@ import org.opengis.coverage.grid.SequenceType;
* @author Rémi Maréchal (Geomatys)
* @author Martin Desruisseaux (Geomatys)
*/
+@SuppressWarnings("exports")
public class PixelIteratorTest extends TestCase {
/**
* The pixel iterator being tested.
@@ -228,8 +229,8 @@ public class PixelIteratorTest extends TestCase {
subMaxY = StrictMath.min(ymax, subArea.y + subArea.height);
}
expected = new float[StrictMath.max(subMaxX - subMinX, 0) *
StrictMath.max(subMaxY - subMinY, 0) * numBands];
- final TiledImageMock image = new TiledImageMock(dataType, numBands,
xmin, ymin, width, height,
- tileWidth, tileHeight, minTileX,
minTileY, useBandedSampleModel);
+ final var image = new TiledImageMock(dataType, numBands, xmin, ymin,
width, height,
+ tileWidth, tileHeight, minTileX, minTileY,
useBandedSampleModel);
image.validate();
/*
* At this point, all data structures have been created an initialized
to zero sample values.
diff --git
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/PlanarImageTest.java
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/PlanarImageTest.java
index 95aeb48a6f..39076ff3ab 100644
---
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/PlanarImageTest.java
+++
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/PlanarImageTest.java
@@ -33,6 +33,7 @@ import static
org.apache.sis.feature.Assertions.assertValuesEqual;
*
* @author Martin Desruisseaux (Geomatys)
*/
+@SuppressWarnings("exports")
public final class PlanarImageTest extends TestCase {
/**
* Size of tiles in this test. The width should be different than the
height
@@ -45,7 +46,7 @@ public final class PlanarImageTest extends TestCase {
*/
private static PlanarImage createImage() {
final Random random = TestUtilities.createRandomNumberGenerator();
- final TiledImageMock image = new TiledImageMock(
+ final var image = new TiledImageMock(
DataBuffer.TYPE_USHORT, 1, // dataType and numBands
random.nextInt(20) - 10, // minX
random.nextInt(20) - 10, // minY
diff --git
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/ResampledImageTest.java
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/ResampledImageTest.java
index 1119375699..decc4dee85 100644
---
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/ResampledImageTest.java
+++
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/ResampledImageTest.java
@@ -47,6 +47,7 @@ import org.apache.sis.test.TestUtilities;
* @author Rémi Marechal (Geomatys)
* @author Martin Desruisseaux (Geomatys)
*/
+@SuppressWarnings("exports")
public final class ResampledImageTest extends TestCase {
/**
* The source image. This is initialized to arbitrary values in two bands.
@@ -87,7 +88,7 @@ public final class ResampledImageTest extends TestCase {
final int tileHeight = random.nextInt(8) + 4;
final int numXTiles = random.nextInt(3) + 1;
final int numYTiles = random.nextInt(4) + 1;
- final TiledImageMock image = new TiledImageMock(
+ final var image = new TiledImageMock(
dataType, 2, // dataType and numBands
random.nextInt(32) - 10, // minX
random.nextInt(32) - 10, // minY
diff --git
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/StatisticsCalculatorTest.java
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/StatisticsCalculatorTest.java
index 9faa630361..ca7ddd2764 100644
---
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/StatisticsCalculatorTest.java
+++
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/StatisticsCalculatorTest.java
@@ -39,6 +39,7 @@ import org.apache.sis.test.TestCaseWithLogs;
*
* @author Martin Desruisseaux (Geomatys)
*/
+@SuppressWarnings("exports")
public final class StatisticsCalculatorTest extends TestCaseWithLogs.Isolated {
/**
* Size of the artificial tiles. Should be small enough so we can have
many of them.
@@ -70,7 +71,7 @@ public final class StatisticsCalculatorTest extends
TestCaseWithLogs.Isolated {
* random values.
*/
private static TiledImageMock createImage() {
- final TiledImageMock image = new TiledImageMock(
+ final var image = new TiledImageMock(
DataBuffer.TYPE_USHORT, 2,
+51, // minX
-72, // minY
diff --git
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/TiledImageMock.java
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/TiledImageMock.java
index a3e0945053..1db6fcd075 100644
---
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/TiledImageMock.java
+++
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/TiledImageMock.java
@@ -492,6 +492,7 @@ public final class TiledImageMock extends PlanarImage
implements WritableRendere
* Current implementation can set raster covering only one tile.
*/
@Override
+ @SuppressWarnings("LocalVariableHidesMemberVariable")
public synchronized void setData(final Raster r) {
final int minX = r.getMinX();
final int minY = r.getMinY();
@@ -508,6 +509,7 @@ public final class TiledImageMock extends PlanarImage
implements WritableRendere
*
* @return this image as a more complete implementation.
*/
+ @SuppressWarnings("exports")
public synchronized WritableTiledImage toWritableTiledImage() {
return new WritableTiledImage(null, null, width, height, minTileX,
minTileY, tiles);
}
diff --git
a/endorsed/src/org.apache.sis.storage.geotiff/test/org/apache/sis/storage/geotiff/GeoTiffStoreTest.java
b/endorsed/src/org.apache.sis.storage.geotiff/test/org/apache/sis/storage/geotiff/GeoTiffStoreTest.java
index 6eb8646c16..614db3ee38 100644
---
a/endorsed/src/org.apache.sis.storage.geotiff/test/org/apache/sis/storage/geotiff/GeoTiffStoreTest.java
+++
b/endorsed/src/org.apache.sis.storage.geotiff/test/org/apache/sis/storage/geotiff/GeoTiffStoreTest.java
@@ -56,7 +56,7 @@ import static
org.opengis.test.Assertions.assertAxisDirectionsEqual;
/**
- * integration tests for {@link GeoTiffStore}.
+ * Integration tests for {@link GeoTiffStore}.
* This class tests indirectly (via {@link GeoTiffStore}) the {@link Reader}
and {@link Writer} classes.
*
* @author Martin Desruisseaux (Geomatys)
@@ -68,6 +68,11 @@ public final class GeoTiffStoreTest extends TestCase {
*/
static final String UNTILED = "untiled.tiff";
+ /**
+ * Name of a test file for an image similar to {@link #UNTILED} but tiled.
+ */
+ static final String TILED = "tiled.tiff";
+
/**
* Creates a new test case.
*/
@@ -127,8 +132,6 @@ public final class GeoTiffStoreTest extends TestCase {
/**
* Writes an image and compare with the {@code "untiled.tiff"} file.
- * This is an anti-regression test, as this test does not inspect the
- * content of the file.
*
* @throws TransformException if an error occurred while computing the
domain of the image.
* @throws DataStoreException if an error occurred while writing the
GeoTIFF file.
@@ -136,25 +139,54 @@ public final class GeoTiffStoreTest extends TestCase {
*/
@Test
public void testWriteUntiled() throws TransformException,
DataStoreException, IOException {
+ testWrite(UNTILED, new Dimension(32, 16), null, 2284);
+ }
+
+ /**
+ * Writes an image and compare with the {@code "tiled.tiff"} file.
+ *
+ * @throws TransformException if an error occurred while computing the
domain of the image.
+ * @throws DataStoreException if an error occurred while writing the
GeoTIFF file.
+ * @throws IOException if an error occurred while reading the file of
expected content.
+ */
+ @Test
+ public void testWriteTiled() throws TransformException,
DataStoreException, IOException {
+ final var tileSize = new Dimension(16, 16); // TIFF tile size must
be multiple of 16.
+ testWrite(TILED, new Dimension(tileSize.width * 3, tileSize.height *
2), tileSize, 3564);
+ }
+
+ /**
+ * Implementation of {@link #testWriteUntiled()} and {@link
#testWriteTiled()}.
+ *
+ * @param filename name of the file which contain the expected image.
+ * @param imageSize size of the image to create.
+ * @param tileSize size of the tiles, or {@code null} for the image
size.
+ * @param length expected length in bytes.
+ */
+ private static void testWrite(final String filename, final Dimension
imageSize, final Dimension tileSize, final int length)
+ throws TransformException, DataStoreException, IOException
+ {
var geographicArea = new GeneralEnvelope(HardCodedCRS.WGS84);
geographicArea.setRange(0, 132, 145); // Range of longitude values.
geographicArea.setRange(1, 30, 42); // Range of latitude values.
final GridCoverage coverage = new GridCoverageBuilder()
.setDomain(Envelopes.transform(geographicArea,
HardCodedConversions.mercator()))
- .setValues(DataType.BYTE, new Dimension(32, 16), (x, y) -> 100
* y + x)
+ .setValues(DataType.BYTE, imageSize, (x, y) -> 100 * y + x)
+ .setPreferredTileSize(tileSize)
.flipGridAxis(1)
.build();
- final var buffer = new ByteArrayOutputStream(2284);
+ final var buffer = new ByteArrayOutputStream(length);
try (DataStore ds = DataStores.openWritable(buffer, "geotiff")) {
assertInstanceOf(GeoTiffStore.class, ds).append(coverage, null);
}
final byte[] actual = buffer.toByteArray();
final byte[] expected;
- try (InputStream in =
GeoTiffStoreTest.class.getResourceAsStream(UNTILED)) {
- assertNotNull(in, UNTILED);
+ try (InputStream in =
GeoTiffStoreTest.class.getResourceAsStream(filename)) {
+ assertNotNull(in, filename);
expected = in.readAllBytes();
}
assertArrayEquals(expected, actual);
+ assertEquals(length, actual.length);
}
}
diff --git
a/endorsed/src/org.apache.sis.storage.geotiff/test/org/apache/sis/storage/geotiff/ReaderTest.java
b/endorsed/src/org.apache.sis.storage.geotiff/test/org/apache/sis/storage/geotiff/ReaderTest.java
index 6addcc99eb..3534cb5a70 100644
---
a/endorsed/src/org.apache.sis.storage.geotiff/test/org/apache/sis/storage/geotiff/ReaderTest.java
+++
b/endorsed/src/org.apache.sis.storage.geotiff/test/org/apache/sis/storage/geotiff/ReaderTest.java
@@ -16,6 +16,7 @@
*/
package org.apache.sis.storage.geotiff;
+import java.util.List;
import java.util.SortedMap;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
@@ -45,8 +46,6 @@ import org.apache.sis.test.TestCase;
* but indirectly via {@link GeoTiffStore}.
*
* @author Martin Desruisseaux (Geomatys)
- *
- * @todo We should rewrite {@code "untiled.tiff"} as a tiled image.
*/
@SuppressWarnings("exports")
public class ReaderTest extends TestCase {
@@ -59,11 +58,12 @@ public class ReaderTest extends TestCase {
/**
* Creates a new data store for the test file.
*
+ * @param filename name of the file to load.
* @return the data store to test.
* @throws DataStoreException if an error occurred while creating the data
store.
*/
- private static GeoTiffStore createStore() throws DataStoreException {
- return new GeoTiffStore(null, new
StorageConnector(ReaderTest.class.getResource(GeoTiffStoreTest.UNTILED)));
+ private static GeoTiffStore createStore(final String filename) throws
DataStoreException {
+ return new GeoTiffStore(null, new
StorageConnector(ReaderTest.class.getResource(filename)));
}
/**
@@ -77,21 +77,33 @@ public class ReaderTest extends TestCase {
}
/**
- * Tests the tile matrix set.
+ * Tests the tile matrix set or an untiled image.
*
- * @todo Need to be updated if we rewrite {@code "untiled.tiff"} as a more
interesting image.
+ * @throws DataStoreException if an error occurred while creating the data
store.
+ */
+ @Test
+ public void testUntiled() throws DataStoreException {
+ try (GeoTiffStore ds = createStore(GeoTiffStoreTest.UNTILED)) {
+ final GridCoverageResource resource =
assertInstanceOf(GridCoverageResource.class, assertSingleton(ds.components()));
+ assertInstanceOf(ProjectedCRS.class,
resource.getGridGeometry().getCoordinateReferenceSystem());
+ assertTrue(assertInstanceOf(TiledResource.class,
resource).getTileMatrixSets().isEmpty());
+ }
+ }
+
+ /**
+ * Tests the tile matrix set.
*
* @throws DataStoreException if an error occurred while creating the data
store.
*/
@Test
public void testTileMatrixSet() throws DataStoreException {
- try (GeoTiffStore ds = createStore()) {
+ try (GeoTiffStore ds = createStore(GeoTiffStoreTest.TILED)) {
final GridCoverageResource resource =
assertInstanceOf(GridCoverageResource.class, assertSingleton(ds.components()));
assertInstanceOf(ProjectedCRS.class,
resource.getGridGeometry().getCoordinateReferenceSystem());
final TileMatrixSet pyramid =
assertSingleton(assertInstanceOf(TiledResource.class,
resource).getTileMatrixSets());
assertSame(resource.getGridGeometry().getCoordinateReferenceSystem(),
pyramid.getCoordinateReferenceSystem());
- assertEquals("untiled:1:TMS", pyramid.getIdentifier().toString());
+ assertEquals("tiled:1:TMS", pyramid.getIdentifier().toString());
assertFalse(pyramid.getEnvelope().isEmpty());
final SortedMap<GenericName, ? extends TileMatrix> matrices =
pyramid.getTileMatrices();
@@ -101,15 +113,16 @@ public class ReaderTest extends TestCase {
assertTrue(matrices.subMap(matrices.firstKey(),
matrices.lastKey()).isEmpty());
final TileMatrix matrix = assertSingleton(matrices.values());
- assertEquals("untiled:1:TMS:L0",
matrix.getIdentifier().toFullyQualifiedName().toString());
+ assertEquals("tiled:1:TMS:L0",
matrix.getIdentifier().toFullyQualifiedName().toString());
assertEquals("TMS:L0", matrix.getIdentifier().toString());
assertEquals(assertSingleton(matrices.keySet()),
matrix.getIdentifier());
assertArrayEquals(resource.getGridGeometry().getResolution(false),
matrix.getResolution());
- assertSame(TileStatus.OUTSIDE_EXTENT, matrix.getTileStatus(1, 0));
- assertSame(TileStatus.OUTSIDE_EXTENT, matrix.getTileStatus(0, 1));
+ assertSame(TileStatus.OUTSIDE_EXTENT, matrix.getTileStatus(3, 0));
+ assertSame(TileStatus.OUTSIDE_EXTENT, matrix.getTileStatus(0, 2));
assertSame(TileStatus.UNKNOWN, matrix.getTileStatus(0, 0));
// Because the tile has not yet been loaded.
+ assertSame(TileStatus.UNKNOWN, matrix.getTileStatus(2, 1));
- assertMessageContains(assertThrows(NoSuchDataException.class, ()
-> matrix.getTile(1, 0)));
+ assertMessageContains(assertThrows(NoSuchDataException.class, ()
-> matrix.getTile(3, 0)));
final Tile tile = matrix.getTile(0, 0).orElseThrow();
assertArrayEquals(new long[] {0, 0}, tile.getIndices());
assertEquals(TileStatus.EXISTS, tile.getStatus());
@@ -117,8 +130,10 @@ public class ReaderTest extends TestCase {
final Raster raster = raster(tile);
assertEquals(TileStatus.EXISTS, tile.getStatus());
- assertArrayEquals(tile.getIndices(),
assertSingleton(matrix.getTiles(null, false).toList()).getIndices());
- assertSame(raster, raster(assertSingleton(matrix.getTiles(null,
false).toList())));
+ final List<Tile> tiles = matrix.getTiles(null, false).toList();
+ assertEquals(3 * 2, tiles.size());
+ assertArrayEquals(tile.getIndices(), tiles.get(0).getIndices());
+ assertEquals(raster.getBounds(), raster(tiles.get(0)).getBounds());
}
}
}
diff --git
a/endorsed/src/org.apache.sis.storage.geotiff/test/org/apache/sis/storage/geotiff/tiled.tiff
b/endorsed/src/org.apache.sis.storage.geotiff/test/org/apache/sis/storage/geotiff/tiled.tiff
new file mode 100644
index 0000000000..fb34ea58d7
Binary files /dev/null and
b/endorsed/src/org.apache.sis.storage.geotiff/test/org/apache/sis/storage/geotiff/tiled.tiff
differ
diff --git
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TiledGridCoverageResource.java
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TiledGridCoverageResource.java
index 833361beb1..886d952c6f 100644
---
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TiledGridCoverageResource.java
+++
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TiledGridCoverageResource.java
@@ -192,6 +192,23 @@ public abstract class TiledGridCoverageResource extends
AbstractGridCoverageReso
yDimension = 1;
}
+ /**
+ * Returns whether a resource with the given extent contains more than one
tile.
+ *
+ * @param extent value of {@code getGridGeometry().getExtent()}.
+ * @return whether a resource with the given extent contains more than one
tile.
+ * @throws DataStoreException if an error occurred while fetching the tile
size.
+ */
+ private boolean isTiled(final GridExtent extent) throws DataStoreException
{
+ final int[] tileSize = getTileSize();
+ for (int i = Math.min(tileSize.length, extent.getDimension()); --i >=
0;) {
+ if (extent.getSize(i) > tileSize[i]) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Returns the size of tiles in this resource.
* The length of the returned array is the number of dimensions,
@@ -494,16 +511,19 @@ check: if (dataType.isInteger()) {
synchronized (getSynchronizationLock()) {
if (tileMatrixSets == null) {
final List<Pyramid> pyramids = getPyramids();
- final var sets = new TileMatrixSet[pyramids.size()];
- if (sets.length != 0) { // For avoiding an index out of
bounds in call to `get(0)`.
+ final int n = pyramids.size();
+ if (n != 0) { // For avoiding an index out of bounds in call
to `get(0)`.
+ final var sets = new TileMatrixSet[n];
final GenericName scope = getIdentifier().orElseGet(
() ->
pyramids.get(0).nameFactory().createLocalName(null, listeners.getSourceName()));
final var processor = new GridCoverageProcessor();
- for (int i=0; i<sets.length; i++) {
+ for (int i=0; i<n; i++) {
sets[i] = new ImagePyramid(scope, pyramids.get(i),
processor, listeners.getLocale());
}
+ tileMatrixSets = List.of(sets);
+ } else {
+ tileMatrixSets = List.of();
}
- tileMatrixSets = List.of(sets);
}
return tileMatrixSets;
}
@@ -1117,12 +1137,13 @@ check: if (dataType.isInteger()) {
/**
* Returns information about the {@code TileMatrixSet} instances to create.
- * The first element in the returned list <em>shall</em> be the default
pyramid
+ * If the returned list is non-empty, then the first element
<em>shall</em> be the default pyramid
* using the same Coordinate Reference System (<abbr>CRS</abbr>) as this
Grid Coverage Resource.
* Other elements, if any, can use any <abbr>CRS</abbr>.
*
* <p>This method is invoked by the default implementation of {@link
#getTileMatrixSets()} when first needed.
- * By default, this method returns a list of only one element, which
itself describes a pyramid of only one level.
+ * By default, this method returns an empty list if this resource is
untiled, or otherwise a list of exactly
+ * one element describing a pyramid of only one level.
* This single level describes a {@link TileMatrix} at the resolution of
this {@code TiledGridCoverageResource}.</p>
*
* @return information about the tile matrix sets to create.
@@ -1132,15 +1153,18 @@ check: if (dataType.isInteger()) {
* @see #getTileMatrixSets()
*/
protected List<Pyramid> getPyramids() throws DataStoreException {
- if (!getGridGeometry().isDefined(GridGeometry.EXTENT |
GridGeometry.GRID_TO_CRS | GridGeometry.RESOLUTION)) {
- return List.of();
- }
- return List.of(new Pyramid() {
- @Override public OptionalInt numberOfLevels() {return
OptionalInt.of(1);}
- @Override public TiledGridCoverageResource forPyramidLevel(int
level) {
- return (level == 0) ? TiledGridCoverageResource.this : null;
+ final GridGeometry gridGeometry = getGridGeometry();
+ if (gridGeometry.isDefined(GridGeometry.EXTENT |
GridGeometry.GRID_TO_CRS | GridGeometry.RESOLUTION)) {
+ if (isTiled(gridGeometry.getExtent())) {
+ return List.of(new Pyramid() {
+ @Override public OptionalInt numberOfLevels() {return
OptionalInt.of(1);}
+ @Override public TiledGridCoverageResource
forPyramidLevel(int level) {
+ return (level == 0) ? TiledGridCoverageResource.this :
null;
+ }
+ });
}
- });
+ }
+ return List.of();
}
/**
diff --git
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TiledResource.java
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TiledResource.java
index 4c9d6e338a..416a61f1a0 100644
---
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TiledResource.java
+++
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TiledResource.java
@@ -41,6 +41,8 @@ public interface TiledResource extends Resource {
/**
* Returns the collection of all available tile matrix sets in this
resource.
* The returned collection typically contains exactly one instance.
+ * It may be an empty collection if the implementation supports tiling,
+ * but this particular resource instance is untiled.
*
* @return all available {@link TileMatrixSet} instances, or an empty
collection if none.
* @throws DataStoreException if an error occurred while fetching the tile
matrix sets.