001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2006, Geotools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or (at your option) any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: */
016: package org.geotools.coverage.processing.operation;
017:
018: import java.awt.RenderingHints;
019: import java.awt.image.RenderedImage;
020: import java.awt.image.renderable.ParameterBlock;
021:
022: import javax.media.jai.ImageLayout;
023: import javax.media.jai.Interpolation;
024: import javax.media.jai.JAI;
025: import javax.media.jai.PlanarImage;
026: import javax.media.jai.operator.SubsampleAverageDescriptor;
027:
028: import org.geotools.coverage.GridSampleDimension;
029: import org.geotools.coverage.grid.GeneralGridRange;
030: import org.geotools.coverage.grid.GridCoverage2D;
031: import org.geotools.coverage.grid.GridGeometry2D;
032: import org.geotools.coverage.processing.OperationJAI;
033: import org.geotools.factory.Hints;
034: import org.geotools.resources.coverage.CoverageUtilities;
035: import org.geotools.resources.image.ImageUtilities;
036: import org.opengis.coverage.Coverage;
037: import org.opengis.coverage.grid.GridCoverage;
038: import org.opengis.parameter.ParameterValueGroup;
039:
040: /**
041: * Subsample the provided coverage by averaging overa moving window.
042: *
043: * <p>
044: * Citing the JAI documentation at {@linkplain}http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/SubsampleAverageDescriptor.html
045: * for the {@link SubsampleAverageDescriptor}:
046: *
047: *
048: * <p>
049: * The "SubsampleAverage" operation subsamples an image by averaging over a
050: * moving window. The scale factors supplied to the operation are forward
051: * mapping coefficients representing the geometric transformation from source to
052: * destination image coordinates. For example, if both scale factors were equal
053: * to 0.5, the operation would produce an output image of half the size of the
054: * input image in both dimensions. Both scale factors must be in the range (0.0,
055: * 1.0] or an exception will be thrown when the operation is created. The
056: * default value of the vertical scale factor is the value of the horizontal
057: * scale factor. If both scale factors equal 1.0, the source image is returned
058: * directly.
059: *
060: *
061: *
062: * @author Simone Giannecchini
063: * @since 2.3
064: */
065: final class SubsampleAveragedCoverage extends GridCoverage2D {
066:
067: /**
068: *
069: */
070: private static final long serialVersionUID = 5274708130300017804L;
071:
072: /**
073: * Constructor for a {@link SubsampleAveragedCoverage}.
074: *
075: * @param image
076: * @param sourceCoverage
077: * @param actionTaken
078: */
079: private SubsampleAveragedCoverage(PlanarImage image,
080: GridCoverage2D sourceCoverage, int actionTaken) {
081: super (
082: sourceCoverage.getName(),
083: image,
084: new GridGeometry2D(new GeneralGridRange(image),
085: sourceCoverage.getEnvelope()),
086: (GridSampleDimension[]) (actionTaken == 1 ? null
087: : sourceCoverage.getSampleDimensions().clone()),
088: new GridCoverage[] { sourceCoverage }, sourceCoverage
089: .getProperties());
090: }
091:
092: /**
093: * Craetes a new {@link Coverage} by applying the {@link SubsampleAverage}
094: * operation to the provided source using the provided parameters and hints.
095: *
096: * @param parameters
097: * Parameters to control this operation.
098: * @param hints
099: * Hints to control this operation.
100: * @return A subsample coverage.
101: */
102: static Coverage create(ParameterValueGroup parameters, Hints hints) {
103: // /////////////////////////////////////////////////////////////////////
104: //
105: // Getting the input parameters
106: //
107: // /////////////////////////////////////////////////////////////////////
108: final Double scaleX = (Double) parameters.parameter("scaleX")
109: .getValue();
110: final Double scaleY = (Double) parameters.parameter("scaleY")
111: .getValue();
112: final Interpolation interpolation = (Interpolation) parameters
113: .parameter("Interpolation").getValue();
114:
115: // /////////////////////////////////////////////////////////////////////
116: //
117: // Getting the source coverage
118: //
119: // /////////////////////////////////////////////////////////////////////
120: GridCoverage2D sourceCoverage = (GridCoverage2D) parameters
121: .parameter("Source").getValue();
122: RenderedImage sourceImage = sourceCoverage.getRenderedImage();
123:
124: // /////////////////////////////////////////////////////////////////////
125: //
126: // Do we need to explode the Palette to RGB(A)?
127: //
128: // /////////////////////////////////////////////////////////////////////
129: int actionTaken = CoverageUtilities
130: .prepareSourcesForGCOperation(sourceCoverage,
131: interpolation, true, hints);
132: switch (actionTaken) {
133: case 2:
134: // in this case we need to go back the geophysics view of the
135: // source coverage
136: sourceCoverage = sourceCoverage.geophysics(true);
137: sourceImage = sourceCoverage.getRenderedImage();
138:
139: break;
140: case 3:
141: // we cannot accept working on the non-geophysics view because it
142: // usually has an IndexColorModel.
143: assert (false);
144: break;
145: }
146:
147: // /////////////////////////////////////////////////////////////////////
148: //
149: // Managing Hints for coverage's layout purposes
150: //
151: // /////////////////////////////////////////////////////////////////////
152: RenderingHints targetHints = ImageUtilities
153: .getRenderingHints(sourceImage);
154: if (targetHints == null) {
155: targetHints = new RenderingHints(hints);
156: } else if (hints != null) {
157: targetHints.add(hints);
158: }
159: ImageLayout layout = (ImageLayout) targetHints
160: .get(JAI.KEY_IMAGE_LAYOUT);
161: if (layout != null) {
162: layout = (ImageLayout) layout.clone();
163: } else {
164: layout = new ImageLayout(sourceImage);
165: layout.unsetTileLayout();
166: // At this point, only the color model and sample model are left
167: // valids.
168: }
169: if ((layout.getValidMask() & (ImageLayout.TILE_WIDTH_MASK
170: | ImageLayout.TILE_HEIGHT_MASK
171: | ImageLayout.TILE_GRID_X_OFFSET_MASK | ImageLayout.TILE_GRID_Y_OFFSET_MASK)) == 0) {
172: layout.setTileGridXOffset(layout.getMinX(sourceImage));
173: layout.setTileGridYOffset(layout.getMinY(sourceImage));
174: final int width = layout.getWidth(sourceImage);
175: final int height = layout.getHeight(sourceImage);
176: if (layout.getTileWidth(sourceImage) > width)
177: layout.setTileWidth(width);
178: if (layout.getTileHeight(sourceImage) > height)
179: layout.setTileHeight(height);
180: }
181: targetHints.put(JAI.KEY_IMAGE_LAYOUT, layout);
182: // it is crucial to correctly manage the Hints to control the
183: // replacement of IndexColorModel. It is worth to point out that setting
184: // the JAI.KEY_REPLACE_INDEX_COLOR_MODEL hint to true is not enough to
185: // force the operators to do an expansion.
186: // If we explicitly provide an ImageLayout built with the source image
187: // where the CM and the SM are valid. those will be employed overriding
188: // a the possibility to expand the color model.
189: if (actionTaken != 1)
190: targetHints
191: .add(ImageUtilities.DONT_REPLACE_INDEX_COLOR_MODEL);
192: else {
193: targetHints.add(ImageUtilities.REPLACE_INDEX_COLOR_MODEL);
194: layout.unsetValid(ImageLayout.COLOR_MODEL_MASK);
195: layout.unsetValid(ImageLayout.SAMPLE_MODEL_MASK);
196: }
197:
198: // /////////////////////////////////////////////////////////////////////
199: //
200: // preparing the parameters for the scale operation
201: //
202: // /////////////////////////////////////////////////////////////////////
203: final ParameterBlock pbjSubsampleAverage = new ParameterBlock();
204: pbjSubsampleAverage.addSource(sourceImage);
205: pbjSubsampleAverage.add(scaleX).add(scaleY).add(interpolation)
206: .add(sourceImage);
207:
208: // /////////////////////////////////////////////////////////////////////
209: //
210: // preparing the new gridgeometry
211: //
212: // /////////////////////////////////////////////////////////////////////
213: targetHints.add(new RenderingHints(JAI.KEY_BORDER_EXTENDER,
214: parameters.parameter("BorderExtender").getValue()));
215: targetHints.add(new RenderingHints(JAI.KEY_INTERPOLATION,
216: interpolation));
217: final JAI processor = OperationJAI.getJAI(targetHints);
218:
219: // /////////////////////////////////////////////////////////////////////
220: //
221: // Preparing the resulting coverage
222: //
223: // /////////////////////////////////////////////////////////////////////
224: GridCoverage2D result;
225: if (!processor.equals(JAI.getDefaultInstance())) {
226:
227: result = new SubsampleAveragedCoverage(processor.createNS(
228: "SubsampleAverage", pbjSubsampleAverage,
229: targetHints), sourceCoverage, actionTaken);
230: }
231: // no supplied processor
232: else
233: result = new SubsampleAveragedCoverage(JAI.create(
234: "SubsampleAverage", pbjSubsampleAverage,
235: targetHints), sourceCoverage, actionTaken);
236:
237: // now let's see what we need to do in order to clean things up
238: if (actionTaken == 2)
239: return result.geophysics(false);
240:
241: return result;
242: }
243: }
|