use of org.apache.sis.coverage.grid.GridExtent in project sis by apache.
the class RenderingData method setCoverageSpace.
/**
* Sets the input space (domain) and output space (ranges) of the coverage to be rendered.
* It should be followed by a call to {@link #ensureImageLoaded(GridCoverage, GridExtent)}.
*
* @param domain the two-dimensional grid geometry, or {@code null} if there is no data.
* @param ranges descriptions of bands, or {@code null} if there is no data.
*
* @see #isEmpty()
*/
@SuppressWarnings("AssignmentToCollectionOrArrayFieldFromParameter")
public final void setCoverageSpace(final GridGeometry domain, final List<SampleDimension> ranges) {
processor.setFillValues(SampleDimensions.backgrounds(ranges));
// Not cloned because already an unmodifiable list.
dataRanges = ranges;
dataGeometry = domain;
/*
* If the grid geometry does not define a "grid to CRS" transform, set it to an identity transform.
* We do that because this class needs a complete `GridGeometry` as much as possible.
*/
if (domain != null && !domain.isDefined(GridGeometry.GRID_TO_CRS) && domain.isDefined(GridGeometry.EXTENT)) {
CoordinateReferenceSystem crs = null;
if (domain.isDefined(GridGeometry.CRS)) {
crs = domain.getCoordinateReferenceSystem();
}
final GridExtent extent = domain.getExtent();
dataGeometry = new GridGeometry(extent, PixelInCell.CELL_CENTER, MathTransforms.identity(extent.getDimension()), crs);
}
}
use of org.apache.sis.coverage.grid.GridExtent in project sis by apache.
the class GridGeometryBuilder method build.
/**
* Creates the grid geometry and collect related metadata.
* This method shall be invoked exactly once after {@link #validateMandatoryTags()}.
* After this method call (if successful), the returned value is guaranteed non-null
* and can be used as a flag for determining that the build has been completed.
*
* @param width the image width in pixels.
* @param height the image height in pixels.
* @return the grid geometry, guaranteed non-null.
* @throws FactoryException if an error occurred while creating a CRS or a transform.
*/
@SuppressWarnings("fallthrough")
public GridGeometry build(final Reader reader, final long width, final long height) throws FactoryException {
CoordinateReferenceSystem crs = null;
if (keyDirectory != null) {
final CRSBuilder helper = new CRSBuilder(reader);
try {
crs = helper.build(this);
description = helper.description;
cellGeometry = helper.cellGeometry;
} catch (NoSuchIdentifierException | ParameterNotFoundException e) {
short key = Resources.Keys.UnsupportedProjectionMethod_1;
if (e instanceof NoSuchAuthorityCodeException) {
key = Resources.Keys.UnknownCRS_1;
}
reader.store.warning(reader.resources().getString(key, reader.store.getDisplayName()), e);
} catch (IllegalArgumentException | NoSuchElementException | ClassCastException e) {
if (!helper.alreadyReported) {
canNotCreate(reader, e);
}
}
}
/*
* If the CRS is non-null, then it is either two- or three-dimensional.
* The `affine` matrix may be for a greater number of dimensions, so it
* may need to be reduced.
*/
int n = (crs != null) ? crs.getCoordinateSystem().getDimension() : 2;
final DimensionNameType[] axisTypes = new DimensionNameType[n];
final long[] high = new long[n];
switch(n) {
// Fallthrough everywhere.
default:
axisTypes[2] = DimensionNameType.VERTICAL;
case 2:
axisTypes[1] = DimensionNameType.ROW;
high[1] = height - 1;
case 1:
axisTypes[0] = DimensionNameType.COLUMN;
high[0] = width - 1;
case 0:
break;
}
final GridExtent extent = new GridExtent(axisTypes, null, high, true);
boolean pixelIsPoint = CellGeometry.POINT.equals(cellGeometry);
final MathTransformFactory factory = DefaultFactories.forBuildin(MathTransformFactory.class);
GridGeometry gridGeometry;
try {
MathTransform gridToCRS;
if (affine != null) {
gridToCRS = factory.createAffineTransform(Matrices.resizeAffine(affine, ++n, n));
} else {
pixelIsPoint = true;
gridToCRS = Localization.nonLinear(modelTiePoints);
gridToCRS = factory.createPassThroughTransform(0, gridToCRS, n - 2);
}
gridGeometry = new GridGeometry(extent, pixelIsPoint ? PixelInCell.CELL_CENTER : PixelInCell.CELL_CORNER, gridToCRS, crs);
} catch (TransformException e) {
GeneralEnvelope envelope = null;
if (crs != null) {
envelope = new GeneralEnvelope(crs);
envelope.setToNaN();
}
gridGeometry = new GridGeometry(extent, envelope, GridOrientation.HOMOTHETY);
canNotCreate(reader, e);
/*
* Note: we catch TransformExceptions because they may be caused by erroneous data in the GeoTIFF file,
* but let FactoryExceptions propagate because they are more likely to be a SIS configuration problem.
*/
}
// Not needed anymore, so let GC do its work.
keyDirectory = null;
numericParameters = null;
asciiParameters = null;
modelTiePoints = null;
affine = null;
return gridGeometry;
}
use of org.apache.sis.coverage.grid.GridExtent in project sis by apache.
the class Grid method getExtent.
/**
* Builds the grid extent if the shape is available. The shape may not be available
* if a dimension has unlimited length. The dimension names are informative only.
*
* @param axes value of {@link #getAxes(Decoder)}. Element order does not matter for this method.
* @return the extent, or {@code null} if not available.
*/
@SuppressWarnings("fallthrough")
private GridExtent getExtent(final Axis[] axes) {
final List<Dimension> dimensions = getDimensions();
final int n = dimensions.size();
final long[] high = new long[n];
for (int i = 0; i < n; i++) {
final long length = dimensions.get(i).length();
if (length <= 0)
return null;
high[(n - 1) - i] = length;
}
final DimensionNameType[] names = new DimensionNameType[n];
switch(n) {
// Fall through
default:
names[1] = DimensionNameType.ROW;
// Fall through
case 1:
names[0] = DimensionNameType.COLUMN;
case 0:
break;
}
for (final Axis axis : axes) {
if (axis.getNumDimensions() == 1) {
final DimensionNameType name;
if (AxisDirections.isVertical(axis.direction)) {
name = DimensionNameType.VERTICAL;
} else if (AxisDirections.isTemporal(axis.direction)) {
name = DimensionNameType.TIME;
} else {
continue;
}
int dim = axis.gridDimensionIndices[0];
// Convert netCDF order to "natural" order.
dim = names.length - 1 - dim;
if (dim >= 0)
names[dim] = name;
}
}
return new GridExtent(names, null, high, false);
}
use of org.apache.sis.coverage.grid.GridExtent in project sis by apache.
the class MetadataBuilder method addSpatialRepresentation.
/**
* Adds and populates a "spatial representation info" node using the given grid geometry.
* This method invokes implicitly {@link #newGridRepresentation(GridType)}, unless this
* method returns {@code false} in which case nothing has been done.
* Storage locations are:
*
* <ul>
* <li>{@code metadata/spatialRepresentationInfo/transformationDimensionDescription}</li>
* <li>{@code metadata/spatialRepresentationInfo/transformationParameterAvailability}</li>
* <li>{@code metadata/spatialRepresentationInfo/axisDimensionProperties/dimensionName}</li>
* <li>{@code metadata/spatialRepresentationInfo/axisDimensionProperties/dimensionSize}</li>
* <li>{@code metadata/spatialRepresentationInfo/axisDimensionProperties/resolution}</li>
* <li>{@code metadata/identificationInfo/spatialRepresentationType}</li>
* <li>{@code metadata/referenceSystemInfo}</li>
* </ul>
*
* This method does not add the envelope provided by {@link GridGeometry#getEnvelope()}.
* That envelope appears in a separated node, which can be added by {@link #addExtent(Envelope)}.
* This separation is required by {@link AbstractGridResource} for instance.
*
* @param description a general description of the "grid to CRS" transformation, or {@code null} if none.
* Can also be specified later by a call to {@link #setGridToCRS(CharSequence)}.
* @param grid the grid extent, "grid to CRS" transform and target CRS, or {@code null} if none.
* @param addResolution whether to declare the resolutions. Callers should set this argument to {@code false} if they intend
* to provide the resolution themselves, or if grid axes are not in the same order than CRS axes.
* @return whether a "spatial representation info" node has been added.
*/
public final boolean addSpatialRepresentation(final String description, final GridGeometry grid, final boolean addResolution) {
final GridType type;
if (grid == null) {
if (description == null) {
return false;
}
type = GridType.UNSPECIFIED;
} else {
type = grid.isConversionLinear(0, 1) ? GridType.GEORECTIFIED : GridType.GEOREFERENCEABLE;
}
addSpatialRepresentation(SpatialRepresentationType.GRID);
newGridRepresentation(type);
setGridToCRS(description);
if (grid != null) {
setGeoreferencingAvailability(grid.isDefined(GridGeometry.GRID_TO_CRS), false, false);
CoordinateSystem cs = null;
if (grid.isDefined(GridGeometry.CRS)) {
final CoordinateReferenceSystem crs = grid.getCoordinateReferenceSystem();
cs = crs.getCoordinateSystem();
addReferenceSystem(crs);
}
if (grid.isDefined(GridGeometry.EXTENT)) {
final GridExtent extent = grid.getExtent();
final int dimension = extent.getDimension();
for (int i = 0; i < dimension; i++) {
final Optional<DimensionNameType> axisType = extent.getAxisType(i);
if (axisType.isPresent()) {
setAxisName(i, axisType.get());
}
setAxisSize(i, extent.getSize(i));
}
}
if (addResolution && grid.isDefined(GridGeometry.RESOLUTION)) {
final double[] resolution = grid.getResolution(false);
for (int i = 0; i < resolution.length; i++) {
setAxisResolution(i, resolution[i], (cs != null) ? cs.getAxis(i).getUnit() : null);
}
}
}
return true;
}
use of org.apache.sis.coverage.grid.GridExtent in project sis by apache.
the class TiledGridCoverage method render.
/**
* Returns a two-dimensional slice of grid data as a rendered image.
*
* @param sliceExtent a subspace of this grid coverage extent, or {@code null} for the whole image.
* @return the grid slice as a rendered image. Image location is relative to {@code sliceExtent}.
*/
@Override
public RenderedImage render(GridExtent sliceExtent) {
final GridExtent available = getGridGeometry().getExtent();
final int dimension = available.getDimension();
if (sliceExtent == null) {
sliceExtent = available;
} else {
final int sd = sliceExtent.getDimension();
if (sd != dimension) {
throw new MismatchedDimensionException(Errors.format(Errors.Keys.MismatchedDimension_3, "sliceExtent", dimension, sd));
}
}
final int[] selectedDimensions = sliceExtent.getSubspaceDimensions(BIDIMENSIONAL);
if (selectedDimensions[1] != 1) {
// TODO
throw new UnsupportedOperationException("Non-horizontal slices not yet implemented.");
}
final RenderedImage image;
try {
// Indices of first tile to read, inclusive.
final int[] tileLower = new int[dimension];
// Indices of last tile to read, exclusive.
final int[] tileUpper = new int[dimension];
// Pixel offset compared to Area Of Interest.
final int[] offsetAOI = new int[dimension];
// Subsampled image size.
final int[] imageSize = new int[dimension];
for (int i = 0; i < dimension; i++) {
// Lowest valid coordinate in subsampled image.
final long min = available.getLow(i);
// Highest valid coordinate, inclusive.
final long max = available.getHigh(i);
// Requested coordinate in subsampled image.
final long aoiMin = sliceExtent.getLow(i);
final long aoiMax = sliceExtent.getHigh(i);
final long tileUp = incrementExact(toTileMatrixCoordinate(Math.min(aoiMax, max), i));
final long tileLo = toTileMatrixCoordinate(Math.max(aoiMin, min), i);
if (tileUp <= tileLo) {
final String message = Errors.getResources(getLocale()).getString(Errors.Keys.IllegalRange_2, aoiMin, aoiMax);
if (aoiMin > aoiMax) {
throw new IllegalArgumentException(message);
} else {
throw new DisjointExtentException(message);
}
}
// Lower and upper coordinates in subsampled image, rounded to integer number of tiles and clipped to available data.
final long lower = /* inclusive */
Math.max(toSubsampledPixel(/* inclusive */
multiplyExact(tileLo, tileSize[i]), i), min);
final long upper = incrementExact(Math.min(toSubsampledPixel(decrementExact(multiplyExact(tileUp, tileSize[i])), i), max));
imageSize[i] = toIntExact(subtractExact(upper, lower));
offsetAOI[i] = toIntExact(subtractExact(lower, aoiMin));
tileLower[i] = toIntExact(subtractExact(tileLo, tmcOfFirstTile[i]));
tileUpper[i] = toIntExact(subtractExact(tileUp, tmcOfFirstTile[i]));
}
/*
* Prepare an iterator over all tiles to read, together with the following properties:
* - Two-dimensional conversion from pixel coordinates to "real world" coordinates.
*/
final AOI iterator = new AOI(tileLower, tileUpper, offsetAOI, dimension);
final Map<String, Object> properties = DeferredProperty.forGridGeometry(getGridGeometry(), selectedDimensions);
if (deferredTileReading) {
image = new TiledDeferredImage(imageSize, tileLower, properties, iterator);
} else {
/*
* If the loading strategy is not `RasterLoadingStrategy.AT_GET_TILE_TIME`, get all tiles
* in the area of interest now. I/O operations, if needed, happen in `readTiles(…)` call.
*/
final Raster[] result = readTiles(iterator);
image = new TiledImage(properties, colors, imageSize[X_DIMENSION], imageSize[Y_DIMENSION], tileLower[X_DIMENSION], tileLower[Y_DIMENSION], result);
}
} catch (Exception e) {
// Too many exception types for listing them all.
throw new CannotEvaluateException(Resources.forLocale(getLocale()).getString(Resources.Keys.CanNotRenderImage_1, getDisplayName()), e);
}
return image;
}
Aggregations