/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/Rule.js * @requires OpenLayers/Format/SLD.js * @requires OpenLayers/Format/Filter/v1_0_0.js * @requires OpenLayers/Symbolizer/Point.js * @requires OpenLayers/Symbolizer/Line.js * @requires OpenLayers/Symbolizer/Polygon.js * @requires OpenLayers/Symbolizer/Text.js * @requires OpenLayers/Symbolizer/Raster.js */ /** * Class: OpenLayers.Format.SLD.v1 * Superclass for SLD version 1 parsers. * * Inherits from: * - */ OpenLayers.Format.SLD.v1 = OpenLayers.Class(OpenLayers.Format.Filter.v1_0_0, { /** * Property: namespaces * {Object} Mapping of namespace aliases to namespace URIs. */ namespaces: { sld: "http://www.opengis.net/sld", ogc: "http://www.opengis.net/ogc", gml: "http://www.opengis.net/gml", xlink: "http://www.w3.org/1999/xlink", xsi: "http://www.w3.org/2001/XMLSchema-instance" }, /** * Property: defaultPrefix */ defaultPrefix: "sld", /** * Property: schemaLocation * {String} Schema location for a particular minor version. */ schemaLocation: null, /** * APIProperty: multipleSymbolizers * {Boolean} Support multiple symbolizers per rule. Default is false. if * true, an OpenLayers.Style2 instance will be created to represent * user styles instead of an OpenLayers.Style instace. The * OpenLayers.Style2 class allows collections of rules with multiple * symbolizers, but is not currently useful for client side rendering. * If multiple symbolizers is true, multiple FeatureTypeStyle elements * are preserved in reading/writing by setting symbolizer zIndex values. * In addition, the property is ignored if * multiple symbolizers are supported (defaults should be applied * when rendering). */ multipleSymbolizers: false, /** * Property: featureTypeCounter * {Number} Private counter for multiple feature type styles. */ featureTypeCounter: null, /** * APIProperty: defaultSymbolizer. * {Object} A symbolizer with the SLD defaults. */ defaultSymbolizer: { fillColor: "#808080", fillOpacity: 1, strokeColor: "#000000", strokeOpacity: 1, strokeWidth: 1, strokeDashstyle: "solid", pointRadius: 3, graphicName: "square" }, /** * Constructor: OpenLayers.Format.SLD.v1 * Instances of this class are not created directly. Use the * constructor instead. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ /** * Method: read * * Parameters: * data - {DOMElement} An SLD document element. * options - {Object} Options for the reader. * * Valid options: * namedLayersAsArray - {Boolean} Generate a namedLayers array. If false, * the namedLayers property value will be an object keyed by layer name. * Default is false. * * Returns: * {Object} An object representing the SLD. */ read: function(data, options) { options = OpenLayers.Util.applyDefaults(options, this.options); var sld = { namedLayers: options.namedLayersAsArray === true ? [] : {} }; this.readChildNodes(data, sld); return sld; }, /** * Property: readers * Contains public functions, grouped by namespace prefix, that will * be applied when a namespaced node is found matching the function * name. The function will be applied in the scope of this parser * with two arguments: the node being read and a context object passed * from the parent. */ readers: OpenLayers.Util.applyDefaults({ "sld": { "StyledLayerDescriptor": function(node, sld) { sld.version = node.getAttribute("version"); this.readChildNodes(node, sld); }, "Name": function(node, obj) { obj.name = this.getChildValue(node); }, "Title": function(node, obj) { obj.title = this.getChildValue(node); }, "Abstract": function(node, obj) { obj.description = this.getChildValue(node); }, "NamedLayer": function(node, sld) { var layer = { userStyles: [], namedStyles: [] }; this.readChildNodes(node, layer); // give each of the user styles this layer name for(var i=0, len=layer.userStyles.length; i 1/3 && x < 2/3) { labelAlign = 'c'; } else if (x >= 2/3) { labelAlign = 'r'; } if (y <= 1/3) { labelAlign += 'b'; } else if (y > 1/3 && y < 2/3) { labelAlign += 'm'; } else if (y >= 2/3) { labelAlign += 't'; } config.labelAlign = labelAlign; OpenLayers.Util.applyDefaults(symbolizer, config); }, "AnchorPoint": function(node, symbolizer) { this.readChildNodes(node, symbolizer); }, "AnchorPointX": function(node, symbolizer) { var labelAnchorPointX = this.readers.ogc._expression.call(this, node); // always string, could be empty string if(labelAnchorPointX) { symbolizer.labelAnchorPointX = labelAnchorPointX; } }, "AnchorPointY": function(node, symbolizer) { var labelAnchorPointY = this.readers.ogc._expression.call(this, node); // always string, could be empty string if(labelAnchorPointY) { symbolizer.labelAnchorPointY = labelAnchorPointY; } }, "Displacement": function(node, symbolizer) { this.readChildNodes(node, symbolizer); }, "DisplacementX": function(node, symbolizer) { var labelXOffset = this.readers.ogc._expression.call(this, node); // always string, could be empty string if(labelXOffset) { symbolizer.labelXOffset = labelXOffset; } }, "DisplacementY": function(node, symbolizer) { var labelYOffset = this.readers.ogc._expression.call(this, node); // always string, could be empty string if(labelYOffset) { symbolizer.labelYOffset = labelYOffset; } }, "LinePlacement": function(node, symbolizer) { this.readChildNodes(node, symbolizer); }, "PerpendicularOffset": function(node, symbolizer) { var labelPerpendicularOffset = this.readers.ogc._expression.call(this, node); // always string, could be empty string if(labelPerpendicularOffset) { symbolizer.labelPerpendicularOffset = labelPerpendicularOffset; } }, "Label": function(node, symbolizer) { var value = this.readers.ogc._expression.call(this, node); if (value) { symbolizer.label = value; } }, "Font": function(node, symbolizer) { this.readChildNodes(node, symbolizer); }, "Halo": function(node, symbolizer) { // halo has a fill, so send fresh object var obj = {}; this.readChildNodes(node, obj); symbolizer.haloRadius = obj.haloRadius; symbolizer.haloColor = obj.fillColor; symbolizer.haloOpacity = obj.fillOpacity; }, "Radius": function(node, symbolizer) { var radius = this.readers.ogc._expression.call(this, node); if(radius != null) { // radius is only used for halo symbolizer.haloRadius = radius; } }, "RasterSymbolizer": function(node, rule) { var config = {}; this.readChildNodes(node, config); if (this.multipleSymbolizers) { config.zIndex = this.featureTypeCounter; rule.symbolizers.push( new OpenLayers.Symbolizer.Raster(config) ); } else { rule.symbolizer["Raster"] = OpenLayers.Util.applyDefaults( config, rule.symbolizer["Raster"] ); } }, "Geometry": function(node, obj) { obj.geometry = {}; this.readChildNodes(node, obj.geometry); }, "ColorMap": function(node, symbolizer) { symbolizer.colorMap = []; this.readChildNodes(node, symbolizer.colorMap); }, "ColorMapEntry": function(node, colorMap) { var q = node.getAttribute("quantity"); var o = node.getAttribute("opacity"); colorMap.push({ color: node.getAttribute("color"), quantity: q !== null ? parseFloat(q) : undefined, label: node.getAttribute("label") || undefined, opacity: o !== null ? parseFloat(o) : undefined }); }, "LineSymbolizer": function(node, rule) { var config = {}; this.readChildNodes(node, config); if (this.multipleSymbolizers) { config.zIndex = this.featureTypeCounter; rule.symbolizers.push( new OpenLayers.Symbolizer.Line(config) ); } else { rule.symbolizer["Line"] = OpenLayers.Util.applyDefaults( config, rule.symbolizer["Line"] ); } }, "PolygonSymbolizer": function(node, rule) { var config = { fill: false, stroke: false }; if (!this.multipleSymbolizers) { config = rule.symbolizer["Polygon"] || config; } this.readChildNodes(node, config); if (this.multipleSymbolizers) { config.zIndex = this.featureTypeCounter; rule.symbolizers.push( new OpenLayers.Symbolizer.Polygon(config) ); } else { rule.symbolizer["Polygon"] = config; } }, "PointSymbolizer": function(node, rule) { var config = { fill: false, stroke: false, graphic: false }; if (!this.multipleSymbolizers) { config = rule.symbolizer["Point"] || config; } this.readChildNodes(node, config); if (this.multipleSymbolizers) { config.zIndex = this.featureTypeCounter; rule.symbolizers.push( new OpenLayers.Symbolizer.Point(config) ); } else { rule.symbolizer["Point"] = config; } }, "Stroke": function(node, symbolizer) { symbolizer.stroke = true; this.readChildNodes(node, symbolizer); }, "Fill": function(node, symbolizer) { symbolizer.fill = true; this.readChildNodes(node, symbolizer); }, "CssParameter": function(node, symbolizer) { var cssProperty = node.getAttribute("name"); var symProperty = this.cssMap[cssProperty]; // for labels, fill should map to fontColor and fill-opacity // to fontOpacity if (symbolizer.label) { if (cssProperty === 'fill') { symProperty = "fontColor"; } else if (cssProperty === 'fill-opacity') { symProperty = "fontOpacity"; } } if(symProperty) { // Limited support for parsing of OGC expressions var value = this.readers.ogc._expression.call(this, node); // always string, could be an empty string if(value) { symbolizer[symProperty] = value; } } }, "Graphic": function(node, symbolizer) { symbolizer.graphic = true; var graphic = {}; // painter's order not respected here, clobber previous with next this.readChildNodes(node, graphic); // directly properties with names that match symbolizer properties var properties = [ "stroke", "strokeColor", "strokeWidth", "strokeOpacity", "strokeLinecap", "fill", "fillColor", "fillOpacity", "graphicName", "rotation", "graphicFormat" ]; var prop, value; for(var i=0, len=properties.length; i. * * Parameters: * sym - {String} A symbolizer property name. * * Returns: * {String} A CSS property name or null if none found. */ getCssProperty: function(sym) { var css = null; for(var prop in this.cssMap) { if(this.cssMap[prop] == sym) { css = prop; break; } } return css; }, /** * Method: getGraphicFormat * Given a href for an external graphic, try to determine the mime-type. * This method doesn't try too hard, and will fall back to * if one of the known is not * the file extension of the provided href. * * Parameters: * href - {String} * * Returns: * {String} The graphic format. */ getGraphicFormat: function(href) { var format, regex; for(var key in this.graphicFormats) { if(this.graphicFormats[key].test(href)) { format = key; break; } } return format || this.defaultGraphicFormat; }, /** * Property: defaultGraphicFormat * {String} If none other can be determined from , this * default will be returned. */ defaultGraphicFormat: "image/png", /** * Property: graphicFormats * {Object} Mapping of image mime-types to regular extensions matching * well-known file extensions. */ graphicFormats: { "image/jpeg": /\.jpe?g$/i, "image/gif": /\.gif$/i, "image/png": /\.png$/i }, /** * Method: write * * Parameters: * sld - {Object} An object representing the SLD. * * Returns: * {DOMElement} The root of an SLD document. */ write: function(sld) { return this.writers.sld.StyledLayerDescriptor.apply(this, [sld]); }, /** * Property: writers * As a compliment to the readers property, this structure contains public * writing functions grouped by namespace alias and named like the * node names they produce. */ writers: OpenLayers.Util.applyDefaults({ "sld": { "_OGCExpression": function(nodeName, value) { // only the simplest of ogc:expression handled // {label: "some text and a ${propertyName}"} var node = this.createElementNSPlus(nodeName); var tokens = typeof value == "string" ? value.split("${") : [value]; node.appendChild(this.createTextNode(tokens[0])); var item, last; for(var i=1, len=tokens.length; i 0) { this.writeNode( "ogc:PropertyName", {property: item.substring(0, last)}, node ); node.appendChild( this.createTextNode(item.substring(++last)) ); } else { // no ending }, so this is a literal ${ node.appendChild( this.createTextNode("${" + item) ); } } return node; }, "StyledLayerDescriptor": function(sld) { var root = this.createElementNSPlus( "sld:StyledLayerDescriptor", {attributes: { "version": this.VERSION, "xsi:schemaLocation": this.schemaLocation }} ); // For ArcGIS Server it is necessary to define this // at the root level (see ticket:2166). root.setAttribute("xmlns:ogc", this.namespaces.ogc); root.setAttribute("xmlns:gml", this.namespaces.gml); // add in optional name if(sld.name) { this.writeNode("Name", sld.name, root); } // add in optional title if(sld.title) { this.writeNode("Title", sld.title, root); } // add in optional description if(sld.description) { this.writeNode("Abstract", sld.description, root); } // add in named layers // allow namedLayers to be an array if(OpenLayers.Util.isArray(sld.namedLayers)) { for(var i=0, len=sld.namedLayers.length; i 0) { clone = style.clone(); clone.rules = rulesByZ[zValues[i]]; this.writeNode("FeatureTypeStyle", clone, node); } } } else { this.writeNode("FeatureTypeStyle", style, node); } return node; }, "IsDefault": function(bool) { return this.createElementNSPlus( "sld:IsDefault", {value: (bool) ? "1" : "0"} ); }, "FeatureTypeStyle": function(style) { var node = this.createElementNSPlus("sld:FeatureTypeStyle"); // OpenLayers currently stores no Name, Title, Abstract, // FeatureTypeName, or SemanticTypeIdentifier information // related to FeatureTypeStyle // add in rules for(var i=0, len=style.rules.length; i