diff --git a/examples/graphic-name.html b/examples/graphic-name.html new file mode 100644 index 0000000000..54649fc257 --- /dev/null +++ b/examples/graphic-name.html @@ -0,0 +1,83 @@ + + + OpenLayers Graphic Names + + + + + + +

Named Graphics Example

+ +
+ +

+ Shows how to use well-known graphic names. +

+ +
+ +
+ OpenLayers supports well-known names for a few graphics. You can use + the names "star", "cross", "x", "square", "triangle", and "circle" as + the value for the graphicName property of a symbolizer. +
+ + \ No newline at end of file diff --git a/examples/tasmania/TasmaniaCities.xml b/examples/tasmania/TasmaniaCities.xml index 780d23ce35..11f5bd7c03 100644 --- a/examples/tasmania/TasmaniaCities.xml +++ b/examples/tasmania/TasmaniaCities.xml @@ -23,4 +23,18 @@ 100,000 to 250,000 + + + + + + + 147,-41.1 + + + + + Australia + + diff --git a/examples/tasmania/sld-tasmania.xml b/examples/tasmania/sld-tasmania.xml index ce3e3e5538..1ffba6ec03 100644 --- a/examples/tasmania/sld-tasmania.xml +++ b/examples/tasmania/sld-tasmania.xml @@ -528,13 +528,27 @@ 1 + + + image/png - 0.5 + 0.7 + 14 + + + + + + + + + cross + 10 diff --git a/examples/vector-features.html b/examples/vector-features.html index 78ea7364bf..bcde5e5b45 100644 --- a/examples/vector-features.html +++ b/examples/vector-features.html @@ -26,6 +26,11 @@ var style_blue = OpenLayers.Util.extend({}, layer_style); style_blue.strokeColor = "blue"; style_blue.fillColor = "blue"; + style_blue.graphicName = "star"; + style_blue.pointRadius = 10; + style_blue.strokeWidth = 3; + style_blue.rotation = 45; + style_blue.strokeLinecap = "butt"; /* * Green style diff --git a/lib/OpenLayers/Feature/Vector.js b/lib/OpenLayers/Feature/Vector.js index 127ef48258..338fce6ec8 100644 --- a/lib/OpenLayers/Feature/Vector.js +++ b/lib/OpenLayers/Feature/Vector.js @@ -323,9 +323,10 @@ OpenLayers.Feature.Vector = OpenLayers.Class(OpenLayers.Feature, { * - externalGraphic, * - graphicWidth, * - graphicHeight, - * - graphicOpacity - * - graphicXOffset - * - graphicYOffset + * - graphicOpacity, + * - graphicXOffset, + * - graphicYOffset, + * - graphicName, * - display */ OpenLayers.Feature.Vector.style = { diff --git a/lib/OpenLayers/Format/SLD/v1.js b/lib/OpenLayers/Format/SLD/v1.js index cdffc6e36b..db2a88063d 100644 --- a/lib/OpenLayers/Format/SLD/v1.js +++ b/lib/OpenLayers/Format/SLD/v1.js @@ -52,7 +52,8 @@ OpenLayers.Format.SLD.v1 = OpenLayers.Class(OpenLayers.Format.XML, { strokeColor: "#000000", strokeOpacity: 1, strokeWidth: 1, - pointRadius: 6 + pointRadius: 3, + graphicName: "square" }, /** diff --git a/lib/OpenLayers/Renderer/Elements.js b/lib/OpenLayers/Renderer/Elements.js index fc025ffda7..e7cf0658cb 100644 --- a/lib/OpenLayers/Renderer/Elements.js +++ b/lib/OpenLayers/Renderer/Elements.js @@ -371,6 +371,35 @@ OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, { } return node; }, - + + /** + * Method: isComplexSymbol + * Determines if a symbol cannot be rendered using drawCircle + * + * Parameters: + * graphicName - {String} + * + * Returns + * {Boolean} true if the symbol is complex, false if not + */ + isComplexSymbol: function(graphicName) { + return (graphicName != "circle") && !!graphicName; + }, + CLASS_NAME: "OpenLayers.Renderer.Elements" }); + + +/** + * Constant: OpenLayers.Renderer.symbol + * Coordinate arrays for well known (named) symbols. + */ +OpenLayers.Renderer.symbol = { + "star": [350,75, 379,161, 469,161, 397,215, 423,301, 350,250, 277,301, + 303,215, 231,161, 321,161, 350,75], + "cross": [4,0, 6,0, 6,4, 10,4, 10,6, 6,6, 6,10, 4,10, 4,6, 0,6, 0,4, 4,4, + 4,0], + "x": [0,0, 25,0, 50,35, 75,0, 100,0, 65,50, 100,100, 75,100, 50,65, 25,100, 0,100, 35,50, 0,0], + "square": [0,0, 0,1, 1,1, 1,0, 0,0], + "triangle": [0,10, 10,10, 5,0, 0,10] +} diff --git a/lib/OpenLayers/Renderer/SVG.js b/lib/OpenLayers/Renderer/SVG.js index a158338272..ce18991fa7 100644 --- a/lib/OpenLayers/Renderer/SVG.js +++ b/lib/OpenLayers/Renderer/SVG.js @@ -19,6 +19,12 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, { * {String} */ xmlns: "http://www.w3.org/2000/svg", + + /** + * Property: xlinkns + * {String} + */ + xlinkns: "http://www.w3.org/1999/xlink", /** * Constant: MAX_PIXEL @@ -33,6 +39,12 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, { * {Float} */ localResolution: null, + + /** + * Property: symbolSize + * {Object} Cache for symbol sizes according to their svg coordinate space + */ + symbolSize: {}, /** * Constructor: OpenLayers.Renderer.SVG @@ -154,7 +166,13 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, { var nodeType = null; switch (geometry.CLASS_NAME) { case "OpenLayers.Geometry.Point": - nodeType = style.externalGraphic ? "image" : "circle"; + if (style.externalGraphic) { + nodeType = "image"; + } else if (this.isComplexSymbol(style.graphicName)) { + nodeType = "use"; + } else { + nodeType = "circle"; + } break; case "OpenLayers.Geometry.Rectangle": nodeType = "rect"; @@ -194,10 +212,11 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, { style = style || node._style; options = options || node._options; var r = parseFloat(node.getAttributeNS(null, "r")); + var widthFactor = 1; + var pos; if (node._geometryClass == "OpenLayers.Geometry.Point" && r) { if (style.externalGraphic) { - var x = parseFloat(node.getAttributeNS(null, "cx")); - var y = parseFloat(node.getAttributeNS(null, "cy")); + pos = this.getPosition(node); if (style.graphicWidth && style.graphicHeight) { node.setAttributeNS(null, "preserveAspectRatio", "none"); @@ -213,19 +232,31 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, { var opacity = style.graphicOpacity || style.fillOpacity; - node.setAttributeNS(null, "x", (x + xOffset).toFixed()); - node.setAttributeNS(null, "y", (y + yOffset).toFixed()); + node.setAttributeNS(null, "x", (pos.x + xOffset).toFixed()); + node.setAttributeNS(null, "y", (pos.y + yOffset).toFixed()); node.setAttributeNS(null, "width", width); node.setAttributeNS(null, "height", height); - node.setAttributeNS("http://www.w3.org/1999/xlink", "href", style.externalGraphic); + node.setAttributeNS(this.xlinkns, "href", style.externalGraphic); node.setAttributeNS(null, "style", "opacity: "+opacity); + } else if (this.isComplexSymbol(style.graphicName)) { + // the symbol viewBox is three times as large as the symbol + var offset = style.pointRadius * 3; + var size = offset * 2; + var id = this.importSymbol(style.graphicName); + pos = this.getPosition(node); + widthFactor = this.symbolSize[id] / size; + node.setAttributeNS(this.xlinkns, "href", "#" + id); + node.setAttributeNS(null, "width", size); + node.setAttributeNS(null, "height", size); + node.setAttributeNS(null, "x", pos.x - offset); + node.setAttributeNS(null, "y", pos.y - offset); } else { node.setAttributeNS(null, "r", style.pointRadius); } - if (style.rotation) { + if (style.rotation && pos) { var rotation = OpenLayers.String.format( - "rotate(${0} ${1} ${2})", [style.rotation, x, y]); + "rotate(${0} ${1} ${2})", [style.rotation, pos.x, pos.y]); node.setAttributeNS(null, "transform", rotation); } } @@ -240,7 +271,7 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, { if (options.isStroked) { node.setAttributeNS(null, "stroke", style.strokeColor); node.setAttributeNS(null, "stroke-opacity", style.strokeOpacity); - node.setAttributeNS(null, "stroke-width", style.strokeWidth); + node.setAttributeNS(null, "stroke-width", style.strokeWidth * widthFactor); node.setAttributeNS(null, "stroke-linecap", style.strokeLinecap); } else { node.setAttributeNS(null, "stroke", "none"); @@ -308,6 +339,18 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, { return this.nodeFactory(this.container.id + "_root", "g"); }, + /** + * Method: createDefs + * + * Returns: + * {DOMElement} The element to which we'll add the symbol definitions + */ + createDefs: function() { + var defs = this.nodeFactory("ol-renderer-defs", "defs"); + this.rendererRoot.appendChild(defs); + return defs; + }, + /************************************** * * * GEOMETRY DRAWING FUNCTIONS * @@ -510,6 +553,88 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, { return false; } }, + + /** + * Method: getPosition + * Finds the position of an svg node. + * + * Parameters: + * node - {DOMElement} + * + * Returns: + * {Object} hash with x and y properties, representing the coordinates + * within the svg coordinate system + */ + getPosition: function(node) { + return({ + x: parseFloat(node.getAttributeNS(null, "cx")), + y: parseFloat(node.getAttributeNS(null, "cy")) + }); + }, + + /** + * Method: importSymbol + * add a new symbol definition from the rendererer's symbol hash + * + * Parameters: + * graphicName - {String} name of the symbol to import + * + * Returns: + * {String} - id of the imported symbol + */ + importSymbol: function (graphicName) { + if (!this.defs) { + // create svg defs tag + this.defs = this.createDefs(); + } + var id = this.container.id + "-" + graphicName; + + // check if symbol already exists in the defs + if (document.getElementById(id) != null) { + return id; + } + + var symbol = OpenLayers.Renderer.symbol[graphicName]; + if (!symbol) { + throw new Error(graphicName + ' is not a valid symbol name'); + return; + } + + var symbolNode = this.nodeFactory(id, "symbol"); + var node = this.nodeFactory(null, "polygon"); + symbolNode.appendChild(node); + var symbolExtent = new OpenLayers.Bounds( + Number.MAX_VALUE, Number.MAX_VALUE, 0, 0); + + var points = ""; + var x,y; + for (var i=0; i diff --git a/tests/Renderer/VML.html b/tests/Renderer/VML.html index 5f2e513cf5..173bb43b57 100644 --- a/tests/Renderer/VML.html +++ b/tests/Renderer/VML.html @@ -338,7 +338,39 @@ t.eq(node.style.height, "8px", "node.style.height is correct"); } + function test_vml_getnodetype(t) { + if (!OpenLayers.Renderer.VML.prototype.supported()) { + t.plan(0); + return; + } + t.plan(1); + + var r = new OpenLayers.Renderer.VML(document.body); + + var g = {CLASS_NAME: "OpenLayers.Geometry.Point"} + var s = {graphicName: "square"}; + + t.eq(r.getNodeType(g, s), "olv:shape", "Correct node type for well known symbols"); + } + + function test_vml_importsymbol(t) { + if (!OpenLayers.Renderer.VML.prototype.supported()) { + t.plan(0); + return; + } + + t.plan(2); + + var r = new OpenLayers.Renderer.VML(document.body); + + var cache = r.importSymbol("square"); + + t.eq(cache.path, "m 0 0 l 0 1 1 1 1 0 0 0 x e", "Square symbol rendered correctly"); + t.ok(r.symbolCache["-square"], "Symbol has been cached correctly."); + + } +