//$HeadURL: https://sushibar/svn/deegree/base/trunk/resources/eclipse/svn_classfile_header_template.xml $
/*---------------- FILE HEADER ------------------------------------------
This file is part of deegree.
Copyright (C) 2001-2007 by:
Department of Geography, University of Bonn
http://www.giub.uni-bonn.de/deegree/
lat/lon GmbH
http://www.lat-lon.de
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; either
version 2.1 of the License, or (at your option) any later version.
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.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Contact:
Andreas Poth
lat/lon GmbH
Aennchenstr. 19
53177 Bonn
Germany
E-Mail: poth@lat-lon.de
Prof. Dr. Klaus Greve
Department of Geography
University of Bonn
Meckenheimer Allee 166
53115 Bonn
Germany
E-Mail: greve@giub.uni-bonn.de
---------------------------------------------------------------------------*/
package org.openjump.util;
import static java.awt.Color.decode;
import static java.awt.Font.BOLD;
import static java.awt.Font.ITALIC;
import static java.awt.Font.PLAIN;
import static java.lang.Double.parseDouble;
import static java.lang.Math.round;
import static org.openjump.util.XPathUtils.getElement;
import static org.openjump.util.XPathUtils.getElements;
import static org.openjump.util.XPathUtils.getInt;
import java.awt.Color;
import java.awt.Font;
import java.awt.Paint;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import javax.xml.namespace.NamespaceContext;
import javax.xml.xpath.XPathExpressionException;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import com.vividsolutions.jump.util.Range;
import com.vividsolutions.jump.workbench.ui.renderer.style.BasicStyle;
import com.vividsolutions.jump.workbench.ui.renderer.style.ColorThemingStyle;
import com.vividsolutions.jump.workbench.ui.renderer.style.LabelStyle;
import com.vividsolutions.jump.workbench.ui.renderer.style.SquareVertexStyle;
import com.vividsolutions.jump.workbench.ui.renderer.style.Style;
import com.vividsolutions.jump.workbench.ui.renderer.style.VertexStyle;
import de.latlon.deejump.plugin.style.BitmapVertexStyle;
import de.latlon.deejump.plugin.style.CircleVertexStyle;
import de.latlon.deejump.plugin.style.CrossVertexStyle;
import de.latlon.deejump.plugin.style.StarVertexStyle;
import de.latlon.deejump.plugin.style.TriangleVertexStyle;
/**
* <code>SLDImporter</code>
*
* @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a>
* @author last edited by: $Author:$
*
* @version $Revision:$, $Date:$
*/
public class SLDImporter {
private static final Logger LOG = Logger.getLogger(SLDImporter.class);
/**
* The SLD namespace URI.
*/
public static final String SLDNS = "http://www.opengis.net/sld";
/**
* The OGC namespace URI.
*/
public static final String OGCNS = "http://www.opengis.net/ogc";
/**
* Namespace context with sld and ogc namespaces.
*/
public static final NamespaceContext NSCONTEXT = new NamespaceContext() {
public String getNamespaceURI(String prefix) {
if (prefix.equals("sld")) {
return SLDNS;
}
if (prefix.equals("ogc")) {
return OGCNS;
}
return null;
}
public String getPrefix(String namespace) {
if (namespace.equals(SLDNS)) {
return "sld";
}
if (namespace.equals(OGCNS)) {
return "ogc";
}
return null;
}
public Iterator<String> getPrefixes(String namespace) {
if (namespace.equals(SLDNS)) {
return new Iterator<String>() {
boolean done = false;
public boolean hasNext() {
return !done;
}
public String next() {
done = true;
return "sld";
}
public void remove() {
// ignore
}
};
}
if (namespace.equals(OGCNS)) {
return new Iterator<String>() {
boolean done = false;
public boolean hasNext() {
return !done;
}
public String next() {
done = true;
return "ogc";
}
public void remove() {
// ignore
}
};
}
return null;
}
};
/**
* @param doc
* @return a list of SLD rule names
*/
public static LinkedList<String> getRuleNames(Document doc) {
LinkedList<String> list = new LinkedList<String>();
try {
LinkedList<Element> elems = getElements("//sld:Rule/sld:Name", doc.getDocumentElement(), NSCONTEXT);
for (Element e : elems) {
list.add(e.getTextContent());
}
} catch (XPathExpressionException e) {
// only happens if the xpath is not valid
LOG.error(e);
e.printStackTrace();
return null;
}
return list;
}
/**
* @param doc
* @return a list of SLD rule names
*/
public static LinkedList<String> getRuleNamesWithGeometrySymbolizers(Document doc) {
LinkedList<String> list = new LinkedList<String>();
try {
LinkedList<Element> elems = getElements("//sld:Rule[(count(sld:PointSymbolizer)+count(sld:LineSymbolizer)+"
+ "count(sld:PolygonSymbolizer))>0]/sld:Name", doc.getDocumentElement(), NSCONTEXT);
for (Element e : elems) {
list.add(e.getTextContent());
}
} catch (XPathExpressionException e) {
// only happens if the xpath is not valid
LOG.error(e);
e.printStackTrace();
return null;
}
return list;
}
/**
* @param doc
* @return a list of SLD FeatureTypeStyle names
*/
public static LinkedList<String> getPossibleColorThemingStyleNames(Document doc) {
LinkedList<String> list = new LinkedList<String>();
try {
LinkedList<Element> elems = getElements("//sld:UserStyle[count(sld:FeatureTypeStyle"
+ "/sld:Rule/ogc:Filter) > 0]/sld:Name", doc.getDocumentElement(), NSCONTEXT);
for (Element e : elems) {
list.add(e.getTextContent());
}
} catch (XPathExpressionException e) {
// only happens if the xpath is not valid
LOG.error(e);
e.printStackTrace();
return null;
}
return list;
}
/**
* @param doc
* @return a list of SLD rule names
*/
public static LinkedList<String> getRuleNamesWithTextSymbolizers(Document doc) {
LinkedList<String> list = new LinkedList<String>();
try {
LinkedList<Element> elems = getElements("//sld:Rule[count(sld:TextSymbolizer)>0]/sld:Name", doc
.getDocumentElement(), NSCONTEXT);
for (Element e : elems) {
list.add(e.getTextContent());
}
} catch (XPathExpressionException e) {
// only happens if the xpath is not valid
LOG.error(e);
e.printStackTrace();
return null;
}
return list;
}
/**
* @param name
* @param doc
* @return a corresponding BasicStyle
* @see #getBasicStyle(Element)
*/
public static BasicStyle getBasicStyle(String name, Document doc) {
try {
return getBasicStyle(getElement("//sld:Rule[sld:Name='" + name + "']", doc.getDocumentElement(), NSCONTEXT));
} catch (XPathExpressionException e) {
// only happens if some xpath is not valid
LOG.error(e);
e.printStackTrace();
return null;
}
}
/**
* Ignores any filters, and uses the information from Point-, Line- and
* PolygonSymbolizers.
*
* @param rule
* @return a corresponding BasicStyle
*/
public static BasicStyle getBasicStyle(Element rule) {
if (rule == null) {
return null;
}
try {
BasicStyle style = new BasicStyle();
style.setRenderingFill(false);
style.setRenderingFillPattern(false);
style.setRenderingLine(false);
style.setRenderingLinePattern(false);
boolean oneApplied = false;
Element symbolizer = getElement("sld:PointSymbolizer", rule, NSCONTEXT);
if (symbolizer != null) {
oneApplied = true;
}
applyPointSymbolizer(symbolizer, style);
symbolizer = getElement("sld:LineSymbolizer", rule, NSCONTEXT);
if (symbolizer != null) {
oneApplied = true;
}
applyLineSymbolizer(symbolizer, style);
symbolizer = getElement("sld:PolygonSymbolizer", rule, NSCONTEXT);
if (symbolizer != null) {
oneApplied = true;
}
applyPolygonSymbolizer(symbolizer, style);
if (!oneApplied) {
return null;
}
return style;
} catch (XPathExpressionException e) {
// only happens if some xpath is not valid
LOG.error(e);
e.printStackTrace();
return null;
}
}
/**
* @param name
* @param doc
* @return a vertex style, if a special one was found (use the basic style
* from #getBasicStyle if this is null)
*/
public static VertexStyle getVertexStyle(String name, Document doc) {
try {
Element rule = getElement("//sld:Rule[sld:Name='" + name + "']", doc.getDocumentElement(), NSCONTEXT);
if (rule == null) {
return null;
}
Element symbolizer = getElement("sld:PointSymbolizer", rule, NSCONTEXT);
return applyPointSymbolizer(symbolizer, new BasicStyle());
} catch (XPathExpressionException e) {
// only happens if some xpath is not valid
LOG.error(e);
e.printStackTrace();
return null;
}
}
private static VertexStyle applyPointSymbolizer(Element symbolizer, BasicStyle style)
throws XPathExpressionException {
if (symbolizer == null) {
return null;
}
Element e = getElement(".//sld:WellKnownName", symbolizer, NSCONTEXT);
VertexStyle extra = null;
if (e != null) {
String n = e.getTextContent();
if (n != null) {
n = n.trim();
}
if (n != null) {
if (n.equalsIgnoreCase("circle")) {
extra = new CircleVertexStyle();
}
if (n.equalsIgnoreCase("cross")) {
extra = new CrossVertexStyle();
}
if (n.equalsIgnoreCase("square")) {
extra = new SquareVertexStyle();
}
if (n.equalsIgnoreCase("star")) {
extra = new StarVertexStyle();
}
if (n.equalsIgnoreCase("triangle")) {
extra = new TriangleVertexStyle();
}
}
}
if (extra == null) {
extra = parseGraphic(symbolizer);
}
int size = getInt("sld:size", symbolizer, NSCONTEXT);
if (size != 0) {
extra.setSize(size / 2);
}
Element fill = getElement(".//sld:Fill", symbolizer, NSCONTEXT);
Element stroke = getElement(".//sld:Stroke", symbolizer, NSCONTEXT);
applyFill(fill, style);
applyStroke(stroke, style);
if (extra != null) {
applyFill(fill, extra);
applyStroke(stroke, extra);
}
return extra;
}
private static BasicStyle applyLineSymbolizer(Element symbolizer, BasicStyle style) throws XPathExpressionException {
if (symbolizer == null) {
return null;
}
Element fill = getElement("sld:Fill", symbolizer, NSCONTEXT);
Element stroke = getElement("sld:Stroke", symbolizer, NSCONTEXT);
applyFill(fill, style);
applyStroke(stroke, style);
return style;
}
private static BasicStyle applyPolygonSymbolizer(Element symbolizer, BasicStyle style)
throws XPathExpressionException {
if (symbolizer == null) {
return null;
}
Element fill = getElement("sld:Fill", symbolizer, NSCONTEXT);
Element stroke = getElement("sld:Stroke", symbolizer, NSCONTEXT);
URL u = parseGraphicURL(symbolizer);
if (u != null) {
Paint p = new CustomTexturePaint(u);
style.setFillPattern(p);
style.setRenderingFillPattern(true);
}
applyFill(fill, style);
applyStroke(stroke, style);
return style;
}
private static void applyFill(Element fill, StrokeFillStyle style) throws XPathExpressionException {
if (fill == null) {
return;
}
if (style instanceof BasicStyle) {
((BasicStyle) style).setRenderingFill(true);
}
LinkedList<Element> params = getElements("sld:CssParameter", fill, NSCONTEXT);
for (Element p : params) {
String type = p.getAttribute("name");
String a = p.getTextContent();
if (a == null || a.trim().length() == 0) {
continue;
}
a = a.trim();
if (type.equals("fill")) {
style.setFillColor(decode(a));
}
if (type.equals("fill-opacity")) {
style.setAlpha((int) (255 * parseDouble(a)));
}
}
}
private static void applyStroke(Element stroke, StrokeFillStyle style) throws XPathExpressionException {
if (stroke == null) {
return;
}
if (style instanceof BasicStyle) {
((BasicStyle) style).setRenderingLine(true);
}
LinkedList<Element> params = getElements("sld:CssParameter", stroke, NSCONTEXT);
for (Element p : params) {
String type = p.getAttribute("name");
String a = p.getTextContent();
if (a == null || a.trim().length() == 0) {
continue;
}
a = a.trim();
if (type.equals("stroke")) {
style.setLineColor(decode(a));
}
if (type.equals("stroke-width")) {
style.setLineWidth((int) parseDouble(a));
}
if (type.equals("stroke-opacity")) {
style.setAlpha((int) (255 * parseDouble(a)));
}
if (type.equals("stroke-dasharray")) {
style.setLinePattern(a.replace(' ', ','));
style.setRenderingLinePattern(true);
}
}
}
private static URL parseGraphicURL(Element e) throws XPathExpressionException {
e = getElement(".//sld:OnlineResource", e, NSCONTEXT);
if (e == null) {
return null;
}
// assume, it's an external graphic
String s = e.getAttributeNS("http://www.w3.org/1999/xlink", "href");
URL u = null;
try {
u = new URL(s);
} catch (MalformedURLException ex) {
try {
u = new File(s).toURI().toURL();
} catch (MalformedURLException e1) {
// ignore it
}
}
return u;
}
private static VertexStyle parseGraphic(Element e) throws XPathExpressionException {
URL u = parseGraphicURL(e);
if (u != null) {
return new BitmapVertexStyle(u.getFile());
}
return null;
}
/**
* Converts a TextSymbolizer.
*
* @param name
* @param doc
* @return the label style or null, if none was found
*/
public static LabelStyle getLabelStyle(String name, Document doc) {
try {
LabelStyle style = new LabelStyle();
Element symbolizer = getElement("//sld:Rule[sld:Name='" + name + "']/sld:TextSymbolizer", doc
.getDocumentElement(), NSCONTEXT);
if (symbolizer == null) {
return null;
}
Element label = getElement("sld:Label", symbolizer, NSCONTEXT);
String lAtt = getElement("ogc:PropertyName", label, NSCONTEXT).getTextContent();
lAtt = lAtt.substring(lAtt.indexOf(':') + 1);
style.setAttribute(lAtt);
Element fill = getElement("sld:Fill", symbolizer, NSCONTEXT);
if (fill != null) {
LinkedList<Element> params = getElements("sld:CssParameter", fill, NSCONTEXT);
for (Element p : params) {
String type = p.getAttribute("name");
String a = p.getTextContent();
if (a == null || a.trim().length() == 0) {
continue;
}
a = a.trim();
if (type.equals("fill")) {
style.setColor(decode(a));
}
}
}
Element font = getElement("sld:Font", symbolizer, NSCONTEXT);
LinkedList<Element> params = getElements("sld:CssParameter", font, NSCONTEXT);
String fFamily = null;
int fStyle = 0, fSize = 0;
for (Element p : params) {
String type = p.getAttribute("name");
String a = p.getTextContent();
if (a == null || a.trim().length() == 0) {
continue;
}
a = a.trim();
if (type.equals("font-family")) {
fFamily = a;
}
if (type.equals("font-style")) {
if (a.equalsIgnoreCase("normal")) {
fStyle |= PLAIN;
}
if (a.equalsIgnoreCase("italic")) {
fStyle |= ITALIC;
}
}
if (type.equals("font-weight")) {
if (a.equalsIgnoreCase("normal")) {
fStyle |= PLAIN;
}
if (a.equalsIgnoreCase("bold")) {
fStyle |= BOLD;
}
}
if (type.equals("font-size")) {
fSize = (int) round(parseDouble(a));
}
}
style.setFont(new Font(fFamily, fStyle, fSize));
style.setEnabled(true);
Element halo = getElement("sld:Halo", symbolizer, NSCONTEXT);
if (halo != null) {
style.setOutlineShowing(true);
Element rad = getElement("sld:Radius", halo, NSCONTEXT);
if (rad != null) {
style.setOutlineWidth((int) parseDouble(rad.getTextContent()));
}
params = getElements("sld:CssParameter", getElement("sld:Fill", halo, NSCONTEXT), NSCONTEXT);
for (Element p : params) {
String type = p.getAttribute("name");
String a = p.getTextContent();
if (a == null || a.trim().length() == 0) {
continue;
}
a = a.trim();
if (type.equals("fill")) {
style.setOutlineColor(decode(a));
}
}
}
return style;
} catch (XPathExpressionException e) {
// only happens if some xpath is not valid
LOG.error(e);
e.printStackTrace();
return null;
}
}
// TODO this method does not really check for the content of the filter
// expressions
// it assumes ogc:And structures, so in most cases this method will actually
// return something that's WRONG
private static Object parseValues(Element filter) throws XPathExpressionException {
if (filter == null) {
LOG.warn("An ogc:filter could not be found while trying to parse a color theming style.");
return null;
}
Element lower = getElement(".//ogc:LowerBoundary", filter, NSCONTEXT);
Element upper = getElement(".//ogc:UpperBoundary", filter, NSCONTEXT);
if (lower != null && upper != null) {
String s1 = getElement("ogc:Literal", lower, NSCONTEXT).getTextContent().trim();
String s2 = getElement("ogc:Literal", upper, NSCONTEXT).getTextContent().trim();
return new Range(s1, true, s2, false);
}
// try different filters, as used by uDig
boolean lowerEqual = false;
boolean upperEqual = false;
upper = getElement(".//ogc:PropertyIsLessThan", filter, NSCONTEXT);
if (upper == null) {
upper = getElement(".//ogc:PropertyIsLessThanOrEqualTo", filter, NSCONTEXT);
upperEqual = true;
}
lower = getElement(".//ogc:PropertyIsGreaterThan", filter, NSCONTEXT);
if (lower == null) {
lower = getElement(".//ogc:PropertyIsGreaterThanOrEqualTo", filter, NSCONTEXT);
lowerEqual = true;
}
if (lower != null && upper != null) {
String s1 = getElement("ogc:Literal", lower, NSCONTEXT).getTextContent().trim();
String s2 = getElement("ogc:Literal", upper, NSCONTEXT).getTextContent().trim();
return new Range(s1, lowerEqual, s2, upperEqual);
}
return getElement(".//ogc:Literal", filter, NSCONTEXT).getTextContent().trim();
}
/**
* @param name
* the name of the feature type style
* @param doc
* @return the color theming style
*/
public static ColorThemingStyle getColorThemingStyle(String name, Document doc) {
try {
Element featureTypeStyle = getElement("//sld:UserStyle[(count(sld:FeatureTypeStyle"
+ "/sld:Rule/ogc:Filter) > 0) and sld:Name='" + name + "']/sld:FeatureTypeStyle", doc
.getDocumentElement(), NSCONTEXT);
if (featureTypeStyle == null) {
return null;
}
ColorThemingStyle style = new ColorThemingStyle();
String att = getElement(".//ogc:PropertyName", featureTypeStyle, NSCONTEXT).getTextContent();
style.setAttributeName(att);
HashMap<Object, StrokeFillStyle> map = new HashMap<Object, StrokeFillStyle>();
HashMap<Object, String> labelMap = new HashMap<Object, String>();
LinkedList<Element> rules = getElements("sld:Rule", featureTypeStyle, NSCONTEXT);
for (Element rule : rules) {
BasicStyle basic = getBasicStyle(rule);
Object val = parseValues(getElement("ogc:Filter", rule, NSCONTEXT));
if (val != null) {
map.put(val, basic);
labelMap.put(val, val.toString());
}
}
style.setAttributeValueToBasicStyleMap(map);
style.setAttributeValueToLabelMap(labelMap);
return style;
} catch (XPathExpressionException e) {
// only happens if some xpath is not valid
LOG.error(e);
e.printStackTrace();
return null;
}
}
/**
* <code>FillStyle</code>
*
* @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a>
* @author last edited by: $Author:$
*
* @version $Revision:$, $Date:$
*/
public static interface FillStyle {
/**
* @param c
*/
public void setFillColor(Color c);
/**
* @param a
*/
public void setAlpha(int a);
}
/**
* <code>StrokeStyle</code>
*
* @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a>
* @author last edited by: $Author:$
*
* @version $Revision:$, $Date:$
*/
public static interface StrokeStyle {
/**
* @param c
*/
public void setLineColor(Color c);
/**
* @param w
*/
public void setLineWidth(int w);
/**
* @param a
*/
public void setAlpha(int a);
/**
* @param b
* @return a basic style
*/
public BasicStyle setRenderingLinePattern(boolean b);
/**
* @param p
* @return a basic style
*/
public BasicStyle setLinePattern(String p);
}
/**
* <code>SizedStyle</code>
*
* @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a>
* @author last edited by: $Author:$
*
* @version $Revision:$, $Date:$
*/
public static interface SizedStyle {
/**
* @param s
*/
public void setSize(int s);
}
/**
* <code>StrokeFillStyle</code>
*
* @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a>
* @author last edited by: $Author:$
*
* @version $Revision:$, $Date:$
*/
public static interface StrokeFillStyle extends StrokeStyle, FillStyle, Style {
// no methods, they're combined by the stroke and fill interfaces
}
/**
* <code>SizedStrokeFillStyle</code>
*
* @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a>
* @author last edited by: $Author:$
*
* @version $Revision:$, $Date:$
*/
public static interface SizedStrokeFillStyle extends StrokeFillStyle, SizedStyle {
// no methods, they're combined
}
}