OK, I had a chance to look at the function and it was quite easy to
modify it - at least get it working with GeoTools 8. However, please
treat it as example code rather than production quality. In
particular, the function presently *assumes* that every feature you
give it belongs to the SimpleFeatureSource or SimpleFeatureCollection
initially passed to it as a parameter. It would be easy to add a check
for this but I leave that up to you if you require it.
First up, here is the code for the function (you can of course use any
package name you like)...
package org.geotools.myfunctions;
import java.awt.Color;
import java.util.HashMap;
import java.util.Map;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.feature.visitor.UniqueVisitor;
import org.geotools.filter.FunctionExpressionImpl;
import org.geotools.filter.capability.FunctionNameImpl;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.filter.capability.FunctionName;
import org.opengis.filter.expression.Expression;
/**
*
* @author michael
*/
public class ColorLookupFunction extends FunctionExpressionImpl {
private static final float INITIAL_HUE = 0.1f;
private static final int SOURCE_PARAM_INDEX = 0;
private static final String SOURCE_PARAM_NAME = "featureSource";
private static final int COLOUR_PARAM_INDEX = 1;
private static final String COLOUR_PARAM_NAME = "colourExpr";
// Set name, return type, and names and types of parameters
public FunctionName NAME = new FunctionNameImpl("colorlookup",
Color.class, // return type
FunctionNameImpl.parameter(SOURCE_PARAM_NAME,
SimpleFeatureSource.class),
FunctionNameImpl.parameter(COLOUR_PARAM_NAME, Expression.class));
Map<Object, Color> lookup;
private int numColours;
private float hue;
private float hueIncr;
private float saturation = 0.7f;
private float brightness = 0.7f;
public ColorLookupFunction() {
super("colorlookup");
}
@Override
public Object evaluate(Object obj) {
if (obj instanceof SimpleFeature) {
SimpleFeature feature = (SimpleFeature) obj;
Object key =
getParameters().get(COLOUR_PARAM_INDEX).evaluate(feature);
if (lookup == null) {
createLookup();
}
Color color = lookup.get(key);
if (color == null) {
color = addColor(key);
}
return color;
}
return null;
}
private void createLookup() {
lookup = new HashMap<Object, Color>();
try {
SimpleFeatureCollection fc;
Object o = getParameters().get(SOURCE_PARAM_INDEX).evaluate(null);
if (o instanceof SimpleFeatureSource) {
fc = ((SimpleFeatureSource) o).getFeatures();
} else if (o instanceof SimpleFeatureCollection) {
fc = (SimpleFeatureCollection) o;
} else {
throw new IllegalArgumentException(
"First parameter must be either a
SimpleFeatureSource or SimpleFeatureCollection");
}
Expression colourExpr = getParameters().get(COLOUR_PARAM_INDEX);
UniqueVisitor visitor = new UniqueVisitor(colourExpr);
fc.accepts(visitor, null);
numColours = visitor.getUnique().size();
hue = INITIAL_HUE;
hueIncr = (1.0f - hue) / numColours;
} catch (Exception ex) {
throw new IllegalStateException("Problem creating colour
lookup", ex);
}
}
private Color addColor(Object key) {
Color c = new Color(Color.HSBtoRGB(hue, saturation, brightness));
hue += hueIncr;
lookup.put(key, c);
return c;
}
}
Next you need to create the file
META-INF/services/org.opengis.filter.expression.Function (under
src/main/resources in your project if using maven) and put this line
into it...
org.geotools.myfunctions.ColorLookupFunction
Now here is an example app that loads a shapefile, prompts for a
feature attribute to use for colouring (each unique value gets a
colour), creates a Style and displays the map. Note that in the
createStyle method, we now pass the feature source (or feature
collection) and attribute to use for colouring as parameters to the
FilterFactory2.function method...
package org.geotools.demo;
import java.io.File;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import com.vividsolutions.jts.geom.Geometry;
import org.geotools.data.FileDataStore;
import org.geotools.data.FileDataStoreFinder;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.geometry.jts.Geometries;
import org.geotools.map.FeatureLayer;
import org.geotools.map.Layer;
import org.geotools.map.MapContent;
import org.geotools.styling.Fill;
import org.geotools.styling.Graphic;
import org.geotools.styling.Mark;
import org.geotools.styling.SLD;
import org.geotools.styling.Stroke;
import org.geotools.styling.Style;
import org.geotools.styling.StyleFactory;
import org.geotools.styling.Symbolizer;
import org.geotools.swing.JMapFrame;
import org.geotools.swing.data.JFileDataStoreChooser;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.filter.FilterFactory2;
import org.opengis.filter.expression.Function;
/**
* Displays a shapefile using a Style in which ColorLookupFunction is used to
* set feature colours based on unique values of a selected attribute.
*
* @author michael
*/
public class App {
public static void main(String[] args) throws Exception {
File file = JFileDataStoreChooser.showOpenFile("shp", null);
if (file != null) {
FileDataStore store = FileDataStoreFinder.getDataStore(file);
SimpleFeatureSource source = store.getFeatureSource();
String fieldName = getFieldForColour(source);
if (fieldName != null && fieldName.length() > 0) {
Style style = createStyle(source, fieldName);
Layer layer = new FeatureLayer(source, style);
MapContent map = new MapContent();
map.addLayer(layer);
JMapFrame.showMap(map);
}
}
}
private static String getFieldForColour(SimpleFeatureSource
source) throws Exception {
final String fieldName[] = new String[1];
final String[] fieldNames = new
String[source.getSchema().getAttributeCount()];
int k = 0;
for (AttributeDescriptor desc :
source.getSchema().getAttributeDescriptors()) {
fieldNames[k++] = desc.getLocalName();
}
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
Object obj = JOptionPane.showInputDialog(null,
"Choose an attribute for colouring",
"Feature attribute",
JOptionPane.PLAIN_MESSAGE,
null,
fieldNames,
fieldNames[0]);
if (obj != null) {
fieldName[0] = (String) obj;
}
}
});
return fieldName[0];
}
private static Style createStyle(SimpleFeatureSource source,
String fieldName) throws Exception {
StyleFactory styleFactory = CommonFactoryFinder.getStyleFactory();
FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
Function colourFn = ff.function("colorlookup",
ff.literal(source), ff.property(fieldName));
Stroke stroke = styleFactory.createStroke(
colourFn,
ff.literal(1.0f), // line width
ff.literal(1.0f)); // opacity
Fill fill = styleFactory.createFill(
colourFn,
ff.literal(1.0f)); // opacity
Class<?> geomClass =
source.getSchema().getGeometryDescriptor().getType().getBinding();
Symbolizer sym = null;
Geometries geomType = Geometries.getForBinding((Class<?
extends Geometry>) geomClass);
switch (geomType) {
case POLYGON:
case MULTIPOLYGON:
sym = styleFactory.createPolygonSymbolizer(stroke, fill, null);
break;
case LINESTRING:
case MULTILINESTRING:
sym = styleFactory.createLineSymbolizer(stroke, null);
break;
case POINT:
case MULTIPOINT:
Graphic gr = styleFactory.createDefaultGraphic();
gr.graphicalSymbols().clear();
Mark mark = styleFactory.getCircleMark();
mark.setFill(fill);
mark.setStroke(stroke);
gr.graphicalSymbols().add(mark);
gr.setSize(ff.literal(10.0f));
sym = styleFactory.createPointSymbolizer(gr, null);
break;
default:
throw new IllegalArgumentException("Unsupported geometry type");
}
Style style = SLD.wrapSymbolizers(sym);
return style;
}
}
Share and enjoy,
Michael
------------------------------------------------------------------------------
Virtualization & Cloud Management Using Capacity Planning
Cloud computing makes use of virtualization - but cloud computing
also focuses on allowing computing to be delivered as a service.
http://www.accelacomm.com/jaw/sfnl/114/51521223/
_______________________________________________
GeoTools-GT2-Users mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/geotools-gt2-users