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

View File

@@ -0,0 +1,109 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<style type="text/css">
#map {
width: 800px;
height: 475px;
border: 1px solid black;
}
</style>
<script src="../lib/OpenLayers.js" type="text/javascript"></script>
<script type="text/javascript">
var map;
function init(){
map = new OpenLayers.Map('map');
var layer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
"http://labs.metacarta.com/wms/vmap0", {layers: 'basic'} );
map.addLayer(layer);
// allow testing of specific renderers via "?renderer=Canvas", etc
var renderer = OpenLayers.Util.getParameters(window.location.href).renderer;
renderer = (renderer) ? [renderer] : OpenLayers.Layer.Vector.prototype.renderers;
var vectorLayer = new OpenLayers.Layer.Vector("Simple Geometry", {
styleMap: new OpenLayers.StyleMap({'default':{
strokeColor: "#00FF00",
strokeOpacity: 1,
strokeWidth: 3,
fillColor: "#FF5500",
fillOpacity: 0.5,
pointRadius: 6,
pointerEvents: "visiblePainted",
label : "name: ${name}, age: ${age}",
fontColor: "${favColor}",
fontSize: "12px",
fontFamily: "Courier New, monospace",
fontWeight: "bold",
labelAlign: "${align}"
}}),
renderers: renderer
});
// create a point feature
var point = new OpenLayers.Geometry.Point(-111.04, 45.68);
var pointFeature = new OpenLayers.Feature.Vector(point);
pointFeature.attributes = {
name: "toto",
age: 20,
favColor: 'red',
align: "cm"
};
// create a polygon feature from a linear ring of points
var pointList = [];
for(var p=0; p<6; ++p) {
var a = p * (2 * Math.PI) / 7;
var r = Math.random(1) + 1;
var newPoint = new OpenLayers.Geometry.Point(point.x + 5 + (r * Math.cos(a)),
point.y + 5 + (r * Math.sin(a)));
pointList.push(newPoint);
}
pointList.push(pointList[0]);
var linearRing = new OpenLayers.Geometry.LinearRing(pointList);
var polygonFeature = new OpenLayers.Feature.Vector(
new OpenLayers.Geometry.Polygon([linearRing]));
polygonFeature.attributes = {
name: "dude",
age: 21,
favColor: 'purple',
align: 'lb'
};
multiFeature = new OpenLayers.Feature.Vector(
new OpenLayers.Geometry.Collection([
new OpenLayers.Geometry.LineString([
new OpenLayers.Geometry.Point(-105,40),
new OpenLayers.Geometry.Point(-95,45)
]),
new OpenLayers.Geometry.Point(-105, 40)
]),
{
name: "ball-and-chain",
age: 30,
favColor: 'black',
align: 'rt'
});
var nullFeature = new OpenLayers.Feature.Vector(null);
nullFeature.attributes = {
name: "toto is some text about the world",
age: 20,
favColor: 'red',
align: "cm"
};
map.addLayer(vectorLayer);
vectorLayer.drawFeature(multiFeature);
map.setCenter(new OpenLayers.LonLat(point.x, point.y), 5);
vectorLayer.addFeatures([pointFeature, polygonFeature, multiFeature, nullFeature ]);
}
</script>
</head>
<body onload="init()">
<div id="map"></div>
<p>This example shows drawing simple vector features with a label</p>
</body>
</html>

View File

