added new graphicName symbolizer property, which allows to render well-known graphic symbols named "square", "cross", "x" and "triangle", in addition to the existing "circle". Thanks Tim for the tweaks and the example. r=tschaub,elemoine (closes #1398)

git-svn-id: http://svn.openlayers.org/trunk/openlayers@7634 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf
This commit is contained in:
ahocevar
2008-07-31 17:02:10 +00:00
parent 9bdd7bc77a
commit 3dcc30a24c
11 changed files with 451 additions and 28 deletions

View File

@@ -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]
}

View File

@@ -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<symbol.length; i=i+2) {
x = symbol[i];
y = symbol[i+1];
symbolExtent.left = Math.min(symbolExtent.left, x);
symbolExtent.bottom = Math.min(symbolExtent.bottom, y);
symbolExtent.right = Math.max(symbolExtent.right, x);
symbolExtent.top = Math.max(symbolExtent.top, y);
points += " " + x + "," + y;
}
node.setAttributeNS(null, "points", points);
// Hard-coded linejoin for now, to make it look the same as in VML.
// There is no strokeLinejoin property yet for symbolizers.
node.setAttributeNS(null, "stroke-linejoin", "round");
var width = symbolExtent.getWidth();
var height = symbolExtent.getHeight();
// create a viewBox three times as large as the symbol itself,
// to allow for strokeWidth being displayed correctly at the corners.
var viewBox = [symbolExtent.left - width,
symbolExtent.bottom - height, width * 3, height * 3];
symbolNode.setAttributeNS(null, "viewBox", viewBox.join(" "));
this.symbolSize[id] = Math.max(width, height) * 3;
this.defs.appendChild(symbolNode);
return symbolNode.id;
},
CLASS_NAME: "OpenLayers.Renderer.SVG"
});

View File

@@ -25,6 +25,13 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
* {String} XML Namespace URN
*/
xmlns: "urn:schemas-microsoft-com:vml",
/**
* Property: symbolCache
* {DOMElement} node holding symbols. This hash is keyed by symbol name,
* and each value is a hash with a "path" and an "extent" property.
*/
symbolCache: {},
/**
* Constructor: OpenLayers.Renderer.VML
@@ -123,7 +130,13 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
var nodeType = null;
switch (geometry.CLASS_NAME) {
case "OpenLayers.Geometry.Point":
nodeType = style.externalGraphic ? "olv:rect" : "olv:oval";
if (style.externalGraphic) {
nodeType = "olv:rect";
} else if (this.isComplexSymbol(style.graphicName)) {
nodeType = "olv:shape";
} else {
nodeType = "olv:oval";
}
break;
case "OpenLayers.Geometry.Rectangle":
nodeType = "olv:rect";
@@ -156,6 +169,7 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
setStyle: function(node, style, options, geometry) {
style = style || node._style;
options = options || node._options;
var widthFactor = 1;
if (node._geometryClass == "OpenLayers.Geometry.Point") {
if (style.externalGraphic) {
@@ -179,7 +193,22 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
// modify style/options for fill and stroke styling below
style.fillColor = "none";
options.isStroked = false;
} else if (this.isComplexSymbol(style.graphicName)) {
var cache = this.importSymbol(style.graphicName);
var symbolExtent = cache.extent;
var width = symbolExtent.getWidth();
var height = symbolExtent.getHeight();
node.setAttribute("path", cache.path);
node.setAttribute("coordorigin", symbolExtent.left + "," +
symbolExtent.bottom);
node.setAttribute("coordsize", width + "," + height);
node.style.left = symbolExtent.left + "px";
node.style.top = symbolExtent.bottom + "px";
node.style.width = width + "px";
node.style.height = height + "px";
this.drawCircle(node, geometry, style.pointRadius);
node.style.flip = "y";
} else {
this.drawCircle(node, geometry, style.pointRadius);
}
@@ -217,22 +246,25 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
if (!(style.graphicWidth && style.graphicHeight)) {
fill.aspect = "atmost";
}
// additional rendering for rotated graphics
if (style.rotation) {
this.graphicRotate(node, xOffset, yOffset);
// make the fill fully transparent, because we now have
// the graphic as imagedata element. We cannot just remove
// the fill, because this is part of the hack described
// in graphicRotate
fill.setAttribute("opacity", 0);
}
}
if (fill.parentNode != node) {
node.appendChild(fill);
}
}
// additional rendering for rotated graphics or symbols
if (style.rotation) {
if (style.externalGraphic) {
this.graphicRotate(node, xOffset, yOffset);
// make the fill fully transparent, because we now have
// the graphic as imagedata element. We cannot just remove
// the fill, because this is part of the hack described
// in graphicRotate
fill.setAttribute("opacity", 0);
} else {
node.style.rotation = style.rotation;
}
}
// stroke
if (options.isStroked) {
@@ -694,6 +726,61 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
node.path = path.join("");
},
/**
* Method: importSymbol
* add a new symbol definition from the rendererer's symbol hash
*
* Parameters:
* graphicName - {String} name of the symbol to import
*
* Returns:
* {Object} - hash of {DOMElement} "symbol" and {Number} "size"
*/
importSymbol: function (graphicName) {
var id = this.container.id + "-" + graphicName;
// check if symbol already exists in the cache
var cache = this.symbolCache[id];
if (cache) {
return cache;
}
var symbol = OpenLayers.Renderer.symbol[graphicName];
if (!symbol) {
throw new Error(graphicName + ' is not a valid symbol name');
return;
}
var symbolExtent = new OpenLayers.Bounds(
Number.MAX_VALUE, Number.MAX_VALUE, 0, 0);
var pathitems = ["m"];
for (var i=0; i<symbol.length; i=i+2) {
x = symbol[i];
y = symbol[i+1];
symbolExtent.left = Math.min(symbolExtent.left, x);
symbolExtent.bottom = Math.min(symbolExtent.bottom, y);
symbolExtent.right = Math.max(symbolExtent.right, x);
symbolExtent.top = Math.max(symbolExtent.top, y);
pathitems.push(x);
pathitems.push(y);
if (i == 0) {
pathitems.push("l");
}
}
pathitems.push("x e");
var path = pathitems.join(" ");
cache = {
path: path,
extent: symbolExtent
};
this.symbolCache[id] = cache;
return cache;
},
CLASS_NAME: "OpenLayers.Renderer.VML"
});