I have noticed that I get a lot of lag while drawing with terra draw on maplibre (mapbox really since its the adapter that does everything).
For some reason it gets notoriously worse when you have the browser console open.
First I came to that conclusion since in the demo https://terradraw.io/ (it uses leaflet) it goes perfectly.
I was able to improve the performance notoriously locally and I'm going to explain all the process.
The idea is to reduce the most changes as possible to the maplibre sources and layers when handling the pointermove event (which is the most frequently fired)
I will start from the most impacting things
Updating closing points
Is it really necessary to update the closing points every time the user moves the pointer?, I think this line could be deleted without impacting anywhere. And it improves the fluency of the movements a lot.
|
if (this.closingPoints.ids.length) { |
|
this.closingPoints.update(updatedCoordinates); |
|
} |
This way instead of calling the _render() method of the adapter 2 times per movement now it should only be called once.
Only updating source if necessary
Currently all the sources are updated even if there wasn't any change on them.
|
const pointId = this._setGeoJSONLayerData<Point>( |
|
mode, |
|
"Point", |
|
points as Feature<Point>[] |
|
); |
|
this._setGeoJSONLayerData<LineString>( |
|
mode, |
|
"LineString", |
|
linestrings as Feature<LineString>[] |
|
); |
|
|
|
this._setGeoJSONLayerData<Polygon>( |
|
mode, |
|
"Polygon", |
|
polygons as Feature<Polygon>[] |
|
); |
Why don't add a validation for each like
if(points.length || areDeleted)
if(linestrings.length || areDeleted)
if(polygons.length || areDeleted)
And of course define the areDeleted
const at line 366, so that we would correctly delete features when needed.
const areDeleted = changes.deletedIds.length;
If we do this we should divide the features in changed and unchanged so that we ONLY update a source when there are changed features, and not every time we have unchanged (which is usual when user have draw different features already).
Creating a lot of sources and layers
Right now for each mode terra draw creates 3 sources and 4 layers, so potentially if a user has all modes enabled there could be 30 sources and 40 layers being updated on each movement.
There could be only 3 sources and 4 layers no matter the amount of modes.
Again, the most we can reduce the amount of sources and layers to update per movement the better.
I guess it was first created that way so that each mode would have its own layer potentially with its own different style.
But if that's true then each feature style is being described in its properties.
|
properties.pointColor = styles.pointColor; |
|
properties.pointOutlineColor = styles.pointOutlineColor; |
|
properties.pointOutlineWidth = styles.pointOutlineWidth; |
|
properties.pointWidth = styles.pointWidth; |
And then being styled based on those properties using maplibre expressions
|
"fill-color": ["get", "polygonFillColor"], |
|
"fill-opacity": ["get", "polygonFillOpacity"], |
So it seems like there is not really a need of having separate sources and layers per mode.
We could also delete the filters of all the layers
|
filter: [ |
|
"all", |
|
["match", ["geometry-type"], "Polygon", true, false], |
|
["match", ["get", "mode"], mode, true, false], |
|
], |
Option to use maplibre outline instead of creating other layer
So far applying the above improvements the performance get much better but even having to update 2 layers it seems to cause a little bit of lag in maplibre, so the proposal is to have an option in maplibre adapter to avoid running this line.
|
this._addFillOutlineLayer(id, mode, beneath); |
And instead use the outline of maplibre (which I understand is not highly customizable, but at least wont impact as much in the performance).
https://maplibre.org/maplibre-style-spec/layers/#paint-fill-fill-outline-color
And apply it here:
|
"fill-color": ["get", "polygonFillColor"], |
|
"fill-opacity": ["get", "polygonFillOpacity"], |
like: "fill-outline-color": ["get", "polygonOutlineColor"]
This way people could chose between the best performance or slightly worse but with better outlines.
Results
Doing all this we dramatically reduce the amount of work have to be done by maplibre and the fluency of the movements.
Description |
Pointer Movement |
Render Calls |
Sources Updated |
Layers Impacted |
All modes enabled, current behavior |
1 |
2 |
30 |
40 |
All modes enabled, fixed with outline in another layer |
1 |
1 |
1 |
2 |
All modes enabled, fixed without outline |
1 |
1 |
1 |
1 |
Sending a PR for you to test it, and maybe use it to apply the changes by your own. Thanks for the help ❤️