This is an automated email from the ASF dual-hosted git repository. asf-gitbox-commits pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/sis.git
commit f7bef1427dd8d8afa3e8c827da6d89018e005c0a Merge: 20ea158492 9db36b9428 Author: Martin Desruisseaux <[email protected]> AuthorDate: Fri Jun 19 19:32:02 2026 +0200 Merge branch 'geoapi-3.1'. Contains developments of HEIF reader with support of `tili` image scheme and pyramid. .../sis/coverage/grid/BufferedGridCoverage.java | 2 +- .../org/apache/sis/coverage/grid/GridExtent.java | 38 ++ .../org/apache/sis/coverage/grid/GridGeometry.java | 41 +- .../apache/sis/coverage/grid/ImageRenderer.java | 2 +- .../sis/geometry/wrapper/jts/GeometryWalker.java | 184 ++++++++ .../apache/sis/geometry/wrapper/jts/Wrapper.java | 64 +-- .../main/org/apache/sis/image/ComputedImage.java | 2 +- .../main/org/apache/sis/image/ImageProcessor.java | 34 +- .../main/org/apache/sis/image/MaskImage.java | 7 +- .../main/org/apache/sis/image/OverviewImage.java | 310 +++++++++++++ .../main/org/apache/sis/image/PixelIterator.java | 6 +- .../main/org/apache/sis/image/PlanarImage.java | 29 +- .../sis/image/PositionalConsistencyImage.java | 2 + .../main/org/apache/sis/image/ResampledImage.java | 5 +- .../org/apache/sis/image/StatisticsCalculator.java | 8 +- .../main/org/apache/sis/image/UserProperties.java | 2 +- .../image/internal/shared/DeferredProperty.java | 5 +- .../apache/sis/coverage/grid/GridGeometryTest.java | 38 ++ .../apache/sis/geometry/wrapper/jts/JTSTest.java | 8 +- .../org/apache/sis/image/BandedIteratorTest.java | 1 + .../org/apache/sis/image/ComputedImageTest.java | 1 + .../test/org/apache/sis/image/DataTypeTest.java | 1 + .../test/org/apache/sis/image/ImageLayoutTest.java | 1 + .../org/apache/sis/image/ImageOverlayTest.java | 1 + .../test/org/apache/sis/image/ImageTestCase.java | 1 + .../org/apache/sis/image/InterpolationTest.java | 1 + .../org/apache/sis/image/LinearIteratorTest.java | 1 + .../test/org/apache/sis/image/MaskedImageTest.java | 1 + .../org/apache/sis/image/OverviewImageTest.java | 135 ++++++ .../test/org/apache/sis/image/TiledImageMock.java | 17 + .../sis/map/coverage/RenderingWorkaround.java | 2 + .../main/org/apache/sis/io/wkt/Formatter.java | 2 +- .../main/module-info.java | 6 +- .../apache/sis/storage/geotiff/Compression.java | 27 +- .../org/apache/sis/storage/geotiff/DataCube.java | 8 +- .../org/apache/sis/storage/geotiff/DataSubset.java | 4 +- .../apache/sis/storage/geotiff/FormatModifier.java | 19 +- .../apache/sis/storage/geotiff/GeoTiffStore.java | 45 +- .../sis/storage/geotiff/ImageFileDirectory.java | 16 +- .../apache/sis/storage/geotiff/NativeMetadata.java | 6 +- .../apache/sis/storage/geotiff/WritableStore.java | 36 +- .../org/apache/sis/storage/geotiff/Writer.java | 49 ++- .../{Compression.java => CompressionMethod.java} | 57 +-- .../apache/sis/storage/geotiff/base/Predictor.java | 37 +- .../org/apache/sis/storage/geotiff/base/Tags.java | 2 +- .../sis/storage/geotiff/inflater/CCITTRLE.java | 14 +- .../storage/geotiff/inflater/CopyFromBytes.java | 4 +- .../geotiff/inflater/HorizontalPredictor.java | 19 +- .../sis/storage/geotiff/inflater/Inflater.java | 45 +- .../apache/sis/storage/geotiff/inflater/LZW.java | 16 +- .../sis/storage/geotiff/inflater/PackBits.java | 3 +- .../sis/storage/geotiff/inflater/PixelChannel.java | 52 --- .../sis/storage/geotiff/inflater/package-info.java | 6 +- .../apache/sis/storage/geotiff/package-info.java | 3 + .../sis/storage/geotiff/writer/TileMatrix.java | 18 +- .../sis/storage/geotiff/GeoTiffStoreTest.java | 78 ++++ .../sis/storage/geotiff/base/CompressionTest.java | 20 +- .../sis/storage/geotiff/inflater/CCITTRLETest.java | 3 +- .../org/apache/sis/storage/netcdf/base/Raster.java | 4 +- .../sis/storage/netcdf/classic/VariableInfo.java | 6 +- .../org.apache.sis.storage/main/module-info.java | 4 + .../org/apache/sis/io/stream/ChannelDataInput.java | 15 +- .../apache/sis/io/stream/HyperRectangleReader.java | 1 - .../apache/sis/io/stream/HyperRectangleWriter.java | 2 +- .../main/org/apache/sis/io/stream/Region.java | 14 +- .../io/stream/inflater/CompressionException.java} | 34 +- .../io/stream/inflater/ComputedByteChannel.java | 115 +++++ .../apache/sis/io/stream/inflater/Deflate.java} | 35 +- .../sis/io/stream/inflater/InflaterChannel.java} | 77 +--- .../sis/io/stream}/inflater/PredictorChannel.java | 35 +- .../sis/io/stream/inflater/package-info.java} | 26 +- .../sis/storage/AbstractGridCoverageResource.java | 9 + .../apache/sis/storage/GridCoverageResource.java | 1 + .../sis/storage/aggregate/CoverageAggregator.java | 1 - .../{base => aggregate}/PseudoResource.java | 4 +- .../apache/sis/storage/base/OverviewIterator.java} | 35 +- .../storage/base/WritableTiledResourceSupport.java | 86 ++++ .../apache/sis/storage/tiling/ImagePyramid.java | 50 ++- .../apache/sis/storage/tiling/ImageTileMatrix.java | 10 + .../apache/sis/storage/tiling/TileMatrixSet.java | 2 +- .../sis/storage/tiling/TileMatrixSetFormat.java | 101 +++-- .../apache/sis/storage/tiling/TileReadEvent.java | 7 +- .../sis/storage/tiling/TiledGridCoverage.java | 17 +- .../storage/tiling/TiledGridCoverageResource.java | 30 +- .../io/stream/SubsampledRectangleWriterTest.java | 4 +- .../apache/sis/util/internal/shared/Numerics.java | 115 +---- .../sis/storage/geoheif/CoverageBuilder.java | 481 +++++++++------------ .../apache/sis/storage/geoheif/FromImageIO.java | 66 ++- .../apache/sis/storage/geoheif/GeoHeifStore.java | 26 +- .../main/org/apache/sis/storage/geoheif/Image.java | 24 +- .../org/apache/sis/storage/geoheif/ImageModel.java | 322 ++++++++++++++ .../apache/sis/storage/geoheif/ImageResource.java | 123 ++++-- .../org/apache/sis/storage/geoheif/Pyramid.java | 211 ++++++++- .../sis/storage/geoheif/ResourceBuilder.java | 245 +++++++---- .../org/apache/sis/storage/geoheif/TiledImage.java | 119 +++++ .../sis/storage/geoheif/UncompressedImage.java | 167 +++++-- .../sis/storage/isobmff/MainBoxRegistry.java | 10 + .../org/apache/sis/storage/isobmff/Reader.java | 4 +- .../org/apache/sis/storage/isobmff/TreeNode.java | 4 +- .../apache/sis/storage/isobmff/base/ItemData.java | 10 +- .../sis/storage/isobmff/base/ItemInfoEntry.java | 3 +- .../isobmff/image/AuxiliaryImageReference.java | 54 +++ .../storage/isobmff/image/BaseImageReference.java | 53 +++ .../isobmff/image/PremultipliedImageReference.java | 53 +++ .../storage/isobmff/image/ThumbnailReference.java | 53 +++ .../{geo => image}/TiledImageConfiguration.java | 3 +- .../sis/storage/isobmff/mpeg/CleanAperture.java | 57 +++ .../apache/sis/storage/isobmff/mpeg/Component.java | 19 +- .../isobmff/mpeg/CompressedUnitsItemInfo.java | 122 ++++++ .../sis/storage/isobmff/mpeg/CompressionAV1.java | 57 +++ .../isobmff/mpeg/CompressionConfiguration.java | 90 ++++ .../sis/storage/isobmff/mpeg/CompressionJP2K.java | 57 +++ .../isobmff/mpeg/HevcConfigurationItem.java | 57 +++ .../apache/sis/storage/isobmff/mpeg/UnitType.java | 71 +++ .../main/org/apache/sis/gui/DataViewer.java | 29 +- .../main/org/apache/sis/gui/Option.java | 96 ++++ .../apache/sis/gui/coverage/TileMatrixSetPane.java | 33 +- 117 files changed, 3978 insertions(+), 1126 deletions(-) diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridExtent.java index f8c358d1c4,bc099923d4..bff8d5336e --- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridExtent.java +++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridExtent.java @@@ -878,7 -920,9 +879,8 @@@ public class GridExtent implements Seri * @see #getLow(int) * @see #getHigh(int) * @see #resize(long...) + * @see #SIZES_COMPARATOR */ - @Override public long getSize(final int index) { final int dimension = getDimension(); Objects.checkIndex(index, dimension); diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/OverviewImage.java index 0000000000,1c381c7e96..02cb6c9403 mode 000000,100644..100644 --- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/OverviewImage.java +++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/OverviewImage.java @@@ -1,0 -1,313 +1,310 @@@ + /* + * 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.image; + + import java.awt.Rectangle; + import java.awt.image.ColorModel; + import java.awt.image.Raster; + import java.awt.image.WritableRaster; + import java.awt.image.RenderedImage; + import java.awt.image.ImagingOpException; + import org.apache.sis.feature.internal.Resources; + import org.apache.sis.util.Disposable; + import org.apache.sis.util.internal.shared.Numerics; + import org.apache.sis.image.internal.shared.ImageUtilities; + -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import org.opengis.coverage.grid.SequenceType; - + + /** + * An image which is the result of averaging 4 pixels of the image at higher resolution. + * It can be seen as a special case of {@link ResampledImage} with bilinear interpolation + * at the exact center of a block of 4 pixels. + * + * @todo Add an auxiliary image with contains the rest of the division by 4 (when sample values are integers) + * or the number of averaged sample values (when sample values are floating points). + * Use that information when computing overview of overviews, for better accuracy. + * + * @author Estelle Idée (Geomatys) + * @author Martin Desruisseaux (Geomatys) + */ + final class OverviewImage extends ComputedImage { + /** + * The image origin. + */ + private final int minX, minY; + + /** + * The image size in pixels. + */ + private final int width, height; + + /** + * The offset to add after conversion from target to source pixel coordinates. + * This is either 0 or 1. + */ + private final byte offsetX, offsetY; + + /** + * Creates a new image which will create an overview of the given image. + * + * @param source the image at higher resolution. + */ + OverviewImage(final RenderedImage source) { + this(targetBounds(ImageUtilities.getBounds(source)), source); + } + + /** Workaround for RFE #4093999 ("Relax constraint on placement of this()/super() call in constructors"). */ + private static Rectangle targetBounds(final Rectangle bounds) { + bounds.x >>= 1; // Round toward negative infinity. + bounds.y >>= 1; + bounds.width /= 2; // Round toward 0. + bounds.height /= 2; + return bounds; + } + + /** Workaround for RFE #4093999 ("Relax constraint on placement of this()/super() call in constructors"). */ + private OverviewImage(final Rectangle bounds, final RenderedImage source) { + super(ImageLayout.DEFAULT.createCompatibleSampleModel(source, bounds), source); + offsetX = (byte) (source.getMinX() & 1); // TODO: move before `targetBounds(…)` after RFE #4093999. + offsetY = (byte) (source.getMinY() & 1); + minX = bounds.x; + minY = bounds.y; + width = bounds.width; + height = bounds.height; + } + + /** + * Returns the color model of this resampled image. + * Default implementation assumes that this image has the same color model as the source image. + * + * @return the color model, or {@code null} if unspecified. + */ + @Override + public ColorModel getColorModel() { + return getSource().getColorModel(); + } + + /** + * Returns the minimum tile index in the <var>x</var> direction. + */ + @Override + public final int getMinTileX() { + return getSource().getMinTileX() / 2; // Round toward zero. + } + + /** + * Returns the minimum tile index in the <var>y</var> direction. + */ + @Override + public final int getMinTileY() { + return getSource().getMinTileY() / 2; + } + + /** + * Returns the minimum <var>x</var> coordinate (inclusive) of this image. + */ + @Override + public final int getMinX() { + return minX; + } + + /** + * Returns the minimum <var>y</var> coordinate (inclusive) of this image. + */ + @Override + public final int getMinY() { + return minY; + } + + /** + * Returns the number of columns in this image. + */ + @Override + public final int getWidth() { + return width; + } + + /** + * Returns the number of rows in this image. + */ + @Override + public final int getHeight() { + return height; + } + + /** + * Invoked when a tile needs to be computed or updated. + * + * @param tileX the column index of the tile to compute. + * @param tileY the row index of the tile to compute. + * @param tile if the tile already exists but needs to be updated, the tile to update. Otherwise {@code null}. + * @return computed tile for the given indices. + */ + @Override + protected Raster computeTile(final int tileX, final int tileY, WritableRaster tile) { + if (tile == null) { + tile = createTile(tileX, tileY); + } + Rectangle bounds = tile.getBounds(); + bounds.width <<= 1; + bounds.height <<= 1; + bounds.x <<= 1; + bounds.y <<= 1; + bounds.x += offsetX; + bounds.y += offsetY; + final PixelIterator it = new PixelIterator.Builder() + .setIteratorOrder(SequenceType.LINEAR) + .setRegionOfInterest(bounds) + .create(getSource()); + /* + * The iterator may have intersected the given bounds with the source image bounds. + * Therefore, we derive the limits from these bounds instead of from tile bounds. + * It should cover the whole valid area of the tile. + */ + bounds = it.getDomain(); + if (((bounds.width | bounds.height) & 1) != 0) { + throw new ImagingOpException(Resources.format(Resources.Keys.IncompatibleTile_2, tileX, tileY)); + } + bounds.x >>= 1; // Round toward negative infinity. + bounds.y >>= 1; + bounds.width >>= 1; + bounds.height >>= 1; + final int numBands = tile.getNumBands(); + final var buffer = new double[Math.multiplyExact(bounds.width, numBands)]; + final var counts = new byte[buffer.length]; + double[] left = null; + double[] right = null; + final int ymax = bounds.y + bounds.height; + for (int y = bounds.y; y < ymax; y++) { + int x = bounds.x; + /* + * Memorize the sum of two consecutive pixels for all pixels in the current source row. + * The `counts` array contains the number of valid values, which will by 2, 3 or 4 on + * the assumption that the next row will not contain NaN value (verified in next loop). + */ + for (int i=0; i < buffer.length;) { + if (it.next()) { + left = it.getPixel(left); + if (it.next()) { + right = it.getPixel(right); + for (int b=0; b<numBands; b++) { + byte count = 4; + double sum = left[b] + right[b]; + if (Double.isNaN(sum)) { + // Give precedence to the left side if both sides are NaN. + count = (Double.isNaN(sum = right[b]) && + Double.isNaN(sum = left[b])) ? (byte) 2 : (byte) 3; + } + buffer[i] = sum; + counts[i++] = count; + } + continue; + } + } + throw new ImagingOpException(Resources.format(Resources.Keys.OutOfIteratorDomain_2, i/numBands + x, y)); + } + /* + * Read the next row and compute the average with the previous row which was memorized by above loop. + * If some values are NaN, the number of valid values gien by `counts` is adjusted. + */ + for (int i=0; i < buffer.length; x++) { + if (it.next()) { + left = it.getPixel(left); + if (it.next()) { + right = it.getPixel(right); + for (int b=0; b<numBands; b++, i++) { + int count = counts[i]; + double add = left[b] + right[b]; + double sum = add + buffer[i]; + // Test `isNaN(sum)` first because it will be false in the vast majority of cases. + if (Double.isNaN(sum) && Double.isNaN(sum = add)) { + sum = buffer[i]; + if (Double.isNaN(add = right[b]) && Double.isNaN(add = left[b])) { + count -= 2; // The two values of the current row are invalid. + } else { + count--; // Exactly one value of the current row is valid. + sum = Double.isNaN(sum) ? add : sum + add; + } + if (count <= 1) { + // Avoid a division by 0 in order to preserve the NaN bits pattern. + left[b] = sum; + continue; + } + } + left[b] = sum / count; + } + tile.setPixel(x, y, left); + continue; + } + } + throw new ImagingOpException(Resources.format(Resources.Keys.OutOfIteratorDomain_2, x, y)); + } + } + return tile; + } + + /** + * Notifies the source image that tiles will be computed soon in the given region. + * If the source image is an instance of {@link ComputedImage}, then this method + * forwards the notification to it. + */ + @Override + protected Disposable prefetch(final Rectangle tiles) { + final RenderedImage source = getSource(); + if (source instanceof PlanarImage) { + final long xmin = 2L * tiles.x + offsetX; + final long ymin = 2L * tiles.y + offsetY; + final long xmax = 2L * tiles.width + xmin; + final long ymax = 2L * tiles.height + ymin; + final int x = Numerics.clamp(xmin); + final int y = Numerics.clamp(ymin); + return ((PlanarImage) source).prefetch( + new Rectangle(x, y, Numerics.clamp(xmax - x), + Numerics.clamp(ymax - y))); + } + return super.prefetch(tiles); + } + + /** + * Compares the given object with this image for equality. + * + * @param object the object to compare with this image. + * @return {@code true} if the given object is an image performing the same overview as this image. + */ + @Override + public boolean equals(final Object object) { + if (equalsBase(object)) { + final var other = (OverviewImage) object; + return minX == other.minX && + minY == other.minY && + width == other.width && + height == other.height && + offsetX == other.offsetX && + offsetY == other.offsetY; + } + return false; + } + + /** + * Returns a hash code value for this image. The {@link #minX}, {@link #minY}, {@link #width} and {@link #height} + * fields are included in the hash computation as a matter of principle, but this is actually not very important + * because they are derived information. + */ + @Override + public int hashCode() { + return hashCodeBase() + (minX + 31*(minY + 31*(width + 31*height))); + } + } diff --cc endorsed/src/org.apache.sis.feature/test/org/apache/sis/geometry/wrapper/jts/JTSTest.java index d1420045a3,4c38c1cf32..19300ba65c --- a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/geometry/wrapper/jts/JTSTest.java +++ b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/geometry/wrapper/jts/JTSTest.java @@@ -170,8 -170,8 +170,8 @@@ public final class JTSTest extends Test wrapper.setCoordinateReferenceSystem(crs); final GeneralEnvelope envelope = wrapper.getEnvelope(); assertEquals(crs, envelope.getCoordinateReferenceSystem()); - assertArrayEquals(new double[] {5, 6, Double.NaN}, envelope.getLowerCorner().getCoordinate()); - assertArrayEquals(new double[] {5, 6, Double.NaN}, envelope.getUpperCorner().getCoordinate()); - assertArrayEquals(new double[] {5, 6, 7}, envelope.getLowerCorner().getCoordinates()); - assertArrayEquals(new double[] {5, 6, 7}, envelope.getUpperCorner().getCoordinates()); ++ assertArrayEquals(new double[] {5, 6, 7}, envelope.getLowerCorner().getCoordinate()); ++ assertArrayEquals(new double[] {5, 6, 7}, envelope.getUpperCorner().getCoordinate()); } } diff --cc endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/OverviewImageTest.java index 0000000000,e67639cbd0..b5c0fc961b mode 000000,100644..100644 --- a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/OverviewImageTest.java +++ b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/OverviewImageTest.java @@@ -1,0 -1,138 +1,135 @@@ + /* + * 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.image; + + import java.awt.Point; + import java.awt.image.RenderedImage; + import java.util.Random; + -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import org.opengis.coverage.grid.SequenceType; - + // Test dependencies + import org.junit.jupiter.api.Test; + import static org.junit.jupiter.api.Assertions.*; + import org.apache.sis.test.TestCase; + import org.apache.sis.test.TestUtilities; + + + /** + * Tests {@link OverviewImage}. + * + * @author Estelle Idée (Geomatys) + */ + @SuppressWarnings("exports") + public final class OverviewImageTest extends TestCase { + /** + * Creates a new test case. + */ + public OverviewImageTest() { + } + + /** + * Tests on an image filled with integer values. + */ + @Test + public void testOnIntegers() { + testForType(DataType.INT); + } + + /** + * Tests on an image filled with floating point values. + * Some random values are set to NaN. + */ + @Test + public void testOnFloats() { + testForType(DataType.DOUBLE); + } + + /** + * Runs the test on an image of the specified type. + * + * @param type type of data stored in the image. + */ + private static void testForType(final DataType type) { + final Random r = TestUtilities.createRandomNumberGenerator(); + final var source = new TiledImageMock( + type.toDataBufferType(), + r.nextInt( 2) + 1, // num bands + r.nextInt( 9) - 4, // min X + r.nextInt( 9) - 4, // min Y + r.nextInt(20) + 10, // width + r.nextInt(20) + 10, // height + r.nextInt( 5) + 5, // tile width + r.nextInt( 5) + 5, // tile height + r.nextInt( 9) - 4, // min tile X + r.nextInt( 9) - 4, // min tile Y + true); // banded + + source.initializeAllTiles(); + if (!type.isInteger()) { + source.setRandomNaN(r); + } + verify(source, new OverviewImage(source), type.isInteger()); + } + + /** + * Verifies an image which is expected to be the result of an image overview operation. + * + * @param source the image used for computing the overview. + * @param target the result of the image overview operation. + * @param isInteger whether the images use an integer type. + */ + public static void verify(final RenderedImage source, final RenderedImage target, final boolean isInteger) { + assertEquals(source.getWidth() / 2, target.getWidth()); + assertEquals(source.getHeight() / 2, target.getHeight()); + final int offsetX = source.getMinX() & 1; + final int offsetY = source.getMinY() & 1; + + double[] p00 = null, p01 = null, p10 = null, p11 = null; + double[] actual = null; + + final PixelIterator itSource = new PixelIterator.Builder().setIteratorOrder(SequenceType.LINEAR).create(source); + final PixelIterator itTarget = PixelIterator.create(target); + int count = 0; + while (itTarget.next()) { + final Point p = itTarget.getPosition(); + final int sx = p.x * 2 + offsetX; + final int sy = p.y * 2 + offsetY; + + // Read 2×2 block from source. + itSource.moveTo(sx, sy); p00 = itSource.getPixel(p00); + assertTrue(itSource.next()); p01 = itSource.getPixel(p01); + itSource.moveTo(sx, sy + 1); p10 = itSource.getPixel(p10); + assertTrue(itSource.next()); p11 = itSource.getPixel(p11); + + actual = itTarget.getPixel(actual); + for (int b = 0; b < actual.length; b++) { + int n = 0; + double sum = 0, v; + if (!Double.isNaN(v = p00[b])) {sum += v; n++;} + if (!Double.isNaN(v = p01[b])) {sum += v; n++;} + if (!Double.isNaN(v = p10[b])) {sum += v; n++;} + if (!Double.isNaN(v = p11[b])) {sum += v; n++;} + double expected = (n != 0) ? sum / n : Double.NaN; + if (isInteger) { + expected = (int) expected; + } + assertEquals(expected, actual[b], 1E-10, () -> "Mismatch at (" + p.x + ", " + p.y + ')'); + } + count++; + } + assertEquals(target.getWidth() * target.getHeight(), count); + } + } diff --cc endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TileReadEvent.java index 8c8716b0f1,bc28951b95..9f0dd77ca0 --- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TileReadEvent.java +++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TileReadEvent.java @@@ -132,6 -133,8 +133,8 @@@ public final class TileReadEvent extend tr = MathTransforms.concatenate(tr, crsToObjective.getMathTransform()); imageToObjective = MathTransforms.bidimensional(tr); this.crsToObjective = crsToObjective; // Store only after the rest was successful. + } catch (FactoryException e) { - throw new TransformException(e); ++ throw new TransformException(e.toString(), e); } return imageToObjective; }