@@ -339,6 +339,17 @@ OpenLayers.Feature.Vector = OpenLayers.Class(OpenLayers.Feature, {
* backgroundYOffset - {Number} The y offset (in pixels) for the background graphic. * backgroundYOffset - {Number} The y offset (in pixels) for the background graphic.
* backgroundHeight - {Number} The height of the background graphic. If not provided, the graphicHeight will be used. * backgroundHeight - {Number} The height of the background graphic. If not provided, the graphicHeight will be used.
* backgroundWidth - {Number} The width of the background width. If not provided, the graphicWidth will be used. * backgroundWidth - {Number} The width of the background width. If not provided, the graphicWidth will be used.
* label - {String} The text for an optional label. For browsers that use the canvas renderer, this requires either
* fillText or mozDrawText to be available.
* labelAlign - {String} Label alignment. This specifies the insertion point relative to the text. It is a string
* composed of two characters. The first character is for the horizontal alignment, the second for the vertical
* alignment. Valid values for horizontal alignment: "l"=left, "c"=center, "r"=right. Valid values for vertical
* alignment: "t"=top, "m"=middle, "b"=bottom. Example values: "lt", "cm", "rb". The canvas renderer does not
* support vertical alignment, it will always use "b".
* fontColor - {String} The font color for the label, to be provided like CSS.
* fontFamily - {String} The font family for the label, to be provided like in CSS.
* fontSize - {String} The font size for the label, to be provided like in CSS.
* fontWeight - {String} The font weight for the label, to be provided like in CSS.
* display - {String} Symbolizers will have no effect if display is set to "none". All other values have no effect. * display - {String} Symbolizers will have no effect if display is set to "none". All other values have no effect.
*/ */
OpenLayers.Feature.Vector.style = { OpenLayers.Feature.Vector.style = {

View File

@@ -224,6 +224,17 @@ OpenLayers.Geometry = OpenLayers.Class({
// //
return 0.0; return 0.0;
}, },
/**
* APIMethod: getCentroid
* Calculate the centroid of this geometry. This method is defined in subclasses.
*
* Returns:
* {<OpenLayers.Geometry.Point>} The centroid of the collection
*/
getCentroid: function() {
return null;
},
/** /**
* Method: toString * Method: toString

View File

@@ -256,6 +256,27 @@ OpenLayers.Geometry.Collection = OpenLayers.Class(OpenLayers.Geometry, {
} }
return area; return area;
}, },
/**
* APIMethod: getCentroid
*
* Returns:
* {<OpenLayers.Geometry.Point>} The centroid of the collection
*/
getCentroid: function() {
return this.components.length && this.components[0].getCentroid();
/*
var centroid;
for (var i=0, len=this.components.length; i<len; i++) {
if (!centroid) {
centroid = this.components[i].getCentroid();
} else {
centroid.resize(this.components[i].getCentroid(), 0.5);
}
}
return centroid;
*/
},
/** /**
* APIMethod: getGeodesicLength * APIMethod: getGeodesicLength

View File

@@ -182,6 +182,29 @@ OpenLayers.Geometry.LinearRing = OpenLayers.Class(
} }
return this; return this;
}, },
/**
* APIMethod: getCentroid
*
* Returns:
* {<OpenLayers.Geometry.Point>} The centroid of the collection
*/
getCentroid: function() {
if ( this.components && (this.components.length > 2)) {
var sumX = 0.0;
var sumY = 0.0;
for (var i = 0; i < this.components.length - 1; i++) {
var b = this.components[i];
var c = this.components[i+1];
sumX += (b.x + c.x) * (b.x * c.y - c.x * b.y);
sumY += (b.y + c.y) * (b.x * c.y - c.x * b.y);
}
var area = -1 * this.getArea();
var x = sumX / (6 * area);
var y = sumY / (6 * area);
}
return new OpenLayers.Geometry.Point(x, y);
},
/** /**
* APIMethod: getArea * APIMethod: getArea

View File

@@ -186,6 +186,16 @@ OpenLayers.Geometry.Point = OpenLayers.Class(OpenLayers.Geometry, {
this.y = origin.y + (radius * Math.sin(theta)); this.y = origin.y + (radius * Math.sin(theta));
this.clearBounds(); this.clearBounds();
}, },
/**
* APIMethod: getCentroid
*
* Returns:
* {<OpenLayers.Geometry.Point>} The centroid of the collection
*/
getCentroid: function() {
return new OpenLayers.Geometry.Point(this.x, this.y);
},
/** /**
* APIMethod: resize * APIMethod: resize

View File

@@ -591,7 +591,7 @@ OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {
feature.layer = null; feature.layer = null;
if (feature.geometry) { if (feature.geometry) {
this.renderer.eraseGeometry(feature.geometry); this.renderer.eraseFeatures(feature);
} }
//in the case that this feature is one of the selected features, //in the case that this feature is one of the selected features,

View File

@@ -176,7 +176,13 @@ OpenLayers.Renderer = OpenLayers.Class({
if (!bounds.intersectsBounds(this.extent)) { if (!bounds.intersectsBounds(this.extent)) {
style = {display: "none"}; style = {display: "none"};
} }
return this.drawGeometry(feature.geometry, style, feature.id); var rendered = this.drawGeometry(feature.geometry, style, feature.id);
if(style.display != "none" && style.label && rendered !== false) {
this.drawText(feature.id, style, feature.geometry.getCentroid());
} else {
this.removeText(feature.id);
}
return rendered;
} }
} }
}, },
@@ -196,6 +202,28 @@ OpenLayers.Renderer = OpenLayers.Class({
*/ */
drawGeometry: function(geometry, style, featureId) {}, drawGeometry: function(geometry, style, featureId) {},
/**
* Method: drawText
* Function for drawing text labels.
* This method is only called by the renderer itself.
*
* Parameters:
* featureId - {String}
* style -
* location - {<OpenLayers.Geometry.Point>}
*/
drawText: function(featureId, style, location) {},
/**
* Method: removeText
* Function for removing text labels.
* This method is only called by the renderer itself.
*
* Parameters:
* featureId - {String}
*/
removeText: function(featureId) {},
/** /**
* Method: clear * Method: clear
* Clear all vectors from the renderer. * Clear all vectors from the renderer.
@@ -231,6 +259,7 @@ OpenLayers.Renderer = OpenLayers.Class({
} }
for(var i=0, len=features.length; i<len; ++i) { for(var i=0, len=features.length; i<len; ++i) {
this.eraseGeometry(features[i].geometry); this.eraseGeometry(features[i].geometry);
this.removeText(features[i].id);
} }
}, },

View File

@@ -129,7 +129,9 @@ OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, {
'strokeOpacity': 1 'strokeOpacity': 1
}, style); }, style);
this.features[feature.id] = [feature, 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(); this.redraw();
}, },
@@ -142,7 +144,6 @@ OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, {
* Parameters: * Parameters:
* geometry - {<OpenLayers.Geometry>} * geometry - {<OpenLayers.Geometry>}
* style - {Object} * style - {Object}
* featureId - {<String>}
*/ */
drawGeometry: function(geometry, style) { drawGeometry: function(geometry, style) {
var className = geometry.CLASS_NAME; var className = geometry.CLASS_NAME;
@@ -334,6 +335,52 @@ OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, {
); // inner rings are 'empty' ); // 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 * Method: getLocalXY
@@ -415,13 +462,34 @@ OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, {
redraw: function() { redraw: function() {
if (!this.locked) { if (!this.locked) {
this.clear(); this.clear();
var labelMap = [];
var feature, style;
for (var id in this.features) { for (var id in this.features) {
if (!this.features.hasOwnProperty(id)) { continue; } if (!this.features.hasOwnProperty(id)) { continue; }
if (!this.features[id][0].geometry) { continue; } feature = this.features[id][0];
this.drawGeometry(this.features[id][0].geometry, this.features[id][1]); 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" CLASS_NAME: "OpenLayers.Renderer.Canvas"
}); });
/**
* Constant: OpenLayers.Renderer.Canvas.LABEL_ALIGN
* {Object}
*/
OpenLayers.Renderer.Canvas.LABEL_ALIGN = {
"l": "left",
"r": "right"
};

View File

@@ -43,7 +43,7 @@ OpenLayers.ElementsIndexer = OpenLayers.Class({
* the Z_ORDER_DRAWING_ORDER comparison method. * the Z_ORDER_DRAWING_ORDER comparison method.
*/ */
compare: null, compare: null,
/** /**
* APIMethod: initialize * APIMethod: initialize
* Create a new indexer with * Create a new indexer with
@@ -353,6 +353,24 @@ OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
*/ */
rendererRoot: null, rendererRoot: null,
/**
* Property: root
* {DOMElement}
*/
root: null,
/**
* Property: vectorRoot
* {DOMElement}
*/
vectorRoot: null,
/**
* Property: textRoot
* {DOMElement}
*/
textRoot: null,
/** /**
* Property: xmlns * Property: xmlns
* {String} * {String}
@@ -373,6 +391,12 @@ OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
*/ */
BACKGROUND_ID_SUFFIX: "_background", BACKGROUND_ID_SUFFIX: "_background",
/**
* Constant: BACKGROUND_ID_SUFFIX
* {String}
*/
LABEL_ID_SUFFIX: "_label",
/** /**
* Property: minimumSymbolizer * Property: minimumSymbolizer
* {Object} * {Object}
@@ -399,7 +423,12 @@ OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
OpenLayers.Renderer.prototype.initialize.apply(this, arguments); OpenLayers.Renderer.prototype.initialize.apply(this, arguments);
this.rendererRoot = this.createRenderRoot(); 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.rendererRoot.appendChild(this.root);
this.container.appendChild(this.rendererRoot); this.container.appendChild(this.rendererRoot);
@@ -428,9 +457,14 @@ OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
* Remove all the elements from the root * Remove all the elements from the root
*/ */
clear: function() { clear: function() {
if (this.root) { if (this.vectorRoot) {
while (this.root.childNodes.length > 0) { while (this.vectorRoot.childNodes.length > 0) {
this.root.removeChild(this.root.firstChild); this.vectorRoot.removeChild(this.vectorRoot.firstChild);
}
}
if (this.textRoot) {
while (this.textRoot.childNodes.length > 0) {
this.textRoot.removeChild(this.textRoot.firstChild);
} }
} }
if (this.indexer) { if (this.indexer) {
@@ -544,15 +578,15 @@ OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
if (this.indexer) { if (this.indexer) {
var insert = this.indexer.insert(node); var insert = this.indexer.insert(node);
if (insert) { if (insert) {
this.root.insertBefore(node, insert); this.vectorRoot.insertBefore(node, insert);
} else { } else {
this.root.appendChild(node); this.vectorRoot.appendChild(node);
} }
} else { } else {
// if there's no indexer, simply append the node to root, // if there's no indexer, simply append the node to root,
// but only if the node is a new one // but only if the node is a new one
if (node.parentNode !== this.root){ 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) {}, 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 * Method: getFeatureIdFromEvent
* *

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 * {Object} Cache for symbol sizes according to their svg coordinate space
*/ */
symbolSize: {}, symbolSize: {},
/**
* Property: isGecko
* {Boolean}
*/
isGecko: null,
/** /**
* Constructor: OpenLayers.Renderer.SVG * Constructor: OpenLayers.Renderer.SVG
@@ -59,6 +65,7 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
OpenLayers.Renderer.Elements.prototype.initialize.apply(this, OpenLayers.Renderer.Elements.prototype.initialize.apply(this,
arguments); arguments);
this.translationParameters = {x: 0, y: 0}; 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) { if (style.cursor != null) {
node.setAttributeNS(null, "cursor", style.cursor); node.setAttributeNS(null, "cursor", style.cursor);
} }
return node; return node;
}, },
@@ -416,11 +424,14 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
/** /**
* Method: createRoot * Method: createRoot
* *
* Parameter:
* suffix - {String} suffix to append to the id
*
* Returns: * Returns:
* {DOMElement} The main root element to which we'll add vectors * {DOMElement}
*/ */
createRoot: function() { createRoot: function(suffix) {
return this.nodeFactory(this.container.id + "_root", "g"); return this.nodeFactory(this.container.id + suffix, "g");
}, },
/** /**
@@ -633,7 +644,61 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
return false; 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 * Method: getComponentString
* *
@@ -828,3 +893,28 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
CLASS_NAME: "OpenLayers.Renderer.SVG" 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"
};

View File

@@ -108,13 +108,19 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
left = left - this.offset.x; left = left - this.offset.x;
top = top - this.offset.y; top = top - this.offset.y;
} }
var org = left + " " + top; var org = left + " " + top;
this.root.setAttribute("coordorigin", org); 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; var size = this.size.w + " " + this.size.h;
this.root.setAttribute("coordsize", size); root.setAttribute("coordsize", size);
}
// flip the VML display Y axis upside down so it // flip the VML display Y axis upside down so it
// matches the display Y axis of the map // matches the display Y axis of the map
this.root.style.flip = "y"; this.root.style.flip = "y";
@@ -132,12 +138,23 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
*/ */
setSize: function(size) { setSize: function(size) {
OpenLayers.Renderer.prototype.setSize.apply(this, arguments); OpenLayers.Renderer.prototype.setSize.apply(this, arguments);
this.rendererRoot.style.width = this.size.w + "px"; // setting width and height on all roots to avoid flicker which we
this.rendererRoot.style.height = this.size.h + "px"; // would get with 100% width and height on child roots
var roots = [
this.root.style.width = this.size.w + "px"; this.rendererRoot,
this.root.style.height = this.size.h + "px"; 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 * Method: createRoot
* Create the main root element * Create the main root element
*
* Parameters:
* suffix - {String} suffix to append to the id
* *
* Returns: * Returns:
* {DOMElement} The main root element to which we'll add vectors * {DOMElement}
*/ */
createRoot: function() { createRoot: function(suffix) {
return this.nodeFactory(this.container.id + "_root", "olv:group"); return this.nodeFactory(this.container.id + suffix, "olv:group");
}, },
/************************************** /**************************************
@@ -762,6 +782,55 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
return node; 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 * Method: drawSurface
@@ -886,3 +955,16 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
CLASS_NAME: "OpenLayers.Renderer.VML" 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
};

View File

@@ -426,7 +426,7 @@
graphicYOffset: -16 graphicYOffset: -16
}); });
var root = renderer.root; var root = renderer.vectorRoot;
if (layer.renderer.CLASS_NAME == 'OpenLayers.Renderer.SVG') { if (layer.renderer.CLASS_NAME == 'OpenLayers.Renderer.SVG') {
feature.style = customStyle1; feature.style = customStyle1;
layer.drawFeature(feature); layer.drawFeature(feature);

View File

@@ -5,7 +5,6 @@
var layer, map; var layer, map;
function test_RootContainer_collectResetRoots(t) { function test_RootContainer_collectResetRoots(t) {
t.plan(4);
map = new OpenLayers.Map("map"); map = new OpenLayers.Map("map");
var layer1 = new OpenLayers.Layer.Vector("layer1"); var layer1 = new OpenLayers.Layer.Vector("layer1");
@@ -13,15 +12,24 @@
layer = new OpenLayers.Layer.Vector.RootContainer("layer_1_2", { layer = new OpenLayers.Layer.Vector.RootContainer("layer_1_2", {
layers: [layer1, layer2] layers: [layer1, layer2]
}); });
// we cannot test this with a renderer that does not hava a rendererRoot
var plan = layer.renderer.rendererRoot ? 4 : 0;
t.plan(plan);
if(plan == 0) {
return;
}
var numRoots = layer.renderer.rendererRoot.childNodes.length;
// addLayers will call setMap() for layer, which will call collectRoots() // addLayers will call setMap() for layer, which will call collectRoots()
map.addLayers([layer1, layer2, layer]); map.addLayers([layer1, layer2, layer]);
t.eq(layer.renderer.rendererRoot.childNodes.length, 3, "layer has correct number of renderer roots"); t.eq(layer.renderer.rendererRoot.childNodes.length, numRoots * 3, "layer has correct number of renderer roots");
t.eq(layer1.renderer.rendererRoot.childNodes.length, 0, "layer1 has no own renderer root"); t.eq(layer1.renderer.rendererRoot.childNodes.length, 0, "layer1 has no own renderer root");
layer.resetRoots(); layer.resetRoots();
t.eq(layer.renderer.rendererRoot.childNodes.length, 1, "roots removed from container"); t.eq(layer.renderer.rendererRoot.childNodes.length, numRoots, "roots removed from container");
t.eq(layer1.renderer.rendererRoot.childNodes.length, 1, "root re-added to original layer"); t.eq(layer1.renderer.rendererRoot.childNodes.length, numRoots, "root re-added to original layer");
} }
function test_RootContainer_getFeatureFromEvent(t) { function test_RootContainer_getFeatureFromEvent(t) {

View File

@@ -17,9 +17,8 @@
OpenLayers.Renderer.Elements.prototype._createRoot = OpenLayers.Renderer.Elements.prototype._createRoot =
OpenLayers.Renderer.Elements.prototype.createRoot; OpenLayers.Renderer.Elements.prototype.createRoot;
var root = document.createElement("div");
OpenLayers.Renderer.Elements.prototype.createRoot = function() { OpenLayers.Renderer.Elements.prototype.createRoot = function() {
return root; return document.createElement("div");
}; };
OpenLayers.Renderer.Elements.prototype._createNode = OpenLayers.Renderer.Elements.prototype._createNode =
@@ -108,7 +107,7 @@
} }
function test_Elements_clear(t) { function test_Elements_clear(t) {
t.plan(1); t.plan(2);
setUp(); setUp();
@@ -121,7 +120,8 @@
r.clear(); r.clear();
t.ok(r.root.childNodes.length == 0, "root is correctly cleared"); t.ok(r.vectorRoot.childNodes.length == 0, "vector root is correctly cleared");
t.ok(r.textRoot.childNodes.length == 0, "text root is correctly cleared");
tearDown(); tearDown();
} }
@@ -134,7 +134,7 @@
var r = create_renderer(); var r = create_renderer();
var element = document.createElement("div"); var element = document.createElement("div");
r.root = element; r.vectorRoot = element;
r.nodeFactory = function(id, type) { r.nodeFactory = function(id, type) {
var element = document.createElement("div"); var element = document.createElement("div");
@@ -149,7 +149,7 @@
r.redrawBackgroundNode = function(id, geometry, style, featureId) { r.redrawBackgroundNode = function(id, geometry, style, featureId) {
b_Node = r.nodeFactory(); b_Node = r.nodeFactory();
b_Node.id = "foo_background"; b_Node.id = "foo_background";
r.root.appendChild(b_Node); element.appendChild(b_Node);
}; };
r.getNodeType = function(geometry, style) { r.getNodeType = function(geometry, style) {
@@ -162,8 +162,8 @@
var style = {'backgroundGraphic': 'foo'}; var style = {'backgroundGraphic': 'foo'};
var featureId = 'dude'; var featureId = 'dude';
r.drawGeometry(geometry, style, featureId); r.drawGeometry(geometry, style, featureId);
t.ok(g_Node.parentNode == r.root, "node is correctly appended to root"); t.ok(g_Node.parentNode == element, "node is correctly appended to root");
t.ok(b_Node.parentNode == r.root, "redrawBackgroundNode appended background node"); t.ok(b_Node.parentNode == element, "redrawBackgroundNode appended background node");
t.eq(g_Node._featureId, 'dude', "_featureId is correct"); t.eq(g_Node._featureId, 'dude', "_featureId is correct");
t.eq(g_Node._style.backgroundGraphic, "foo", "_style is correct"); t.eq(g_Node._style.backgroundGraphic, "foo", "_style is correct");
t.eq(g_Node._geometryClass, 'bar', "_geometryClass is correct"); t.eq(g_Node._geometryClass, 'bar', "_geometryClass is correct");
@@ -178,8 +178,8 @@
style = {'display':'none'}; style = {'display':'none'};
r.drawGeometry(geometry, style, featureId); r.drawGeometry(geometry, style, featureId);
t.ok(g_Node.parentNode != r.root, "node is correctly removed"); t.ok(g_Node.parentNode != element, "node is correctly removed");
t.ok(b_Node.parentNode != r.root, "background node correctly removed") t.ok(b_Node.parentNode != element, "background node correctly removed")
document.getElementById = _getElement; document.getElementById = _getElement;
@@ -474,7 +474,7 @@
var r = create_renderer(null, {zIndexing: true}); var r = create_renderer(null, {zIndexing: true});
var element = document.createElement("div"); var element = document.createElement("div");
r.root = element; r.vectorRoot = element;
document.body.appendChild(element); document.body.appendChild(element);
r.createNode = function(type, id) { r.createNode = function(type, id) {
@@ -505,14 +505,14 @@
return result; return result;
} }
t.eq(r.root.childNodes.length, 1, "root is correctly filled"); t.eq(element.childNodes.length, 1, "root is correctly filled");
t.eq(r.indexer.maxZIndex, 10, "indexer.maxZIndex is correctly filled"); t.eq(r.indexer.maxZIndex, 10, "indexer.maxZIndex is correctly filled");
t.eq(r.indexer.order.length, 1, "indexer.order is correctly filled"); t.eq(r.indexer.order.length, 1, "indexer.order is correctly filled");
t.eq(count(r.indexer.indices), 1, "indexer.indices is correctly filled"); t.eq(count(r.indexer.indices), 1, "indexer.indices is correctly filled");
r.eraseGeometry(geometry); r.eraseGeometry(geometry);
t.eq(r.root.childNodes.length, 0, "root is correctly cleared"); t.eq(element.childNodes.length, 0, "root is correctly cleared");
t.eq(r.indexer.maxZIndex, 0, "indexer.maxZIndex is correctly reset"); t.eq(r.indexer.maxZIndex, 0, "indexer.maxZIndex is correctly reset");
t.eq(r.indexer.order.length, 0, "indexer.order is correctly reset"); t.eq(r.indexer.order.length, 0, "indexer.order is correctly reset");
t.eq(count(r.indexer.indices), 0, "indexer.indices is correctly reset"); t.eq(count(r.indexer.indices), 0, "indexer.indices is correctly reset");
@@ -520,14 +520,14 @@
delete(style.graphicZIndex); delete(style.graphicZIndex);
r.drawGeometry(geometry, style, featureId); r.drawGeometry(geometry, style, featureId);
t.eq(r.root.childNodes.length, 1, "root is correctly filled"); t.eq(element.childNodes.length, 1, "root is correctly filled");
t.eq(r.indexer.maxZIndex, 0, "indexer.maxZIndex is correctly filled"); t.eq(r.indexer.maxZIndex, 0, "indexer.maxZIndex is correctly filled");
t.eq(r.indexer.order.length, 1, "indexer.order is correctly filled"); t.eq(r.indexer.order.length, 1, "indexer.order is correctly filled");
t.eq(count(r.indexer.indices), 1, "indexer.indices is correctly filled"); t.eq(count(r.indexer.indices), 1, "indexer.indices is correctly filled");
r.clear(); r.clear();
t.eq(r.root.childNodes.length, 0, "root is correctly cleared"); t.eq(element.childNodes.length, 0, "root is correctly cleared");
t.eq(r.indexer.maxZIndex, 0, "indexer.maxZIndex is correctly reset"); t.eq(r.indexer.maxZIndex, 0, "indexer.maxZIndex is correctly reset");
t.eq(r.indexer.order.length, 0, "indexer.order is correctly reset"); t.eq(r.indexer.order.length, 0, "indexer.order is correctly reset");
t.eq(count(r.indexer.indices), 0, "indexer.indices is correctly reset"); t.eq(count(r.indexer.indices), 0, "indexer.indices is correctly reset");
@@ -535,7 +535,7 @@
style.graphicZIndex = 12; style.graphicZIndex = 12;
r.drawGeometry(geometry, style, featureId); r.drawGeometry(geometry, style, featureId);
t.eq(r.root.childNodes.length, 1, "root is correctly filled"); t.eq(element.childNodes.length, 1, "root is correctly filled");
t.eq(r.indexer.maxZIndex, 12, "indexer.maxZIndex is correctly filled"); t.eq(r.indexer.maxZIndex, 12, "indexer.maxZIndex is correctly filled");
t.eq(r.indexer.order.length, 1, "indexer.order is correctly filled"); t.eq(r.indexer.order.length, 1, "indexer.order is correctly filled");
t.eq(count(r.indexer.indices), 1, "indexer.indices is correctly filled"); t.eq(count(r.indexer.indices), 1, "indexer.indices is correctly filled");