use of org.apache.sis.coverage.SampleDimension in project sis by apache.
the class MetadataBuilder method mergeMetadata.
/**
* Merge the given metadata into the metadata created by this builder.
* The given source should be an instance of {@link Metadata},
* but some types of metadata components are accepted as well.
*
* <p>This method should be invoked last, just before the call to {@link #build(boolean)}.
* Any identification information, responsible party, extent, coverage description, <i>etc.</i>
* added after this method call will be stored in new metadata object (not merged).</p>
*
* @param source the source metadata to merge. Will never be modified.
* @param locale the locale to use for error message in exceptions, or {@code null} for the default locale.
* @return {@code true} if the given source has been merged,
* or {@code false} if its type is not managed by this builder.
* @throws RuntimeException if the merge failed (may be {@link IllegalArgumentException},
* {@link ClassCastException}, {@link org.apache.sis.metadata.InvalidMetadataException}…)
*
* @see Merger
*/
public boolean mergeMetadata(final Object source, final Locale locale) {
flush();
final ModifiableMetadata target;
if (source instanceof Metadata)
target = metadata();
else if (source instanceof DataIdentification)
target = identification();
else if (source instanceof Citation)
target = citation();
else if (source instanceof Series)
target = series();
else if (source instanceof DefaultResponsibleParty)
target = responsibility();
else if (source instanceof AbstractParty)
target = party();
else if (source instanceof LegalConstraints)
target = constraints();
else if (source instanceof Extent)
target = extent();
else if (source instanceof AcquisitionInformation)
target = acquisition();
else if (source instanceof Platform)
target = platform();
else if (source instanceof FeatureCatalogueDescription)
target = featureDescription();
else if (source instanceof CoverageDescription)
target = coverageDescription();
else if (source instanceof DefaultAttributeGroup)
target = attributeGroup();
else if (source instanceof SampleDimension)
target = sampleDimension();
else if (source instanceof GridSpatialRepresentation)
target = gridRepresentation();
else if (source instanceof GCPCollection)
target = groundControlPoints();
else if (source instanceof Distribution)
target = distribution();
else if (source instanceof Format)
target = format();
else if (source instanceof Lineage)
target = lineage();
else if (source instanceof ProcessStep)
target = processStep();
else if (source instanceof Processing)
target = processing();
else
return false;
final Merger merger = new Merger(locale);
merger.copy(source, target);
return true;
}
use of org.apache.sis.coverage.SampleDimension 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.SampleDimension in project sis by apache.
the class RasterResource method getSampleDimensions.
/**
* Returns the ranges of sample values together with the conversion from samples to real values.
*
* @return ranges of sample values together with their mapping to "real values".
* @throws DataStoreException if an error occurred while reading definitions from the underlying data store.
*/
@Override
@SuppressWarnings("ReturnOfCollectionOrArrayField")
public List<SampleDimension> getSampleDimensions() throws DataStoreException {
SampleDimension.Builder builder = null;
try {
synchronized (lock) {
for (int i = 0; i < ranges.length; i++) {
if (ranges[i] == null) {
if (builder == null) {
builder = new SampleDimension.Builder();
}
ranges[i] = createSampleDimension(builder, getVariable(i), i);
builder.clear();
}
}
}
} catch (RuntimeException e) {
throw new DataStoreContentException(e);
}
return UnmodifiableArrayList.wrap(ranges);
}
use of org.apache.sis.coverage.SampleDimension 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.SampleDimension in project sis by apache.
the class BandRangeTable method getStringValue.
/**
* Invoked when the table needs to render a textual cell in the sample dimension table.
*/
private static ObservableValue<String> getStringValue(final CellDataFeatures<SampleDimension, String> cell) {
final Optional<?> text;
final SampleDimension sd = cell.getValue();
switch(cell.getTableColumn().getId()) {
case NAME:
text = Optional.ofNullable(sd.getName());
break;
case UNITS:
text = sd.getUnits();
break;
// Should not happen.
default:
throw new AssertionError();
}
return text.map(Object::toString).map(ReadOnlyObjectWrapper::new).orElse(null);
}
Aggregations