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 7190f99 RasterResource needs to take subsampling in account when
computing the offset for reading a multi-banded variable.
7190f99 is described below
commit 7190f99935f9c4af79c438481c1ac9e3195843cb
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Thu Mar 21 21:21:13 2019 +0100
RasterResource needs to take subsampling in account when computing the
offset for reading a multi-banded variable.
---
.../java/org/apache/sis/math/MathFunctions.java | 6 +-
.../org/apache/sis/math/MathFunctionsTest.java | 7 +-
.../org/apache/sis/internal/netcdf/Raster.java | 2 +-
.../apache/sis/internal/netcdf/RasterResource.java | 16 ++--
.../sis/internal/storage/AbstractGridResource.java | 91 +++++++++++++++++++---
5 files changed, 98 insertions(+), 24 deletions(-)
diff --git
a/core/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java
b/core/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java
index 6269415..7b0e279 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java
@@ -861,9 +861,11 @@ testNextNumber: while (true) { // Simulate a
"goto" statement (usua
}
/**
- * Returns the divisors which are common to all the specified numbers.
+ * Returns the positive divisors which are common to all the specified
numbers.
+ * The returned array always starts with value 1, unless the given value
is 0
+ * in which case this method returns an empty array.
*
- * @param numbers the numbers for which to compute the divisors.
+ * @param numbers the numbers for which to compute the divisors, in any
order.
* @return the divisors common to all the given numbers, in strictly
increasing order.
*/
public static int[] commonDivisors(final int... numbers) {
diff --git
a/core/sis-utility/src/test/java/org/apache/sis/math/MathFunctionsTest.java
b/core/sis-utility/src/test/java/org/apache/sis/math/MathFunctionsTest.java
index cb0664a..5e6b35f 100644
--- a/core/sis-utility/src/test/java/org/apache/sis/math/MathFunctionsTest.java
+++ b/core/sis-utility/src/test/java/org/apache/sis/math/MathFunctionsTest.java
@@ -418,8 +418,9 @@ public final strictfp class MathFunctionsTest extends
TestCase {
@Test
@DependsOnMethod("testDivisors")
public void testCommonDivisors() {
- assertArrayEquals(new int[] {
- 1, 5
- }, commonDivisors(2000, 15));
+ assertArrayEquals(new int[] {1, 2, 4, 8}, commonDivisors(8));
+ assertArrayEquals(new int[] {1, 5}, commonDivisors(2000, 15));
+ assertArrayEquals(new int[] {1, 2}, commonDivisors(-4, 2));
+ assertArrayEquals(new int[] {}, commonDivisors(0));
}
}
diff --git
a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Raster.java
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Raster.java
index a916d5b..e65ded4 100644
---
a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Raster.java
+++
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Raster.java
@@ -50,7 +50,7 @@ final class Raster extends GridCoverage {
/**
* Creates a new raster from the given resource.
*/
- Raster(final GridGeometry domain, final List<SampleDimension> range, final
DataBuffer data, final String label) {
+ Raster(final GridGeometry domain, final List<SampleDimension> range, final
DataBuffer data, final int bandStride, final String label) {
super(domain, range);
this.data = data;
this.label = label;
diff --git
a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/RasterResource.java
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/RasterResource.java
index 3501ee1..6403def 100644
---
a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/RasterResource.java
+++
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/RasterResource.java
@@ -44,7 +44,6 @@ import org.apache.sis.math.MathFunctions;
import org.apache.sis.measure.MeasurementRange;
import org.apache.sis.measure.NumberRange;
import org.apache.sis.util.Numbers;
-import org.apache.sis.util.ArraysExt;
import org.apache.sis.util.resources.Errors;
import org.apache.sis.util.resources.Vocabulary;
@@ -444,7 +443,7 @@ public final class RasterResource extends
AbstractGridResource implements Resour
if (domain == null) {
domain = gridGeometry;
}
- final Variable first = data[rangeIndices.first()];
+ final Variable first = data[rangeIndices.getFirstSpecified()];
final DataType dataType = first.getDataType();
if (bandDimension < 0) {
for (int i=0; i<rangeIndices.getNumBands(); i++) {
@@ -473,8 +472,7 @@ public final class RasterResource extends
AbstractGridResource implements Resour
int[] subsamplings = scales;
if (bandDimension >= 0) {
areaOfInterest =
rangeIndices.insertBandDimension(areaOfInterest, bandDimension);
- subsamplings = ArraysExt.insert(subsamplings, bandDimension,
1);
- subsamplings[bandDimension] = 1;
+ subsamplings = rangeIndices.insertSubsampling
(subsamplings, bandDimension);
}
/*
* Iterate over netCDF variables in the order they appear in the
file, not in the order requested
@@ -499,7 +497,13 @@ public final class RasterResource extends
AbstractGridResource implements Resour
values = variable.read(areaOfInterest,
subsamplings).buffer().get();
}
if (bandDimension == 0) {
- values.position(r);
+ /*
+ * This block is executed only if the band dimension
is first, in which case we have interleaved
+ * values like (band0, band1) for each pixel. Those
values are read only once in above block.
+ * This block sets the offset of the first value to
read. The pixel stride is not specified here;
+ * it will be specified later, at
java.awt.image.SampleModel construction time.
+ */
+ values.position(rangeIndices.getSubsampledIndex(i));
}
final int p = rangeIndices.getTargetIndex(i);
sampleValues[p] = values;
@@ -522,7 +526,7 @@ public final class RasterResource extends
AbstractGridResource implements Resour
if (imageBuffer == null) {
throw new
DataStoreContentException(Errors.getResources(getLocale()).getString(Errors.Keys.UnsupportedType_1,
dataType.name()));
}
- return new Raster(domain, UnmodifiableArrayList.wrap(bands),
imageBuffer, first.getStandardName());
+ return new Raster(domain, UnmodifiableArrayList.wrap(bands),
imageBuffer, rangeIndices.getBandStride(), first.getStandardName());
}
/**
diff --git
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractGridResource.java
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractGridResource.java
index 5ffb2a9..defb835 100644
---
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractGridResource.java
+++
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractGridResource.java
@@ -24,9 +24,11 @@ import org.apache.sis.storage.GridCoverageResource;
import org.apache.sis.coverage.grid.GridGeometry;
import org.apache.sis.coverage.grid.GridExtent;
import org.apache.sis.coverage.SampleDimension;
+import org.apache.sis.math.MathFunctions;
import org.apache.sis.storage.Resource;
import org.apache.sis.util.logging.WarningListeners;
import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.ArraysExt;
import org.opengis.metadata.spatial.DimensionNameType;
@@ -150,6 +152,13 @@ public abstract class AbstractGridResource extends
AbstractResource implements G
private final long[] packed;
/**
+ * If a {@linkplain #insertSubsampling subsampling} has been applied,
indices of the first and last band
+ * to read, together with the interval (stride) between bands. Those
information are computed only when
+ * the {@code insertFoo(…)} methods are invoked.
+ */
+ private int first, last, interval;
+
+ /**
* A builder for sample dimensions, created when first needed.
*/
private SampleDimension.Builder builder;
@@ -159,6 +168,7 @@ public abstract class AbstractGridResource extends
AbstractResource implements G
*/
RangeArgument(final long[] packed) {
this.packed = packed;
+ interval = 1;
}
/**
@@ -171,6 +181,21 @@ public abstract class AbstractGridResource extends
AbstractResource implements G
}
/**
+ * Returns the value of the first index specified by the user. This is
not necessarily equal to
+ * {@code getBandIndex(0)} if the user specified the bands out of
order.
+ *
+ * @return index of the first value in the user-specified {@code
range} array.
+ */
+ public int getFirstSpecified() {
+ for (final long p : packed) {
+ if (((int) p) == 0) {
+ return (int) (p >>> Integer.SIZE);
+ }
+ }
+ throw new IllegalStateException(); // Should never
happen.
+ }
+
+ /**
* Returns the i<sup>th</sup> index of the band to read from the
resource.
* Indices are returned in strictly increasing order.
*
@@ -193,30 +218,72 @@ public abstract class AbstractGridResource extends
AbstractResource implements G
}
/**
- * Returns the value of the first index specified by the user. This is
not necessarily equal to
- * {@code getBandIndex(0)} if the user specified bands out of order.
+ * Returns the i<sup>th</sup> index of the band to read from the
resource, after subsampling has been applied.
+ * The subsampling results from calls to {@link
#insertBandDimension(GridExtent, int)} and
+ * {@link #insertSubsampling(int[], int)} methods.
*
- * @return index of the first value in the user-specified {@code
range} array.
+ * {@preformat java
+ * areaOfInterest =
rangeIndices.insertBandDimension(areaOfInterest, bandDimension);
+ * subsamplings = rangeIndices.insertSubsampling (subsamplings,
bandDimension);
+ * data = myReadMethod(areaOfInterest, subsamplings);
+ * for (int i=0; i<numBands; i++) {
+ * int bandIndexInTheDataWeJustRead = getSubsampledIndex(i);
+ * }
+ * }
+ *
+ * If the {@code insert} methods have never been invoked, then this
method is equivalent to {@link #getSourceIndex(int)}.
+ *
+ * @param i index of the range index to get, from 0 inclusive to
{@link #getNumBands()} exclusive.
+ * @return index of the i<sup>th</sup> band to read from the resource,
after subsampling.
*/
- public int first() {
- for (final long p : packed) {
- if (((int) p) == 0) {
- return (int) (p >>> Integer.SIZE);
- }
- }
- throw new IllegalStateException(); // Should never
happen.
+ public int getSubsampledIndex(final int i) {
+ return (getSourceIndex(i) - first) / interval;
+ }
+
+ /**
+ * Returns the increment to apply on index for moving to the next
pixel in the same band.
+ * If the {@code insert} methods have never been invoked, then this
method returns the number of bands.
+ *
+ * @return the increment to apply on index for moving to the next
pixel in the same band.
+ */
+ public int getBandStride() {
+ return (1 + last - first) / interval;
}
/**
* Returns the given extent with a new dimension added for the bands.
The extent in the new dimension
* will range from the minimum {@code range} value to the maximum
{@code range} value inclusive.
+ * This method should be used together with {@link
#insertSubsampling(int[], int)}.
*
* @param areaOfInterest the extent to which to add a new dimension
for bands.
* @param bandDimension index of the band dimension.
- * @return a new extent with the same value than the given extent plus
one dimension for bands.
+ * @return a new extent with the same values than the given extent
plus one dimension for bands.
*/
public GridExtent insertBandDimension(final GridExtent areaOfInterest,
final int bandDimension) {
- return areaOfInterest.insert(bandDimension, BAND,
getSourceIndex(0), getSourceIndex(packed.length - 1), true);
+ first = getSourceIndex(0);
+ last = getSourceIndex(packed.length - 1);
+ return areaOfInterest.insert(bandDimension, BAND, first, last,
true);
+ }
+
+ /**
+ * Returns the given subsampling with a new dimension added for the
bands. The subsampling in the new
+ * dimension will be the greatest common divisor of the difference
between all user-specified values.
+ * This method should be used together with {@link
#insertBandDimension(GridExtent, int)}.
+ *
+ * @param subsamplings the subsampling to which to add a new
dimension for bands.
+ * @param bandDimension index of the band dimension.
+ * @return a new subsampling array with the same values than the given
array plus one dimension for bands.
+ */
+ public int[] insertSubsampling(int[] subsamplings, final int
bandDimension) {
+ final int[] delta = new int[packed.length - 1];
+ for (int i=0; i<delta.length; i++) {
+ delta[i] = getSourceIndex(i+1) - getSourceIndex(i);
+ }
+ final int[] divisors = MathFunctions.commonDivisors(delta);
+ interval = (divisors.length != 0) ? divisors[divisors.length - 1]
: 1;
+ subsamplings = ArraysExt.insert(subsamplings, bandDimension, 1);
+ subsamplings[bandDimension] = interval;
+ return subsamplings;
}
/**