Skip to content

Commit 8cb7cfe

Browse files
authored
Merge pull request #442 from bhaskarvk/feature/attachLegend
Auto show/hide legends with support for LayerControl and groupOptions
2 parents f15492c + 8808094 commit 8cb7cfe

File tree

8 files changed

+205
-54
lines changed

8 files changed

+205
-54
lines changed

.Rbuildignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,4 @@
1212
^javascript$
1313
^package\.json$
1414
^node_modules$
15-
1615
^data-raw$

R/legend.R

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,19 @@
4949
#' @param layerId the ID of the legend; subsequent calls to \code{addLegend}
5050
#' or \code{addControl} with the same \code{layerId} will replace this
5151
#' legend. The ID can also be used with \code{removeControl}.
52+
#' @param group \code{group} name of a leaflet layer group.
53+
#' Supplying this value will tie the legend to the leaflet layer group
54+
#' with this name and will auto add/remove the legend as the
55+
#' group is added/removed, for example via layerControl.
56+
#' You will need to set the \code{group} when you add a layer
57+
#' (e.g. \code{\link{addPolygons}}) and supply the same name here.
5258
#' @example inst/examples/legend.R
5359
#' @export
5460
addLegend <- function(
5561
map, position = c('topright', 'bottomright', 'bottomleft', 'topleft'),
56-
pal, values, na.label = 'NA', bins = 7, colors, opacity = 0.5, labels,
62+
pal, values, na.label = 'NA', bins = 7, colors, opacity = 0.5, labels = NULL,
5763
labFormat = labelFormat(), title = NULL, className = "info legend",
58-
layerId = NULL
64+
layerId = NULL, group = NULL
5965
) {
6066
position = match.arg(position)
6167
type = 'unknown'; na.color = NULL
@@ -147,7 +153,7 @@ addLegend <- function(
147153
colors = I(unname(colors)), labels = I(unname(labels)),
148154
na_color = na.color, na_label = na.label, opacity = opacity,
149155
position = position, type = type, title = title, extra = extra,
150-
layerId = layerId, className = className
156+
layerId = layerId, className = className, group = group
151157
)
152158
invokeMethod(map, getMapData(map), "addLegend", legend)
153159
}

inst/examples/groupOptions.R

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
library(leaflet)
2+
pal <- colorQuantile("YlOrRd", quakes$mag)
3+
4+
leaflet(quakes) %>%
5+
addProviderTiles(providers$Esri.OceanBasemap, group = "basic") %>%
6+
addCircleMarkers(group = "detail", fillOpacity = 0.5,
7+
radius = ~mag * 5, color = ~pal(mag), stroke = FALSE) %>%
8+
addLegend(pal = pal, values = ~mag, group='detail', position='bottomleft')
9+
10+
l <- leaflet(quakes) %>%
11+
addProviderTiles(providers$Esri.OceanBasemap, group = "basic") %>%
12+
addMarkers(data = quakes, group = "basic") %>%
13+
addCircleMarkers(group = "detail", fillOpacity = 0.5,
14+
radius = ~mag * 5, color = ~pal(mag), stroke = FALSE) %>%
15+
addLegend(pal = pal, values = ~mag, group='detail', position='bottomleft') %>%
16+
groupOptions("detail", zoomLevels = 7:18) %>%
17+
addControl(htmltools::HTML("Zoom Level"), position = "topright",
18+
layerId = "zoom_display")
19+
20+
# Just to show the zoom level
21+
htmlwidgets::onRender(l, jsCode = htmlwidgets::JS(
22+
"function(el, x) {
23+
debugger;
24+
var map = this;
25+
detailsControl = document.getElementById('zoom_display');
26+
detailsControl.innerHTML = '<div>Zoom Level:'+map.getZoom()+'</div>';
27+
map.on('zoomend', function(e) {
28+
detailsControl = document.getElementById('zoom_display');
29+
detailsControl.innerHTML = '<div>Zoom Level:'+map.getZoom()+'</div>';
30+
});
31+
}"))

inst/examples/legend.R

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,25 @@ df = local({
1616
})
1717
pal = colorNumeric('OrRd', df$z)
1818
leaflet(df) %>%
19-
addCircleMarkers(~x, ~y, color = ~pal(z)) %>%
20-
addLegend(pal = pal, values = ~z)
19+
addTiles() %>%
20+
addCircleMarkers(~x, ~y, color = ~pal(z), group='circles') %>%
21+
addLegend(pal = pal, values = ~z, group='circles', position='bottomleft') %>%
22+
addLayersControl(overlayGroups = c('circles'))
2123

2224
# format legend labels
2325
df = data.frame(x = rnorm(100), y = rexp(100, 2), z = runif(100))
2426
pal = colorBin('PuOr', df$z, bins = c(0, .1, .4, .9, 1))
2527
leaflet(df) %>%
26-
addCircleMarkers(~x, ~y, color = ~pal(z)) %>%
27-
addLegend(pal = pal, values = ~z)
28+
addTiles() %>%
29+
addCircleMarkers(~x, ~y, color = ~pal(z), group='circles') %>%
30+
addLegend(pal = pal, values = ~z, group='circles', position='bottomleft') %>%
31+
addLayersControl(overlayGroups = c('circles'))
2832

2933
leaflet(df) %>%
30-
addCircleMarkers(~x, ~y, color = ~pal(z)) %>%
34+
addTiles() %>%
35+
addCircleMarkers(~x, ~y, color = ~pal(z), group='circles') %>%
3136
addLegend(pal = pal, values = ~z, labFormat = labelFormat(
3237
prefix = '(', suffix = ')%', between = ', ',
3338
transform = function(x) 100 * x
34-
))
39+
), group='circles', position='bottomleft' ) %>%
40+
addLayersControl(overlayGroups = c('circles'))

