/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2005-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.coverage.processing; import java.awt.geom.AffineTransform; import java.awt.geom.NoninvertibleTransformException; import java.awt.geom.Rectangle2D; import java.util.Collections; import java.util.HashSet; import java.util.Set; import java.util.logging.Logger; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptor; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.ROIShape; import javax.media.jai.StatisticsOpImage; import javax.media.jai.registry.RenderedRegistryMode; import org.geotools.coverage.grid.GridCoverage2D; import org.geotools.coverage.grid.GridGeometry2D; import org.geotools.geometry.DirectPosition2D; import org.geotools.geometry.Envelope2D; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.metadata.iso.citation.Citations; import org.geotools.parameter.DefaultParameterDescriptor; import org.geotools.parameter.ImagingParameterDescriptors; import org.geotools.parameter.ImagingParameters; import org.geotools.referencing.CRS; import org.geotools.referencing.operation.transform.ProjectiveTransform; import org.geotools.util.logging.Logging; import org.opengis.metadata.spatial.PixelOrientation; import org.opengis.parameter.ParameterDescriptor; import org.opengis.parameter.ParameterValueGroup; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.TransformException; import com.vividsolutions.jts.geom.CoordinateSequence; import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.LineString; import com.vividsolutions.jts.geom.Polygon; /** * This class is the root class for the Statistics operations based on * {@link JAI}'s {@link StatisticsOpImage} like Extrema and Histogram. It * provides basic capabilities for management of geospatial parameters like * {@link javax.media.jai.ROI}s and subsampling factors. * * @author Simone Giannecchini * @since 2.4.x * * * @source $URL$ */ public abstract class AbstractStatisticsOperationJAI extends OperationJAI { /** * */ private static final long serialVersionUID = 6830028735162290160L; /** {@link Logger} for this class. */ public final static Logger LOGGER = Logging.getLogger("org.geotools.coverage.processing"); /** * The parameter descriptor for the SPATIAL_SUBSAMPLING_X */ public static final ParameterDescriptor<Double> SPATIAL_SUBSAMPLING_X = new DefaultParameterDescriptor<Double>( Citations.JAI, "xPeriod", Double.class, // Value class (mandatory) null, // Array of valid values null, // Default value null, // Minimal value null, // Maximal value null, // Unit of measure true); /** * The parameter descriptor for the SPATIAL_SUBSAMPLING_Y */ public static final ParameterDescriptor<Double> SPATIAL_SUBSAMPLING_Y = new DefaultParameterDescriptor<Double>( Citations.JAI, "yPeriod", Double.class, // Value class (mandatory) null, // Array of valid values null, // Default value null, // Minimal value null, // Maximal value null, // Unit of measure true); /** * The parameter descriptor for the Region Of Interest. */ public static final ParameterDescriptor<Polygon> ROI = new DefaultParameterDescriptor<Polygon>( Citations.JAI, "roi", Polygon.class, // Value class (mandatory) null, // Array of valid values null, // Default value null, // Minimal value null, // Maximal value null, // Unit of measure true); private static Set<ParameterDescriptor> REPLACED_DESCRIPTORS; static { final Set<ParameterDescriptor> replacedDescriptors = new HashSet<ParameterDescriptor>(); replacedDescriptors.add( SPATIAL_SUBSAMPLING_X); replacedDescriptors.add( SPATIAL_SUBSAMPLING_Y); replacedDescriptors.add( ROI); REPLACED_DESCRIPTORS = Collections.unmodifiableSet(replacedDescriptors); } /** * Constructor for {@link AbstractStatisticsOperationJAI}. * * @param operationDescriptor * {@link OperationDescriptor} for the underlying JAI operation. */ public AbstractStatisticsOperationJAI(OperationDescriptor operationDescriptor) { super(operationDescriptor, new ImagingParameterDescriptors( getOperationDescriptor(operationDescriptor.getName()), REPLACED_DESCRIPTORS)); } /** * Constructor for {@link AbstractStatisticsOperationJAI}. * * @param operationDescriptor * {@link OperationDescriptor} for the underlying JAI operation. * @param replacements * {@link ImagingParameterDescriptors} that should replace the * correspondent {@link ImagingParameters} in order to change the * default behavior they have inside JAI. */ public AbstractStatisticsOperationJAI( OperationDescriptor operationDescriptor, ImagingParameterDescriptors replacements) { super(operationDescriptor, new ImagingParameterDescriptors( ImagingParameterDescriptors.properties(operationDescriptor), operationDescriptor, RenderedRegistryMode.MODE_NAME, ImagingParameterDescriptors.DEFAULT_SOURCE_TYPE_MAP, REPLACED_DESCRIPTORS)); } /** * Constructor for {@link AbstractStatisticsOperationJAI}. * @param name of the underlying JAI operation. */ public AbstractStatisticsOperationJAI(String name) { super(getOperationDescriptor(name),new ImagingParameterDescriptors( getOperationDescriptor(name), new HashSet<ParameterDescriptor>(REPLACED_DESCRIPTORS))); } /** * Copies parameter values from the specified {@link ParameterValueGroup} to the * {@link ParameterBlockJAI} * * @param parameters * The {@link ParameterValueGroup} to be copied. * @return A copy of the provided {@link ParameterValueGroup} as a JAI block. * * @see org.geotools.coverage.processing.OperationJAI#prepareParameters(org.opengis.parameter.ParameterValueGroup) */ protected ParameterBlockJAI prepareParameters(ParameterValueGroup parameters) { // ///////////////////////////////////////////////////////////////////// // // Make a copy of the input parameters. // // /////////////////////////////////////////////////////////////////// final ImagingParameters copy = (ImagingParameters) descriptor .createValue(); final ParameterBlockJAI block = (ParameterBlockJAI) copy.parameters; try { // ///////////////////////////////////////////////////////////////////// // // // Now transcode the parameters as needed by this operation. // // // /////////////////////////////////////////////////////////////////// // XXX make it robust final GridCoverage2D source = (GridCoverage2D) parameters .parameter(operation.getSourceNames()[PRIMARY_SOURCE_INDEX]) .getValue(); final AffineTransform gridToWorldTransformCorrected = new AffineTransform( (AffineTransform) ((GridGeometry2D) source .getGridGeometry()) .getGridToCRS2D(PixelOrientation.UPPER_LEFT)); final MathTransform worldToGridTransform; try { worldToGridTransform = ProjectiveTransform .create(gridToWorldTransformCorrected.createInverse()); } catch (NoninvertibleTransformException e) { // // // // Something bad happened here, namely the transformation to go // from grid to world was not invertible. Let's wrap and // propagate the error. // // // final CoverageProcessingException ce = new CoverageProcessingException( e); throw ce; } // // // // get the original envelope and the crs // // // final CoordinateReferenceSystem crs = source .getCoordinateReferenceSystem2D(); final Envelope2D envelope = source.getEnvelope2D(); // ///////////////////////////////////////////////////////////////////// // // Transcode the xPeriod and yPeriod parameters by applying the // WorldToGrid transformation for the source coverage. // // I am assuming that the supplied values are in the same CRS as the // source coverage. We here apply // // ///////////////////////////////////////////////////////////////////// final double xPeriod = parameters.parameter("xPeriod") .doubleValue(); final double yPeriod = parameters.parameter("yPeriod") .doubleValue(); if(!Double.isNaN(xPeriod)&&!Double.isNaN(yPeriod)){ // build the new one that spans over the requested area // NOTE: final DirectPosition2D LLC = new DirectPosition2D(crs, envelope.x, envelope.y); LLC.setCoordinateReferenceSystem(crs); final DirectPosition2D URC = new DirectPosition2D(crs, envelope.x + xPeriod, envelope.y + yPeriod); URC.setCoordinateReferenceSystem(crs); final Envelope2D shrinkedEnvelope = new Envelope2D(LLC, URC); // transform back into raster space final Rectangle2D transformedEnv = CRS.transform( worldToGridTransform, shrinkedEnvelope).toRectangle2D(); // block settings block.setParameter("xPeriod", Integer.valueOf((int) transformedEnv .getWidth())); block.setParameter("yPeriod", Integer.valueOf((int) transformedEnv .getHeight())); } // ///////////////////////////////////////////////////////////////////// // // Transcode the polygon parameter into a roi. // // I am assuming that the supplied values are in the same // CRS as the source coverage. We here apply // // ///////////////////////////////////////////////////////////////////// final Object o = parameters.parameter("roi").getValue(); if (o != null && o instanceof Polygon) { final Polygon roiInput = (Polygon) o; if (new ReferencedEnvelope(roiInput.getEnvelopeInternal(), source.getCoordinateReferenceSystem2D()) .intersects((Envelope) new ReferencedEnvelope(envelope))) { final java.awt.Polygon shapePolygon = convertPolygon( roiInput, worldToGridTransform); block.setParameter("roi", new ROIShape(shapePolygon)); } } return block; } catch (Exception e) { // // // // Something bad happened here Let's wrap and propagate the error. // // // final CoverageProcessingException ce = new CoverageProcessingException( e); throw ce; } } /** * Converte a JTS {@link Polygon}, which represents a ROI, into an AWT * {@link java.awt.Polygon} by means of the provided {@link MathTransform}. * * @param roiInput * the input ROI as a JTS {@link Polygon}. * @param worldToGridTransform * the {@link MathTransform} to apply to the input ROI. * @return an AWT {@link java.awt.Polygon}. * @throws TransformException * in case the provided {@link MathTransform} chokes. */ private static java.awt.Polygon convertPolygon(final Polygon roiInput, MathTransform worldToGridTransform) throws TransformException { final boolean isIdentity = worldToGridTransform.isIdentity(); final java.awt.Polygon retValue = new java.awt.Polygon(); final double coords[] = new double[2]; final LineString exteriorRing = roiInput.getExteriorRing(); final CoordinateSequence exteriorRingCS = exteriorRing .getCoordinateSequence(); final int numCoords = exteriorRingCS.size(); for (int i = 0; i < numCoords; i++) { // get the actual coord coords[0] = exteriorRingCS.getX(i); coords[1] = exteriorRingCS.getY(i); // transform it if (!isIdentity) worldToGridTransform.transform(coords, 0, coords, 0, 1); // send it back to the returned polygon retValue.addPoint((int) (coords[0] + 0.5d), (int) (coords[1] + 0.5d)); } // return the created polygon. return retValue; } }