/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2001-2008, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.coverage.processing; import java.awt.RenderingHints; import java.awt.image.ColorModel; import java.awt.image.RenderedImage; import java.io.Serializable; import java.util.List; import java.util.Map; import java.util.Arrays; import java.util.Locale; import java.util.Collections; import javax.measure.unit.Unit; import javax.media.jai.ImageLayout; import javax.media.jai.JAI; import javax.media.jai.OperationRegistry; import javax.media.jai.OperationDescriptor; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.registry.RenderedRegistryMode; import org.opengis.coverage.Coverage; import org.opengis.coverage.processing.OperationNotFoundException; import org.opengis.referencing.FactoryException; import org.opengis.referencing.IdentifiedObject; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.MathTransform2D; import org.opengis.referencing.operation.MathTransformFactory; import org.opengis.referencing.operation.TransformException; import org.opengis.parameter.ParameterDescriptorGroup; import org.opengis.parameter.ParameterValueGroup; import org.opengis.util.InternationalString; import org.geotools.coverage.Category; import org.geotools.coverage.GridSampleDimension; import org.geotools.coverage.grid.ViewType; import org.geotools.coverage.grid.GridCoverage2D; import org.geotools.coverage.grid.GridGeometry2D; import org.geotools.coverage.grid.InvalidGridGeometryException; import org.geotools.factory.Hints; import org.geotools.parameter.ImagingParameters; import org.geotools.parameter.ImagingParameterDescriptors; import org.geotools.referencing.CRS; import org.geotools.referencing.ReferencingFactoryFinder; import org.geotools.referencing.operation.transform.DimensionFilter; import org.geotools.image.jai.Registry; import org.geotools.resources.XArray; import org.geotools.resources.CRSUtilities; import org.geotools.resources.coverage.CoverageUtilities; import org.geotools.resources.i18n.Errors; import org.geotools.resources.i18n.ErrorKeys; import org.geotools.resources.image.ImageUtilities; import org.geotools.util.AbstractInternationalString; import org.geotools.util.Utilities; import org.geotools.util.NumberRange; import org.geotools.util.logging.Logging; /** * Wraps a JAI's {@link OperationDescriptor} for interoperability with * Java Advanced Imaging. * This class help to leverage the rich set of JAI operators in an GeoAPI framework. * {@code OperationJAI} inherits operation name and argument types from {@link OperationDescriptor}, * except the source argument type (usually {@linkplain RenderedImage}.class) which is * set to {@linkplain GridCoverage2D}.class. If there is only one source argument, it * will be renamed {@code "source"} for better compliance with OpenGIS usage. *

* The entry point for applying an operation is the usual {@link #doOperation doOperation} method. * The default implementation forward the call to other methods for different bits of tasks, * resulting in the following chain of calls: *

*

* * * * * * * * * * * * * * * * *
{@link #doOperation doOperation}: the entry point.
{@link #resampleToCommonGeometry resampleToCommonGeometry}: reprojects all sources to the same coordinate reference system.
{@link #deriveGridCoverage deriveGridCoverage}: gets the destination properties.
{@link #deriveSampleDimension deriveSampleDimension}: gets the destination sample dimensions.
{@link #deriveCategory deriveCategory}: gets the destination categories.
{@link #deriveRange deriveRange}: gets the expected range of values.
{@link #deriveUnit deriveUnit}: gets the destination units.
{@link #createRenderedImage createRenderedImage}: the actual call to {@link JAI#createNS JAI.createNS}.
* * @since 2.2 * * * @source $URL$ * @version $Id$ * @author Martin Desruisseaux (IRD) * @author Simone Giannecchini */ public class OperationJAI extends Operation2D { /** * Serial number for interoperability with different versions. */ private static final long serialVersionUID = -5974520239347639965L; /** * The rendered mode for JAI operation. */ protected static final String RENDERED_MODE = RenderedRegistryMode.MODE_NAME; /** * The JAI's operation descriptor. */ protected final OperationDescriptor operation; /** * Constructs a grid coverage operation from a JAI operation name. This convenience * constructor fetch the {@link OperationDescriptor} from the specified operation * name using the default {@link JAI} instance. * * @param operation JAI operation name (e.g. {@code "GradientMagnitude"}). * @throws OperationNotFoundException if no JAI descriptor was found for the given name. */ public OperationJAI(final String operation) throws OperationNotFoundException { this(getOperationDescriptor(operation)); } /** * Constructs a grid coverage operation backed by a JAI operation. The operation descriptor * must supports the {@code "rendered"} mode (which is the case for most JAI operations). * * @param operation The JAI operation descriptor. */ public OperationJAI(final OperationDescriptor operation) { this(operation, new ImagingParameterDescriptors(operation)); } /** * Constructs a grid coverage operation backed by a JAI operation. The operation descriptor * must supports the {@code "rendered"} mode (which is the case for most JAI operations). * * @param operation The JAI operation descriptor. * @param descriptor The OGC parameters descriptor. */ protected OperationJAI(final OperationDescriptor operation, final ParameterDescriptorGroup descriptor) { super(descriptor); this.operation = operation; Utilities.ensureNonNull("operation", operation); /* * Check argument validity. */ ensureRenderedImage(operation.getDestClass(RENDERED_MODE)); final Class[] sourceClasses = operation.getSourceClasses(RENDERED_MODE); if (sourceClasses != null) { final int length = sourceClasses.length; assert length == operation.getNumSources(); for (int i=0; i classe) throws IllegalArgumentException { if (!RenderedImage.class.isAssignableFrom(classe)) { // TODO: provide localized message throw new IllegalArgumentException(classe.getName()); } } /** * Copies parameter values from the specified {@link ParameterValueGroup} to the * {@link ParameterBlockJAI}, except the sources. *

* Note: it would be possible to use {@link ImagingParameters#parameters} * directly in some occasions. However, we perform an unconditional copy instead * because some operations (e.g. "GradientMagnitude") may change the values. * * @param parameters The {@link ParameterValueGroup} to be copied. * @return A copy of the provided {@link ParameterValueGroup} as a JAI block. * * @since 2.4 */ protected ParameterBlockJAI prepareParameters(final ParameterValueGroup parameters) { final ImagingParameters copy = (ImagingParameters) descriptor.createValue(); final ParameterBlockJAI block = (ParameterBlockJAI) copy.parameters; org.geotools.parameter.Parameters.copy(parameters, copy); return block; } /** * Applies a process operation to a grid coverage. * The default implementation performs the following steps: * *

    *
  1. Converts source grid coverages to their geophysics view using * {@linkplain GridCoverage2D#geophysics GridCoverage2D.geophysics}(true). * This allow to performs all computation on geophysics values instead of encoded * samples. Note: this step is disabled if * {@link #computeOnGeophysicsValues computeOnGeophysicsValues} returns * {@code false}.
  2. * *
  3. Ensures that every sources {@code GridCoverage2D}s use the same coordinate reference * system (at least for the two-dimensional part) with the same * {@link GridGeometry2D#getGridToCRS2D gridToCRS} relationship.
  4. * *
  5. Invokes {@link #deriveGridCoverage}. * The sources in the {@code ParameterBlock} are {@link RenderedImage} objects * obtained from {@link GridCoverage2D#getRenderedImage()}.
  6. * *
  7. If a changes from non-geophysics to geophysics view were performed at step 1, * converts the result back to the original view using * {@linkplain GridCoverage2D#geophysics GridCoverage2D.geophysics}(false). *
  8. *
* * @param parameters List of name value pairs for the parameters required for the operation. * @param hints A set of rendering hints, or {@code null} if none. * @return The result as a grid coverage. * @throws CoverageProcessingException if the operation can't be applied. * * @see #deriveGridCoverage */ public Coverage doOperation(final ParameterValueGroup parameters, final Hints hints) throws CoverageProcessingException { final ParameterBlockJAI block = prepareParameters(parameters); /* * Extracts the source grid coverages now as an array. The sources will be set in the * ParameterBlockJAI (as RenderedImages) later, after the reprojection performed in the * next block. */ final String[] sourceNames = operation.getSourceNames(); final GridCoverage2D[] sources = new GridCoverage2D[sourceNames.length]; ViewType primarySourceType = extractSources(parameters, sourceNames, sources); /* * Ensures that all coverages use the same CRS and has the same 'gridToCRS' relationship. * After the reprojection, the method still checks all CRS in case the user overridden the * {@link #resampleToCommonGeometry} method. */ resampleToCommonGeometry(sources, null, null, hints); GridCoverage2D coverage = sources[PRIMARY_SOURCE_INDEX]; final CoordinateReferenceSystem crs = coverage.getCoordinateReferenceSystem2D(); // TODO: remove the cast when we will be allowed to compile for J2SE 1.5. final MathTransform2D gridToCRS = coverage.getGridGeometry().getGridToCRS2D(); for (int i=0; inot forced to a common geometry; only the two dimensions that apply to a * {@link javax.media.jai.PlanarImage} are. This is because the extra dimensions don't need to * be compatible for all operations. For example if a source image is a slice in a time series, * a second source image could be a slice in the frequency representation of this time series. *

* Subclasses should override this method if they want to specify target * {@linkplain GridGeometry2D grid geometry} and * {@linkplain CoordinateReferenceSystem coordinate reference system} different than the * default ones. For example if a subclass wants to force all images to be referenced in a * {@linkplain org.geotools.referencing.crs.DefaultGeographicCRS#WGS84 WGS 84} CRS, then * it may overrides this method as below: * *

     * protected void resampleToCommonGeometry(...) {
     *    crs2D = DefaultGeographicCRS.WGS84;
     *    super.resampleToCommonGeometry(sources, crs2D, gridToCrs2D, hints);
     * }
* * @param sources The source grid coverages to resample. This array is updated in-place as * needed (for example if a grid coverage is replaced by a projected one). * @param crs2D The target coordinate reference system to use, or {@code null} for a * default one. * @param gridToCrs2D The target "grid to coordinate reference system" transform, or * {@code null} for a default one. * @param hints The rendering hints, or {@code null} if none. * * @throws InvalidGridGeometryException if a source coverage has an unsupported grid geometry. * @throws CannotReprojectException if a grid coverage can't be resampled for some other reason. */ protected void resampleToCommonGeometry(final GridCoverage2D[] sources, CoordinateReferenceSystem crs2D, MathTransform2D gridToCrs2D, final Hints hints) throws InvalidGridGeometryException, CannotReprojectException { if (sources==null || sources.length==0) { return; // Nothing to reproject. } /* * Ensures that the target CRS is two-dimensional. If no target CRS were specified, * uses the CRS of the primary source. The math transform must be 2D too, but this * is ensured by the interface type (MathTransform2D). */ final GridCoverage2D primarySource = sources[PRIMARY_SOURCE_INDEX]; if (crs2D == null) { if (gridToCrs2D==null && sources.length==1) { return; // No need to reproject. } crs2D = primarySource.getCoordinateReferenceSystem2D(); } else try { crs2D = CRSUtilities.getCRS2D(crs2D); } catch (TransformException exception) { // TODO: localize throw new CannotReprojectException("Unsupported CRS: "+crs2D.getName().getCode()); } if (gridToCrs2D == null) { gridToCrs2D = primarySource.getGridGeometry().getGridToCRS2D(); } /* * 'crs2D' is the two dimensional part of the target CRS. Now for each source coverages, * substitute their two-dimensional CRS by this 'crs2D'. A source may have more than two * dimensions. For example it may have a time or a depth axis. In such case, their "head" * and "tail" CRS will be preserved before and after 'crs2D'. */ final CoverageProcessor processor = CoverageProcessor.getInstance(hints); for (int i=0; i *
  • Gets the {@linkplain GridSampleDimension sample dimensions} for the target images by * invoking the {@link #deriveSampleDimension deriveSampleDimension(...)} method.
  • *
  • Applied the JAI operation using {@link #createRenderedImage}.
  • *
  • Wraps the result in a {@link GridCoverage2D} object.
  • * * * @param sources The source coverages. * @param parameters Parameters, rendering hints and coordinate reference system to use. * @return The result as a grid coverage. * * @see #doOperation * @see #deriveSampleDimension * @see JAI#createNS */ protected GridCoverage2D deriveGridCoverage(final GridCoverage2D[] sources, final Parameters parameters) { GridCoverage2D primarySource = sources[PRIMARY_SOURCE_INDEX]; /* * Gets the target SampleDimensions. If they are identical to the SampleDimensions of * one of the source GridCoverage2D, then this GridCoverage2D will be used at the primary * source. It will affect the target GridCoverage2D's name and the visible band. Then, * a new color model will be constructed from the new SampleDimensions, taking in * account the visible band. */ final GridSampleDimension[][] list = new GridSampleDimension[sources.length][]; for (int i=0; i= sampleDims.length) { visibleBand = 0; } final ColorModel colors; colors = sampleDims[visibleBand].getColorModel(visibleBand, sampleDims.length); if (colors != null) { if (layout == null) { layout = new ImageLayout(); } layout = layout.setColorModel(colors); } } } if (layout != null) { if (hints == null) { hints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout); } else { hints.put(JAI.KEY_IMAGE_LAYOUT, layout); } } if (parameters.hints != null) { if (hints != null) { hints.add(parameters.hints); // May overwrite the image layout we have just set. } else { hints = parameters.hints; } } /* * Performs the operation using JAI and construct the new grid coverage. * Uses the coordinate system from the main source coverage in order to * preserve the extra dimensions (if any). The first two dimensions should * be equal to the coordinate system set in the 'parameters' block. */ final InternationalString name = deriveName(sources, primarySourceIndex, parameters); final CoordinateReferenceSystem crs = primarySource.getCoordinateReferenceSystem(); final MathTransform toCRS = primarySource.getGridGeometry().getGridToCRS(); final RenderedImage data = createRenderedImage(parameters.parameters, hints); final Map properties = getProperties(data,crs,name,toCRS,sources,parameters); return getFactory(parameters.hints) .create(name, // The grid coverage name data, // The underlying data crs, // The coordinate system (may not be 2D). toCRS, // The grid transform (may not be 2D). sampleDims, // The sample dimensions sources, // The source grid coverages. properties); // Properties } /** * Prepares the properties to be given to the coverage created by the * {@link #deriveGridCoverage deriveGridCoverage} method. The default * implementation returns {@code null}. * * @param data * The {@link RenderedImage} created by this operation. * @param crs * The coordinate reference system assigned to the coverage this * {@code OperationJAI} will produce. * @param name * The name assigned to the coverage this {@code OperationJAI} will produce. * @param gridToCRS * The {@linkplain MathTransform transform} from grid to {@code crs} to be * assigned to the coverage this {@link OperationJAI} will produce. * @param sources * The sources to be assigned to the coverage this {@link OperationJAI} will * produce. * @param parameters * The parameters that were used by this {@link OperationJAI}. * @return a {@link Map} with the properties generated by this * {@link OperationJAI} or null if we haven't any. * * @since 2.4 */ protected Map getProperties(RenderedImage data, CoordinateReferenceSystem crs, InternationalString name, MathTransform gridToCRS, GridCoverage2D[] sources, Parameters parameters) { return null; } /** * Returns the index of the quantitative category, providing that there is * one and only one quantitative category. If {@code categories} contains 0, * 2 or more quantitative category, then this method returns {@code -1}. * * @param categories The categories to test. * @return The index of the quantitative category, or {@code -1} if none can be choosen. * * @since 2.4 */ protected static int getQuantitative(final Category[] categories) { int index = -1; for (int i=0; i= 0) { return -1; } index = i; } } return index; } /** * Returns the {@linkplain GridSampleDimension sample dimensions} for the target * {@linkplain GridCoverage2D grid coverage}. This method is invoked automatically by * {@link #deriveGridCoverage deriveGridCoverage} with a {@code bandLists} argument * initialized as below: *

    *

      *
    • The {@code bandLists} array length is equals to the number of source coverages.
    • *
    • The bandLists[i] array length is equals to the number of * sample dimensions in the source coverage i.
    • *
    • The sample dimension for a band at index band in the source at index * source is {@code bandLists[source][band]}.
    • *
    *

    * This method shall returns an array with a length equals to the number of bands in the target * image. If the sample dimensions can't be determined, then this method is allowed to returns * {@code null}. *

    * The default implementation iterates among all bands and invokes the {@link #deriveCategory * deriveCategory} and {@link #deriveUnit deriveUnit} methods for each of them. Subclasses * should override this method if they know a more accurate algorithm for determining sample * dimensions. * * @param bandLists The set of sample dimensions for each source {@link GridCoverage2D}s. * @param parameters Parameters, rendering hints and coordinate reference system to use. * @return The sample dimensions for each band in the destination image, or {@code null} * if unknown. * * @see #deriveCategory * @see #deriveUnit */ protected GridSampleDimension[] deriveSampleDimension(final GridSampleDimension[][] bandLists, final Parameters parameters) { /* * Computes the number of bands. Sources with only 1 band are treated as a special case: * their unique band is applied to all bands in other sources. If sources don't have the * same number of bands, then this method returns {@code null} since we don't know how to * handle those cases. */ int numBands = 1; for (int i=0; i[] unitXS = new Unit[bandLists.length]; while (--numBands >= 0) { GridSampleDimension sampleDim = null; Category[] categoryArray = null; int indexOfQuantitative = 0; assert PRIMARY_SOURCE_INDEX == 0; // See comment below. for (int i=bandLists.length; --i>=0;) { if (bandLists[i] == null) { continue; } /* * Iterates among all sources (i) for the current band. We iterate * sources in reverse order because the primary source MUST be the * last one iterated, in order to have proper values for variables * 'sampleDim', 'categoryArray' and 'indexOfQuantitative' after the * loop. */ final GridSampleDimension[] allBands = bandLists[i]; sampleDim = allBands[allBands.length == 1 ? 0 : numBands]; final List categories = sampleDim.getCategories(); // GridSampleDimension may contain no categories if (categories == null || categories.isEmpty()) { result[numBands] = sampleDim; continue; } categoryArray = (Category[]) categories.toArray(); indexOfQuantitative = getQuantitative(categoryArray); if (indexOfQuantitative < 0) { return null; } unitXS [i] = sampleDim.getUnits(); categoryXS[i] = categoryArray[indexOfQuantitative]; } if (categoryArray == null) { continue; } final Category oldCategory = categoryArray[indexOfQuantitative]; final Unit oldUnit = sampleDim.getUnits(); final Category newCategory = deriveCategory(categoryXS, parameters); final Unit newUnit = deriveUnit(unitXS, parameters); if (newCategory == null) { return null; } if (!oldCategory.equals(newCategory) || !Utilities.equals(oldUnit, newUnit)) { /* * Create a new sample dimension. Note that we use a null title, not the same * title than the original sample dimension, because the new sample dimension * may be quite different. For example the original sample dimension may be * about "Temperature" in °C units, and the new one about "Gradiant magnitude * of Temperature" in °C/km units. The GridSampleDimension constructor will * infers the title from what looks like the "main" category. */ final CharSequence title = null; categoryArray[indexOfQuantitative] = newCategory; result[numBands] = new GridSampleDimension(title, categoryArray, newUnit); } else { // Reuse the category list from the primary source. result[numBands] = sampleDim; } } return result; } /** * Returns the quantitative category for a single {@linkplain GridSampleDimension sample dimension} * in the target {@linkplain GridCoverage2D grid coverage}. This method is invoked automatically * by the {@link #deriveSampleDimension deriveSampleDimension} method for each band in the * target image. The default implementation creates a default category from the target range * of values returned by {@link #deriveRange deriveRange}. * * @param categories The quantitative categories from every sources. For unary operations * like {@code "GradientMagnitude"}, this array has a length of 1. For binary * operations like {@code "add"} and {@code "multiply"}, this array has a length of 2. * @param parameters Parameters, rendering hints and coordinate reference system to use. * @return The quantitative category to use in the destination image, or {@code null} if unknown. */ protected Category deriveCategory(final Category[] categories, final Parameters parameters) { final NumberRange[] ranges = new NumberRange[categories.length]; for (int i=0; i

         * double min = ranges[0].getMinimum() + ranges[1].getMinimum();
         * double max = ranges[0}.getMaximum() + ranges[1}.getMaximum();
         * return new NumberRange(min, max);
         * 
    * * @param ranges The range of values from every sources. For unary operations like * {@code "GradientMagnitude"}, this array has a length of 1. For binary operations * like {@code "add"} and {@code "multiply"}, this array has a length of 2. * @param parameters Parameters, rendering hints and coordinate reference system to use. * @return The range of values to use in the destination image, or {@code null} if unknow. */ protected NumberRange deriveRange(final NumberRange[] ranges, final Parameters parameters) { return null; } /** * Returns the unit of data for a single {@linkplain GridSampleDimension sample dimension} in the * target {@linkplain GridCoverage2D grid coverage}. This method is invoked automatically by * the {@link #deriveSampleDimension deriveSampleDimension} method for each band in the target * image. Subclasses should override this method in order to compute the target units from the * source units. For example a {@code "multiply"} operation may implement this method as below: * *
         * if (units[0]!=null && units[1]!=null) {
         *     return units[0].{@link Unit#multiply(Unit) multiply}(units[1]);
         * } else {
         *     return super.deriveUnit(units, cs, parameters);
         * }
         * 
    * * @param units The units from every sources. For unary operations like * {@code "GradientMagnitude"}, this array has a length of 1. For binary operations * like {@code "add"} and {@code "multiply"}, this array has a length of 2. * @param parameters Parameters, rendering hints and coordinate reference system to use. * @return The unit of data in the destination image, or {@code null} if unknow. */ protected Unit deriveUnit(final Unit[] units, final Parameters parameters) { return null; } /** * Returns a name for the target {@linkplain GridCoverage2D grid coverage} based on the given * sources. This method is invoked once by the {@link #deriveGridCoverage deriveGridCoverage} * method. The default implementation returns the operation name followed by the source name * between parenthesis, for example "GradientMagnitude(Sea Surface Temperature)". * * @param sources The sources grid coverage. * @param primarySourceIndex The index of what seems to be the primary source, or {@code -1} * if none of unknown. * @param parameters Parameters, rendering hints and coordinate reference system to use. * @return A name for the target grid coverage. */ protected InternationalString deriveName(final GridCoverage2D[] sources, final int primarySourceIndex, final Parameters parameters) { final InternationalString[] names; if (primarySourceIndex >= 0) { names = new InternationalString[] { sources[primarySourceIndex].getName() }; } else { names = new InternationalString[sources.length]; for (int i=0; i
         * {@linkplain #getJAI getJAI}(hints).{@linkplain JAI#createNS createNS}({@linkplain #operation}.getName(), parameters, hints)
         * 
    * * Subclasses may override this method in order to invokes a different JAI operation * according the parameters. * * @param parameters The parameters to be given to JAI. * @param hints The rendering hints to be given to JAI. * @return The result of JAI operation using the given parameters and hints. */ protected RenderedImage createRenderedImage(final ParameterBlockJAI parameters, final RenderingHints hints) { return getJAI(hints).createNS(operation.getName(), parameters, hints); } /** * Returns the {@link JAI} instance to use for operations on {@link RenderedImage}. * If no JAI instance is defined for the {@link Hints#JAI_INSTANCE} key, then the * default instance is returned. * * @param hints The rendering hints, or {@code null} if none. * @return The JAI instance to use (never {@code null}). */ public final static JAI getJAI(final RenderingHints hints) { if (hints != null) { final Object value = hints.get(Hints.JAI_INSTANCE); if (value instanceof JAI) { return (JAI) value; } } return JAI.getDefaultInstance(); } // /** // * // * @param polygon // * @param worldToGridTransform // * @return // * @throws FactoryException // * @throws TransformException // */ // public final static ROI polygonToRoi(final Polygon polygon, // final MathTransform worldToGridTransform) throws TransformException, // FactoryException { // // return new ROIShape(new LiteShape2(polygon, worldToGridTransform, null, false)); // } // // /** // * // * @param polygon // * @param transform // * @return // * @throws FactoryException // * @throws TransformException // */ // public final static ROI polygonToRoi(final Polygon polygon, // final AffineTransform worldToGridTransform) throws TransformException, // FactoryException { // // return polygonToRoi(polygon, ProjectiveTransform.create(worldToGridTransform)); // } // /** * Compares the specified object with this operation for equality. */ @Override public boolean equals(final Object object) { if (object == this) { // Slight optimisation return true; } if (super.equals(object)) { final OperationJAI that = (OperationJAI) object; return Utilities.equals(this.operation, that.operation); } return false; } /** * A block of parameters for a {@link GridCoverage2D} processed by a {@link OperationJAI}. * This parameter is given to the following methods: * *
      *
    • {@link OperationJAI#deriveSampleDimension deriveSampleDimension}
    • *
    • {@link OperationJAI#deriveCategory deriveCategory}
    • *
    • {@link OperationJAI#deriveUnit deriveUnit}
    • *
    * * @since 2.2 * @version $Id$ * @author Martin Desruisseaux (IRD) */ protected static final class Parameters { /** * The two dimensional coordinate reference system for all sources and the * destination {@link GridCoverage2D}. Sources coverages will be projected in * this CRS as needed. */ public final CoordinateReferenceSystem crs; /** * The "grid to coordinate reference system" transform common to all source grid coverages. */ public final MathTransform2D gridToCRS; /** * The parameters to be given to the {@link JAI#createNS} method. */ public final ParameterBlockJAI parameters; /** * The rendering hints to be given to the {@link JAI#createNS} method. * The {@link JAI} instance to use for the {@code createNS} call will * be fetch from the {@link Hints#JAI_INSTANCE} key. */ public final Hints hints; /** * Constructs a new parameter block with the specified values. */ Parameters(final CoordinateReferenceSystem crs, final MathTransform2D gridToCRS, final ParameterBlockJAI parameters, final Hints hints) { this.crs = crs; this.gridToCRS = gridToCRS; this.parameters = parameters; this.hints = hints; } /** * Returns the first source image, or {@code null} if none. */ final RenderedImage getSource() { final int n = parameters.getNumSources(); for (int i=0; i