Merge pull request #140 from aabt/outline

Add label outline to SVG and Canvas renderers
This commit is contained in:
Antoine Abt
2012-01-17 04:45:50 -08:00
6 changed files with 63 additions and 13 deletions

View File

@@ -39,7 +39,9 @@
fontWeight: "bold", fontWeight: "bold",
labelAlign: "${align}", labelAlign: "${align}",
labelXOffset: "${xOffset}", labelXOffset: "${xOffset}",
labelYOffset: "${yOffset}" labelYOffset: "${yOffset}",
labelOutlineColor: "white",
labelOutlineWidth: 3
}}), }}),
renderers: renderer renderers: renderer
}); });

View File

@@ -414,11 +414,13 @@ OpenLayers.Feature.Vector = OpenLayers.Class(OpenLayers.Feature, {
* labelAlign - {String} Label alignment. This specifies the insertion point relative to the text. It is a string * 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 * 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. 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". * alignment: "t"=top, "m"=middle, "b"=bottom. Example values: "lt", "cm", "rb". Default is "cm".
* labelXOffset - {Number} Pixel offset along the positive x axis for displacing the label. Not supported by the canvas renderer. * labelXOffset - {Number} Pixel offset along the positive x axis for displacing the label. Not supported by the canvas renderer.
* labelYOffset - {Number} Pixel offset along the positive y axis for displacing the label. Not supported by the canvas renderer. * labelYOffset - {Number} Pixel offset along the positive y axis for displacing the label. Not supported by the canvas renderer.
* labelSelect - {Boolean} If set to true, labels will be selectable using SelectFeature or similar controls. * labelSelect - {Boolean} If set to true, labels will be selectable using SelectFeature or similar controls.
* Default is false. * Default is false.
* labelOutlineColor - {String} The color of the label outline. Default is 'white'. Only supported by the canvas & SVG renderers.
* labelOutlineWidth - {Number} The width of the label outline. Default is 3, set to 0 or null to disable. Only supported by the canvas & SVG renderers.
* fontColor - {String} The font color for the label, to be provided like CSS. * fontColor - {String} The font color for the label, to be provided like CSS.
* fontOpacity - {Number} Opacity (0-1) for the label * fontOpacity - {Number} Opacity (0-1) for the label
* fontFamily - {String} The font family for the label, to be provided like in CSS. * fontFamily - {String} The font family for the label, to be provided like in CSS.
@@ -445,7 +447,11 @@ OpenLayers.Feature.Vector.style = {
hoverPointRadius: 1, hoverPointRadius: 1,
hoverPointUnit: "%", hoverPointUnit: "%",
pointerEvents: "visiblePainted", pointerEvents: "visiblePainted",
cursor: "inherit" cursor: "inherit",
fontColor: "#000000",
labelAlign: "cm",
labelOutlineColor: "white",
labelOutlineWidth: 3
}, },
'select': { 'select': {
fillColor: "blue", fillColor: "blue",
@@ -464,7 +470,12 @@ OpenLayers.Feature.Vector.style = {
hoverPointRadius: 1, hoverPointRadius: 1,
hoverPointUnit: "%", hoverPointUnit: "%",
pointerEvents: "visiblePainted", pointerEvents: "visiblePainted",
cursor: "pointer" cursor: "pointer",
fontColor: "#000000",
labelAlign: "cm",
labelOutlineColor: "white",
labelOutlineWidth: 3
}, },
'temporary': { 'temporary': {
fillColor: "#66cccc", fillColor: "#66cccc",
@@ -483,7 +494,12 @@ OpenLayers.Feature.Vector.style = {
hoverPointRadius: 1, hoverPointRadius: 1,
hoverPointUnit: "%", hoverPointUnit: "%",
pointerEvents: "visiblePainted", pointerEvents: "visiblePainted",
cursor: "inherit" cursor: "inherit",
fontColor: "#000000",
labelAlign: "cm",
labelOutlineColor: "white",
labelOutlineWidth: 3
}, },
'delete': { 'delete': {
display: "none" display: "none"

View File

@@ -525,10 +525,6 @@ OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, {
* style - {Object} * style - {Object}
*/ */
drawText: function(location, style) { drawText: function(location, style) {
style = OpenLayers.Util.extend({
fontColor: "#000000",
labelAlign: "cm"
}, style);
var pt = this.getLocalXY(location); var pt = this.getLocalXY(location);
this.setCanvasStyle("reset"); this.setCanvasStyle("reset");
@@ -560,6 +556,13 @@ OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, {
this.canvas.measureText('xx').width; this.canvas.measureText('xx').width;
pt[1] += lineHeight*vfactor*(numRows-1); pt[1] += lineHeight*vfactor*(numRows-1);
for (var i = 0; i < numRows; i++) { for (var i = 0; i < numRows; i++) {
if (style.labelOutlineWidth) {
this.canvas.save();
this.canvas.strokeStyle = style.labelOutlineColor;
this.canvas.lineWidth = style.labelOutlineWidth;
this.canvas.strokeText(labelRows[i], pt[0], pt[1] + (lineHeight*i) + 1);
this.canvas.restore();
}
this.canvas.fillText(labelRows[i], pt[0], pt[1] + (lineHeight*i)); this.canvas.fillText(labelRows[i], pt[0], pt[1] + (lineHeight*i));
} }
} else if (this.canvas.mozDrawText) { } else if (this.canvas.mozDrawText) {

View File

@@ -415,6 +415,12 @@ OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
*/ */
LABEL_ID_SUFFIX: "_label", LABEL_ID_SUFFIX: "_label",
/**
* Constant: LABEL_OUTLINE_SUFFIX
* {String}
*/
LABEL_OUTLINE_SUFFIX: "_outline",
/** /**
* Constructor: OpenLayers.Renderer.Elements * Constructor: OpenLayers.Renderer.Elements
* *
@@ -879,6 +885,10 @@ OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
if (label) { if (label) {
this.textRoot.removeChild(label); this.textRoot.removeChild(label);
} }
var outline = document.getElementById(featureId + this.LABEL_OUTLINE_SUFFIX);
if (outline) {
this.textRoot.removeChild(outline);
}
}, },
/** /**

View File

@@ -643,12 +643,25 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
* location - {<OpenLayers.Geometry.Point>} * location - {<OpenLayers.Geometry.Point>}
*/ */
drawText: function(featureId, style, location) { drawText: function(featureId, style, location) {
var drawOutline = (!!style.labelOutlineWidth);
// First draw text in halo color and size and overlay the
// normal text afterwards
if (drawOutline) {
var outlineStyle = OpenLayers.Util.extend({}, style);
outlineStyle.fontColor = outlineStyle.labelOutlineColor;
outlineStyle.fontStrokeColor = outlineStyle.labelOutlineColor;
outlineStyle.fontStrokeWidth = style.labelOutlineWidth;
delete outlineStyle.labelOutlineWidth;
this.drawText(featureId, outlineStyle, location);
}
var resolution = this.getResolution(); var resolution = this.getResolution();
var x = ((location.x - this.featureDx) / resolution + this.left); var x = ((location.x - this.featureDx) / resolution + this.left);
var y = (location.y / resolution - this.top); var y = (location.y / resolution - this.top);
var label = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX, "text"); var suffix = (drawOutline)?this.LABEL_OUTLINE_SUFFIX:this.LABEL_ID_SUFFIX;
label = this.nodeFactory(featureId + suffix, "text");
label.setAttributeNS(null, "x", x); label.setAttributeNS(null, "x", x);
label.setAttributeNS(null, "y", -y); label.setAttributeNS(null, "y", -y);
@@ -656,6 +669,12 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
if (style.fontColor) { if (style.fontColor) {
label.setAttributeNS(null, "fill", style.fontColor); label.setAttributeNS(null, "fill", style.fontColor);
} }
if (style.fontStrokeColor) {
label.setAttributeNS(null, "stroke", style.fontStrokeColor);
}
if (style.fontStrokeWidth) {
label.setAttributeNS(null, "stroke-width", style.fontStrokeWidth);
}
if (style.fontOpacity) { if (style.fontOpacity) {
label.setAttributeNS(null, "opacity", style.fontOpacity); label.setAttributeNS(null, "opacity", style.fontOpacity);
} }
@@ -677,7 +696,7 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
} else { } else {
label.setAttributeNS(null, "pointer-events", "none"); label.setAttributeNS(null, "pointer-events", "none");
} }
var align = style.labelAlign || "cm"; var align = style.labelAlign;
label.setAttributeNS(null, "text-anchor", label.setAttributeNS(null, "text-anchor",
OpenLayers.Renderer.SVG.LABEL_ALIGN[align[0]] || "middle"); OpenLayers.Renderer.SVG.LABEL_ALIGN[align[0]] || "middle");
@@ -692,7 +711,7 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
label.removeChild(label.lastChild); label.removeChild(label.lastChild);
} }
for (var i = 0; i < numRows; i++) { for (var i = 0; i < numRows; i++) {
var tspan = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_tspan_" + i, "tspan"); var tspan = this.nodeFactory(featureId + suffix + "_tspan_" + i, "tspan");
if (style.labelSelect === true) { if (style.labelSelect === true) {
tspan._featureId = featureId; tspan._featureId = featureId;
tspan._geometry = location; tspan._geometry = location;

View File

@@ -584,7 +584,7 @@ OpenLayers.Renderer.SVG2 = OpenLayers.Class(OpenLayers.Renderer.NG, {
} else { } else {
text.setAttributeNS(null, "pointer-events", "none"); text.setAttributeNS(null, "pointer-events", "none");
} }
var align = style.labelAlign || "cm"; var align = style.labelAlign;
text.setAttributeNS(null, "text-anchor", text.setAttributeNS(null, "text-anchor",
OpenLayers.Renderer.SVG2.LABEL_ALIGN[align[0]] || "middle"); OpenLayers.Renderer.SVG2.LABEL_ALIGN[align[0]] || "middle");