/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license. * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt * for the full text of the license. */ /** * @requires OpenLayers/Format/XML.js * @requires OpenLayers/Style.js * @requires OpenLayers/Rule/FeatureId.js * @requires OpenLayers/Rule/Logical.js * @requires OpenLayers/Rule/Comparison.js */ /** * Class: OpenLayers.Format.SLD * Read/Wite SLD. Create a new instance with the * constructor. * * Inherits from: * - */ OpenLayers.Format.SLD = OpenLayers.Class(OpenLayers.Format.XML, { /** * APIProperty: sldns * Namespace used for sld. */ sldns: "http://www.opengis.net/sld", /** * APIProperty: ogcns * Namespace used for ogc. */ ogcns: "http://www.opengis.net/ogc", /** * APIProperty: gmlns * Namespace used for gml. */ gmlns: "http://www.opengis.net/gml", /** * APIProperty: defaultStyle. * {Object} * A simple style, preset with the SLD defaults. */ defaultStyle: { fillColor: "#808080", fillOpacity: 1, strokeColor: "#000000", strokeOpacity: 1, strokeWidth: 1, pointRadius: 6 }, /** * Property: withNamedLayer * {Boolean} Option set during . Default is false. If true, the * return from will be a two item array ([styles, namedLayer]): * - styles - {Array()} * - namedLayer - {Object} hash of userStyles, keyed by * sld:NamedLayer/Name, each again keyed by * sld:UserStyle/Name. Each entry of namedLayer is a * StyleMap for a layer, with the userStyle names as style * keys. */ withNamedLayer: false, /** * APIProperty: overrideDefaultStyleKey * {Boolean} Store styles with key of "default" instead of user style name. * If true, userStyles with sld:IsDefault==1 will be stored with * key "default" instead of the sld:UserStyle/Name in the style map. * Default is true. */ overrideDefaultStyleKey: true, /** * Constructor: OpenLayers.Format.SLD * Create a new parser for SLD. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ initialize: function(options) { OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); }, /** * APIMethod: read * Read data from a string, and return a list of features. * * Parameters: * data - {String} or {XMLNode} data to read/parse. * options - {Object} Object that sets optional read configuration values. * These include , and . * * Returns: * {Array()} List of styles. If is * true, return will be a two item array where the first item is * a list of styles and the second is the namedLayer object. */ read: function(data, options) { if (typeof data == "string") { data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); } OpenLayers.Util.applyDefaults(options, { withNamedLayer: false, overrideDefaultStyleKey: true }); var userStyles = this.getElementsByTagNameNS( data, this.sldns, "UserStyle" ); var result = {}; if (userStyles.length > 0) { var namedLayer = {}; var styles = new Array(userStyles.length); var styleName, userStyle, style; for (var i=0; i} */ parseUserStyle: function(xmlNode, name) { var userStyle = new OpenLayers.Style(this.defaultStyle, {name: name}); userStyle.isDefault = ( this.parseProperty(xmlNode, this.sldns, "IsDefault") == 1 ); // get the name of the layer if we have a NamedLayer var namedLayerNode = xmlNode.parentNode; var nameNodes = this.getElementsByTagNameNS( namedLayerNode, this.sldns, "Name" ); if (namedLayerNode.nodeName.indexOf("NamedLayer") != -1 && nameNodes && nameNodes.length > 0 && nameNodes[0].parentNode == namedLayerNode) { userStyle.layerName = this.getChildValue(nameNodes[0]); } var ruleNodes = this.getElementsByTagNameNS( xmlNode, this.sldns, "Rule" ); if (ruleNodes.length > 0) { var rules = userStyle.rules; var ruleName; for (var i=0; i} * * Returns: * {Object} Hash of rule properties */ parseRule: function(xmlNode, name) { // FILTERS var filter = this.getElementsByTagNameNS(xmlNode, this.ogcns, "Filter"); if (filter && filter.length > 0) { var rule = this.parseFilter(filter[0]); } else { // rule applies to all features var rule = new OpenLayers.Rule(); } rule.name = name; // SCALE DENOMINATORS // MinScaleDenominator var minScale = this.getElementsByTagNameNS( xmlNode, this.sldns, "MinScaleDenominator" ); if (minScale && minScale.length > 0) { rule.minScale = parseFloat(this.getChildValue(minScale[0])); } // MaxScaleDenominator var maxScale = this.getElementsByTagNameNS( xmlNode, this.sldns, "MaxScaleDenominator" ); if (maxScale && maxScale.length > 0) { rule.maxScale = parseFloat(this.getChildValue(maxScale[0])); } // STYLES // walk through all symbolizers var prefixes = OpenLayers.Style.SYMBOLIZER_PREFIXES; for (var s=0; s 0) { var style = {}; // externalGraphic var graphic = this.getElementsByTagNameNS( symbolizer[0], this.sldns, "Graphic" ); if (graphic && graphic.length > 0) { style.externalGraphic = this.parseProperty( graphic[0], this.sldns, "OnlineResource", "xlink:href" ); style.pointRadius = this.parseProperty( graphic[0], this.sldns, "Size" ); style.graphicOpacity = this.parseProperty( graphic[0], this.sldns, "Opacity" ); } // fill var fill = this.getElementsByTagNameNS( symbolizer[0], this.sldns, "Fill" ); if (fill && fill.length > 0) { style.fillColor = this.parseProperty( fill[0], this.sldns, "CssParameter", "name", "fill" ); style.fillOpacity = this.parseProperty( fill[0], this.sldns, "CssParameter", "name", "fill-opacity" ) || 1; } // stroke var stroke = this.getElementsByTagNameNS( symbolizer[0], this.sldns, "Stroke" ); if (stroke && stroke.length > 0) { style.strokeColor = this.parseProperty( stroke[0], this.sldns, "CssParameter", "name", "stroke" ); style.strokeOpacity = this.parseProperty( stroke[0], this.sldns, "CssParameter", "name", "stroke-opacity" ) || 1; style.strokeWidth = this.parseProperty( stroke[0], this.sldns, "CssParameter", "name", "stroke-width" ); style.strokeLinecap = this.parseProperty( stroke[0], this.sldns, "CssParameter", "name", "stroke-linecap" ); } // set the [point|line|polygon]Symbolizer property of the rule rule.symbolizer[prefixes[s]] = style; } } return rule; }, /** * Method: parseFilter * Parses ogc fiters. * * Parameters: * xmlNode - {} * * Returns: * {} rule representing the filter */ parseFilter: function(xmlNode) { // ogc:FeatureId filter var filter = this.getNodeOrChildrenByTagName(xmlNode, "FeatureId"); if (filter) { var rule = new OpenLayers.Rule.FeatureId(); for (var i=0; i)} or null if no matching content is found */ getNodeOrChildrenByTagName: function(xmlNode, tagName) { var nodeName = (xmlNode.prefix) ? xmlNode.nodeName.split(":")[1] : xmlNode.nodeName; if (nodeName == tagName) { return [xmlNode]; } else { var nodelist = this.getElementsByTagNameNS( xmlNode, this.ogcns, tagName); } // make a new list which only contains matching child nodes if (nodelist.length > 0) { var node; var list = []; for (var i=0; i 0 ? list : null; } return null; }, /** * Method: parseProperty * Convenience method to parse the different kinds of properties * found in the sld and ogc namespace. * * Parses an ogc node that can either contain a value directly, * or inside a property. The parsing can also be limited * to nodes with certain attribute names and/or values. * * Parameters: * xmlNode - {} * namespace - {String} namespace of the node to find * propertyName - {String} name of the property to parse * attributeName - {String} optional name of the property to match * attributeValue - {String} optional value of the specified attribute * * Returns: * {String} The value for the requested property. */ parseProperty: function(xmlNode, namespace, propertyName, attributeName, attributeValue) { var result = null; var propertyNodeList = this.getElementsByTagNameNS( xmlNode, namespace, propertyName); if (propertyNodeList && propertyNodeList.length > 0) { var propertyNode = attributeName ? this.getNodeWithAttribute(propertyNodeList, attributeName) : propertyNodeList[0]; // strip namespace from attribute name for Opera browsers if (window.opera && attributeName) { var nsDelimiterPos = attributeName.indexOf(":"); if (nsDelimiterPos != -1) { attributeName = attributeName.substring(++nsDelimiterPos); } } // get the property value from the node matching attributeName // and attributeValue, eg.: // // red // // or: // red if (attributeName && attributeValue) { propertyNode = this.getNodeWithAttribute(propertyNodeList, attributeName, attributeValue); result = this.parseParameter(propertyNode); } // get the attribute value and use it as result, eg.: // if (attributeName && !attributeValue) { var propertyNode = this.getNodeWithAttribute(propertyNodeList, attributeName); result = propertyNode.getAttribute(attributeName); } // get the property value directly or from an ogc:propertyName, // ogc:Literal or any other property at the level of the property // node, eg.: // 0.5 if (!attributeName) { var result = this.parseParameter(propertyNode); } } // adjust the result to be a trimmed string or a number if (result) { result = OpenLayers.String.trim(result); if (!isNaN(result)) { result = parseFloat(result); } } return result; }, /** * Method: parseParameter * parses a property for propertyNames, Literals and textContent and * creates the according value string. * * Parameters: * xmlNode - {} * * Returns: * {String} a string holding a value suitable for OpenLayers.Style.value */ parseParameter: function(xmlNode) { if (!xmlNode) { return null; } var childNodes = xmlNode.childNodes; if (!childNodes) { return null; } var value = new Array(childNodes.length); for (var i=0; i)} list to search * attributeName - {String} name of the attribute to match * attributeValue - {String} optional value of the attribute */ getNodeWithAttribute: function(xmlNodeList, attributeName, attributeValue) { for (var i=0; i