OpenLayers JavaScript – Line Labels with ‘Line’ Placement Not Working

javascriptlabelingopenlayers

Using OpenLayers 6.13.0, I'm attempting to get my lines layer to have labels that follow the line (ie, parallel to the line, and bending with the line) as per the example at: https://openlayers.org/en/latest/examples/street-labels.html

In order to achieve this, I'm using the ol.style.Text property placement set to line (default is point). As required by line placement, the geometry type specified in the geoJSON for this layer is MultiLineString.

If I specify the placement as 'point', then the labels appear fine, but are simply straight, horizontal and on the centre-point of the line. If I specify the placement as 'line', then I get no label at all. No errors are logged in the javascript console.

My style is defined as per below.

What am I doing wrong here?

var layerStyles = function (feature, resolution) {
    return [
        new ol.style.Style({stroke: new ol.style.Stroke({color: '#FFFFFF', width: 4.000000})}),
        new ol.style.Style({stroke: new ol.style.Stroke({color: '#FF0000', width: 2.000000})}),
        new ol.style.Style({text: new ol.style.Text({
            text: feature.get('label')['0'],
            font: ' 10pt sans-serif',
            fill: new ol.style.Fill({color: '#ff0000ff'}),
            stroke: new ol.style.Stroke({color: '#ffffffff', width: 3.0}),
            placement: 'line'
        })})
    ]
};

Best Answer

As @Mike wrote, there are some additional settings that affect labels with a line placement, including maxAngle and overflow. The example linked to in @Mike's comment makes it very easy to test some of the relevant labelling settings, including maxAngle.

I my test case, the maxAngle default of 45 was the issue (these lines were from off-track bushwalking (hiking) track recording, which zig-zags all over the place and even include small loops). In other cases, my app is likely to have lines somewhat shorter than the label from time to time, and so the overflow setting would also make a difference in those cases.

My style works after updating it with these two settings, as follows:

var layerStyles = function (feature, resolution) {
    return [
        new ol.style.Style({stroke: new ol.style.Stroke({color: '#FFFFFF', width: 4.000000})}),
        new ol.style.Style({stroke: new ol.style.Stroke({color: '#FF0000', width: 2.000000})}),
        new ol.style.Style({text: new ol.style.Text({
            text: feature.get('label')['0'],
            font: ' 10pt sans-serif',
            fill: new ol.style.Fill({color: '#ff0000ff'}),
            stroke: new ol.style.Stroke({color: '#ffffffff', width: 3.0}),
            overflow: 'true',
            maxAngle: 360,
            placement: 'line'
        })})
    ]
};

Of course some of the labels are quite unreadable with that much bend (angle) in them, but I can now at least decide on whether it is better that way than no label, and consider setting a different maxAngle value.

UPDATE - Simplification of Geometry

Just for the sake of completeness, I have found it necessary to simplify the geometry of the lines for these labels, as the complex angles in the lines makes the labels unreadable otherwise. So I now use two separate layers: One layer with a style that includes only the lines themselves (no labels) and which uses the original/unmodified geometry; a second layer with a style that does NOT render the lines at all, and only displays the labels and uses a simplified geometry (simplification factor is automatically calculated using map scale).

This simplification of geometry only for the labels works quite well. There are some odd anomalies that occur when zooming in/out after the map has been rendered, but that may be a topic for another question.

Related Question