/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2007-2008, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotools.gce.geotiff;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import org.geotools.coverage.grid.GeneralGridEnvelope;
import org.geotools.coverage.grid.GridEnvelope2D;
import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.coverage.grid.io.AbstractGridFormat;
import org.geotools.coverage.grid.io.OverviewPolicy;
import org.geotools.data.DataSourceException;
import org.geotools.factory.Hints;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.metadata.iso.spatial.PixelTranslation;
import org.geotools.referencing.CRS;
import org.geotools.referencing.operation.LinearTransform;
import org.geotools.referencing.operation.builder.GridToEnvelopeMapper;
import org.geotools.referencing.operation.matrix.XAffineTransform;
import org.geotools.referencing.operation.transform.ProjectiveTransform;
import org.geotools.resources.geometry.XRectangle2D;
import org.opengis.geometry.BoundingBox;
import org.opengis.geometry.Envelope;
import org.opengis.geometry.MismatchedDimensionException;
import org.opengis.metadata.Identifier;
import org.opengis.parameter.GeneralParameterDescriptor;
import org.opengis.parameter.GeneralParameterValue;
import org.opengis.parameter.ParameterDescriptor;
import org.opengis.parameter.ParameterValue;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.ReferenceIdentifier;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.crs.SingleCRS;
import org.opengis.referencing.datum.PixelInCell;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.MathTransform2D;
import org.opengis.referencing.operation.NoninvertibleTransformException;
import org.opengis.referencing.operation.TransformException;
/**
* A class to handle coverage requests to a reader for a single 2D layer..
*
* @author Daniele Romagnoli, GeoSolutions
* @author Simone Giannecchini, GeoSolutions
*/
class RasterLayerRequest {
/** Logger. */
private final static Logger LOGGER = org.geotools.util.logging.Logging
.getLogger(RasterLayerRequest.class);
private ReadType readType = AbstractGridFormat.USE_JAI_IMAGEREAD.getDefaultValue() ? ReadType.JAI_IMAGEREAD
: ReadType.DIRECT_READ;
/** The {@link BoundingBox} requested */
private BoundingBox requestedBBox;
/** The {@link BoundingBox} of the portion of the coverage that intersects the requested bbox */
private BoundingBox cropBBox;
/** The desired overview Policy for this request */
private OverviewPolicy overviewPolicy;
/** The region where to fit the requested envelope */
private Rectangle requestedRasterArea;
/** The region of the */
private Rectangle destinationRasterArea;
/**
* Set to {@code true} if this request will produce an empty result, and the coverageResponse
* will produce a {@code null} coverage.
*/
private boolean empty;
private Color inputTransparentColor = AbstractGridFormat.INPUT_TRANSPARENT_COLOR.getDefaultValue();;
// private Color outputTransparentColor=JP2KFormat.OUTPUT_TRANSPARENT_COLOR.getDefaultValue();;
private AffineTransform requestedGridToWorld;
private double[] requestedResolution;
private double[] backgroundValues;
RasterManager rasterManager;
private MathTransform destinationToSourceTransform;
private GeneralEnvelope requestedBBOXInCoverageGeographicCRS;
private double[] requestedRasterScaleFactors;
private MathTransform requestCRSToCoverageGeographicCRS2D;
private GeneralEnvelope approximateRequestedBBoInNativeCRS;
private Dimension tileDimensions;
private SingleCRS requestCRS;
// private boolean multithreadingAllowed;
/**
* Build a new {@code CoverageRequest} given a set of input parameters.
*
* @param params
* The {@code GeneralParameterValue}s to initialize this request
* @param baseGridCoverage2DReader
* @throws DataSourceException
*/
public RasterLayerRequest(final GeneralParameterValue[] params,
final RasterManager rasterManager) throws DataSourceException {
// //
//
// Setting default parameters
//
// //
this.rasterManager = rasterManager;
setDefaultParameterValues();
// //
//
// Parsing parameter that can be used to control this request
//
// //
if (params != null) {
for (GeneralParameterValue gParam : params) {
if (gParam instanceof ParameterValue<?>) {
final ParameterValue<?> param = (ParameterValue<?>) gParam;
final ReferenceIdentifier name = param.getDescriptor().getName();
extractParameter(param, name);
}
}
}
// //
//
// Set specific imageIO parameters: type of read operation,
// imageReadParams
//
// //
checkReadType();
prepare();
}
private void setDefaultParameterValues() {
final ParameterValueGroup readParams = this.rasterManager.parent.getFormat()
.getReadParameters();
if (readParams == null) {
if (LOGGER.isLoggable(Level.FINE))
LOGGER.fine("No default values for the read parameters!");
return;
}
final List<GeneralParameterDescriptor> parametersDescriptors = readParams.getDescriptor()
.descriptors();
for (GeneralParameterDescriptor descriptor : parametersDescriptors) {
// we canc get the default vale only with the ParameterDescriptor class
if (!(descriptor instanceof ParameterDescriptor)) {
continue;
}
// get name and default value
final ParameterDescriptor desc = (ParameterDescriptor) descriptor;
final ReferenceIdentifier name = desc.getName();
final Object value = desc.getDefaultValue();
// //
//
// Requested GridGeometry2D parameter
//
// //
if (descriptor.getName().equals(AbstractGridFormat.READ_GRIDGEOMETRY2D.getName())) {
if (value == null)
continue;
final GridGeometry2D gg = (GridGeometry2D) value;
requestedBBox = new ReferencedEnvelope((Envelope) gg.getEnvelope2D());
requestedRasterArea = gg.getGridRange2D().getBounds();
requestedGridToWorld = (AffineTransform) gg.getGridToCRS2D();
continue;
}
// //
//
// Use JAI ImageRead parameter
//
// //
if (name.equals(AbstractGridFormat.USE_JAI_IMAGEREAD.getName())) {
if (value == null)
continue;
readType = ((Boolean) value) ? ReadType.JAI_IMAGEREAD : ReadType.DIRECT_READ;
continue;
}
// //
//
// Overview Policy parameter
//
// //
if (name.equals(AbstractGridFormat.OVERVIEW_POLICY.getName())) {
if (value == null)
continue;
overviewPolicy = (OverviewPolicy) value;
continue;
}
if (name.equals(AbstractGridFormat.INPUT_TRANSPARENT_COLOR.getName())) {
if (value == null)
continue;
inputTransparentColor = (Color) value;
// paranoiac check on the provided transparent color
inputTransparentColor = new Color(inputTransparentColor.getRed(),
inputTransparentColor.getGreen(), inputTransparentColor.getBlue());
continue;
}
// if (name.equals(
// GeoTiffFormat.OUTPUT_TRANSPARENT_COLOR.getName())) {
// if(value==null)
// continue;
// outputTransparentColor = (Color) value;
// // paranoiac check on the provided transparent color
// outputTransparentColor = new Color(
// outputTransparentColor.getRed(),
// outputTransparentColor.getGreen(),
// outputTransparentColor.getBlue());
// continue;
//
// }
//
if (name.equals(GeoTiffFormat.BACKGROUND_VALUES.getName())) {
if (value == null)
continue;
backgroundValues = (double[]) value;
continue;
}
//
// if (name.equals(ImageMosaicFormat.MAX_ALLOWED_TILES.getName())) {
// if(value==null)
// continue;
// maximumNumberOfGranules=(Integer)value;
// continue;
// }
//
//
// if (name.equals(GeoTiffFormat.ALLOW_MULTITHREADING.getName())) {
// if (value == null) {
// continue;
// }
// multithreadingAllowed = ((Boolean) value).booleanValue();
// continue;
// }
// //
//
// Suggested tile size parameter. It must be specified with
// the syntax: "TileWidth,TileHeight" (without quotes where TileWidth
// and TileHeight are integer values)
//
// //
if (name.equals(AbstractGridFormat.SUGGESTED_TILE_SIZE.getName())) {
final String suggestedTileSize = (String) value;
// Preliminary checks on parameter value
if ((suggestedTileSize != null) && (suggestedTileSize.trim().length() > 0)) {
if (suggestedTileSize.contains(AbstractGridFormat.TILE_SIZE_SEPARATOR)) {
final String[] tilesSize = suggestedTileSize
.split(AbstractGridFormat.TILE_SIZE_SEPARATOR);
if (tilesSize.length == 2) {
try {
// Getting suggested tile size
final int tileWidth = Integer.valueOf(tilesSize[0].trim());
final int tileHeight = Integer.valueOf(tilesSize[1].trim());
tileDimensions = new Dimension(tileWidth, tileHeight);
} catch (NumberFormatException nfe) {
if (LOGGER.isLoggable(Level.WARNING)) {
LOGGER.log(Level.WARNING, "Unable to parse "
+ "suggested tile size parameter");
}
}
}
}
}
}
}
}
/**
* Set proper fields from the specified input parameter.
*
* @param param
* the input {@code ParamaterValue} object
* @param name
* the name of the parameter
*/
private void extractParameter(ParameterValue<?> param, Identifier name) {
// //
//
// Requested GridGeometry2D parameter
//
// //
if (name.equals(AbstractGridFormat.READ_GRIDGEOMETRY2D.getName())) {
final Object value = param.getValue();
if (value == null) {
return;
}
final GridGeometry2D gg = (GridGeometry2D) param.getValue();
requestedBBox = new ReferencedEnvelope((Envelope) gg.getEnvelope2D());
requestedRasterArea = gg.getGridRange2D().getBounds();
requestedGridToWorld = (AffineTransform) gg.getGridToCRS2D();
return;
}
// //
//
// Use JAI ImageRead parameter
//
// //
if (name.equals(AbstractGridFormat.USE_JAI_IMAGEREAD.getName())) {
final Object value = param.getValue();
if (value == null) {
return;
}
readType = param.booleanValue() ? ReadType.JAI_IMAGEREAD : ReadType.DIRECT_READ;
return;
}
// //
//
// Overview Policy parameter
//
// //
if (name.equals(AbstractGridFormat.OVERVIEW_POLICY.getName())) {
final Object value = param.getValue();
if (value == null) {
return;
}
overviewPolicy = (OverviewPolicy) param.getValue();
return;
}
if (name.equals(AbstractGridFormat.INPUT_TRANSPARENT_COLOR.getName())) {
final Object value = param.getValue();
if (value == null) {
return;
}
inputTransparentColor = (Color) param.getValue();
// paranoiac check on the provided transparent color
inputTransparentColor = new Color(inputTransparentColor.getRed(),
inputTransparentColor.getGreen(), inputTransparentColor.getBlue());
return;
}
//
// if (name.equals(ImageMosaicFormat.FADING.getName())) {
// final Object value = param.getValue();
// if(value==null)
// return;
// blend = ((Boolean) param.getValue()).booleanValue();
// return;
//
// }
// if (name.equals(
// GeoTiffFormat.OUTPUT_TRANSPARENT_COLOR.getName())) {
// final Object value = param.getValue();
// if(value==null)
// return;
// outputTransparentColor = (Color) param.getValue();
// // paranoiac check on the provided transparent color
// outputTransparentColor = new Color(
// outputTransparentColor.getRed(),
// outputTransparentColor.getGreen(),
// outputTransparentColor.getBlue());
// return;
//
// }
//
if (name.equals(GeoTiffFormat.BACKGROUND_VALUES.getName())) {
final Object value = param.getValue();
if (value == null) {
return;
}
backgroundValues = (double[]) param.getValue();
return;
}
//
// if (name.equals(ImageMosaicFormat.MAX_ALLOWED_TILES.getName())) {
// final Object value = param.getValue();
// if(value==null)
// return;
// maximumNumberOfGranules=param.intValue();
// return;
// }
//
//
// if (name.equals(GeoTiffFormat.ALLOW_MULTITHREADING.getName())) {
// final Object value = param.getValue();
// if (value == null) {
// return;
// }
// multithreadingAllowed = ((Boolean) param.getValue()).booleanValue();
// return;
// }
// //
//
// Suggested tile size parameter. It must be specified with
// the syntax: "TileWidth,TileHeight" (without quotes where TileWidth
// and TileHeight are integer values)
//
// //
if (name.equals(AbstractGridFormat.SUGGESTED_TILE_SIZE.getName())) {
final String suggestedTileSize = (String) param.getValue();
// Preliminary checks on parameter value
if ((suggestedTileSize != null) && (suggestedTileSize.trim().length() > 0)) {
if (suggestedTileSize.contains(AbstractGridFormat.TILE_SIZE_SEPARATOR)) {
final String[] tilesSize = suggestedTileSize
.split(AbstractGridFormat.TILE_SIZE_SEPARATOR);
if (tilesSize.length == 2) {
try {
// Getting suggested tile size
final int tileWidth = Integer.valueOf(tilesSize[0].trim());
final int tileHeight = Integer.valueOf(tilesSize[1].trim());
tileDimensions = new Dimension(tileWidth, tileHeight);
} catch (NumberFormatException nfe) {
if (LOGGER.isLoggable(Level.WARNING)) {
LOGGER.log(Level.WARNING, "Unable to parse "
+ "suggested tile size parameter");
}
}
}
}
}
}
}
/**
* Compute this specific request settings all the parameters needed by a visiting
* {@link RasterLayerResponse} object.
*
* @throws DataSourceException
*/
private void prepare() throws DataSourceException {
//
// DO WE HAVE A REQUESTED AREA?
//
// Check if we have something to load by intersecting the
// requested envelope with the bounds of this data set.
//
if (requestedBBox == null) {
//
// In case we have nothing to look at we should get the whole coverage
//
requestedBBox = rasterManager.domainManager.coverageBBox;
cropBBox = rasterManager.domainManager.coverageBBox;
requestedRasterArea = (Rectangle) rasterManager.domainManager.coverageRasterArea.clone();
destinationRasterArea = (Rectangle) rasterManager.domainManager.coverageRasterArea.clone();
requestedResolution = rasterManager.domainManager.coverageFullResolution.clone();
// TODO harmonize the various types of transformations
requestedGridToWorld = (AffineTransform) rasterManager.domainManager.coverageGridToWorld2D;
return;
}
//
// Adjust requested bounding box and source region in order to fall within the source
// coverage
//
computeRequestSpatialElements();
}
private void inspectCoordinateReferenceSystems() throws DataSourceException {
// get the crs for the requested bbox
requestCRS = CRS.getHorizontalCRS(requestedBBox.getCoordinateReferenceSystem());
//
// Check if the request CRS is different from the coverage native CRS
//
if (!CRS.equalsIgnoreMetadata(requestCRS, rasterManager.domainManager.coverageCRS2D))
try {
destinationToSourceTransform = CRS.findMathTransform(requestCRS,
rasterManager.domainManager.coverageCRS2D, true);
} catch (FactoryException e) {
throw new DataSourceException("Unable to inspect request CRS", e);
}
// now transform the requested envelope to source crs
if (destinationToSourceTransform != null && destinationToSourceTransform.isIdentity()) {
destinationToSourceTransform = null;// the CRS is basically the same
} else if (destinationToSourceTransform instanceof AffineTransform) {
//
// k, the transformation between the various CRS is not null or the
// Identity, let's see if it is an affine transform, which case we
// can incorporate it into the requested grid to world
//
// we should not have any problems with regards to BBOX reprojection
// update the requested grid to world transformation by pre concatenating the destination to source transform
AffineTransform mutableTransform = (AffineTransform) requestedGridToWorld.clone();
mutableTransform.preConcatenate((AffineTransform) destinationToSourceTransform);
// update the requested envelope
try {
final MathTransform tempTransform = PixelTranslation.translate(
ProjectiveTransform.create(mutableTransform), PixelInCell.CELL_CENTER,
PixelInCell.CELL_CORNER);
requestedBBox = new ReferencedEnvelope(CRS.transform(tempTransform,
new GeneralEnvelope(requestedRasterArea)));
} catch (MismatchedDimensionException e) {
throw new DataSourceException("Unable to inspect request CRS", e);
} catch (TransformException e) {
throw new DataSourceException("Unable to inspect request CRS", e);
}
// now clean up all the traces of the transformations
destinationToSourceTransform = null;
}
}
/**
* Check the type of read operation which will be performed and return {@code true} if a JAI
* imageRead operation need to be performed or {@code false} if a simple read operation is
* needed.
*
* @return {@code true} if the read operation will use a JAI ImageRead operation instead of a
* simple {@code ImageReader.read(...)} call.
*/
private void checkReadType() {
if (readType != ReadType.UNSPECIFIED) {
return;
}
// //
//
// Ok, the request did not explicitly set the read type, let's check the
// hints.
//
// //
final Hints hints = rasterManager.getHints();
if (hints != null) {
final Object o = hints.get(Hints.USE_JAI_IMAGEREAD);
if (o != null) {
readType = (ReadType) o;
return;
}
}
// //
//
// Last chance is to use the default read type.
//
// //
readType = ReadType.getDefault();
}
/**
* Return a crop region from a specified envelope, leveraging on the grid to world
* transformation.
*
* @param refinedRequestedBBox
* the crop envelope
* @return a {@code Rectangle} representing the crop region.
* @throws TransformException
* in case a problem occurs when going back to raster space.
* @throws DataSourceException
*/
private void computeCropRasterArea() throws DataSourceException {
// we have nothing to crop
if (cropBBox == null) {
destinationRasterArea = null;
return;
}
//
// We need to invert the requested gridToWorld and then adjust the requested raster area are
// accordingly
//
// invert the requested grid to world keeping into account the fact that it is related to
// cell center
// while the raster is related to cell corner
MathTransform2D requestedWorldToGrid;
try {
requestedWorldToGrid = (MathTransform2D) PixelTranslation.translate(
ProjectiveTransform.create(requestedGridToWorld), PixelInCell.CELL_CENTER,
PixelInCell.CELL_CORNER).inverse();
} catch (NoninvertibleTransformException e) {
throw new DataSourceException(e);
}
if (destinationToSourceTransform == null || destinationToSourceTransform.isIdentity()) {
// now get the requested bbox which have been already adjusted and project it back to
// raster space
try {
destinationRasterArea = new GeneralGridEnvelope(CRS.transform(requestedWorldToGrid,
new GeneralEnvelope(cropBBox)), PixelInCell.CELL_CORNER, false)
.toRectangle();
} catch (IllegalStateException e) {
throw new DataSourceException(e);
} catch (TransformException e) {
throw new DataSourceException(e);
}
} else {
//
// reproject the crop bbox back and then crop, notice that we are imposing
//
try {
final GeneralEnvelope cropBBOXInRequestCRS = CRS.transform(
destinationToSourceTransform.inverse(), cropBBox);
cropBBOXInRequestCRS.setCoordinateReferenceSystem(requestedBBox
.getCoordinateReferenceSystem());
// make sure it falls within the requested envelope
cropBBOXInRequestCRS.intersect(requestedBBox);
// now go back to raster space
destinationRasterArea = new GeneralGridEnvelope(CRS.transform(requestedWorldToGrid,
cropBBOXInRequestCRS), PixelInCell.CELL_CORNER, false).toRectangle();
// intersect with the original requested raster space to be sure that we stay within
// the requested raster area
XRectangle2D.intersect(destinationRasterArea, requestedRasterArea,
destinationRasterArea);
} catch (NoninvertibleTransformException e) {
throw new DataSourceException(e);
} catch (TransformException e) {
throw new DataSourceException(e);
}
}
// is it empty??
if (destinationRasterArea.isEmpty()) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "Requested envelope too small resulting in empty cropped " +
"raster region");
}
// TODO: Future versions may define a 1x1 rectangle starting
// from the lower coordinate
empty = true;
return;
}
}
/**
* Evaluates the requested envelope and builds a new adjusted version of it fitting this
* coverage envelope.
*
* <p>
* While adjusting the requested envelope this methods also compute the source region as a
* rectangle which is suitable for a successive read operation with {@link ImageIO} to do
* crop-on-read.
*
*
* @param requestedBBox
* is the envelope we are requested to load.
* @param destinationRasterArea
* represents the area to load in raster space. This parameter cannot be null since
* it gets filled with whatever the crop region is depending on the
* <code>requestedEnvelope</code>.
* @param requestedRasterArea
* is the requested region where to load data of the specified envelope.
* @param readGridToWorld
* the Grid to world transformation to be used
* @return the adjusted requested envelope, empty if no requestedEnvelope has been specified,
* {@code null} in case the requested envelope does not intersect the coverage envelope
* or in case the adjusted requested envelope is covered by a too small raster region
* (an empty region).
* @throws DataSourceException
*
* @throws DataSourceException
* in case something bad occurs
*/
private void computeRequestSpatialElements() throws DataSourceException {
//
// Inspect the request and precompute transformation between CRS. We
// also check if we can simply adjust the requested GG in case the
// request CRS is different from the coverage native CRS but the
// transformation is simpl ean affine transformation.
//
// In such a case we can simplify our work by adjusting the
// requested grid to world, preconcatenating the coordinate
// operation to change CRS
//
inspectCoordinateReferenceSystems();
//
// WE DO HAVE A REQUESTED AREA!
//
//
// Create the crop bbox in the coverage CRS for cropping it later
// on.
//
computeCropBBOX();
if (empty || (cropBBox != null && cropBBox.isEmpty())) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "RequestedBBox empty or null");
}
// this means that we do not have anything to load at all!
empty = true;
return;
}
//
// CROP SOURCE REGION using the refined requested envelope
//
computeCropRasterArea();
if (empty || (destinationRasterArea != null && destinationRasterArea.isEmpty())) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "CropRasterArea empty or null");
}
// this means that we do not have anything to load at all!
return;
}
if (LOGGER.isLoggable(Level.FINE)) {
final String loggedMessage = "Adjusted Requested Envelope = " + requestedBBox.toString() + "\n" +
"Requested raster dimension = " + requestedRasterArea.toString() + "\n" +
"Corresponding raster source region = " + requestedRasterArea.toString();
LOGGER.log(Level.FINE, loggedMessage);
}
//
// Compute the request resolution from the request
//
computeRequestedResolution();
}
/**
* Computes the requested resolution which is going to be used for selecting overviews and or
* deciding decimation factors on the target coverage.
*
* <p>
* In case the requested envelope is in the same {@link CoordinateReferenceSystem} of the
* coverage we compute the resolution using the requested {@link MathTransform}. Notice that it
* must be a {@link LinearTransform} or else we fail.
*
* <p>
* In case the requested envelope is not in the same {@link CoordinateReferenceSystem} of the
* coverage we
*
* @throws DataSourceException
* in case something bad happens during reprojections and/or intersections.
*/
private void computeRequestedResolution() throws DataSourceException {
try {
// let's try to get the resolution from the requested gridToWorld
if (requestedGridToWorld instanceof LinearTransform) {
//
// the crs of the request and the one of the coverage are NOT the
// same and the conversion is not , we can get the resolution from envelope + raster
// directly
//
if (destinationToSourceTransform != null
&& !destinationToSourceTransform.isIdentity()) {
//
// compute the approximated resolution in the request crs, notice that we are
// assuming a reprojection that keeps the raster area unchanged hence
// the effect is a degradation of quality, but we take that into account
// emprically
//
requestedResolution = null;
// compute the raster that correspond to the crop bbox at the highest resolution
final Rectangle sourceRasterArea = new GeneralGridEnvelope(CRS.transform(
PixelTranslation.translate(rasterManager.getRaster2Model(),
PixelInCell.CELL_CENTER, PixelInCell.CELL_CORNER).inverse(),
cropBBox), PixelInCell.CELL_CORNER, false).toRectangle();
XRectangle2D.intersect(sourceRasterArea,
rasterManager.domainManager.coverageRasterArea, sourceRasterArea);
if (sourceRasterArea.isEmpty())
throw new DataSourceException("aaa");
// transform the crop bbox to the request model space
final GeneralEnvelope envelope = CRS.transform(
destinationToSourceTransform.inverse(), cropBBox);
final GridToEnvelopeMapper geMapper = new GridToEnvelopeMapper(
new GridEnvelope2D(sourceRasterArea), envelope);
final AffineTransform tempTransform = geMapper.createAffineTransform();
final double scaleX = XAffineTransform
.getScaleX0((AffineTransform) requestedGridToWorld)
/ XAffineTransform.getScaleX0(tempTransform);
final double scaleY = XAffineTransform
.getScaleY0((AffineTransform) requestedGridToWorld)
/ XAffineTransform.getScaleY0(tempTransform);
//
// empiric adjustment to get a finer resolution to have better quality when
// reprojecting
// TODO make it parametric
//
requestedRasterScaleFactors = new double[2];
requestedRasterScaleFactors[0] = scaleX * 1.0;
requestedRasterScaleFactors[1] = scaleY * 1.0;
// adjust the final grid to world
final GridToEnvelopeMapper geMapper1 = new GridToEnvelopeMapper(
new GridEnvelope2D(destinationRasterArea), cropBBox);
requestedGridToWorld = geMapper1.createAffineTransform();
} else {
//
// the crs of the request and the one of the coverage are the
// same, we can get the resolution from the grid to world
//
// if(requestedGridToWorld instanceof AffineTransform){
requestedResolution = new double[] {
XAffineTransform.getScaleX0(requestedGridToWorld),
XAffineTransform.getScaleY0(requestedGridToWorld) };
// }
// else{
// // get the matrix
// final Matrix matrix= ((LinearTransform)requestedGridToWorld).getMatrix();
// final XAffineTransform transform=new XAffineTransform(
// matrix.getElement(0, 0),
// matrix.getElement(1, 0),
// matrix.getElement(0, 1),
// matrix.getElement(1, 1),
// matrix.getElement(0, 2),
// matrix.getElement(1, 2));
// requestedResolution= new double[]
// {
// XAffineTransform.getScaleX0(transform),
// XAffineTransform.getScaleY0(transform)
// };
// }
//
}
}
// leave
return;
} catch (Throwable e) {
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.log(Level.INFO, "Unable to compute requested resolution", e);
}
}
//
// use the coverage resolution since we cannot compute the requested one
//
LOGGER.log(Level.WARNING, "Unable to compute requested resolution, using highest available");
requestedResolution = rasterManager.domainManager.coverageFullResolution;
}
private void computeCropBBOX() throws DataSourceException {
// get the crs for the requested bbox
if(requestCRS == null)
requestCRS = CRS.getHorizontalCRS(requestedBBox.getCoordinateReferenceSystem());
try {
//
// If this approach succeeds, either the request crs is the same of
// the coverage crs or the request bbox can be reprojected to that
// crs
//
// STEP 1: reproject requested BBox to native CRS if needed
// now transform the requested envelope to source crs
if (destinationToSourceTransform != null && !destinationToSourceTransform.isIdentity()) {
final GeneralEnvelope temp = CRS.transform(destinationToSourceTransform,
requestedBBox);
temp.setCoordinateReferenceSystem(rasterManager.domainManager.coverageCRS2D);
cropBBox = new ReferencedEnvelope(temp);
} else {
// we do not need to do anything, but we do this in order to aboid problems with the
// envelope checks
cropBBox = new ReferencedEnvelope(requestedBBox.getMinX(), requestedBBox.getMaxX(),
requestedBBox.getMinY(), requestedBBox.getMaxY(),
rasterManager.domainManager.coverageCRS2D);
}
//
// STEP 2: intersect requested BBox in native CRS with coverage native bbox to get the
// crop bbox
//
// intersect the requested area with the bounds of this
// layer in native crs
if (!cropBBox.intersects((BoundingBox) rasterManager.domainManager.coverageBBox)) {
cropBBox = null;
empty = true;
return;
}
// TODO XXX Optimize when referenced envelope has intersection method that actually
// retains the CRS, this is the JTS one
cropBBox = new ReferencedEnvelope(
((ReferencedEnvelope) cropBBox)
.intersection(rasterManager.domainManager.coverageBBox),
rasterManager.domainManager.coverageCRS2D);
return;
} catch (TransformException te) {
// something bad happened while trying to transform this
// envelope. let's try with wgs84
if (LOGGER.isLoggable(Level.FINE))
LOGGER.log(Level.FINE, te.getLocalizedMessage(), te);
}
try {
//
// If we can not reproject the requested envelope to the native CRS,
// we go back to reproject in the geographic crs of the native
// coverage since this usually happens for conversions between CRS
// whose area of definition is different
//
// STEP 1 reproject the requested envelope to the coverage geographic bbox
if (destinationToSourceTransform != null && !destinationToSourceTransform.isIdentity()) {
// try to convert the requested bbox to the coverage geocrs
requestedBBOXInCoverageGeographicCRS = CRS.transform(
requestCRSToCoverageGeographicCRS2D, requestedBBox);
requestedBBOXInCoverageGeographicCRS
.setCoordinateReferenceSystem(rasterManager.domainManager.coverageGeographicCRS2D);
}
if (requestedBBOXInCoverageGeographicCRS == null)
requestedBBOXInCoverageGeographicCRS = new GeneralEnvelope(requestCRS);
// STEP 2 intersection with the geographic bbox for this coverage
if (!requestedBBOXInCoverageGeographicCRS.intersects(
rasterManager.domainManager.coverageGeographicBBox, true)) {
cropBBox = null;
empty = true;
return;
}
// intersect with the coverage native geographic bbox
// note that for the moment we got to use general envelope since there is no
// intersection otherwise
requestedBBOXInCoverageGeographicCRS
.intersect(rasterManager.domainManager.coverageGeographicBBox);
requestedBBOXInCoverageGeographicCRS
.setCoordinateReferenceSystem(rasterManager.domainManager.coverageGeographicCRS2D);
// now go back to the coverage native CRS in order to compute an approximate requested
// resolution
final MathTransform transform = CRS.findMathTransform(
requestedBBOXInCoverageGeographicCRS.getCoordinateReferenceSystem(),
rasterManager.domainManager.coverageCRS2D, true);
approximateRequestedBBoInNativeCRS = CRS.transform(transform,
requestedBBOXInCoverageGeographicCRS);
approximateRequestedBBoInNativeCRS
.setCoordinateReferenceSystem(rasterManager.domainManager.coverageCRS2D);
cropBBox = new ReferencedEnvelope(approximateRequestedBBoInNativeCRS);
} catch (TransformException te) {
// something bad happened while trying to transform this
// envelope. let's try with wgs84
if (LOGGER.isLoggable(Level.FINE))
LOGGER.log(Level.FINE, te.getLocalizedMessage(), te);
} catch (FactoryException fe) {
// something bad happened while trying to transform this
// envelope. let's try with wgs84
if (LOGGER.isLoggable(Level.FINE))
LOGGER.log(Level.FINE, fe.getLocalizedMessage(), fe);
}
LOGGER.log(Level.INFO,
"We did not manage to crop the requested envelope, we fall back onto loading the whole coverage.");
cropBBox = null;
}
/**
* @return
* @uml.property name="empty"
*/
public boolean isEmpty() {
return empty;
}
public BoundingBox getRequestedBBox() {
return requestedBBox;
}
public OverviewPolicy getOverviewPolicy() {
return overviewPolicy;
}
public Rectangle getRequestedRasterArea() {
return (Rectangle) (requestedRasterArea != null ? requestedRasterArea.clone()
: requestedRasterArea);
}
public double[] getRequestedResolution() {
return requestedResolution != null ? requestedResolution.clone() : null;
}
public Color getInputTransparentColor() {
return inputTransparentColor;
}
// public Color getOutputTransparentColor() {
// return outputTransparentColor;
// }
//
// public int getMaximumNumberOfGranules() {
// return maximumNumberOfGranules;
// }
//
public double[] getBackgroundValues() {
return backgroundValues;
}
public ReadType getReadType() {
return readType;
}
public Rectangle getDestinationRasterArea() {
return destinationRasterArea;
}
public BoundingBox getCropBBox() {
return cropBBox;
}
public AffineTransform getRequestedGridToWorld() {
return requestedGridToWorld;
}
public Dimension getTileDimensions() {
return tileDimensions;
}
public double[] getRequestedRasterScaleFactors() {
return requestedRasterScaleFactors != null ? requestedRasterScaleFactors.clone()
: requestedRasterScaleFactors;
}
// public boolean isMultithreadingAllowed() {
// return multithreadingAllowed;
// }
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("RasterLayerRequest description: \n");
builder.append("\tRequestedBBox=").append(requestedBBox).append("\n");
builder.append("\tRequestedRasterArea=").append(requestedRasterArea).append("\n");
builder.append("\tRequestedGridToWorld=").append(requestedGridToWorld).append("\n");
builder.append("\tReadType=").append(readType);
return builder.toString();
}
}