Is it possible to set different colors to each segment of a LineString without meeting a huge performance hit?
Asked Answered
W

2

5

My openlayers 3 application draws several LineString features on the map (from a few dozen up to 2000-3000).

When setting different colors for each segment of the LineStrings, I meet a huge performance hit (starting from just a few LineStrings on the map). The zoom and pan become completely unresponsive making my aplication not usable in this form.

Since I don't want to set a new geometry for each segment (but only change its color), I imagine there must be a more performance effective way of achieving this ?

Here's my code :

var styleFunction = function(feature, resolution) {

    var i = 0, geometry = feature.getGeometry();

    geometry.forEachSegment(function (start, end) {

        color = colors[i];

        styles.push(new ol.style.Style({
            geometry: new ol.geom.LineString([start, end]),
            fill: new ol.style.Fill({
                color: color
            }),
            stroke: new ol.style.Stroke({
                color: color,
                width: 2
            })
        }));

        i++;
    });

    return styles;
}

var vectorLayer = new ol.layer.Vector({
    source: vectorSource,
    style: styleFunction
});
Wil answered 29/10, 2015 at 14:15 Comment(2)
How many colors are there? How many segments are there on the LineStrings? Will the geometries ever change after initial load?Micheal
@AlvinLindstam There are 5 - 400 colors depending on user settings. Each linestring has between 1 and 400 segments (115 average). The linestring geometries never change after being set. However, width/colors change when hovering/clicking on a feature (feature.setStyle) or selecting a different color setting (color array updated).Wil
W
2

Since we could not solve the problem, even with tsauerwein's recommendations, this feature was put to the fridge for a while. Now we got back to it with fresh ideas and actually found a "solution".

Something that I made not clear in the question is that segments of a LineString are colored according to a property of their starting point. Several consecutive segments may thus share the same color if the value of this property doesn't change between them.

The idea is to avoid creating a new style for each and every segment but rather to create a new style only when necessary (when the color changes) :

let i = 0,
    color = '#FE2EF7', //pink. should not be displayed
    previousColor = '#FE2EF7',
    lineString,
    lastCoordinate = geometry.getLastCoordinate();

geometry.forEachSegment(((start, end) => {

        color = this.getColorFromProperty(LinePoints[i].myProperty);

        //First segment
        if (lineString == null) {
            lineString = new ol.geom.LineString([start, end]);
        } else {

            //Color changes: push the current segment and start a new one
            if (color !== previousColor) {

                styles.push(new ol.style.Style({
                    geometry: lineString,
                    fill: new ol.style.Fill({
                        color: previousColor
                    }),
                    stroke: new ol.style.Stroke({
                        color: previousColor,
                        width: 2
                    })
                }));

                lineString = new ol.geom.LineString([start, end]);

            } else {
                //Color is same: continue the current segment
                lineString.appendCoordinate(end);
            }

            //Last segment
            if (end[0] === lastCoordinate[0] && end[1] === lastCoordinate[1]) {
                styles.push(new ol.style.Style({
                    geometry: lineString,
                    fill: new ol.style.Fill({
                        color: color
                    }),
                    stroke: new ol.style.Stroke({
                        color: color,
                        width: 2
                    })
                }));
            }
        }

        previousColor = color;
        i++;
    }
Wil answered 21/6, 2018 at 12:46 Comment(0)
I
7

There are a few things that you can try to optimize:

Cache fill and stroke style

var fillStyles = colors.map(function(color, i) {
  return new ol.style.Fill({
    color: color
  })
});

var strokeStyles = colors.map(function(color, i) {
  return new ol.style.Stroke({
      color: color,
      width: 2
  })
});

Cache the style for each feature

vectorSource.forEach(function(feature, i) {
  var geometry = feature.getGeometry();
  var styles = [];
  var i = 0;

  geometry.forEachSegment(function (start, end) {
    styles.push(new ol.style.Style({
        geometry: new ol.geom.LineString([start, end]),
        fill: fillStyles[i],
        stroke: strokeStyles[i]
    }));
    i++;
  });
  feature.setStyle(styles);
});

Disable that the rendering is updated during animations and interactions

var vectorLayer = new ol.layer.Vector({
    source: vectorSource,
    updateWhileAnimating: false,
    updateWhileInteracting: false
});

See ol.layer.Vector.

If all this doesn't help, you might also want to take a look at ol.source.ImageVector(example).

Immaterialism answered 30/10, 2015 at 8:9 Comment(0)
W
2

Since we could not solve the problem, even with tsauerwein's recommendations, this feature was put to the fridge for a while. Now we got back to it with fresh ideas and actually found a "solution".

Something that I made not clear in the question is that segments of a LineString are colored according to a property of their starting point. Several consecutive segments may thus share the same color if the value of this property doesn't change between them.

The idea is to avoid creating a new style for each and every segment but rather to create a new style only when necessary (when the color changes) :

let i = 0,
    color = '#FE2EF7', //pink. should not be displayed
    previousColor = '#FE2EF7',
    lineString,
    lastCoordinate = geometry.getLastCoordinate();

geometry.forEachSegment(((start, end) => {

        color = this.getColorFromProperty(LinePoints[i].myProperty);

        //First segment
        if (lineString == null) {
            lineString = new ol.geom.LineString([start, end]);
        } else {

            //Color changes: push the current segment and start a new one
            if (color !== previousColor) {

                styles.push(new ol.style.Style({
                    geometry: lineString,
                    fill: new ol.style.Fill({
                        color: previousColor
                    }),
                    stroke: new ol.style.Stroke({
                        color: previousColor,
                        width: 2
                    })
                }));

                lineString = new ol.geom.LineString([start, end]);

            } else {
                //Color is same: continue the current segment
                lineString.appendCoordinate(end);
            }

            //Last segment
            if (end[0] === lastCoordinate[0] && end[1] === lastCoordinate[1]) {
                styles.push(new ol.style.Style({
                    geometry: lineString,
                    fill: new ol.style.Fill({
                        color: color
                    }),
                    stroke: new ol.style.Stroke({
                        color: color,
                        width: 2
                    })
                }));
            }
        }

        previousColor = color;
        i++;
    }
Wil answered 21/6, 2018 at 12:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.