Source Code Cross Referenced for OperationJAI.java in  » GIS » GeoTools-2.4.1 » org » geotools » coverage » processing » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » GIS » GeoTools 2.4.1 » org.geotools.coverage.processing 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         *    GeoTools - OpenSource mapping toolkit
0003:         *    http://geotools.org
0004:         *    (C) 2005-2006, Geotools Project Managment Committee (PMC)
0005:         *    (C) 2001, Institut de Recherche pour le Développement
0006:         *
0007:         *    This library is free software; you can redistribute it and/or
0008:         *    modify it under the terms of the GNU Lesser General Public
0009:         *    License as published by the Free Software Foundation; either
0010:         *    version 2.1 of the License, or (at your option) any later version.
0011:         *
0012:         *    This library is distributed in the hope that it will be useful,
0013:         *    but WITHOUT ANY WARRANTY; without even the implied warranty of
0014:         *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015:         *    Lesser General Public License for more details.
0016:         */
0017:        package org.geotools.coverage.processing;
0018:
0019:        // J2SE dependencies and extensions
0020:        import java.awt.RenderingHints;
0021:        import java.awt.image.ColorModel;
0022:        import java.awt.image.RenderedImage;
0023:        import java.io.Serializable;
0024:        import java.util.Map;
0025:        import java.util.Arrays;
0026:        import java.util.Locale;
0027:        import java.util.Collections;
0028:        import javax.units.Unit;
0029:
0030:        // JAI dependencies
0031:        import javax.media.jai.ImageLayout;
0032:        import javax.media.jai.JAI;
0033:        import javax.media.jai.OperationRegistry;
0034:        import javax.media.jai.OperationDescriptor;
0035:        import javax.media.jai.ParameterBlockJAI;
0036:        import javax.media.jai.registry.RenderedRegistryMode;
0037:
0038:        // OpenGIS dependencies
0039:        import org.opengis.coverage.Coverage;
0040:        import org.opengis.coverage.processing.OperationNotFoundException;
0041:        import org.opengis.referencing.FactoryException;
0042:        import org.opengis.referencing.IdentifiedObject;
0043:        import org.opengis.referencing.crs.CoordinateReferenceSystem;
0044:        import org.opengis.referencing.operation.MathTransform;
0045:        import org.opengis.referencing.operation.MathTransform2D;
0046:        import org.opengis.referencing.operation.MathTransformFactory;
0047:        import org.opengis.referencing.operation.TransformException;
0048:        import org.opengis.parameter.ParameterDescriptorGroup;
0049:        import org.opengis.parameter.ParameterNotFoundException;
0050:        import org.opengis.parameter.ParameterValueGroup;
0051:        import org.opengis.util.InternationalString;
0052:
0053:        // Geotools dependencies
0054:        import org.geotools.coverage.Category;
0055:        import org.geotools.coverage.GridSampleDimension;
0056:        import org.geotools.coverage.grid.ViewType;
0057:        import org.geotools.coverage.grid.GridCoverage2D;
0058:        import org.geotools.coverage.grid.GridGeometry2D;
0059:        import org.geotools.coverage.grid.InvalidGridGeometryException;
0060:        import org.geotools.factory.Hints;
0061:        import org.geotools.parameter.ImagingParameters;
0062:        import org.geotools.parameter.ImagingParameterDescriptors;
0063:        import org.geotools.referencing.CRS;
0064:        import org.geotools.referencing.ReferencingFactoryFinder;
0065:        import org.geotools.referencing.operation.transform.DimensionFilter;
0066:        import org.geotools.image.jai.Registry;
0067:        import org.geotools.resources.XArray;
0068:        import org.geotools.resources.Utilities;
0069:        import org.geotools.resources.CRSUtilities;
0070:        import org.geotools.resources.coverage.CoverageUtilities;
0071:        import org.geotools.resources.i18n.Errors;
0072:        import org.geotools.resources.i18n.ErrorKeys;
0073:        import org.geotools.resources.image.ImageUtilities;
0074:        import org.geotools.util.AbstractInternationalString;
0075:        import org.geotools.util.NumberRange;
0076:        import org.geotools.util.logging.Logging;
0077:
0078:        /**
0079:         * Wraps a JAI's {@link OperationDescriptor} for interoperability with
0080:         * <A HREF="http://java.sun.com/products/java-media/jai/">Java Advanced Imaging</A>.
0081:         * This class help to leverage the rich set of JAI operators in an GeoAPI framework.
0082:         * {@code OperationJAI} inherits operation name and argument types from {@link OperationDescriptor},
0083:         * except the source argument type (usually <code>{@linkplain RenderedImage}.class</code>) which is
0084:         * set to <code>{@linkplain GridCoverage2D}.class</code>. If there is only one source argument, it
0085:         * will be renamed {@code "source"} for better compliance with OpenGIS usage.
0086:         * <p>
0087:         * The entry point for applying an operation is the usual {@link #doOperation doOperation} method.
0088:         * The default implementation forward the call to other methods for different bits of tasks,
0089:         * resulting in the following chain of calls:
0090:         * <p>
0091:         * <blockquote><table>
0092:         *   <tr><td>{@link #doOperation doOperation}:&nbsp;</td>
0093:         *       <td>the entry point.</td></tr>
0094:         *   <tr><td>{@link #resampleToCommonGeometry resampleToCommonGeometry}:&nbsp;</td>
0095:         *       <td>reprojects all sources to the same coordinate reference system.</td></tr>
0096:         *   <tr><td>{@link #deriveGridCoverage deriveGridCoverage}:&nbsp;</td>
0097:         *       <td>gets the destination properties.</td></tr>
0098:         *   <tr><td>{@link #deriveSampleDimension deriveSampleDimension}:&nbsp;</td>
0099:         *       <td>gets the destination sample dimensions.</td></tr>
0100:         *   <tr><td>{@link #deriveCategory deriveCategory}:&nbsp;</td>
0101:         *       <td>gets the destination categories.</td></tr>
0102:         *   <tr><td>{@link #deriveRange deriveRange}:&nbsp;</td>
0103:         *       <td>gets the expected range of values.</td></tr>
0104:         *   <tr><td>{@link #deriveUnit deriveUnit}:&nbsp;</td>
0105:         *       <td>gets the destination units.</td></tr>
0106:         *   <tr><td>{@link #createRenderedImage createRenderedImage}:&nbsp;</td>
0107:         *       <td>the actual call to {@link JAI#createNS JAI.createNS}.</td></tr>
0108:         * </table></blockquote>
0109:         *
0110:         * @since 2.2
0111:         * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/coverage/src/main/java/org/geotools/coverage/processing/OperationJAI.java $
0112:         * @version $Id: OperationJAI.java 27848 2007-11-12 13:10:32Z desruisseaux $
0113:         * @author Martin Desruisseaux
0114:         * @author Simone Giannecchini
0115:         */
0116:        public class OperationJAI extends Operation2D {
0117:            /**
0118:             * Serial number for interoperability with different versions.
0119:             */
0120:            private static final long serialVersionUID = -5974520239347639965L;
0121:
0122:            /**
0123:             * The rendered mode for JAI operation.
0124:             */
0125:            protected static final String RENDERED_MODE = RenderedRegistryMode.MODE_NAME;
0126:
0127:            /**
0128:             * The JAI's operation descriptor.
0129:             */
0130:            protected final OperationDescriptor operation;
0131:
0132:            /**
0133:             * Constructs a grid coverage operation from a JAI operation name. This convenience
0134:             * constructor fetch the {@link OperationDescriptor} from the specified operation
0135:             * name using the default {@link JAI} instance.
0136:             *
0137:             * @param operation JAI operation name (e.g. {@code "GradientMagnitude"}).
0138:             * @throws OperationNotFoundException if no JAI descriptor was found for the given name.
0139:             */
0140:            public OperationJAI(final String operation)
0141:                    throws OperationNotFoundException {
0142:                this (getOperationDescriptor(operation));
0143:            }
0144:
0145:            /**
0146:             * Constructs a grid coverage operation backed by a JAI operation. The operation descriptor
0147:             * must supports the {@code "rendered"} mode (which is the case for most JAI operations).
0148:             *
0149:             * @param operation The JAI operation descriptor.
0150:             */
0151:            public OperationJAI(final OperationDescriptor operation) {
0152:                this (operation, new ImagingParameterDescriptors(operation));
0153:            }
0154:
0155:            /**
0156:             * Constructs a grid coverage operation backed by a JAI operation. The operation descriptor
0157:             * must supports the {@code "rendered"} mode (which is the case for most JAI operations).
0158:             *
0159:             * @param operation  The JAI operation descriptor.
0160:             * @param descriptor The OGC parameters descriptor.
0161:             */
0162:            protected OperationJAI(final OperationDescriptor operation,
0163:                    final ParameterDescriptorGroup descriptor) {
0164:                super (descriptor);
0165:                this .operation = operation;
0166:                ensureNonNull("operation", operation);
0167:                /*
0168:                 * Check argument validity.
0169:                 */
0170:                ensureRenderedImage(operation.getDestClass(RENDERED_MODE));
0171:                final Class[] sourceClasses = operation
0172:                        .getSourceClasses(RENDERED_MODE);
0173:                if (sourceClasses != null) {
0174:                    final int length = sourceClasses.length;
0175:                    assert length == operation.getNumSources();
0176:                    for (int i = 0; i < length; i++) {
0177:                        ensureRenderedImage(sourceClasses[i]);
0178:                    }
0179:                }
0180:                assert super .getNumSources() == operation.getNumSources();
0181:            }
0182:
0183:            /**
0184:             * Returns the operation descriptor for the specified JAI operation name. This method
0185:             * uses the default {@link JAI} instance and looks for the {@value #RENDERED_MODE} mode.
0186:             *
0187:             * @param  name The operation name.
0188:             * @return The operation descriptor for the given name.
0189:             * @throws OperationNotFoundException if no JAI descriptor was found for the given name.
0190:             *
0191:             * @since 2.4
0192:             */
0193:            protected static OperationDescriptor getOperationDescriptor(
0194:                    final String name) throws OperationNotFoundException {
0195:                final OperationRegistry registry = JAI.getDefaultInstance()
0196:                        .getOperationRegistry();
0197:                OperationDescriptor operation = (OperationDescriptor) registry
0198:                        .getDescriptor(RENDERED_MODE, name);
0199:                if (operation != null) {
0200:                    return operation;
0201:                }
0202:                if (name.startsWith("org.geotools.")
0203:                        && registry.getDescriptor(RENDERED_MODE,
0204:                                "org.geotools.Combine") == null) {
0205:                    try {
0206:                        // try and register our operations
0207:                        Registry.registerGeotoolsServices(registry);
0208:                    } catch (RuntimeException e) {
0209:                        Logging.GEOTOOLS.unexpectedException(
0210:                                AbstractProcessor.LOGGER, OperationJAI.class,
0211:                                "getOperationDescriptor", e);
0212:                    }
0213:
0214:                    // try to get it again
0215:                    operation = (OperationDescriptor) registry.getDescriptor(
0216:                            RENDERED_MODE, name);
0217:                    if (operation != null) {
0218:                        return operation;
0219:                    }
0220:                }
0221:                throw new OperationNotFoundException(Errors.format(
0222:                        ErrorKeys.OPERATION_NOT_FOUND_$1, name));
0223:            }
0224:
0225:            /**
0226:             * Ensures that the specified class is assignable to {@link RenderedImage}.
0227:             */
0228:            private static final void ensureRenderedImage(final Class classe)
0229:                    throws IllegalArgumentException {
0230:                if (!RenderedImage.class.isAssignableFrom(classe)) {
0231:                    // TODO: provide localized message
0232:                    throw new IllegalArgumentException(classe.getName());
0233:                }
0234:            }
0235:
0236:            /**
0237:             * Copies parameter values from the specified {@link ParameterValueGroup} to the
0238:             * {@link ParameterBlockJAI}, except the sources.
0239:             * <p>
0240:             * <b>Note:</b> it would be possible to use {@link ImagingParameters#parameters}
0241:             * directly in some occasions. However, we peform an unconditional copy instead
0242:             * because some operations (e.g. "GradientMagnitude") may change the values.
0243:             * 
0244:             * @param parameters The {@link ParameterValueGroup} to be copied.
0245:             * @return A copy of the provided {@link ParameterValueGroup} as a JAI block.
0246:             *
0247:             * @since 2.4
0248:             */
0249:            protected ParameterBlockJAI prepareParameters(
0250:                    final ParameterValueGroup parameters) {
0251:                final ImagingParameters copy = (ImagingParameters) descriptor
0252:                        .createValue();
0253:                final ParameterBlockJAI block = (ParameterBlockJAI) copy.parameters;
0254:                org.geotools.parameter.Parameters.copy(parameters, copy);
0255:                return block;
0256:            }
0257:
0258:            /**
0259:             * Applies a process operation to a grid coverage.
0260:             * The default implementation performs the following steps:
0261:             *
0262:             * <ol>
0263:             *   <li>Converts source grid coverages to their <cite>geophysics</cite> view using
0264:             *       <code>{@linkplain GridCoverage2D#geophysics GridCoverage2D.geophysics}(true)</code>.
0265:             *       This allow to performs all computation on geophysics values instead of encoded
0266:             *       samples. <strong>Note:</strong> this step is disabled if
0267:             *       {@link #computeOnGeophysicsValues computeOnGeophysicsValues} returns
0268:             *       {@code false}.</li>
0269:             *
0270:             *   <li>Ensures that every sources {@code GridCoverage2D}s use the same coordinate reference
0271:             *       system (at least for the two-dimensional part) with the same
0272:             *       {@link GridGeometry2D#getGridToCRS2D gridToCRS} relationship.</li>
0273:             *
0274:             *   <li>Invokes {@link #deriveGridCoverage}.
0275:             *       The sources in the {@code ParameterBlock} are {@link RenderedImage} objects
0276:             *       obtained from {@link GridCoverage2D#getRenderedImage()}.</li>
0277:             *
0278:             *   <li>If a changes from non-geophysics to geophysics view were performed at step 1,
0279:             *       converts the result back to the original view using
0280:             *       <code>{@linkplain GridCoverage2D#geophysics GridCoverage2D.geophysics}(false)</code>.
0281:             *       </li>
0282:             * </ol>
0283:             *
0284:             * @param  parameters List of name value pairs for the parameters required for the operation.
0285:             * @param  hints A set of rendering hints, or {@code null} if none.
0286:             * @return The result as a grid coverage.
0287:             * @throws CoverageProcessingException if the operation can't be applied.
0288:             *
0289:             * @see #deriveGridCoverage
0290:             */
0291:            public Coverage doOperation(final ParameterValueGroup parameters,
0292:                    final Hints hints) throws CoverageProcessingException {
0293:                final ParameterBlockJAI block = prepareParameters(parameters);
0294:                /*
0295:                 * Extracts the source grid coverages now as an array. The sources will be set in the
0296:                 * ParameterBlockJAI (as RenderedImages) later, after the reprojection performed in the
0297:                 * next block.
0298:                 */
0299:                final String[] sourceNames = operation.getSourceNames();
0300:                final GridCoverage2D[] sources = new GridCoverage2D[sourceNames.length];
0301:                ViewType primarySourceType = extractSources(parameters,
0302:                        sourceNames, sources);
0303:                /*
0304:                 * Ensures that all coverages use the same CRS and has the same 'gridToCRS' relationship.
0305:                 * After the reprojection, the method still checks all CRS in case the user overridden the
0306:                 * {@link #resampleToCommonGeometry} method.
0307:                 */
0308:                resampleToCommonGeometry(sources, null, null, hints);
0309:                GridCoverage2D coverage = sources[PRIMARY_SOURCE_INDEX];
0310:                final CoordinateReferenceSystem crs = coverage
0311:                        .getCoordinateReferenceSystem2D();
0312:                // TODO: remove the cast when we will be allowed to compile for J2SE 1.5.
0313:                final MathTransform2D gridToCRS = ((GridGeometry2D) coverage
0314:                        .getGridGeometry()).getGridToCRS2D();
0315:                for (int i = 0; i < sources.length; i++) {
0316:                    final GridCoverage2D source = sources[i];
0317:                    if (!CRS.equalsIgnoreMetadata(crs, source
0318:                            .getCoordinateReferenceSystem2D())
0319:                            || !CRS.equalsIgnoreMetadata(gridToCRS,
0320:                                    ((GridGeometry2D) source.getGridGeometry())
0321:                                            .getGridToCRS2D())) {
0322:                        throw new IllegalArgumentException(Errors
0323:                                .format(ErrorKeys.INCOMPATIBLE_GRID_GEOMETRY));
0324:                    }
0325:                    block.setSource(sourceNames[i], source.getRenderedImage());
0326:                }
0327:                /*
0328:                 * Applies the operation. This delegates the work to the chain of 'deriveXXX' methods.
0329:                 */
0330:                coverage = deriveGridCoverage(sources, new Parameters(crs,
0331:                        gridToCRS, block, hints));
0332:                return postProcessResult(coverage, primarySourceType);
0333:            }
0334:
0335:            /**
0336:             * Post processing on the coverage resulting from JAI operation.
0337:             *
0338:             * @param coverage
0339:             *            {@link GridCoverage2D} resulting from the operation.
0340:             * @param primarySourceType
0341:             *            Tells if we have to change the "geo-view" for the provided {@link GridCoverage2D}.
0342:             *
0343:             * @return the prepared {@link GridCoverage2D}.
0344:             */
0345:            private static GridCoverage2D postProcessResult(
0346:                    GridCoverage2D coverage, final ViewType primarySourceType) {
0347:                if (primarySourceType != null) {
0348:                    coverage = coverage.geophysics(ViewType.GEOPHYSICS
0349:                            .equals(primarySourceType));
0350:                }
0351:                return coverage;
0352:            }
0353:
0354:            /**
0355:             * Returns a sub-coordinate reference system for the specified dimension range.
0356:             * This method is for internal use by {@link #resampleToCommonGeometry}.
0357:             *
0358:             * @param  crs   The coordinate reference system to decompose.
0359:             * @param  lower The first dimension to keep, inclusive.
0360:             * @param  upper The last  dimension to keep, exclusive.
0361:             * @return The sub-coordinate system, or {@code null} if {@code lower} is equals to {@code upper}.
0362:             * @throws InvalidGridGeometryException if the CRS can't be separated.
0363:             */
0364:            private static CoordinateReferenceSystem getSubCRS(
0365:                    final CoordinateReferenceSystem crs, final int lower,
0366:                    final int upper) throws InvalidGridGeometryException {
0367:                if (lower == upper) {
0368:                    return null;
0369:                }
0370:                final CoordinateReferenceSystem candidate = CRSUtilities
0371:                        .getSubCRS(crs, lower, upper);
0372:                if (candidate == null) {
0373:                    throw new InvalidGridGeometryException("Unsupported CRS: "
0374:                            + crs.getName().getCode());
0375:                }
0376:                return candidate;
0377:            }
0378:
0379:            /**
0380:             * Ensures that the source and target dimensions are the same. This method is for internal
0381:             * use by {@link #resampleToCommonGeometry}.
0382:             */
0383:            private static void ensureStableDimensions(
0384:                    final DimensionFilter filter)
0385:                    throws InvalidGridGeometryException {
0386:                final int[] source = filter.getSourceDimensions();
0387:                Arrays.sort(source);
0388:                final int[] target = filter.getTargetDimensions();
0389:                Arrays.sort(target);
0390:                if (!Arrays.equals(source, target)) {
0391:                    // TODO: localize
0392:                    throw new InvalidGridGeometryException(
0393:                            "Unsupported math transform.");
0394:                }
0395:            }
0396:
0397:            /**
0398:             * Resamples all sources grid coverages to the same {@linkplain GridGeometry2D two-dimensional
0399:             * geometry} before to apply the {@linkplain #operation}. This method is invoked automatically
0400:             * by the {@link #doOperation doOperation} method. Only the two-dimensional part is reprojected
0401:             * (usually the spatial component of a CRS). Extra dimension (if any) are left unchanged. Extra
0402:             * dimensions are typically time axis or depth. Note that extra dimensions are
0403:             * <strong>not</strong> forced to a common geometry; only the two dimensions that apply to a
0404:             * {@link javax.media.jai.PlanarImage} are. This is because the extra dimensions don't need to
0405:             * be compatible for all operations. For example if a source image is a slice in a time series,
0406:             * a second source image could be a slice in the frequency representation of this time series.
0407:             * <p>
0408:             * Subclasses should override this method if they want to specify target
0409:             * {@linkplain GridGeometry2D grid geometry} and
0410:             * {@linkplain CoordinateReferenceSystem coordinate reference system} different than the
0411:             * default ones. For example if a subclass wants to force all images to be referenced in a
0412:             * {@linkplain org.geotools.referencing.crs.DefaultGeographicCRS#WGS84 WGS 84} CRS, then
0413:             * it may overrides this method as below:
0414:             *
0415:             * <blockquote><pre>
0416:             * protected void resampleToCommonGeometry(...) {
0417:             *    crs2D = DefaultGeographicCRS.WGS84;
0418:             *    super.resampleToCommonGeometry(sources, crs2D, gridToCrs2D, hints);
0419:             * }</pre></blockquote>
0420:             *
0421:             * @param  sources     The source grid coverages to resample. This array is updated in-place as
0422:             *                     needed (for example if a grid coverage is replaced by a projected one).
0423:             * @param  crs2D       The target coordinate reference system to use, or {@code null} for a
0424:             *                     default one.
0425:             * @param  gridToCrs2D The target "grid to coordinate reference system" transform, or
0426:             *                     {@code null} for a default one.
0427:             * @param  hints       The rendering hints, or {@code null} if none.
0428:             *
0429:             * @throws InvalidGridGeometryException if a source coverage has an unsupported grid geometry.
0430:             * @throws CannotReprojectException if a grid coverage can't be resampled for some other reason.
0431:             */
0432:            protected void resampleToCommonGeometry(
0433:                    final GridCoverage2D[] sources,
0434:                    CoordinateReferenceSystem crs2D,
0435:                    MathTransform2D gridToCrs2D, final Hints hints)
0436:                    throws InvalidGridGeometryException,
0437:                    CannotReprojectException {
0438:                if (sources == null || sources.length == 0) {
0439:                    return; // Nothing to reproject.
0440:                }
0441:                /*
0442:                 * Ensures that the target CRS is two-dimensional. If no target CRS were specified,
0443:                 * uses the CRS of the primary source. The math transform must be 2D too, but this
0444:                 * is ensured by the interface type (MathTransform2D).
0445:                 */
0446:                final GridCoverage2D primarySource = sources[PRIMARY_SOURCE_INDEX];
0447:                if (crs2D == null) {
0448:                    if (gridToCrs2D == null && sources.length == 1) {
0449:                        return; // No need to reproject.
0450:                    }
0451:                    crs2D = primarySource.getCoordinateReferenceSystem2D();
0452:                } else
0453:                    try {
0454:                        crs2D = CRSUtilities.getCRS2D(crs2D);
0455:                    } catch (TransformException exception) {
0456:                        // TODO: localize
0457:                        throw new CannotReprojectException("Unsupported CRS: "
0458:                                + crs2D.getName().getCode());
0459:                    }
0460:                if (gridToCrs2D == null) {
0461:                    // TODO: Remove cast when we will be allowed to compile for J2SE 1.5.
0462:                    gridToCrs2D = ((GridGeometry2D) primarySource
0463:                            .getGridGeometry()).getGridToCRS2D();
0464:                }
0465:                /*
0466:                 * 'crs2D' is the two dimensional part of the target CRS. Now for each source coverages,
0467:                 * substitute their two-dimensional CRS by this 'crs2D'. A source may have more than two
0468:                 * dimensions. For example it may have a time or a depth axis. In such case, their "head"
0469:                 * and "tail" CRS will be preserved before and after 'crs2D'.
0470:                 */
0471:                final AbstractProcessor processor = getProcessor(hints);
0472:                for (int i = 0; i < sources.length; i++) {
0473:                    final GridCoverage2D source = sources[i];
0474:                    final GridGeometry2D geometry = (GridGeometry2D) source
0475:                            .getGridGeometry();
0476:                    final CoordinateReferenceSystem srcCrs2D = source
0477:                            .getCoordinateReferenceSystem2D();
0478:                    final CoordinateReferenceSystem sourceCRS = source
0479:                            .getCoordinateReferenceSystem();
0480:                    final CoordinateReferenceSystem targetCRS;
0481:                    if (CRS.equalsIgnoreMetadata(crs2D, srcCrs2D)) {
0482:                        targetCRS = sourceCRS; // No reprojection needed for this source coverage.
0483:                    } else {
0484:                        /*
0485:                         * Replaces the 2D part in the source CRS, while preserving the leading and
0486:                         * trailing CRS (if any). Leading and trailing CRS are typically time axis or
0487:                         * depth axis. Current implementation requires that the 2D part appears in two
0488:                         * consecutive dimensions. Those dimensions are (0,1) in the majority of cases.
0489:                         */
0490:                        final int lowerDim = Math.min(geometry.axisDimensionX,
0491:                                geometry.axisDimensionY);
0492:                        final int upperDim = Math.max(geometry.axisDimensionX,
0493:                                geometry.axisDimensionY) + 1;
0494:                        final int sourceDim = sourceCRS.getCoordinateSystem()
0495:                                .getDimension();
0496:                        if (upperDim - lowerDim != srcCrs2D
0497:                                .getCoordinateSystem().getDimension()) {
0498:                            // TODO: localize
0499:                            throw new InvalidGridGeometryException(
0500:                                    "Unsupported CRS: "
0501:                                            + sourceCRS.getName().getCode());
0502:                        }
0503:                        final CoordinateReferenceSystem headCRS = getSubCRS(
0504:                                sourceCRS, 0, lowerDim);
0505:                        final CoordinateReferenceSystem tailCRS = getSubCRS(
0506:                                sourceCRS, upperDim, sourceDim);
0507:                        CoordinateReferenceSystem[] components = new CoordinateReferenceSystem[3];
0508:                        int count = 0;
0509:                        if (headCRS != null)
0510:                            components[count++] = headCRS;
0511:                        components[count++] = crs2D;
0512:                        if (tailCRS != null)
0513:                            components[count++] = tailCRS;
0514:                        components = (CoordinateReferenceSystem[]) XArray
0515:                                .resize(components, count);
0516:                        if (count == 1) {
0517:                            targetCRS = components[0];
0518:                        } else
0519:                            try {
0520:                                targetCRS = ReferencingFactoryFinder
0521:                                        .getCRSFactory(hints)
0522:                                        .createCompoundCRS(
0523:                                                Collections
0524:                                                        .singletonMap(
0525:                                                                IdentifiedObject.NAME_KEY,
0526:                                                                crs2D
0527:                                                                        .getName()
0528:                                                                        .getCode()),
0529:                                                components);
0530:                            } catch (FactoryException exception) {
0531:                                throw new CannotReprojectException(exception
0532:                                        .getLocalizedMessage(), exception);
0533:                            }
0534:                    }
0535:                    /*
0536:                     * Constructs the 'gridToCRS' transform in the same way than the CRS:
0537:                     * leading and trailing dimensions (if any) are preserved.
0538:                     */
0539:                    final MathTransform toSource2D = geometry.getGridToCRS2D();
0540:                    final MathTransform toSource = geometry.getGridToCRS();
0541:                    MathTransform toTarget;
0542:                    if (CRS.equalsIgnoreMetadata(gridToCrs2D, toSource2D)) {
0543:                        toTarget = toSource;
0544:                    } else {
0545:                        /*
0546:                         * Replaces the 2D part in the source MT, while preserving the leading and
0547:                         * trailing MT (if any). This is similar to the 'lowerDim' and 'upperDim'
0548:                         * variables in the CRS case above, except that we operate on "grid" space
0549:                         * rather than "axis" spaces. The index are usually the same, but not always.
0550:                         */
0551:                        final int lowerDim = Math.min(geometry.gridDimensionX,
0552:                                geometry.gridDimensionY);
0553:                        final int upperDim = Math.max(geometry.gridDimensionX,
0554:                                geometry.gridDimensionY) + 1;
0555:                        final int sourceDim = toSource.getSourceDimensions();
0556:                        if (upperDim - lowerDim != toSource2D
0557:                                .getSourceDimensions()) {
0558:                            // TODO: localize
0559:                            throw new InvalidGridGeometryException(
0560:                                    "Unsupported math transform.");
0561:                        }
0562:                        final MathTransformFactory factory = ReferencingFactoryFinder
0563:                                .getMathTransformFactory(hints);
0564:                        final DimensionFilter filter = new DimensionFilter(
0565:                                factory);
0566:                        toTarget = gridToCrs2D;
0567:                        try {
0568:                            if (lowerDim != 0) {
0569:                                filter.addSourceDimensionRange(0, lowerDim);
0570:                                MathTransform step = filter.separate(toSource);
0571:                                ensureStableDimensions(filter);
0572:                                step = factory.createPassThroughTransform(0,
0573:                                        step, sourceDim - lowerDim);
0574:                                toTarget = factory.createConcatenatedTransform(
0575:                                        step, toTarget);
0576:                            }
0577:                            if (upperDim != sourceDim) {
0578:                                filter.clear();
0579:                                filter.addSourceDimensionRange(upperDim,
0580:                                        sourceDim);
0581:                                MathTransform step = filter.separate(toSource);
0582:                                ensureStableDimensions(filter);
0583:                                step = factory.createPassThroughTransform(
0584:                                        upperDim, step, 0);
0585:                                toTarget = factory.createConcatenatedTransform(
0586:                                        toTarget, step);
0587:                            }
0588:                        } catch (FactoryException exception) {
0589:                            throw new CannotReprojectException(Errors.format(
0590:                                    ErrorKeys.CANT_REPROJECT_$1, source
0591:                                            .getName()), exception);
0592:                        }
0593:                    }
0594:                    final GridGeometry2D targetGeom = new GridGeometry2D(null,
0595:                            toTarget, targetCRS);
0596:                    final ParameterValueGroup param = processor.getOperation(
0597:                            "Resample").getParameters();
0598:                    param.parameter("Source").setValue(source);
0599:                    param.parameter("GridGeometry").setValue(targetGeom);
0600:                    param.parameter("CoordinateReferenceSystem").setValue(
0601:                            targetCRS);
0602:                    sources[i] = (GridCoverage2D) processor.doOperation(param);
0603:                }
0604:            }
0605:
0606:            /**
0607:             * Applies a JAI operation to a grid coverage. This method is invoked automatically by
0608:             * {@link #doOperation}. The default implementation performs the following steps:
0609:             *
0610:             * <ul>
0611:             *   <li>Gets the {@linkplain GridSampleDimension sample dimensions} for the target images by
0612:             *       invoking the {@link #deriveSampleDimension deriveSampleDimension(...)} method.</li>
0613:             *   <li>Applied the JAI operation using {@link #createRenderedImage}.</li>
0614:             *   <li>Wraps the result in a {@link GridCoverage2D} object.</li>
0615:             * </ul>
0616:             *
0617:             * @param  sources The source coverages.
0618:             * @param  parameters Parameters, rendering hints and coordinate reference system to use.
0619:             * @return The result as a grid coverage.
0620:             *
0621:             * @see #doOperation
0622:             * @see #deriveSampleDimension
0623:             * @see JAI#createNS
0624:             */
0625:            protected GridCoverage2D deriveGridCoverage(
0626:                    final GridCoverage2D[] sources, final Parameters parameters) {
0627:                GridCoverage2D primarySource = sources[PRIMARY_SOURCE_INDEX];
0628:                /*
0629:                 * Gets the target SampleDimensions. If they are identical to the SampleDimensions of
0630:                 * one of the source GridCoverage2D, then this GridCoverage2D will be used at the primary
0631:                 * source. It will affect the target GridCoverage2D's name and the visible band. Then,
0632:                 * a new color model will be constructed from the new SampleDimensions, taking in
0633:                 * account the visible band.
0634:                 */
0635:                final GridSampleDimension[][] list = new GridSampleDimension[sources.length][];
0636:                for (int i = 0; i < list.length; i++) {
0637:                    list[i] = sources[i].getSampleDimensions();
0638:                }
0639:                final GridSampleDimension[] sampleDims = deriveSampleDimension(
0640:                        list, parameters);
0641:                int primarySourceIndex = -1;
0642:                for (int i = 0; i < list.length; i++) {
0643:                    if (Arrays.equals(sampleDims, list[i])) {
0644:                        primarySource = sources[i];
0645:                        primarySourceIndex = i;
0646:                        break;
0647:                    }
0648:                }
0649:                /*
0650:                 * Set the rendering hints image layout. Only the following properties will be set:
0651:                 *
0652:                 *     - Color model
0653:                 *     - Tile width
0654:                 *     - Tile height
0655:                 */
0656:                RenderingHints hints = ImageUtilities
0657:                        .getRenderingHints(parameters.getSource());
0658:                ImageLayout layout = (hints != null) ? (ImageLayout) hints
0659:                        .get(JAI.KEY_IMAGE_LAYOUT) : null;
0660:                if (layout == null
0661:                        || !layout.isValid(ImageLayout.COLOR_MODEL_MASK)) {
0662:                    if (sampleDims != null && sampleDims.length != 0) {
0663:                        if (layout == null) {
0664:                            layout = new ImageLayout();
0665:                        }
0666:                        int visibleBand = CoverageUtilities
0667:                                .getVisibleBand(primarySource
0668:                                        .getRenderedImage());
0669:                        if (visibleBand >= sampleDims.length) {
0670:                            visibleBand = 0;
0671:                        }
0672:                        final ColorModel colors;
0673:                        colors = sampleDims[visibleBand].getColorModel(
0674:                                visibleBand, sampleDims.length);
0675:                        layout = layout.setColorModel(colors);
0676:                    }
0677:                }
0678:                if (layout != null) {
0679:                    if (hints == null) {
0680:                        hints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout);
0681:                    } else {
0682:                        hints.put(JAI.KEY_IMAGE_LAYOUT, layout);
0683:                    }
0684:                }
0685:                if (parameters.hints != null) {
0686:                    if (hints != null) {
0687:                        hints.add(parameters.hints); // May overwrite the image layout we have just set.
0688:                    } else {
0689:                        hints = parameters.hints;
0690:                    }
0691:                }
0692:                /*
0693:                 * Performs the operation using JAI and construct the new grid coverage.
0694:                 * Uses the coordinate system from the main source coverage in order to
0695:                 * preserve the extra dimensions (if any). The first two dimensions should
0696:                 * be equal to the coordinate system set in the 'parameters' block.
0697:                 */
0698:                final InternationalString name = deriveName(sources,
0699:                        primarySourceIndex, parameters);
0700:                final CoordinateReferenceSystem crs = primarySource
0701:                        .getCoordinateReferenceSystem();
0702:                final MathTransform toCRS = primarySource.getGridGeometry()
0703:                        .getGridToCRS();
0704:                final RenderedImage data = createRenderedImage(
0705:                        parameters.parameters, hints);
0706:                final Map properties = getProperties(data, crs, name, toCRS,
0707:                        sources, parameters);
0708:                return getFactory(parameters.hints).create(name, // The grid coverage name
0709:                        data, // The underlying data
0710:                        crs, // The coordinate system (may not be 2D).
0711:                        toCRS, // The grid transform (may not be 2D).
0712:                        sampleDims, // The sample dimensions
0713:                        sources, // The source grid coverages.
0714:                        properties); // Properties
0715:            }
0716:
0717:            /**
0718:             * Prepares the properties to be given to the coverage created by the
0719:             * {@link #deriveGridCoverage deriveGridCoverage} method. The default
0720:             * implementation returns {@code null}.
0721:             * 
0722:             * @param data
0723:             *            The {@link RenderedImage} created by this operation.
0724:             * @param crs
0725:             *            The coordinate reference system assigned to the coverage this
0726:             *            {@code OperationJAI} will produce.
0727:             * @param name
0728:             *            The name assigned to the coverage this {@code OperationJAI} will produce.
0729:             * @param gridToCRS
0730:             *            The {@linkplain MathTransform transform} from grid to {@code crs} to be
0731:             *            assigned to the coverage this {@link OperationJAI} will produce.
0732:             * @param sources
0733:             *            The sources to be assigned to the coverage this {@link OperationJAI} will
0734:             *            produce.
0735:             * @param parameters
0736:             *            The parameters that were used by this {@link OperationJAI}.
0737:             * @return a {@link Map} with the properties generated by this
0738:             *         {@link OperationJAI} or null if we haven't any.
0739:             *
0740:             * @since 2.4
0741:             */
0742:            protected Map getProperties(RenderedImage data,
0743:                    CoordinateReferenceSystem crs, InternationalString name,
0744:                    MathTransform gridToCRS, GridCoverage2D[] sources,
0745:                    Parameters parameters) {
0746:                return null;
0747:            }
0748:
0749:            /**
0750:             * Returns the index of the quantitative category, providing that there is
0751:             * one and only one quantitative category. If {@code categories} contains 0,
0752:             * 2 or more quantative category, then this method returns {@code -1}.
0753:             *
0754:             * @since 2.4
0755:             */
0756:            protected static int getQuantitative(final Category[] categories) {
0757:                int index = -1;
0758:                for (int i = 0; i < categories.length; i++) {
0759:                    if (categories[i].isQuantitative()) {
0760:                        if (index >= 0) {
0761:                            return -1;
0762:                        }
0763:                        index = i;
0764:                    }
0765:                }
0766:                return index;
0767:            }
0768:
0769:            /**
0770:             * Returns the {@linkplain GridSampleDimension sample dimensions} for the target
0771:             * {@linkplain GridCoverage2D grid coverage}. This method is invoked automatically by
0772:             * {@link #deriveGridCoverage deriveGridCoverage} with a {@code bandLists} argument
0773:             * initialized as below:
0774:             * <p>
0775:             * <ul>
0776:             *   <li>The {@code bandLists} array length is equals to the number of source coverages.</li>
0777:             *   <li>The <code>bandLists[<var>i</var>]</code> array length is equals to the number of
0778:             *       sample dimensions in the source coverage <var>i</var>.</li>
0779:             *   <li>The sample dimension for a band at index <var>band</var> in the source at index
0780:             *       <var>source</var> is {@code bandLists[source][band]}.</li>
0781:             * </ul>
0782:             * <p>
0783:             * This method shall returns an array with a length equals to the number of bands in the target
0784:             * image. If the sample dimensions can't be determined, then this method is allowed to returns
0785:             * {@code null}.
0786:             * <p>
0787:             * The default implementation iterates among all bands and invokes the {@link #deriveCategory
0788:             * deriveCategory} and {@link #deriveUnit deriveUnit} methods for each of them. Subclasses
0789:             * should override this method if they know a more accurate algorithm for determining sample
0790:             * dimensions.
0791:             *
0792:             * @param  bandLists The set of sample dimensions for each source {@link GridCoverage2D}s.
0793:             * @param  parameters Parameters, rendering hints and coordinate reference system to use.
0794:             * @return The sample dimensions for each band in the destination image, or {@code null}
0795:             *         if unknow.
0796:             *
0797:             * @see #deriveCategory
0798:             * @see #deriveUnit
0799:             */
0800:            protected GridSampleDimension[] deriveSampleDimension(
0801:                    final GridSampleDimension[][] bandLists,
0802:                    final Parameters parameters) {
0803:                /*
0804:                 * Computes the number of bands. Sources with only 1 band are treated as a special case:
0805:                 * their unique band is applied to all bands in other sources. If sources don't have the
0806:                 * same number of bands, then this method returns {@code null} since we don't know how to
0807:                 * handle those cases.
0808:                 */
0809:                int numBands = 1;
0810:                for (int i = 0; i < bandLists.length; i++) {
0811:                    final int nb = bandLists[i].length;
0812:                    if (nb != 1) {
0813:                        if (numBands != 1 && nb != numBands) {
0814:                            return null;
0815:                        }
0816:                        numBands = nb;
0817:                    }
0818:                }
0819:                /*
0820:                 * Iterates among all bands. The 'result' array will contains SampleDimensions created
0821:                 * during the iteration for each individual band. The 'XS' suffix designates temporary
0822:                 * arrays of categories and units accross all sources for one particular band.
0823:                 */
0824:                final GridSampleDimension[] result = new GridSampleDimension[numBands];
0825:                final Category[] categoryXS = new Category[bandLists.length];
0826:                final Unit[] unitXS = new Unit[bandLists.length];
0827:                while (--numBands >= 0) {
0828:                    GridSampleDimension sampleDim = null;
0829:                    Category[] categoryArray = null;
0830:                    int indexOfQuantitative = 0;
0831:                    assert PRIMARY_SOURCE_INDEX == 0; // See comment below.
0832:                    for (int i = bandLists.length; --i >= 0;) {
0833:                        /*
0834:                         * Iterates among all sources (i) for the current band. We iterate
0835:                         * sources in reverse order because the primary source MUST be the
0836:                         * last one iterated, in order to have proper values for variables
0837:                         * 'sampleDim', 'categoryArray' and 'indexOfQuantitative' after the
0838:                         * loop.
0839:                         */
0840:                        final GridSampleDimension[] allBands = bandLists[i];
0841:                        sampleDim = allBands[allBands.length == 1 ? 0
0842:                                : numBands];
0843:                        categoryArray = (Category[]) sampleDim.getCategories()
0844:                                .toArray();
0845:                        indexOfQuantitative = getQuantitative(categoryArray);
0846:                        if (indexOfQuantitative < 0) {
0847:                            return null;
0848:                        }
0849:                        unitXS[i] = sampleDim.getUnits();
0850:                        categoryXS[i] = categoryArray[indexOfQuantitative];
0851:                    }
0852:                    final Category oldCategory = categoryArray[indexOfQuantitative];
0853:                    final Unit oldUnit = sampleDim.getUnits();
0854:                    final Category newCategory = deriveCategory(categoryXS,
0855:                            parameters);
0856:                    final Unit newUnit = deriveUnit(unitXS, parameters);
0857:                    if (newCategory == null) {
0858:                        return null;
0859:                    }
0860:                    if (!oldCategory.equals(newCategory)
0861:                            || !Utilities.equals(oldUnit, newUnit)) {
0862:                        /*
0863:                         * Create a new sample dimension. Note that we use a null title, not the same
0864:                         * title than the original sample dimension, because the new sample dimension
0865:                         * may be quite different. For example the original sample dimension may be
0866:                         * about "Temperature" in °C units, and the new one about "Gradiant magnitude
0867:                         * of Temperature" in °C/km units. The GridSampleDimension constructor will
0868:                         * infers the title from what looks like the "main" category.
0869:                         */
0870:                        final CharSequence title = null;
0871:                        categoryArray[indexOfQuantitative] = newCategory;
0872:                        result[numBands] = new GridSampleDimension(title,
0873:                                categoryArray, newUnit);
0874:                    } else {
0875:                        // Reuse the category list from the primary source.
0876:                        result[numBands] = sampleDim;
0877:                    }
0878:                }
0879:                return result;
0880:            }
0881:
0882:            /**
0883:             * Returns the quantitative category for a single {@linkplain GridSampleDimension sample dimension}
0884:             * in the target {@linkplain GridCoverage2D grid coverage}. This method is invoked automatically
0885:             * by the {@link #deriveSampleDimension deriveSampleDimension} method for each band in the
0886:             * target image. The default implementation creates a default category from the target range
0887:             * of values returned by {@link #deriveRange deriveRange}.
0888:             *
0889:             * @param  categories The quantitative categories from every sources. For unary operations
0890:             *         like {@code "GradientMagnitude"}, this array has a length of 1. For binary
0891:             *         operations like {@code "add"} and {@code "multiply"}, this array has a length of 2.
0892:             * @param  parameters Parameters, rendering hints and coordinate reference system to use.
0893:             * @return The quantative category to use in the destination image, or {@code null} if unknow.
0894:             */
0895:            protected Category deriveCategory(final Category[] categories,
0896:                    final Parameters parameters) {
0897:                final NumberRange[] ranges = new NumberRange[categories.length];
0898:                for (int i = 0; i < ranges.length; i++) {
0899:                    ranges[i] = categories[i].getRange();
0900:                }
0901:                final NumberRange range = deriveRange(ranges, parameters);
0902:                if (range != null) {
0903:                    final Category category = categories[PRIMARY_SOURCE_INDEX];
0904:                    return new Category(category.getName(), category
0905:                            .getColors(),
0906:                            category.geophysics(false).getRange(), range)
0907:                            .geophysics(true);
0908:                }
0909:                return null;
0910:            }
0911:
0912:            /**
0913:             * Returns the range of value for a single {@linkplain GridSampleDimension sample dimension}
0914:             * in the target {@linkplain GridCoverage2D grid coverage}. This method is invoked automatically
0915:             * by the {@link #deriveCategory deriveCategory} method for each band in the target image.
0916:             * Subclasses should override this method in order to compute the target range of values.
0917:             * For example, the {@code "add"} operation may implements this method as below:
0918:             *
0919:             * <blockquote><pre>
0920:             * double min = ranges[0].getMinimum() + ranges[1].getMinimum();
0921:             * double max = ranges[0}.getMaximum() + ranges[1}.getMaximum();
0922:             * return new NumberRange(min, max);
0923:             * </pre></blockquote>
0924:             *
0925:             * @param  ranges The range of values from every sources. For unary operations like
0926:             *         {@code "GradientMagnitude"}, this array has a length of 1. For binary operations
0927:             *         like {@code "add"} and {@code "multiply"}, this array has a length of 2.
0928:             * @param  parameters Parameters, rendering hints and coordinate reference system to use.
0929:             * @return The range of values to use in the destination image, or {@code null} if unknow.
0930:             */
0931:            protected NumberRange deriveRange(final NumberRange[] ranges,
0932:                    final Parameters parameters) {
0933:                return null;
0934:            }
0935:
0936:            /**
0937:             * Returns the unit of data for a single {@linkplain GridSampleDimension sample dimension} in the
0938:             * target {@linkplain GridCoverage2D grid coverage}. This method is invoked automatically by
0939:             * the {@link #deriveSampleDimension deriveSampleDimension} method for each band in the target
0940:             * image. Subclasses should override this method in order to compute the target units from the
0941:             * source units. For example a {@code "multiply"} operation may implement this method as below:
0942:             *
0943:             * <blockquote><pre>
0944:             * if (units[0]!=null && units[1]!=null) {
0945:             *     return units[0].{@link Unit#multiply(Unit) multiply}(units[1]);
0946:             * } else {
0947:             *     return super.deriveUnit(units, cs, parameters);
0948:             * }
0949:             * </pre></blockquote>
0950:             *
0951:             * @param  units The units from every sources. For unary operations like
0952:             *         {@code "GradientMagnitude"}, this array has a length of 1. For binary operations
0953:             *         like {@code "add"} and {@code "multiply"}, this array has a length of 2.
0954:             * @param  parameters Parameters, rendering hints and coordinate reference system to use.
0955:             * @return The unit of data in the destination image, or {@code null} if unknow.
0956:             */
0957:            protected Unit deriveUnit(final Unit[] units,
0958:                    final Parameters parameters) {
0959:                return null;
0960:            }
0961:
0962:            /**
0963:             * Returns a name for the target {@linkplain GridCoverage2D grid coverage} based on the given
0964:             * sources. This method is invoked once by the {@link #deriveGridCoverage deriveGridCoverage}
0965:             * method. The default implementation returns the operation name followed by the source name
0966:             * between parenthesis, for example "<cite>GradientMagnitude(Sea Surface Temperature)</cite>".
0967:             *
0968:             * @param  sources The sources grid coverage.
0969:             * @param  primarySourceIndex The index of what seems to be the primary source, or {@code -1}
0970:             *         if none of unknow.
0971:             * @param  parameters Parameters, rendering hints and coordinate reference system to use.
0972:             * @return A name for the target grid coverage.
0973:             */
0974:            protected InternationalString deriveName(
0975:                    final GridCoverage2D[] sources,
0976:                    final int primarySourceIndex, final Parameters parameters) {
0977:                final InternationalString[] names;
0978:                if (primarySourceIndex >= 0) {
0979:                    names = new InternationalString[] { sources[primarySourceIndex]
0980:                            .getName() };
0981:                } else {
0982:                    names = new InternationalString[sources.length];
0983:                    for (int i = 0; i < names.length; i++) {
0984:                        names[i] = sources[i].getName();
0985:                    }
0986:                }
0987:                return new Name(getName(), names);
0988:            }
0989:
0990:            /**
0991:             * A localized name for the default implementation of {@link OperationJAI#deriveName}.
0992:             */
0993:            private static final class Name extends AbstractInternationalString
0994:                    implements  Serializable {
0995:                /** Serial number for cross-versions compatibility. */
0996:                private static final long serialVersionUID = -8096255331549347383L;
0997:
0998:                /** The operation name. */
0999:                private final String operation;
1000:
1001:                /** Names of source grid coverages. */
1002:                private final InternationalString[] sources;
1003:
1004:                /** Constructs a name from the given source names. */
1005:                public Name(final String operation,
1006:                        final InternationalString[] sources) {
1007:                    this .operation = operation;
1008:                    this .sources = sources;
1009:                }
1010:
1011:                /** Returns a string localized in the given locale. */
1012:                public String toString(final Locale locale) {
1013:                    final StringBuffer buffer = new StringBuffer(operation);
1014:                    buffer.append('(');
1015:                    for (int i = 0; i < sources.length; i++) {
1016:                        if (i != 0) {
1017:                            buffer.append(", ");
1018:                        }
1019:                        buffer.append(sources[i].toString(locale));
1020:                    }
1021:                    buffer.append(')');
1022:                    return buffer.toString();
1023:                }
1024:            }
1025:
1026:            /**
1027:             * Applies the JAI operation. The operation name can be fetch from {@link #operation}.
1028:             * The JAI instance to use can be fetch from {@link #getJAI}. The default implementation
1029:             * returns the following:
1030:             *
1031:             * <blockquote><pre>
1032:             * {@linkplain #getJAI getJAI}(hints).{@linkplain JAI#createNS createNS}({@linkplain #operation}.getName(), parameters, hints)
1033:             * </pre></blockquote></li>
1034:             *
1035:             * Subclasses may override this method in order to invokes a different JAI operation
1036:             * according the parameters.
1037:             *
1038:             * @param parameters The parameters to be given to JAI.
1039:             * @param hints The rendering hints to be given to JAI.
1040:             */
1041:            protected RenderedImage createRenderedImage(
1042:                    final ParameterBlockJAI parameters,
1043:                    final RenderingHints hints) {
1044:                return getJAI(hints).createNS(operation.getName(), parameters,
1045:                        hints);
1046:            }
1047:
1048:            /**
1049:             * Returns the {@link JAI} instance to use for operations on {@link RenderedImage}.
1050:             * If no JAI instance is defined for the {@link Hints#JAI_INSTANCE} key, then the
1051:             * default instance is returned.
1052:             *
1053:             * @param  hints The rendering hints, or {@code null} if none.
1054:             * @return The JAI instance to use (never {@code null}).
1055:             */
1056:            public static JAI getJAI(final RenderingHints hints) {
1057:                if (hints != null) {
1058:                    final Object value = hints.get(Hints.JAI_INSTANCE);
1059:                    if (value instanceof  JAI) {
1060:                        return (JAI) value;
1061:                    }
1062:                }
1063:                return JAI.getDefaultInstance();
1064:            }
1065:
1066:            /**
1067:             * Compares the specified object with this operation for equality.
1068:             */
1069:            public boolean equals(final Object object) {
1070:                if (object == this ) {
1071:                    // Slight optimisation
1072:                    return true;
1073:                }
1074:                if (super .equals(object)) {
1075:                    final OperationJAI that = (OperationJAI) object;
1076:                    return Utilities.equals(this .operation, that.operation);
1077:                }
1078:                return false;
1079:            }
1080:
1081:            /**
1082:             * A block of parameters for a {@link GridCoverage2D} processed by a {@link OperationJAI}.
1083:             * This parameter is given to the following methods:
1084:             *
1085:             * <ul>
1086:             *   <li>{@link OperationJAI#deriveSampleDimension deriveSampleDimension}</li>
1087:             *   <li>{@link OperationJAI#deriveCategory deriveCategory}</li>
1088:             *   <li>{@link OperationJAI#deriveUnit deriveUnit}</li>
1089:             * </ul>
1090:             *
1091:             * @since 2.2
1092:             * @version $Id: OperationJAI.java 27848 2007-11-12 13:10:32Z desruisseaux $
1093:             * @author Martin Desruisseaux
1094:             */
1095:            protected static final class Parameters {
1096:                /**
1097:                 * The two dimensional coordinate reference system for all sources and the
1098:                 * destination {@link GridCoverage2D}. Sources coverages will be projected in
1099:                 * this CRS as needed.
1100:                 */
1101:                public final CoordinateReferenceSystem crs;
1102:
1103:                /**
1104:                 * The "grid to coordinate reference system" transform common to all source grid coverages.
1105:                 */
1106:                public final MathTransform2D gridToCRS;
1107:
1108:                /**
1109:                 * The parameters to be given to the {@link JAI#createNS} method.
1110:                 */
1111:                public final ParameterBlockJAI parameters;
1112:
1113:                /**
1114:                 * The rendering hints to be given to the {@link JAI#createNS} method.
1115:                 * The {@link JAI} instance to use for the {@code createNS} call will
1116:                 * be fetch from the {@link Hints#JAI_INSTANCE} key.
1117:                 */
1118:                public final Hints hints;
1119:
1120:                /**
1121:                 * Constructs a new parameter block with the specified values.
1122:                 */
1123:                Parameters(final CoordinateReferenceSystem crs,
1124:                        final MathTransform2D gridToCRS,
1125:                        final ParameterBlockJAI parameters, final Hints hints) {
1126:                    this .crs = crs;
1127:                    this .gridToCRS = gridToCRS;
1128:                    this .parameters = parameters;
1129:                    this .hints = hints;
1130:                }
1131:
1132:                /**
1133:                 * Returns the first source image, or {@code null} if none.
1134:                 */
1135:                final RenderedImage getSource() {
1136:                    final int n = parameters.getNumSources();
1137:                    for (int i = 0; i < n; i++) {
1138:                        final Object source = parameters.getSource(i);
1139:                        if (source instanceof  RenderedImage) {
1140:                            return (RenderedImage) source;
1141:                        }
1142:                    }
1143:                    return null;
1144:                }
1145:            }
1146:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.