openlayers
openlayers copied to clipboard
Style expression performance
Describe the bug When adding more then 10-15k features to a layer, the layer flashes and becomes nearly impossible to navigate.
To Reproduce See sandbox here: https://codesandbox.io/p/sandbox/vector-layer-forked-yqm88r?file=%2Fmain.js
Clicking on the page will render 3k new points. Notice how after 3-5 clicks the display becomes unusable.
Expected behavior Ideally style expressions could support 100k+ features.
Since that is a considerable number of features, have you tried to lazy load them? Load 1k, wait a few seconds and load another 1k
In the example above, the performance issue that I see has to do with the number of Image
elements being created (one per point feature). You can see similarly bad performance if you do something like this:
const vectorLayer = new VectorLayer({
source: new VectorSource({}),
style: () =>
new Style({
image: new CircleStyle({
radius: 3,
fill: new Fill({color: '#e4f22'}),
stroke: new Stroke({color: '#e4f22', width: 2}),
}),
}),
});
(And then try to render many thousands of point features.)
With style functions, you can write more optimal code by creating the shared image objects once. There is not currently a way to do the same with style expressions.
@MoonE has put up #15169 to use a shared cache for regular shape symbolizers (like circles). When I run the above example with the branch from #15169, the performance is significantly better.
i have same issue when i add vector source with lot of features, example here
In the example above, the performance issue that I see has to do with the number of
Image
elements being created (one per point feature). You can see similarly bad performance if you do something like this:const vectorLayer = new VectorLayer({ source: new VectorSource({}), style: () => new Style({ image: new CircleStyle({ radius: 3, fill: new Fill({color: '#e4f22'}), stroke: new Stroke({color: '#e4f22', width: 2}), }), }), });
(And then try to render many thousands of point features.)
With style functions, you can write more optimal code by creating the shared image objects once. There is not currently a way to do the same with style expressions.
@MoonE has put up #15169 to use a shared cache for regular shape symbolizers (like circles). When I run the above example with the branch from #15169, the performance is significantly better. I haven't looked closely at #15169 yet, but it may be that we want to create a more general image cache rather than just a regular shape cache (so we would get the same benefit when people use image urls in styles).
This is exactly the issue! Here's an example of how we utilize a cache for styles.
function createColorStyle(
fillColor,
strokeColor,
strokeWidth,
opacity = 0.5,
radius = 1
) {
const cachedStyle =
createColorStyleCache[
`${fillColor},${strokeColor},${strokeWidth},${opacity},${radius}`
];
if (cachedStyle) return cachedStyle;
let colorArray = [0, 0, 0, 0];
let colorWithOpacity = [0, 0, 0, 0];
if (fillColor) {
colorArray = hexToRgb(fillColor);
colorWithOpacity = colorArray.slice();
colorWithOpacity[3] = opacity;
}
const fill = new Fill({ color: colorWithOpacity });
const circleStyle = new Circle({
radius: radius,
fill: new Fill({ color: colorArray }),
stroke: createStroke(strokeColor, strokeWidth, 1),
});
circleStyle.setOpacity(opacity);
const style = new Style({
fill,
stroke: createStroke(strokeColor, strokeWidth, opacity),
image: circleStyle,
});
createColorStyleCache[
`${fillColor},${strokeColor},${strokeWidth},${opacity},${radius}`
] = style;
return style;
}