/** * <strong>Cropping</strong><br> * The crop operation is responsible for selecting geographic subareas of the source coverage. * * @param coverage Coverage * @param sourceEnvelope GeneralEnvelope * @param sourceCRS CoordinateReferenceSystem * @param destinationEnvelopeInSourceCRS GeneralEnvelope * @return GridCoverage2D * @throws WcsException */ public static GridCoverage2D crop( final Coverage coverage, final GeneralEnvelope sourceEnvelope, final CoordinateReferenceSystem sourceCRS, final GeneralEnvelope destinationEnvelopeInSourceCRS, final Boolean conserveEnvelope) throws WcsException { // /////////////////////////////////////////////////////////////////// // // CROP // // // /////////////////////////////////////////////////////////////////// final GridCoverage2D croppedGridCoverage; // intersect the envelopes final GeneralEnvelope intersectionEnvelope = new GeneralEnvelope(destinationEnvelopeInSourceCRS); intersectionEnvelope.setCoordinateReferenceSystem(sourceCRS); intersectionEnvelope.intersect((GeneralEnvelope) sourceEnvelope); // dow we have something to show? if (intersectionEnvelope.isEmpty()) { throw new WcsException("The Intersection is null. Check the requested BBOX!"); } if (!intersectionEnvelope.equals((GeneralEnvelope) sourceEnvelope)) { // get the cropped grid geometry // final GridGeometry2D cropGridGeometry = getCroppedGridGeometry( // intersectionEnvelope, gridCoverage); /* Operations.DEFAULT.crop(coverage, intersectionEnvelope) */ final ParameterValueGroup param = (ParameterValueGroup) cropParams.clone(); param.parameter("Source").setValue(coverage); param.parameter("Envelope").setValue(intersectionEnvelope); // param.parameter("ConserveEnvelope").setValue(conserveEnvelope); croppedGridCoverage = (GridCoverage2D) cropFactory.doOperation(param, hints); } else { croppedGridCoverage = (GridCoverage2D) coverage; } // prefetch to be faster afterwards. // This step is important since at this stage we might be loading tiles // from disk croppedGridCoverage.prefetch(intersectionEnvelope.toRectangle2D()); return croppedGridCoverage; }
/** * Applies a crop operation to a coverage. * * @see * org.geotools.coverage.processing.AbstractOperation#doOperation(org.opengis.parameter.ParameterValueGroup, * org.geotools.factory.Hints) */ @SuppressWarnings("unchecked") public Coverage doOperation(ParameterValueGroup parameters, Hints hints) { final Geometry cropRoi; // extracted from parameters GeneralEnvelope cropEnvelope = null; // extracted from parameters final GridCoverage2D source; // extracted from parameters final double roiTolerance = parameters.parameter(Crop.PARAMNAME_ROITOLERANCE).doubleValue(); final boolean forceMosaic = parameters.parameter(Crop.PARAMNAME_FORCEMOSAIC).booleanValue(); // ///////////////////////////////////////////////////////////////////// // // Assigning and checking input parameters // // /////////////////////////////////////////////////////////////////// // source coverage final ParameterValue sourceParameter = parameters.parameter("Source"); if (sourceParameter == null || !(sourceParameter.getValue() instanceof GridCoverage2D)) { throw new CannotCropException( Errors.format(ErrorKeys.NULL_PARAMETER_$2, "Source", GridCoverage2D.class.toString())); } source = (GridCoverage2D) sourceParameter.getValue(); // Check Envelope and ROI existence - we need at least one of them final ParameterValue envelopeParameter = parameters.parameter(PARAMNAME_ENVELOPE); final ParameterValue roiParameter = parameters.parameter(PARAMNAME_ROI); if ((envelopeParameter == null || envelopeParameter.getValue() == null) && (roiParameter == null || roiParameter.getValue() == null)) throw new CannotCropException( Errors.format( ErrorKeys.NULL_PARAMETER_$2, PARAMNAME_ENVELOPE, GeneralEnvelope.class.toString())); Object envelope = envelopeParameter.getValue(); if (envelope != null) { if (envelope instanceof GeneralEnvelope) { cropEnvelope = (GeneralEnvelope) envelope; } else if (envelope instanceof Envelope) { cropEnvelope = new GeneralEnvelope((Envelope) envelope); } } // may be null // Check crop ROI try { cropRoi = IntersectUtils.unrollGeometries( (Geometry) roiParameter.getValue()); // may throw if format not correct } catch (IllegalArgumentException ex) { throw new CannotCropException( Errors.format(ErrorKeys.ILLEGAL_ARGUMENT_$2, PARAMNAME_ROI, ex.getMessage()), ex); } // Setting a GeneralEnvelope from ROI if needed if (cropRoi != null && cropEnvelope == null) { Envelope e2d = JTS.getEnvelope2D(cropRoi.getEnvelopeInternal(), source.getCoordinateReferenceSystem()); cropEnvelope = new GeneralEnvelope(e2d); } // ///////////////////////////////////////////////////////////////////// // // Initialization // // We take the crop envelope and the source envelope then we check their // crs and we also check if they ever overlap. // // ///////////////////////////////////////////////////////////////////// // envelope of the source coverage final Envelope2D sourceEnvelope = source.getEnvelope2D(); // crop envelope Envelope2D destinationEnvelope = new Envelope2D(cropEnvelope); CoordinateReferenceSystem sourceCRS = sourceEnvelope.getCoordinateReferenceSystem(); CoordinateReferenceSystem destinationCRS = destinationEnvelope.getCoordinateReferenceSystem(); if (destinationCRS == null) { // Do not change the user provided object - clone it first. final Envelope2D ge = new Envelope2D(destinationEnvelope); destinationCRS = source.getCoordinateReferenceSystem2D(); ge.setCoordinateReferenceSystem(destinationCRS); destinationEnvelope = ge; } // // // // Source and destination crs must be equals // // // if (!CRS.equalsIgnoreMetadata(sourceCRS, destinationCRS)) { throw new CannotCropException( Errors.format( ErrorKeys.MISMATCHED_ENVELOPE_CRS_$2, sourceCRS.getName().getCode(), destinationCRS.getName().getCode())); } if (cropRoi != null) { // TODO: check ROI SRID } // // // // Check the intersection and, if needed, do the crop operation. // // // final GeneralEnvelope intersectionEnvelope = new GeneralEnvelope((Envelope) destinationEnvelope); intersectionEnvelope.setCoordinateReferenceSystem(source.getCoordinateReferenceSystem()); // intersect the envelopes intersectionEnvelope.intersect(sourceEnvelope); if (intersectionEnvelope.isEmpty()) throw new CannotCropException(Errors.format(ErrorKeys.CANT_CROP)); // intersect the ROI with the intersection envelope and throw an error if they do not intersect if (cropRoi != null) { final Geometry jis = JTS.toGeometry( (com.vividsolutions.jts.geom.Envelope) new ReferencedEnvelope(intersectionEnvelope)); if (!IntersectUtils.intersects(cropRoi, jis)) throw new CannotCropException(Errors.format(ErrorKeys.CANT_CROP)); } // // // // Get the grid-to-world transform by keeping into account translation // of grid geometry constructor for respecting OGC PIXEL-IS-CENTER // ImageDatum assumption. // // // final AffineTransform sourceCornerGridToWorld = (AffineTransform) ((GridGeometry2D) source.getGridGeometry()).getGridToCRS(PixelInCell.CELL_CORNER); // // // // I set the tolerance as half the scale factor of the grid-to-world // transform. This should more or less means in most cases "don't bother // to crop if the new envelope is as close to the old one that we go // deep under pixel size." // // // final double tolerance = XAffineTransform.getScale(sourceCornerGridToWorld); if (cropRoi != null || !intersectionEnvelope.equals(sourceEnvelope, tolerance / 2.0, false)) { cropEnvelope = intersectionEnvelope.clone(); return buildResult( cropEnvelope, cropRoi, roiTolerance, forceMosaic, (hints instanceof Hints) ? (Hints) hints : new Hints(hints), source, sourceCornerGridToWorld); } else { // // // // Note that in case we don't crop at all, WE DO NOT UPDATE the // envelope. If we did we might end up doing multiple successive // crop without actually cropping the image but, still, we would // shrink the envelope each time. Just think about having a loop // that crops recursively the same coverage specifying each time an // envelope whose URC is only a a scale quarter close to the LLC of // the old one. We would never crop the raster but we would modify // the grid-to-world transform each time. // // // return source; } }