diff --git a/NAMESPACE b/NAMESPACE index b84fb841f..9ffcdf950 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -155,6 +155,8 @@ export(renderLeaflet) export(safeLabel) export(scaleBarOptions) export(setMaxBounds) +export(setStyle) +export(setStyleFast) export(setView) export(showGroup) export(tileOptions) diff --git a/R/methods.R b/R/methods.R index d394ae1aa..408d7c52e 100644 --- a/R/methods.R +++ b/R/methods.R @@ -32,6 +32,31 @@ setView <- function(map, lng, lat, zoom, options = list()) { ) } +#' Efficiently style a group that has already been added to the map +#' +#' Call with a group and a vector of style lists of length N. The first N +#' features of the group will be restyled. +#' +#' @examples +#' \donttest{ +#' renderLeaflet("map", { +#' leaflet() %>% addPolygons(data = zones, group = "zones", color = "red") +#' }) +#' colour = "blue" +#' styles = lapply(pal(values), function(colour) {list(fillColor=colour, color=colour)}) +#' leafletProxy("map") %>% +#' setStyle("zones", styles) +#' } +#' @export +setStyle = function(map, group, styles, label = NULL, offset = 0) { + invokeMethod(map, NULL, "setStyle", group, styles, label, offset - 1) +} + +#' @export +setStyleFast = function(map, group, color = NULL, weight = NULL, label = NULL, stroke = NULL, fill = NULL) { + invokeMethod(map, NULL, "setStyleFast", group, color, weight, label, stroke, fill) +} + #' @describeIn map-methods Flys to a given location/zoom-level using smooth pan-zoom. #' @export flyTo <- function(map, lng, lat, zoom, options = list()) { diff --git a/inst/htmlwidgets/leaflet.js b/inst/htmlwidgets/leaflet.js index 3d0104a49..21cf16342 100644 --- a/inst/htmlwidgets/leaflet.js +++ b/inst/htmlwidgets/leaflet.js @@ -655,7 +655,13 @@ _htmlwidgets2.default.widget({ _shiny2.default.onInputChange(map.id + "_click", { lat: e.latlng.lat, lng: e.latlng.lng, - ".nonce": Math.random() // Force reactivity if lat/lng hasn't changed + ".nonce": Math.random(), // Force reactivity if lat/lng hasn't changed + modifiers: { + alt: e.originalEvent.altKey, + ctrl: e.originalEvent.ctrlKey, + meta: e.originalEvent.metaKey, + shift: e.originalEvent.shiftKey + } }); }); @@ -1282,6 +1288,61 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de var methods = {}; exports.default = methods; +/** Much more performant way to style loaded geometry */ + +methods.setStyle = function (group, styles, labels) { + var offset = arguments.length <= 3 || arguments[3] === undefined ? 0 : arguments[3]; + + window.map = this; + var layers = this.layerManager.getLayerGroup(group).getLayers(); + + if (styles) { + for (var i = 0; i < styles.length; i++) { + layers[i + offset].setStyle(styles[i]); + } + } + if (labels) { + for (var _i = 0; _i < styles.length; _i++) { + layers[_i + offset].bindTooltip(labels[_i]); + } + } +}; + +/** Much more performant way to style loaded geometry */ +methods.setStyleFast = function (group, colors, weights, labels, strokes, fills) { + window.map = this; + var layers = this.layerManager.getLayerGroup(group).getLayers(); + + if (labels) { + for (var i = 0; i < labels.length; i++) { + layers[i].bindTooltip(labels[i]); + } + } + + if (colors) { + for (var _i2 = 0; _i2 < colors.length; _i2++) { + layers[_i2].setStyle({ color: colors[_i2], fillColor: colors[_i2] }); + } + } + + if (weights) { + for (var _i3 = 0; _i3 < weights.length; _i3++) { + layers[_i3].setStyle({ weight: weights[_i3] }); + } + } + + if (strokes) { + for (var _i4 = 0; _i4 < strokes.length; _i4++) { + layers[_i4].setStyle({ stroke: strokes[_i4] }); + } + } + + if (fills) { + for (var _i5 = 0; _i5 < fills.length; _i5++) { + layers[_i5].setStyle({ fill: fills[_i5] }); + } + } +}; function mouseHandler(mapId, layerId, group, eventName, extraInfo) { return function (e) { @@ -1297,7 +1358,13 @@ function mouseHandler(mapId, layerId, group, eventName, extraInfo) { } var eventInfo = _jquery2.default.extend({ id: layerId, - ".nonce": Math.random() // force reactivity + ".nonce": Math.random(), // force reactivity + modifiers: { + alt: e.originalEvent.altKey, + ctrl: e.originalEvent.ctrlKey, + meta: e.originalEvent.metaKey, + shift: e.originalEvent.shiftKey + } }, group !== null ? { group: group } : null, latLng, extraInfo); _shiny2.default.onInputChange(mapId + "_" + eventName, eventInfo); diff --git a/javascript/src/index.js b/javascript/src/index.js index 3230ec298..711ac4efd 100644 --- a/javascript/src/index.js +++ b/javascript/src/index.js @@ -140,7 +140,13 @@ HTMLWidgets.widget({ Shiny.onInputChange(map.id + "_click", { lat: e.latlng.lat, lng: e.latlng.lng, - ".nonce": Math.random() // Force reactivity if lat/lng hasn't changed + ".nonce": Math.random(), // Force reactivity if lat/lng hasn't changed + modifiers: { + alt: e.originalEvent.altKey, + ctrl: e.originalEvent.ctrlKey, + meta: e.originalEvent.metaKey, + shift: e.originalEvent.shiftKey + } }); }); diff --git a/javascript/src/methods.js b/javascript/src/methods.js index 5cbc7c3e6..91c326b76 100644 --- a/javascript/src/methods.js +++ b/javascript/src/methods.js @@ -13,6 +13,58 @@ import Mipmapper from "./mipmapper"; let methods = {}; export default methods; +/** Much more performant way to style loaded geometry */ +methods.setStyle = function(group, styles, labels, offset = 0) { + window.map = this; + let layers = this.layerManager.getLayerGroup(group).getLayers(); + + if (styles) { + for (let i = 0; i < styles.length; i++) { + layers[i + offset].setStyle(styles[i]); + } + } + if (labels) { + for (let i = 0; i < styles.length; i++) { + layers[i + offset].bindTooltip(labels[i]); + } + } +}; + +/** Much more performant way to style loaded geometry */ +methods.setStyleFast = function(group, colors, weights, labels, strokes, fills) { + window.map = this; + let layers = this.layerManager.getLayerGroup(group).getLayers(); + + if (labels) { + for (let i = 0; i < labels.length; i++) { + layers[i].bindTooltip(labels[i]); + } + } + + if (colors) { + for (let i = 0; i < colors.length; i++) { + layers[i].setStyle({color: colors[i], fillColor: colors[i]}); + } + } + + if (weights) { + for (let i = 0; i < weights.length; i++) { + layers[i].setStyle({weight: weights[i]}); + } + } + + if (strokes) { + for (let i = 0; i < strokes.length; i++) { + layers[i].setStyle({stroke: strokes[i]}); + } + } + + if (fills) { + for (let i = 0; i < fills.length; i++) { + layers[i].setStyle({fill: fills[i]}); + } + } +}; function mouseHandler(mapId, layerId, group, eventName, extraInfo) { return function(e) { @@ -29,7 +81,13 @@ function mouseHandler(mapId, layerId, group, eventName, extraInfo) { let eventInfo = $.extend( { id: layerId, - ".nonce": Math.random() // force reactivity + ".nonce": Math.random(), // force reactivity + modifiers: { + alt: e.originalEvent.altKey, + ctrl: e.originalEvent.ctrlKey, + meta: e.originalEvent.metaKey, + shift: e.originalEvent.shiftKey + } }, group !== null ? {group: group} : null, latLng, diff --git a/man/setStyle.Rd b/man/setStyle.Rd new file mode 100644 index 000000000..24af1cd15 --- /dev/null +++ b/man/setStyle.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/methods.R +\name{setStyle} +\alias{setStyle} +\title{Efficiently style a group that has already been added to the map} +\usage{ +setStyle(map, group, styles, label = NULL, offset = 0) +} +\description{ +Call with a group and a vector of style lists of length N. The first N +features of the group will be restyled. +} +\examples{ +\donttest{ +renderLeaflet("map", { + leaflet() \%>\% addPolygons(data = zones, group = "zones", color = "red") +}) +colour = "blue" +styles = lapply(pal(values), function(colour) {list(fillColor=colour, color=colour)}) +leafletProxy("map") \%>\% + setStyle("zones", styles) +} +}