inst/htmlwidgets/leaflet.js

Lines changed: 82 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,15 @@ var ControlStore = function () {
9292
}
9393
this._map.addControl(control);
9494
}
95+
}, {
96+
key: "get",
97+
value: function get(id) {
98+
var control = null;
99+
if (this._controlsById[id]) {
100+
control = this._controlsById[id];
101+
}
102+
return control;
103+
}
95104
}, {
96105
key: "remove",
97106
value: function remove(id) {
@@ -1753,11 +1762,17 @@ methods.removeControl = function (layerId) {
17531762
this.controls.remove(layerId);
17541763
};
17551764

1765+
methods.getControl = function (layerId) {
1766+
this.controls.get(layerId);
1767+
};
1768+
17561769
methods.clearControls = function () {
17571770
this.controls.clear();
17581771
};
17591772

17601773
methods.addLegend = function (options) {
1774+
var _this5 = this;
1775+
17611776
var legend = _leaflet2.default.control({ position: options.position });
17621777
var gradSpan = void 0;
17631778

@@ -1856,35 +1871,66 @@ methods.addLegend = function (options) {
18561871
return div;
18571872
};
18581873

1874+
if (options.group) {
1875+
(function () {
1876+
// Auto generate a layerID if not provided
1877+
if (!options.layerId) {
1878+
options.layerId = _leaflet2.default.stamp(legend);
1879+
}
1880+
1881+
var map = _this5;
1882+
map.on("overlayadd", function (e) {
1883+
if (e.name === options.group) {
1884+
map.controls.add(legend, options.layerId);
1885+
}
1886+
});
1887+
map.on("overlayremove", function (e) {
1888+
if (e.name === options.group) {
1889+
map.controls.remove(options.layerId);
1890+
}
1891+
});
1892+
map.on("groupadd", function (e) {
1893+
if (e.name === options.group) {
1894+
map.controls.add(legend, options.layerId);
1895+
}
1896+
});
1897+
map.on("groupremove", function (e) {
1898+
if (e.name === options.group) {
1899+
map.controls.remove(options.layerId);
1900+
}
1901+
});
1902+
})();
1903+
}
1904+
18591905
this.controls.add(legend, options.layerId);
18601906
};
18611907

18621908
methods.addLayersControl = function (baseGroups, overlayGroups, options) {
1863-
var _this5 = this;
1909+
var _this6 = this;
18641910

18651911
// Only allow one layers control at a time
18661912
methods.removeLayersControl.call(this);
18671913

18681914
var firstLayer = true;
18691915
var base = {};
18701916
_jquery2.default.each((0, _util.asArray)(baseGroups), function (i, g) {
1871-
var layer = _this5.layerManager.getLayerGroup(g, true);
1917+
var layer = _this6.layerManager.getLayerGroup(g, true);
18721918
if (layer) {
18731919
base[g] = layer;
18741920

18751921
// Check if >1 base layers are visible; if so, hide all but the first one
1876-
if (_this5.hasLayer(layer)) {
1922+
if (_this6.hasLayer(layer)) {
18771923
if (firstLayer) {
18781924
firstLayer = false;
18791925
} else {
1880-
_this5.removeLayer(layer);
1926+
_this6.removeLayer(layer);
18811927
}
18821928
}
18831929
}
18841930
});
18851931
var overlay = {};
18861932
_jquery2.default.each((0, _util.asArray)(overlayGroups), function (i, g) {
1887-
var layer = _this5.layerManager.getLayerGroup(g, true);
1933+
var layer = _this6.layerManager.getLayerGroup(g, true);
18881934
if (layer) {
18891935
overlay[g] = layer;
18901936
}
@@ -1918,23 +1964,23 @@ methods.removeScaleBar = function () {
19181964
};
19191965

19201966
methods.hideGroup = function (group) {
1921-
var _this6 = this;
1967+
var _this7 = this;
19221968

19231969
_jquery2.default.each((0, _util.asArray)(group), function (i, g) {
1924-
var layer = _this6.layerManager.getLayerGroup(g, true);
1970+
var layer = _this7.layerManager.getLayerGroup(g, true);
19251971
if (layer) {
1926-
_this6.removeLayer(layer);
1972+
_this7.removeLayer(layer);
19271973
}
19281974
});
19291975
};
19301976

19311977
methods.showGroup = function (group) {
1932-
var _this7 = this;
1978+
var _this8 = this;
19331979

19341980
_jquery2.default.each((0, _util.asArray)(group), function (i, g) {
1935-
var layer = _this7.layerManager.getLayerGroup(g, true);
1981+
var layer = _this8.layerManager.getLayerGroup(g, true);
19361982
if (layer) {
1937-
_this7.addLayer(layer);
1983+
_this8.addLayer(layer);
19381984
}
19391985
});
19401986
};
@@ -1945,9 +1991,15 @@ function setupShowHideGroupsOnZoom(map) {
19451991
}
19461992
map.leafletr._hasInitializedShowHideGroups = true;
19471993

1948-
function setVisibility(layer, visible) {
1994+
function setVisibility(layer, visible, group) {
19491995
if (visible !== map.hasLayer(layer)) {
1950-
if (visible) map.addLayer(layer);else map.removeLayer(layer);
1996+
if (visible) {
1997+
map.addLayer(layer);
1998+
map.fire("groupadd", { "name": group, "layer": layer });
1999+
} else {
2000+
map.removeLayer(layer);
2001+
map.fire("groupremove", { "name": group, "layer": layer });
2002+
}
19512003
}
19522004
}
19532005

@@ -1958,7 +2010,7 @@ function setupShowHideGroupsOnZoom(map) {
19582010
map.layerManager.getAllGroupNames().forEach(function (group) {
19592011
var layer = map.layerManager.getLayerGroup(group, false);
19602012
if (layer && typeof layer.zoomLevels !== "undefined") {
1961-
setVisibility(layer, layer.zoomLevels === true || layer.zoomLevels.indexOf(zoom) >= 0);
2013+
setVisibility(layer, layer.zoomLevels === true || layer.zoomLevels.indexOf(zoom) >= 0, group);
19622014
}
19632015
});
19642016
}
@@ -1968,10 +2020,10 @@ function setupShowHideGroupsOnZoom(map) {
19682020
}
19692021

19702022
methods.setGroupOptions = function (group, options) {
1971-
var _this8 = this;
2023+
var _this9 = this;
19722024

19732025
_jquery2.default.each((0, _util.asArray)(group), function (i, g) {
1974-
var layer = _this8.layerManager.getLayerGroup(g, true);
2026+
var layer = _this9.layerManager.getLayerGroup(g, true);
19752027
// This slightly tortured check is because 0 is a valid value for zoomLevels
19762028
if (typeof options.zoomLevels !== "undefined" && options.zoomLevels !== null) {
19772029
layer.zoomLevels = (0, _util.asArray)(options.zoomLevels);
@@ -2107,7 +2159,7 @@ methods.addRasterImage = function (uri, bounds, opacity, attribution, layerId, g
21072159
canvasTiles.drawTile = function (canvas, tilePoint, zoom) {
21082160
getImageData(function (imgData, w, h, mipmapper) {
21092161
try {
2110-
var _ret7 = function () {
2162+
var _ret8 = function () {
21112163
// The Context2D we'll being drawing onto. It's always 256x256.
21122164
var ctx = canvas.getContext("2d");
21132165

@@ -2231,7 +2283,7 @@ methods.addRasterImage = function (uri, bounds, opacity, attribution, layerId, g
22312283
}
22322284
}();
22332285

2234-
if ((typeof _ret7 === "undefined" ? "undefined" : _typeof(_ret7)) === "object") return _ret7.v;
2286+
if ((typeof _ret8 === "undefined" ? "undefined" : _typeof(_ret8)) === "object") return _ret8.v;
22352287
} finally {
22362288
canvasTiles.tileDrawn(canvas);
22372289
}
@@ -2265,7 +2317,7 @@ methods.removeMeasure = function () {
22652317
};
22662318

22672319
methods.addSelect = function (ctGroup) {
2268-
var _this9 = this;
2320+
var _this10 = this;
22692321

22702322
methods.removeSelect.call(this);
22712323

@@ -2276,42 +2328,42 @@ methods.addSelect = function (ctGroup) {
22762328
title: "Make a selection",
22772329
onClick: function onClick(btn, map) {
22782330
btn.state("select-active");
2279-
_this9._locationFilter = new _leaflet2.default.LocationFilter2();
2331+
_this10._locationFilter = new _leaflet2.default.LocationFilter2();
22802332

22812333
if (ctGroup) {
22822334
(function () {
22832335
var selectionHandle = new global.crosstalk.SelectionHandle(ctGroup);
22842336
selectionHandle.on("change", function (e) {
22852337
if (e.sender !== selectionHandle) {
2286-
if (_this9._locationFilter) {
2287-
_this9._locationFilter.disable();
2338+
if (_this10._locationFilter) {
2339+
_this10._locationFilter.disable();
22882340
btn.state("select-inactive");
22892341
}
22902342
}
22912343
});
22922344
var handler = function handler(e) {
2293-
_this9.layerManager.brush(_this9._locationFilter.getBounds(), { sender: selectionHandle });
2345+
_this10.layerManager.brush(_this10._locationFilter.getBounds(), { sender: selectionHandle });
22942346
};
2295-
_this9._locationFilter.on("enabled", handler);
2296-
_this9._locationFilter.on("change", handler);
2297-
_this9._locationFilter.on("disabled", function () {
2347+
_this10._locationFilter.on("enabled", handler);
2348+
_this10._locationFilter.on("change", handler);
2349+
_this10._locationFilter.on("disabled", function () {
22982350
selectionHandle.close();
2299-
_this9._locationFilter = null;
2351+
_this10._locationFilter = null;
23002352
});
23012353
})();
23022354
}
23032355

2304-
_this9._locationFilter.addTo(map);
2356+
_this10._locationFilter.addTo(map);
23052357
}
23062358
}, {
23072359
stateName: "select-active",
23082360
icon: "ion-close-round",
23092361
title: "Dismiss selection",
23102362
onClick: function onClick(btn, map) {
23112363
btn.state("select-inactive");
2312-
_this9._locationFilter.disable();
2364+
_this10._locationFilter.disable();
23132365
// If explicitly dismissed, clear the crosstalk selections
2314-
_this9.layerManager.unbrush();
2366+
_this10.layerManager.unbrush();
23152367
}
23162368
}]
23172369
});

javascript/src/control-store.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@ export default class ControlStore {
1818
this._map.addControl(control);
1919
}
2020

21+
get(id) {
22+
let control = null;
23+
if (this._controlsById[id]) {
24+
control = this._controlsById[id];
25+
}
26+
return control;
27+
}
28+
2129
remove(id) {
2230
if (this._controlsById[id]) {
2331
let control = this._controlsById[id];
@@ -39,4 +47,4 @@ export default class ControlStore {
3947
}
4048
this._controlsById = {};
4149
}
42-
}
50+
}

0 commit comments

Comments
 (0)