use of org.apache.sis.coverage.grid.GridDerivation in project sis by apache.
the class CoverageReadConsistency method readAndCompareRandomRegions.
/**
* Implementation of methods testing reading in random sub-regions with random sub-samplings.
*
* @param label a label for the test being run.
* @throws DataStoreException if an error occurred while using the resource.
*/
private void readAndCompareRandomRegions(final String label) throws DataStoreException {
randomConfigureResource();
final GridGeometry gg = resource.getGridGeometry();
final int dimension = gg.getDimension();
final long[] low = new long[dimension];
final long[] high = new long[dimension];
final int[] subsampling = new int[dimension];
final int[] subOffsets = new int[dimension];
final int numBands = resource.getSampleDimensions().size();
/*
* We will collect statistics on execution time only if the
* test is executed in a more verbose mode than the default.
*/
final Statistics durations = (VERBOSE || !failOnMismatch) ? new Statistics(label) : null;
int failuresCount = 0;
for (int it = 0; it < numIterations; it++) {
final GridGeometry domain = randomDomain(gg, low, high, subsampling);
final int[] selectedBands = randomRange(numBands);
/*
* Read a coverage containing the requested sub-domain. Note that the reader is free to read
* more data than requested. The extent actually read is `actualReadExtent`. It shall contain
* fully the requested `domain`.
*/
final long startTime = System.nanoTime();
final GridCoverage subset = resource.read(domain, selectedBands);
final GridExtent actualReadExtent = subset.getGridGeometry().getExtent();
if (failOnMismatch) {
assertEquals("Unexpected number of dimensions.", dimension, actualReadExtent.getDimension());
for (int d = 0; d < dimension; d++) {
if (subsampling[d] == 1) {
assertTrue("Actual extent is too small.", actualReadExtent.getSize(d) > high[d] - low[d]);
assertTrue("Actual extent is too small.", actualReadExtent.getLow(d) <= low[d]);
assertTrue("Actual extent is too small.", actualReadExtent.getHigh(d) >= high[d]);
}
}
}
/*
* If subsampling was enabled, the factors selected by the reader may be different than
* the subsampling factors that we specified. The following block updates those values.
*/
if (allowSubsampling && full != null) {
final GridDerivation change = full.getGridGeometry().derive().subgrid(subset.getGridGeometry());
System.arraycopy(change.getSubsampling(), 0, subsampling, 0, dimension);
System.arraycopy(change.getSubsamplingOffsets(), 0, subOffsets, 0, dimension);
}
/*
* Iterate over all dimensions greater than 2. In the common case where we are reading a
* two-dimensional image, the following loop will be executed only once. If reading a 3D
* or 4D image, the loop is executed for all possible two-dimensional slices in the cube.
*/
final int sd = actualReadExtent.getDimension();
final long[] sliceMin = new long[sd];
final long[] sliceMax = new long[sd];
for (int i = 0; i < sd; i++) {
sliceMin[i] = actualReadExtent.getLow(i);
sliceMax[i] = actualReadExtent.getHigh(i);
}
nextSlice: for (; ; ) {
System.arraycopy(sliceMin, BIDIMENSIONAL, sliceMax, BIDIMENSIONAL, dimension - BIDIMENSIONAL);
final PixelIterator itr = iterator(full, sliceMin, sliceMax, subsampling, subOffsets, allowSubsampling);
final PixelIterator itc = iterator(subset, sliceMin, sliceMax, subsampling, subOffsets, false);
if (itr != null) {
assertEquals(itr.getDomain().getSize(), itc.getDomain().getSize());
final double[] expected = new double[selectedBands.length];
double[] reference = null, actual = null;
while (itr.next()) {
assertTrue(itc.next());
reference = itr.getPixel(reference);
actual = itc.getPixel(actual);
for (int i = 0; i < selectedBands.length; i++) {
expected[i] = reference[selectedBands[i]];
}
if (!Arrays.equals(expected, actual)) {
failuresCount++;
if (!failOnMismatch)
break;
final Point pr = itr.getPosition();
final Point pc = itc.getPosition();
final StringBuilder message = new StringBuilder(100).append("Mismatch at position (").append(pr.x).append(", ").append(pr.y).append(") in full image and (").append(pc.x).append(", ").append(pc.y).append(") in tested sub-image");
findMatchPosition(itr, pr, selectedBands, actual, message);
assertArrayEquals(message.toString(), expected, actual, STRICT);
/*
* POSSIBLE CAUSES FOR TEST FAILURE (known issues):
*
* - If the `GridGeometry` has no `gridToCRS` transform, then `GridDerivation` manages
* to save the scales (subsampling factors) anyway but the translations (subsampling
* offsets) are lost. It causes an image shift if the offsets were not zero. Because
* `gridToCRS` should never be null with spatial data, we do not complexify the code
* for what may be a non-issue.
*/
}
}
assertFalse(itc.next());
} else {
// Unable to create a reference image. Just check that no exception is thrown.
double[] actual = null;
while (itc.next()) {
actual = itc.getPixel(actual);
}
}
/*
* Move to the next two-dimensional slice and read again.
* We stop the loop after we have read all 2D slices.
*/
for (int d = dimension; --d >= BIDIMENSIONAL; ) {
if (sliceMin[d]++ <= actualReadExtent.getHigh(d))
continue nextSlice;
sliceMin[d] = actualReadExtent.getLow(d);
}
break;
}
if (durations != null) {
durations.accept((System.nanoTime() - startTime) / (double) StandardDateFormat.NANOS_PER_MILLISECOND);
}
}
/*
* Show statistics only if the test are executed with the `VERBOSE` flag set,
* or if this `CoverageReadConsistency` is used for benchmark.
*/
if (durations != null) {
if (statistics == null) {
statistics = new ArrayList<>();
}
statistics.add(durations);
final int totalCount = durations.count();
out.println("Number of failures: " + failuresCount + " / " + totalCount + " (" + (failuresCount / (totalCount / 100f)) + "%)");
}
}
use of org.apache.sis.coverage.grid.GridDerivation in project sis by apache.
the class RasterResource method read.
/**
* Loads a subset of the grid coverage represented by this resource.
*
* @param domain desired grid extent and resolution, or {@code null} for reading the whole domain.
* @param range 0-based indices of sample dimensions to read, or {@code null} or an empty sequence for reading them all.
* @return the grid coverage for the specified domain and range.
* @throws DataStoreException if an error occurred while reading the grid coverage data.
*/
@Override
public GridCoverage read(final GridGeometry domain, final int... range) throws DataStoreException {
final long startTime = System.nanoTime();
final RangeArgument rangeIndices = validateRangeArgument(ranges.length, range);
final Variable first = data[bandDimension >= 0 ? 0 : rangeIndices.getFirstSpecified()];
final DataType dataType = first.getDataType();
if (bandDimension < 0) {
for (int i = 0; i < rangeIndices.getNumBands(); i++) {
final Variable variable = data[rangeIndices.getSourceIndex(i)];
if (!dataType.equals(variable.getDataType())) {
throw new DataStoreContentException(Resources.forLocale(getLocale()).getString(Resources.Keys.MismatchedVariableType_3, getFilename(), first.getName(), variable.getName()));
}
}
}
/*
* At this point the arguments and the state of this resource have been validated.
* There is three ways to read the data, determined by `bandDimension` value:
*
* • (bandDimension < 0): one variable per band (usual case).
* • (bandDimension = 0): one variable containing all bands, with bands in the first dimension.
* • (bandDimension > 0): one variable containing all bands, with bands in the last dimension.
*/
final GridGeometry targetDomain;
final DataBuffer imageBuffer;
final SampleDimension[] bands = new SampleDimension[rangeIndices.getNumBands()];
// By default, all bands start at index 0.
int[] bandOffsets = null;
try {
final GridDerivation targetGeometry = gridGeometry.derive().rounding(GridRoundingMode.ENCLOSING).subgrid((domain != null) ? domain : gridGeometry);
// Pixel indices of data to read.
GridExtent areaOfInterest = targetGeometry.getIntersection();
// Slice to read or subsampling to apply.
int[] subsampling = targetGeometry.getSubsampling();
// By default, one variable per band.
int numBuffers = bands.length;
// Adjust user-specified domain to data geometry.
targetDomain = targetGeometry.build();
if (bandDimension >= 0) {
areaOfInterest = rangeIndices.insertBandDimension(areaOfInterest, bandDimension);
subsampling = rangeIndices.insertSubsampling(subsampling, bandDimension);
if (bandDimension == 0) {
// Will be set to non-zero values later.
bandOffsets = new int[numBuffers];
}
// One variable for all bands.
numBuffers = 1;
}
/*
* Iterate over netCDF variables in the order they appear in the file, not in the order requested
* by the `range` argument. The intent is to perform sequential I/O as much as possible, without
* seeking backward. In the (uncommon) case where bands are one of the variable dimension instead
* than different variables, the reading of the whole variable occurs during the first iteration.
*/
Buffer[] sampleValues = new Buffer[numBuffers];
synchronized (lock) {
for (int i = 0; i < bands.length; i++) {
// In strictly increasing order.
int indexInResource = rangeIndices.getSourceIndex(i);
int indexInRaster = rangeIndices.getTargetIndex(i);
Variable variable = getVariable(indexInResource);
SampleDimension b = ranges[indexInResource];
if (b == null) {
ranges[indexInResource] = b = createSampleDimension(rangeIndices.builder(), variable, i);
}
bands[indexInRaster] = b;
if (bandOffsets != null) {
bandOffsets[indexInRaster] = i;
// Pixels interleaved in one bank: sampleValues.length = 1.
indexInRaster = 0;
}
if (i < numBuffers)
try {
// Optional.orElseThrow() below should never fail since Variable.read(…) wraps primitive array.
sampleValues[indexInRaster] = variable.read(areaOfInterest, subsampling).buffer().get();
} catch (ArithmeticException e) {
throw variable.canNotComputePosition(e);
}
}
}
/*
* The following block is executed only if all bands are in a single variable, and the bands dimension is
* the last one (in "natural" order). In such case, the sample model to construct is a BandedSampleModel.
* Contrarily to PixelInterleavedSampleModel (the case when the band dimension is first), banded sample
* model force us to split the buffer in a buffer for each band.
*/
if (bandDimension > 0) {
// Really > 0, not >= 0.
final int stride = Math.toIntExact(data[0].getBandStride());
Buffer values = sampleValues[0].limit(stride);
sampleValues = new Buffer[bands.length];
for (int i = 0; i < sampleValues.length; i++) {
if (i != 0) {
values = JDK9.duplicate(values);
final int p = values.limit();
values.position(p).limit(Math.addExact(p, stride));
}
sampleValues[i] = values;
}
}
/*
* Convert NIO Buffer into Java2D DataBuffer. May throw various RuntimeException.
*/
imageBuffer = RasterFactory.wrap(dataType.rasterDataType, sampleValues);
} catch (IOException | RuntimeException e) {
throw canNotRead(getFilename(), domain, e);
}
/*
* At this point the I/O operation is completed and sample values have been stored in a NIO buffer.
* Provide to `Raster` all information needed for building a `RenderedImage` when requested.
*/
if (imageBuffer == null) {
throw new DataStoreContentException(Errors.getResources(getLocale()).getString(Errors.Keys.UnsupportedType_1, dataType.name()));
}
final Variable main = data[visibleBand];
final Raster raster = new Raster(targetDomain, UnmodifiableArrayList.wrap(bands), imageBuffer, String.valueOf(identifier), rangeIndices.getPixelStride(), bandOffsets, visibleBand, main.decoder.convention().getColors(main));
logReadOperation(location, targetDomain, startTime);
return raster;
}
use of org.apache.sis.coverage.grid.GridDerivation in project sis by apache.
the class CoverageSubset method clip.
/**
* Clips the given domain to the area of interest specified by the query. If any grid geometry is null,
* the other one is returned. The {@code domain} argument should be the domain to read as specified to
* {@link #read(GridGeometry, int...)}, or the full {@code CoverageSubset} domain if no value were given
* to the {@code read(…)} method.
*
* @param domain the domain requested in a read operation, or {@code null}.
* @param rounding whether to clip to nearest box or an enclosing box.
* @param clipping whether to clip the resulting extent to the specified {@code domain} extent.
* @return intersection of the given grid geometry with the query domain.
* @throws DataStoreException if the intersection can not be computed.
*/
private GridGeometry clip(final GridGeometry domain, final GridRoundingMode rounding, final GridClippingMode clipping) throws DataStoreException {
final GridGeometry areaOfInterest = query.getSelection();
if (domain == null)
return areaOfInterest;
if (areaOfInterest == null)
return domain;
try {
final GridDerivation derivation = domain.derive().rounding(rounding).clipping(clipping);
final int expansion = query.getSourceDomainExpansion();
if (expansion != 0) {
final int[] margins = new int[domain.getDimension()];
Arrays.fill(margins, expansion);
derivation.margin(margins);
}
return derivation.subgrid(areaOfInterest).build();
} catch (IllegalArgumentException | IllegalStateException e) {
final String msg = Resources.forLocale(getLocale()).getString(Resources.Keys.CanNotIntersectDataWithQuery_1, getSourceName());
final Throwable cause = e.getCause();
if (cause instanceof FactoryException || cause instanceof TransformException) {
throw new DataStoreReferencingException(msg, cause);
} else if (e instanceof DisjointExtentException) {
throw new NoSuchDataException(msg, e);
} else {
throw new DataStoreException(msg, e);
}
}
}
Aggregations