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:
@@ -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"
|
||||
};
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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"
|
||||
};
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user