added support for text labels. This also adds getCentroid methods to all

geometries. Thanks crschmidt for the great help with this patch, and 
thanks to camptocamp for the initial work on this and rcoup for creating 
the first patches. r=crschmidt (closes #1895)


git-svn-id: http://svn.openlayers.org/trunk/openlayers@9262 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf
This commit is contained in:
ahocevar
2009-04-10 16:05:26 +00:00
parent dd556be0be
commit cc2e19d789
15 changed files with 560 additions and 50 deletions
+72 -4
View File
@@ -129,7 +129,9 @@ OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, {
'strokeOpacity': 1
}, style);
this.features[feature.id] = [feature, style];
this.geometryMap[feature.geometry.id] = feature.id;
if (feature.geometry) {
this.geometryMap[feature.geometry.id] = feature.id;
}
this.redraw();
},
@@ -142,7 +144,6 @@ OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, {
* Parameters:
* geometry - {<OpenLayers.Geometry>}
* style - {Object}
* featureId - {<String>}
*/
drawGeometry: function(geometry, style) {
var className = geometry.CLASS_NAME;
@@ -334,6 +335,52 @@ OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, {
); // inner rings are 'empty'
}
},
/**
* Method: drawText
* This method is only called by the renderer itself.
*
* Parameters:
* location - {<OpenLayers.Point>}
* style - {Object}
*/
drawText: function(location, style) {
var pt = this.getLocalXY(location);
this.setCanvasStyle("reset");
this.canvas.fillStyle = style.fontColor;
this.canvas.globalAlpha = 1;
var fontStyle = style.fontWeight + " " + style.fontSize + " " + style.fontFamily;
if (this.canvas.fillText) {
// HTML5
var labelAlign =
OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[0]] ||
"middle";
this.canvas.font = fontStyle;
this.canvas.textAlign = labelAlign;
this.canvas.fillText(style.label, pt[0], pt[1]);
} else if (this.canvas.mozDrawText) {
// Mozilla pre-Gecko1.9.1 (<FF3.1)
this.canvas.mozTextStyle = fontStyle;
// No built-in text alignment, so we measure and adjust the position
var len = this.canvas.mozMeasureText(style.label);
switch(style.labelAlign[0]) {
case "l":
break;
case "r":
pt[0] -= len;
break;
case "c":
default:
pt[0] -= len / 2;
}
this.canvas.translate(pt[0], pt[1]);
this.canvas.mozDrawText(style.label);
this.canvas.translate(-1*pt[0], -1*pt[1]);
}
this.setCanvasStyle("reset");
},
/**
* Method: getLocalXY
@@ -415,13 +462,34 @@ OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, {
redraw: function() {
if (!this.locked) {
this.clear();
var labelMap = [];
var feature, style;
for (var id in this.features) {
if (!this.features.hasOwnProperty(id)) { continue; }
if (!this.features[id][0].geometry) { continue; }
this.drawGeometry(this.features[id][0].geometry, this.features[id][1]);
feature = this.features[id][0];
style = this.features[id][1];
if (!feature.geometry) { continue; }
this.drawGeometry(feature.geometry, style);
if(style.label) {
labelMap.push([feature, style]);
}
}
var item;
for (var i=0; len=labelMap.length, i<len; ++i) {
item = labelMap[i];
this.drawText(item[0].geometry.getCentroid(), item[1]);
}
}
},
CLASS_NAME: "OpenLayers.Renderer.Canvas"
});
/**
* Constant: OpenLayers.Renderer.Canvas.LABEL_ALIGN
* {Object}
*/
OpenLayers.Renderer.Canvas.LABEL_ALIGN = {
"l": "left",
"r": "right"
};
+56 -8
View File
@@ -43,7 +43,7 @@ OpenLayers.ElementsIndexer = OpenLayers.Class({
* the Z_ORDER_DRAWING_ORDER comparison method.
*/
compare: null,
/**
* APIMethod: initialize
* Create a new indexer with
@@ -353,6 +353,24 @@ OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
*/
rendererRoot: null,
/**
* Property: root
* {DOMElement}
*/
root: null,
/**
* Property: vectorRoot
* {DOMElement}
*/
vectorRoot: null,
/**
* Property: textRoot
* {DOMElement}
*/
textRoot: null,
/**
* Property: xmlns
* {String}
@@ -373,6 +391,12 @@ OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
*/
BACKGROUND_ID_SUFFIX: "_background",
/**
* Constant: BACKGROUND_ID_SUFFIX
* {String}
*/
LABEL_ID_SUFFIX: "_label",
/**
* Property: minimumSymbolizer
* {Object}
@@ -399,7 +423,12 @@ OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
OpenLayers.Renderer.prototype.initialize.apply(this, arguments);
this.rendererRoot = this.createRenderRoot();
this.root = this.createRoot();
this.root = this.createRoot("_root");
this.vectorRoot = this.createRoot("_vroot");
this.textRoot = this.createRoot("_troot");
this.root.appendChild(this.vectorRoot);
this.root.appendChild(this.textRoot);
this.rendererRoot.appendChild(this.root);
this.container.appendChild(this.rendererRoot);
@@ -428,9 +457,14 @@ OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
* Remove all the elements from the root
*/
clear: function() {
if (this.root) {
while (this.root.childNodes.length > 0) {
this.root.removeChild(this.root.firstChild);
if (this.vectorRoot) {
while (this.vectorRoot.childNodes.length > 0) {
this.vectorRoot.removeChild(this.vectorRoot.firstChild);
}
}
if (this.textRoot) {
while (this.textRoot.childNodes.length > 0) {
this.textRoot.removeChild(this.textRoot.firstChild);
}
}
if (this.indexer) {
@@ -544,15 +578,15 @@ OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
if (this.indexer) {
var insert = this.indexer.insert(node);
if (insert) {
this.root.insertBefore(node, insert);
this.vectorRoot.insertBefore(node, insert);
} else {
this.root.appendChild(node);
this.vectorRoot.appendChild(node);
}
} else {
// if there's no indexer, simply append the node to root,
// but only if the node is a new one
if (node.parentNode !== this.root){
this.root.appendChild(node);
this.vectorRoot.appendChild(node);
}
}
@@ -788,6 +822,20 @@ OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
*/
drawSurface: function(node, geometry) {},
/**
* Method: removeText
* Removes a label
*
* Parameters:
* featureId - {String}
*/
removeText: function(featureId) {
var label = document.getElementById(featureId + this.LABEL_ID_SUFFIX);
if (label) {
this.textRoot.removeChild(label);
}
},
/**
* Method: getFeatureIdFromEvent
*
+93 -3
View File
@@ -45,6 +45,12 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
* {Object} Cache for symbol sizes according to their svg coordinate space
*/
symbolSize: {},
/**
* Property: isGecko
* {Boolean}
*/
isGecko: null,
/**
* Constructor: OpenLayers.Renderer.SVG
@@ -59,6 +65,7 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
OpenLayers.Renderer.Elements.prototype.initialize.apply(this,
arguments);
this.translationParameters = {x: 0, y: 0};
this.isGecko = (navigator.userAgent.toLowerCase().indexOf("gecko/") != -1);
},
/**
@@ -337,6 +344,7 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
if (style.cursor != null) {
node.setAttributeNS(null, "cursor", style.cursor);
}
return node;
},
@@ -416,11 +424,14 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
/**
* Method: createRoot
*
* Parameter:
* suffix - {String} suffix to append to the id
*
* Returns:
* {DOMElement} The main root element to which we'll add vectors
* {DOMElement}
*/
createRoot: function() {
return this.nodeFactory(this.container.id + "_root", "g");
createRoot: function(suffix) {
return this.nodeFactory(this.container.id + suffix, "g");
},
/**
@@ -633,7 +644,61 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
return false;
}
},
/**
* Method: drawText
* This method is only called by the renderer itself.
*
* Parameters:
* featureId - {String}
* style -
* location - {<OpenLayers.Geometry.Point>}
*/
drawText: function(featureId, style, location) {
var resolution = this.getResolution();
var x = (location.x / resolution + this.left);
var y = (location.y / resolution - this.top);
var label = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX, "text");
var tspan = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_tspan", "tspan");
label.setAttributeNS(null, "x", x);
label.setAttributeNS(null, "y", -y);
label.setAttributeNS(null, "pointer-events", "none");
if (style.fontColor) {
label.setAttributeNS(null, "fill", style.fontColor);
}
if (style.fontFamily) {
label.setAttributeNS(null, "font-family", style.fontFamily);
}
if (style.fontSize) {
label.setAttributeNS(null, "font-size", style.fontSize);
}
if (style.fontWeight) {
label.setAttributeNS(null, "font-weight", style.fontWeight);
}
var align = style.labelAlign || "cm";
label.setAttributeNS(null, "text-anchor",
OpenLayers.Renderer.SVG.LABEL_ALIGN[align[0]] || "middle");
if (this.isGecko) {
label.setAttributeNS(null, "dominant-baseline",
OpenLayers.Renderer.SVG.LABEL_ALIGN[align[1]] || "central");
} else {
tspan.setAttributeNS(null, "baseline-shift",
OpenLayers.Renderer.SVG.LABEL_VSHIFT[align[1]] || "-35%");
}
tspan.textContent = style.label;
if(!label.parentNode) {
label.appendChild(tspan);
this.textRoot.appendChild(label);
}
},
/**
* Method: getComponentString
*
@@ -828,3 +893,28 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
CLASS_NAME: "OpenLayers.Renderer.SVG"
});
/**
* Constant: OpenLayers.Renderer.SVG.LABEL_ALIGN
* {Object}
*/
OpenLayers.Renderer.SVG.LABEL_ALIGN = {
"l": "start",
"r": "end",
"b": "bottom",
"t": "hanging"
};
/**
* Constant: OpenLayers.Renderer.SVG.LABEL_VSHIFT
* {Object}
*/
OpenLayers.Renderer.SVG.LABEL_VSHIFT = {
// according to
// http://www.w3.org/Graphics/SVG/Test/20061213/htmlObjectHarness/full-text-align-02-b.html
// a baseline-shift of -70% shifts the text exactly from the
// bottom to the top of the baseline, so -35% moves the text to
// the center of the baseline.
"t": "-70%",
"b": "0"
};
+94 -12
View File
@@ -108,13 +108,19 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
left = left - this.offset.x;
top = top - this.offset.y;
}
var org = left + " " + top;
this.root.setAttribute("coordorigin", org);
var roots = [this.root, this.vectorRoot, this.textRoot];
var root;
for(var i=0, len=roots.length; i<len; ++i) {
root = roots[i];
var size = this.size.w + " " + this.size.h;
this.root.setAttribute("coordsize", size);
var size = this.size.w + " " + this.size.h;
root.setAttribute("coordsize", size);
}
// flip the VML display Y axis upside down so it
// matches the display Y axis of the map
this.root.style.flip = "y";
@@ -132,12 +138,23 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
*/
setSize: function(size) {
OpenLayers.Renderer.prototype.setSize.apply(this, arguments);
this.rendererRoot.style.width = this.size.w + "px";
this.rendererRoot.style.height = this.size.h + "px";
this.root.style.width = this.size.w + "px";
this.root.style.height = this.size.h + "px";
// setting width and height on all roots to avoid flicker which we
// would get with 100% width and height on child roots
var roots = [
this.rendererRoot,
this.root,
this.vectorRoot,
this.textRoot
];
var w = this.size.w + "px";
var h = this.size.h + "px";
var root;
for(var i=0, len=roots.length; i<len; ++i) {
root = roots[i];
root.style.width = w;
root.style.height = h;
}
},
/**
@@ -582,12 +599,15 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
/**
* Method: createRoot
* Create the main root element
*
* Parameters:
* suffix - {String} suffix to append to the id
*
* Returns:
* {DOMElement} The main root element to which we'll add vectors
* {DOMElement}
*/
createRoot: function() {
return this.nodeFactory(this.container.id + "_root", "olv:group");
createRoot: function(suffix) {
return this.nodeFactory(this.container.id + suffix, "olv:group");
},
/**************************************
@@ -762,6 +782,55 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
return node;
},
/**
* Method: drawText
* This method is only called by the renderer itself.
*
* Parameters:
* featureId - {String}
* style -
* location - {<OpenLayers.Geometry.Point>}
*/
drawText: function(featureId, style, location) {
var label = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX, "olv:rect");
var textbox = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_textbox", "olv:textbox");
var resolution = this.getResolution();
label.style.left = (location.x/resolution - this.offset.x).toFixed() + "px";
label.style.top = (location.y/resolution - this.offset.y).toFixed() + "px";
label.style.flip = "y";
textbox.innerText = style.label;
if (style.fillColor) {
textbox.style.color = style.fontColor;
}
if (style.fontFamily) {
textbox.style.fontFamily = style.fontFamily;
}
if (style.fontSize) {
textbox.style.fontSize = style.fontSize;
}
if (style.fontWeight) {
textbox.style.fontWeight = style.fontWeight;
}
textbox.style.whiteSpace = "nowrap";
textbox.inset = "0px,0px,0px,0px";
if(!label.parentNode) {
label.appendChild(textbox);
this.textRoot.appendChild(label);
}
var align = style.labelAlign || "cm";
var xshift = textbox.clientWidth *
(OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(0,1)]);
var yshift = textbox.clientHeight *
(OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(1,1)]);
label.style.left = parseInt(label.style.left)-xshift+"px";
label.style.top = parseInt(label.style.top)+yshift+"px";
},
/**
* Method: drawSurface
@@ -886,3 +955,16 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
CLASS_NAME: "OpenLayers.Renderer.VML"
});
/**
* Constant: OpenLayers.Renderer.VML.LABEL_SHIFT
* {Object}
*/
OpenLayers.Renderer.VML.LABEL_SHIFT = {
"l": 0,
"c": .5,
"r": 1,
"t": 0,
"m": .5,
"b": 1
};