/*
* uDig - User Friendly Desktop Internet GIS client
* http://udig.refractions.net
* (C) 2004, Refractions Research Inc.
*
* 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.swt.styling.simple;
import java.awt.Color;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.swt.graphics.FontData;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.feature.NameImpl;
import org.geotools.filter.Filters;
import org.geotools.styling.FeatureTypeStyle;
import org.geotools.styling.Fill;
import org.geotools.styling.Font;
import org.geotools.styling.LineSymbolizer;
import org.geotools.styling.Mark;
import org.geotools.styling.PointSymbolizer;
import org.geotools.styling.PolygonSymbolizer;
import org.geotools.styling.RasterSymbolizer;
import org.geotools.styling.Rule;
import org.geotools.styling.SLD;
import org.geotools.styling.Stroke;
import org.geotools.styling.Style;
import org.geotools.styling.StyledLayerDescriptor;
import org.geotools.styling.Symbolizer;
import org.geotools.styling.TextSymbolizer;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.GeometryDescriptor;
import org.opengis.filter.FilterFactory;
import org.opengis.filter.expression.Add;
import org.opengis.filter.expression.Divide;
import org.opengis.filter.expression.Expression;
import org.opengis.filter.expression.ExpressionVisitor;
import org.opengis.filter.expression.Function;
import org.opengis.filter.expression.Literal;
import org.opengis.filter.expression.Multiply;
import org.opengis.filter.expression.NilExpression;
import org.opengis.filter.expression.PropertyName;
import org.opengis.filter.expression.Subtract;
import org.opengis.style.Graphic;
import org.opengis.style.GraphicalSymbol;
import org.opengis.style.SemanticType;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.MultiLineString;
import com.vividsolutions.jts.geom.MultiPoint;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;
/**
* Utility class for working with Geotools SLD objects.
* <p>
* This class assumes a subset of the SLD specification:
* <ul>
* <li>Single Rule - matching Filter.INCLUDE
* <li>Symbolizer lookup by name
* </ul>
* </p>
* <p>
* When you start to branch out to SLD information that contains
* multiple rules you will need to modify this class.
* </p>
* @author Jody Garnett, Refractions Research.
* @since 0.7.0
*
*
* @source $URL: http://svn.osgeo.org/geotools/trunk/modules/unsupported/swt/src/main/java/org/geotools/swt/styling/simple/SLDs.java $
*/
public class SLDs extends SLD {
private static FilterFactory ff = CommonFactoryFinder.getFilterFactory(null);
public static final double ALIGN_LEFT = 1.0;
public static final double ALIGN_CENTER = 0.5;
public static final double ALIGN_RIGHT = 0.0;
public static final double ALIGN_BOTTOM = 1.0;
public static final double ALIGN_MIDDLE = 0.5;
public static final double ALIGN_TOP = 0.0;
public static int size( Graphic graphic ) {
if (graphic == null) {
return NOTFOUND;
}
return Filters.asInt(graphic.getSize());
}
public static Color polyFill( PolygonSymbolizer symbolizer ) {
if (symbolizer == null) {
return null;
}
Fill fill = symbolizer.getFill();
if (fill == null) {
return null;
}
Expression color = fill.getColor();
return color(color);
}
public static Color color( Expression expr ) {
if (expr == null) {
return null;
}
try {
return expr.evaluate(null, Color.class);
} catch (Throwable t) {
class ColorVisitor implements ExpressionVisitor {
Color found;
public Object visit( Literal expr, Object data ) {
if (found != null)
return null;
try {
Color color = expr.evaluate(expr, Color.class);
if (color != null) {
found = color;
}
} catch (Throwable t) {
// not a color
}
return data;
}
public Object visit( NilExpression arg0, Object data ) {
return data;
}
public Object visit( Add arg0, Object data ) {
return data;
}
public Object visit( Divide arg0, Object data ) {
return null;
}
public Object visit( Function function, Object data ) {
for( Expression param : function.getParameters() ) {
param.accept(this, data);
}
return data;
}
public Object visit( Multiply arg0, Object data ) {
return data;
}
public Object visit( PropertyName arg0, Object data ) {
return data;
}
public Object visit( Subtract arg0, Object data ) {
return data;
}
}
ColorVisitor search = new ColorVisitor();
expr.accept(search, null);
return search.found;
}
}
/**
* Grabs the font from the first TextSymbolizer.
* <p>
* If you are using something fun like symbols you
* will need to do your own thing.
* </p>
* @param symbolizer Text symbolizer information.
* @return FontData[] of the font's fill, or null if unavailable.
*/
public static FontData[] textFont( TextSymbolizer symbolizer ) {
Font font = font(symbolizer);
if (font == null)
return null;
FontData[] tempFD = new FontData[1];
Expression fontFamilyExpression = font.getFamily().get(0);
Expression sizeExpression = font.getSize();
if (sizeExpression == null || fontFamilyExpression == null)
return null;
Double size = sizeExpression.evaluate(null, Double.class);
try {
String fontFamily = fontFamilyExpression.evaluate(null, String.class);
tempFD[0] = new FontData(fontFamily, size.intValue(), 1);
} catch (NullPointerException ignore) {
return null;
}
if (tempFD[0] != null)
return tempFD;
return null;
}
/**
* Retrieves all colour names defined in a rule
* @param rule the rule
* @return an array of unique colour names
*/
public static String[] colors( Rule rule ) {
Set<String> colorSet = new HashSet<String>();
Color color = null;
for( Symbolizer sym : rule.symbolizers() ) {
if (sym instanceof PolygonSymbolizer) {
PolygonSymbolizer symb = (PolygonSymbolizer) sym;
color = polyFill(symb);
} else if (sym instanceof LineSymbolizer) {
LineSymbolizer symb = (LineSymbolizer) sym;
color = color(symb);
} else if (sym instanceof PointSymbolizer) {
PointSymbolizer symb = (PointSymbolizer) sym;
color = pointFillWithAlpha(symb);
}
if (color != null) {
colorSet.add(SLD.colorToHex(color));
}
}
if (colorSet.size() > 0) {
return colorSet.toArray(new String[0]);
} else {
return new String[0];
}
}
/**
* Extracts the fill color with a given opacity from the {@link PointSymbolizer}.
*
* @param symbolizer the point symbolizer from which to get the color.
* @return the {@link Color} with transparency if available. Returns null if no color is available.
*/
public static Color pointFillWithAlpha( PointSymbolizer symbolizer ) {
if (symbolizer == null) {
return null;
}
Graphic graphic = symbolizer.getGraphic();
if (graphic == null) {
return null;
}
for( GraphicalSymbol gs : graphic.graphicalSymbols() ) {
if ((gs != null) && (gs instanceof Mark)) {
Mark mark = (Mark) gs;
Fill fill = mark.getFill();
if (fill != null) {
Color colour = color(fill.getColor());
if (colour == null) {
return null;
}
Expression opacity = fill.getOpacity();
if (opacity == null)
opacity = ff.literal(1.0);
float alpha = (float) Filters.asDouble(opacity);
colour = new Color(colour.getRed() / 255f, colour.getGreen() / 255f, colour.getBlue() / 255f, alpha);
if (colour != null) {
return colour;
}
}
}
}
return null;
}
/**
* Extracts the stroke color with a given opacity from the {@link PointSymbolizer}.
*
* @param symbolizer the point symbolizer from which to get the color.
* @return the {@link Color} with transparency if available. Returns null if no color is available.
*/
public static Color pointStrokeColorWithAlpha( PointSymbolizer symbolizer ) {
if (symbolizer == null) {
return null;
}
Graphic graphic = symbolizer.getGraphic();
if (graphic == null) {
return null;
}
for( GraphicalSymbol gs : graphic.graphicalSymbols() ) {
if ((gs != null) && (gs instanceof Mark)) {
Mark mark = (Mark) gs;
Stroke stroke = mark.getStroke();
if (stroke != null) {
Color colour = color(stroke);
if (colour == null) {
return null;
}
Expression opacity = stroke.getOpacity();
if (opacity == null)
opacity = ff.literal(1.0);
float alpha = (float) Filters.asDouble(opacity);
colour = new Color(colour.getRed() / 255f, colour.getGreen() / 255f, colour.getBlue() / 255f, alpha);
if (colour != null) {
return colour;
}
}
}
}
return null;
}
public static Font font( TextSymbolizer symbolizer ) {
if (symbolizer == null)
return null;
Font font = symbolizer.getFont();
return font;
}
public static Style getDefaultStyle( StyledLayerDescriptor sld ) {
Style[] styles = styles(sld);
for( int i = 0; i < styles.length; i++ ) {
Style style = styles[i];
List<FeatureTypeStyle> ftStyles = style.featureTypeStyles();
genericizeftStyles(ftStyles);
if (style.isDefault()) {
return style;
}
}
// no default, so just grab the first one
return styles[0];
}
/**
* Converts the type name of all FeatureTypeStyles to Feature so that the all apply to any feature type. This is admittedly dangerous
* but is extremely useful because it means that the style can be used with any feature type.
*
* @param ftStyles
*/
private static void genericizeftStyles( List<FeatureTypeStyle> ftStyles ) {
for( FeatureTypeStyle featureTypeStyle : ftStyles ) {
featureTypeStyle.featureTypeNames().clear();
featureTypeStyle.featureTypeNames().add(new NameImpl(SLDs.GENERIC_FEATURE_TYPENAME));
}
}
public static boolean isSemanticTypeMatch( FeatureTypeStyle fts, String regex ) {
Set<SemanticType> identifiers = fts.semanticTypeIdentifiers();
for( SemanticType semanticType : identifiers ) {
if (semanticType.matches(regex))
return true;
}
return false;
}
/**
* Returns the min scale of the default rule, or 0 if none is set
*/
public static double minScale( FeatureTypeStyle fts ) {
if (fts == null || fts.rules().size() == 0)
return 0.0;
Rule r = fts.rules().get(0);
return r.getMinScaleDenominator();
}
/**
* Returns the max scale of the default rule, or {@linkplain Double#NaN} if none is set
*/
public static double maxScale( FeatureTypeStyle fts ) {
if (fts == null || fts.rules().size() == 0)
return Double.NaN;
Rule r = fts.rules().get(0);
return r.getMaxScaleDenominator();
}
/**
* gets the first FeatureTypeStyle
*/
public static FeatureTypeStyle getFeatureTypeStyle( Style s ) {
List<FeatureTypeStyle> fts = s.featureTypeStyles();
if (fts.size() > 0) {
return fts.get(0);
}
return null;
}
/**
* Find the first rule which contains a rastersymbolizer, and return it
*
* @param s A style to search in
* @return a rule, or null if no raster symbolizers are found.
*/
public static Rule getRasterSymbolizerRule( Style s ) {
List<FeatureTypeStyle> fts = s.featureTypeStyles();
for( int i = 0; i < fts.size(); i++ ) {
FeatureTypeStyle featureTypeStyle = fts.get(i);
List<Rule> rules = featureTypeStyle.rules();
for( int j = 0; j < rules.size(); j++ ) {
Rule rule = rules.get(j);
Symbolizer[] symbolizers = rule.getSymbolizers();
for( int k = 0; k < symbolizers.length; k++ ) {
Symbolizer symbolizer = symbolizers[k];
if (symbolizer instanceof RasterSymbolizer) {
return rule;
}
}
}
}
return null;
}
/**
* The type name that can be used in an SLD in the featuretypestyle that matches all feature types.
*/
public static final String GENERIC_FEATURE_TYPENAME = "Feature";
public static final boolean isPolygon( SimpleFeatureType featureType ) {
if (featureType == null)
return false;
return isPolygon(featureType.getGeometryDescriptor());
}
/* This needed to be a function as it was being written poorly everywhere */
public static final boolean isPolygon( GeometryDescriptor geometryType ) {
if (geometryType == null)
return false;
Class< ? > type = geometryType.getType().getBinding();
return Polygon.class.isAssignableFrom(type) || MultiPolygon.class.isAssignableFrom(type);
}
public static final boolean isLine( SimpleFeatureType featureType ) {
if (featureType == null)
return false;
return isLine(featureType.getGeometryDescriptor());
}
/* This needed to be a function as it was being written poorly everywhere */
public static final boolean isLine( GeometryDescriptor geometryType ) {
if (geometryType == null)
return false;
Class< ? > type = geometryType.getType().getBinding();
return LineString.class.isAssignableFrom(type) || MultiLineString.class.isAssignableFrom(type);
}
public static final boolean isPoint( SimpleFeatureType featureType ) {
if (featureType == null)
return false;
return isPoint(featureType.getGeometryDescriptor());
}
/* This needed to be a function as it was being writen poorly everywhere */
public static final boolean isPoint( GeometryDescriptor geometryType ) {
if (geometryType == null)
return false;
Class< ? > type = geometryType.getType().getBinding();
return Point.class.isAssignableFrom(type) || MultiPoint.class.isAssignableFrom(type);
}
}