// docs start source /* * 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; import com.vividsolutions.jts.geom.LineString; import com.vividsolutions.jts.geom.MultiLineString; import com.vividsolutions.jts.geom.MultiPolygon; import com.vividsolutions.jts.geom.Polygon; import java.awt.Color; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.swing.JOptionPane; import org.geotools.data.FeatureSource; import org.geotools.data.FileDataStore; import org.geotools.data.FileDataStoreFinder; import org.geotools.factory.CommonFactoryFinder; import org.geotools.feature.FeatureCollection; import org.geotools.feature.visitor.UniqueVisitor; import org.geotools.filter.FunctionExpressionImpl; import org.geotools.map.DefaultMapContext; import org.geotools.map.MapContext; 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.FeatureType; import org.opengis.feature.type.PropertyDescriptor; import org.opengis.filter.FilterFactory2; import org.opengis.filter.expression.Expression; public class StyleFunctionLab { public static void main(String[] args) throws Exception { File file = JFileDataStoreChooser.showOpenFile("shp", null); if (file == null) { return; } StyleFunctionLab me = new StyleFunctionLab(); me.displayShapefile(file); } /** * Connect to the shapefile and prompt the user to choose a feature * attribute to base line and fill colours on. A colour will be * generated for each unique value of the chosen attribute. */ private void displayShapefile(File file) throws Exception { FileDataStore store = FileDataStoreFinder.getDataStore(file); FeatureSource featureSource = store.getFeatureSource(); // Create a map context and add our shapefile to it MapContext map = new DefaultMapContext(); map.setTitle("Style Function Lab"); /* * Prompt the user for the feature attribute used to * determine the line and fill colour for each feature */ FeatureType type = featureSource.getSchema(); PropertyDescriptor geomDesc = type.getGeometryDescriptor(); List<String> attributeNames = new ArrayList<String>(); for (PropertyDescriptor desc : type.getDescriptors()) { if (!desc.equals(geomDesc)) { attributeNames.add(desc.getName().getLocalPart()); } } Object selection = JOptionPane.showInputDialog(null, "Unique values of the attribute will be used to create a colour lookup table", "Select feature attribute", JOptionPane.PLAIN_MESSAGE, null, attributeNames.toArray(), null); /** * Create the Style and display the shapefile */ if (selection != null) { Style style = createStyle(featureSource, (String)selection); map.addLayer(featureSource, style); // Now display the map JMapFrame.showMap(map); } } // docs end display // docs start create style /** * Create a rendering style to display features from the given feature source * by matching unique values of the specified feature attribute to colours * * @param featureSource the feature source * @return a new Style instance */ private Style createStyle(FeatureSource featureSource, String attributeName) throws Exception { FilterFactory2 filterFactory = CommonFactoryFinder.getFilterFactory2(null); StyleFactory styleFactory = CommonFactoryFinder.getStyleFactory(null); ColorLookupFunction colourFn = new ColorLookupFunction( featureSource.getFeatures(), filterFactory.property(attributeName)); Stroke stroke = styleFactory.createStroke( colourFn, // function to choose feature colour filterFactory.literal(1.0f), // line width filterFactory.literal(1.0f)); // opacity Fill fill = styleFactory.createFill( colourFn, // function to choose feature colour filterFactory.literal(1.0f)); // opacity Class<?> geomClass = featureSource.getSchema().getGeometryDescriptor().getType().getBinding(); Symbolizer sym = null; if (Polygon.class.isAssignableFrom(geomClass) || MultiPolygon.class.isAssignableFrom(geomClass)) { sym = styleFactory.createPolygonSymbolizer(stroke, fill, null); } else if (LineString.class.isAssignableFrom(geomClass) || MultiLineString.class.isAssignableFrom(geomClass)) { sym = styleFactory.createLineSymbolizer(stroke, null); } else { Graphic gr = styleFactory.createDefaultGraphic(); gr.graphicalSymbols().clear(); Mark mark = styleFactory.getCircleMark(); mark.setFill(fill); mark.setStroke(stroke); gr.graphicalSymbols().add(mark); gr.setSize(filterFactory.literal(10.0f)); sym = styleFactory.createPointSymbolizer(gr, null); } Style style = SLD.wrapSymbolizers(sym); return style; } // docs end create style:w // docs start function /** * A function to dynamically allocate colours to features. It works with a lookup table * where the key is a user-specified feature attribute. Colours are generated using * a simple colour ramp algorithm. */ static class ColorLookupFunction extends FunctionExpressionImpl { private static final float INITIAL_HUE = 0.1f; private final FeatureCollection collection; Map<Object, Color> lookup; private int numColours; private float hue; private float hueIncr; private float saturation = 0.7f; private float brightness = 0.7f; /** * Creates an instance of the function for the given feature collection. Features will * be assigned fill colours by matching the value of the specified feature attribute * in a lookup table of unique attribute values with associated colours. * * @param collection the feature collection * * @param colourAttribute a literal expression that specifies the feature attribute * to use for colour lookup */ public ColorLookupFunction(FeatureCollection collection, Expression colourAttribute) { super("UniqueColour"); this.collection = collection; this.params.add(colourAttribute); this.fallback = CommonFactoryFinder.getFilterFactory2(null).literal(Color.WHITE); } @Override public int getArgCount() { return 1; } /** * Evalute this function for a given feature and return a * Color. * * @param object the feature for which a colour is being requested * * @return the colour for this feature */ @Override public Object evaluate(Object feature) { if (lookup == null) { createLookup(); } Object key = ((Expression)params.get(0)).evaluate(feature); Color color = lookup.get(key); if (color == null) { color = addColor(key); } return color; } /** * Creates the lookup table and initializes variables used in * colour generation */ private void createLookup() { lookup = new HashMap<Object, Color>(); try { UniqueVisitor visitor = new UniqueVisitor((Expression)params.get(0)); collection.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); } } /* * Generates a new colour for the colour ramp and adds it to * the lookup table */ private Color addColor(Object key) { Color c = new Color(Color.HSBtoRGB(hue, saturation, brightness)); hue += hueIncr; lookup.put(key, c); return c; } } } // docs end source