/*
* Geotools 2 - OpenSource mapping toolkit
* (C) 2003, Geotools Project Management Committee (PMC)
* (C) 2003, 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
*/
package org.geotools.gp;
// J2SE dependencies
import java.awt.Color;
import javax.media.jai.KernelJAI;
import javax.media.jai.ParameterListDescriptor;
import javax.media.jai.ParameterListDescriptorImpl;
import org.geotools.cv.Category;
import org.geotools.gc.GridCoverage;
import org.geotools.resources.Utilities;
import org.geotools.util.NumberRange;
/**
* An operation for convolution. This operation is built on top of the JAI's operation
* "Convolve". It includes the OpenGIS "LaplaceType1Filter" and "LaplaceType2Filter"
* operations.
*
* @source $URL$
* @version $Id$
* @author Martin Desruisseaux
*/
final class ConvolveOperation extends OperationJAI {
/**
* Kernel for the Laplace type 1 filter.
*/
public static final KernelJAI LAPLACE_TYPE_1 = new KernelJAI(3, 3, new float[] {
0/9f, -1/9f, 0/9f,
-1/9f, 4/9f, -1/9f,
0/9f, -1/9f, 0/9f
});
/**
* Kernel for the Laplace type 2 filter.
*/
public static final KernelJAI LAPLACE_TYPE_2 = new KernelJAI(3, 3, new float[] {
-1/9f, -1/9f, -1/9f,
-1/9f, 8/9f, -1/9f,
-1/9f, -1/9f, -1/9f
});
/**
* Small number for avoiding rounding errors. Should be close to the precision
* of float
type.
*/
private static final double EPS = 1E-5;
/**
* The default scale factor to apply on the range computed by
* {@link #deriveCategory}. For example a value of 0.04 means
* that only values from 0 to 4% of the maximum will appears
* in different colors.
*/
private static final double DEFAULT_RANGE_SCALE = 0.04;
/**
* The default color palette for the gradients.
*/
private static final Color[] DEFAULT_COLOR_PALETTE = new Color[] {
new Color(192,224,255),
new Color( 16, 32, 16),
new Color(255,224,192)
};
/**
* Parameter descriptor common to all convolution built on top of a predefined kernel
* (e.g. "LaplaceType1Filter", "LaplaceType2Filter").
*/
private static final ParameterListDescriptor DESCRIPTOR = new ParameterListDescriptorImpl(
null, // the object to be reflected upon for enumerated values.
new String[] { // the names of each parameter.
"Source",
// "SampleDimension"
},
new Class[] // the class of each parameter.
{
GridCoverage.class,
// Integer.class
},
new Object[] // The default values for each parameter.
{
ParameterListDescriptor.NO_PARAMETER_DEFAULT,
// ZERO
},
new Object[] // Defines the valid values for each parameter.
{
null,
// RANGE_0
});
/**
* The kernel to use for this convolution, or null
* if the kernel must be fetched from user-provided parameter.
*/
private final KernelJAI kernel;
/**
* Construct a default convolve operation.
*/
public ConvolveOperation() {
super("Convolve");
kernel = null;
}
/**
* Construct a convolve operation with the specified kernel.
*
* @param name The operation name to be registered to {@link GridCoverageProcessor}.
* This is not the JAI name.
* @param kernel The kernel to use.
*/
public ConvolveOperation(final String name, final KernelJAI kernel) {
super(name, getOperationDescriptor("Convolve"), DESCRIPTOR);
this.kernel = kernel;
}
/**
* Returns a scale factor for the specified kernel. This scale factor will be used
* for scaling the range of values of the target category.
*/
private static final double getFactor(final KernelJAI kernel) {
double sum = 0;
final int width = kernel.getWidth();
final int height = kernel.getHeight();
for (int y=height; --y>=0;) {
for (int x=width; --x>=0;) {
sum += kernel.getElement(x,y);
}
}
return sum;
}
/**
* Apply an operation. This method add the kernel to the parameter list
* and invokes the super-class method.
*/
protected GridCoverage deriveGridCoverage(final GridCoverage[] sources,
final Parameters parameters)
{
if (kernel != null) {
parameters.parameters.setParameter("kernel", kernel);
}
return super.deriveGridCoverage(sources, parameters);
}
/**
* Derive the quantitative category for a band in the destination image.
* This implementation compute the expected values range from the kernel.
*/
protected Category deriveCategory(final Category[] categories, final Parameters parameters) {
Category category = categories[0];
final KernelJAI kernel = (KernelJAI) parameters.parameters.getObjectParameter("kernel");
double factor = getFactor(kernel);
if (Math.abs(factor-1) > EPS) {
final boolean isGeophysics = (category == category.geophysics(true));
Color[] colors = category.getColors();
final NumberRange range = category.geophysics(true).getRange();
double minimum = range.getMinimum();
double maximum = range.getMaximum();
if (Math.abs(factor) <= EPS) {
// Heuristic approach
maximum -= minimum;
minimum = -maximum;
factor = DEFAULT_RANGE_SCALE;
colors = DEFAULT_COLOR_PALETTE;
}
minimum *= factor;
maximum *= factor;
if (minimum > maximum) {
final double swap = minimum;
minimum = maximum;
maximum = swap;
}
category = new Category(category.getName(null), colors,
category.geophysics(false).getRange(),
new NumberRange(minimum, maximum));
return category.geophysics(isGeophysics);
}
return category;
}
/**
* 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 ConvolveOperation that = (ConvolveOperation) object;
return Utilities.equals(this.kernel, that.kernel);
}
return false;
}
}