001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2005-2007, Geotools Project Managment Committee (PMC)
005: * (C) 2007, GeoSolutions S.A.S.
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: */
017: package org.geotools.coverage.processing;
018:
019: import java.awt.geom.AffineTransform;
020: import java.awt.geom.NoninvertibleTransformException;
021: import java.awt.geom.Rectangle2D;
022: import java.util.Collections;
023: import java.util.HashSet;
024: import java.util.Set;
025: import java.util.logging.Logger;
026:
027: import javax.media.jai.JAI;
028: import javax.media.jai.OperationDescriptor;
029: import javax.media.jai.ParameterBlockJAI;
030: import javax.media.jai.ROIShape;
031: import javax.media.jai.StatisticsOpImage;
032: import javax.media.jai.registry.RenderedRegistryMode;
033:
034: import org.geotools.coverage.grid.GridCoverage2D;
035: import org.geotools.coverage.grid.GridGeometry2D;
036: import org.geotools.geometry.DirectPosition2D;
037: import org.geotools.geometry.Envelope2D;
038: import org.geotools.geometry.jts.ReferencedEnvelope;
039: import org.geotools.metadata.iso.citation.Citations;
040: import org.geotools.parameter.DefaultParameterDescriptor;
041: import org.geotools.parameter.ImagingParameterDescriptors;
042: import org.geotools.parameter.ImagingParameters;
043: import org.geotools.referencing.CRS;
044: import org.geotools.referencing.operation.transform.ProjectiveTransform;
045: import org.opengis.metadata.spatial.PixelOrientation;
046: import org.opengis.parameter.ParameterDescriptor;
047: import org.opengis.parameter.ParameterValueGroup;
048: import org.opengis.referencing.crs.CoordinateReferenceSystem;
049: import org.opengis.referencing.operation.MathTransform;
050: import org.opengis.referencing.operation.TransformException;
051:
052: import com.vividsolutions.jts.geom.CoordinateSequence;
053: import com.vividsolutions.jts.geom.Envelope;
054: import com.vividsolutions.jts.geom.LineString;
055: import com.vividsolutions.jts.geom.Polygon;
056:
057: /**
058: * This class is the root class for the Statistics operations based on
059: * {@link JAI}'s {@link StatisticsOpImage} like Extrema and Histogram. It
060: * provides basic capabilities for management of geospatial parameters like
061: * {@link javax.media.jai.ROI}s and subsampling factors.
062: *
063: * @author Simone Giannecchini
064: * @since 2.4.x
065: *
066: */
067: public abstract class AbstractStatisticsOperationJAI extends
068: OperationJAI {
069: //
070: /** {@link Logger} for this class. */
071: public final static Logger LOGGER = org.geotools.util.logging.Logging
072: .getLogger("org.geotools.coverage.processing");
073:
074: /**
075: * The parameter descriptor for the SPATIAL_SUBSAMPLING_X
076: */
077: public static final ParameterDescriptor SPATIAL_SUBSAMPLING_X = new DefaultParameterDescriptor(
078: Citations.JAI, "xPeriod", Double.class, // Value class (mandatory)
079: null, // Array of valid values
080: null, // Default value
081: null, // Minimal value
082: null, // Maximal value
083: null, // Unit of measure
084: true);
085:
086: /**
087: * The parameter descriptor for the SPATIAL_SUBSAMPLING_Y
088: */
089: public static final ParameterDescriptor SPATIAL_SUBSAMPLING_Y = new DefaultParameterDescriptor(
090: Citations.JAI, "yPeriod", Double.class, // Value class (mandatory)
091: null, // Array of valid values
092: null, // Default value
093: null, // Minimal value
094: null, // Maximal value
095: null, // Unit of measure
096: true);
097:
098: /**
099: * The parameter descriptor for the coordinate reference system.
100: */
101: public static final ParameterDescriptor ROI = new DefaultParameterDescriptor(
102: Citations.JAI, "roi", Polygon.class, // Value class (mandatory)
103: null, // Array of valid values
104: null, // Default value
105: null, // Minimal value
106: null, // Maximal value
107: null, // Unit of measure
108: true);
109:
110: private static Set REPLACED_DESCRIPTORS;
111:
112: static {
113: final Set replacedDescriptors = new HashSet(3, 1.0f);
114: replacedDescriptors.add(SPATIAL_SUBSAMPLING_X);
115: replacedDescriptors.add(SPATIAL_SUBSAMPLING_Y);
116: replacedDescriptors.add(ROI);
117: REPLACED_DESCRIPTORS = Collections
118: .unmodifiableSet(replacedDescriptors);
119: }
120:
121: /**
122: * Constructor for {@link AbstractStatisticsOperationJAI}.
123: *
124: * @param operationDescriptor
125: * {@link OperationDescriptor} for the underlying JAI operation.
126: */
127: public AbstractStatisticsOperationJAI(
128: OperationDescriptor operationDescriptor) {
129: super (operationDescriptor, new ImagingParameterDescriptors(
130: getOperationDescriptor(operationDescriptor.getName()),
131: REPLACED_DESCRIPTORS));
132: }
133:
134: /**
135: * Constructor for {@link AbstractStatisticsOperationJAI}.
136: *
137: * @param operationDescriptor
138: * {@link OperationDescriptor} for the underlying JAI operation.
139: * @param replacements
140: * {@link ImagingParameterDescriptors} that should replace the
141: * correspondent {@link ImagingParameters} in order to change the
142: * default behaviour they have inside JAI.
143: */
144: public AbstractStatisticsOperationJAI(
145: OperationDescriptor operationDescriptor,
146: ImagingParameterDescriptors replacements) {
147: super (operationDescriptor, new ImagingParameterDescriptors(
148: ImagingParameterDescriptors
149: .properties(operationDescriptor),
150: operationDescriptor, RenderedRegistryMode.MODE_NAME,
151: ImagingParameterDescriptors.DEFAULT_SOURCE_TYPE_MAP,
152: REPLACED_DESCRIPTORS));
153: }
154:
155: /**
156: * Constructor for {@link AbstractStatisticsOperationJAI}.
157: * @param name of the underlying JAI operation.
158: */
159: public AbstractStatisticsOperationJAI(String name) {
160: super (getOperationDescriptor(name),
161: new ImagingParameterDescriptors(
162: getOperationDescriptor(name), new HashSet(
163: REPLACED_DESCRIPTORS)));
164: }
165:
166: /*
167: * (non-Javadoc)
168: * @see org.geotools.coverage.processing.OperationJAI#prepareParameters(org.opengis.parameter.ParameterValueGroup)
169: */
170: protected ParameterBlockJAI prepareParameters(
171: ParameterValueGroup parameters) {
172: // /////////////////////////////////////////////////////////////////////
173: //
174: // Make a copy of the input parameters.
175: //
176: // ///////////////////////////////////////////////////////////////////
177: final ImagingParameters copy = (ImagingParameters) descriptor
178: .createValue();
179: final ParameterBlockJAI block = (ParameterBlockJAI) copy.parameters;
180: try {
181:
182: // /////////////////////////////////////////////////////////////////////
183: //
184: //
185: // Now transcode the parameters as needed by this operation.
186: //
187: //
188: // ///////////////////////////////////////////////////////////////////
189: // XXX make it robust
190: final GridCoverage2D source = (GridCoverage2D) parameters
191: .parameter(
192: operation.getSourceNames()[PRIMARY_SOURCE_INDEX])
193: .getValue();
194: final AffineTransform gridToWorldTransformCorrected = new AffineTransform(
195: (AffineTransform) ((GridGeometry2D) source
196: .getGridGeometry())
197: .getGridToCRS2D(PixelOrientation.UPPER_LEFT));
198: final MathTransform worldToGridTransform;
199: try {
200: worldToGridTransform = ProjectiveTransform
201: .create(gridToWorldTransformCorrected
202: .createInverse());
203: } catch (NoninvertibleTransformException e) {
204: // //
205: //
206: // Something bad happened here, namely the transformation to go
207: // from grid to world was not invertible. Let's wrap and
208: // propagate the error.
209: //
210: // //
211: final CoverageProcessingException ce = new CoverageProcessingException(
212: e);
213: throw ce;
214: }
215:
216: // /////////////////////////////////////////////////////////////////////
217: //
218: // Transcode the xPeriod and yPeriod params by applying the
219: // WorldToGRid
220: // transformation for the source coverage.
221: //
222: // I am assuming that the supplied values are in the same
223: // CRS as the source coverage. We here apply
224: //
225: // /////////////////////////////////////////////////////////////////////
226: final double xPeriod = parameters.parameter("xPeriod")
227: .doubleValue();
228: final double yPeriod = parameters.parameter("yPeriod")
229: .doubleValue();
230:
231: // //
232: //
233: // get the original envelope and the crs
234: //
235: // //
236: final CoordinateReferenceSystem crs = source
237: .getCoordinateReferenceSystem2D();
238: final Envelope2D envelope = source.getEnvelope2D();
239: // build the new one that spans over the requested area
240: // NOTE:
241: final DirectPosition2D LLC = new DirectPosition2D(crs,
242: envelope.x, envelope.y);
243: LLC.setCoordinateReferenceSystem(crs);
244: final DirectPosition2D URC = new DirectPosition2D(crs,
245: envelope.x + xPeriod, envelope.y + yPeriod);
246: URC.setCoordinateReferenceSystem(crs);
247: final Envelope2D shrinkedEnvelope = new Envelope2D(LLC, URC);
248:
249: // transform back into raster space
250: final Rectangle2D transformedEnv = CRS.transform(
251: worldToGridTransform, shrinkedEnvelope)
252: .toRectangle2D();
253:
254: // block settings
255: block.setParameter("xPeriod", new Integer(
256: (int) transformedEnv.getWidth()));
257: block.setParameter("yPeriod", new Integer(
258: (int) transformedEnv.getHeight()));
259:
260: // /////////////////////////////////////////////////////////////////////
261: //
262: // Transcode the polygon parameter into a roi.
263: //
264: // I am assuming that the supplied values are in the same
265: // CRS as the source coverage. We here apply
266: //
267: // /////////////////////////////////////////////////////////////////////
268: final Object o = parameters.parameter("roi").getValue();
269: if (o != null && o instanceof Polygon) {
270: final Polygon roiInput = (Polygon) o;
271: if (new ReferencedEnvelope(roiInput
272: .getEnvelopeInternal(), source
273: .getCoordinateReferenceSystem2D())
274: .intersects((Envelope) new ReferencedEnvelope(
275: envelope))) {
276: final java.awt.Polygon shapePolygon = convertPolygon(
277: roiInput, worldToGridTransform);
278:
279: block.setParameter("roi",
280: new ROIShape(shapePolygon));
281: }
282: }
283: return block;
284: } catch (Exception e) {
285: // //
286: //
287: // Something bad happened here Let's wrap and
288: // propagate the error.
289: //
290: // //
291: final CoverageProcessingException ce = new CoverageProcessingException(
292: e);
293: throw ce;
294: }
295: }
296:
297: /**
298: * Converte a JTS {@link Polygon}, which represents a ROI, int an AWT
299: * {@link java.awt.Polygon} by means of the provided {@link MathTransform}.
300: *
301: * @param roiInput
302: * the input ROI as a JTS {@link Polygon}.
303: * @param worldToGridTransform
304: * the {@link MathTransform} to tapply to the input ROI.
305: * @return an AWT {@link java.awt.Polygon}.
306: * @throws TransformException
307: * in case the provided {@link MathTransform} chockes.
308: */
309: private static java.awt.Polygon convertPolygon(
310: final Polygon roiInput, MathTransform worldToGridTransform)
311: throws TransformException {
312: final boolean isIdentity = worldToGridTransform.isIdentity();
313: final java.awt.Polygon retValue = new java.awt.Polygon();
314: final double coords[] = new double[2];
315: final LineString exteriorRing = roiInput.getExteriorRing();
316: final CoordinateSequence exteriorRingCS = exteriorRing
317: .getCoordinateSequence();
318: final int numCoords = exteriorRingCS.size();
319: for (int i = 0; i < numCoords; i++) {
320: // get the actual coord
321: coords[0] = exteriorRingCS.getX(i);
322: coords[1] = exteriorRingCS.getY(i);
323:
324: // transform it
325: if (!isIdentity)
326: worldToGridTransform.transform(coords, 0, coords, 0, 1);
327:
328: // send it back to the returned polygon
329: retValue.addPoint((int) (coords[0] + 0.5d),
330: (int) (coords[1] + 0.5d));
331:
332: }
333:
334: // return the created polygon.
335: return retValue;
336: }
337: }
|