-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #75 from trafficonese/layergroupconditional
Layergroupconditional
- Loading branch information
Showing
11 changed files
with
419 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
layerGroupConditionalDependency <- function() { | ||
list( | ||
htmltools::htmlDependency( | ||
"lfx-conditional", | ||
version = "1.0.0", | ||
src = system.file("htmlwidgets/lfx-conditional", package = "leaflet.extras2"), | ||
script = c( | ||
"leaflet.layergroup.conditional.js", | ||
"leaflet.layergroup.conditional-bindings.js" | ||
) | ||
) | ||
) | ||
} | ||
|
||
#' addLayerGroupConditional | ||
#' | ||
#' @param map A map widget object created from \code{\link[leaflet]{leaflet}}. | ||
#' @param groups A character vector of layer group names already added to the map. | ||
#' These layer groups will be conditionally displayed based on the specified \code{conditions}. | ||
#' @param conditions A named list where: | ||
#' - Each **name** is a JavaScript function (using \code{\link[leaflet]{JS}}) defining a condition for displaying a layer group. | ||
#' - Each **value** corresponds to a layer group name (or names) from the \code{layers} parameter. | ||
#' Example: | ||
#' \preformatted{ | ||
#' condition = list( | ||
#' "(zoomLevel) => zoomLevel < 4" = "group1", | ||
#' "(zoomLevel) => zoomLevel >= 4 && zoomLevel < 6" = "group2", | ||
#' "(zoomLevel) => zoomLevel >= 6" = c("group3", "group4") | ||
#' ) | ||
#' } | ||
#' | ||
#' @seealso \url{https://github.com/Solfisk/Leaflet.LayerGroup.Conditional?tab=readme-ov-file} for more details about the plugin. | ||
#' @family LayerGroupConditional Plugin | ||
#' @export | ||
#' @examples | ||
#' library(leaflet) | ||
#' library(sf) | ||
#' library(leaflet.extras2) | ||
#' | ||
#' breweries91 <- st_as_sf(breweries91) | ||
#' lines <- st_as_sf(atlStorms2005) | ||
#' polys <- st_as_sf(leaflet::gadmCHE) | ||
#' groups <- c("atlStorms", "breweries", "gadmCHE") | ||
#' | ||
#' leaflet() %>% | ||
#' addTiles() %>% | ||
#' #leafem::addMouseCoordinates() %>% | ||
#' addPolylines(data = lines, label = ~Name, group = groups[1]) %>% | ||
#' addCircleMarkers(data = breweries91, label = ~brewery, group = groups[2]) %>% | ||
#' addPolygons(data = polys, label = ~NAME_1, group = groups[3]) %>% | ||
#' addLayerGroupConditional( | ||
#' groups = groups, | ||
#' conditions = list( | ||
#' "(zoomLevel) => zoomLevel < 4" = groups[1], | ||
#' "(zoomLevel) => zoomLevel >= 4 & zoomLevel < 6 " = groups[2], | ||
#' "(zoomLevel) => zoomLevel >= 6" = groups[3] | ||
#' ) | ||
#' ) %>% | ||
#' hideGroup(groups) %>% | ||
#' addLayersControl( | ||
#' overlayGroups = groups, | ||
#' options = layersControlOptions(collapsed = FALSE) | ||
#' ) | ||
#' | ||
addLayerGroupConditional <- function(map, groups = NULL, conditions = NULL) { | ||
map$dependencies <- c(map$dependencies, layerGroupConditionalDependency()) | ||
invokeMethod( | ||
map, getMapData(map), "addLayerGroupConditional", | ||
groups, conditions | ||
) | ||
} | ||
|
||
#' removeConditionalLayer | ||
#' | ||
#' @inheritParams addLayerGroupConditional | ||
#' @family LayerGroupConditional Plugin | ||
#' @export | ||
removeConditionalLayer <- function(map, groups) { | ||
invokeMethod(map, getMapData(map), "removeConditionalLayer", groups) | ||
} | ||
|
||
#' clearConditionalLayers | ||
#' | ||
#' @inheritParams addLayerGroupConditional | ||
#' @family LayerGroupConditional Plugin | ||
#' @export | ||
clearConditionalLayers <- function(map) { | ||
invokeMethod(map, getMapData(map), "clearConditionalLayers") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
library(shiny) | ||
library(leaflet) | ||
library(sf) | ||
library(leaflet.extras2) | ||
|
||
breweries91 <- st_as_sf(breweries91) | ||
lines <- st_as_sf(atlStorms2005) | ||
polys <- st_as_sf(leaflet::gadmCHE) | ||
groups <- c("atlStorms","breweries","gadmCHE") | ||
|
||
ui <- fluidPage( | ||
leafletOutput("map", height = 900), | ||
actionButton("remove", "Remove by Group"), | ||
actionButton("clear", "Clear") | ||
) | ||
|
||
server <- function(input, output, session) { | ||
output$map <- renderLeaflet({ | ||
leaflet() %>% | ||
addTiles() %>% | ||
leafem::addMouseCoordinates() %>% | ||
addPolylines(data = lines, label = ~Name, group = groups[1]) %>% | ||
addCircleMarkers(data = breweries91, label = ~brewery, group = groups[2]) %>% | ||
addPolygons(data = polys, label = ~NAME_1, group = groups[3]) %>% | ||
addLayerGroupConditional(groups = groups, | ||
conditions = list( | ||
"(zoomLevel) => zoomLevel < 4" = groups[1], | ||
"(zoomLevel) => zoomLevel >= 4 & zoomLevel < 6 " = groups[2], | ||
"(zoomLevel) => zoomLevel >= 6" = c(groups[3]) | ||
)) %>% | ||
hideGroup(groups) %>% | ||
addLayersControl(overlayGroups = groups, | ||
options = layersControlOptions(collapsed=FALSE)) | ||
}) | ||
observeEvent(input$clear, { | ||
leafletProxy("map") %>% | ||
clearConditionalLayers() | ||
}) | ||
observeEvent(input$remove, { | ||
leafletProxy("map") %>% | ||
removeConditionalLayer(groups=groups[1]) | ||
}) | ||
} | ||
shinyApp(ui, server) |
62 changes: 62 additions & 0 deletions
62
inst/htmlwidgets/lfx-conditional/leaflet.layergroup.conditional-bindings.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
/* global LeafletWidget, $, L, Shiny, HTMLWidgets */ | ||
LeafletWidget.methods.addLayerGroupConditional = function(groups, conditions) { | ||
|
||
const map = this; | ||
let conditionalGroup = L.layerGroup.conditional(); | ||
map.conditionalGroup = conditionalGroup; | ||
|
||
// Loop through each group | ||
groups.forEach(function(group) { | ||
// Loop through conditions for each group | ||
Object.keys(conditions).forEach(function(condition) { | ||
let groupList = conditions[condition]; | ||
if (!Array.isArray(groupList)) { | ||
groupList = [groupList]; | ||
} | ||
groupList.forEach(function(group) { | ||
let layer = map.layerManager.getLayerGroup(group); | ||
if (!layer) { | ||
console.warn("Layer not found in group " + group); | ||
return; | ||
} | ||
// Add the layer with the associated condition | ||
conditionalGroup.addConditionalLayer(eval(condition), layer); | ||
}) | ||
}); | ||
}); | ||
|
||
// Add the conditional group to the map | ||
conditionalGroup.addTo(map) | ||
|
||
// Set up the zoom handler to update conditional layers | ||
let zoomHandler = function() { | ||
conditionalGroup.updateConditionalLayers(map.getZoom()); | ||
}; | ||
map.on("zoomend", zoomHandler); | ||
|
||
// Set initial state of conditional layers | ||
setTimeout(function() { | ||
zoomHandler() | ||
}, 500); | ||
|
||
}; | ||
|
||
|
||
LeafletWidget.methods.removeConditionalLayer = function(groups) { | ||
const map = this; | ||
if (!Array.isArray(groups)) { | ||
groups = [groups]; | ||
} | ||
groups.forEach(function(group) { | ||
let layer = map.layerManager.getLayerGroup(group); | ||
if (layer && map.conditionalGroup) { | ||
map.conditionalGroup.removeConditionalLayer(layer) | ||
} else { | ||
console.warn("Layer not found " + group); | ||
} | ||
}) | ||
}; | ||
|
||
LeafletWidget.methods.clearConditionalLayers = function() { | ||
this.conditionalGroup.clearConditionalLayers() | ||
}; |
104 changes: 104 additions & 0 deletions
104
inst/htmlwidgets/lfx-conditional/leaflet.layergroup.conditional.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
L.LayerGroup.Conditional = L.LayerGroup.extend({ | ||
|
||
initialize: function (layers, options) { | ||
this._conditionalLayers = {}; | ||
L.LayerGroup.prototype.initialize.call(this, layers, options); | ||
}, | ||
|
||
// @method addConditionalLayer(conditionFunction: (object) => boolean, layer: Layer): this | ||
// Adds the given layer to the group with a condition. | ||
addConditionalLayer: function (conditionFunction, layer) { | ||
var id = this.getLayerId(layer); | ||
|
||
this._conditionalLayers[id] = { | ||
"condition": conditionFunction, | ||
"layer": layer, | ||
"active": false, | ||
} | ||
|
||
return this; | ||
}, | ||
|
||
// @method removeConditionalLayer(layer: Layer): this | ||
// Removes the given conditional layer from the group. | ||
// @alternative | ||
// @method removeConditionalLayer(id: Number): this | ||
// Removes the conditional layer with the given internal ID from the group. | ||
removeConditionalLayer: function (layer) { | ||
var id = layer in this._conditionalLayers ? layer : this.getLayerId(layer); | ||
|
||
if (this._conditionalLayers[id] && this._conditionalLayers[id].active) { | ||
this.removeLayer(id); | ||
} | ||
|
||
delete this._conditionalLayers[id]; | ||
|
||
return this; | ||
}, | ||
|
||
// @method hasConditionalLayer(layer: Layer): Boolean | ||
// Returns `true` if the given layer is currently added to the group with a condition, regardless of whether it is active | ||
// @alternative | ||
// @method hasConditionalLayer(id: Number): Boolean | ||
// Returns `true` if the given internal ID is currently added to the group with a condition, regardless of whether it is active. | ||
hasConditionalLayer: function (layer) { | ||
if (!layer) { return false; } | ||
var layerId = typeof layer === 'number' ? layer : this.getLayerId(layer); | ||
return layerId in this._conditionalLayers; | ||
}, | ||
|
||
// @method isConditionalLayerActive(layer: Layer): Boolean | ||
// Returns `true` if the given layer is currently added to the group with a condition, and is currently active | ||
// @alternative | ||
// @method hasConditionalLayer(id: Number): Boolean | ||
// Returns `true` if the given internal ID is currently added to the group with a condition, and is currently active. | ||
isConditionalLayerActive: function (layer) { | ||
if (!layer) { return false; } | ||
var layerId = typeof layer === 'number' ? layer : this.getLayerId(layer); | ||
return (layerId in this._conditionalLayers) && this._conditionalLayers[layerId].active; | ||
}, | ||
|
||
|
||
// @method clearConditionalLayers(): this | ||
// Removes all the conditional layers from the group. | ||
clearConditionalLayers: function () { | ||
for (var i in this._conditionalLayers) { | ||
this.removeConditionalLayer(i); | ||
} | ||
return this; | ||
}, | ||
|
||
// @method getConditionalLayers(): Layer[] | ||
// Returns an array of all the conditional layers added to the group. | ||
getConditionalLayers: function () { | ||
var layers = []; | ||
for (var i in this._conditionalLayers) { | ||
layers.push(this._conditionalLayers[i].layer); | ||
} | ||
return layers; | ||
}, | ||
|
||
// @method updateConditionalLayers(o: object): this | ||
// Update the status of all conditional layers, passing an optional argument to each layer's condition function. | ||
updateConditionalLayers: function(o) { | ||
for (var i in this._conditionalLayers) { | ||
var condLayer = this._conditionalLayers[i]; | ||
var active = condLayer.condition(o); | ||
if (active && !condLayer.active) { | ||
this.addLayer(condLayer.layer); | ||
} else if (condLayer.active && !active) { | ||
this.removeLayer(condLayer.layer); | ||
} | ||
condLayer.active = active; | ||
} | ||
|
||
return this; | ||
} | ||
}); | ||
|
||
|
||
// @factory L.layerGroup.Conditional(layers?: Layer[], options?: Object) | ||
// Create a conditional layer group, optionally given an initial set of non-conditinoal layers and an `options` object. | ||
L.layerGroup.conditional = function (layers, options) { | ||
return new L.LayerGroup.Conditional(layers, options); | ||
}; |
Oops, something went wrong.