Support for basic literal/propertyname combinations in SLD. r=elemoine (closes http://trac.osgeo.org/openlayers/ticket/3506)
This commit is contained in:
@@ -77,6 +77,27 @@ OpenLayers.Format.Filter.v1 = OpenLayers.Class(OpenLayers.Format.XML, {
|
||||
*/
|
||||
readers: {
|
||||
"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) {
|
||||
// Filters correspond to subclasses of OpenLayers.Filter.
|
||||
// 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) {
|
||||
filter.lowerBoundary = OpenLayers.String.numericIf(
|
||||
this.readOgcExpression(node));
|
||||
this.readers.ogc._expression.call(this, node));
|
||||
},
|
||||
"UpperBoundary": function(node, filter) {
|
||||
filter.upperBoundary = OpenLayers.String.numericIf(
|
||||
this.readOgcExpression(node));
|
||||
this.readers.ogc._expression.call(this, node));
|
||||
},
|
||||
"Intersects": function(node, obj) {
|
||||
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);
|
||||
},
|
||||
|
||||
/**
|
||||
* 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
|
||||
* Limited support for writing OGC expressions. Currently it supports
|
||||
|
||||
@@ -219,16 +219,9 @@ OpenLayers.Format.SLD.v1 = OpenLayers.Class(OpenLayers.Format.Filter.v1_0_0, {
|
||||
}
|
||||
},
|
||||
"Label": function(node, symbolizer) {
|
||||
// only supporting literal or property name
|
||||
var obj = {};
|
||||
this.readChildNodes(node, obj);
|
||||
if(obj.property) {
|
||||
symbolizer.label = "${" + obj.property + "}";
|
||||
} else {
|
||||
var value = this.readOgcExpression(node);
|
||||
if(value) {
|
||||
symbolizer.label = value;
|
||||
}
|
||||
var value = this.readers.ogc._expression.call(this, node);
|
||||
if (value) {
|
||||
symbolizer.label = value;
|
||||
}
|
||||
},
|
||||
"Font": function(node, symbolizer) {
|
||||
@@ -243,7 +236,7 @@ OpenLayers.Format.SLD.v1 = OpenLayers.Class(OpenLayers.Format.Filter.v1_0_0, {
|
||||
symbolizer.haloOpacity = obj.fillOpacity;
|
||||
},
|
||||
"Radius": function(node, symbolizer) {
|
||||
var radius = this.readOgcExpression(node);
|
||||
var radius = this.readers.ogc._expression.call(this, node);
|
||||
if(radius != null) {
|
||||
// radius is only used for halo
|
||||
symbolizer.haloRadius = radius;
|
||||
@@ -345,7 +338,7 @@ OpenLayers.Format.SLD.v1 = OpenLayers.Class(OpenLayers.Format.Filter.v1_0_0, {
|
||||
var symProperty = this.cssMap[cssProperty];
|
||||
if(symProperty) {
|
||||
// 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
|
||||
if(value) {
|
||||
symbolizer[symProperty] = value;
|
||||
@@ -376,7 +369,13 @@ OpenLayers.Format.SLD.v1 = OpenLayers.Class(OpenLayers.Format.Filter.v1_0_0, {
|
||||
symbolizer.graphicOpacity = graphic.opacity;
|
||||
}
|
||||
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) {
|
||||
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);
|
||||
},
|
||||
"Opacity": function(node, obj) {
|
||||
var opacity = this.readOgcExpression(node);
|
||||
var opacity = this.readers.ogc._expression.call(this, node);
|
||||
// always string, could be empty string
|
||||
if(opacity) {
|
||||
obj.opacity = opacity;
|
||||
}
|
||||
},
|
||||
"Size": function(node, obj) {
|
||||
var size = this.readOgcExpression(node);
|
||||
var size = this.readers.ogc._expression.call(this, node);
|
||||
// always string, could be empty string
|
||||
if(size) {
|
||||
obj.size = size;
|
||||
}
|
||||
},
|
||||
"Rotation": function(node, obj) {
|
||||
var rotation = this.readOgcExpression(node);
|
||||
var rotation = this.readers.ogc._expression.call(this, node);
|
||||
// always string, could be empty string
|
||||
if(rotation) {
|
||||
obj.rotation = rotation;
|
||||
@@ -530,6 +529,36 @@ OpenLayers.Format.SLD.v1 = OpenLayers.Class(OpenLayers.Format.Filter.v1_0_0, {
|
||||
*/
|
||||
writers: OpenLayers.Util.applyDefaults({
|
||||
"sld": {
|
||||
"_OGCExpression": function(nodeName, value) {
|
||||
// only the simplest of ogc:expression handled
|
||||
// {label: "some text and a ${propertyName}"}
|
||||
var node = this.createElementNSPlus(nodeName);
|
||||
var tokens = typeof value == "string" ?
|
||||
value.split("${") :
|
||||
[value];
|
||||
node.appendChild(this.createTextNode(tokens[0]));
|
||||
var item, last;
|
||||
for(var i=1, len=tokens.length; i<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) {
|
||||
var root = this.createElementNSPlus(
|
||||
"sld:StyledLayerDescriptor",
|
||||
@@ -897,32 +926,9 @@ OpenLayers.Format.SLD.v1 = OpenLayers.Class(OpenLayers.Format.Filter.v1_0_0, {
|
||||
return node;
|
||||
},
|
||||
"Label": function(label) {
|
||||
// only the simplest of ogc:expression handled
|
||||
// {label: "some text and a ${propertyName}"}
|
||||
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;
|
||||
return this.writers.sld._OGCExpression.call(
|
||||
this, "sld:Label", label
|
||||
);
|
||||
},
|
||||
"Halo": function(symbolizer) {
|
||||
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) {
|
||||
this.writeNode("Size", symbolizer.pointRadius * 2, node);
|
||||
} else if (symbolizer.graphicWidth != undefined) {
|
||||
this.writeNode("Size", symbolizer.graphicWidth, node);
|
||||
}
|
||||
if(symbolizer.rotation != undefined) {
|
||||
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) {
|
||||
return this.createElementNSPlus("sld:Size", {
|
||||
value: value
|
||||
});
|
||||
return this.writers.sld._OGCExpression.call(
|
||||
this, "sld:Size", value
|
||||
);
|
||||
},
|
||||
"Rotation": function(value) {
|
||||
return this.createElementNSPlus("sld:Rotation", {
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
'</PolygonSymbolizer>' +
|
||||
'<TextSymbolizer>' +
|
||||
'<Label>' +
|
||||
'<ogc:PropertyName>FOO</ogc:PropertyName>' +
|
||||
'A <ogc:PropertyName>FOO</ogc:PropertyName> label' +
|
||||
'</Label>' +
|
||||
'<Font>' +
|
||||
'<CssParameter name="font-family">Arial</CssParameter>' +
|
||||
@@ -121,6 +121,7 @@
|
||||
'<CssParameter name="stroke-width">2</CssParameter>' +
|
||||
'</Stroke>' +
|
||||
'</Mark>' +
|
||||
'<Size><ogc:PropertyName>SIZE</ogc:PropertyName></Size>' +
|
||||
'</Graphic>' +
|
||||
'</PointSymbolizer>' +
|
||||
'</Rule>' +
|
||||
@@ -130,7 +131,7 @@
|
||||
'</StyledLayerDescriptor>';
|
||||
|
||||
function test_read(t) {
|
||||
t.plan(22);
|
||||
t.plan(23);
|
||||
|
||||
var xml = new OpenLayers.Format.XML();
|
||||
var sldxml = xml.read(sld);
|
||||
@@ -173,7 +174,7 @@
|
||||
t.eq(poly.fillColor, "#ffffff", "(AAA161) first rule has proper fill");
|
||||
t.eq(poly.strokeColor, "#000000", "(AAA161) first rule has proper stroke");
|
||||
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(text.fontFamily, "Arial", "(AAA161) first rule has proper font family");
|
||||
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.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)
|
||||
// I'll add more tests here later.
|
||||
|
||||
@@ -211,7 +218,7 @@
|
||||
function test_write(t) {
|
||||
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 xml = new OpenLayers.Format.XML();
|
||||
var sldxml = xml.read(sld);
|
||||
|
||||
Reference in New Issue
Block a user