This is an automated email from the ASF dual-hosted git repository.
jsorel 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 92465b6 Coverage : add Linear pixel iterator
92465b6 is described below
commit 92465b666d927e051f69897a6192d1f2dc113692
Author: jsorel <[email protected]>
AuthorDate: Wed Mar 13 10:07:43 2019 +0100
Coverage : add Linear pixel iterator
---
.../java/org/apache/sis/image/DefaultIterator.java | 12 +-
.../java/org/apache/sis/image/LinearIterator.java | 79 ++
.../java/org/apache/sis/image/PixelIterator.java | 8 +-
.../org/apache/sis/image/LinearIteratorTest.java | 1135 ++++++++++++++++++++
.../org/apache/sis/test/suite/RasterTestSuite.java | 1 +
5 files changed, 1227 insertions(+), 8 deletions(-)
diff --git
a/core/sis-raster/src/main/java/org/apache/sis/image/DefaultIterator.java
b/core/sis-raster/src/main/java/org/apache/sis/image/DefaultIterator.java
index 7450cbf..b737d35 100644
--- a/core/sis-raster/src/main/java/org/apache/sis/image/DefaultIterator.java
+++ b/core/sis-raster/src/main/java/org/apache/sis/image/DefaultIterator.java
@@ -54,27 +54,27 @@ import org.apache.sis.util.ArgumentChecks;
*
* @todo Change iteration order on tiles for using Hilbert iterator.
*/
-final class DefaultIterator extends WritablePixelIterator {
+class DefaultIterator extends WritablePixelIterator {
/**
* Tile coordinate of {@link #currentRaster}.
*/
- private int tileX, tileY;
+ protected int tileX, tileY;
/**
* Current column index in current raster.
*/
- private int x;
+ protected int x;
/**
* Current row index in current raster.
*/
- private int y;
+ protected int y;
/**
* Bounds of the region traversed by the iterator in current raster.
* When iteration reaches the upper coordinates, the iterator needs to
move to next tile.
*/
- private int currentLowerX, currentUpperX, currentUpperY;
+ protected int currentLowerX, currentUpperX, currentUpperY;
/**
* Creates an iterator for the given region in the given raster.
@@ -238,7 +238,7 @@ final class DefaultIterator extends WritablePixelIterator {
* All fields prefixed by {@code current} are updated by this method. This
method also updates
* the {@link #y} field, but caller is responsible for updating the {@link
#x} field.
*/
- private void fetchTile() {
+ void fetchTile() {
currentRaster = null;
if (destination != null) {
destRaster = destination.getWritableTile(tileX, tileY);
diff --git
a/core/sis-raster/src/main/java/org/apache/sis/image/LinearIterator.java
b/core/sis-raster/src/main/java/org/apache/sis/image/LinearIterator.java
new file mode 100644
index 0000000..58e4c2c
--- /dev/null
+++ b/core/sis-raster/src/main/java/org/apache/sis/image/LinearIterator.java
@@ -0,0 +1,79 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.apache.sis.image;
+
+import java.awt.Dimension;
+import java.awt.Rectangle;
+import java.awt.image.Raster;
+import java.awt.image.RenderedImage;
+import java.awt.image.WritableRaster;
+import java.awt.image.WritableRenderedImage;
+import org.apache.sis.internal.raster.Resources;
+import org.opengis.coverage.grid.SequenceType;
+
+/**
+ * Linear iterator used when common line y line iteration is requiered.
+ * This iterator uses the {@link Raster} API for traversing the pixels of the
image.
+ * Calls to {@link #next()} move the current position by increasing the
following values, in order:
+ *
+ * <ol>
+ * <li>Column index in image (from left to right)</li>
+ * <li>Row index in image (from top to bottom).</li>
+ * </ol>
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 1.0
+ * @since 1.0
+ * @module
+ *
+ */
+public class LinearIterator extends DefaultIterator {
+
+ public LinearIterator(Raster input, WritableRaster output, Rectangle
subArea, Dimension window) {
+ super(input, output, subArea, window);
+ }
+
+ public LinearIterator(final RenderedImage input, final
WritableRenderedImage output, final Rectangle subArea, final Dimension window) {
+ super(input, output, subArea, window);
+ }
+
+ @Override
+ public SequenceType getIterationOrder() {
+ return SequenceType.LINEAR;
+ }
+
+ @Override
+ public boolean next() {
+
+ if (++x >= upperX) {
+ //move to next line
+ x = lowerX;
+ if (++y >= upperY) {
+ x = lowerX;
+ return false;
+ }
+ } else if (y >= upperY) {
+ x = lowerX;
+ //second time or more get in the next method, raise error
+ throw new
IllegalStateException(Resources.format(Resources.Keys.IterationIsFinished));
+ }
+
+ if (image != null) {
+ final int tx = Math.floorDiv(x - tileGridXOffset, tileWidth);
+ final int ty = Math.floorDiv(y - tileGridYOffset, tileHeight);
+ if (tx != tileX || ty != tileY) {
+ close(); // Release current writable raster, if any.
+ tileX = tx;
+ tileY = ty;
+ int ry = y;
+ fetchTile();
+ y = ry; //y is changed by fetchTile method
+ }
+ }
+ return true;
+ }
+
+}
diff --git
a/core/sis-raster/src/main/java/org/apache/sis/image/PixelIterator.java
b/core/sis-raster/src/main/java/org/apache/sis/image/PixelIterator.java
index fc6642d..8423205 100644
--- a/core/sis-raster/src/main/java/org/apache/sis/image/PixelIterator.java
+++ b/core/sis-raster/src/main/java/org/apache/sis/image/PixelIterator.java
@@ -295,7 +295,9 @@ public abstract class PixelIterator {
*/
public PixelIterator create(final RenderedImage data) {
ArgumentChecks.ensureNonNull("data", data);
- if (order != null) {
+ if (order == SequenceType.LINEAR) {
+ return new LinearIterator(data, null, subArea, window);
+ } else if (order != null) {
throw new
IllegalStateException(Errors.format(Errors.Keys.UnsupportedType_1, order));
}
// TODO: check here for cases that we can optimize (after we
ported corresponding implementations).
@@ -348,7 +350,9 @@ public abstract class PixelIterator {
public WritablePixelIterator createWritable(final RenderedImage input,
final WritableRenderedImage output) {
ArgumentChecks.ensureNonNull("input", input);
ArgumentChecks.ensureNonNull("output", output);
- if (order != null) {
+ if (order == SequenceType.LINEAR) {
+ return new LinearIterator(input, output, subArea, window);
+ } else if (order != null) {
throw new
IllegalStateException(Errors.format(Errors.Keys.UnsupportedType_1, order));
}
// TODO: check here for cases that we can optimize (after we
ported corresponding implementations).
diff --git
a/core/sis-raster/src/test/java/org/apache/sis/image/LinearIteratorTest.java
b/core/sis-raster/src/test/java/org/apache/sis/image/LinearIteratorTest.java
new file mode 100644
index 0000000..3cb6835
--- /dev/null
+++ b/core/sis-raster/src/test/java/org/apache/sis/image/LinearIteratorTest.java
@@ -0,0 +1,1135 @@
+/*
+ * 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.Dimension;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.image.DataBuffer;
+import java.awt.image.PixelInterleavedSampleModel;
+import java.awt.image.Raster;
+import java.awt.image.WritableRaster;
+import java.awt.image.WritableRenderedImage;
+import java.nio.FloatBuffer;
+import org.apache.sis.test.DependsOnMethod;
+import org.apache.sis.test.TestCase;
+import org.apache.sis.util.ArraysExt;
+import org.junit.After;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.opengis.coverage.grid.SequenceType;
+
+/**
+ * This base class tests the linear read-write iterator
+ * on signed short integer values.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @author Johann Sorel (Geomatys)
+ * @version 1.0
+ * @since 1.0
+ */
+public class LinearIteratorTest extends TestCase {
+ /**
+ * The pixel iterator being tested.
+ * This field is initialized by a call to one of the {@code
createPixelIterator(…)} methods.
+ */
+ private WritablePixelIterator iterator;
+
+ /**
+ * The raster or image data type as one of the {@link DataBuffer}
constants.
+ */
+ private final int dataType;
+
+ /**
+ * <var>x</var> coordinate of upper left corner in raster or image.
+ */
+ private int xmin;
+
+ /**
+ * <var>y</var> coordinate of upper left corner in raster or image.
+ */
+ private int ymin;
+
+ /**
+ * Number of pixels along the <var>x</var> axis in raster or image.
+ */
+ private int width;
+
+ /**
+ * Number of pixels along the <var>y</var> axis in raster or image.
+ */
+ private int height;
+
+ /**
+ * Number of bands in the raster or image.
+ */
+ private int numBands;
+
+ /**
+ * Number of pixels along the <var>x</var> axis of each tile in image
tiles.
+ * This is zero if the tests are performed on a raster instead than a
tiled image.
+ */
+ private int tileWidth;
+
+ /**
+ * Number of pixels along the <var>y</var> axis of each tile in image
tiles.
+ * This is zero if the tests are performed on a raster instead than a
tiled image.
+ */
+ private int tileHeight;
+
+ /**
+ * The minimum tile index.
+ */
+ private int minTileX, minTileY;
+
+ /**
+ * The expected values as a flat, row-major array. The content of this
array is a copy
+ * of the sample values stored in the raster or image used as a source of
test data.
+ */
+ private float[] expected;
+
+ /**
+ * {@code true} for testing write operations in addition of read
operations.
+ */
+ private boolean isWritable;
+
+ /**
+ * Creates a new test case for the given data type.
+ *
+ * @param dataType the raster or image data type as one of the {@link
DataBuffer} constants.
+ */
+ LinearIteratorTest(final int dataType) {
+ this.dataType = dataType;
+ }
+
+ /**
+ * Creates a new test case.
+ */
+ public LinearIteratorTest() {
+ this(DataBuffer.TYPE_SHORT);
+ }
+
+ /**
+ * Creates a {@code WritableRaster} to use as the source of test data.
+ * The raster is filled with arbitrary sample values.
+ *
+ * <p><b>Pre-conditions:</b>
+ * before invocation, the {@link #xmin}, {@link #ymin}, {@link #width},
{@link #height} and {@link #numBands}
+ * fields must be initialized.</p>
+ *
+ * @param subArea the raster subarea on which to perform iteration, or
{@code null} for the whole area.
+ * @return a raster filled with arbitrary sample values.
+ */
+ private WritableRaster createRaster(final Rectangle subArea) {
+ final int xmax = xmin + width; // Maximum
value is exclusive.
+ final int ymax = ymin + height;
+ final int subMinX, subMinY, subMaxX, subMaxY;
+ if (subArea == null) {
+ subMinX = xmin;
+ subMinY = ymin;
+ subMaxX = xmax;
+ subMaxY = ymax;
+ } else {
+ subMinX = StrictMath.max(xmin, subArea.x);
+ subMinY = StrictMath.max(ymin, subArea.y);
+ subMaxX = StrictMath.min(xmax, subArea.x + subArea.width);
+ subMaxY = StrictMath.min(ymax, subArea.y + subArea.height);
+ }
+ expected = new float[StrictMath.max(subMaxX - subMinX, 0) *
StrictMath.max(subMaxY - subMinY, 0) * numBands];
+ final WritableRaster raster = Raster.createWritableRaster(new
PixelInterleavedSampleModel(dataType,
+ width, height, numBands, width * numBands, ArraysExt.range(0,
numBands)), new Point(xmin, ymin));
+ /*
+ * At this point, all data structures have been created an initialized
to zero sample values.
+ * Now fill the data structures with arbitrary values.
+ */
+ int n = 0;
+ float value = (dataType == DataBuffer.TYPE_FLOAT) ? -100.5f : 100f;
+ for (int y=ymin; y<ymax; y++) {
+ final boolean rowIncluded = (y >= subMinY && y < subMaxY);
+ for (int x=xmin; x<xmax; x++) {
+ final boolean included = rowIncluded && (x >= subMinX && x <
subMaxX);
+ for (int b = 0; b < numBands; b++) {
+ raster.setSample(x, y, b, value);
+ if (included) {
+ expected[n++] = value;
+ }
+ value++;
+ }
+ }
+ value += 10; // Arbitrary offset.
+ }
+ assertEquals("Number of expected values", expected.length, n);
+ return raster;
+ }
+
+ /**
+ * Creates a {@code RenderedImage} to use as the source of test data.
+ * The image is filled with arbitrary sample values.
+ *
+ * <p><b>Pre-conditions:</b>
+ * before invocation, the {@link #xmin}, {@link #ymin}, {@link #width},
{@link #height},
+ * {@link #tileWidth}, {@link #tileHeight} and {@link #numBands} fields
must be initialized.</p>
+ *
+ * @param subArea the image subarea on which to perform iteration, or
{@code null} for the whole area.
+ * @return an image filled with arbitrary sample values.
+ */
+ private WritableRenderedImage createImage(final Rectangle subArea) {
+ assertEquals(0, width % tileWidth);
+ assertEquals(0, height % tileHeight);
+ final int numXTiles = (width + tileWidth -1) / tileWidth;
+ final int numYTiles = (height + tileHeight-1) / tileHeight;
+ final int xmax = xmin + width; //
Maximum value is exclusive.
+ final int ymax = ymin + height;
+ final int subMinX, subMinY, subMaxX, subMaxY;
+ if (subArea == null) {
+ subMinX = xmin;
+ subMinY = ymin;
+ subMaxX = xmax;
+ subMaxY = ymax;
+ } else {
+ subMinX = StrictMath.max(xmin, subArea.x);
+ subMinY = StrictMath.max(ymin, subArea.y);
+ subMaxX = StrictMath.min(xmax, subArea.x + subArea.width);
+ 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);
+ /*
+ * At this point, all data structures have been created an initialized
to zero sample values.
+ * Now fill the data structures with arbitrary values. We fill them
tile-by-tile.
+ */
+ int n = 0;
+ float value = (dataType == DataBuffer.TYPE_FLOAT) ? -100.5f : 100f;
+ for(int y = subMinY; y < subMaxY; y++) {
+ for(int x = subMinX; x < subMaxX; x++) {
+ for (int b = 0; b < numBands; b++) {
+ image.setSample(x, y, b, value);
+ expected[n++] = value;
+ value++;
+ }
+ value += 4; // Arbitrary offset.
+ }
+ value += 7; // Arbitrary offset.
+ }
+
+ assertEquals("Number of expected values", expected.length, n);
+ return image;
+ }
+
+ /**
+ * Creates a {@code PixelIterator} for a sub-area of given raster.
+ * The iterator shall be assigned to the {@link #iterator} field.
+ *
+ * <p>The default implementation creates {@link LinearIterator} instances.
+ * Tests for other kinds of iterator need to override.</p>
+ *
+ * @param raster the data on which to perform iteration.
+ * @param subArea the boundary of the raster sub-area where to perform
iteration.
+ */
+ void createPixelIterator(WritableRaster raster, Rectangle subArea) {
+ iterator = new LinearIterator(raster, isWritable ? raster : null,
subArea, null);
+ assertEquals("getIterationOrder()", SequenceType.LINEAR,
iterator.getIterationOrder());
+ assertEquals("isWritable", isWritable, iterator.isWritable());
+ }
+
+ /**
+ * Creates a {@code PixelIterator} for a sub-area of given image.
+ * The iterator shall be assigned to the {@link #iterator} field.
+ *
+ * <p>The default implementation creates {@link LinearIterator} instances.
+ * Tests for other kinds of iterator need to override.</p>
+ *
+ * @param image the data on which to perform iteration.
+ * @param subArea the boundary of the image sub-area where to perform
iteration.
+ */
+ void createPixelIterator(WritableRenderedImage image, Rectangle subArea) {
+ iterator = new LinearIterator(image, isWritable ? image : null,
subArea, null);
+ assertEquals("isWritable", isWritable, iterator.isWritable());
+ }
+
+ /**
+ * Creates a {@code PixelIterator} for a window in the given image.
+ * The iterator shall be assigned to the {@link #iterator} field.
+ *
+ * <p>The default implementation creates {@link LinearIterator} instances.
+ * Tests for other kinds of iterator need to override.</p>
+ *
+ * @param image the data on which to perform iteration.
+ * @param window size of the window to use in {@link
PixelIterator#createWindow(TransferType)} method.
+ */
+ void createWindowIterator(WritableRenderedImage image, Dimension window) {
+ iterator = new LinearIterator(image, isWritable ? image : null, null,
window);
+ assertEquals("isWritable", isWritable, iterator.isWritable());
+ }
+
+ /**
+ * Invoked after every tests for releasing resources.
+ */
+ @After
+ public void dispose() {
+ iterator.close();
+ }
+
+ /**
+ * Verifies the sample value at current iterator position.
+ * If the iterator is writable, tests also setting a value.
+ *
+ * @param i index in {@link #expected} array.
+ * @param b band number at current iterator position.
+ */
+ private void verifySample(final int i, final int b) {
+ final float e = expected[i];
+ final float a = iterator.getSampleFloat(b);
+ if (Float.floatToRawIntBits(a) != Float.floatToRawIntBits(e)) {
+ fail("Pixel iteration at index " + i + ": expected " + e + " but
got " + a);
+ }
+ if (isWritable) {
+ final float newValue = a + 20;
+ iterator.setSample(b, newValue);
+ assertEquals("Verification after write", newValue,
iterator.getSampleFloat(b), 0f);
+ expected[i] = newValue;
+ }
+ }
+
+ /**
+ * Iterates over all values returned by the current {@link #iterator} and
compares with expected values.
+ *
+ * @param verifyIndices whether to verify also iterator {@code
getPosition()} return values.
+ * This is usually {@code true} if an only if the
iterator cover the full raster area.
+ *
+ * @see #verifyIterationAfterMove(int, int)
+ * @see #verifyWindow(Dimension)
+ */
+ private void verifyIteration(final boolean verifyIndices) {
+ int i = 0;
+ while (iterator.next()) {
+ for (int b=0; b<numBands; b++) {
+ verifySample(i, b);
+ if (verifyIndices) {
+ final int p = i / numBands;
+ final Point position = iterator.getPosition();
+ assertEquals("x", (p % width) + xmin, position.x);
+ assertEquals("y", (p / width) + ymin, position.y);
+ }
+ i++;
+ }
+ }
+ assertEquals("Too few elements in iteration.", expected.length, i);
+ }
+
+ /**
+ * Tests iteration over all pixels in a single raster.
+ * Raster location and number of bands are different than other tests
(each test uses arbitrary values).
+ */
+ @Test
+ public void testOnRaster() {
+ width = 7;
+ height = 10;
+ numBands = 3;
+ createPixelIterator(createRaster(null), null);
+ verifyIteration(true);
+ }
+
+ /**
+ * Tests {@link PixelIterator#rewind()}.
+ * Raster location and number of bands are different than other tests
(each test uses arbitrary values).
+ */
+ @Test
+ @DependsOnMethod("testOnRaster")
+ public void testRasterRewind() {
+ xmin = -3;
+ ymin = -5;
+ width = 8;
+ height = 7;
+ numBands = 2;
+ createPixelIterator(createRaster(null), null);
+ verifyIteration(true);
+
+ iterator.rewind();
+ verifyIteration(true);
+ }
+
+ /**
+ * Tests iteration over a sub-area in a single raster.
+ * This test iterates in the upper-left corner of the raster.
+ * Raster location and number of bands are different than other tests
(each test uses arbitrary values).
+ */
+ @Test
+ @DependsOnMethod("testOnRaster")
+ public void testOnRasterUpperLeft() {
+ xmin = 5;
+ ymin = 7;
+ width = 9;
+ height = 8;
+ numBands = 3;
+ final Rectangle subArea = new Rectangle(4, 6, 5, 4);
+ createPixelIterator(createRaster(subArea), subArea);
+ assertTrue("Expected a non-empty set of values.", expected.length !=
0);
+ verifyIteration(false);
+ }
+
+ /**
+ * Tests iteration over a sub-area in a single raster.
+ * This test iterates in the upper-right corner of the raster.
+ * Raster location and number of bands are different than other tests
(each test uses arbitrary values).
+ */
+ @Test
+ @DependsOnMethod("testOnRaster")
+ public void testOnRasterUpperRight() {
+ xmin = 11;
+ ymin = 12;
+ width = 10;
+ height = 11;
+ numBands = 1;
+ final Rectangle subArea = new Rectangle(16, 9, 10, 6);
+ createPixelIterator(createRaster(subArea), subArea);
+ assertTrue("Expected a non-empty set of values.", expected.length !=
0);
+ verifyIteration(false);
+ }
+
+ /**
+ * Tests iteration over a sub-area in a single raster.
+ * This test iterates in the lower-right corner of the raster.
+ * Raster location and number of bands are different than other tests
(each test uses arbitrary values).
+ */
+ @Test
+ @DependsOnMethod("testOnRaster")
+ public void testOnRasterLowerRight() {
+ xmin = -4;
+ ymin = -6;
+ width = 8;
+ height = 8;
+ numBands = 4;
+ final Rectangle subArea = new Rectangle(2, -2, 10, 12);
+ createPixelIterator(createRaster(subArea), subArea);
+ assertTrue("Expected a non-empty set of values.", expected.length !=
0);
+ verifyIteration(false);
+ }
+
+ /**
+ * Tests iteration over a sub-area in a single raster.
+ * This test iterates in the lower-left corner of the raster.
+ * Raster location and number of bands are different than other tests
(each test uses arbitrary values).
+ */
+ @Test
+ @DependsOnMethod("testOnRaster")
+ public void testOnRasterLowerLeft() {
+ xmin = 6;
+ ymin = 7;
+ width = 5;
+ height = 9;
+ numBands = 3;
+ final Rectangle subArea = new Rectangle(3, 10, 4, 9);
+ createPixelIterator(createRaster(subArea), subArea);
+ assertTrue("Expected a non-empty set of values.", expected.length !=
0);
+ verifyIteration(false);
+ }
+
+ /**
+ * Tests iteration over a sub-area in a single raster.
+ * Raster location and number of bands are different than other tests
(each test uses arbitrary values).
+ * This method tests also {@link PixelIterator#rewind()}.
+ */
+ @Test
+ @DependsOnMethod({"testOnRaster", "testRasterRewind"})
+ public void testOnRasterSubArea() {
+ xmin = 5;
+ ymin = 7;
+ width = 17;
+ height = 16;
+ numBands = 2;
+ final Rectangle subArea = new Rectangle(10, 9, 8, 6);
+ createPixelIterator(createRaster(subArea), subArea);
+ assertTrue("Expected a non-empty set of values.", expected.length !=
0);
+ verifyIteration(false);
+
+ iterator.rewind();
+ verifyIteration(false);
+ }
+
+ /**
+ * Tests iteration over a sub-area that actually contains all the raster
area.
+ * Raster location and number of bands are different than other tests
(each test uses arbitrary values).
+ * This method tests also {@link PixelIterator#rewind()}.
+ */
+ @Test
+ @DependsOnMethod({"testOnRaster", "testRasterRewind"})
+ public void testOnRasterFullArea() {
+ xmin = 7;
+ ymin = 9;
+ width = 11;
+ height = 17;
+ numBands = 3;
+ final Rectangle subArea = new Rectangle(2, 3, 25, 17);
+ createPixelIterator(createRaster(subArea), subArea);
+ assertTrue("Expected a non-empty set of values.", expected.length !=
0);
+ verifyIteration(true);
+
+ iterator.rewind();
+ verifyIteration(false);
+ }
+
+ /**
+ * Tests iteration over a sub-area that do not intersect the raster area.
+ * Raster location and number of bands are different than other tests
(each test uses arbitrary values).
+ * This method tests also {@link PixelIterator#rewind()}.
+ */
+ @Test
+ @DependsOnMethod({"testOnRaster", "testRasterRewind"})
+ public void testOnRasterEmptyArea() {
+ xmin = 6;
+ ymin = 5;
+ width = 3;
+ height = 2;
+ numBands = 3;
+ final Rectangle subArea = new Rectangle(-17, -20, 5, 15);
+ createPixelIterator(createRaster(subArea), subArea);
+ assertEquals("Expected an empty set of values.", 0, expected.length);
+ verifyIteration(true);
+
+ iterator.rewind();
+ verifyIteration(false);
+ }
+
+ /**
+ * Verifies that invoking {@link PixelIterator#moveTo(int, int)} with
illegal indices causes
+ * an exception to be thrown.
+ */
+ @Test
+ public void testIllegalMoveOnRaster() {
+ xmin = 4;
+ ymin = 7;
+ width = 5;
+ height = 4;
+ numBands = 1;
+ createPixelIterator(createRaster(null), null);
+ assertTrue("Expected a non-empty set of values.", expected.length !=
0);
+ try {
+ iterator.moveTo(2, 3);
+ fail("Expected IndexOutOfBoundsException.");
+ } catch (IndexOutOfBoundsException e) {
+ assertNotNull(e.getMessage());
+ }
+ try {
+ iterator.moveTo(9, 3);
+ fail("Expected IndexOutOfBoundsException.");
+ } catch (IndexOutOfBoundsException e) {
+ assertNotNull(e.getMessage());
+ }
+ try {
+ iterator.moveTo(2, 10);
+ fail("Expected IndexOutOfBoundsException.");
+ } catch (IndexOutOfBoundsException e) {
+ assertNotNull(e.getMessage());
+ }
+ }
+
+ /**
+ * Tests iteration over all pixels in a tiled image.
+ * Image location and number of bands are different than other tests (each
test uses arbitrary values).
+ * This method tests also {@link PixelIterator#rewind()}.
+ */
+ @Test
+ @DependsOnMethod("testOnRaster")
+ public void testOnImage() {
+ width = 24;
+ height = 15;
+ tileWidth = 8;
+ tileHeight = 5;
+ numBands = 3;
+ createPixelIterator(createImage(null), null);
+ assertEquals("getIterationOrder()", SequenceType.LINEAR,
iterator.getIterationOrder());
+ verifyIteration(false);
+ }
+
+ /**
+ * Tests iteration over all pixels in a tiled image.
+ * Image location and number of bands are different than other tests (each
test uses arbitrary values).
+ * This method tests also {@link PixelIterator#rewind()}.
+ */
+ @Test
+ @DependsOnMethod("testOnImage")
+ public void testImageRewind() {
+ xmin = 1;
+ ymin = -4;
+ width = 24;
+ height = 15;
+ tileWidth = 8;
+ tileHeight = 5;
+ numBands = 2;
+ createPixelIterator(createImage(null), null);
+ assertEquals("getIterationOrder()", SequenceType.LINEAR,
iterator.getIterationOrder());
+ verifyIteration(false);
+
+ iterator.rewind();
+ verifyIteration(false);
+ }
+
+ /**
+ * Tests iteration over a sub-area in a tiled image.
+ * This test iterates in the upper-left corner of the image.
+ * The sub-area is small enough for the iteration to happen in a single
tile.
+ * Image location and number of bands are different than other tests (each
test uses arbitrary values).
+ */
+ @Test
+ @DependsOnMethod({"testOnImage", "testOnRasterUpperLeft"})
+ public void testOnTileUpperLeft() {
+ xmin = -5;
+ ymin = 5;
+ width = 20;
+ height = 10;
+ tileWidth = 4;
+ tileHeight = 5;
+ numBands = 1;
+ minTileX = -1;
+ final Rectangle subArea = new Rectangle(-10, -20, 8, 28);
+ createPixelIterator(createImage(subArea), subArea);
+ assertTrue("Expected a non-empty set of values.", expected.length !=
0);
+ assertEquals("getIterationOrder()", SequenceType.LINEAR,
iterator.getIterationOrder());
+ verifyIteration(false);
+ }
+
+ /**
+ * Tests iteration over a sub-area in a tiled image.
+ * This test iterates in the upper-right corner of the image.
+ * The sub-area is small enough for the iteration to happen in a single
tile.
+ * Image location and number of bands are different than other tests (each
test uses arbitrary values).
+ */
+ @Test
+ @DependsOnMethod({"testOnImage", "testOnRasterUpperRight"})
+ public void testOnTileUpperRight() {
+ xmin = 35;
+ ymin = 7;
+ width = 15;
+ height = 12;
+ tileWidth = 5;
+ tileHeight = 3;
+ numBands = 3;
+ minTileY = -2;
+ final Rectangle subArea = new Rectangle(45, -20, 30, 29);
+ createPixelIterator(createImage(subArea), subArea);
+ assertTrue("Expected a non-empty set of values.", expected.length !=
0);
+ assertEquals("getIterationOrder()", SequenceType.LINEAR,
iterator.getIterationOrder());
+ verifyIteration(false);
+ }
+
+ /**
+ * Tests iteration over a sub-area in a tiled image.
+ * This test iterates in the lower-right corner of the image.
+ * The sub-area is small enough for the iteration to happen in a single
tile.
+ * Image location and number of bands are different than other tests (each
test uses arbitrary values).
+ */
+ @Test
+ @DependsOnMethod({"testOnImage", "testOnRasterLowerRight"})
+ public void testOnTileLowerRight() {
+ xmin = 55;
+ ymin = -7;
+ width = 18;
+ height = 16;
+ tileWidth = 6;
+ tileHeight = 4;
+ numBands = 2;
+ minTileX = 12;
+ minTileY = 20;
+ final Rectangle subArea = new Rectangle(68, 5, 4, 4);
+ createPixelIterator(createImage(subArea), subArea);
+ assertTrue("Expected a non-empty set of values.", expected.length !=
0);
+ assertEquals("getIterationOrder()", SequenceType.LINEAR,
iterator.getIterationOrder());
+ verifyIteration(false);
+ }
+
+ /**
+ * Tests iteration over a sub-area in a tiled image.
+ * This test iterates in the lower-left corner of the image.
+ * The sub-area is small enough for the iteration to happen in a single
tile.
+ * Image location and number of bands are different than other tests (each
test uses arbitrary values).
+ */
+ @Test
+ @DependsOnMethod({"testOnImage", "testOnRasterLowerLeft"})
+ public void testOnTileLowerLeft() {
+ xmin = 2;
+ ymin = -15;
+ width = 21;
+ height = 20;
+ tileWidth = 7;
+ tileHeight = 5;
+ numBands = 3;
+ minTileX = -12;
+ minTileY = 20;
+ final Rectangle subArea = new Rectangle(0, 0, 9, 50);
+ createPixelIterator(createImage(subArea), subArea);
+ assertTrue("Expected a non-empty set of values.", expected.length !=
0);
+ assertEquals("getIterationOrder()", SequenceType.LINEAR,
iterator.getIterationOrder());
+ verifyIteration(false);
+ }
+
+ /**
+ * Tests iteration over a sub-area in a tiled image.
+ * The sub-area is small enough for the iteration to happen in a single
tile.
+ */
+ @Test
+ @DependsOnMethod({"testOnImage", "testOnRasterSubArea"})
+ public void testOnTileSubArea() {
+ xmin = -5;
+ ymin = 7;
+ width = 15;
+ height = 24;
+ tileWidth = 5;
+ tileHeight = 6;
+ numBands = 2;
+ minTileX = 2;
+ minTileY = 3;
+ final Rectangle subArea = new Rectangle(6, 20, 4, 5);
+ createPixelIterator(createImage(subArea), subArea);
+ assertTrue("Expected a non-empty set of values.", expected.length !=
0);
+ assertEquals("getIterationOrder()", SequenceType.LINEAR,
iterator.getIterationOrder());
+ verifyIteration(false);
+ }
+
+ /**
+ * Tests iteration over a sub-area in a tiled image.
+ * This test iterates in the upper-left corner of the image.
+ * The sub-area is large enough for covering more than one tile.
+ */
+ @Test
+ @DependsOnMethod("testOnTileUpperLeft")
+ public void testOnImageUpperLeft() {
+ xmin = -5;
+ ymin = 5;
+ width = 27;
+ height = 20;
+ tileWidth = 9;
+ tileHeight = 5;
+ numBands = 3;
+ minTileX = 8;
+ minTileY = 8;
+ final Rectangle subArea = new Rectangle(-10, -5, 25, 22);
+ createPixelIterator(createImage(subArea), subArea);
+ assertTrue("Expected a non-empty set of values.", expected.length !=
0);
+ assertEquals("getIterationOrder()", SequenceType.LINEAR,
iterator.getIterationOrder());
+ verifyIteration(false);
+ }
+
+ /**
+ * Tests iteration over a sub-area in a tiled image.
+ * This test iterates in the upper-right corner of the image.
+ * The sub-area is large enough for covering more than one tile.
+ */
+ @Test
+ @DependsOnMethod("testOnTileUpperRight")
+ public void testOnImageUpperRight() {
+ xmin = 20;
+ ymin = 0;
+ width = 25;
+ height = 24;
+ tileWidth = 5;
+ tileHeight = 6;
+ numBands = 2;
+ minTileX = -2;
+ minTileY = 20;
+ final Rectangle subArea = new Rectangle(27, -20, 30, 37);
+ createPixelIterator(createImage(subArea), subArea);
+ assertTrue("Expected a non-empty set of values.", expected.length !=
0);
+ assertEquals("getIterationOrder()", SequenceType.LINEAR,
iterator.getIterationOrder());
+ verifyIteration(false);
+ }
+
+ /**
+ * Tests iteration over a sub-area in a tiled image.
+ * This test iterates in the lower-right corner of the image.
+ * The sub-area is large enough for covering more than one tile.
+ */
+ @Test
+ @DependsOnMethod("testOnTileLowerRight")
+ public void testOnImageLowerRight() {
+ xmin = 30;
+ ymin = 1;
+ width = 15;
+ height = 12;
+ tileWidth = 3;
+ tileHeight = 4;
+ numBands = 4;
+ minTileX = 1;
+ minTileY = 2;
+ final Rectangle subArea = new Rectangle(36, 8, 12, 20);
+ createPixelIterator(createImage(subArea), subArea);
+ assertTrue("Expected a non-empty set of values.", expected.length !=
0);
+ assertEquals("getIterationOrder()", SequenceType.LINEAR,
iterator.getIterationOrder());
+ verifyIteration(false);
+ }
+
+ /**
+ * Tests iteration over a sub-area in a tiled image.
+ * This test iterates in the lower-left corner of the image.
+ * The sub-area is large enough for covering more than one tile.
+ */
+ @Test
+ @DependsOnMethod("testOnTileLowerLeft")
+ public void testOnImageLowerLeft() {
+ xmin = -2;
+ ymin = -7;
+ width = 15;
+ height = 16;
+ tileWidth = 5;
+ tileHeight = 4;
+ numBands = 1;
+ minTileX = -9;
+ minTileY = -9;
+ final Rectangle subArea = new Rectangle(-20, -1, 30, 20);
+ createPixelIterator(createImage(subArea), subArea);
+ assertTrue("Expected a non-empty set of values.", expected.length !=
0);
+ assertEquals("getIterationOrder()", SequenceType.LINEAR,
iterator.getIterationOrder());
+ verifyIteration(false);
+ }
+
+ /**
+ * Tests iteration over a sub-area in a tiled image.
+ * The sub-area is large enough for covering more than one tile.
+ */
+ @Test
+ @DependsOnMethod("testOnTileSubArea")
+ public void testOnImageSubArea() {
+ xmin = -5;
+ ymin = 7;
+ width = 70;
+ height = 48;
+ tileWidth = 7;
+ tileHeight = 4;
+ numBands = 2;
+ minTileX = -12;
+ minTileY = -20;
+ final Rectangle subArea = new Rectangle(20, 10, 30, 25);
+ createPixelIterator(createImage(subArea), subArea);
+ assertTrue("Expected a non-empty set of values.", expected.length !=
0);
+ assertEquals("getIterationOrder()", SequenceType.LINEAR,
iterator.getIterationOrder());
+ verifyIteration(false);
+ }
+
+ /**
+ * Tests iteration over a all the region of a tiled image.
+ */
+ @Test
+ @DependsOnMethod({"testOnImage", "testOnRasterFullArea"})
+ public void testOnImageFullArea() {
+ xmin = -5;
+ ymin = -3;
+ width = 60;
+ height = 50;
+ tileWidth = 6;
+ tileHeight = 5;
+ numBands = 1;
+ minTileX = 999;
+ minTileY = -99;
+ final Rectangle subArea = new Rectangle(-10, -10, 150, 80);
+ createPixelIterator(createImage(subArea), subArea);
+ assertTrue("Expected a non-empty set of values.", expected.length !=
0);
+ assertEquals("getIterationOrder()", SequenceType.LINEAR,
iterator.getIterationOrder());
+ verifyIteration(false);
+ }
+
+ /**
+ * Tests iteration over a sub-area that do not intersect the image area.
+ */
+ @Test
+ @DependsOnMethod("testOnRasterEmptyArea")
+ public void testOnImageEmptyArea() {
+ xmin = 5;
+ ymin = 6;
+ width = 8;
+ height = 9;
+ tileWidth = 2;
+ tileHeight = 3;
+ numBands = 2;
+ final Rectangle subArea = new Rectangle(-100, -50, 5, 17);
+ createPixelIterator(createImage(subArea), subArea);
+ assertEquals("Expected an empty set of values.", 0, expected.length);
+ verifyIteration(true);
+ }
+
+ /**
+ * Verifies that invoking {@link PixelIterator#moveTo(int, int)} with
illegal indices causes
+ * an exception to be thrown.
+ */
+ @Test
+ @DependsOnMethod("testIllegalMoveOnRaster")
+ public void testIllegalMoveOnImage() {
+ xmin = 0;
+ ymin = 0;
+ width = 8;
+ height = 6;
+ tileWidth = 4;
+ tileHeight = 3;
+ numBands = 1;
+ createPixelIterator(createImage(null), null);
+ assertTrue("Expected a non-empty set of values.", expected.length !=
0);
+ try {
+ iterator.moveTo(102, 53);
+ fail("Expected IndexOutOfBoundsException.");
+ } catch (IndexOutOfBoundsException e) {
+ assertNotNull(e.getMessage());
+ }
+ }
+
+ /**
+ * Verifies that invoking {@link PixelIterator#next()} after iteration end
causes an exception to be thrown.
+ */
+ @Test
+ @DependsOnMethod("testOnImage")
+ public void testIllegalNext() {
+ xmin = -1;
+ ymin = 3;
+ width = 8;
+ height = 6;
+ tileWidth = 4;
+ tileHeight = 3;
+ numBands = 1;
+ createPixelIterator(createImage(null), null);
+ assertTrue("Expected a non-empty set of values.", expected.length !=
0);
+ verifyIteration(false);
+ try {
+ iterator.next();
+ fail("Expected IllegalStateException.");
+ } catch (IllegalStateException e) {
+ assertNotNull(e.getMessage());
+ }
+ }
+
+ /**
+ * Tests {@link PixelIterator#getPosition()}.
+ */
+ @Test
+ public void testGetPosition() {
+ xmin = 56;
+ ymin = 1;
+ width = 20;
+ height = 24;
+ tileWidth = 5;
+ tileHeight = 8;
+ numBands = 2;
+ minTileX = 10;
+ minTileY = 100;
+ createPixelIterator(createImage(null), null);
+ assertTrue("Expected a non-empty set of values.", expected.length !=
0);
+ int i = 0;
+ for (int y = ymin; y < ymin+height; y++) {
+ for (int x = xmin; x < xmin+width; x++) {
+ assertTrue(iterator.next());
+ final Point position = iterator.getPosition();
+ assertEquals("x", x, position.x);
+ assertEquals("y", y, position.y);
+ for (int b=0; b<numBands; b++) {
+ assertEquals(expected[i++], iterator.getSampleFloat(b),
0f);
+ }
+ }
+ }
+ assertEquals("Too few elements in iteration.", expected.length, i);
+ }
+
+ /**
+ * Moves the iterator to the given position and verifies the iteration.
+ * This method is used for implementation of {@code testMoveXXX()} methods.
+ *
+ * @see #verifyIteration(boolean)
+ * @see #verifyWindow(Dimension)
+ */
+ private void verifyIterationAfterMove(int x, int y) {
+ /*
+ * Move the iterator and verify location after the move.
+ */
+ iterator.moveTo(x, y);
+ final Point p = iterator.getPosition();
+ assertEquals("x", x, p.x);
+ assertEquals("y", y, p.y);
+ /*
+ * Compute index of the (x,y) position in the array of expected values.
+ * Iteration verification will need to begin at that value.
+ */
+ x -= xmin;
+ y -= ymin;
+ int i = y * width + x;
+ i *= numBands;
+ /*
+ * Iteration verification happens here. Note that contrarily to
'verifyIteration(boolean)' method,
+ * we use a do … while loop instead than a while loop because the call
to 'moveTo(x, y)' should be
+ * understood as an implicit 'next()' method call.
+ */
+ do {
+ for (int b=0; b<numBands; b++) {
+ verifySample(i++, b);
+ }
+ } while (iterator.next());
+ assertEquals("Too few elements in iteration.", expected.length, i);
+ }
+
+ /**
+ * Tests iteration after a call to {@link PixelIterator#moveTo(int, int)}
in a raster.
+ */
+ @Test
+ @DependsOnMethod("testOnRaster")
+ public void testMoveIntoRaster() {
+ xmin = 5;
+ ymin = 7;
+ width = 8;
+ height = 9;
+ numBands = 2;
+ createPixelIterator(createRaster(null), null);
+ verifyIterationAfterMove(8, 10);
+ }
+
+ /**
+ * Tests iteration after a call to {@link PixelIterator#moveTo(int, int)}
in a tiled image.
+ */
+ @Test
+ @DependsOnMethod({"testOnImage", "testMoveIntoRaster"})
+ public void testMoveIntoImage() {
+ xmin = -1;
+ ymin = 3;
+ width = 12;
+ height = 15;
+ tileWidth = 4;
+ tileHeight = 5;
+ numBands = 1;
+ minTileX = 120;
+ minTileY = 200;
+ createPixelIterator(createImage(null), null);
+ verifyIterationAfterMove(7, 5);
+ }
+
+ /**
+ * Verifies {@link PixelIterator#createWindow(TransferType)}.
+ * This method assumes that the iterator traverses the full image (no
sub-area).
+ *
+ * @see #verifyIteration(boolean)
+ * @see #verifyIterationAfterMove(int, int)
+ */
+ private void verifyWindow(final Dimension window) {
+ final PixelIterator.Window<FloatBuffer> w =
iterator.createWindow(TransferType.FLOAT);
+ final FloatBuffer values = w.values;
+ final int tileSize = tileWidth * tileHeight;
+ final int tileStride = tileSize * (width / tileWidth);
+ while (iterator.next()) {
+ final Point pos = iterator.getPosition();
+ pos.translate(-xmin, -ymin);
+ w.update();
+ for (int y=0; y<window.height; y++) {
+ int p,t;
+ p = pos.y + y;
+ t = p / tileHeight;
+ p %= tileHeight;
+ final int start = t * tileStride + p * tileWidth;
+ for (int x=0; x<window.width; x++) {
+ p = pos.x + x;
+ t = p / tileWidth;
+ p %= tileWidth;
+ int offset = (start + t * tileSize + p) * numBands;
+ for (int b=0; b<numBands; b++) {
+ final float e = expected[offset++];
+ final float a = values.get();
+ if (Float.floatToRawIntBits(a) !=
Float.floatToRawIntBits(e)) {
+ fail("Index (" + x + ", " + y + ") in window
starting at index ("
+ + pos.x + ", " + pos.y + "), band " + b +
": expected " + e + " but got " + a);
+ }
+ }
+ }
+ }
+ assertEquals("buffer.remaining()", 0, values.remaining());
+ }
+ }
+
+ /**
+ * Tests {@link PixelIterator#createWindow(TransferType)} on a single tile.
+ */
+ @Test
+ @DependsOnMethod("testMoveIntoImage")
+ public void testWindowOnTile() {
+ xmin = 1;
+ ymin = -2;
+ width = 8;
+ height = 10;
+ numBands = 2;
+ tileWidth = width;
+ tileHeight = height;
+ final Dimension window = new Dimension(3, 4);
+ createWindowIterator(createImage(null), window);
+ assertTrue("Expected a non-empty set of values.", expected.length !=
0);
+ assertEquals("getIterationOrder()", SequenceType.LINEAR,
iterator.getIterationOrder());
+ verifyWindow(window);
+ }
+
+ /**
+ * Tests {@link PixelIterator#createWindow(TransferType)} on a tiled image.
+ * TODO fix me
+ */
+ @Ignore
+ @Test
+ @DependsOnMethod("testWindowOnTile")
+ public void testWindowOnImage() {
+ xmin = 1;
+ ymin = -2;
+ width = 9;
+ height = 12;
+ tileWidth = 3;
+ tileHeight = 4;
+ numBands = 2;
+ minTileX = 100;
+ minTileY = 200;
+ final Dimension window = new Dimension(2, 3);
+ createWindowIterator(createImage(null), window);
+ assertTrue("Expected a non-empty set of values.", expected.length !=
0);
+ assertEquals("getIterationOrder()", SequenceType.LINEAR,
iterator.getIterationOrder());
+ verifyWindow(window);
+ }
+
+ /**
+ * Tests write operations in a single raster.
+ * The destination image is the same than the source image.
+ */
+ @Test
+ @DependsOnMethod("testOnRasterSubArea")
+ public void testOnWritableRaster() {
+ isWritable = true;
+ testOnRasterSubArea();
+ }
+
+ /**
+ * Tests write operations in a single tile of an image.
+ * The destination image is the same than the source image.
+ */
+ @Test
+ @DependsOnMethod({"testOnWritableRaster", "testOnTileSubArea"})
+ public void testOnWritableTile() {
+ isWritable = true;
+ testOnTileSubArea();
+ }
+
+ /**
+ * Tests write operations in a tiled image.
+ * The destination image is the same than the source image.
+ */
+ @Test
+ @DependsOnMethod({"testOnWritableTile", "testOnImageSubArea"})
+ public void testOnWritableImage() {
+ isWritable = true;
+ testOnImageSubArea();
+ }
+
+}
diff --git
a/core/sis-raster/src/test/java/org/apache/sis/test/suite/RasterTestSuite.java
b/core/sis-raster/src/test/java/org/apache/sis/test/suite/RasterTestSuite.java
index 8cdacd2..f6ad622 100644
---
a/core/sis-raster/src/test/java/org/apache/sis/test/suite/RasterTestSuite.java
+++
b/core/sis-raster/src/test/java/org/apache/sis/test/suite/RasterTestSuite.java
@@ -31,6 +31,7 @@ import org.junit.BeforeClass;
*/
@Suite.SuiteClasses({
org.apache.sis.image.DefaultIteratorTest.class,
+ org.apache.sis.image.LinearIteratorTest.class,
org.apache.sis.coverage.grid.PixelTranslationTest.class,
org.apache.sis.coverage.grid.GridExtentTest.class,
org.apache.sis.coverage.grid.GridGeometryTest.class,