use of org.apache.sis.coverage.grid.GridExtent in project sis by apache.
the class MemoryGridResource method read.
/**
* Returns a subset of the wrapped grid coverage. If a non-null grid geometry is specified, then
* this method tries to return a grid coverage matching the given grid geometry on a best-effort basis.
* In current implementation this is either a {@link org.apache.sis.coverage.grid.GridCoverage2D} or
* the original grid coverage.
*
* @param domain desired grid extent and resolution, or {@code null} for 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.
*/
@Override
public GridCoverage read(GridGeometry domain, final int... range) {
List<SampleDimension> bands = coverage.getSampleDimensions();
final RangeArgument rangeIndices = validateRangeArgument(bands.size(), range);
/*
* The given `domain` may use arbitrary `gridToCRS` and `CRS` properties.
* For this simple implementation we need the same `gridToCRS` and `CRS`
* than the wrapped coverage; only domain `extent` is allowed to differ.
* Subsampling is ignored for now because it is an expensive operation.
* Clipping and range selection are light and do not copy any data.
*
* TODO: a future implementation may apply subsampling efficiently,
* by adjusting the pixel stride in SampleModel.
*/
GridExtent intersection = null;
final GridGeometry source = coverage.getGridGeometry();
if (domain == null) {
domain = source;
} else {
intersection = source.derive().rounding(GridRoundingMode.ENCLOSING).subgrid(domain).getIntersection();
if (intersection.equals(source.getExtent())) {
// Will request the whole image.
intersection = null;
domain = source;
}
}
/*
* Quick check before to invoke the potentially costly `coverage.render(…)` method.
*/
final boolean sameBands = rangeIndices.isIdentity();
if (sameBands && intersection == null) {
return coverage;
}
/*
* After `render(…)` execution, the (minX, minY) image coordinates are the differences between
* the extent that we requested and the one that we got. If that differences is not zero, then
* we need to translate the `GridExtent` in order to make it matches what we got. But before to
* apply that translation, we adjust the grid size (because it may add another translation).
*/
RenderedImage data = coverage.render(intersection);
if (intersection != null) {
final int[] sd = intersection.getSubspaceDimensions(2);
final int dimX = sd[0];
final int dimY = sd[1];
final long ox = intersection.getLow(dimX);
final long oy = intersection.getLow(dimY);
final long[] changes = new long[Math.max(dimX, dimY) + 1];
for (int i = changes.length; --i >= 0; ) {
// We need only the dimensions that may change.
changes[i] = intersection.getSize(i);
}
changes[dimX] = data.getWidth();
changes[dimY] = data.getHeight();
intersection = intersection.resize(changes);
/*
* Apply the translation after we resized the grid extent, because the resize operation
* may have caused an additional translation. We cancel that translation with terms that
* restore the (ox,oy) lower coordinates before to add the data minimum X,Y.
*/
Arrays.fill(changes, 0);
changes[dimX] = Math.addExact(ox - intersection.getLow(dimX), data.getMinX());
changes[dimY] = Math.addExact(oy - intersection.getLow(dimY), data.getMinX());
intersection = intersection.translate(changes);
/*
* If the result is the same intersection than the source coverage,
* we may be able to return that coverage directly.
*/
if (intersection.equals(source.getExtent())) {
if (sameBands) {
return coverage;
}
domain = source;
} else {
domain = new GridGeometry(intersection, PixelInCell.CELL_CORNER, source.getGridToCRS(PixelInCell.CELL_CORNER), source.getCoordinateReferenceSystem());
}
}
if (!sameBands) {
data = new ImageProcessor().selectBands(data, range);
bands = UnmodifiableArrayList.wrap(rangeIndices.select(bands));
}
return new GridCoverageBuilder().setValues(data).setRanges(bands).setDomain(domain).build();
}
use of org.apache.sis.coverage.grid.GridExtent in project sis by apache.
the class CoverageReadConsistency method randomDomain.
/**
* Creates a random domain to be used as a query on the {@link #resource} to test.
* All arrays given to this method will have their values overwritten.
*
* @param gg value of {@link GridCoverage#getGridGeometry()} on the resource to test.
* @param low pre-allocated array where to write the lower grid coordinates, inclusive.
* @param high pre-allocated array where to write the upper grid coordinates, inclusive.
* @param subsampling pre-allocated array where to write the subsampling.
*/
private GridGeometry randomDomain(final GridGeometry gg, final long[] low, final long[] high, final int[] subsampling) {
final GridExtent fullExtent = gg.getExtent();
final int dimension = fullExtent.getDimension();
for (int d = 0; d < dimension; d++) {
final int span = StrictMath.toIntExact(fullExtent.getSize(d));
// Span of the sub-region - 1.
final int rs = random.nextInt(span);
if (allowOffsets) {
// Note: (span - rs) > 0.
low[d] = random.nextInt(span - rs);
}
high[d] = low[d] + rs;
subsampling[d] = 1;
if (allowSubsampling) {
subsampling[d] += random.nextInt(StrictMath.max(rs / 16, 1));
}
}
return gg.derive().subgrid(new GridExtent(null, low, high, true), subsampling).build();
}
use of org.apache.sis.coverage.grid.GridExtent 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.GridExtent in project sis by apache.
the class GridResourceMock method read.
/**
* Returns a grid geometry wrapping a dummy image having exactly the requested size.
* The image will always be a {@link BufferedImage} with pixel coordinates starting at (0,0).
*
* @param domain desired grid extent and resolution, or {@code null} for the whole domain.
* @param range must be null, empty or a singleton containing only value 0.
* @return the grid coverage for the specified domain.
*/
@Override
public GridCoverage read(GridGeometry domain, final int... range) {
assertTrue(range == null || range.length == 0 || (range.length == 1 && range[0] == 0));
if (domain == null) {
domain = gridGeometry;
} else {
domain = gridGeometry.derive().subgrid(domain).build();
}
final GridExtent extent = domain.getExtent();
final BufferedImage img = new BufferedImage(StrictMath.toIntExact(extent.getSize(0)), StrictMath.toIntExact(extent.getSize(1)), BufferedImage.TYPE_BYTE_BINARY);
return new GridCoverage2D(domain, sampleDimensions, img);
}
use of org.apache.sis.coverage.grid.GridExtent in project sis by apache.
the class GridMapping method createGridCRS.
/**
* Creates a new grid geometry for the given extent.
* This method should be invoked only when no existing {@link GridGeometry} can be used as template.
*/
GridGeometry createGridCRS(final Variable variable) {
final List<Dimension> dimensions = variable.getGridDimensions();
final long[] upper = new long[dimensions.size()];
for (int i = 0; i < upper.length; i++) {
// Convert CRS dimension to netCDF dimension.
final int d = (upper.length - 1) - i;
upper[i] = dimensions.get(d).length();
}
return new GridGeometry(new GridExtent(null, null, upper, false), PixelInCell.CELL_CENTER, gridToCRS, crs);
}
Aggregations