5

I need text labels with surrounding a box and a specific fill color. Actually I have the following function which only shows the text but I don't know how to draw a box around the text. Text length is always different, so it must consider the text length. Can anybody show me how to implement it?

function showLabel(mapPos) {
  var textStop = replaceAll(mapPos['text'],"|","");
  //textStop = replaceAll(textStop," "," ");
  //textStop = replaceAll(textStop,"-","–");
  var lblFeature = new ol.Feature({
    geometry: new ol.geom.Point(ol.proj.transform([mapPos['lng'],mapPos['lat']],'EPSG:4326','EPSG:3857'))
  });
  var textPos = new ol.style.Text({
    textAlign: 'left',
    textBaseline: 'middle',
    //font: 'bold 16px arial',
    font: '16px arial',
    text: textStop,
    fill: new ol.style.Fill({color: '#000000'}),
    //stroke has bad performance
    //stroke: new ol.style.Stroke({color: mapPos['color'], width: 1}),
    offsetX: 16,
    offsetY: -16,
    rotation: 0
  });
  //TODO: Draw a box arround the text
  var lblStyle = new ol.style.Style({
    text: textPos
  });
  lblFeature.setStyle(lblStyle);
  return lblFeature;
} //End showLabel()

So far I do not get any error in the console.log. I guess it must be done near my TODO Label. mapPos is an array where in mapPos['text'] is text available.

In version 2 I was able to do it like this function:

var markerFlag = new Array();
function showPosition(mapPos) {
  var size = new OpenLayers.Size(24,24);
  var icon = new OpenLayers.Icon('../images/map24/'+mapPos['symbolid']+'.gif',size);
  var myLonLat = new OpenLayers.LonLat(mapPos['lng'],mapPos['lat']).transform(
    proj4326, //Transform from WGS 1984
    projmerc //To Spherical Mercator Projection
  );
  var textPos = replaceAll(mapPos['text'],"|","");
  textPos = replaceAll(textPos," "," ");
  textPos = replaceAll(textPos,"-","–");
  var myOptionsText = {
    mouseOver: false,
    labelOffsetTop: '-58px',
    labelOffsetLeft: '22px',
    backGroundColor: mapPos['color']
  };
  layer_position.addMarker(new OpenLayers.Marker(myLonLat,icon.clone(),myOptionsText));
  if (mapPos['text']) {
    layer_banner.addMarker(new OpenLayers.Marker.Label(myLonLat,icon.clone(),textPos,myOptionsText));
  }
} //End showPosition()

4 Answers 4

6

You may be able to achieve the desired effect using the ol.Overlay class of OpenLayers:

Here is a small snippet:

// a coordinate in the view projection
var pos = ol.proj.fromLonLat([16.3725, 48.208889]);

// Vienna label
var vienna = new ol.Overlay({
  position: pos,
  element: document.getElementById('vienna')
});
map.addOverlay(vienna);

In your HTML you'll need a matching element:

<a class="overlay" id="vienna" target="_blank" 
  href="http://en.wikipedia.org/wiki/Vienna">Vienna</a>

And in the CSS the following styles:

#vienna {
  text-decoration: none;
  color: white;
  font-size: 11pt;
  font-weight: bold;
  text-shadow: black 0.1em 0.1em 0.2em;
  padding: 0.2em;
  background-color: rgba(255,255,255,0.3);
  border: 1px solid rgba(255,255,255,0.8);
}

This looks as follows

The rendered ol.Overlay

Here is the running jsfiddle, which is just the original overlay example with a bit of CSS added.

1
  • NB this would not be rendered on the canvas
    – Rivenfall
    Commented Mar 4, 2020 at 9:22
4

I managed to get this done by using a SVG shape behind the Text element and fill-up the shape with color, to achieve the same result.

I had to resize the background shape to match the size of text element and it wasn't as straightforward as I was hoping for.

The main blocks were:

  • SVG items don't have background style, and fill attribute on text element would only change the text color not its background color. That's why I had to use another shape in the first place.
  • When a SVG element is used as the source of an image element, all the javascript code inside it is ignored for security reasons. Therefore, you can't dynamically check/change sizes.
  • Shapes and Paths inside the SVG element are not part of DOM, so you can't access or style them with JavaScript or CSS.

First thing I did was to use the following code -- that I found somewhere on Stackoverflow, to get the width of the rendered text in pixels:

/* Get the rendered size of a text, in pixels */
/* _text: "Blah blah" */
/* _fontStyle: "Normal 12px Arial" */
function getTextWidth ( _text, _fontStyle ) {

    var canvas = undefined,
        context = undefined,
        metrics = undefined;

    canvas = document.createElement( "canvas" )

    context = canvas.getContext( "2d" );

    context.font = _fontStyle;
    metrics = context.measureText( _text );

    return metrics.width;
}

Then I used it inside my other function which was generating the marker and its label:

...
labelFontStyle = "Normal 12px Arial";

labelWidth = getTextWidth( _text, labelFontStyle );
labelWidth = labelWidth + 10;

iconSVG = '<svg ' +
            'version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" ' +
            'x="0px" y="0px" width="' + labelWidth + 'px" height="16px" ' +
            'viewBox="0 0 ' + labelWidth + ' 16" enable-background="new 0 0 ' + labelWidth + ' 16" xml:space="preserve">'+
                '<rect x="0" y="0" width="' + labelWidth + '" height="16" stroke="#000000" fill="#DEEFAE" stroke-width="2"></rect>' +
                '<text x="5" y="13" fill="#000000" font-family="Arial" font-size="12" font-weight="normal">' + _text + '</text>' +
        '</svg>';

imageElement = new Image();
imageElement.src = 'data:image/svg+xml,' + escape( iconSVG );

iconStyle = new ol.style.Style({
    "image": new ol.style.Icon({
        "img": imageElement,
        "imgSize":[labelWidth, 66],
        "anchor": [0.5, 0.5],
        "offset": [0, -50]
    })
});

feature = new ol.Feature({
    "geometry": new ol.geom.Point(
        ol.proj.fromLonLat( [_longitude, _latitude] )
    )
});

feature.setStyle( iconStyle );

return feature;
2

Starting from version 4.6.0, there are now BackgroundFill, BackgroundStroke and Padding properties for ol.style.Text. You can set them to draw rectangles behind your labels :

backgroundFill: new ol.style.Fill({color: 'black'})

https://openlayers.org/en/latest/apidoc/module-ol_style_Text-Text.html#setBackgroundFill

5
  • Thank you, this is the answer I needed. Your link was dead so I edited it to this current one: openlayers.org/en/latest/apidoc/… Commented Aug 16, 2019 at 17:29
  • 1
    Anyone has a problem with this? where the background fills a whole screen in height
    – Rivenfall
    Commented Mar 4, 2020 at 9:24
  • @Rivenfall Did you find a solution?
    – Tim Autin
    Commented May 2, 2022 at 13:37
  • @TimAutin It's been a while I don't remember this specific issue
    – Rivenfall
    Commented May 3, 2022 at 15:36
  • Someone having problems with background fill and safari browser. In my case the text overlaps box in safari. Chrome works fine
    – Hanzo
    Commented Dec 21, 2022 at 10:56
1

Actually I do not know how to add a rectangle to a label in OL, but if it's just for readability purposes maybe a stroke around the labels like shown in this OpenLayers Example could fit your needs.

1
  • Yes I've seen this example, but it would not help. A stroke will also not help because using a lot of labels like I do it gives a very bad performance. so this is not the case.
    – Holimatic
    Commented Mar 4, 2016 at 9:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.