diff --git a/.Rbuildignore b/.Rbuildignore
index 947ace5..debbe42 100644
--- a/.Rbuildignore
+++ b/.Rbuildignore
@@ -7,3 +7,4 @@
^_pkgdown\.yml$
^docs$
^pkgdown$
+^private$
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 234f028..366c77c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,4 @@
.RData
.Ruserdata
docs
+private
diff --git a/NAMESPACE b/NAMESPACE
index c8f0044..c5c3fa8 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -1,5 +1,17 @@
# Generated by roxygen2: do not edit by hand
+S3method(leafem::addLineFeatures,leaflet)
+S3method(leafem::addLineFeatures,leaflet_proxy)
+S3method(leafem::addLineFeatures,mapdeck)
+S3method(leafem::addLineFeatures,mapview)
+S3method(leafem::addPointFeatures,leaflet)
+S3method(leafem::addPointFeatures,leaflet_proxy)
+S3method(leafem::addPointFeatures,mapdeck)
+S3method(leafem::addPointFeatures,mapview)
+S3method(leafem::addPolygonFeatures,leaflet)
+S3method(leafem::addPolygonFeatures,leaflet_proxy)
+S3method(leafem::addPolygonFeatures,mapdeck)
+S3method(leafem::addPolygonFeatures,mapview)
export(addCOG)
export(addCopyExtent)
export(addExtent)
@@ -23,6 +35,7 @@ export(addStaticLabels)
export(addTileFolder)
export(clip2sfc)
export(colorOptions)
+export(extendLayerControl)
export(garnishMap)
export(hideLogo)
export(imagequeryOptions)
diff --git a/R/features.R b/R/features.R
index 0e90bdf..4fdc3f4 100644
--- a/R/features.R
+++ b/R/features.R
@@ -95,6 +95,7 @@ addPointFeatures = function(map, ...) UseMethod("addPointFeatures")
### Point Features leaflet
+#' @exportS3Method leafem::addPointFeatures
addPointFeatures.leaflet <- function(map,
data,
pane,
@@ -125,12 +126,15 @@ addPointFeatures.leaflet <- function(map,
}
### Point Features leaflet_proxy
+#' @exportS3Method leafem::addPointFeatures
addPointFeatures.leaflet_proxy <- addPointFeatures.leaflet
### Point Features mapview
+#' @exportS3Method leafem::addPointFeatures
addPointFeatures.mapview = addPointFeatures.leaflet
### Point Features mapdeck
+#' @exportS3Method leafem::addPointFeatures
addPointFeatures.mapdeck <- function(map,
data,
...) {
@@ -151,6 +155,7 @@ addPointFeatures.mapdeck <- function(map,
addLineFeatures = function(map, ...) UseMethod("addLineFeatures")
### Line Features leaflet
+#' @exportS3Method leafem::addLineFeatures
addLineFeatures.leaflet <- function(map,
data,
pane,
@@ -177,12 +182,15 @@ addLineFeatures.leaflet <- function(map,
}
### Line Features leaflet_proxy
+#' @exportS3Method leafem::addLineFeatures
addLineFeatures.leaflet_proxy <- addLineFeatures.leaflet
### Line Features mapview
+#' @exportS3Method leafem::addLineFeatures
addLineFeatures.mapview = addLineFeatures.leaflet
### Line Features mapdeck
+#' @exportS3Method leafem::addLineFeatures
addLineFeatures.mapdeck <- function(map,
data,
...) {
@@ -202,6 +210,7 @@ addLineFeatures.mapdeck <- function(map,
addPolygonFeatures = function(map, ...) UseMethod("addPolygonFeatures")
### Polygon Features leaflet
+#' @exportS3Method leafem::addPolygonFeatures
addPolygonFeatures.leaflet <- function(map,
data,
pane,
@@ -228,12 +237,15 @@ addPolygonFeatures.leaflet <- function(map,
}
### Polygon Features leaflet_proxy
+#' @exportS3Method leafem::addPolygonFeatures
addPolygonFeatures.leaflet_proxy <- addPolygonFeatures.leaflet
### Polygon Features mapview
+#' @exportS3Method leafem::addPolygonFeatures
addPolygonFeatures.mapview = addPolygonFeatures.leaflet
### Polygon Features mapdeck
+#' @exportS3Method leafem::addPolygonFeatures
addPolygonFeatures.mapdeck <- function(map,
data,
...) {
diff --git a/R/layerviewcontrol.R b/R/layerviewcontrol.R
new file mode 100644
index 0000000..d6e4089
--- /dev/null
+++ b/R/layerviewcontrol.R
@@ -0,0 +1,195 @@
+#' Extend Layers Control in Leaflet Map
+#'
+#' This function extends an existing layers control in a `leaflet` map by adding custom views, home buttons,
+#' opacity controls, and legends. It enhances the functionality of a layers control created with `leaflet`
+#' or `leaflet.extras`.
+#'
+#' @param map A `leaflet` or `mapview` object to which the extended layers control will be added.
+#' @param view_settings A list specifying the view settings for each layer. Each list element should contain
+#' either:
+#' \itemize{
+#' \item \code{coords}: A vector of length 2 (latitude, longitude) for setting the view, or length 4
+#' (bounding box: lat1, lng1, lat2, lng2) for fitting the bounds.
+#' \item \code{zoom}: The zoom level (used for `setView`).
+#' \item \code{fly} (optional): A logical indicating whether to use `flyTo` or `flyToBounds` instead of `setView` or `fitBounds`.
+#' \item \code{options} (optional): Additional options to pass to `setView`, `fitBounds`, or `flyTo`.
+#' }
+#' @param home_btns Logical. If `TRUE`, adds a "home" button next to each layer name in the layer control.
+#' Clicking the home button zooms the map to the view specified for that layer in \code{view_settings}.
+#' @param setviewonselect Logical. If `TRUE` (default) sets the view when the layer is selected.
+#' @param home_btn_options A list of options to customize the home button appearance and behavior.
+#' Possible options include:
+#' - `text`: The text or emoji to display on the button (default is '🏠').
+#' - `cursor`: CSS cursor style for the button (default is 'pointer').
+#' - `class`: CSS class name for the button (default is 'leaflet-home-btn').
+#' - `styles`: Semicolon separated CSS-string (default is 'float: inline-end;').
+#'
+#' @param opacityControl A list specifying the opacity control settings for each layer. Each list element should contain:
+#' \itemize{
+#' \item \code{min}: Minimum opacity value (default is 0).
+#' \item \code{max}: Maximum opacity value (default is 1).
+#' \item \code{step}: Step size for the opacity slider (default is 0.1).
+#' \item \code{default}: Default opacity value (default is 1).
+#' \item \code{width}: Width of the opacity slider (default is '100%').
+#' \item \code{class}: CSS class name for the slider (default is 'leaflet-opacity-slider').
+#' }
+#'
+#' @param includelegends Logical. If `TRUE` (default), appends legends to the layer control. Legends are matched
+#' to layers by their group name. The legends need to be added with corresponding layer IDs.
+#'
+#' @return A modified `leaflet` map object with extended layers control including view controls, home buttons, opacity controls, and legends.
+#'
+#' @details
+#' This function generates JavaScript that listens for `overlayadd` or `baselayerchange` events
+#' and automatically sets the view or zoom level according to the specified \code{view_settings}.
+#' If `home_btns` is enabled, a home button is added next to each layer in the layer control.
+#' When clicked, it zooms the map to the predefined view of that layer.
+#' The opacity control slider allows users to adjust the opacity of layers. The legend will be appended
+#' to the corresponding layer control, matched by the layer's group name.
+#'
+#' @examples
+#' library(sf)
+#' library(leaflet)
+#' library(leafem)
+#'
+#' # Example data ##########
+#' breweries91 <- st_as_sf(breweries91)
+#' lines <- st_as_sf(atlStorms2005)
+#' polys <- st_as_sf(leaflet::gadmCHE)
+#'
+#' # View settings ##########
+#' view_settings <- list(
+#' "Base_tiles1" = list(
+#' coords = c(20, 50),
+#' zoom = 3
+#' ),
+#' "Base_tiles2" = list(
+#' coords = c(-110, 50),
+#' zoom = 5
+#' ),
+#' "breweries91" = list(
+#' coords = as.numeric(st_coordinates(st_centroid(st_union(breweries91)))),
+#' zoom = 8
+#' ),
+#' "atlStorms2005" = list(
+#' coords = as.numeric(st_bbox(lines)),
+#' options = list(padding = c(110, 110))
+#' ),
+#' "gadmCHE" = list(
+#' coords = as.numeric(st_bbox(polys)),
+#' options = list(padding = c(2, 2)),
+#' fly = TRUE
+#' )
+#' )
+#'
+#' # Opacity control settings ##########
+#' opacityControl <- list(
+#' "breweries91" = list(
+#' min = 0,
+#' max = 1,
+#' step = 0.1,
+#' default = 1,
+#' width = '100%',
+#' class = 'opacity-slider'
+#' )
+#' )
+#'
+#' # Legends ##########
+#' legends <- list(
+#' "breweries91" = "
Legend for breweries
"
+#' )
+#'
+#' leaflet() %>%
+#' ## Baselayer
+#' addTiles(group = "Base_tiles1") %>%
+#' addProviderTiles("CartoDB", group = "Base_tiles2") %>%
+#'
+#' ## Overlays
+#' addCircleMarkers(data = breweries91, group = "breweries91") %>%
+#' addPolylines(data = lines, group = "atlStorms2005") %>%
+#' addPolygons(data = polys, group = "gadmCHE") %>%
+#'
+#' ## Extend Layers Control
+#' extendLayerControl(
+#' view_settings, home_btns = TRUE,
+#' home_btn_options = list(
+#' "Base_tiles1" = list(text = '🏡', cursor = 'ns-resize', class = 'homebtn'),
+#' "Base_tiles2" = list(text = '❤️', cursor = 'pointer'),
+#' "atlStorms2005" = list(text = '🌎', cursor = 'all-scroll'),
+#' "breweries91" = list(text = '🌎', styles = 'background-color: red'),
+#' "gadmCHE" = list(text = '🌎', styles = 'float: none;')
+#' ),
+#' opacityControl = opacityControl,
+#' includelegends = TRUE
+#' ) %>%
+#'
+#' ## LayersControl
+#' addLayersControl(
+#' baseGroups = c("Base_tiles1", "Base_tiles2"),
+#' overlayGroups = c("breweries91", "atlStorms2005", "gadmCHE"),
+#' options = layersControlOptions(collapsed = FALSE, autoZIndex = TRUE)
+#' )
+#'
+#' @export
+extendLayerControl <- function(map, view_settings, home_btns = FALSE,
+ home_btn_options = list(), setviewonselect = TRUE,
+ opacityControl = list(),
+ includelegends = TRUE) {
+
+ # Initialize data structures for view settings and home buttons
+ view_data <- list()
+ home_data <- list()
+
+ # Loop over each layer to populate view_data and home_data
+ for (layer in names(view_settings)) {
+ setting <- view_settings[[layer]]
+
+ # Store coordinates and zoom options for setView or fitBounds
+ if (length(setting$coords) == 2) {
+ view_data[[layer]] <- list(
+ coords = setting$coords,
+ zoom = setting$zoom,
+ fly = ifelse(is.null(setting[["fly"]]), FALSE, setting[["fly"]]),
+ options = setting$options
+ )
+ } else if (length(setting$coords) == 4) {
+ view_data[[layer]] <- list(
+ bounds = setting$coords,
+ fly = ifelse(is.null(setting[["fly"]]), FALSE, setting[["fly"]]),
+ options = setting$options
+ )
+ }
+
+ # Store home button data if enabled
+ if (isTRUE(home_btns)) {
+ home_data[[layer]] <- as.list(c(
+ layer = layer, home_btn_options[[layer]]
+ ))
+ }
+ }
+
+ # Add deps & Pass view and home button data using invokeMethod
+ map$dependencies <- c(map$dependencies, layerViewControlDependencies())
+ leaflet::invokeMethod(
+ map,
+ NULL,
+ 'extendLayerControl',
+ view_data,
+ home_data,
+ setviewonselect,
+ opacityControl,
+ includelegends
+ )
+}
+
+
+layerViewControlDependencies <- function() {
+ list(
+ htmltools::htmlDependency(
+ "layerViewControl",
+ '0.0.1',
+ system.file("htmlwidgets/lib/layerviewcontrol", package = "leafem"),
+ script = "layerviewcontrol.js",
+ stylesheet = "layerviewcontrol.css"
+ ))
+}
diff --git a/R/logo.R b/R/logo.R
index 750354b..ab714a2 100644
--- a/R/logo.R
+++ b/R/logo.R
@@ -123,7 +123,6 @@ addLogo <- function(map,
}
#' updateLogo
-#' @inheritParams addLogo
#' @rdname addLogo
#' @export
updateLogo <- function(map, img, layerId) {
@@ -137,7 +136,6 @@ updateLogo <- function(map, img, layerId) {
}
#' removeLogo
-#' @inheritParams addLogo
#' @rdname addLogo
#' @export
removeLogo <- function(map, layerId) {
@@ -149,7 +147,6 @@ removeLogo <- function(map, layerId) {
}
#' hideLogo
-#' @inheritParams addLogo
#' @rdname addLogo
#' @export
hideLogo <- function(map, layerId) {
@@ -161,7 +158,6 @@ hideLogo <- function(map, layerId) {
}
#' showLogo
-#' @inheritParams addLogo
#' @rdname addLogo
#' @export
showLogo <- function(map, layerId) {
diff --git a/inst/.gitignore b/inst/.gitignore
new file mode 100644
index 0000000..038d718
--- /dev/null
+++ b/inst/.gitignore
@@ -0,0 +1 @@
+testing
diff --git a/inst/htmlwidgets/lib/layerviewcontrol/layerviewcontrol.css b/inst/htmlwidgets/lib/layerviewcontrol/layerviewcontrol.css
new file mode 100644
index 0000000..3aec75f
--- /dev/null
+++ b/inst/htmlwidgets/lib/layerviewcontrol/layerviewcontrol.css
@@ -0,0 +1,8 @@
+.leaflet-control-layers-overlays .legend {
+ width: 100% !important;
+ padding: 0 !important;
+ box-shadow: unset !important;
+ margin: 0;
+ left: 13px;
+}
+
diff --git a/inst/htmlwidgets/lib/layerviewcontrol/layerviewcontrol.js b/inst/htmlwidgets/lib/layerviewcontrol/layerviewcontrol.js
new file mode 100644
index 0000000..e7d615d
--- /dev/null
+++ b/inst/htmlwidgets/lib/layerviewcontrol/layerviewcontrol.js
@@ -0,0 +1,160 @@
+LeafletWidget.methods.extendLayerControl = function(viewSettings, homeSettings, setviewonselect, opacityControl, includelegends) {
+ const map = this;
+
+ // Handle view settings for each layer on 'overlayadd' or 'baselayerchange'
+ map.on('overlayadd baselayerchange', function(e) {
+ let setting = viewSettings[e.name];
+ if (setting && setviewonselect) handleView(map, setting);
+ });
+
+ // Handle home buttons
+ if (homeSettings) {
+ setTimeout(() => {
+ Object.entries(homeSettings).forEach(([layer, options]) => {
+ let homeButton = document.createElement('span');
+ Object.assign(homeButton.style, {
+ cursor: options.cursor || 'pointer',
+ cssText: options.styles || 'float: inline-end;'
+ });
+ homeButton.className = options.class || 'leaflet-home-btn';
+ homeButton.dataset.layer = layer;
+ homeButton.innerHTML = options.text || '🏠';
+
+ appendToLabelHome(layer, homeButton);
+
+ homeButton.addEventListener('click', function(event) {
+ event.preventDefault();
+ event.stopPropagation();
+ let setting = viewSettings[this.dataset.layer];
+ if (setting) handleView(map, setting);
+ });
+ });
+ }, 20);
+ }
+
+ // Handle opacity control
+ if (opacityControl) {
+ // Helper function to check if the layer is active
+ function isLayerActive(layerName) {
+ return Object.keys(map.layerManager._groupContainers).some(function(layer) {
+ return layer === layerName;
+ });
+ }
+
+ setTimeout(() => {
+ Object.entries(opacityControl).forEach(([layer, options]) => {
+ let sliderContainer = document.createElement('div');
+ let slider = document.createElement('input');
+ Object.assign(slider, {
+ type: 'range',
+ min: options.min || 0,
+ max: options.max || 1,
+ step: options.step || 0.1,
+ value: options.default || 1,
+ style: `width: ${options.width || '100%'};`
+ });
+ slider.className = options.class || 'leaflet-opacity-slider';
+ sliderContainer.style.display = 'none';
+ sliderContainer.appendChild(slider);
+
+ appendToLabel(layer, sliderContainer);
+
+ slider.addEventListener('input', function() {
+ let opacityVal = parseFloat(this.value);
+ Object.values(map.layerManager._byGroup[layer]).forEach(layer => {
+ if (layer.setOpacity) {
+ layer.setOpacity(opacityVal);
+ } else if (layer.setStyle) {
+ layer.setStyle({ opacity: opacityVal, fillOpacity: opacityVal });
+ }
+ });
+ });
+
+ // Initialize slider visibility based on the current state of the layer
+ if (isLayerActive(layer)) {
+ sliderContainer.style.display = 'block';
+ }
+
+ // Handle layer visibility
+ map.on('overlayadd overlayremove baselayerchange', function(e) {
+ if (e.name === layer) {
+ sliderContainer.style.display = (e.type === 'overlayadd' || e.type === 'baselayerchange') ? 'block' : 'none';
+ }
+ });
+
+ });
+ }, 30);
+ }
+
+ // Handle legends
+ if (includelegends) {
+ function moveLegends() {
+ Object.entries(map.controls._controlsById).forEach(([controlId, control]) => {
+ let legendContainer = control._container;
+ if (legendContainer) {
+ appendToLabel(controlId, legendContainer)
+ }
+ });
+
+ // Fix for leaflegend package
+ let elements = document.querySelectorAll('[class*="leaflegend-group"]');
+ elements.forEach(function(element) {
+ // Find the class that starts with 'leaflegend-group-'
+ let groupClass = Array.from(element.classList).find(cls => cls.startsWith('leaflegend-group-'));
+
+ if (groupClass) {
+ // Extract everything after 'leaflegend-group-'
+ let groupName = groupClass.split('leaflegend-group-')[1];
+ appendToLabel(groupName, element)
+ }
+ });
+
+ }
+ setTimeout(moveLegends, 40);
+ map.on('overlayadd baselayerchange', () => setTimeout(moveLegends, 20));
+ }
+};
+
+
+
+// function to handle setting view or bounds
+function handleView(map, setting) {
+ if (setting.coords) {
+ const method = setting.fly ? 'flyTo' : 'setView';
+ map[method]([setting.coords[1], setting.coords[0]], setting.zoom, setting.options);
+ } else if (setting.bounds) {
+ const method = setting.fly ? 'flyToBounds' : 'fitBounds';
+ const bounds = [[setting.bounds[1], setting.bounds[0]], [setting.bounds[3], setting.bounds[2]]];
+ map[method](bounds, setting.options);
+ }
+}
+
+// function to find the correct label element
+function findLabel(layerName) {
+ return Array.from(document.querySelectorAll('.leaflet-control-layers label:not([class])')).find(label =>
+ $(label).find("span")[0].textContent.trim() === layerName
+ );
+}
+
+// function to append a child to the label element
+function appendToLabel(layer, childElement) {
+ const label = findLabel(layer);
+ if (label) {
+ let labelDiv = label.querySelector('div') || document.createElement('div');
+ labelDiv.appendChild(childElement);
+ label.appendChild(labelDiv);
+ }
+}
+
+// function to append a child to the label element
+function appendToLabelHome(layer, childElement) {
+ const label = findLabel(layer);
+ if (label) {
+ let labelDiv = label.querySelector('div')
+ if (labelDiv) {
+ labelDiv.appendChild(childElement);
+ } else {
+ label.appendChild(childElement);
+ }
+ }
+}
diff --git a/inst/test_layerviewcontrol.R b/inst/test_layerviewcontrol.R
new file mode 100644
index 0000000..d80ea89
--- /dev/null
+++ b/inst/test_layerviewcontrol.R
@@ -0,0 +1,190 @@
+library(sf)
+library(shiny)
+library(leaflet)
+library(leaflet.extras)
+library(leaflegend)
+library(leafem)
+options("shiny.autoreload" = TRUE)
+
+# Example data ##########
+breweries91 <- st_as_sf(breweries91)
+lines <- st_as_sf(atlStorms2005)
+data("gadmCHE")
+gadmCHE@data$x <- sample(c('A', 'B', 'C'), nrow(gadmCHE@data), replace = TRUE)
+polys <- st_as_sf(gadmCHE)
+overlay1 <- "Overlay with Legend (orange)"
+overlay2 <- "Overlay with Legend (blue)"
+
+n = 300
+df1 = data.frame(id = 1:n,
+ x = rnorm(n, 20, 3),
+ y = rnorm(n, -49, 1.8))
+pts = st_as_sf(df1, coords = c("x", "y"), crs = 4326)
+dfnew <- local({
+ n <- 300; x <- rnorm(n, mean = 30); y <- rnorm(n, 50)
+ z <- sqrt(x ^ 2 + y ^ 2); z[sample(n, 10)] <- NA
+ data.frame(x, y, z)
+})
+palnew <- colorNumeric("OrRd", dfnew$z)
+palnew2 <- colorNumeric("Blues", dfnew$z)
+
+# View settings: Each entry is a list with 'coords', 'zoom', and optional 'options' (e.g., padding) ##########
+view_settings <- list(
+ "Base_tiles1" = list(
+ coords = c(20, 50)
+ , zoom = 3
+ ),
+ "Base_tiles2" = list(
+ coords = c(-110, 50)
+ , zoom = 5
+ ),
+ "breweries91" = list(
+ coords = as.numeric(st_coordinates(st_centroid(st_union(breweries91))))
+ , zoom = 8
+ , options = NULL
+ ),
+ "atlStorms2005" = list(
+ coords = as.numeric(st_bbox(lines))
+ # , options = list(padding = c(10, 10), maxZoom = 6)
+ ),
+ "gadmCHE" = list(
+ coords = as.numeric(st_bbox(polys))
+ , options = list(padding = c(10, 10))
+ , fly = TRUE
+ ),
+ "random_points" = list(
+ coords = as.numeric(st_coordinates(st_centroid(st_union(pts))))
+ , zoom = 7
+ , fly = TRUE
+ ),
+ overlay1 = list(
+ coords = c(mean(dfnew$x), mean(dfnew$y))
+ , zoom = 7
+ ) ,
+ overlay2 = list(
+ coords = c(mean(dfnew$x), mean(dfnew$y))
+ , zoom = 7
+ )
+)
+names(view_settings)[names(view_settings)=="overlay1"] <- overlay1
+names(view_settings)[names(view_settings)=="overlay2"] <- overlay2
+
+# Create leaflet map and apply the layer control function #########
+ui <- fluidPage(
+ ## Custom CSS ###########
+ tags$head(tags$style("
+ .home-btn-layer3 {
+ background-color: gray;
+ padding; 4px
+ }
+ .home-btn-layer3 {
+ background-image: url(https://png.pngtree.com/png-clipart/20190904/original/pngtree-zoom-in-icon-png-image_4490537.jpg);
+ content: '';
+ color: transparent;
+ width: 22px;
+ height: 22px;
+ background-size: cover;
+ background-position: center;
+ background-repeat: no-repeat;
+ }
+ ")),
+ ## Input + Map ###########
+ tags$div(style="display: inline-flex",
+ selectInput("layercontrol", "Layer Control",
+ choices = c("layercontrol", "groupedlayercontrol")),
+ shiny::checkboxInput("includelegends", "Inlcude Legends", value = TRUE),
+ shiny::checkboxInput("homebtns", "Home Buttons", value = TRUE),
+ shiny::checkboxInput("setviewonselect", "Set View on select", value = TRUE),
+ ),
+ leafletOutput("map", height = 800)
+)
+
+## Server ##################
+server <- function(input, output, session) {
+ output$map <- renderLeaflet({
+
+ factorPal <- colorFactor(c('#1f77b4', '#ff7f0e' , '#2ca02c'), gadmCHE@data$x)
+ binPal <- colorBin('Set1', lines$MaxWind, bins = 4)
+ quantPal <- colorQuantile('Reds', lines$MaxWind, n = 3)
+
+ m <- leaflet() %>%
+ ## Baselayer ##########
+ addTiles(group = "Base_tiles1") %>%
+ addProviderTiles("CartoDB", group = "Base_tiles2") %>%
+
+ ## Overlays ##########
+ addCircleMarkers(data = breweries91, group = "breweries91") %>%
+ addCircleMarkers(data = pts, opacity = 1, fillOpacity = .4,
+ group = "random_points", color = "red", weight = 1) %>%
+ # addLegendSize(values = 1, color = 'red', shape = 'circle', breaks = 1, group = "random_points", layerId="random_points") %>%
+ addLegendImage(images = makeSymbol(shape="circle", color = "red", opacity = 1, fillOpacity = .4, width = 10),
+ labels = "", group = "random_points", layerId="random_points",
+ orientation = 'horizontal') %>%
+ addPolylines(data = lines, color = ~quantPal(MaxWind), label=~MaxWind, group = "atlStorms2005") %>%
+ addLegendQuantile(data = lines, pal = quantPal, values = ~MaxWind, numberFormat = NULL,
+ group = "atlStorms2005", position = 'topright') %>%
+ addPolygons(data = polys, color = ~factorPal(x), label=~x, group = "gadmCHE") %>%
+ addLegendFactor(pal = factorPal, shape = 'polygon', fillOpacity = .5,
+ opacity = 0, values = ~x,
+ position = 'topright', data = gadmCHE, group = 'gadmCHE') %>%
+ addCircleMarkers(data = dfnew, ~x, ~y, color = ~palnew(z), label=~z, group = overlay1) %>%
+ addCircleMarkers(data = dfnew, ~x, ~y, color = ~palnew2(z), label=~z, group = overlay2) %>%
+ addLegendNumeric(orientation = "horizontal", width = 180, height = 20,
+ data = dfnew, pal = palnew, layerId = overlay1,
+ values = ~z, group = overlay1, position = "bottomleft") %>%
+ addLegend(data = dfnew, pal = palnew2, layerId = overlay2, values = ~z, group = overlay2, position = "bottomleft") %>%
+
+ ## extendLayerControl ##########
+ extendLayerControl(view_settings
+ , includelegends = input$includelegends
+ , home_btns = input$homebtns
+ , setviewonselect = input$setviewonselect
+ , home_btn_options = list(
+ "Base_tiles1" = list(text = '🏡', cursor = 'ns-resize', class = 'homebtn home-btn-layer1'),
+ "Base_tiles2" = list(text = '❤️', cursor = 'pointer', class = 'homebtn home-btn-layer2'),
+ "random_points" = list(text = '🌎', cursor = 'all-scroll', class = 'homebtn home-btn-layer3'),
+ "Overlay with Legend (orange)" = list(text = '🚊', cursor = 'all-scroll', class = 'homebtn home-btn-layer3'),
+ "Overlay with Legend (blue)" = list(text = '🚊', cursor = 'all-scroll', class = 'homebtn home-btn-layer3')
+ )
+ , opacityControl = list(
+ "random_points" = list(min= 0, max= 1, step= 0.01, default= 0.7, width= '140px'),
+ "Overlay with Legend (orange)" = list(min= 0.1, max= 0.8, step= 0.1, default= 1),
+ "Overlay with Legend (blue)" = list(default= 0.5)
+ )
+ )
+
+ ## LayersControls ##########
+ if (input$layercontrol == "layercontrol") {
+ m %>%
+ addLayersControl(
+ baseGroups = c("Base_tiles1", "Base_tiles2"
+ ),
+ overlayGroups = c("breweries91", "random_points",
+ overlay1, overlay2,
+ "atlStorms2005", "gadmCHE"),
+ options = layersControlOptions(collapsed = FALSE, autoZIndex = TRUE)
+ )
+ } else {
+ m %>%
+ addGroupedLayersControl(
+ baseGroups = c("Base_tiles1","Base_tiles2"),
+ overlayGroups = list(
+ "Group1" = c("breweries91","random_points",
+ overlay1, overlay2),
+ "Group2" = c("atlStorms2005", "gadmCHE")),
+ position = "topright",
+ options = groupedLayersControlOptions(groupCheckboxes = TRUE,
+ collapsed = FALSE,
+ groupsCollapsable = TRUE,
+ groupsExpandedClass = "glyphicon glyphicon-chevron-down",
+ groupsCollapsedClass = "glyphicon glyphicon-chevron-right",
+ sortLayers = FALSE,
+ sortGroups = FALSE,
+ sortBaseLayers = FALSE,
+ exclusiveGroups = "Group2")
+ )
+ }
+
+ })
+}
+shinyApp(ui, server)
diff --git a/man/extendLayerControl.Rd b/man/extendLayerControl.Rd
new file mode 100644
index 0000000..17faca9
--- /dev/null
+++ b/man/extendLayerControl.Rd
@@ -0,0 +1,154 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/layerviewcontrol.R
+\name{extendLayerControl}
+\alias{extendLayerControl}
+\title{Extend Layers Control in Leaflet Map}
+\usage{
+extendLayerControl(
+ map,
+ view_settings,
+ home_btns = FALSE,
+ home_btn_options = list(),
+ setviewonselect = TRUE,
+ opacityControl = list(),
+ includelegends = TRUE
+)
+}
+\arguments{
+\item{map}{A `leaflet` or `mapview` object to which the extended layers control will be added.}
+
+\item{view_settings}{A list specifying the view settings for each layer. Each list element should contain
+either:
+\itemize{
+ \item \code{coords}: A vector of length 2 (latitude, longitude) for setting the view, or length 4
+ (bounding box: lat1, lng1, lat2, lng2) for fitting the bounds.
+ \item \code{zoom}: The zoom level (used for `setView`).
+ \item \code{fly} (optional): A logical indicating whether to use `flyTo` or `flyToBounds` instead of `setView` or `fitBounds`.
+ \item \code{options} (optional): Additional options to pass to `setView`, `fitBounds`, or `flyTo`.
+}}
+
+\item{home_btns}{Logical. If `TRUE`, adds a "home" button next to each layer name in the layer control.
+Clicking the home button zooms the map to the view specified for that layer in \code{view_settings}.}
+
+\item{home_btn_options}{A list of options to customize the home button appearance and behavior.
+Possible options include:
+- `text`: The text or emoji to display on the button (default is '🏠').
+- `cursor`: CSS cursor style for the button (default is 'pointer').
+- `class`: CSS class name for the button (default is 'leaflet-home-btn').
+- `styles`: Semicolon separated CSS-string (default is 'float: inline-end;').}
+
+\item{setviewonselect}{Logical. If `TRUE` (default) sets the view when the layer is selected.}
+
+\item{opacityControl}{A list specifying the opacity control settings for each layer. Each list element should contain:
+\itemize{
+ \item \code{min}: Minimum opacity value (default is 0).
+ \item \code{max}: Maximum opacity value (default is 1).
+ \item \code{step}: Step size for the opacity slider (default is 0.1).
+ \item \code{default}: Default opacity value (default is 1).
+ \item \code{width}: Width of the opacity slider (default is '100%').
+ \item \code{class}: CSS class name for the slider (default is 'leaflet-opacity-slider').
+}}
+
+\item{includelegends}{Logical. If `TRUE` (default), appends legends to the layer control. Legends are matched
+to layers by their group name. The legends need to be added with corresponding layer IDs.}
+}
+\value{
+A modified `leaflet` map object with extended layers control including view controls, home buttons, opacity controls, and legends.
+}
+\description{
+This function extends an existing layers control in a `leaflet` map by adding custom views, home buttons,
+opacity controls, and legends. It enhances the functionality of a layers control created with `leaflet`
+or `leaflet.extras`.
+}
+\details{
+This function generates JavaScript that listens for `overlayadd` or `baselayerchange` events
+and automatically sets the view or zoom level according to the specified \code{view_settings}.
+If `home_btns` is enabled, a home button is added next to each layer in the layer control.
+When clicked, it zooms the map to the predefined view of that layer.
+The opacity control slider allows users to adjust the opacity of layers. The legend will be appended
+to the corresponding layer control, matched by the layer's group name.
+}
+\examples{
+library(sf)
+library(leaflet)
+library(leafem)
+
+# Example data ##########
+breweries91 <- st_as_sf(breweries91)
+lines <- st_as_sf(atlStorms2005)
+polys <- st_as_sf(leaflet::gadmCHE)
+
+# View settings ##########
+view_settings <- list(
+ "Base_tiles1" = list(
+ coords = c(20, 50),
+ zoom = 3
+ ),
+ "Base_tiles2" = list(
+ coords = c(-110, 50),
+ zoom = 5
+ ),
+ "breweries91" = list(
+ coords = as.numeric(st_coordinates(st_centroid(st_union(breweries91)))),
+ zoom = 8
+ ),
+ "atlStorms2005" = list(
+ coords = as.numeric(st_bbox(lines)),
+ options = list(padding = c(110, 110))
+ ),
+ "gadmCHE" = list(
+ coords = as.numeric(st_bbox(polys)),
+ options = list(padding = c(2, 2)),
+ fly = TRUE
+ )
+)
+
+# Opacity control settings ##########
+opacityControl <- list(
+ "breweries91" = list(
+ min = 0,
+ max = 1,
+ step = 0.1,
+ default = 1,
+ width = '100\%',
+ class = 'opacity-slider'
+ )
+)
+
+# Legends ##########
+legends <- list(
+ "breweries91" = "Legend for breweries
"
+)
+
+leaflet() \%>\%
+ ## Baselayer
+ addTiles(group = "Base_tiles1") \%>\%
+ addProviderTiles("CartoDB", group = "Base_tiles2") \%>\%
+
+ ## Overlays
+ addCircleMarkers(data = breweries91, group = "breweries91") \%>\%
+ addPolylines(data = lines, group = "atlStorms2005") \%>\%
+ addPolygons(data = polys, group = "gadmCHE") \%>\%
+
+ ## Extend Layers Control
+ extendLayerControl(
+ view_settings, home_btns = TRUE,
+ home_btn_options = list(
+ "Base_tiles1" = list(text = '🏡', cursor = 'ns-resize', class = 'homebtn'),
+ "Base_tiles2" = list(text = '❤️', cursor = 'pointer'),
+ "atlStorms2005" = list(text = '🌎', cursor = 'all-scroll'),
+ "breweries91" = list(text = '🌎', styles = 'background-color: red'),
+ "gadmCHE" = list(text = '🌎', styles = 'float: none;')
+ ),
+ opacityControl = opacityControl,
+ includelegends = TRUE
+ ) \%>\%
+
+ ## LayersControl
+ addLayersControl(
+ baseGroups = c("Base_tiles1", "Base_tiles2"),
+ overlayGroups = c("breweries91", "atlStorms2005", "gadmCHE"),
+ options = layersControlOptions(collapsed = FALSE, autoZIndex = TRUE)
+ )
+
+}