From 76454516f5e9ad6eaa7a33d43fe83b7c622d5f12 Mon Sep 17 00:00:00 2001 From: Bart van den Eijnden Date: Tue, 24 Sep 2013 11:33:53 +0200 Subject: [PATCH 01/12] Add skeleton for SLD parser --- examples/data/countries.sld | 42 ++ examples/vector-layer-sld.html | 61 +++ examples/vector-layer-sld.js | 45 ++ src/ol/parser/ogc/sldparser.js | 38 ++ src/ol/parser/ogc/sldparser_v1.js | 429 ++++++++++++++++++ src/ol/parser/ogc/sldparser_v1_0_0.js | 18 + .../parser/ogc/sldparser_v1_0_0_GeoServer.js | 43 ++ test/spec/ol/parser/ogc/sld_v1_0_0.test.js | 78 ++++ test/spec/ol/parser/ogc/xml/sld_v1_0_0.xml | 129 ++++++ 9 files changed, 883 insertions(+) create mode 100644 examples/data/countries.sld create mode 100644 examples/vector-layer-sld.html create mode 100644 examples/vector-layer-sld.js create mode 100644 src/ol/parser/ogc/sldparser.js create mode 100644 src/ol/parser/ogc/sldparser_v1.js create mode 100644 src/ol/parser/ogc/sldparser_v1_0_0.js create mode 100644 src/ol/parser/ogc/sldparser_v1_0_0_GeoServer.js create mode 100644 test/spec/ol/parser/ogc/sld_v1_0_0.test.js create mode 100644 test/spec/ol/parser/ogc/xml/sld_v1_0_0.xml diff --git a/examples/data/countries.sld b/examples/data/countries.sld new file mode 100644 index 0000000000..79fb36d465 --- /dev/null +++ b/examples/data/countries.sld @@ -0,0 +1,42 @@ + + + + countries + + countries + A sample style for countries + 1 + A sample style for countries + + name + + Sample + Sample + + + #ff0000 + 0.6 + + + #00FF00 + 0.5 + 4 + + + + + + + name + + + Arial + 10 + Normal + + + + + + + diff --git a/examples/vector-layer-sld.html b/examples/vector-layer-sld.html new file mode 100644 index 0000000000..52b9b0e80d --- /dev/null +++ b/examples/vector-layer-sld.html @@ -0,0 +1,61 @@ + + + + + + + + + + + Vector layer with styling from SLD example + + + + + +
+ +
+
+
+
+
+ +
+ +
+

Vector layer example

+

Example of a countries vector layer with country information on hover and country labels at higher zoom levels.

+
+

See the vector-layer-sld.js source to see how this is done.

