/*
* Geotools 2 - OpenSource mapping toolkit
* (C) 2003, Geotools Project Management Committee (PMC)
* (C) 2001, Institut de Recherche pour le Développement
*
* 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; either
* version 2.1 of the License, or (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*
* This package contains documentation from OpenGIS specifications.
* OpenGIS consortium's work is fully acknowledged here.
*
* @deprecated Replaced by {@link org.geotools.coverage.processing.OperationJAI}.
*/
package org.geotools.gp;
// J2SE dependencies
import java.awt.RenderingHints;
import java.awt.image.ColorModel;
import java.awt.image.RenderedImage;
import java.util.Arrays;
import java.util.Locale;
import java.util.MissingResourceException;
import javax.media.jai.ImageLayout;
import javax.media.jai.IntegerSequence;
import javax.media.jai.JAI;
import javax.media.jai.OperationDescriptor;
import javax.media.jai.ParameterBlockJAI;
import javax.media.jai.ParameterList;
import javax.media.jai.ParameterListDescriptor;
import javax.media.jai.ParameterListDescriptorImpl;
import javax.media.jai.registry.RenderedRegistryMode;
import javax.media.jai.util.Range;
import org.geotools.cs.CompoundCoordinateSystem;
import org.geotools.cs.CoordinateSystem;
import org.geotools.ct.CoordinateTransformationFactory;
import org.geotools.ct.MathTransform;
import org.geotools.ct.MathTransform2D;
import org.geotools.ct.MathTransformFactory;
import org.geotools.cv.Category;
import org.geotools.cv.SampleDimension;
import org.geotools.gc.GridCoverage;
import org.geotools.gc.GridGeometry;
import org.geotools.gc.InvalidGridGeometryException;
import org.geotools.resources.CTSUtilities;
import org.geotools.resources.LegacyGCSUtilities;
import org.geotools.resources.Utilities;
import org.geotools.resources.i18n.ErrorKeys;
import org.geotools.resources.i18n.Errors;
import org.geotools.resources.image.ImageUtilities;
import org.geotools.resources.image.JAIUtilities;
import org.geotools.units.Unit;
import org.opengis.referencing.FactoryException;
/**
* Wrap an {@link OperationDescriptor} for interoperability with
* Java Advanced Imaging.
* This class help to leverage the rich set of JAI operators in an OpenGIS framework.
* OperationJAI
inherits operation name and argument types from
* {@link OperationDescriptor}, except source argument type which is set to
* GridCoverage.class
. If there is only one source argument, il will be
* renamed "Source"
for better compliance to OpenGIS usage.
*
* The entry point for applying 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:
*
*
null
,
* then the JAI operation name is used.
* @param operationDescriptor The operation descriptor. This descriptor must supports
* supports the "rendered" mode (which is the case for most JAI operations).
* @param paramDescriptor The parameters descriptor. If null
,
* then it will be infered from the JAI's parameter descriptor.
*
* @throws NullPointerException if operationDescriptor
is null.
*/
protected OperationJAI(final String name,
final OperationDescriptor operationDescriptor,
final ParameterListDescriptor paramDescriptor)
{
super((name!=null) ? name : getName(operationDescriptor),
(paramDescriptor!=null) ? paramDescriptor
: getParameterListDescriptor(operationDescriptor));
this.descriptor = operationDescriptor;
}
/**
* Returns a name from the specified operation descriptor. If the
* name begin with "org.geotools" prefix, the prefix will be ignored.
*
* @task TODO: Should be inlined in the constructor if only Sun was to fix RFE #4093999
* ("Relax constraint on placement of this()/super() call in constructors").
*/
private static String getName(final OperationDescriptor descriptor) {
final String prefix = "org.geotools.";
String name = descriptor.getName();
if (name.startsWith(prefix)) {
name = name.substring(prefix.length());
}
return name;
}
/**
* Returns the operation descriptor for the specified JAI operation name.
* This method uses the default {@link JAI} instance and looks for the
* "rendered"
mode.
*
* @param name The operation name.
* @return The operation descriptor for the given name.
* @throws OperationNotFoundException if no JAI descriptor was found for the given name.
*/
protected static OperationDescriptor getOperationDescriptor(final String name)
throws OperationNotFoundException
{
final OperationDescriptor descriptor = (OperationDescriptor) JAI.getDefaultInstance().
getOperationRegistry().getDescriptor(RENDERED_MODE, name);
if (descriptor == null) {
throw new OperationNotFoundException(Errors.format(
ErrorKeys.OPERATION_NOT_FOUND_$1, name));
}
return descriptor;
}
/**
* Gets the parameter list descriptor for an operation descriptor.
* {@link OperationDescriptor} parameter list do not include sources.
* This method will add them in front of the parameter list.
*/
private static ParameterListDescriptor getParameterListDescriptor
(final OperationDescriptor descriptor)
{
ensureValid(descriptor.getDestClass(RENDERED_MODE));
final Class[] sourceClasses = descriptor.getSourceClasses(RENDERED_MODE);
if (sourceClasses != null) {
for (int i=0; inames
contains the element name
.
* Search is done in case-insensitive manner. This method is efficient enough
* if names
is very short (less than 10 entries).
*/
private static boolean contains(final String[] names, final String name) {
for (int i=0; inull
for the default locale.
*/
public String getDescription(Locale locale) {
if (locale == null) {
locale = Locale.getDefault();
}
try {
return descriptor.getResourceBundle(locale).getString("Description");
} catch (MissingResourceException exception) {
// No description available. Returns 'null', which is
// a legal value according this method specification.
return null;
}
}
/**
* Returns the number of source grid coverages required for the operation. The
* default implementation fetch the information from the {@linkplain #descriptor}.
*/
public int getNumSources() {
return descriptor.getNumSources();
}
/**
* Set a parameter. This method can be overriden in order to apply some conversions
* from OpenGIS to JAI parameters.
*
* @param block The parameter block in which to set a parameter.
* @param name The parameter OpenGIS name.
* @param value The parameter OpenGIS value.
*/
void setParameter(final ParameterBlockJAI block, final String name, final Object value) {
block.setParameter(name, value);
}
/**
* Returns true
if grid coverage should be transformed from sample values
* to geophysics value before to apply an operation.
*/
boolean computeOnGeophysicsValues() {
return true;
}
/**
* Apply a process operation to a grid coverage.
* The default implementation performs the following steps:
*
* {@link GridCoverage#geophysics GridCoverage.geophysics}(true)
.
* This allow to performs all computation on geophysics values instead of encoded
* samples.GridCoverage
s use the same coordinate
* system and have the same envelope.ParameterBlock
are {@link RenderedImage} objects
* obtained from {@link GridCoverage#getRenderedImage()}.GridCoverage
s was not a geophysics view, convert the
* result back to the same type with
* {@link GridCoverage#geophysics GridCoverage.geophysics}(false)
.null
if none.
* @return The result as a grid coverage.
*
* @see #deriveGridCoverage
*/
protected GridCoverage doOperation(final ParameterList parameters,
final RenderingHints hints)
{
/*
* Copy parameter values from the ParameterList to the ParameterBlockJAI.
* The sources GridCoverages are extracted in the process and the source
* RenderedImage are set in the ParameterBlockJAI. The first array of
* range specifiers, if any, is treated especialy.
*/
RangeSpecifier[] ranges = null;
Boolean requireGeophysicsType = null;
final ParameterBlockJAI block = new ParameterBlockJAI(descriptor, RENDERED_MODE);
final String[] paramNames = parameters.getParameterListDescriptor().getParamNames();
final String[] blockParamNames = block.getParameterListDescriptor().getParamNames();
final String[] sourceNames = getSourceNames(parameters);
final GridCoverage[] sources = new GridCoverage[length(sourceNames)];
for (int srcCount=0,i=0; inull
for a default one.
* @param hints The rendering hints, or 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 GridCoverage[] sources,
CoordinateSystem coordinateSystem,
MathTransform gridToCS,
final RenderingHints hints)
throws InvalidGridGeometryException, CannotReprojectException
{
if (sources.length == 0) {
return;
}
if (coordinateSystem == null) {
if (gridToCS==null && sources.length==1) {
return;
}
coordinateSystem = getCoordinateSystem(sources[MASTER_SOURCE_INDEX]);
}
if (gridToCS == null) {
gridToCS = sources[MASTER_SOURCE_INDEX].getGridGeometry().getGridToCoordinateSystem2D();
}
final int dimension = coordinateSystem.getDimension(); // Usually 2.
final GridGeometry geometry = new GridGeometry(null, gridToCS);
final GridCoverageProcessor processor = getGridCoverageProcessor(hints);
for (int i=0; i* {@link JAI#createNS JAI.createNS}({@link #descriptor}.getName(), parameters, hints) *
categories
* contains 0, 2 or more quantative category, then this method returns
* -1
.
*/
private static int getQuantitative(final Category[] categories) {
int index = -1;
for (int i=0; iGridCoverage
s. For a band (or "sample dimension")
* band
in a source coverage source
, the
* corresponding SampleDimension
is
*
* bandLists[source][band]
.
*
* @param parameters Parameters, rendering hints and coordinate system to use.
* @return The sample dimensions for each band in the destination image. The
* length of this array must matches the number of bands in the
* destination image. If the SampleDimension
s are unknow,
* then this method may returns null
.
*
* @see #deriveCategory
* @see #deriveUnit
*/
protected SampleDimension[] deriveSampleDimension(final SampleDimension[][] bandLists,
final Parameters parameters)
{
/*
* Compute the number of bands. Sources with only 1 band are treated as
* a special case: their unique band is applied to every band in other
* sources. If sources don't have the same number of bands, then this
* method returns null
since we don't know how to handle
* those cases.
*/
int numBands = 1;
for (int i=0; iadd
" operation may implements this method as below:
*
* * * @param categories The quantitative categories from every sources. For unary operations * like "GradientMagnitude", this array has a length of 1. For binary * operations like "add" and "multiply", this array has a length of 2. * @param parameters Parameters, rendering hints and coordinate system to use. * @return The quantative category to use in the destination image, * or* NumberRange r0 = categories[0].getRange(); * NumberRange r1 = categories[0].getRange(); * double min = r0.getMinimum() + r1.getMinimum(); * double min = r0.getMaximum() + r1.getMaximum(); * NumberRange newRange = new NumberRange(min, max); * return new Category("My category", null, r0, newRange); *
null
if unknow.
*/
protected Category deriveCategory(final Category[] categories, final Parameters parameters) {
return null;
}
/**
* Derive the unit of data for a {@linkplain SampleDimension sample dimension} in the
* destination coverage. This method is invoked automatically by the
* {@link #deriveSampleDimension deriveSampleDimension} method for each band in the
* destination image. Subclasses should override this method in order to compute the
* destination units from the source units. For example, the "multiply
"
* operation may implement this method as below:
*
* * * @param units The units from every sources. For unary operations like * "GradientMagnitude", this array has a length of 1. * For binary operations like "add" and "multiply", this array has a length of 2. * @param parameters Parameters, rendering hints and coordinate system to use. * @return The unit of data in the destination image, or* if (units[0]!=null && units[1]!=null) { * return units[0].{@link Unit#multiply(Unit) multiply}(units[1]); * } else { * return super.deriveUnit(units, cs, parameters); * } *
null
if unknow.
*/
protected Unit deriveUnit(final Unit[] units, final Parameters parameters) {
return null;
}
/**
* Returns a name for the target {@linkplain GridCoverage grid coverage} based on the given
* source. The default implementation returns the operation name followed by the source name
* between parenthesis.
*
* @param source The source grid coverage.
* @param parameters Parameters, rendering hints and coordinate system to use.
* @return A name for the target grid coverage.
*/
protected String deriveName(final GridCoverage source, final Parameters parameters) {
return getName()+'('+source.getName(null)+')';
}
/**
* Apply the JAI operation. The operation name can be fetch from {@link #descriptor}.
* The JAI instance to use can be fetch from {@link #getJAI}. The default implementation
* invokes {@link JAI#createNS JAI.createNS}. Subclasses may overrides 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.
*/
protected RenderedImage createRenderedImage(final ParameterBlockJAI parameters,
final RenderingHints hints)
{
return getJAI(hints).createNS(descriptor.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 null
if none.
* @return The JAI instance to use (never null
).
*/
protected 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();
}
/**
* Compares the specified object with this operation for equality.
*/
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.descriptor, that.descriptor);
}
return false;
}
/**
* A block of parameters for a {@link GridCoverage} processed by a {@link OperationJAI}.
* This parameter is given to the following methods:
*
* createNS
call
* will be fetch from the {@link Hints#JAI_INSTANCE} key.
*/
public final RenderingHints hints;
/**
* The range, colors and units of the main quantitative {@link Category} to be created.
* If non-null, then this array length matches the number of sources.
*/
final RangeSpecifier[] rangeSpecifiers;
/**
* Construct a new parameter block with the specified values.
*/
Parameters(final CoordinateSystem coordinateSystem,
final MathTransform2D gridToCoordinateSystem,
final ParameterBlockJAI parameters,
final RenderingHints hints,
final RangeSpecifier[] rangeSpecifiers)
{
this.coordinateSystem = coordinateSystem;
this.gridToCoordinateSystem = gridToCoordinateSystem;
this.parameters = parameters;
this.hints = hints;
this.rangeSpecifiers = rangeSpecifiers;
}
/**
* Returns the range specifier for the first source, or null
if none.
*/
final RangeSpecifier getRangeSpecifier() {
return (rangeSpecifiers!=null && rangeSpecifiers.length!=0) ? rangeSpecifiers[0] : null;
}
/**
* Returns the first source image, or null
if none.
*/
final RenderedImage getSource() {
final int n = parameters.getNumSources();
for (int i=0; i