/* (c) 2014 Open Source Geospatial Foundation - all rights reserved
* (c) 2001 - 2013 OpenPlans
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.kml.icons;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.filter.visitor.IsStaticExpressionVisitor;
import org.geotools.renderer.style.ExpressionExtractor;
import org.geotools.styling.ExternalGraphic;
import org.geotools.styling.Fill;
import org.geotools.styling.Graphic;
import org.geotools.styling.Mark;
import org.geotools.styling.PointSymbolizer;
import org.geotools.styling.Stroke;
import org.geotools.styling.Style;
import org.geotools.styling.StyleFactory;
import org.geotools.styling.Symbol;
import org.opengis.filter.FilterFactory;
import org.opengis.filter.expression.Expression;
import org.opengis.style.GraphicalSymbol;
/**
* Utility to inject static property values into a style
*
* @author David Winslow, OpenGeo
*
*/
final public class IconPropertyInjector {
private final FilterFactory filterFactory;
private final StyleFactory styleFactory;
private final Map<String, String> properties;
private IconPropertyInjector(Map<String, String> properties) {
this.filterFactory = CommonFactoryFinder.getFilterFactory();
this.styleFactory = CommonFactoryFinder.getStyleFactory();
this.properties = properties;
}
private List<List<MiniRule>> injectProperties(List<List<MiniRule>> ftStyles) {
List<List<MiniRule>> result = new ArrayList<List<MiniRule>>();
for (int ftIdx = 0; ftIdx < ftStyles.size(); ftIdx++) {
List<MiniRule> origRules = ftStyles.get(ftIdx);
List<MiniRule> resultRules = new ArrayList<MiniRule>();
for (int ruleIdx = 0; ruleIdx < origRules.size(); ruleIdx++) {
MiniRule origRule = origRules.get(ruleIdx);
List<PointSymbolizer> resultSymbolizers = new ArrayList<PointSymbolizer>();
for (int symbIdx = 0; symbIdx < origRule.symbolizers.size(); symbIdx++) {
String key = ftIdx + "." + ruleIdx + "." + symbIdx;
if (properties.containsKey(key)) {
PointSymbolizer ptSym = origRule.symbolizers.get(symbIdx);
resultSymbolizers.add(injectPointSymbolizer(key, ptSym));
}
}
resultRules.add(new MiniRule(null, false, resultSymbolizers));
}
result.add(resultRules);
}
return result;
}
private boolean isStatic(Expression ex) {
return (Boolean) ex.accept(IsStaticExpressionVisitor.VISITOR, null);
}
private boolean shouldUpdate(String key, Expression exp) {
return exp != null && properties.containsKey(key) && !isStatic(exp);
}
private Expression getLiteral(String key) {
return filterFactory.literal(properties.get(key));
}
private PointSymbolizer injectPointSymbolizer(String key, PointSymbolizer original) {
PointSymbolizer copy = styleFactory.createPointSymbolizer();
if (original.getGraphic() != null) {
copy.setGraphic(injectGraphic(key, original.getGraphic()));
}
return copy;
}
private Graphic injectGraphic(String key, Graphic original) {
final ExternalGraphic[] externalGraphics;
final Mark[] marks;
final Symbol[] symbols = new Symbol[0];
Expression opacity = original.getOpacity();
Expression size = original.getSize();
Expression rotation = original.getRotation();
if (shouldUpdate(key + ".opacity", opacity)) {
opacity = getLiteral(key + ".opacity");
}
if (shouldUpdate(key + ".rotation", rotation)) {
rotation = getLiteral(key + ".rotation");
}
if (shouldUpdate(key + ".size", size)) {
size = getLiteral(key + ".size");
}
if (!original.graphicalSymbols().isEmpty()) {
List<Mark> markList = new ArrayList<Mark>();
List<ExternalGraphic> externalGraphicList = new ArrayList<ExternalGraphic>();
for (GraphicalSymbol symbol : original.graphicalSymbols()) {
if (symbol instanceof Mark) {
markList.add(injectMark(key, (Mark) symbol));
} else if (symbol instanceof ExternalGraphic) {
externalGraphicList.add(injectExternalGraphic(key, (ExternalGraphic) symbol));
}
}
marks = markList.toArray(new Mark[0]);
externalGraphics = externalGraphicList.toArray(new ExternalGraphic[0]);
} else {
marks = new Mark[0];
externalGraphics = new ExternalGraphic[0];
}
return styleFactory.createGraphic(externalGraphics, marks, symbols, opacity, size, rotation);
}
private ExternalGraphic injectExternalGraphic(String key, ExternalGraphic original) {
try {
final String format = original.getFormat();
final URL location;
final Expression locationExpression;
if (original.getLocation() == null) {
locationExpression = null;
} else {
locationExpression = ExpressionExtractor.extractCqlExpressions(original.getLocation().toExternalForm());
}
if (locationExpression == null || isStatic(locationExpression)) {
location = original.getLocation();
} else {
location = new URL(properties.get(key + ".url"));
}
return styleFactory.createExternalGraphic(location, format);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
private Mark injectMark(String key, Mark mark) {
final Expression wellKnownName;
final Stroke stroke;
final Fill fill;
final Expression size = null; // size and fill are handled only at the PointSymbolizer level - bug?
final Expression rotation = null;
if (mark.getWellKnownName() == null || isStatic(mark.getWellKnownName())) {
wellKnownName = mark.getWellKnownName();
} else {
wellKnownName = getLiteral(key + ".name");
}
if (mark.getFill() == null) {
fill = null;
} else {
fill = injectFill(key + ".fill", mark.getFill());
}
if (mark.getStroke() == null) {
stroke = null;
} else {
stroke = injectStroke(key + ".stroke", mark.getStroke());
}
return styleFactory.createMark(wellKnownName, stroke, fill, size, rotation);
}
private Stroke injectStroke(String key, Stroke stroke) {
final Expression color;
final Expression width;
final Expression opacity;
final Expression lineJoin;
final Expression lineCap;
final float[] dashArray;
final Expression dashOffset;
final Graphic graphicFill;
final Graphic graphicStroke;
if (stroke.getColor() == null || isStatic(stroke.getColor())) {
color = stroke.getColor();
} else {
color = getLiteral(key + ".color");
}
if (stroke.getDashOffset() == null || isStatic(stroke.getDashOffset())) {
dashOffset = stroke.getDashOffset();
} else {
dashOffset = getLiteral(key + ".linecap");
}
if (stroke.getLineCap() == null || isStatic(stroke.getDashOffset())) {
lineCap = stroke.getLineCap();
} else {
lineCap = getLiteral(key + ".linecap");
}
if (stroke.getLineJoin() == null || isStatic(stroke.getLineJoin())) {
lineJoin = stroke.getLineJoin();
} else {
lineJoin = getLiteral(key + ".linejoin");
}
if (stroke.getOpacity() == null || isStatic(stroke.getOpacity())) {
opacity = stroke.getOpacity();
} else {
opacity = getLiteral(key + ".opacity");
}
if (stroke.getWidth() == null || isStatic(stroke.getWidth())) {
width = stroke.getOpacity();
} else {
width = getLiteral(key + ".opacity");
}
if (stroke.getGraphicStroke() == null) {
graphicStroke = null;
} else {
graphicStroke = injectGraphic(key + ".graphic", stroke.getGraphicStroke());
}
if (stroke.getGraphicFill() == null) {
graphicFill = null;
} else {
graphicFill = injectGraphic(key + ".graphic", stroke.getGraphicFill());
}
if (stroke.getDashArray() == null) {
dashArray = null;
} else {
dashArray = Arrays.copyOf(stroke.getDashArray(), stroke.getDashArray().length);
}
return styleFactory.createStroke(color, width, opacity, lineJoin, lineCap, dashArray, dashOffset, graphicFill, graphicStroke);
}
private Fill injectFill(String key, Fill fill) {
final Expression color;
final Expression backgroundColor = null;
final Expression opacity;
final Graphic graphicFill;
if (fill.getColor() == null || isStatic(fill.getColor())) {
color = fill.getColor();
} else {
color = getLiteral(key + ".color");
}
if (fill.getOpacity() == null || isStatic(fill.getOpacity())) {
opacity = fill.getOpacity();
} else {
opacity = getLiteral(key + ".opacity");
}
if (fill.getGraphicFill() == null) {
graphicFill = null;
} else {
graphicFill = injectGraphic(key + ".graphic", fill.getGraphicFill());
}
return styleFactory.createFill(color, backgroundColor, opacity, graphicFill);
}
public static Style injectProperties(Style style, Map<String, String> properties) {
List<List<MiniRule>> ftStyles = MiniRule.minify(style);
StyleFactory factory = CommonFactoryFinder.getStyleFactory();
return MiniRule.makeStyle(factory, new IconPropertyInjector(properties).injectProperties(ftStyles));
}
}