/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2008-2010, 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.filter.function;
import java.awt.Color;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.geotools.data.Parameter;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.filter.capability.FunctionNameImpl;
import org.geotools.text.Text;
import org.geotools.util.Converters;
import org.geotools.util.KVP;
import org.opengis.filter.FilterFactory2;
import org.opengis.filter.capability.FunctionName;
import org.opengis.filter.expression.Expression;
import org.opengis.filter.expression.ExpressionVisitor;
import org.opengis.filter.expression.Function;
import org.opengis.filter.expression.Literal;
/**
* This is an implemenation of the Interpolate function as defined by
* OGC Symbology Encoding (SE) 1.1 specification.
*
* The first parameter should be either the name of a numeric feature property
* or, if this function is being used as a raster colormap, the String "RasterData"
* (case-insensitive).
*
* Following this there should be a sequence of interpolation points, each of which
* is described by two parameters: the first a datum and the second a return value.
* In the SE speicification these parameters are expected to be Literals but in this
* implementation more general Expressions are also supported.
*
* Two optional parameters can be provided following the interpolation points:
* A "method" parameter which can take the values "numeric" or "color" and a
* "mode" parameter which can take the values "linear", "cosine" or "cubic"
* (Note: it would make more sense if these terms were reversed but we are
* adhering to their use as published in the OGC specification).
*
* Number of points and interpolation modes
*
*
* Linear and cosine interpolation each require at least two interpolation points to
* be supplied.
*
*
* Cubic interpolation normally requires at least four points with at least two points
* either side of the value being interpolated. In this function, deal generously with
* values that lie in the first or last interpolation segment by adding a duplicate of
* the first or last point as an extra point. This means that it is allowed (though not
* necessarily sensible) to use cubic interpolation with only three points.
*
*
* If only two points are supplied but cubic interpolation is specified, the function
* will fall back to linear interpolation.
*
*
* For all interpolation modes, incoming values outside the range of the interpolation
* points will be mapped to the value of the closest point (min or max).
*
*
* The function will accept a single interpolation point, but all incoming values will
* simply be mapped to the value of that point regardless of the type of interpolation
* requested.
*
*
* If no interpolation points are supplied, an {@code Exception} is thrown.
*
*
*
* @author Michael Bedward
* @author Johann Sorel (Geomatys)
*
*
*
* @source $URL$
* @version $Id$
*/
public class InterpolateFunction implements Function {
private static final Logger LOGGER = Logger.getLogger(InterpolateFunction.class.getName());
private static final FilterFactory2 ff2 = CommonFactoryFinder.getFilterFactory2(null);
private static final double EPS = 1.0e-8;
/** Use as a literal value to indicate interpolation mode */
public static final String MODE_LINEAR = "linear";
/** Use as a literal value to indicate interpolation mode */
public static final String MODE_COSINE = "cosine";
/** Use as a literal value to indicate interpolation mode */
public static final String MODE_CUBIC = "cubic";
/*
* TODO: Surely the SE spec has "method" and "mode" the wrong
* way around. It makes much more sense to have mode as numeric
* or color and method as linear, cosine or cubic.
*/
/** Use as a literal value to indicate interpolation method */
public static final String METHOD_NUMERIC = "numeric";
/** Use as a literal value to indicate interpolation method */
public static final String METHOD_COLOR = "color";
/** Alternate spelling - being kind to users */
private static final String METHOD_COLOUR = "colour";
private static enum Mode {
LINEAR,
COSINE,
CUBIC
};
private static enum Method {
NUMERIC,
COLOR
};
private Mode mode;
private boolean modeSpecified;
private Method method;
private boolean methodSpecified;
private static class InterpPoint {
Expression data;
Expression value;
public InterpPoint(Expression data, Expression value) {
this.data = data;
this.value = value;
}
}
private List interpPoints;
/**
* Use as a PropertyName when defining a color map.
* The "Raterdata" is expected to apply to only a single band;
*/
public static final String RASTER_DATA = "Rasterdata";
private final List parameters;
private final Literal fallback;
/**
* Make the instance of FunctionName available in
* a consistent spot.
*/
public static final FunctionName NAME;
static {
Parameter