Skip to content

Commit cef407a

Browse files
committed
Provide faster setStyle implementation
This work sponsored by [Integrated Transport Planning](https://www.itpworld.net) and the World Bank. Performance still wasn't good enough when styling 20000 features at once. Turns out it's something to do with how shiny sends different data types between R and JS. Shiny passes arrays much faster than objects. I don't know if the delay is in encoding or transmission. You can then reassemble the required objects in JS basically for free. The same optimisation will probably be possible for addPolylines, etc. At the moment shiny sends an array of arrays of arrays of objects of arrays. Which is unnecessarily convoluted and requires the creation of N small objects and at least 4N small arrays. I suspect that it will be faster to send a smaller number of larger arrays -- something like the format provided by st_coordinates -- or possibly to use a space efficient encoding scheme like Google's encoded polyline (plugin available for leaflet).
1 parent 0dd6252 commit cef407a

File tree

4 files changed

+36
-8
lines changed

4 files changed

+36
-8
lines changed

R/methods.R

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,12 @@ setView <- function(map, lng, lat, zoom, options = list()) {
4848
#' setStyle("zones", styles)
4949
#' }
5050
#' @export
51-
setStyle = function(map, group, styles, label = NULL) {
52-
invokeMethod(map, NULL, "setStyle", group, styles, label)
51+
setStyle = function(map, group, styles, label = NULL, offset = 0) {
52+
invokeMethod(map, NULL, "setStyle", group, styles, label, offset)
53+
}
54+
55+
setStyleFast = function(map, group, color, label = NULL) {
56+
invokeMethod(map, NULL, "setStyleFast", group, color, label)
5357
}
5458

5559
#' @describeIn map-methods Flys to a given location/zoom-level using smooth pan-zoom.

inst/htmlwidgets/leaflet.js

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1285,21 +1285,34 @@ exports.default = methods;
12851285
/** Much more performant way to style loaded geometry */
12861286

12871287
methods.setStyle = function (group, styles, labels) {
1288+
var offset = arguments.length <= 3 || arguments[3] === undefined ? 0 : arguments[3];
1289+
12881290
window.map = this;
12891291
var layers = this.layerManager.getLayerGroup(group).getLayers();
12901292

12911293
if (styles) {
12921294
for (var i = 0; i < styles.length; i++) {
1293-
layers[i].setStyle(styles[i]);
1295+
layers[i + offset].setStyle(styles[i]);
12941296
}
12951297
}
12961298
if (labels) {
12971299
for (var _i = 0; _i < styles.length; _i++) {
1298-
layers[_i].bindTooltip(labels[_i]);
1300+
layers[_i + offset].bindTooltip(labels[_i]);
12991301
}
13001302
}
13011303
};
13021304

1305+
/** Much more performant way to style loaded geometry */
1306+
methods.setStyleFast = function (group, colors, labels) {
1307+
window.map = this;
1308+
var layers = this.layerManager.getLayerGroup(group).getLayers();
1309+
1310+
for (var i = 0; i < colors.length; i++) {
1311+
layers[i].setStyle({ color: colors[i], fillColor: colors[i] });
1312+
layers[i].bindTooltip(labels[i]);
1313+
}
1314+
};
1315+
13031316
function mouseHandler(mapId, layerId, group, eventName, extraInfo) {
13041317
return function (e) {
13051318
if (!_htmlwidgets2.default.shinyMode) return;

javascript/src/methods.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,33 @@ let methods = {};
1414
export default methods;
1515

1616
/** Much more performant way to style loaded geometry */
17-
methods.setStyle = function(group, styles, labels) {
17+
methods.setStyle = function(group, styles, labels, offset = 0) {
1818
window.map = this;
1919
let layers = this.layerManager.getLayerGroup(group).getLayers();
2020

2121
if (styles) {
2222
for (let i = 0; i < styles.length; i++) {
23-
layers[i].setStyle(styles[i]);
23+
layers[i + offset].setStyle(styles[i]);
2424
}
2525
}
2626
if (labels) {
2727
for (let i = 0; i < styles.length; i++) {
28-
layers[i].bindTooltip(labels[i]);
28+
layers[i + offset].bindTooltip(labels[i]);
2929
}
3030
}
3131
};
3232

33+
/** Much more performant way to style loaded geometry */
34+
methods.setStyleFast = function(group, colors, labels) {
35+
window.map = this;
36+
let layers = this.layerManager.getLayerGroup(group).getLayers();
37+
38+
for (let i = 0; i < colors.length; i++) {
39+
layers[i].setStyle({color: colors[i], fillColor: colors[i]})
40+
layers[i].bindTooltip(labels[i]);
41+
}
42+
};
43+
3344
function mouseHandler(mapId, layerId, group, eventName, extraInfo) {
3445
return function(e) {
3546
if (!HTMLWidgets.shinyMode) return;

man/setStyle.Rd

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)