/*
* GeoTools - The Open Source Java GIS Tookit
* http://geotools.org
*
* (C) 2006-2008, Open Source Geospatial Foundation (OSGeo)
*
* This file is hereby placed into the Public Domain. This means anyone is
* free to do whatever they wish with this file. Use it well and enjoy!
*/
package org.geotools.demo.filter.function;
import java.awt.Color;
import java.util.ArrayList;
import java.util.List;
import org.geotools.factory.CommonFactoryFinder;
import org.opengis.filter.FilterFactory;
import org.opengis.filter.expression.Expression;
import org.opengis.filter.expression.ExpressionVisitor;
import org.opengis.filter.expression.Function;
import org.opengis.filter.expression.Literal;
/**
* Illustrates how to write a custom filter function that implements the
* {@linkplain org.opengis.filter.expression.Function} interface directly.
* The example here is a function that can be used in a {@linkplain org.geotools.styling.Style}
* to dynamically set the fill color of polygon features based on a numeric
* feature attribute (e.g. country population size).
* <p>
* GeoTools provides a great many useful filter functions for numeric and
* spatial operations but sometimes it is still necessary, or perhaps easier,
* for users to create custom functions. This can be done by extending the
* GeoTools classes or, as in this example, implementing the {@code Function}
* interface directly.
*
* @author Michael Bedward
*
* @source $URL$
*/
public class ColorRampFunction implements Function {
private static final float EPS = 1.0e-6F;
private static FilterFactory filterFactory = CommonFactoryFinder.getFilterFactory(null);
private String name;
private List<Expression> paramList;
private final Literal fallback;
private float minValue;
private float maxValue;
private float saturation;
private float brightness;
/**
* Constructor.
*
* @param valueExpr the expression that will be used to get the feature value
* from which the fill color will be calculated
*
* @param minValue minimum expected feature value
*
* @param maxValue maximum expected feature value
*
* @param saturation HSB color model saturation (between 0 and 1)
*
* @param brightness HSB color model brightness (between 0 and 1)
*/
public ColorRampFunction(Expression valueExpr, float minValue, float maxValue, float saturation, float brightness) {
this.name = "ColorRamp";
this.paramList = new ArrayList<Expression>();
fallback = filterFactory.literal(Color.GRAY);
if (minValue - maxValue > -EPS) {
throw new IllegalArgumentException("minValue must be greater than maxValue");
}
this.minValue = minValue;
this.maxValue = maxValue;
if (saturation < 0.0d || saturation > 1.0d ||
brightness < 0.0d || brightness > 1.0d) {
throw new IllegalArgumentException("saturation and brightness must between 0 and 1");
}
this.saturation = saturation;
this.brightness = brightness;
this.paramList.add(valueExpr);
}
/**
* Get the name of this function
*
* @return function name
*/
public String getName() {
return name;
}
/**
* Get the parameters that have been set for this function
* (only one parameter for the current implementation).
*
* @return live list of function parameters
*/
public List<Expression> getParameters() {
return paramList;
}
/**
* Get the fallback value for this function
*
* @return {@code Color.GRAY} as a {@code Literal} object
*/
public Literal getFallbackValue() {
return fallback;
}
/**
* Evaluate the function for a given feature. The feature's
* value will be extracted and a color calculated to use as the
* polygon fill.
*
* @param feature the input feature
* @return a {@code Color} object
*/
public Object evaluate(Object feature) {
float value = ((Number) getParameters().get(0).evaluate(feature)).floatValue();
System.out.println("value = " + value);
return valueToColor(value);
}
/**
* Evaluate the function for a given feature. The feature's
* value will be extracted and a color calculated to use as the
* polygon fill.
*
* @param <T> type parameter - must be {@code Color} or a subclass
* @param feature the input feature
* @param clazz a Class object for the type (e.g. {@code Color.class})
*
* @return a {@code Color} object
*/
public <T> T evaluate(Object feature, Class<T> clazz) {
if (Color.class.isAssignableFrom(clazz)) {
return clazz.cast(evaluate(feature));
}
throw new UnsupportedOperationException("Color is the only supported class");
}
/**
* Accept a visitor that wishes to examine this function's
* parameters. This method will be invoked during the rendering
* process to get Stroke and Fill attributes.
*
* @param visitor a visitor object
* @param arg visitor-specific argument
*
* @return result of the visit
*/
public Object accept(ExpressionVisitor visitor, Object arg) {
return visitor.visit(this, arg);
}
/**
* Calculate a color based on a float value. The calculation
* involves range-coding the input value and then using this as
* the hue in an HSB color ramp.
*
* @param value the input feature value
* @return a new {@code Color} object or null if the input value
* was outside the range minValue:maxValue
*/
private Color valueToColor(float value) {
float hue = (value - minValue) / (maxValue - minValue);
if (hue < 0.0f || hue > 1.0f) {
return null;
}
int rgb = Color.HSBtoRGB(hue * 0.5f, saturation, brightness);
return new Color(rgb);
}
}