Support for basic literal/propertyname combinations in SLD. r=elemoine (closes http://trac.osgeo.org/openlayers/ticket/3506)

This commit is contained in:
ahocevar
2011-11-13 22:04:07 +01:00
parent 53963fabe0
commit 754312b20b
3 changed files with 87 additions and 71 deletions
+23 -22
View File
@@ -77,6 +77,27 @@ OpenLayers.Format.Filter.v1 = OpenLayers.Class(OpenLayers.Format.XML, {
*/ */
readers: { readers: {
"ogc": { "ogc": {
"_expression": function(node) {
// only the simplest of ogc:expression handled
// "some text and an <PropertyName>attribute</PropertyName>"}
var obj, value = "";
for(var child=node.firstChild; child; child=child.nextSibling) {
switch(child.nodeType) {
case 1:
obj = this.readNode(child);
if (obj.property) {
value += "${" + obj.property + "}";
} else if (obj.value !== undefined) {
value += obj.value;
}
break;
case 3: // text node
case 4: // cdata section
value += child.nodeValue;
}
}
return value;
},
"Filter": function(node, parent) { "Filter": function(node, parent) {
// Filters correspond to subclasses of OpenLayers.Filter. // Filters correspond to subclasses of OpenLayers.Filter.
// Since they contain information we don't persist, we // Since they contain information we don't persist, we
@@ -166,11 +187,11 @@ OpenLayers.Format.Filter.v1 = OpenLayers.Class(OpenLayers.Format.XML, {
}, },
"LowerBoundary": function(node, filter) { "LowerBoundary": function(node, filter) {
filter.lowerBoundary = OpenLayers.String.numericIf( filter.lowerBoundary = OpenLayers.String.numericIf(
this.readOgcExpression(node)); this.readers.ogc._expression.call(this, node));
}, },
"UpperBoundary": function(node, filter) { "UpperBoundary": function(node, filter) {
filter.upperBoundary = OpenLayers.String.numericIf( filter.upperBoundary = OpenLayers.String.numericIf(
this.readOgcExpression(node)); this.readers.ogc._expression.call(this, node));
}, },
"Intersects": function(node, obj) { "Intersects": function(node, obj) {
this.readSpatial(node, obj, OpenLayers.Filter.Spatial.INTERSECTS); this.readSpatial(node, obj, OpenLayers.Filter.Spatial.INTERSECTS);
@@ -218,26 +239,6 @@ OpenLayers.Format.Filter.v1 = OpenLayers.Class(OpenLayers.Format.XML, {
obj.filters.push(filter); obj.filters.push(filter);
}, },
/**
* Method: readOgcExpression
* Limited support for OGC expressions.
*
* Parameters:
* node - {DOMElement} A DOM element that contains an ogc:expression.
*
* Returns:
* {String} A value to be used in a symbolizer.
*/
readOgcExpression: function(node) {
var obj = {};
this.readChildNodes(node, obj);
var value = obj.value;
if(value === undefined) {
value = this.getChildValue(node);
}
return value;
},
/** /**
* Method: writeOgcExpression * Method: writeOgcExpression
* Limited support for writing OGC expressions. Currently it supports * Limited support for writing OGC expressions. Currently it supports
+53 -45
View File
@@ -219,16 +219,9 @@ OpenLayers.Format.SLD.v1 = OpenLayers.Class(OpenLayers.Format.Filter.v1_0_0, {
} }
}, },
"Label": function(node, symbolizer) { "Label": function(node, symbolizer) {
// only supporting literal or property name var value = this.readers.ogc._expression.call(this, node);
var obj = {}; if (value) {
this.readChildNodes(node, obj); symbolizer.label = value;
if(obj.property) {
symbolizer.label = "${" + obj.property + "}";
} else {
var value = this.readOgcExpression(node);
if(value) {
symbolizer.label = value;
}
} }
}, },
"Font": function(node, symbolizer) { "Font": function(node, symbolizer) {
@@ -243,7 +236,7 @@ OpenLayers.Format.SLD.v1 = OpenLayers.Class(OpenLayers.Format.Filter.v1_0_0, {
symbolizer.haloOpacity = obj.fillOpacity; symbolizer.haloOpacity = obj.fillOpacity;
}, },
"Radius": function(node, symbolizer) { "Radius": function(node, symbolizer) {
var radius = this.readOgcExpression(node); var radius = this.readers.ogc._expression.call(this, node);
if(radius != null) { if(radius != null) {
// radius is only used for halo // radius is only used for halo
symbolizer.haloRadius = radius; symbolizer.haloRadius = radius;
@@ -345,7 +338,7 @@ OpenLayers.Format.SLD.v1 = OpenLayers.Class(OpenLayers.Format.Filter.v1_0_0, {
var symProperty = this.cssMap[cssProperty]; var symProperty = this.cssMap[cssProperty];
if(symProperty) { if(symProperty) {
// Limited support for parsing of OGC expressions // Limited support for parsing of OGC expressions
var value = this.readOgcExpression(node); var value = this.readers.ogc._expression.call(this, node);
// always string, could be an empty string // always string, could be an empty string
if(value) { if(value) {
symbolizer[symProperty] = value; symbolizer[symProperty] = value;
@@ -376,7 +369,13 @@ OpenLayers.Format.SLD.v1 = OpenLayers.Class(OpenLayers.Format.Filter.v1_0_0, {
symbolizer.graphicOpacity = graphic.opacity; symbolizer.graphicOpacity = graphic.opacity;
} }
if(graphic.size != undefined) { if(graphic.size != undefined) {
symbolizer.pointRadius = graphic.size / 2; var pointRadius = graphic.size / 2;
if (isNaN(pointRadius)) {
// likely a property name
symbolizer.graphicWidth = graphic.size;
} else {
symbolizer.pointRadius = graphic.size / 2;
}
} }
if(graphic.href != undefined) { if(graphic.href != undefined) {
symbolizer.externalGraphic = graphic.href; symbolizer.externalGraphic = graphic.href;
@@ -395,21 +394,21 @@ OpenLayers.Format.SLD.v1 = OpenLayers.Class(OpenLayers.Format.Filter.v1_0_0, {
graphic.graphicName = this.getChildValue(node); graphic.graphicName = this.getChildValue(node);
}, },
"Opacity": function(node, obj) { "Opacity": function(node, obj) {
var opacity = this.readOgcExpression(node); var opacity = this.readers.ogc._expression.call(this, node);
// always string, could be empty string // always string, could be empty string
if(opacity) { if(opacity) {
obj.opacity = opacity; obj.opacity = opacity;
} }
}, },
"Size": function(node, obj) { "Size": function(node, obj) {
var size = this.readOgcExpression(node); var size = this.readers.ogc._expression.call(this, node);
// always string, could be empty string // always string, could be empty string
if(size) { if(size) {
obj.size = size; obj.size = size;
} }
}, },
"Rotation": function(node, obj) { "Rotation": function(node, obj) {
var rotation = this.readOgcExpression(node); var rotation = this.readers.ogc._expression.call(this, node);
// always string, could be empty string // always string, could be empty string
if(rotation) { if(rotation) {
obj.rotation = rotation; obj.rotation = rotation;
@@ -530,6 +529,36 @@ OpenLayers.Format.SLD.v1 = OpenLayers.Class(OpenLayers.Format.Filter.v1_0_0, {
*/ */
writers: OpenLayers.Util.applyDefaults({ writers: OpenLayers.Util.applyDefaults({
"sld": { "sld": {
"_OGCExpression": function(nodeName, value) {
// only the simplest of ogc:expression handled
// {label: "some text and a ${propertyName}"}
var node = this.createElementNSPlus(nodeName);
var tokens = typeof value == "string" ?
value.split("${") :
[value];
node.appendChild(this.createTextNode(tokens[0]));
var item, last;
for(var i=1, len=tokens.length; i<len; i++) {
item = tokens[i];
last = item.indexOf("}");
if(last > 0) {
this.writeNode(
"ogc:PropertyName",
{property: item.substring(0, last)},
node
);
node.appendChild(
this.createTextNode(item.substring(++last))
);
} else {
// no ending }, so this is a literal ${
node.appendChild(
this.createTextNode("${" + item)
);
}
}
return node;
},
"StyledLayerDescriptor": function(sld) { "StyledLayerDescriptor": function(sld) {
var root = this.createElementNSPlus( var root = this.createElementNSPlus(
"sld:StyledLayerDescriptor", "sld:StyledLayerDescriptor",
@@ -897,32 +926,9 @@ OpenLayers.Format.SLD.v1 = OpenLayers.Class(OpenLayers.Format.Filter.v1_0_0, {
return node; return node;
}, },
"Label": function(label) { "Label": function(label) {
// only the simplest of ogc:expression handled return this.writers.sld._OGCExpression.call(
// {label: "some text and a ${propertyName}"} this, "sld:Label", label
var node = this.createElementNSPlus("sld:Label"); );
var tokens = label.split("${");
node.appendChild(this.createTextNode(tokens[0]));
var item, last;
for(var i=1, len=tokens.length; i<len; i++) {
item = tokens[i];
last = item.indexOf("}");
if(last > 0) {
this.writeNode(
"ogc:PropertyName",
{property: item.substring(0, last)},
node
);
node.appendChild(
this.createTextNode(item.substring(++last))
);
} else {
// no ending }, so this is a literal ${
node.appendChild(
this.createTextNode("${" + item)
);
}
}
return node;
}, },
"Halo": function(symbolizer) { "Halo": function(symbolizer) {
var node = this.createElementNSPlus("sld:Halo"); var node = this.createElementNSPlus("sld:Halo");
@@ -1030,6 +1036,8 @@ OpenLayers.Format.SLD.v1 = OpenLayers.Class(OpenLayers.Format.Filter.v1_0_0, {
} }
if(symbolizer.pointRadius != undefined) { if(symbolizer.pointRadius != undefined) {
this.writeNode("Size", symbolizer.pointRadius * 2, node); this.writeNode("Size", symbolizer.pointRadius * 2, node);
} else if (symbolizer.graphicWidth != undefined) {
this.writeNode("Size", symbolizer.graphicWidth, node);
} }
if(symbolizer.rotation != undefined) { if(symbolizer.rotation != undefined) {
this.writeNode("Rotation", symbolizer.rotation, node); this.writeNode("Rotation", symbolizer.rotation, node);
@@ -1070,9 +1078,9 @@ OpenLayers.Format.SLD.v1 = OpenLayers.Class(OpenLayers.Format.Filter.v1_0_0, {
}); });
}, },
"Size": function(value) { "Size": function(value) {
return this.createElementNSPlus("sld:Size", { return this.writers.sld._OGCExpression.call(
value: value this, "sld:Size", value
}); );
}, },
"Rotation": function(value) { "Rotation": function(value) {
return this.createElementNSPlus("sld:Rotation", { return this.createElementNSPlus("sld:Rotation", {
+11 -4
View File
@@ -39,7 +39,7 @@
'</PolygonSymbolizer>' + '</PolygonSymbolizer>' +
'<TextSymbolizer>' + '<TextSymbolizer>' +
'<Label>' + '<Label>' +
'<ogc:PropertyName>FOO</ogc:PropertyName>' + 'A <ogc:PropertyName>FOO</ogc:PropertyName> label' +
'</Label>' + '</Label>' +
'<Font>' + '<Font>' +
'<CssParameter name="font-family">Arial</CssParameter>' + '<CssParameter name="font-family">Arial</CssParameter>' +
@@ -121,6 +121,7 @@
'<CssParameter name="stroke-width">2</CssParameter>' + '<CssParameter name="stroke-width">2</CssParameter>' +
'</Stroke>' + '</Stroke>' +
'</Mark>' + '</Mark>' +
'<Size><ogc:PropertyName>SIZE</ogc:PropertyName></Size>' +
'</Graphic>' + '</Graphic>' +
'</PointSymbolizer>' + '</PointSymbolizer>' +
'</Rule>' + '</Rule>' +
@@ -130,7 +131,7 @@
'</StyledLayerDescriptor>'; '</StyledLayerDescriptor>';
function test_read(t) { function test_read(t) {
t.plan(22); t.plan(23);
var xml = new OpenLayers.Format.XML(); var xml = new OpenLayers.Format.XML();
var sldxml = xml.read(sld); var sldxml = xml.read(sld);
@@ -173,7 +174,7 @@
t.eq(poly.fillColor, "#ffffff", "(AAA161) first rule has proper fill"); t.eq(poly.fillColor, "#ffffff", "(AAA161) first rule has proper fill");
t.eq(poly.strokeColor, "#000000", "(AAA161) first rule has proper stroke"); t.eq(poly.strokeColor, "#000000", "(AAA161) first rule has proper stroke");
var text = symbolizer["Text"]; var text = symbolizer["Text"];
t.eq(text.label, "${FOO}", "(AAA161) first rule has proper text label"); t.eq(text.label, "A ${FOO} label", "(AAA161) first rule has proper text label");
t.eq(layer.userStyles[0].propertyStyles["label"], true, "label added to propertyStyles"); t.eq(layer.userStyles[0].propertyStyles["label"], true, "label added to propertyStyles");
t.eq(text.fontFamily, "Arial", "(AAA161) first rule has proper font family"); t.eq(text.fontFamily, "Arial", "(AAA161) first rule has proper font family");
t.eq(text.fillColor, "#000000", "(AAA161) first rule has proper text fill"); t.eq(text.fillColor, "#000000", "(AAA161) first rule has proper text fill");
@@ -203,6 +204,12 @@
t.ok(typeof rule.maxScaleDenominator == "number", "MaxScaleDenominator is a number"); t.ok(typeof rule.maxScaleDenominator == "number", "MaxScaleDenominator is a number");
t.eq(rule.evaluate(feature), true, "numeric filter comparison evaluates correctly"); t.eq(rule.evaluate(feature), true, "numeric filter comparison evaluates correctly");
// check for PropertyName size
layer = obj.namedLayers["Second Layer"];
style = layer.userStyles[0];
rule = style.rules[0];
t.eq(rule.symbolizer["Point"].graphicWidth, "${SIZE}", "dynamic size correctly set on graphicWidth");
// etc. I'm convinced read works, really wanted to test write (since examples don't test that) // etc. I'm convinced read works, really wanted to test write (since examples don't test that)
// I'll add more tests here later. // I'll add more tests here later.
@@ -211,7 +218,7 @@
function test_write(t) { function test_write(t) {
t.plan(3); t.plan(3);
// read first - testing that write produces the SLD aboce // read first - testing that write produces the SLD above
var parser = new OpenLayers.Format.SLD.v1_0_0(); var parser = new OpenLayers.Format.SLD.v1_0_0();
var xml = new OpenLayers.Format.XML(); var xml = new OpenLayers.Format.XML();
var sldxml = xml.read(sld); var sldxml = xml.read(sld);