+
+
vector, geojson, style, SLD, Styled Layer Descriptor
+
+
+
+   +
+
+ +
+ +
+ + + + + + diff --git a/examples/vector-layer-sld.js b/examples/vector-layer-sld.js new file mode 100644 index 0000000000..aa8f9f5563 --- /dev/null +++ b/examples/vector-layer-sld.js @@ -0,0 +1,45 @@ +goog.require('ol.Map'); +goog.require('ol.RendererHint'); +goog.require('ol.View2D'); +goog.require('ol.layer.Tile'); +goog.require('ol.layer.Vector'); +goog.require('ol.parser.GeoJSON'); +goog.require('ol.parser.ogc.SLD'); +goog.require('ol.source.MapQuestOpenAerial'); +goog.require('ol.source.Vector'); + + +var raster = new ol.layer.Tile({ + source: new ol.source.MapQuestOpenAerial() +}); + +var xhr = new XMLHttpRequest(); +xhr.open('GET', 'data/countries.sld', true); + + +/** + * onload handler for the XHR request. + */ +xhr.onload = function() { + if (xhr.status == 200) { + var sld = new ol.parser.ogc.SLD().read(xhr.responseText); + var style = sld.namedLayers['countries'].userStyles[0]; + var vector = new ol.layer.Vector({ + source: new ol.source.Vector({ + parser: new ol.parser.GeoJSON(), + url: 'data/countries.geojson' + }), + style: style + }); + new ol.Map({ + layers: [raster, vector], + renderer: ol.RendererHint.CANVAS, + target: 'map', + view: new ol.View2D({ + center: [0, 0], + zoom: 1 + }) + }); + } +}; +xhr.send(); diff --git a/src/ol/parser/ogc/sldparser.js b/src/ol/parser/ogc/sldparser.js new file mode 100644 index 0000000000..8560cdc2e8 --- /dev/null +++ b/src/ol/parser/ogc/sldparser.js @@ -0,0 +1,38 @@ +goog.provide('ol.parser.ogc.SLD'); +goog.require('ol.parser.ogc.SLD_v1_0_0'); +goog.require('ol.parser.ogc.SLD_v1_0_0_GeoServer'); +goog.require('ol.parser.ogc.Versioned'); + + +/** + * @define {boolean} Whether to enable SLD version 1.0.0. + */ +ol.ENABLE_SLD_1_0_0 = true; + + +/** + * @define {boolean} Whether to enable SLD version 1.0.0. + * GeoServer profile. + */ +ol.ENABLE_SLD_1_0_0_GEOSERVER = true; + + + +/** + * @constructor + * @param {Object=} opt_options Options which will be set on this object. + * @extends {ol.parser.ogc.Versioned} + */ +ol.parser.ogc.SLD = function(opt_options) { + opt_options = opt_options || {}; + opt_options['defaultVersion'] = '1.0.0'; + this.parsers = {}; + if (ol.ENABLE_SLD_1_0_0) { + this.parsers['v1_0_0'] = ol.parser.ogc.SLD_v1_0_0; + } + if (ol.ENABLE_SLD_1_0_0_GEOSERVER) { + this.parsers['v1_0_0_GEOSERVER'] = ol.parser.ogc.SLD_v1_0_0_GeoServer; + } + goog.base(this, opt_options); +}; +goog.inherits(ol.parser.ogc.SLD, ol.parser.ogc.Versioned); diff --git a/src/ol/parser/ogc/sldparser_v1.js b/src/ol/parser/ogc/sldparser_v1.js new file mode 100644 index 0000000000..6917240dac --- /dev/null +++ b/src/ol/parser/ogc/sldparser_v1.js @@ -0,0 +1,429 @@ +goog.provide('ol.parser.ogc.SLD_v1'); +goog.require('goog.dom.xml'); +goog.require('goog.object'); +goog.require('ol.parser.XML'); +goog.require('ol.parser.ogc.Filter_v1_0_0'); +goog.require('ol.style.Fill'); +goog.require('ol.style.Rule'); +goog.require('ol.style.Shape'); +goog.require('ol.style.Stroke'); +goog.require('ol.style.Style'); +goog.require('ol.style.Text'); + + + +/** + * Read Styled Layer Descriptor (SLD). + * + * @constructor + * @extends {ol.parser.XML} + */ +ol.parser.ogc.SLD_v1 = function() { + this.defaultNamespaceURI = 'http://www.opengis.net/sld'; + this.readers = { + 'http://www.opengis.net/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); + sld.namedLayers[layer.name] = layer; + }, + 'NamedStyle': function(node, layer) { + layer.namedStyles.push( + this.getChildValue(node.firstChild) + ); + }, + 'UserStyle': function(node, layer) { + var obj = {rules: []}; + this.featureTypeCounter = -1; + this.readChildNodes(node, obj); + layer.userStyles.push(new ol.style.Style(obj)); + }, + 'IsDefault': function(node, style) { + if (this.getChildValue(node) === '1') { + style.isDefault = true; + } + }, + 'FeatureTypeStyle': function(node, style) { + ++this.featureTypeCounter; + var obj = { + rules: style.rules + }; + this.readChildNodes(node, obj); + }, + 'Rule': function(node, obj) { + var config = {symbolizers: []}; + this.readChildNodes(node, config); + var rule = new ol.style.Rule(config); + obj.rules.push(rule); + }, + 'ElseFilter': function(node, rule) { + rule.elseFilter = true; + }, + 'MinScaleDenominator': function(node, rule) { + rule.minScaleDenominator = parseFloat(this.getChildValue(node)); + }, + 'MaxScaleDenominator': function(node, rule) { + rule.maxScaleDenominator = parseFloat(this.getChildValue(node)); + }, + 'TextSymbolizer': function(node, rule) { + var config = {}; + this.readChildNodes(node, config); + config.zIndex = this.featureTypeCounter; + rule.symbolizers.push( + new ol.style.Text(/** @type {ol.style.TextOptions} */(config)) + ); + }, + 'LabelPlacement': function(node, symbolizer) { + this.readChildNodes(node, symbolizer); + }, + 'PointPlacement': function(node, symbolizer) { + var config = {}; + this.readChildNodes(node, config); + config.labelRotation = config.rotation; + delete config.rotation; + var labelAlign, + x = symbolizer.labelAnchorPointX, + y = symbolizer.labelAnchorPointY; + if (x <= 1 / 3) { + labelAlign = 'l'; + } else if (x > 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; + goog.object.extend(symbolizer, config); + }, + 'AnchorPoint': function(node, symbolizer) { + this.readChildNodes(node, symbolizer); + }, + 'AnchorPointX': function(node, symbolizer) { + var ogcreaders = this.readers['http://www.opengis.net/ogc']; + var labelAnchorPointX = ogcreaders._expression.call(this, node); + // always string, could be empty string + if (labelAnchorPointX) { + symbolizer.labelAnchorPointX = labelAnchorPointX; + } + }, + 'AnchorPointY': function(node, symbolizer) { + var ogcreaders = this.readers['http://www.opengis.net/ogc']; + var labelAnchorPointY = ogcreaders._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 ogcreaders = this.readers['http://www.opengis.net/ogc']; + var labelXOffset = ogcreaders._expression.call(this, node); + // always string, could be empty string + if (labelXOffset) { + symbolizer.labelXOffset = labelXOffset; + } + }, + 'DisplacementY': function(node, symbolizer) { + var ogcreaders = this.readers['http://www.opengis.net/ogc']; + var labelYOffset = ogcreaders._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 ogcreaders = this.readers['http://www.opengis.net/ogc']; + var labelPerpendicularOffset = ogcreaders._expression.call(this, node); + // always string, could be empty string + if (labelPerpendicularOffset) { + symbolizer.labelPerpendicularOffset = labelPerpendicularOffset; + } + }, + 'Label': function(node, symbolizer) { + var ogcreaders = this.readers['http://www.opengis.net/ogc']; + var value = ogcreaders._expression.call(this, node); + if (value) { + symbolizer.text = 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 ogcreaders = this.readers['http://www.opengis.net/ogc']; + var radius = ogcreaders._expression.call(this, node); + if (goog.isDef(radius)) { + } + }, + 'RasterSymbolizer': function(node, rule) { + var config = {}; + this.readChildNodes(node, config); + config.zIndex = this.featureTypeCounter; + /* TODO + rule.symbolizers.push( + new OpenLayers.Symbolizer.Raster(config) + ); + */ + }, + '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); + config.zIndex = this.featureTypeCounter; + rule.symbolizers.push( + new ol.style.Stroke(config) + ); + }, + 'PolygonSymbolizer': function(node, rule) { + var config = { + fill: false, + stroke: false + }; + this.readChildNodes(node, config); + config.zIndex = this.featureTypeCounter; + if (config.fill === true) { + var fill = { + color: config['fillColor'], + opacity: config['fillOpacity'] + }; + rule.symbolizers.push( + new ol.style.Fill(fill) + ); + } + if (config.stroke === true) { + var stroke = { + color: config['strokeColor'], + opacity: config['strokeOpacity'], + width: config['strokeWidth'] + }; + rule.symbolizers.push( + new ol.style.Stroke(stroke) + ); + } + + }, + 'PointSymbolizer': function(node, rule) { + var config = { + fill: null, + stroke: null, + graphic: null + }; + this.readChildNodes(node, config); + config.zIndex = this.featureTypeCounter; + // TODO shape or icon? + rule.symbolizers.push( + new ol.style.Shape(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 = ol.parser.ogc.SLD_v1.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 ogcreaders = this.readers['http://www.opengis.net/ogc']; + var value = ogcreaders._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, ii = properties.length; i < ii; ++i) { + prop = properties[i]; + value = graphic[prop]; + if (goog.isDef(value)) { + symbolizer[prop] = value; + } + } + // set other generic properties with specific graphic property names + if (goog.isDef(graphic.opacity)) { + symbolizer.graphicOpacity = graphic.opacity; + } + if (goog.isDef(graphic.size)) { + var pointRadius = graphic.size / 2; + if (isNaN(pointRadius)) { + // likely a property name + symbolizer.graphicWidth = graphic.size; + } else { + symbolizer.pointRadius = graphic.size / 2; + } + } + if (goog.isDef(graphic.href)) { + symbolizer.externalGraphic = graphic.href; + } + if (goog.isDef(graphic.rotation)) { + symbolizer.rotation = graphic.rotation; + } + }, + 'ExternalGraphic': function(node, graphic) { + this.readChildNodes(node, graphic); + }, + 'Mark': function(node, graphic) { + this.readChildNodes(node, graphic); + }, + 'WellKnownName': function(node, graphic) { + graphic.graphicName = this.getChildValue(node); + }, + 'Opacity': function(node, obj) { + var ogcreaders = this.readers['http://www.opengis.net/ogc']; + var opacity = ogcreaders._expression.call(this, node); + // always string, could be empty string + if (opacity) { + obj.opacity = opacity; + } + }, + 'Size': function(node, obj) { + var ogcreaders = this.readers['http://www.opengis.net/ogc']; + var size = ogcreaders._expression.call(this, node); + // always string, could be empty string + if (size) { + obj.size = size; + } + }, + 'Rotation': function(node, obj) { + var ogcreaders = this.readers['http://www.opengis.net/ogc']; + var rotation = ogcreaders._expression.call(this, node); + // always string, could be empty string + if (rotation) { + obj.rotation = rotation; + } + }, + 'OnlineResource': function(node, obj) { + obj.href = this.getAttributeNS( + node, 'http://www.w3.org/1999/xlink', 'href' + ); + }, + 'Format': function(node, graphic) { + graphic.graphicFormat = this.getChildValue(node); + } + } + }; + this.filter_ = new ol.parser.ogc.Filter_v1_0_0(); + for (var uri in this.filter_.readers) { + for (var key in this.filter_.readers[uri]) { + if (!goog.isDef(this.readers[uri])) { + this.readers[uri] = {}; + } + this.readers[uri][key] = goog.bind(this.filter_.readers[uri][key], + this.filter_); + } + } + goog.base(this); +}; +goog.inherits(ol.parser.ogc.SLD_v1, ol.parser.XML); + + +/** + * @private + */ +ol.parser.ogc.SLD_v1.cssMap_ = { + 'stroke': 'strokeColor', + 'stroke-opacity': 'strokeOpacity', + 'stroke-width': 'strokeWidth', + 'stroke-linecap': 'strokeLinecap', + 'stroke-dasharray': 'strokeDashstyle', + 'fill': 'fillColor', + 'fill-opacity': 'fillOpacity', + 'font-family': 'fontFamily', + 'font-size': 'fontSize', + 'font-weight': 'fontWeight', + 'font-style': 'fontStyle' +}; + + +/** + * @param {string|Document|Element} data Data to read. + * @return {Object} An object representing the document. + */ +ol.parser.ogc.SLD_v1.prototype.read = function(data) { + if (goog.isString(data)) { + data = goog.dom.xml.loadXml(data); + } + if (data && data.nodeType == 9) { + data = data.documentElement; + } + var obj = {namedLayers: {}}; + this.readNode(data, obj); + return obj; +}; diff --git a/src/ol/parser/ogc/sldparser_v1_0_0.js b/src/ol/parser/ogc/sldparser_v1_0_0.js new file mode 100644 index 0000000000..d86f897f6a --- /dev/null +++ b/src/ol/parser/ogc/sldparser_v1_0_0.js @@ -0,0 +1,18 @@ +goog.provide('ol.parser.ogc.SLD_v1_0_0'); + +goog.require('ol.parser.ogc.SLD_v1'); + + + +/** + * @constructor + * @extends {ol.parser.ogc.SLD_v1} + */ +ol.parser.ogc.SLD_v1_0_0 = function() { + goog.base(this); + this.version = '1.0.0'; + this.schemaLocation = 'http://www.opengis.net/sld ' + + 'http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd'; +}; +goog.inherits(ol.parser.ogc.SLD_v1_0_0, + ol.parser.ogc.SLD_v1); diff --git a/src/ol/parser/ogc/sldparser_v1_0_0_GeoServer.js b/src/ol/parser/ogc/sldparser_v1_0_0_GeoServer.js new file mode 100644 index 0000000000..77f58d1ab5 --- /dev/null +++ b/src/ol/parser/ogc/sldparser_v1_0_0_GeoServer.js @@ -0,0 +1,43 @@ +goog.provide('ol.parser.ogc.SLD_v1_0_0_GeoServer'); + +goog.require('goog.functions'); +goog.require('goog.object'); +goog.require('ol.parser.ogc.SLD_v1_0_0'); + + + +/** + * @constructor + * @extends {ol.parser.ogc.SLD_v1_0_0} + */ +ol.parser.ogc.SLD_v1_0_0_GeoServer = function() { + goog.base(this); + this.profile = 'GeoServer'; + goog.object.extend(this.readers['http://www.opengis.net/sld'], { + 'Priority': function(node, obj) { + var ogcreaders = this.readers['http://www.opengis.net/ogc']; + var value = ogcreaders._expression.call(this, node); + if (value) { + obj.priority = value; + } + }, + 'VendorOption': function(node, obj) { + if (!goog.isDef(obj.vendorOptions)) { + obj.vendorOptions = {}; + } + obj.vendorOptions[node.getAttribute('name')] = + this.getChildValue(node); + }, + 'TextSymbolizer': goog.functions.sequence( + this.readers['http://www.opengis.net/sld']['TextSymbolizer'], + function(node, rule) { + var symbolizer = rule.symbolizers[rule.symbolizers.length - 1]; + if (!goog.isDef(symbolizer.graphic)) { + symbolizer.graphic = false; + } + } + ) + }); +}; +goog.inherits(ol.parser.ogc.SLD_v1_0_0_GeoServer, + ol.parser.ogc.SLD_v1_0_0); diff --git a/test/spec/ol/parser/ogc/sld_v1_0_0.test.js b/test/spec/ol/parser/ogc/sld_v1_0_0.test.js new file mode 100644 index 0000000000..057d322062 --- /dev/null +++ b/test/spec/ol/parser/ogc/sld_v1_0_0.test.js @@ -0,0 +1,78 @@ +goog.provide('ol.test.parser.ogc.SLD_v1_0_0'); + + +describe('ol.parser.ogc.SLD_v1_0_0', function() { + + var parser = new ol.parser.ogc.SLD(); + + describe('reading and writing', function() { + it('Handles reading', function(done) { + var url = 'spec/ol/parser/ogc/xml/sld_v1_0_0.xml'; + afterLoadXml(url, function(xml) { + var obj = parser.read(xml); + expect(obj.version).to.equal('1.0.0'); + var style = obj.namedLayers['AAA161'].userStyles[0]; + expect(style).to.be.a(ol.style.Style); + expect(style.rules_.length).to.equal(2); + var first = style.rules_[0]; + expect(first).to.be.a(ol.style.Rule); + expect(first.filter_).to.be.a(ol.expr.Comparison); + expect(first.filter_.getLeft()).to.be.a(ol.expr.Identifier); + expect(first.filter_.getLeft().getName()).to.equal('CTE'); + expect(first.filter_.getOperator()).to.equal(ol.expr.ComparisonOp.EQ); + expect(first.filter_.getRight()).to.be.a(ol.expr.Literal); + expect(first.filter_.getRight().getValue()).to.equal('V0305'); + expect(first.getSymbolizers().length).to.equal(3); + expect(first.getSymbolizers()[0]).to.be.a(ol.style.Fill); + expect(first.getSymbolizers()[0].getColor().getValue()).to.equal( + '#ffffff'); + expect(first.getSymbolizers()[0].getOpacity().getValue()).to.equal(0.4); + expect(first.getSymbolizers()[1]).to.be.a(ol.style.Stroke); + expect(first.getSymbolizers()[1].getColor().getValue()).to.equal( + '#000000'); + expect(first.getSymbolizers()[2]).to.be.a(ol.style.Text); + expect(first.getSymbolizers()[2].getText()).to.be.a(ol.expr.Call); + expect(first.getSymbolizers()[2].getText().getArgs().length).to.equal( + 3); + expect(first.getSymbolizers()[2].getText().getArgs()[0]).to.be.a( + ol.expr.Literal); + expect(first.getSymbolizers()[2].getText().getArgs()[0].getValue()). + to.equal('A'); + expect(first.getSymbolizers()[2].getText().getArgs()[1]).to.be.a( + ol.expr.Identifier); + expect(first.getSymbolizers()[2].getText().getArgs()[1].getName()). + to.equal('FOO'); + expect(first.getSymbolizers()[2].getText().getArgs()[2]).to.be.a( + ol.expr.Literal); + expect(first.getSymbolizers()[2].getText().getArgs()[2].getValue()). + to.equal('label'); + expect(first.getSymbolizers()[2].getColor().getValue()).to.equal( + ol.style.TextDefaults.color); + expect(first.getSymbolizers()[2].getFontFamily().getValue()).to.equal( + 'Arial'); + // TODO add tests for haloRadius and haloColor + var second = style.rules_[1]; + expect(second.filter_).to.be.a(ol.expr.Comparison); + expect(second.getSymbolizers().length).to.equal(2); + expect(second.getSymbolizers()[0]).to.be.a(ol.style.Fill); + expect(second.getSymbolizers()[1]).to.be.a(ol.style.Stroke); + done(); + }); + }); + }); + +}); + +goog.require('goog.net.XhrIo'); +goog.require('ol.parser.ogc.SLD_v1_0_0'); +goog.require('ol.parser.ogc.SLD'); +goog.require('ol.expr.Call'); +goog.require('ol.expr.Comparison'); +goog.require('ol.expr.ComparisonOp'); +goog.require('ol.expr.Identifier'); +goog.require('ol.expr.Literal'); +goog.require('ol.style.Fill'); +goog.require('ol.style.Rule'); +goog.require('ol.style.Stroke'); +goog.require('ol.style.Style'); +goog.require('ol.style.Text'); diff --git a/test/spec/ol/parser/ogc/xml/sld_v1_0_0.xml b/test/spec/ol/parser/ogc/xml/sld_v1_0_0.xml new file mode 100644 index 0000000000..6bb9cc654f --- /dev/null +++ b/test/spec/ol/parser/ogc/xml/sld_v1_0_0.xml @@ -0,0 +1,129 @@ + + + + AAA161 + + + + stortsteen + + + CTE + V0305 + + + 50000 + + + #ffffff + + + #000000 + + + + + + Arial + 14 + bold + normal + + + + + 0.5 + 0.5 + + + 5 + 5 + + 45 + + + + 3 + + #ffffff + + + + #000000 + + + + + betonbekleding + + + CTE + 1000 + + + 50000 + + + #ffff00 + + + #0000ff + + + + + + + + Second Layer + + + + first rule second layer + + + + number + + 1064866676 + + + 1065512599 + + + + cat + *dog.food!*good + + + + FOO + 5000 + + + + + 10000 + + + + star + + lime + + + olive + 2 + + + SIZE + + + + + + + From 5abedf66d937aea17966984e92d665aeb1d9eebe Mon Sep 17 00:00:00 2001 From: Bart van den Eijnden Date: Fri, 27 Sep 2013 15:06:36 +0200 Subject: [PATCH 02/12] Add scale to/from resolution calculations --- examples/data/countries.sld | 1 + examples/vector-layer-sld.html | 7 +------ examples/vector-layer-sld.js | 27 +++++++++++++++++---------- src/objectliterals.jsdoc | 6 ++++++ src/ol/parser/ogc/sldparser_v1.js | 30 +++++++++++++++++++++++++++--- 5 files changed, 52 insertions(+), 19 deletions(-) diff --git a/examples/data/countries.sld b/examples/data/countries.sld index 79fb36d465..9ae5eebea3 100644 --- a/examples/data/countries.sld +++ b/examples/data/countries.sld @@ -25,6 +25,7 @@ + 20000000 name diff --git a/examples/vector-layer-sld.html b/examples/vector-layer-sld.html index 52b9b0e80d..eda42d859b 100644 --- a/examples/vector-layer-sld.html +++ b/examples/vector-layer-sld.html @@ -44,12 +44,7 @@
vector, geojson, style, SLD, Styled Layer Descriptor
-
-
-   -
-
- + diff --git a/examples/vector-layer-sld.js b/examples/vector-layer-sld.js index aa8f9f5563..585cc66c09 100644 --- a/examples/vector-layer-sld.js +++ b/examples/vector-layer-sld.js @@ -1,6 +1,8 @@ goog.require('ol.Map'); goog.require('ol.RendererHint'); goog.require('ol.View2D'); +goog.require('ol.control'); +goog.require('ol.control.ScaleLine'); goog.require('ol.layer.Tile'); goog.require('ol.layer.Vector'); goog.require('ol.parser.GeoJSON'); @@ -22,7 +24,20 @@ xhr.open('GET', 'data/countries.sld', true); */ xhr.onload = function() { if (xhr.status == 200) { - var sld = new ol.parser.ogc.SLD().read(xhr.responseText); + var map = new ol.Map({ + controls: ol.control.defaults().extend([ + new ol.control.ScaleLine() + ]), + layers: [raster], + renderer: ol.RendererHint.CANVAS, + target: 'map', + view: new ol.View2D({ + center: [0, 0], + zoom: 1 + }) + }); + var units = map.getView().getProjection().getUnits(); + var sld = new ol.parser.ogc.SLD().read(xhr.responseText, units); var style = sld.namedLayers['countries'].userStyles[0]; var vector = new ol.layer.Vector({ source: new ol.source.Vector({ @@ -31,15 +46,7 @@ xhr.onload = function() { }), style: style }); - new ol.Map({ - layers: [raster, vector], - renderer: ol.RendererHint.CANVAS, - target: 'map', - view: new ol.View2D({ - center: [0, 0], - zoom: 1 - }) - }); + map.getLayers().push(vector); } }; xhr.send(); diff --git a/src/objectliterals.jsdoc b/src/objectliterals.jsdoc index 78a0e7cbeb..37424a6108 100644 --- a/src/objectliterals.jsdoc +++ b/src/objectliterals.jsdoc @@ -567,6 +567,12 @@ * @todo stability experimental */ + /** + * @typedef {Object} ol.parser.SLDReadOptions + * @property {ol.proj.Units} units The units to use in scale to resolution + * calculations. + */ + /** * @typedef {Object} ol.source.BingMapsOptions * @property {string|undefined} culture Culture. diff --git a/src/ol/parser/ogc/sldparser_v1.js b/src/ol/parser/ogc/sldparser_v1.js index 6917240dac..5708a207f0 100644 --- a/src/ol/parser/ogc/sldparser_v1.js +++ b/src/ol/parser/ogc/sldparser_v1.js @@ -76,10 +76,12 @@ ol.parser.ogc.SLD_v1 = function() { rule.elseFilter = true; }, 'MinScaleDenominator': function(node, rule) { - rule.minScaleDenominator = parseFloat(this.getChildValue(node)); + rule.minResolution = this.getResolutionFromScale_( + parseFloat(this.getChildValue(node))); }, 'MaxScaleDenominator': function(node, rule) { - rule.maxScaleDenominator = parseFloat(this.getChildValue(node)); + rule.maxResolution = this.getResolutionFromScale_( + parseFloat(this.getChildValue(node))); }, 'TextSymbolizer': function(node, rule) { var config = {}; @@ -188,6 +190,7 @@ ol.parser.ogc.SLD_v1 = function() { var ogcreaders = this.readers['http://www.opengis.net/ogc']; var radius = ogcreaders._expression.call(this, node); if (goog.isDef(radius)) { + symbolizer.haloRadius = radius; } }, 'RasterSymbolizer': function(node, rule) { @@ -412,11 +415,31 @@ ol.parser.ogc.SLD_v1.cssMap_ = { }; +/** + * @private + * @param {number} scaleDenominator The scale denominator to convert to + * resolution. + * @return {number} resolution. + */ +ol.parser.ogc.SLD_v1.prototype.getResolutionFromScale_ = + function(scaleDenominator) { + var dpi = 25.4 / 0.28; + var mpu = ol.METERS_PER_UNIT[this.units]; + return 1 / ((1 / scaleDenominator) * (mpu * 39.37) * dpi); +}; + + /** * @param {string|Document|Element} data Data to read. + * @param {ol.parser.SLDReadOptions=} opt_options Read options. * @return {Object} An object representing the document. */ -ol.parser.ogc.SLD_v1.prototype.read = function(data) { +ol.parser.ogc.SLD_v1.prototype.read = function(data, opt_options) { + var units = 'm'; + if (goog.isDef(opt_options) && goog.isDef(opt_options.units)) { + units = opt_options.units; + } + this.units = units; if (goog.isString(data)) { data = goog.dom.xml.loadXml(data); } @@ -425,5 +448,6 @@ ol.parser.ogc.SLD_v1.prototype.read = function(data) { } var obj = {namedLayers: {}}; this.readNode(data, obj); + delete this.units; return obj; }; From ee7d46cea1b2ffbf80c3e4bd68d798360010172f Mon Sep 17 00:00:00 2001 From: Bart van den Eijnden Date: Tue, 8 Oct 2013 19:01:42 +0200 Subject: [PATCH 03/12] Add initial pass of write support, always write out ol.style.Stroke as LineSymbolizer --- examples/vector-layer-sld.html | 2 +- src/objectliterals.jsdoc | 6 + src/ol/parser/ogc/sldparser_v1.js | 457 +++++++++++++++++++-- src/ol/style/rule.js | 24 ++ src/ol/style/style.js | 16 + src/ol/style/textsymbolizer.js | 9 + test/spec/ol/parser/ogc/sld_v1_0_0.test.js | 1 + 7 files changed, 491 insertions(+), 24 deletions(-) diff --git a/examples/vector-layer-sld.html b/examples/vector-layer-sld.html index eda42d859b..62900c20ac 100644 --- a/examples/vector-layer-sld.html +++ b/examples/vector-layer-sld.html @@ -38,7 +38,7 @@

Vector layer example

-

Example of a countries vector layer with country information on hover and country labels at higher zoom levels.

+

Example of a countries vector layer with country labels at higher zoom levels, styling info coming from SLD.

See the vector-layer-sld.js source to see how this is done.

diff --git a/src/objectliterals.jsdoc b/src/objectliterals.jsdoc index 37424a6108..545573f100 100644 --- a/src/objectliterals.jsdoc +++ b/src/objectliterals.jsdoc @@ -573,6 +573,12 @@ * calculations. */ + /** + * @typedef {Object} ol.parser.SLDWriteOptions + * @property {ol.proj.Units} units The units to use in resolution to scale + * calculations. + */ + /** * @typedef {Object} ol.source.BingMapsOptions * @property {string|undefined} culture Culture. diff --git a/src/ol/parser/ogc/sldparser_v1.js b/src/ol/parser/ogc/sldparser_v1.js index 5708a207f0..93bf09eb44 100644 --- a/src/ol/parser/ogc/sldparser_v1.js +++ b/src/ol/parser/ogc/sldparser_v1.js @@ -1,9 +1,12 @@ goog.provide('ol.parser.ogc.SLD_v1'); +goog.require('goog.asserts'); goog.require('goog.dom.xml'); goog.require('goog.object'); +goog.require('ol.expr.Literal'); goog.require('ol.parser.XML'); goog.require('ol.parser.ogc.Filter_v1_0_0'); goog.require('ol.style.Fill'); +goog.require('ol.style.Icon'); goog.require('ol.style.Rule'); goog.require('ol.style.Shape'); goog.require('ol.style.Stroke'); @@ -76,11 +79,11 @@ ol.parser.ogc.SLD_v1 = function() { rule.elseFilter = true; }, 'MinScaleDenominator': function(node, rule) { - rule.minResolution = this.getResolutionFromScale_( + rule.minResolution = this.getResolutionFromScaleDenominator_( parseFloat(this.getChildValue(node))); }, 'MaxScaleDenominator': function(node, rule) { - rule.maxResolution = this.getResolutionFromScale_( + rule.maxResolution = this.getResolutionFromScaleDenominator_( parseFloat(this.getChildValue(node))); }, 'TextSymbolizer': function(node, rule) { @@ -230,53 +233,56 @@ ol.parser.ogc.SLD_v1 = function() { ); }, 'PolygonSymbolizer': function(node, rule) { - var config = { - fill: false, - stroke: false - }; + var config = {}; this.readChildNodes(node, config); config.zIndex = this.featureTypeCounter; - if (config.fill === true) { + if (goog.isDef(config.fill)) { var fill = { - color: config['fillColor'], - opacity: config['fillOpacity'] + color: config.fill.fillColor, + opacity: config.fill.fillOpacity }; rule.symbolizers.push( new ol.style.Fill(fill) ); + delete config.fill; } - if (config.stroke === true) { + if (goog.isDef(config.stroke)) { var stroke = { - color: config['strokeColor'], - opacity: config['strokeOpacity'], - width: config['strokeWidth'] + color: config.stroke.strokeColor, + opacity: config.stroke.strokeOpacity, + width: config.stroke.strokeWidth }; rule.symbolizers.push( new ol.style.Stroke(stroke) ); + delete config.stroke; } }, 'PointSymbolizer': function(node, rule) { - var config = { - fill: null, - stroke: null, - graphic: null - }; + var config = {}; this.readChildNodes(node, config); config.zIndex = this.featureTypeCounter; + if (config.fill) { + config.fill = new ol.style.Fill(config.fill); + } + if (config.stroke) { + config.stroke = new ol.style.Stroke(config.stroke); + } // TODO shape or icon? rule.symbolizers.push( new ol.style.Shape(config) ); }, 'Stroke': function(node, symbolizer) { - symbolizer.stroke = true; - this.readChildNodes(node, symbolizer); + var stroke = {}; + this.readChildNodes(node, stroke); + symbolizer.stroke = stroke; }, 'Fill': function(node, symbolizer) { - symbolizer.fill = true; - this.readChildNodes(node, symbolizer); + var fill = {}; + this.readChildNodes(node, fill); + symbolizer.fill = fill; }, 'CssParameter': function(node, symbolizer) { var cssProperty = node.getAttribute('name'); @@ -382,6 +388,353 @@ ol.parser.ogc.SLD_v1 = function() { } } }; + this.writers = { + 'http://www.opengis.net/sld': { + 'StyledLayerDescriptor': function(sld) { + var node = this.createElementNS('sld:StyledLayerDescriptor'); + node.setAttribute('version', this.version); + if (goog.isDef(sld.name)) { + this.writeNode('Name', sld.name, null, node); + } + if (goog.isDef(sld.title)) { + this.writeNode('Title', sld.title, null, node); + } + if (goog.isDef(sld.description)) { + this.writeNode('Abstract', sld.description, null, node); + } + goog.object.forEach(sld.namedLayers, function(layer) { + this.writeNode('NamedLayer', layer, null, node); + }, this); + return node; + }, + 'Name': function(name) { + var node = this.createElementNS('sld:Name'); + node.appendChild(this.createTextNode(name)); + return node; + }, + 'Title': function(title) { + var node = this.createElementNS('sld:Title'); + node.appendChild(this.createTextNode(title)); + return node; + }, + 'Abstract': function(description) { + var node = this.createElementNS('sld:Abstract'); + node.appendChild(this.createTextNode(description)); + return node; + }, + 'NamedLayer': function(layer) { + var node = this.createElementNS('sld:NamedLayer'); + this.writeNode('Name', layer.name, null, node); + var i, ii; + if (layer.namedStyles) { + for (i = 0, ii = layer.namedStyles.length; i < ii; ++i) { + this.writeNode('NamedStyle', layer.namedStyles[i], null, node); + } + } + if (layer.userStyles) { + for (i = 0, ii = layer.userStyles.length; i < ii; ++i) { + this.writeNode('UserStyle', layer.userStyles[i], null, node); + } + } + return node; + }, + 'NamedStyle': function(name) { + var node = this.createElementNS('sld:NamedStyle'); + this.writeNode('Name', name, null, node); + return node; + }, + 'UserStyle': function(style) { + var node = this.createElementNS('sld:UserStyle'); + if (style.name) { + this.writeNode('Name', style.name, null, node); + } + if (style.title) { + this.writeNode('Title', style.title, null, node); + } + if (style.description) { + this.writeNode('Abstract', style.description, null, node); + } + if (style.isDefault) { + this.writeNode('IsDefault', style.isDefault, null, node); + } + if (style.rules) { + // group style objects by symbolizer zIndex + var rulesByZ = { + 0: [] + }; + var zValues = [0]; + var rule, ruleMap, symbolizer, zIndex, clone; + for (var i = 0, ii = style.rules.length; i < ii; ++i) { + rule = style.rules[i]; + var symbolizers = rule.getSymbolizers(); + if (symbolizers) { + ruleMap = {}; + for (var j = 0, jj = symbolizers.length; j < jj; ++j) { + symbolizer = symbolizers[j]; + zIndex = symbolizer.zIndex; + if (!(zIndex in ruleMap)) { + // TODO check if clone works? + clone = goog.object.clone(rule); + clone.setSymbolizers([]); + ruleMap[zIndex] = clone; + } + // TODO check if clone works + ruleMap[zIndex].getSymbolizers().push( + goog.object.clone(symbolizer)); + } + for (zIndex in ruleMap) { + if (!(zIndex in rulesByZ)) { + zValues.push(zIndex); + rulesByZ[zIndex] = []; + } + rulesByZ[zIndex].push(ruleMap[zIndex]); + } + } else { + // no symbolizers in rule + rulesByZ[0].push(goog.object.clone(rule)); + } + } + // write one FeatureTypeStyle per zIndex + zValues.sort(); + var rules; + for (var i = 0, ii = zValues.length; i < ii; ++i) { + rules = rulesByZ[zValues[i]]; + if (rules.length > 0) { + clone = goog.object.clone(style); + clone.setRules(rulesByZ[zValues[i]]); + this.writeNode('FeatureTypeStyle', clone, null, node); + } + } + } else { + this.writeNode('FeatureTypeStyle', style, null, node); + } + return node; + }, + 'IsDefault': function(bool) { + var node = this.createElementNS('sld:IsDefault'); + node.appendChild(this.createTextNode((bool) ? '1' : '0')); + return node; + }, + 'FeatureTypeStyle': function(style) { + var node = this.createElementNS('sld:FeatureTypeStyle'); + // OpenLayers currently stores no Name, Title, Abstract, + // FeatureTypeName, or SemanticTypeIdentifier information + // related to FeatureTypeStyle + // add in rules + var rules = style.getRules(); + for (var i = 0, ii = rules.length; i < ii; ++i) { + this.writeNode('Rule', rules[i], null, node); + } + return node; + }, + 'Rule': function(rule) { + var node = this.createElementNS('sld:Rule'); + var filter = rule.getFilter(); + if (rule.name) { + this.writeNode('Name', rule.name, null, node); + } + if (rule.title) { + this.writeNode('Title', rule.title, null, node); + } + if (rule.description) { + this.writeNode('Abstract', rule.description, null, node); + } + if (rule.elseFilter) { + this.writeNode('ElseFilter', null, null, node); + } else if (filter) { + this.writeNode('Filter', filter, 'http://www.opengis.net/ogc', node); + } + var minResolution = rule.getMinResolution(); + if (minResolution > 0) { + this.writeNode('MinScaleDenominator', + this.getScaleDenominatorFromResolution_(minResolution), + null, node); + } + var maxResolution = rule.getMaxResolution(); + if (maxResolution < Infinity) { + this.writeNode('MaxScaleDenominator', + this.getScaleDenominatorFromResolution_(maxResolution), + null, node); + } + var type, symbolizer, symbolizers = rule.getSymbolizers(); + if (symbolizers) { + for (var i = 0, ii = symbolizers.length; i < ii; ++i) { + symbolizer = symbolizers[i]; + // TODO other types of symbolizers + if (symbolizer instanceof ol.style.Text) { + type = 'Text'; + } else if (symbolizer instanceof ol.style.Stroke) { + type = 'Line'; + } else if (symbolizer instanceof ol.style.Fill) { + type = 'Polygon'; + } else if (symbolizer instanceof ol.style.Shape || + symbolizer instanceof ol.style.Icon) { + type = 'Point'; + } + if (goog.isDef(type)) { + this.writeNode(type + 'Symbolizer', symbolizer, null, node); + } + } + } + return node; + }, + 'PointSymbolizer': function(symbolizer) { + var node = this.createElementNS('sld:PointSymbolizer'); + this.writeNode('Graphic', symbolizer, null, node); + return node; + }, + 'Mark': function(symbolizer) { + var node = this.createElementNS('sld:Mark'); + this.writeNode('WellKnownName', symbolizer.getType(), null, node); + var fill = symbolizer.getFill(); + if (!goog.isNull(fill)) { + this.writeNode('Fill', fill, null, node); + } + var stroke = symbolizer.getStroke(); + if (!goog.isNull(stroke)) { + this.writeNode('Stroke', stroke, null, node); + } + return node; + }, + 'WellKnownName': function(name) { + var node = this.createElementNS('sld:WellKnownName'); + node.appendChild(this.createTextNode(name)); + return node; + }, + 'Graphic': function(symbolizer) { + var node = this.createElementNS('sld:Graphic'); + var size; + if (symbolizer instanceof ol.style.Icon) { + this.writeNode('ExternalGraphic', symbolizer, null, node); + var opacity = symbolizer.getOpacity(); + goog.asserts.assert(opacity instanceof ol.expr.Literal, + 'Only ol.expr.Literal supported for graphicOpacity'); + this.writeNode('Opacity', opacity.getValue(), null, node); + size = symbolizer.getWidth(); + } else if (symbolizer instanceof ol.style.Shape) { + this.writeNode('Mark', symbolizer, null, node); + size = symbolizer.getSize(); + } + goog.asserts.assert(size instanceof ol.expr.Literal, + 'Only ol.expr.Literal supported for in Size'); + this.writeNode('Size', size.getValue(), null, node); + if (symbolizer instanceof ol.style.Icon) { + var rotation = symbolizer.getRotation(); + goog.asserts.assert(rotation instanceof ol.expr.Literal, + 'Only ol.expr.Literal supported for rotation'); + this.writeNode('Rotation', rotation.getValue(), null, node); + } + return node; + }, + 'PolygonSymbolizer': function(symbolizer) { + var node = this.createElementNS('sld:PolygonSymbolizer'); + this.writeNode('Fill', symbolizer, null, node); + return node; + }, + 'Fill': function(symbolizer) { + var node = this.createElementNS('sld:Fill'); + var fillColor = symbolizer.getColor(); + var msg = 'Only ol.expr.Literal supported for Fill properties'; + goog.asserts.assert(fillColor instanceof ol.expr.Literal, msg); + this.writeNode('CssParameter', { + value: fillColor.getValue(), + key: 'fillColor' + }, null, node); + var fillOpacity = symbolizer.getOpacity(); + goog.asserts.assert(fillOpacity instanceof ol.expr.Literal, msg); + this.writeNode('CssParameter', { + value: fillOpacity.getValue(), + key: 'fillOpacity' + }, null, node); + return node; + }, + 'TextSymbolizer': function(symbolizer) { + var node = this.createElementNS('sld:TextSymbolizer'); + var text = symbolizer.getText(); + // TODO in SLD optional, but in ol3 required? + this.writeNode('Label', text, null, node); + // TODO in SLD optional, but in ol3 required? + this.writeNode('Font', symbolizer, null, node); + // TODO map align to labelAnchorPoint etc. + var stroke = symbolizer.getStroke(); + if (!goog.isNull(stroke)) { + this.writeNode('Halo', stroke, null, node); + } + return node; + }, + 'LineSymbolizer': function(symbolizer) { + var node = this.createElementNS('sld:LineSymbolizer'); + this.writeNode('Stroke', symbolizer, null, node); + return node; + }, + 'Stroke': function(symbolizer) { + var node = this.createElementNS('sld:Stroke'); + var strokeColor = symbolizer.getColor(); + var msg = 'SLD writing of stroke properties only supported ' + + 'for ol.expr.Literal'; + goog.asserts.assert(strokeColor instanceof ol.expr.Literal, msg); + this.writeNode('CssParameter', { + value: strokeColor.getValue(), + key: 'strokeColor' + }, null, node); + var strokeOpacity = symbolizer.getOpacity(); + goog.asserts.assert(strokeOpacity instanceof ol.expr.Literal, msg); + this.writeNode('CssParameter', { + value: strokeOpacity.getValue(), + key: 'strokeOpacity' + }, null, node); + var strokeWidth = symbolizer.getWidth(); + goog.asserts.assert(strokeWidth instanceof ol.expr.Literal, msg); + this.writeNode('CssParameter', { + value: strokeWidth.getValue(), + key: 'strokeWidth' + }, null, node); + // TODO strokeDashstyle and strokeLinecap + return node; + }, + 'CssParameter': function(obj) { + // not handling ogc:expressions for now + var node = this.createElementNS('sld:CssParameter'); + node.setAttribute('name', + ol.parser.ogc.SLD_v1.getCssProperty_(obj.key)); + node.appendChild(this.createTextNode(obj.value)); + return node; + }, + 'Label': function(label) { + var node = this.createElementNS('sld:Label'); + this.filter_.writeOgcExpression(label, node); + return node; + }, + 'Font': function(symbolizer) { + var node = this.createElementNS('sld:Font'); + this.writeNode('CssParameter', { + key: 'fontFamily', + value: symbolizer.getFontFamily().getValue() + }, null, node); + this.writeNode('CssParameter', { + key: 'fontSize', + value: symbolizer.getFontSize().getValue() + }, null, node); + // TODO fontWeight and fontStyle + return node; + }, + 'MinScaleDenominator': function(scale) { + var node = this.createElementNS('sld:MinScaleDenominator'); + node.appendChild(this.createTextNode(scale)); + return node; + }, + 'MaxScaleDenominator': function(scale) { + var node = this.createElementNS('sld:MaxScaleDenominator'); + node.appendChild(this.createTextNode(scale)); + return node; + }, + 'Size': function(value) { + var node = this.createElementNS('sld:Size'); + this.filter_.writeOgcExpression(value, node); + return node; + } + } + }; this.filter_ = new ol.parser.ogc.Filter_v1_0_0(); for (var uri in this.filter_.readers) { for (var key in this.filter_.readers[uri]) { @@ -392,6 +745,15 @@ ol.parser.ogc.SLD_v1 = function() { this.filter_); } } + for (var uri in this.filter_.writers) { + for (var key in this.filter_.writers[uri]) { + if (!goog.isDef(this.writers[uri])) { + this.writers[uri] = {}; + } + this.writers[uri][key] = goog.bind(this.filter_.writers[uri][key], + this.filter_); + } + } goog.base(this); }; goog.inherits(ol.parser.ogc.SLD_v1, ol.parser.XML); @@ -415,13 +777,28 @@ ol.parser.ogc.SLD_v1.cssMap_ = { }; +/** + * @private + * @param {string} sym Symbolizer property. + * @return {string|undefined} The css property that matches the symbolizer + * property. + */ +ol.parser.ogc.SLD_v1.getCssProperty_ = function(sym) { + return goog.object.findKey(ol.parser.ogc.SLD_v1.cssMap_, + function(value, key, obj) { + return (sym === value); + } + ); +}; + + /** * @private * @param {number} scaleDenominator The scale denominator to convert to * resolution. * @return {number} resolution. */ -ol.parser.ogc.SLD_v1.prototype.getResolutionFromScale_ = +ol.parser.ogc.SLD_v1.prototype.getResolutionFromScaleDenominator_ = function(scaleDenominator) { var dpi = 25.4 / 0.28; var mpu = ol.METERS_PER_UNIT[this.units]; @@ -429,6 +806,19 @@ ol.parser.ogc.SLD_v1.prototype.getResolutionFromScale_ = }; +/** + * @private + * @param {number} resolution The resolution to convert to scale denominator. + * @return {number} scale denominator. + */ +ol.parser.ogc.SLD_v1.prototype.getScaleDenominatorFromResolution_ = + function(resolution) { + var dpi = 25.4 / 0.28; + var mpu = ol.METERS_PER_UNIT[this.units]; + return resolution * mpu * 39.37 * dpi; +}; + + /** * @param {string|Document|Element} data Data to read. * @param {ol.parser.SLDReadOptions=} opt_options Read options. @@ -451,3 +841,24 @@ ol.parser.ogc.SLD_v1.prototype.read = function(data, opt_options) { delete this.units; return obj; }; + + +/** + * @param {Object} style The style to write out. + * @param {ol.parser.SLDWriteOptions=} opt_options Write options. + * @return {string} The serialized SLD. + */ +ol.parser.ogc.SLD_v1.prototype.write = function(style, opt_options) { + var units = 'm'; + if (goog.isDef(opt_options) && goog.isDef(opt_options.units)) { + units = opt_options.units; + } + this.units = units; + var root = this.writeNode('StyledLayerDescriptor', style); + this.setAttributeNS( + root, 'http://www.w3.org/2001/XMLSchema-instance', + 'xsi:schemaLocation', this.schemaLocation); + var result = this.serialize(root); + delete this.units; + return result; +}; diff --git a/src/ol/style/rule.js b/src/ol/style/rule.js index b06e03cae9..25d342aa5e 100644 --- a/src/ol/style/rule.js +++ b/src/ol/style/rule.js @@ -78,3 +78,27 @@ ol.style.Rule.prototype.applies = function(feature, resolution) { ol.style.Rule.prototype.getSymbolizers = function() { return this.symbolizers_; }; + + +/** + * @return {ol.expr.Expression} + */ +ol.style.Rule.prototype.getFilter = function() { + return this.filter_; +}; + + +/** + * @return {number} + */ +ol.style.Rule.prototype.getMinResolution = function() { + return this.minResolution_; +}; + + +/** + * @return {number} + */ +ol.style.Rule.prototype.getMaxResolution = function() { + return this.maxResolution_; +}; diff --git a/src/ol/style/style.js b/src/ol/style/style.js index 1a99081935..d7e0d38022 100644 --- a/src/ol/style/style.js +++ b/src/ol/style/style.js @@ -207,3 +207,19 @@ ol.style.Style.reduceLiterals_ = function(literals) { } return reduced; }; + + +/** + * @return {Array.} + */ +ol.style.Style.prototype.getRules = function() { + return this.rules_; +}; + + +/** + * @param {Array.} rules The rules to set. + */ +ol.style.Style.prototype.setRules = function(rules) { + this.rules_ = rules; +}; diff --git a/src/ol/style/textsymbolizer.js b/src/ol/style/textsymbolizer.js index bc3e1d3ec7..7b2e8e92cd 100644 --- a/src/ol/style/textsymbolizer.js +++ b/src/ol/style/textsymbolizer.js @@ -215,6 +215,15 @@ ol.style.Text.prototype.getZIndex = function() { }; +/** + * Get the stroke. + * @return {ol.style.Stroke} Stroke. + */ +ol.style.Text.prototype.getStroke = function() { + return this.stroke_; +}; + + /** * Set the font color. * @param {ol.expr.Expression} color Font color. diff --git a/test/spec/ol/parser/ogc/sld_v1_0_0.test.js b/test/spec/ol/parser/ogc/sld_v1_0_0.test.js index 057d322062..5a6b76ef0f 100644 --- a/test/spec/ol/parser/ogc/sld_v1_0_0.test.js +++ b/test/spec/ol/parser/ogc/sld_v1_0_0.test.js @@ -56,6 +56,7 @@ describe('ol.parser.ogc.SLD_v1_0_0', function() { expect(second.getSymbolizers().length).to.equal(2); expect(second.getSymbolizers()[0]).to.be.a(ol.style.Fill); expect(second.getSymbolizers()[1]).to.be.a(ol.style.Stroke); + window.console.log(parser.write(obj)); done(); }); }); From 924e9c6fce980595b2d2523496d5fa5d64adb83e Mon Sep 17 00:00:00 2001 From: Bart van den Eijnden Date: Fri, 11 Oct 2013 14:24:33 +0200 Subject: [PATCH 04/12] write out string concatenation in the more common way --- src/ol/parser/ogc/filterparser_v1.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ol/parser/ogc/filterparser_v1.js b/src/ol/parser/ogc/filterparser_v1.js index a030b9bf78..f0a21995ab 100644 --- a/src/ol/parser/ogc/filterparser_v1.js +++ b/src/ol/parser/ogc/filterparser_v1.js @@ -508,7 +508,14 @@ ol.parser.ogc.Filter_v1.prototype.write = function(filter) { */ ol.parser.ogc.Filter_v1.prototype.writeOgcExpression = function(expr, node) { if (expr instanceof ol.expr.Call) { - this.writeNode('Function', expr, null, node); + if (ol.expr.isLibCall(expr) === ol.expr.functions.CONCAT) { + var args = expr.getArgs(); + for (var i = 0, ii = args.length; i < ii; ++i) { + this.writeOgcExpression(args[i], node); + } + } else { + this.writeNode('Function', expr, null, node); + } } else if (expr instanceof ol.expr.Literal) { this.writeNode('Literal', expr, null, node); } else if (expr instanceof ol.expr.Identifier) { From 9a6c1feddc19a3991ba25df3db7bd90a61b67578 Mon Sep 17 00:00:00 2001 From: Bart van den Eijnden Date: Fri, 11 Oct 2013 14:25:33 +0200 Subject: [PATCH 05/12] Add more changes to the halo functionality, introduce SLD defaults for symbolizers conform the SLD spec --- src/ol/parser/ogc/sldparser_v1.js | 94 ++++++++++++++++------ test/spec/ol/parser/ogc/sld_v1_0_0.test.js | 4 +- 2 files changed, 73 insertions(+), 25 deletions(-) diff --git a/src/ol/parser/ogc/sldparser_v1.js b/src/ol/parser/ogc/sldparser_v1.js index 93bf09eb44..4803ae4db8 100644 --- a/src/ol/parser/ogc/sldparser_v1.js +++ b/src/ol/parser/ogc/sldparser_v1.js @@ -89,6 +89,8 @@ ol.parser.ogc.SLD_v1 = function() { 'TextSymbolizer': function(node, rule) { var config = {}; this.readChildNodes(node, config); + config.color = config.fill.fillColor; + delete config.fill; config.zIndex = this.featureTypeCounter; rule.symbolizers.push( new ol.style.Text(/** @type {ol.style.TextOptions} */(config)) @@ -182,18 +184,24 @@ ol.parser.ogc.SLD_v1 = function() { 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']; + symbolizer.stroke = new ol.style.Stroke({ + color: goog.isDef(obj.fill.fillColor) ? obj.fill.fillColor : + ol.parser.ogc.SLD_v1.defaults_.haloColor, + width: goog.isDef(obj.haloRadius) ? obj.haloRadius * 2 : + ol.parser.ogc.SLD_v1.defaults_.haloRadius, + opacity: goog.isDef(obj.fill.fillOpacity) ? obj.fill.fillOpacity : + ol.parser.ogc.SLD_v1.defaults_.haloOpacity + }); }, 'Radius': function(node, symbolizer) { var ogcreaders = this.readers['http://www.opengis.net/ogc']; var radius = ogcreaders._expression.call(this, node); + goog.asserts.assertInstanceof(radius, ol.expr.Literal, + 'radius expected to be an ol.expr.Literal'); if (goog.isDef(radius)) { - symbolizer.haloRadius = radius; + symbolizer.haloRadius = radius.getValue(); } }, 'RasterSymbolizer': function(node, rule) { @@ -238,8 +246,10 @@ ol.parser.ogc.SLD_v1 = function() { config.zIndex = this.featureTypeCounter; if (goog.isDef(config.fill)) { var fill = { - color: config.fill.fillColor, - opacity: config.fill.fillOpacity + color: config.fill.fillColor.getValue(), + opacity: goog.isDef(config.fill.fillOpacity) ? + config.fill.fillOpacity : + ol.parser.ogc.SLD_v1.defaults_.fillOpacity }; rule.symbolizers.push( new ol.style.Fill(fill) @@ -248,9 +258,13 @@ ol.parser.ogc.SLD_v1 = function() { } if (goog.isDef(config.stroke)) { var stroke = { - color: config.stroke.strokeColor, - opacity: config.stroke.strokeOpacity, - width: config.stroke.strokeWidth + color: config.stroke.strokeColor.getValue(), + opacity: goog.isDef(config.stroke.strokeOpacity) ? + config.stroke.strokeOpacity : + ol.parser.ogc.SLD_v1.defaults_.strokeOpacity, + width: goog.isDef(config.stroke.strokeWidth) ? + config.stroke.strokeWidth : + ol.parser.ogc.SLD_v1.defaults_.strokeWidth }; rule.symbolizers.push( new ol.style.Stroke(stroke) @@ -607,7 +621,7 @@ ol.parser.ogc.SLD_v1 = function() { if (symbolizer instanceof ol.style.Icon) { this.writeNode('ExternalGraphic', symbolizer, null, node); var opacity = symbolizer.getOpacity(); - goog.asserts.assert(opacity instanceof ol.expr.Literal, + goog.asserts.assertInstanceof(opacity, ol.expr.Literal, 'Only ol.expr.Literal supported for graphicOpacity'); this.writeNode('Opacity', opacity.getValue(), null, node); size = symbolizer.getWidth(); @@ -615,12 +629,12 @@ ol.parser.ogc.SLD_v1 = function() { this.writeNode('Mark', symbolizer, null, node); size = symbolizer.getSize(); } - goog.asserts.assert(size instanceof ol.expr.Literal, + goog.asserts.assertInstanceof(size, ol.expr.Literal, 'Only ol.expr.Literal supported for in Size'); this.writeNode('Size', size.getValue(), null, node); if (symbolizer instanceof ol.style.Icon) { var rotation = symbolizer.getRotation(); - goog.asserts.assert(rotation instanceof ol.expr.Literal, + goog.asserts.assertInstanceof(rotation, ol.expr.Literal, 'Only ol.expr.Literal supported for rotation'); this.writeNode('Rotation', rotation.getValue(), null, node); } @@ -635,13 +649,13 @@ ol.parser.ogc.SLD_v1 = function() { var node = this.createElementNS('sld:Fill'); var fillColor = symbolizer.getColor(); var msg = 'Only ol.expr.Literal supported for Fill properties'; - goog.asserts.assert(fillColor instanceof ol.expr.Literal, msg); + goog.asserts.assertInstanceof(fillColor, ol.expr.Literal, msg); this.writeNode('CssParameter', { value: fillColor.getValue(), key: 'fillColor' }, null, node); var fillOpacity = symbolizer.getOpacity(); - goog.asserts.assert(fillOpacity instanceof ol.expr.Literal, msg); + goog.asserts.assertInstanceof(fillOpacity, ol.expr.Literal, msg); this.writeNode('CssParameter', { value: fillOpacity.getValue(), key: 'fillOpacity' @@ -660,6 +674,24 @@ ol.parser.ogc.SLD_v1 = function() { if (!goog.isNull(stroke)) { this.writeNode('Halo', stroke, null, node); } + var color = symbolizer.getColor(); + goog.asserts.assertInstanceof(color, ol.expr.Literal, + 'font color should be ol.expr.Literal'); + this.writeNode('Fill', symbolizer, null, node); + return node; + }, + 'Halo': function(symbolizer) { + var node = this.createElementNS('sld:Halo'); + goog.asserts.assertInstanceof(symbolizer.getWidth(), ol.expr.Literal, + 'Only ol.expr.Literal supported for haloRadius'); + this.writeNode('Radius', symbolizer.getWidth().getValue() / 2, null, + node); + this.writeNode('Fill', symbolizer, null, node); + return node; + }, + 'Radius': function(value) { + var node = this.createElementNS('sld:Radius'); + node.appendChild(this.createTextNode(value)); return node; }, 'LineSymbolizer': function(symbolizer) { @@ -672,19 +704,19 @@ ol.parser.ogc.SLD_v1 = function() { var strokeColor = symbolizer.getColor(); var msg = 'SLD writing of stroke properties only supported ' + 'for ol.expr.Literal'; - goog.asserts.assert(strokeColor instanceof ol.expr.Literal, msg); + goog.asserts.assertInstanceof(strokeColor, ol.expr.Literal, msg); this.writeNode('CssParameter', { value: strokeColor.getValue(), key: 'strokeColor' }, null, node); var strokeOpacity = symbolizer.getOpacity(); - goog.asserts.assert(strokeOpacity instanceof ol.expr.Literal, msg); + goog.asserts.assertInstanceof(strokeOpacity, ol.expr.Literal, msg); this.writeNode('CssParameter', { value: strokeOpacity.getValue(), key: 'strokeOpacity' }, null, node); var strokeWidth = symbolizer.getWidth(); - goog.asserts.assert(strokeWidth instanceof ol.expr.Literal, msg); + goog.asserts.assertInstanceof(strokeWidth, ol.expr.Literal, msg); this.writeNode('CssParameter', { value: strokeWidth.getValue(), key: 'strokeWidth' @@ -694,11 +726,14 @@ ol.parser.ogc.SLD_v1 = function() { }, 'CssParameter': function(obj) { // not handling ogc:expressions for now - var node = this.createElementNS('sld:CssParameter'); - node.setAttribute('name', - ol.parser.ogc.SLD_v1.getCssProperty_(obj.key)); - node.appendChild(this.createTextNode(obj.value)); - return node; + var name = ol.parser.ogc.SLD_v1.getCssProperty_(obj.key); + if (goog.isDef(name) && obj.value !== + ol.parser.ogc.SLD_v1.defaults_[obj.key]) { + var node = this.createElementNS('sld:CssParameter'); + node.setAttribute('name', name); + node.appendChild(this.createTextNode(obj.value)); + return node; + } }, 'Label': function(label) { var node = this.createElementNS('sld:Label'); @@ -777,6 +812,19 @@ ol.parser.ogc.SLD_v1.cssMap_ = { }; +/** + * @private + */ +ol.parser.ogc.SLD_v1.defaults_ = { + fillOpacity: 1, + strokeOpacity: 1, + strokeWidth: 1, + haloColor: '#FFFFFF', + haloOpacity: 1, + haloRadius: 1 +}; + + /** * @private * @param {string} sym Symbolizer property. diff --git a/test/spec/ol/parser/ogc/sld_v1_0_0.test.js b/test/spec/ol/parser/ogc/sld_v1_0_0.test.js index 5a6b76ef0f..f9ec9e569e 100644 --- a/test/spec/ol/parser/ogc/sld_v1_0_0.test.js +++ b/test/spec/ol/parser/ogc/sld_v1_0_0.test.js @@ -26,7 +26,7 @@ describe('ol.parser.ogc.SLD_v1_0_0', function() { expect(first.getSymbolizers()[0]).to.be.a(ol.style.Fill); expect(first.getSymbolizers()[0].getColor().getValue()).to.equal( '#ffffff'); - expect(first.getSymbolizers()[0].getOpacity().getValue()).to.equal(0.4); + expect(first.getSymbolizers()[0].getOpacity().getValue()).to.equal(1); expect(first.getSymbolizers()[1]).to.be.a(ol.style.Stroke); expect(first.getSymbolizers()[1].getColor().getValue()).to.equal( '#000000'); @@ -47,7 +47,7 @@ describe('ol.parser.ogc.SLD_v1_0_0', function() { expect(first.getSymbolizers()[2].getText().getArgs()[2].getValue()). to.equal('label'); expect(first.getSymbolizers()[2].getColor().getValue()).to.equal( - ol.style.TextDefaults.color); + '#000000'); expect(first.getSymbolizers()[2].getFontFamily().getValue()).to.equal( 'Arial'); // TODO add tests for haloRadius and haloColor From c75082c75dd3eee057a24f29f6c5679b38c7a5c2 Mon Sep 17 00:00:00 2001 From: Bart van den Eijnden Date: Fri, 11 Oct 2013 16:49:39 +0200 Subject: [PATCH 06/12] add name and title to ol.style.Rule --- src/objectliterals.jsdoc | 2 ++ src/ol/parser/ogc/sldparser_v1.js | 9 +++------ src/ol/style/rule.js | 30 ++++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/src/objectliterals.jsdoc b/src/objectliterals.jsdoc index 545573f100..8534aef970 100644 --- a/src/objectliterals.jsdoc +++ b/src/objectliterals.jsdoc @@ -819,6 +819,8 @@ * a value is provided, the rule will apply at resolutions greater than or * equal to this value. * @property {Array.|undefined} symbolizers Symbolizers. + * @property {string|undefined} name Name. + * @property {string|undefined} title Title. * @todo stability experimental */ diff --git a/src/ol/parser/ogc/sldparser_v1.js b/src/ol/parser/ogc/sldparser_v1.js index 4803ae4db8..feecb404b1 100644 --- a/src/ol/parser/ogc/sldparser_v1.js +++ b/src/ol/parser/ogc/sldparser_v1.js @@ -544,15 +544,12 @@ ol.parser.ogc.SLD_v1 = function() { 'Rule': function(rule) { var node = this.createElementNS('sld:Rule'); var filter = rule.getFilter(); - if (rule.name) { - this.writeNode('Name', rule.name, null, node); + if (!goog.isNull(rule.getName())) { + this.writeNode('Name', rule.getName(), null, node); } - if (rule.title) { + if (!goog.isNull(rule.getTitle())) { this.writeNode('Title', rule.title, null, node); } - if (rule.description) { - this.writeNode('Abstract', rule.description, null, node); - } if (rule.elseFilter) { this.writeNode('ElseFilter', null, null, node); } else if (filter) { diff --git a/src/ol/style/rule.js b/src/ol/style/rule.js index 25d342aa5e..3495422459 100644 --- a/src/ol/style/rule.js +++ b/src/ol/style/rule.js @@ -54,6 +54,20 @@ ol.style.Rule = function(options) { this.maxResolution_ = goog.isDef(options.maxResolution) ? options.maxResolution : Infinity; + /** + * @type {?string} + * @private + */ + this.name_ = goog.isDef(options.name) ? + options.name : null; + + /** + * @type {?string} + * @private + */ + this.title_ = goog.isDef(options.title) ? + options.title : null; + }; @@ -102,3 +116,19 @@ ol.style.Rule.prototype.getMinResolution = function() { ol.style.Rule.prototype.getMaxResolution = function() { return this.maxResolution_; }; + + +/** + * @return {?string} + */ +ol.style.Rule.prototype.getName = function() { + return this.name_; +}; + + +/** + * @return {?string} + */ +ol.style.Rule.prototype.getTitle = function() { + return this.title_; +}; From 5c8fb35227256724bcb51fb3b86d622ddc9666b9 Mon Sep 17 00:00:00 2001 From: Bart van den Eijnden Date: Fri, 11 Oct 2013 17:19:56 +0200 Subject: [PATCH 07/12] get rid of the readers we don't support in ol3 symbology as yet --- src/ol/parser/ogc/sldparser_v1.js | 105 ------------------------------ 1 file changed, 105 deletions(-) diff --git a/src/ol/parser/ogc/sldparser_v1.js b/src/ol/parser/ogc/sldparser_v1.js index feecb404b1..40d0ef21f8 100644 --- a/src/ol/parser/ogc/sldparser_v1.js +++ b/src/ol/parser/ogc/sldparser_v1.js @@ -96,83 +96,6 @@ ol.parser.ogc.SLD_v1 = function() { new ol.style.Text(/** @type {ol.style.TextOptions} */(config)) ); }, - 'LabelPlacement': function(node, symbolizer) { - this.readChildNodes(node, symbolizer); - }, - 'PointPlacement': function(node, symbolizer) { - var config = {}; - this.readChildNodes(node, config); - config.labelRotation = config.rotation; - delete config.rotation; - var labelAlign, - x = symbolizer.labelAnchorPointX, - y = symbolizer.labelAnchorPointY; - if (x <= 1 / 3) { - labelAlign = 'l'; - } else if (x > 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; - goog.object.extend(symbolizer, config); - }, - 'AnchorPoint': function(node, symbolizer) { - this.readChildNodes(node, symbolizer); - }, - 'AnchorPointX': function(node, symbolizer) { - var ogcreaders = this.readers['http://www.opengis.net/ogc']; - var labelAnchorPointX = ogcreaders._expression.call(this, node); - // always string, could be empty string - if (labelAnchorPointX) { - symbolizer.labelAnchorPointX = labelAnchorPointX; - } - }, - 'AnchorPointY': function(node, symbolizer) { - var ogcreaders = this.readers['http://www.opengis.net/ogc']; - var labelAnchorPointY = ogcreaders._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 ogcreaders = this.readers['http://www.opengis.net/ogc']; - var labelXOffset = ogcreaders._expression.call(this, node); - // always string, could be empty string - if (labelXOffset) { - symbolizer.labelXOffset = labelXOffset; - } - }, - 'DisplacementY': function(node, symbolizer) { - var ogcreaders = this.readers['http://www.opengis.net/ogc']; - var labelYOffset = ogcreaders._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 ogcreaders = this.readers['http://www.opengis.net/ogc']; - var labelPerpendicularOffset = ogcreaders._expression.call(this, node); - // always string, could be empty string - if (labelPerpendicularOffset) { - symbolizer.labelPerpendicularOffset = labelPerpendicularOffset; - } - }, 'Label': function(node, symbolizer) { var ogcreaders = this.readers['http://www.opengis.net/ogc']; var value = ogcreaders._expression.call(this, node); @@ -204,34 +127,6 @@ ol.parser.ogc.SLD_v1 = function() { symbolizer.haloRadius = radius.getValue(); } }, - 'RasterSymbolizer': function(node, rule) { - var config = {}; - this.readChildNodes(node, config); - config.zIndex = this.featureTypeCounter; - /* TODO - rule.symbolizers.push( - new OpenLayers.Symbolizer.Raster(config) - ); - */ - }, - '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); From a7fe89c05d9fd7162404f3f331f9cea332bd0cb3 Mon Sep 17 00:00:00 2001 From: Bart van den Eijnden Date: Mon, 14 Oct 2013 13:22:19 +0200 Subject: [PATCH 08/12] better write support for PointSymbolizer --- src/ol/parser/ogc/sldparser_v1.js | 98 ++++++++++++++----------------- 1 file changed, 44 insertions(+), 54 deletions(-) diff --git a/src/ol/parser/ogc/sldparser_v1.js b/src/ol/parser/ogc/sldparser_v1.js index 40d0ef21f8..52825b2f24 100644 --- a/src/ol/parser/ogc/sldparser_v1.js +++ b/src/ol/parser/ogc/sldparser_v1.js @@ -173,15 +173,38 @@ ol.parser.ogc.SLD_v1 = function() { this.readChildNodes(node, config); config.zIndex = this.featureTypeCounter; if (config.fill) { - config.fill = new ol.style.Fill(config.fill); + var fillConfig = { + color: goog.isDef(config.fill.fillColor) ? + config.fill.fillColor : + ol.parser.ogc.SLD_v1.defaults_.fillColor, + opacity: goog.isDef(config.fill.fillOpacity) ? + config.fill.fillOpacity : + ol.parser.ogc.SLD_v1.defaults_.fillOpacity + }; + config.fill = new ol.style.Fill(fillConfig); } if (config.stroke) { - config.stroke = new ol.style.Stroke(config.stroke); + var strokeConfig = { + color: goog.isDef(config.stroke.strokeColor) ? + config.stroke.strokeColor : + ol.parser.ogc.SLD_v1.defaults_.strokeColor, + width: goog.isDef(config.stroke.strokeWidth) ? + config.stroke.strokeWidth : + ol.parser.ogc.SLD_v1.defaults_.strokeWidth, + opacity: goog.isDef(config.stroke.strokeOpacity) ? + config.stroke.strokeOpacity : + ol.parser.ogc.SLD_v1.defaults_.strokeOpacity + }; + config.stroke = new ol.style.Stroke(strokeConfig); } - // TODO shape or icon? - rule.symbolizers.push( - new ol.style.Shape(config) - ); + var symbolizer; + if (goog.isDef(config.externalGraphic)) { + config.width = config.height = config.size; + symbolizer = new ol.style.Icon(config); + } else { + symbolizer = new ol.style.Shape(config); + } + rule.symbolizers.push(symbolizer); }, 'Stroke': function(node, symbolizer) { var stroke = {}; @@ -196,35 +219,18 @@ ol.parser.ogc.SLD_v1 = function() { 'CssParameter': function(node, symbolizer) { var cssProperty = node.getAttribute('name'); var symProperty = ol.parser.ogc.SLD_v1.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 ogcreaders = this.readers['http://www.opengis.net/ogc']; - var value = ogcreaders._expression.call(this, node); - // always string, could be an empty string - if (value) { - symbolizer[symProperty] = value; - } + symbolizer[symProperty] = ogcreaders._expression.call(this, node); } }, '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' + 'stroke', 'fill', 'rotation', 'opacity' ]; var prop, value; for (var i = 0, ii = properties.length; i < ii; ++i) { @@ -235,23 +241,20 @@ ol.parser.ogc.SLD_v1 = function() { } } // set other generic properties with specific graphic property names - if (goog.isDef(graphic.opacity)) { - symbolizer.graphicOpacity = graphic.opacity; + if (goog.isDef(graphic.graphicName)) { + symbolizer.type = graphic.graphicName; } if (goog.isDef(graphic.size)) { var pointRadius = graphic.size / 2; if (isNaN(pointRadius)) { // likely a property name - symbolizer.graphicWidth = graphic.size; + symbolizer.size = graphic.size; } else { - symbolizer.pointRadius = graphic.size / 2; + symbolizer.size = graphic.size / 2; } } if (goog.isDef(graphic.href)) { - symbolizer.externalGraphic = graphic.href; - } - if (goog.isDef(graphic.rotation)) { - symbolizer.rotation = graphic.rotation; + symbolizer.url = graphic.href; } }, 'ExternalGraphic': function(node, graphic) { @@ -265,27 +268,15 @@ ol.parser.ogc.SLD_v1 = function() { }, 'Opacity': function(node, obj) { var ogcreaders = this.readers['http://www.opengis.net/ogc']; - var opacity = ogcreaders._expression.call(this, node); - // always string, could be empty string - if (opacity) { - obj.opacity = opacity; - } + obj.opacity = ogcreaders._expression.call(this, node); }, 'Size': function(node, obj) { var ogcreaders = this.readers['http://www.opengis.net/ogc']; - var size = ogcreaders._expression.call(this, node); - // always string, could be empty string - if (size) { - obj.size = size; - } + obj.size = ogcreaders._expression.call(this, node); }, 'Rotation': function(node, obj) { var ogcreaders = this.readers['http://www.opengis.net/ogc']; - var rotation = ogcreaders._expression.call(this, node); - // always string, could be empty string - if (rotation) { - obj.rotation = rotation; - } + obj.rotation = ogcreaders._expression.call(this, node); }, 'OnlineResource': function(node, obj) { obj.href = this.getAttributeNS( @@ -521,9 +512,7 @@ ol.parser.ogc.SLD_v1 = function() { this.writeNode('Mark', symbolizer, null, node); size = symbolizer.getSize(); } - goog.asserts.assertInstanceof(size, ol.expr.Literal, - 'Only ol.expr.Literal supported for in Size'); - this.writeNode('Size', size.getValue(), null, node); + this.writeNode('Size', size, null, node); if (symbolizer instanceof ol.style.Icon) { var rotation = symbolizer.getRotation(); goog.asserts.assertInstanceof(rotation, ol.expr.Literal, @@ -619,8 +608,7 @@ ol.parser.ogc.SLD_v1 = function() { 'CssParameter': function(obj) { // not handling ogc:expressions for now var name = ol.parser.ogc.SLD_v1.getCssProperty_(obj.key); - if (goog.isDef(name) && obj.value !== - ol.parser.ogc.SLD_v1.defaults_[obj.key]) { + if (goog.isDef(name)) { var node = this.createElementNS('sld:CssParameter'); node.setAttribute('name', name); node.appendChild(this.createTextNode(obj.value)); @@ -711,9 +699,11 @@ ol.parser.ogc.SLD_v1.defaults_ = { fillOpacity: 1, strokeOpacity: 1, strokeWidth: 1, + strokeColor: '#000000', haloColor: '#FFFFFF', haloOpacity: 1, - haloRadius: 1 + haloRadius: 1, + fillColor: '#808080' }; From 91e834674cc173255d33b2d2f577ef95d02f98e2 Mon Sep 17 00:00:00 2001 From: Bart van den Eijnden Date: Mon, 14 Oct 2013 15:14:56 +0200 Subject: [PATCH 09/12] deal with elseFilter, some cleanup --- src/objectliterals.jsdoc | 2 + src/ol/parser/ogc/sldparser_v1.js | 98 ++++++------------------------- src/ol/style/style.js | 37 ++++++++++++ 3 files changed, 56 insertions(+), 81 deletions(-) diff --git a/src/objectliterals.jsdoc b/src/objectliterals.jsdoc index 8534aef970..bbd675ff59 100644 --- a/src/objectliterals.jsdoc +++ b/src/objectliterals.jsdoc @@ -850,6 +850,8 @@ * @property {Array.|undefined} symbolizers Symbolizers * (that apply if no rules are provided or where none of the provided rules * apply). + * @property {string|undefined} name Name. + * @property {string|undefined} title Title. * @todo stability experimental */ diff --git a/src/ol/parser/ogc/sldparser_v1.js b/src/ol/parser/ogc/sldparser_v1.js index 52825b2f24..a48c20dbcc 100644 --- a/src/ol/parser/ogc/sldparser_v1.js +++ b/src/ol/parser/ogc/sldparser_v1.js @@ -57,11 +57,6 @@ ol.parser.ogc.SLD_v1 = function() { this.readChildNodes(node, obj); layer.userStyles.push(new ol.style.Style(obj)); }, - 'IsDefault': function(node, style) { - if (this.getChildValue(node) === '1') { - style.isDefault = true; - } - }, 'FeatureTypeStyle': function(node, style) { ++this.featureTypeCounter; var obj = { @@ -200,7 +195,8 @@ ol.parser.ogc.SLD_v1 = function() { var symbolizer; if (goog.isDef(config.externalGraphic)) { config.width = config.height = config.size; - symbolizer = new ol.style.Icon(config); + symbolizer = new ol.style.Icon( + /** @type {ol.style.IconOptions} */(config)); } else { symbolizer = new ol.style.Shape(config); } @@ -345,86 +341,30 @@ ol.parser.ogc.SLD_v1 = function() { }, 'UserStyle': function(style) { var node = this.createElementNS('sld:UserStyle'); - if (style.name) { - this.writeNode('Name', style.name, null, node); + var name = style.getName(), title = style.getTitle(); + if (!goog.isNull(name)) { + this.writeNode('Name', name, null, node); } - if (style.title) { - this.writeNode('Title', style.title, null, node); + if (!goog.isNull(title)) { + this.writeNode('Title', title, null, node); } - if (style.description) { - this.writeNode('Abstract', style.description, null, node); - } - if (style.isDefault) { - this.writeNode('IsDefault', style.isDefault, null, node); - } - if (style.rules) { - // group style objects by symbolizer zIndex - var rulesByZ = { - 0: [] - }; - var zValues = [0]; - var rule, ruleMap, symbolizer, zIndex, clone; - for (var i = 0, ii = style.rules.length; i < ii; ++i) { - rule = style.rules[i]; - var symbolizers = rule.getSymbolizers(); - if (symbolizers) { - ruleMap = {}; - for (var j = 0, jj = symbolizers.length; j < jj; ++j) { - symbolizer = symbolizers[j]; - zIndex = symbolizer.zIndex; - if (!(zIndex in ruleMap)) { - // TODO check if clone works? - clone = goog.object.clone(rule); - clone.setSymbolizers([]); - ruleMap[zIndex] = clone; - } - // TODO check if clone works - ruleMap[zIndex].getSymbolizers().push( - goog.object.clone(symbolizer)); - } - for (zIndex in ruleMap) { - if (!(zIndex in rulesByZ)) { - zValues.push(zIndex); - rulesByZ[zIndex] = []; - } - rulesByZ[zIndex].push(ruleMap[zIndex]); - } - } else { - // no symbolizers in rule - rulesByZ[0].push(goog.object.clone(rule)); - } - } - // write one FeatureTypeStyle per zIndex - zValues.sort(); - var rules; - for (var i = 0, ii = zValues.length; i < ii; ++i) { - rules = rulesByZ[zValues[i]]; - if (rules.length > 0) { - clone = goog.object.clone(style); - clone.setRules(rulesByZ[zValues[i]]); - this.writeNode('FeatureTypeStyle', clone, null, node); - } - } - } else { - this.writeNode('FeatureTypeStyle', style, null, node); - } - return node; - }, - 'IsDefault': function(bool) { - var node = this.createElementNS('sld:IsDefault'); - node.appendChild(this.createTextNode((bool) ? '1' : '0')); + // TODO sorting by zIndex + this.writeNode('FeatureTypeStyle', style, null, node); return node; }, 'FeatureTypeStyle': function(style) { var node = this.createElementNS('sld:FeatureTypeStyle'); - // OpenLayers currently stores no Name, Title, Abstract, - // FeatureTypeName, or SemanticTypeIdentifier information - // related to FeatureTypeStyle - // add in rules var rules = style.getRules(); for (var i = 0, ii = rules.length; i < ii; ++i) { this.writeNode('Rule', rules[i], null, node); } + var symbolizers = style.getSymbolizers(); + if (symbolizers.length > 0) { + // wrap this in a Rule with an ElseFilter + var rule = new ol.style.Rule({symbolizers: symbolizers}); + rule.elseFilter = true; + this.writeNode('Rule', rule, null, node); + } return node; }, 'Rule': function(rule) { @@ -436,7 +376,7 @@ ol.parser.ogc.SLD_v1 = function() { if (!goog.isNull(rule.getTitle())) { this.writeNode('Title', rule.title, null, node); } - if (rule.elseFilter) { + if (rule.elseFilter === true) { this.writeNode('ElseFilter', null, null, node); } else if (filter) { this.writeNode('Filter', filter, 'http://www.opengis.net/ogc', node); @@ -457,7 +397,6 @@ ol.parser.ogc.SLD_v1 = function() { if (symbolizers) { for (var i = 0, ii = symbolizers.length; i < ii; ++i) { symbolizer = symbolizers[i]; - // TODO other types of symbolizers if (symbolizer instanceof ol.style.Text) { type = 'Text'; } else if (symbolizer instanceof ol.style.Stroke) { @@ -546,11 +485,8 @@ ol.parser.ogc.SLD_v1 = function() { 'TextSymbolizer': function(symbolizer) { var node = this.createElementNS('sld:TextSymbolizer'); var text = symbolizer.getText(); - // TODO in SLD optional, but in ol3 required? this.writeNode('Label', text, null, node); - // TODO in SLD optional, but in ol3 required? this.writeNode('Font', symbolizer, null, node); - // TODO map align to labelAnchorPoint etc. var stroke = symbolizer.getStroke(); if (!goog.isNull(stroke)) { this.writeNode('Halo', stroke, null, node); diff --git a/src/ol/style/style.js b/src/ol/style/style.js index d7e0d38022..224ec2578c 100644 --- a/src/ol/style/style.js +++ b/src/ol/style/style.js @@ -40,6 +40,19 @@ ol.style.Style = function(options) { this.symbolizers_ = goog.isDef(options.symbolizers) ? options.symbolizers : []; + /** + * @type {?string} + * @private + */ + this.name_ = goog.isDef(options.name) ? + options.name : null; + + /** + * @type {?string} + * @private + */ + this.title_ = goog.isDef(options.title) ? + options.title : null; }; @@ -223,3 +236,27 @@ ol.style.Style.prototype.getRules = function() { ol.style.Style.prototype.setRules = function(rules) { this.rules_ = rules; }; + + +/** + * @return {Array.} + */ +ol.style.Style.prototype.getSymbolizers = function() { + return this.symbolizers_; +}; + + +/** + * @return {?string} + */ +ol.style.Style.prototype.getName = function() { + return this.name_; +}; + + +/** + * @return {?string} + */ +ol.style.Style.prototype.getTitle = function() { + return this.title_; +}; From 429a2e455b92a9bde5f1e651dc0c6a2f17c752a0 Mon Sep 17 00:00:00 2001 From: Bart van den Eijnden Date: Mon, 14 Oct 2013 15:48:56 +0200 Subject: [PATCH 10/12] Add a separate test case for write --- src/ol/parser/ogc/sldparser.js | 11 -- src/ol/parser/ogc/sldparser_v1.js | 6 +- .../parser/ogc/sldparser_v1_0_0_GeoServer.js | 43 ------ test/spec/ol/parser/ogc/sld_v1_0_0.test.js | 19 ++- .../ol/parser/ogc/xml/sld_v1_0_0_write.xml | 133 ++++++++++++++++++ 5 files changed, 152 insertions(+), 60 deletions(-) delete mode 100644 src/ol/parser/ogc/sldparser_v1_0_0_GeoServer.js create mode 100644 test/spec/ol/parser/ogc/xml/sld_v1_0_0_write.xml diff --git a/src/ol/parser/ogc/sldparser.js b/src/ol/parser/ogc/sldparser.js index 8560cdc2e8..c9db020b75 100644 --- a/src/ol/parser/ogc/sldparser.js +++ b/src/ol/parser/ogc/sldparser.js @@ -1,6 +1,5 @@ goog.provide('ol.parser.ogc.SLD'); goog.require('ol.parser.ogc.SLD_v1_0_0'); -goog.require('ol.parser.ogc.SLD_v1_0_0_GeoServer'); goog.require('ol.parser.ogc.Versioned'); @@ -10,13 +9,6 @@ goog.require('ol.parser.ogc.Versioned'); ol.ENABLE_SLD_1_0_0 = true; -/** - * @define {boolean} Whether to enable SLD version 1.0.0. - * GeoServer profile. - */ -ol.ENABLE_SLD_1_0_0_GEOSERVER = true; - - /** * @constructor @@ -30,9 +22,6 @@ ol.parser.ogc.SLD = function(opt_options) { if (ol.ENABLE_SLD_1_0_0) { this.parsers['v1_0_0'] = ol.parser.ogc.SLD_v1_0_0; } - if (ol.ENABLE_SLD_1_0_0_GEOSERVER) { - this.parsers['v1_0_0_GEOSERVER'] = ol.parser.ogc.SLD_v1_0_0_GeoServer; - } goog.base(this, opt_options); }; goog.inherits(ol.parser.ogc.SLD, ol.parser.ogc.Versioned); diff --git a/src/ol/parser/ogc/sldparser_v1.js b/src/ol/parser/ogc/sldparser_v1.js index a48c20dbcc..349603ffb2 100644 --- a/src/ol/parser/ogc/sldparser_v1.js +++ b/src/ol/parser/ogc/sldparser_v1.js @@ -84,7 +84,8 @@ ol.parser.ogc.SLD_v1 = function() { 'TextSymbolizer': function(node, rule) { var config = {}; this.readChildNodes(node, config); - config.color = config.fill.fillColor; + config.color = goog.isDef(config.fill) ? config.fill.fillColor : + ol.parser.ogc.SLD_v1.defaults_.fontColor; delete config.fill; config.zIndex = this.featureTypeCounter; rule.symbolizers.push( @@ -639,7 +640,8 @@ ol.parser.ogc.SLD_v1.defaults_ = { haloColor: '#FFFFFF', haloOpacity: 1, haloRadius: 1, - fillColor: '#808080' + fillColor: '#808080', + fontColor: '#000000' }; diff --git a/src/ol/parser/ogc/sldparser_v1_0_0_GeoServer.js b/src/ol/parser/ogc/sldparser_v1_0_0_GeoServer.js deleted file mode 100644 index 77f58d1ab5..0000000000 --- a/src/ol/parser/ogc/sldparser_v1_0_0_GeoServer.js +++ /dev/null @@ -1,43 +0,0 @@ -goog.provide('ol.parser.ogc.SLD_v1_0_0_GeoServer'); - -goog.require('goog.functions'); -goog.require('goog.object'); -goog.require('ol.parser.ogc.SLD_v1_0_0'); - - - -/** - * @constructor - * @extends {ol.parser.ogc.SLD_v1_0_0} - */ -ol.parser.ogc.SLD_v1_0_0_GeoServer = function() { - goog.base(this); - this.profile = 'GeoServer'; - goog.object.extend(this.readers['http://www.opengis.net/sld'], { - 'Priority': function(node, obj) { - var ogcreaders = this.readers['http://www.opengis.net/ogc']; - var value = ogcreaders._expression.call(this, node); - if (value) { - obj.priority = value; - } - }, - 'VendorOption': function(node, obj) { - if (!goog.isDef(obj.vendorOptions)) { - obj.vendorOptions = {}; - } - obj.vendorOptions[node.getAttribute('name')] = - this.getChildValue(node); - }, - 'TextSymbolizer': goog.functions.sequence( - this.readers['http://www.opengis.net/sld']['TextSymbolizer'], - function(node, rule) { - var symbolizer = rule.symbolizers[rule.symbolizers.length - 1]; - if (!goog.isDef(symbolizer.graphic)) { - symbolizer.graphic = false; - } - } - ) - }); -}; -goog.inherits(ol.parser.ogc.SLD_v1_0_0_GeoServer, - ol.parser.ogc.SLD_v1_0_0); diff --git a/test/spec/ol/parser/ogc/sld_v1_0_0.test.js b/test/spec/ol/parser/ogc/sld_v1_0_0.test.js index f9ec9e569e..e44fb45ac6 100644 --- a/test/spec/ol/parser/ogc/sld_v1_0_0.test.js +++ b/test/spec/ol/parser/ogc/sld_v1_0_0.test.js @@ -4,12 +4,13 @@ goog.provide('ol.test.parser.ogc.SLD_v1_0_0'); describe('ol.parser.ogc.SLD_v1_0_0', function() { var parser = new ol.parser.ogc.SLD(); + var obj; describe('reading and writing', function() { it('Handles reading', function(done) { var url = 'spec/ol/parser/ogc/xml/sld_v1_0_0.xml'; afterLoadXml(url, function(xml) { - var obj = parser.read(xml); + obj = parser.read(xml); expect(obj.version).to.equal('1.0.0'); var style = obj.namedLayers['AAA161'].userStyles[0]; expect(style).to.be.a(ol.style.Style); @@ -50,13 +51,23 @@ describe('ol.parser.ogc.SLD_v1_0_0', function() { '#000000'); expect(first.getSymbolizers()[2].getFontFamily().getValue()).to.equal( 'Arial'); - // TODO add tests for haloRadius and haloColor + expect(first.getSymbolizers()[2].getStroke()).to.be.a(ol.style.Stroke); + expect(first.getSymbolizers()[2].getStroke().getColor().getValue()) + .to.equal('#ffffff'); + expect(first.getSymbolizers()[2].getStroke().getWidth().getValue()) + .to.equal(6); var second = style.rules_[1]; expect(second.filter_).to.be.a(ol.expr.Comparison); expect(second.getSymbolizers().length).to.equal(2); expect(second.getSymbolizers()[0]).to.be.a(ol.style.Fill); expect(second.getSymbolizers()[1]).to.be.a(ol.style.Stroke); - window.console.log(parser.write(obj)); + done(); + }); + }); + it('Handles write', function(done) { + var url = 'spec/ol/parser/ogc/xml/sld_v1_0_0_write.xml'; + afterLoadXml(url, function(xml) { + expect(goog.dom.xml.loadXml(parser.write(obj))).to.xmleql(xml); done(); }); }); @@ -64,7 +75,7 @@ describe('ol.parser.ogc.SLD_v1_0_0', function() { }); -goog.require('goog.net.XhrIo'); +goog.require('goog.dom.xml'); goog.require('ol.parser.ogc.SLD_v1_0_0'); goog.require('ol.parser.ogc.SLD'); goog.require('ol.expr.Call'); diff --git a/test/spec/ol/parser/ogc/xml/sld_v1_0_0_write.xml b/test/spec/ol/parser/ogc/xml/sld_v1_0_0_write.xml new file mode 100644 index 0000000000..eeff7933ac --- /dev/null +++ b/test/spec/ol/parser/ogc/xml/sld_v1_0_0_write.xml @@ -0,0 +1,133 @@ + + + + AAA161 + + + + stortsteen + + + CTE + V0305 + + + 49999.99999999999 + + + #ffffff + 1 + + + + + #000000 + 1 + 1 + + + + AFOOlabel + + Arial + 14 + + + 3 + + #ffffff + 1 + + + + #000000 + 1 + + + + + betonbekleding + + + CTE + 1000 + + + 49999.99999999999 + + + #ffff00 + 1 + + + + + #0000ff + 1 + 1 + + + + + + + + Second Layer + + + + first rule second layer + + + + + FOO + 5000 + + + + cat + *dog.food!*good + + + number + + 1064866676 + + + 1065512599 + + + + + 10000 + + + + star + + lime + 1 + + + olive + 1 + 2 + + + SIZE + + + + + + + From 01c9448c3c520c222094694038b3ec0dc597b4e1 Mon Sep 17 00:00:00 2001 From: Bart van den Eijnden Date: Mon, 14 Oct 2013 16:08:50 +0200 Subject: [PATCH 11/12] do not hard-code the namespaceURIs multiple times --- src/ol/parser/ogc/sldparser_v1.js | 1103 ++++++++++++++--------------- 1 file changed, 551 insertions(+), 552 deletions(-) diff --git a/src/ol/parser/ogc/sldparser_v1.js b/src/ol/parser/ogc/sldparser_v1.js index 349603ffb2..ed9757b038 100644 --- a/src/ol/parser/ogc/sldparser_v1.js +++ b/src/ol/parser/ogc/sldparser_v1.js @@ -23,568 +23,567 @@ goog.require('ol.style.Text'); */ ol.parser.ogc.SLD_v1 = function() { this.defaultNamespaceURI = 'http://www.opengis.net/sld'; - this.readers = { - 'http://www.opengis.net/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); - sld.namedLayers[layer.name] = layer; - }, - 'NamedStyle': function(node, layer) { - layer.namedStyles.push( - this.getChildValue(node.firstChild) - ); - }, - 'UserStyle': function(node, layer) { - var obj = {rules: []}; - this.featureTypeCounter = -1; - this.readChildNodes(node, obj); - layer.userStyles.push(new ol.style.Style(obj)); - }, - 'FeatureTypeStyle': function(node, style) { - ++this.featureTypeCounter; - var obj = { - rules: style.rules - }; - this.readChildNodes(node, obj); - }, - 'Rule': function(node, obj) { - var config = {symbolizers: []}; - this.readChildNodes(node, config); - var rule = new ol.style.Rule(config); - obj.rules.push(rule); - }, - 'ElseFilter': function(node, rule) { - rule.elseFilter = true; - }, - 'MinScaleDenominator': function(node, rule) { - rule.minResolution = this.getResolutionFromScaleDenominator_( - parseFloat(this.getChildValue(node))); - }, - 'MaxScaleDenominator': function(node, rule) { - rule.maxResolution = this.getResolutionFromScaleDenominator_( - parseFloat(this.getChildValue(node))); - }, - 'TextSymbolizer': function(node, rule) { - var config = {}; - this.readChildNodes(node, config); - config.color = goog.isDef(config.fill) ? config.fill.fillColor : - ol.parser.ogc.SLD_v1.defaults_.fontColor; - delete config.fill; - config.zIndex = this.featureTypeCounter; - rule.symbolizers.push( - new ol.style.Text(/** @type {ol.style.TextOptions} */(config)) - ); - }, - 'Label': function(node, symbolizer) { - var ogcreaders = this.readers['http://www.opengis.net/ogc']; - var value = ogcreaders._expression.call(this, node); - if (value) { - symbolizer.text = value; - } - }, - 'Font': function(node, symbolizer) { - this.readChildNodes(node, symbolizer); - }, - 'Halo': function(node, symbolizer) { - var obj = {}; - this.readChildNodes(node, obj); - symbolizer.stroke = new ol.style.Stroke({ - color: goog.isDef(obj.fill.fillColor) ? obj.fill.fillColor : - ol.parser.ogc.SLD_v1.defaults_.haloColor, - width: goog.isDef(obj.haloRadius) ? obj.haloRadius * 2 : - ol.parser.ogc.SLD_v1.defaults_.haloRadius, - opacity: goog.isDef(obj.fill.fillOpacity) ? obj.fill.fillOpacity : - ol.parser.ogc.SLD_v1.defaults_.haloOpacity - }); - }, - 'Radius': function(node, symbolizer) { - var ogcreaders = this.readers['http://www.opengis.net/ogc']; - var radius = ogcreaders._expression.call(this, node); - goog.asserts.assertInstanceof(radius, ol.expr.Literal, - 'radius expected to be an ol.expr.Literal'); - if (goog.isDef(radius)) { - symbolizer.haloRadius = radius.getValue(); - } - }, - 'LineSymbolizer': function(node, rule) { - var config = {}; - this.readChildNodes(node, config); - config.zIndex = this.featureTypeCounter; - rule.symbolizers.push( - new ol.style.Stroke(config) - ); - }, - 'PolygonSymbolizer': function(node, rule) { - var config = {}; - this.readChildNodes(node, config); - config.zIndex = this.featureTypeCounter; - if (goog.isDef(config.fill)) { - var fill = { - color: config.fill.fillColor.getValue(), - opacity: goog.isDef(config.fill.fillOpacity) ? - config.fill.fillOpacity : - ol.parser.ogc.SLD_v1.defaults_.fillOpacity - }; - rule.symbolizers.push( - new ol.style.Fill(fill) - ); - delete config.fill; - } - if (goog.isDef(config.stroke)) { - var stroke = { - color: config.stroke.strokeColor.getValue(), - opacity: goog.isDef(config.stroke.strokeOpacity) ? - config.stroke.strokeOpacity : - ol.parser.ogc.SLD_v1.defaults_.strokeOpacity, - width: goog.isDef(config.stroke.strokeWidth) ? - config.stroke.strokeWidth : - ol.parser.ogc.SLD_v1.defaults_.strokeWidth - }; - rule.symbolizers.push( - new ol.style.Stroke(stroke) - ); - delete config.stroke; - } - - }, - 'PointSymbolizer': function(node, rule) { - var config = {}; - this.readChildNodes(node, config); - config.zIndex = this.featureTypeCounter; - if (config.fill) { - var fillConfig = { - color: goog.isDef(config.fill.fillColor) ? - config.fill.fillColor : - ol.parser.ogc.SLD_v1.defaults_.fillColor, - opacity: goog.isDef(config.fill.fillOpacity) ? - config.fill.fillOpacity : - ol.parser.ogc.SLD_v1.defaults_.fillOpacity - }; - config.fill = new ol.style.Fill(fillConfig); - } - if (config.stroke) { - var strokeConfig = { - color: goog.isDef(config.stroke.strokeColor) ? - config.stroke.strokeColor : - ol.parser.ogc.SLD_v1.defaults_.strokeColor, - width: goog.isDef(config.stroke.strokeWidth) ? - config.stroke.strokeWidth : - ol.parser.ogc.SLD_v1.defaults_.strokeWidth, - opacity: goog.isDef(config.stroke.strokeOpacity) ? - config.stroke.strokeOpacity : - ol.parser.ogc.SLD_v1.defaults_.strokeOpacity - }; - config.stroke = new ol.style.Stroke(strokeConfig); - } - var symbolizer; - if (goog.isDef(config.externalGraphic)) { - config.width = config.height = config.size; - symbolizer = new ol.style.Icon( - /** @type {ol.style.IconOptions} */(config)); - } else { - symbolizer = new ol.style.Shape(config); - } - rule.symbolizers.push(symbolizer); - }, - 'Stroke': function(node, symbolizer) { - var stroke = {}; - this.readChildNodes(node, stroke); - symbolizer.stroke = stroke; - }, - 'Fill': function(node, symbolizer) { - var fill = {}; - this.readChildNodes(node, fill); - symbolizer.fill = fill; - }, - 'CssParameter': function(node, symbolizer) { - var cssProperty = node.getAttribute('name'); - var symProperty = ol.parser.ogc.SLD_v1.cssMap_[cssProperty]; - if (symProperty) { - var ogcreaders = this.readers['http://www.opengis.net/ogc']; - symbolizer[symProperty] = ogcreaders._expression.call(this, node); - } - }, - 'Graphic': function(node, symbolizer) { - 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', 'fill', 'rotation', 'opacity' - ]; - var prop, value; - for (var i = 0, ii = properties.length; i < ii; ++i) { - prop = properties[i]; - value = graphic[prop]; - if (goog.isDef(value)) { - symbolizer[prop] = value; - } - } - // set other generic properties with specific graphic property names - if (goog.isDef(graphic.graphicName)) { - symbolizer.type = graphic.graphicName; - } - if (goog.isDef(graphic.size)) { - var pointRadius = graphic.size / 2; - if (isNaN(pointRadius)) { - // likely a property name - symbolizer.size = graphic.size; - } else { - symbolizer.size = graphic.size / 2; - } - } - if (goog.isDef(graphic.href)) { - symbolizer.url = graphic.href; - } - }, - 'ExternalGraphic': function(node, graphic) { - this.readChildNodes(node, graphic); - }, - 'Mark': function(node, graphic) { - this.readChildNodes(node, graphic); - }, - 'WellKnownName': function(node, graphic) { - graphic.graphicName = this.getChildValue(node); - }, - 'Opacity': function(node, obj) { - var ogcreaders = this.readers['http://www.opengis.net/ogc']; - obj.opacity = ogcreaders._expression.call(this, node); - }, - 'Size': function(node, obj) { - var ogcreaders = this.readers['http://www.opengis.net/ogc']; - obj.size = ogcreaders._expression.call(this, node); - }, - 'Rotation': function(node, obj) { - var ogcreaders = this.readers['http://www.opengis.net/ogc']; - obj.rotation = ogcreaders._expression.call(this, node); - }, - 'OnlineResource': function(node, obj) { - obj.href = this.getAttributeNS( - node, 'http://www.w3.org/1999/xlink', 'href' - ); - }, - 'Format': function(node, graphic) { - graphic.graphicFormat = this.getChildValue(node); + this.readers = {}; + this.readers[this.defaultNamespaceURI] = { + '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); + sld.namedLayers[layer.name] = layer; + }, + 'NamedStyle': function(node, layer) { + layer.namedStyles.push( + this.getChildValue(node.firstChild) + ); + }, + 'UserStyle': function(node, layer) { + var obj = {rules: []}; + this.featureTypeCounter = -1; + this.readChildNodes(node, obj); + layer.userStyles.push(new ol.style.Style(obj)); + }, + 'FeatureTypeStyle': function(node, style) { + ++this.featureTypeCounter; + var obj = { + rules: style.rules + }; + this.readChildNodes(node, obj); + }, + 'Rule': function(node, obj) { + var config = {symbolizers: []}; + this.readChildNodes(node, config); + var rule = new ol.style.Rule(config); + obj.rules.push(rule); + }, + 'ElseFilter': function(node, rule) { + rule.elseFilter = true; + }, + 'MinScaleDenominator': function(node, rule) { + rule.minResolution = this.getResolutionFromScaleDenominator_( + parseFloat(this.getChildValue(node))); + }, + 'MaxScaleDenominator': function(node, rule) { + rule.maxResolution = this.getResolutionFromScaleDenominator_( + parseFloat(this.getChildValue(node))); + }, + 'TextSymbolizer': function(node, rule) { + var config = {}; + this.readChildNodes(node, config); + config.color = goog.isDef(config.fill) ? config.fill.fillColor : + ol.parser.ogc.SLD_v1.defaults_.fontColor; + delete config.fill; + config.zIndex = this.featureTypeCounter; + rule.symbolizers.push( + new ol.style.Text(/** @type {ol.style.TextOptions} */(config)) + ); + }, + 'Label': function(node, symbolizer) { + var ogcreaders = this.readers[this.filter_.defaultNamespaceURI]; + var value = ogcreaders._expression.call(this, node); + if (value) { + symbolizer.text = value; } + }, + 'Font': function(node, symbolizer) { + this.readChildNodes(node, symbolizer); + }, + 'Halo': function(node, symbolizer) { + var obj = {}; + this.readChildNodes(node, obj); + symbolizer.stroke = new ol.style.Stroke({ + color: goog.isDef(obj.fill.fillColor) ? obj.fill.fillColor : + ol.parser.ogc.SLD_v1.defaults_.haloColor, + width: goog.isDef(obj.haloRadius) ? obj.haloRadius * 2 : + ol.parser.ogc.SLD_v1.defaults_.haloRadius, + opacity: goog.isDef(obj.fill.fillOpacity) ? obj.fill.fillOpacity : + ol.parser.ogc.SLD_v1.defaults_.haloOpacity + }); + }, + 'Radius': function(node, symbolizer) { + var ogcreaders = this.readers[this.filter_.defaultNamespaceURI]; + var radius = ogcreaders._expression.call(this, node); + goog.asserts.assertInstanceof(radius, ol.expr.Literal, + 'radius expected to be an ol.expr.Literal'); + if (goog.isDef(radius)) { + symbolizer.haloRadius = radius.getValue(); + } + }, + 'LineSymbolizer': function(node, rule) { + var config = {}; + this.readChildNodes(node, config); + config.zIndex = this.featureTypeCounter; + rule.symbolizers.push( + new ol.style.Stroke(config) + ); + }, + 'PolygonSymbolizer': function(node, rule) { + var config = {}; + this.readChildNodes(node, config); + config.zIndex = this.featureTypeCounter; + if (goog.isDef(config.fill)) { + var fill = { + color: config.fill.fillColor.getValue(), + opacity: goog.isDef(config.fill.fillOpacity) ? + config.fill.fillOpacity : + ol.parser.ogc.SLD_v1.defaults_.fillOpacity + }; + rule.symbolizers.push( + new ol.style.Fill(fill) + ); + delete config.fill; + } + if (goog.isDef(config.stroke)) { + var stroke = { + color: config.stroke.strokeColor.getValue(), + opacity: goog.isDef(config.stroke.strokeOpacity) ? + config.stroke.strokeOpacity : + ol.parser.ogc.SLD_v1.defaults_.strokeOpacity, + width: goog.isDef(config.stroke.strokeWidth) ? + config.stroke.strokeWidth : + ol.parser.ogc.SLD_v1.defaults_.strokeWidth + }; + rule.symbolizers.push( + new ol.style.Stroke(stroke) + ); + delete config.stroke; + } + + }, + 'PointSymbolizer': function(node, rule) { + var config = {}; + this.readChildNodes(node, config); + config.zIndex = this.featureTypeCounter; + if (config.fill) { + var fillConfig = { + color: goog.isDef(config.fill.fillColor) ? + config.fill.fillColor : + ol.parser.ogc.SLD_v1.defaults_.fillColor, + opacity: goog.isDef(config.fill.fillOpacity) ? + config.fill.fillOpacity : + ol.parser.ogc.SLD_v1.defaults_.fillOpacity + }; + config.fill = new ol.style.Fill(fillConfig); + } + if (config.stroke) { + var strokeConfig = { + color: goog.isDef(config.stroke.strokeColor) ? + config.stroke.strokeColor : + ol.parser.ogc.SLD_v1.defaults_.strokeColor, + width: goog.isDef(config.stroke.strokeWidth) ? + config.stroke.strokeWidth : + ol.parser.ogc.SLD_v1.defaults_.strokeWidth, + opacity: goog.isDef(config.stroke.strokeOpacity) ? + config.stroke.strokeOpacity : + ol.parser.ogc.SLD_v1.defaults_.strokeOpacity + }; + config.stroke = new ol.style.Stroke(strokeConfig); + } + var symbolizer; + if (goog.isDef(config.externalGraphic)) { + config.width = config.height = config.size; + symbolizer = new ol.style.Icon( + /** @type {ol.style.IconOptions} */(config)); + } else { + symbolizer = new ol.style.Shape(config); + } + rule.symbolizers.push(symbolizer); + }, + 'Stroke': function(node, symbolizer) { + var stroke = {}; + this.readChildNodes(node, stroke); + symbolizer.stroke = stroke; + }, + 'Fill': function(node, symbolizer) { + var fill = {}; + this.readChildNodes(node, fill); + symbolizer.fill = fill; + }, + 'CssParameter': function(node, symbolizer) { + var cssProperty = node.getAttribute('name'); + var symProperty = ol.parser.ogc.SLD_v1.cssMap_[cssProperty]; + if (symProperty) { + var ogcreaders = this.readers[this.filter_.defaultNamespaceURI]; + symbolizer[symProperty] = ogcreaders._expression.call(this, node); + } + }, + 'Graphic': function(node, symbolizer) { + 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', 'fill', 'rotation', 'opacity' + ]; + var prop, value; + for (var i = 0, ii = properties.length; i < ii; ++i) { + prop = properties[i]; + value = graphic[prop]; + if (goog.isDef(value)) { + symbolizer[prop] = value; + } + } + // set other generic properties with specific graphic property names + if (goog.isDef(graphic.graphicName)) { + symbolizer.type = graphic.graphicName; + } + if (goog.isDef(graphic.size)) { + var pointRadius = graphic.size / 2; + if (isNaN(pointRadius)) { + // likely a property name + symbolizer.size = graphic.size; + } else { + symbolizer.size = graphic.size / 2; + } + } + if (goog.isDef(graphic.href)) { + symbolizer.url = graphic.href; + } + }, + 'ExternalGraphic': function(node, graphic) { + this.readChildNodes(node, graphic); + }, + 'Mark': function(node, graphic) { + this.readChildNodes(node, graphic); + }, + 'WellKnownName': function(node, graphic) { + graphic.graphicName = this.getChildValue(node); + }, + 'Opacity': function(node, obj) { + var ogcreaders = this.readers[this.filter_.defaultNamespaceURI]; + obj.opacity = ogcreaders._expression.call(this, node); + }, + 'Size': function(node, obj) { + var ogcreaders = this.readers[this.filter_.defaultNamespaceURI]; + obj.size = ogcreaders._expression.call(this, node); + }, + 'Rotation': function(node, obj) { + var ogcreaders = this.readers[this.filter_.defaultNamespaceURI]; + obj.rotation = ogcreaders._expression.call(this, node); + }, + 'OnlineResource': function(node, obj) { + obj.href = this.getAttributeNS( + node, 'http://www.w3.org/1999/xlink', 'href' + ); + }, + 'Format': function(node, graphic) { + graphic.graphicFormat = this.getChildValue(node); } }; - this.writers = { - 'http://www.opengis.net/sld': { - 'StyledLayerDescriptor': function(sld) { - var node = this.createElementNS('sld:StyledLayerDescriptor'); - node.setAttribute('version', this.version); - if (goog.isDef(sld.name)) { - this.writeNode('Name', sld.name, null, node); + this.writers = {}; + this.writers[this.defaultNamespaceURI] = { + 'StyledLayerDescriptor': function(sld) { + var node = this.createElementNS('sld:StyledLayerDescriptor'); + node.setAttribute('version', this.version); + if (goog.isDef(sld.name)) { + this.writeNode('Name', sld.name, null, node); + } + if (goog.isDef(sld.title)) { + this.writeNode('Title', sld.title, null, node); + } + if (goog.isDef(sld.description)) { + this.writeNode('Abstract', sld.description, null, node); + } + goog.object.forEach(sld.namedLayers, function(layer) { + this.writeNode('NamedLayer', layer, null, node); + }, this); + return node; + }, + 'Name': function(name) { + var node = this.createElementNS('sld:Name'); + node.appendChild(this.createTextNode(name)); + return node; + }, + 'Title': function(title) { + var node = this.createElementNS('sld:Title'); + node.appendChild(this.createTextNode(title)); + return node; + }, + 'Abstract': function(description) { + var node = this.createElementNS('sld:Abstract'); + node.appendChild(this.createTextNode(description)); + return node; + }, + 'NamedLayer': function(layer) { + var node = this.createElementNS('sld:NamedLayer'); + this.writeNode('Name', layer.name, null, node); + var i, ii; + if (layer.namedStyles) { + for (i = 0, ii = layer.namedStyles.length; i < ii; ++i) { + this.writeNode('NamedStyle', layer.namedStyles[i], null, node); } - if (goog.isDef(sld.title)) { - this.writeNode('Title', sld.title, null, node); + } + if (layer.userStyles) { + for (i = 0, ii = layer.userStyles.length; i < ii; ++i) { + this.writeNode('UserStyle', layer.userStyles[i], null, node); } - if (goog.isDef(sld.description)) { - this.writeNode('Abstract', sld.description, null, node); - } - goog.object.forEach(sld.namedLayers, function(layer) { - this.writeNode('NamedLayer', layer, null, node); - }, this); - return node; - }, - 'Name': function(name) { - var node = this.createElementNS('sld:Name'); - node.appendChild(this.createTextNode(name)); - return node; - }, - 'Title': function(title) { - var node = this.createElementNS('sld:Title'); - node.appendChild(this.createTextNode(title)); - return node; - }, - 'Abstract': function(description) { - var node = this.createElementNS('sld:Abstract'); - node.appendChild(this.createTextNode(description)); - return node; - }, - 'NamedLayer': function(layer) { - var node = this.createElementNS('sld:NamedLayer'); - this.writeNode('Name', layer.name, null, node); - var i, ii; - if (layer.namedStyles) { - for (i = 0, ii = layer.namedStyles.length; i < ii; ++i) { - this.writeNode('NamedStyle', layer.namedStyles[i], null, node); - } - } - if (layer.userStyles) { - for (i = 0, ii = layer.userStyles.length; i < ii; ++i) { - this.writeNode('UserStyle', layer.userStyles[i], null, node); - } - } - return node; - }, - 'NamedStyle': function(name) { - var node = this.createElementNS('sld:NamedStyle'); + } + return node; + }, + 'NamedStyle': function(name) { + var node = this.createElementNS('sld:NamedStyle'); + this.writeNode('Name', name, null, node); + return node; + }, + 'UserStyle': function(style) { + var node = this.createElementNS('sld:UserStyle'); + var name = style.getName(), title = style.getTitle(); + if (!goog.isNull(name)) { this.writeNode('Name', name, null, node); - return node; - }, - 'UserStyle': function(style) { - var node = this.createElementNS('sld:UserStyle'); - var name = style.getName(), title = style.getTitle(); - if (!goog.isNull(name)) { - this.writeNode('Name', name, null, node); - } - if (!goog.isNull(title)) { - this.writeNode('Title', title, null, node); - } - // TODO sorting by zIndex - this.writeNode('FeatureTypeStyle', style, null, node); - return node; - }, - 'FeatureTypeStyle': function(style) { - var node = this.createElementNS('sld:FeatureTypeStyle'); - var rules = style.getRules(); - for (var i = 0, ii = rules.length; i < ii; ++i) { - this.writeNode('Rule', rules[i], null, node); - } - var symbolizers = style.getSymbolizers(); - if (symbolizers.length > 0) { - // wrap this in a Rule with an ElseFilter - var rule = new ol.style.Rule({symbolizers: symbolizers}); - rule.elseFilter = true; - this.writeNode('Rule', rule, null, node); - } - return node; - }, - 'Rule': function(rule) { - var node = this.createElementNS('sld:Rule'); - var filter = rule.getFilter(); - if (!goog.isNull(rule.getName())) { - this.writeNode('Name', rule.getName(), null, node); - } - if (!goog.isNull(rule.getTitle())) { - this.writeNode('Title', rule.title, null, node); - } - if (rule.elseFilter === true) { - this.writeNode('ElseFilter', null, null, node); - } else if (filter) { - this.writeNode('Filter', filter, 'http://www.opengis.net/ogc', node); - } - var minResolution = rule.getMinResolution(); - if (minResolution > 0) { - this.writeNode('MinScaleDenominator', - this.getScaleDenominatorFromResolution_(minResolution), - null, node); - } - var maxResolution = rule.getMaxResolution(); - if (maxResolution < Infinity) { - this.writeNode('MaxScaleDenominator', - this.getScaleDenominatorFromResolution_(maxResolution), - null, node); - } - var type, symbolizer, symbolizers = rule.getSymbolizers(); - if (symbolizers) { - for (var i = 0, ii = symbolizers.length; i < ii; ++i) { - symbolizer = symbolizers[i]; - if (symbolizer instanceof ol.style.Text) { - type = 'Text'; - } else if (symbolizer instanceof ol.style.Stroke) { - type = 'Line'; - } else if (symbolizer instanceof ol.style.Fill) { - type = 'Polygon'; - } else if (symbolizer instanceof ol.style.Shape || - symbolizer instanceof ol.style.Icon) { - type = 'Point'; - } - if (goog.isDef(type)) { - this.writeNode(type + 'Symbolizer', symbolizer, null, node); - } + } + if (!goog.isNull(title)) { + this.writeNode('Title', title, null, node); + } + // TODO sorting by zIndex + this.writeNode('FeatureTypeStyle', style, null, node); + return node; + }, + 'FeatureTypeStyle': function(style) { + var node = this.createElementNS('sld:FeatureTypeStyle'); + var rules = style.getRules(); + for (var i = 0, ii = rules.length; i < ii; ++i) { + this.writeNode('Rule', rules[i], null, node); + } + var symbolizers = style.getSymbolizers(); + if (symbolizers.length > 0) { + // wrap this in a Rule with an ElseFilter + var rule = new ol.style.Rule({symbolizers: symbolizers}); + rule.elseFilter = true; + this.writeNode('Rule', rule, null, node); + } + return node; + }, + 'Rule': function(rule) { + var node = this.createElementNS('sld:Rule'); + var filter = rule.getFilter(); + if (!goog.isNull(rule.getName())) { + this.writeNode('Name', rule.getName(), null, node); + } + if (!goog.isNull(rule.getTitle())) { + this.writeNode('Title', rule.title, null, node); + } + if (rule.elseFilter === true) { + this.writeNode('ElseFilter', null, null, node); + } else if (filter) { + this.writeNode('Filter', filter, this.filter_.defaultNamespaceURI, + node); + } + var minResolution = rule.getMinResolution(); + if (minResolution > 0) { + this.writeNode('MinScaleDenominator', + this.getScaleDenominatorFromResolution_(minResolution), + null, node); + } + var maxResolution = rule.getMaxResolution(); + if (maxResolution < Infinity) { + this.writeNode('MaxScaleDenominator', + this.getScaleDenominatorFromResolution_(maxResolution), + null, node); + } + var type, symbolizer, symbolizers = rule.getSymbolizers(); + if (symbolizers) { + for (var i = 0, ii = symbolizers.length; i < ii; ++i) { + symbolizer = symbolizers[i]; + if (symbolizer instanceof ol.style.Text) { + type = 'Text'; + } else if (symbolizer instanceof ol.style.Stroke) { + type = 'Line'; + } else if (symbolizer instanceof ol.style.Fill) { + type = 'Polygon'; + } else if (symbolizer instanceof ol.style.Shape || + symbolizer instanceof ol.style.Icon) { + type = 'Point'; + } + if (goog.isDef(type)) { + this.writeNode(type + 'Symbolizer', symbolizer, null, node); } } - return node; - }, - 'PointSymbolizer': function(symbolizer) { - var node = this.createElementNS('sld:PointSymbolizer'); - this.writeNode('Graphic', symbolizer, null, node); - return node; - }, - 'Mark': function(symbolizer) { - var node = this.createElementNS('sld:Mark'); - this.writeNode('WellKnownName', symbolizer.getType(), null, node); - var fill = symbolizer.getFill(); - if (!goog.isNull(fill)) { - this.writeNode('Fill', fill, null, node); - } - var stroke = symbolizer.getStroke(); - if (!goog.isNull(stroke)) { - this.writeNode('Stroke', stroke, null, node); - } - return node; - }, - 'WellKnownName': function(name) { - var node = this.createElementNS('sld:WellKnownName'); - node.appendChild(this.createTextNode(name)); - return node; - }, - 'Graphic': function(symbolizer) { - var node = this.createElementNS('sld:Graphic'); - var size; - if (symbolizer instanceof ol.style.Icon) { - this.writeNode('ExternalGraphic', symbolizer, null, node); - var opacity = symbolizer.getOpacity(); - goog.asserts.assertInstanceof(opacity, ol.expr.Literal, - 'Only ol.expr.Literal supported for graphicOpacity'); - this.writeNode('Opacity', opacity.getValue(), null, node); - size = symbolizer.getWidth(); - } else if (symbolizer instanceof ol.style.Shape) { - this.writeNode('Mark', symbolizer, null, node); - size = symbolizer.getSize(); - } - this.writeNode('Size', size, null, node); - if (symbolizer instanceof ol.style.Icon) { - var rotation = symbolizer.getRotation(); - goog.asserts.assertInstanceof(rotation, ol.expr.Literal, - 'Only ol.expr.Literal supported for rotation'); - this.writeNode('Rotation', rotation.getValue(), null, node); - } - return node; - }, - 'PolygonSymbolizer': function(symbolizer) { - var node = this.createElementNS('sld:PolygonSymbolizer'); - this.writeNode('Fill', symbolizer, null, node); - return node; - }, - 'Fill': function(symbolizer) { - var node = this.createElementNS('sld:Fill'); - var fillColor = symbolizer.getColor(); - var msg = 'Only ol.expr.Literal supported for Fill properties'; - goog.asserts.assertInstanceof(fillColor, ol.expr.Literal, msg); - this.writeNode('CssParameter', { - value: fillColor.getValue(), - key: 'fillColor' - }, null, node); - var fillOpacity = symbolizer.getOpacity(); - goog.asserts.assertInstanceof(fillOpacity, ol.expr.Literal, msg); - this.writeNode('CssParameter', { - value: fillOpacity.getValue(), - key: 'fillOpacity' - }, null, node); - return node; - }, - 'TextSymbolizer': function(symbolizer) { - var node = this.createElementNS('sld:TextSymbolizer'); - var text = symbolizer.getText(); - this.writeNode('Label', text, null, node); - this.writeNode('Font', symbolizer, null, node); - var stroke = symbolizer.getStroke(); - if (!goog.isNull(stroke)) { - this.writeNode('Halo', stroke, null, node); - } - var color = symbolizer.getColor(); - goog.asserts.assertInstanceof(color, ol.expr.Literal, - 'font color should be ol.expr.Literal'); - this.writeNode('Fill', symbolizer, null, node); - return node; - }, - 'Halo': function(symbolizer) { - var node = this.createElementNS('sld:Halo'); - goog.asserts.assertInstanceof(symbolizer.getWidth(), ol.expr.Literal, - 'Only ol.expr.Literal supported for haloRadius'); - this.writeNode('Radius', symbolizer.getWidth().getValue() / 2, null, - node); - this.writeNode('Fill', symbolizer, null, node); - return node; - }, - 'Radius': function(value) { - var node = this.createElementNS('sld:Radius'); - node.appendChild(this.createTextNode(value)); - return node; - }, - 'LineSymbolizer': function(symbolizer) { - var node = this.createElementNS('sld:LineSymbolizer'); - this.writeNode('Stroke', symbolizer, null, node); - return node; - }, - 'Stroke': function(symbolizer) { - var node = this.createElementNS('sld:Stroke'); - var strokeColor = symbolizer.getColor(); - var msg = 'SLD writing of stroke properties only supported ' + - 'for ol.expr.Literal'; - goog.asserts.assertInstanceof(strokeColor, ol.expr.Literal, msg); - this.writeNode('CssParameter', { - value: strokeColor.getValue(), - key: 'strokeColor' - }, null, node); - var strokeOpacity = symbolizer.getOpacity(); - goog.asserts.assertInstanceof(strokeOpacity, ol.expr.Literal, msg); - this.writeNode('CssParameter', { - value: strokeOpacity.getValue(), - key: 'strokeOpacity' - }, null, node); - var strokeWidth = symbolizer.getWidth(); - goog.asserts.assertInstanceof(strokeWidth, ol.expr.Literal, msg); - this.writeNode('CssParameter', { - value: strokeWidth.getValue(), - key: 'strokeWidth' - }, null, node); - // TODO strokeDashstyle and strokeLinecap - return node; - }, - 'CssParameter': function(obj) { - // not handling ogc:expressions for now - var name = ol.parser.ogc.SLD_v1.getCssProperty_(obj.key); - if (goog.isDef(name)) { - var node = this.createElementNS('sld:CssParameter'); - node.setAttribute('name', name); - node.appendChild(this.createTextNode(obj.value)); - return node; - } - }, - 'Label': function(label) { - var node = this.createElementNS('sld:Label'); - this.filter_.writeOgcExpression(label, node); - return node; - }, - 'Font': function(symbolizer) { - var node = this.createElementNS('sld:Font'); - this.writeNode('CssParameter', { - key: 'fontFamily', - value: symbolizer.getFontFamily().getValue() - }, null, node); - this.writeNode('CssParameter', { - key: 'fontSize', - value: symbolizer.getFontSize().getValue() - }, null, node); - // TODO fontWeight and fontStyle - return node; - }, - 'MinScaleDenominator': function(scale) { - var node = this.createElementNS('sld:MinScaleDenominator'); - node.appendChild(this.createTextNode(scale)); - return node; - }, - 'MaxScaleDenominator': function(scale) { - var node = this.createElementNS('sld:MaxScaleDenominator'); - node.appendChild(this.createTextNode(scale)); - return node; - }, - 'Size': function(value) { - var node = this.createElementNS('sld:Size'); - this.filter_.writeOgcExpression(value, node); + } + return node; + }, + 'PointSymbolizer': function(symbolizer) { + var node = this.createElementNS('sld:PointSymbolizer'); + this.writeNode('Graphic', symbolizer, null, node); + return node; + }, + 'Mark': function(symbolizer) { + var node = this.createElementNS('sld:Mark'); + this.writeNode('WellKnownName', symbolizer.getType(), null, node); + var fill = symbolizer.getFill(); + if (!goog.isNull(fill)) { + this.writeNode('Fill', fill, null, node); + } + var stroke = symbolizer.getStroke(); + if (!goog.isNull(stroke)) { + this.writeNode('Stroke', stroke, null, node); + } + return node; + }, + 'WellKnownName': function(name) { + var node = this.createElementNS('sld:WellKnownName'); + node.appendChild(this.createTextNode(name)); + return node; + }, + 'Graphic': function(symbolizer) { + var node = this.createElementNS('sld:Graphic'); + var size; + if (symbolizer instanceof ol.style.Icon) { + this.writeNode('ExternalGraphic', symbolizer, null, node); + var opacity = symbolizer.getOpacity(); + goog.asserts.assertInstanceof(opacity, ol.expr.Literal, + 'Only ol.expr.Literal supported for graphicOpacity'); + this.writeNode('Opacity', opacity.getValue(), null, node); + size = symbolizer.getWidth(); + } else if (symbolizer instanceof ol.style.Shape) { + this.writeNode('Mark', symbolizer, null, node); + size = symbolizer.getSize(); + } + this.writeNode('Size', size, null, node); + if (symbolizer instanceof ol.style.Icon) { + var rotation = symbolizer.getRotation(); + goog.asserts.assertInstanceof(rotation, ol.expr.Literal, + 'Only ol.expr.Literal supported for rotation'); + this.writeNode('Rotation', rotation.getValue(), null, node); + } + return node; + }, + 'PolygonSymbolizer': function(symbolizer) { + var node = this.createElementNS('sld:PolygonSymbolizer'); + this.writeNode('Fill', symbolizer, null, node); + return node; + }, + 'Fill': function(symbolizer) { + var node = this.createElementNS('sld:Fill'); + var fillColor = symbolizer.getColor(); + var msg = 'Only ol.expr.Literal supported for Fill properties'; + goog.asserts.assertInstanceof(fillColor, ol.expr.Literal, msg); + this.writeNode('CssParameter', { + value: fillColor.getValue(), + key: 'fillColor' + }, null, node); + var fillOpacity = symbolizer.getOpacity(); + goog.asserts.assertInstanceof(fillOpacity, ol.expr.Literal, msg); + this.writeNode('CssParameter', { + value: fillOpacity.getValue(), + key: 'fillOpacity' + }, null, node); + return node; + }, + 'TextSymbolizer': function(symbolizer) { + var node = this.createElementNS('sld:TextSymbolizer'); + var text = symbolizer.getText(); + this.writeNode('Label', text, null, node); + this.writeNode('Font', symbolizer, null, node); + var stroke = symbolizer.getStroke(); + if (!goog.isNull(stroke)) { + this.writeNode('Halo', stroke, null, node); + } + var color = symbolizer.getColor(); + goog.asserts.assertInstanceof(color, ol.expr.Literal, + 'font color should be ol.expr.Literal'); + this.writeNode('Fill', symbolizer, null, node); + return node; + }, + 'Halo': function(symbolizer) { + var node = this.createElementNS('sld:Halo'); + goog.asserts.assertInstanceof(symbolizer.getWidth(), ol.expr.Literal, + 'Only ol.expr.Literal supported for haloRadius'); + this.writeNode('Radius', symbolizer.getWidth().getValue() / 2, null, + node); + this.writeNode('Fill', symbolizer, null, node); + return node; + }, + 'Radius': function(value) { + var node = this.createElementNS('sld:Radius'); + node.appendChild(this.createTextNode(value)); + return node; + }, + 'LineSymbolizer': function(symbolizer) { + var node = this.createElementNS('sld:LineSymbolizer'); + this.writeNode('Stroke', symbolizer, null, node); + return node; + }, + 'Stroke': function(symbolizer) { + var node = this.createElementNS('sld:Stroke'); + var strokeColor = symbolizer.getColor(); + var msg = 'SLD writing of stroke properties only supported ' + + 'for ol.expr.Literal'; + goog.asserts.assertInstanceof(strokeColor, ol.expr.Literal, msg); + this.writeNode('CssParameter', { + value: strokeColor.getValue(), + key: 'strokeColor' + }, null, node); + var strokeOpacity = symbolizer.getOpacity(); + goog.asserts.assertInstanceof(strokeOpacity, ol.expr.Literal, msg); + this.writeNode('CssParameter', { + value: strokeOpacity.getValue(), + key: 'strokeOpacity' + }, null, node); + var strokeWidth = symbolizer.getWidth(); + goog.asserts.assertInstanceof(strokeWidth, ol.expr.Literal, msg); + this.writeNode('CssParameter', { + value: strokeWidth.getValue(), + key: 'strokeWidth' + }, null, node); + // TODO strokeDashstyle and strokeLinecap + return node; + }, + 'CssParameter': function(obj) { + // not handling ogc:expressions for now + var name = ol.parser.ogc.SLD_v1.getCssProperty_(obj.key); + if (goog.isDef(name)) { + var node = this.createElementNS('sld:CssParameter'); + node.setAttribute('name', name); + node.appendChild(this.createTextNode(obj.value)); return node; } + }, + 'Label': function(label) { + var node = this.createElementNS('sld:Label'); + this.filter_.writeOgcExpression(label, node); + return node; + }, + 'Font': function(symbolizer) { + var node = this.createElementNS('sld:Font'); + this.writeNode('CssParameter', { + key: 'fontFamily', + value: symbolizer.getFontFamily().getValue() + }, null, node); + this.writeNode('CssParameter', { + key: 'fontSize', + value: symbolizer.getFontSize().getValue() + }, null, node); + // TODO fontWeight and fontStyle + return node; + }, + 'MinScaleDenominator': function(scale) { + var node = this.createElementNS('sld:MinScaleDenominator'); + node.appendChild(this.createTextNode(scale)); + return node; + }, + 'MaxScaleDenominator': function(scale) { + var node = this.createElementNS('sld:MaxScaleDenominator'); + node.appendChild(this.createTextNode(scale)); + return node; + }, + 'Size': function(value) { + var node = this.createElementNS('sld:Size'); + this.filter_.writeOgcExpression(value, node); + return node; } }; this.filter_ = new ol.parser.ogc.Filter_v1_0_0(); From 25ae9fe78436e453372beccdf424fe18a0df7712 Mon Sep 17 00:00:00 2001 From: Bart van den Eijnden Date: Mon, 25 Nov 2013 10:48:30 +0100 Subject: [PATCH 12/12] change simple type values to undefined instead of null --- src/ol/parser/ogc/sldparser_v1.js | 13 +++++++------ src/ol/style/rule.js | 12 ++++++------ src/ol/style/style.js | 12 ++++++------ 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/ol/parser/ogc/sldparser_v1.js b/src/ol/parser/ogc/sldparser_v1.js index ed9757b038..a1516d8c5a 100644 --- a/src/ol/parser/ogc/sldparser_v1.js +++ b/src/ol/parser/ogc/sldparser_v1.js @@ -342,10 +342,10 @@ ol.parser.ogc.SLD_v1 = function() { 'UserStyle': function(style) { var node = this.createElementNS('sld:UserStyle'); var name = style.getName(), title = style.getTitle(); - if (!goog.isNull(name)) { + if (goog.isDef(name)) { this.writeNode('Name', name, null, node); } - if (!goog.isNull(title)) { + if (goog.isDef(title)) { this.writeNode('Title', title, null, node); } // TODO sorting by zIndex @@ -370,11 +370,12 @@ ol.parser.ogc.SLD_v1 = function() { 'Rule': function(rule) { var node = this.createElementNS('sld:Rule'); var filter = rule.getFilter(); - if (!goog.isNull(rule.getName())) { - this.writeNode('Name', rule.getName(), null, node); + var name = rule.getName(), title = rule.getTitle(); + if (goog.isDef(name)) { + this.writeNode('Name', name, null, node); } - if (!goog.isNull(rule.getTitle())) { - this.writeNode('Title', rule.title, null, node); + if (goog.isDef(title)) { + this.writeNode('Title', title, null, node); } if (rule.elseFilter === true) { this.writeNode('ElseFilter', null, null, node); diff --git a/src/ol/style/rule.js b/src/ol/style/rule.js index 3495422459..d8234de95b 100644 --- a/src/ol/style/rule.js +++ b/src/ol/style/rule.js @@ -55,18 +55,18 @@ ol.style.Rule = function(options) { options.maxResolution : Infinity; /** - * @type {?string} + * @type {string|undefined} * @private */ this.name_ = goog.isDef(options.name) ? - options.name : null; + options.name : undefined; /** - * @type {?string} + * @type {string|undefined} * @private */ this.title_ = goog.isDef(options.title) ? - options.title : null; + options.title : undefined; }; @@ -119,7 +119,7 @@ ol.style.Rule.prototype.getMaxResolution = function() { /** - * @return {?string} + * @return {string|undefined} */ ol.style.Rule.prototype.getName = function() { return this.name_; @@ -127,7 +127,7 @@ ol.style.Rule.prototype.getName = function() { /** - * @return {?string} + * @return {string|undefined} */ ol.style.Rule.prototype.getTitle = function() { return this.title_; diff --git a/src/ol/style/style.js b/src/ol/style/style.js index 224ec2578c..c8d70ef635 100644 --- a/src/ol/style/style.js +++ b/src/ol/style/style.js @@ -41,18 +41,18 @@ ol.style.Style = function(options) { options.symbolizers : []; /** - * @type {?string} + * @type {string|undefined} * @private */ this.name_ = goog.isDef(options.name) ? - options.name : null; + options.name : undefined; /** - * @type {?string} + * @type {string|undefined} * @private */ this.title_ = goog.isDef(options.title) ? - options.title : null; + options.title : undefined; }; @@ -247,7 +247,7 @@ ol.style.Style.prototype.getSymbolizers = function() { /** - * @return {?string} + * @return {string|undefined} */ ol.style.Style.prototype.getName = function() { return this.name_; @@ -255,7 +255,7 @@ ol.style.Style.prototype.getName = function() { /** - * @return {?string} + * @return {string|undefined} */ ol.style.Style.prototype.getTitle = function() { return this.title_;