[OpenLayers-Trac] Re: [OpenLayers] #2965: Add halos to vector labels

OpenLayers trac-20090302 at openlayers.org
Sun Mar 18 14:41:26 EDT 2012


#2965: Add halos to vector labels
----------------------+-----------------------------------------------------
 Reporter:  rdewit    |       Owner:                  
     Type:  feature   |      Status:  new             
 Priority:  minor     |   Milestone:  2.13 Release    
Component:  Renderer  |     Version:  2.10            
 Keywords:            |       State:  Needs Discussion
----------------------+-----------------------------------------------------

Comment(by DonaldKerr):

 I have had a look at this today and have made some changes to the method
 which I think works well with various font sizes and families. I have
 checked all alignments and it now gets as close to textbox as I think may
 be possible.


 {{{
     /**
      * Method: drawText
      * This method is only called by the renderer itself.
      *
      * Parameters:
      * featureId - {String}
      * style -
      * location - {<OpenLayers.Geometry.Point>}
      * hasOutline - {Boolean}
      */
     drawText: function (featureId, style, location, hasOutline) {

         if (style.labelHaloColor) {
             var haloStyle = OpenLayers.Util.extend({}, style);
             haloStyle.fontStrokeColor = haloStyle.labelHaloColor;
             haloStyle.fontStrokeWidth = haloStyle.labelHaloWidth || 2;
             delete haloStyle.labelHaloColor;
             this.drawText(featureId, haloStyle, location, true);
         }

         var label = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX +
 (hasOutline ? this.HALO_ID_SUFFIX : ""), "olv:shape");

         if(!label.parentNode) {
             this.textRoot.appendChild(label);
         }

         var resolution = this.getResolution();
         label.style.left = ((location.x/resolution - this.offset.x) | 0) +
 "px";
         label.style.top = ((location.y/resolution - this.offset.y) | 0) -
 (style.fontSize / 2) - 4 +"px"; // textpath off by 4 compared to textbox
         label.style.flip = "y";
         label.style.position = "absolute";
         label.style.width = "1px";
         label.style.height = "1px";
         label.style.antialias = "true";

         var myFill = document.createElement("olv:fill");
         myFill.on = "true";
         myFill.color = style.fontColor;
         label.appendChild(myFill);

         var myStroke = document.createElement("olv:stroke");
         if (style.fontStrokeColor) {
             myStroke.on = "true";
             myStroke.color = style.fontStrokeColor;
         } else {
             myStroke.on = "false";
         }
         if (style.fontStrokeWidth) {
             myStroke.weight = style.fontStrokeWidth;
         }
         label.appendChild(myStroke);

         var myPath = document.createElement("olv:path");
         myPath.textpathok = "True";
         myPath.v = "m 0,0 l 1,0 e";
         label.appendChild(myPath);

         var textpath = document.createElement("olv:textpath");
         textpath.on = "true";
         textpath.fitpath = "false";
         textpath.string = style.label;
         label.appendChild(textpath);

         if (style.cursor != "inherit" && style.cursor != null) {
             label.style.cursor = style.cursor;
         }

         textpath.style.font = "10 "+style.fontFamily;
         if (style.fontSize) {
                     textpath.style.fontSize = style.fontSize;
         }

         if (style.fontOpacity) {
             myFill.opacity = style.fontOpacity;
             myStroke.opacity = style.fontOpacity;
         }

         if (style.fontWeight) {
             textpath.style.fontWeight = style.fontWeight;
         }

         if (style.fontStyle) {
             textpath.style.fontStyle = style.fontStyle;
         }

         if(style.labelSelect === true) {
             label._featureId = featureId;
             textpath._featureId = featureId;
             textpath._geometry = location;
             textpath._geometryClass = location.CLASS_NAME;
         }

         var align = style.labelAlign || "cm";
         if (align.length == 1) {
             align += "m";
         }

         var hAlign;
         switch (align.substr(0,1)) {
             case 'l': hAlign = "left"; break;
             case 'c': hAlign = "center"; break;
             case 'r': hAlign = "right"; break;
         }
         textpath.style['v-text-align'] = hAlign;

         var yshift = parseInt(textpath.style.fontSize) *
             (OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(1,1)]);
         label.style.top = parseInt(label.style.top)+yshift+"px";
     },
 }}}

 The changes made are as follows:
 {{{
 // somehow our label is about 10 pixels off vertically
 // TODO: find out what causes it and then fix it
 yshift -= 7;
 label.style.top = parseInt(label.style.top)+yshift+"px";
 }}}

 Changed:

 {{{
 label.style.top = ((location.y/resolution - this.offset.y) | 0) + "px";
 }}}

 to:

 {{{
 label.style.top = ((location.y/resolution - this.offset.y) | 0) -
 (style.fontSize / 2) - 4 +"px"; // textpath off by 4 compared to textbox
 }}}

 Unlike textbox, textpath draws on the shape center line and is therefore
 always vertically half the fontSize from where it needs to be. If the
 shape.style.top is changed to reflect this then the vertical placement
 later in the code now works.
 {{{
 OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(1,1)]
 }}}

 The above code returns
 {{{
     "t": 0,
     "m": .5,
     "b": 1
 }}}

 So, the label is now correctly position vertically based on its new
 starting top position.

 {{{
 // Set the vertical align by using the fontSize as the height
 // We default to 0.5 in case style.labelAlign comes from a
 // variable like "${labelAlign}" as happens when an OL.Graticule
 // ends up in a GeoExt LegenPanel
 var yshift = parseInt(textpath.style.fontSize) *
     (OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(1,1)] || 0.5);
 }}}

 Is the "0.5" default still necessary? If labelAlign isn't specified then
 the label will be half the fontSize out.

 textpath would appear to be consistently off by 4 pixels in the y axis.
 This seems to be the case for all font sizes. I have included this offset
 in the code.

 {{{
 // Somehow our label is 1 pixel off horizontally
 // TODO: find out what causes it and then fix it
 var xshift = 1; // hack
 label.style.left = parseInt(label.style.left)+xshift+"px";
 }}}

 I have not found this to be the case and have removed this piece of code.

 There was a problem setting the fontFamily. This was dealt with in a
 previous post.
 {{{
 // Setting the font family does not seem to work
 // TODO: make this work!
 if (style.fontFamily) {
     textpath.style.fontfamily = style.fontFamily;
     //textpath.style['font-family'] = style.fontFamily;
     label.style.fontFamily = style.fontFamily;
 }
 // We need to set the fontSize to prevent JS errors
 textpath.style.fontSize = style.fontSize || 10;
 }}}

 Changed the above to:

 {{{
 textpath.style.font = "10 " + style.fontFamily;
 if (style.fontSize) {
     textpath.style.fontSize = style.fontSize;
 }
 }}}

 path changed to:
 {{{
 myPath.v = "m 0,0 l 1,0 e";
 }}}

 There are probably some other changes that I've made but I cannot remember
 exactly.

 Having looked at Antoine's (aabt's) SVG patch
 ==
 https://github.com/aabt/openlayers/blob/be585c5f232bde95a8f54da8048c00bf1ac392c2/lib/OpenLayers/Renderer/SVG.js
 ==

 The following seems a neater way to do the initial method code:
 {{{
 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);
         }
 }}}

 I suggested previously that I think you should adopt the naming convention
 of

 {{{
 labelOutlineColor
 labelOutlineWidth
 }}}

 I think it would also be a good idea to also adopt Antoine's way of
 setting up the method for outlines. It would mean that the VML drawText
 method would be more consistent across renderers.

 I hope the above is helpful and that you can get it into 2.12. It would
 certainly be useful for my purposes where I cannot move to IE9 (SVG) and
 am forced to use IE8 (VML). If I can be of assistance then please let me
 know.

 Regards,

 Donald

-- 
Ticket URL: <http://trac.openlayers.org/ticket/2965#comment:28>
OpenLayers <http://openlayers.org/>
A free AJAX map viewer


More information about the Trac mailing list