From 60fb4e7398828d301105db39b69e1ec2cd988418 Mon Sep 17 00:00:00 2001 From: Daniel Lopez Date: Sat, 1 Nov 2025 22:49:29 -0400 Subject: [PATCH 01/17] fix: Resolve warnings by correcting Rmd image paths --- inst/shiny/Rmd/text_about.Rmd | 2 +- inst/shiny/Rmd/text_team.Rmd | 40 +++++++++++++++++------------------ 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/inst/shiny/Rmd/text_about.Rmd b/inst/shiny/Rmd/text_about.Rmd index 4d8be876..c7d35b68 100644 --- a/inst/shiny/Rmd/text_about.Rmd +++ b/inst/shiny/Rmd/text_about.Rmd @@ -5,7 +5,7 @@ output: html_document ### **What is *Wallace*?** -logo +logo Welcome to *Wallace*, a flexible application for reproducible ecological modeling, built for community expansion. The current version of *Wallace* (v2.2.0) steps the user through a full niche/distribution modeling analysis, from data acquisition to visualizing results. diff --git a/inst/shiny/Rmd/text_team.Rmd b/inst/shiny/Rmd/text_team.Rmd index f557af4e..7826f6a8 100644 --- a/inst/shiny/Rmd/text_team.Rmd +++ b/inst/shiny/Rmd/text_team.Rmd @@ -8,102 +8,102 @@ output: html_document ## Developers -bethany +bethany Bethany A. Johnson (lead developer) received her MS in Biology from the City College of New York and is currently a Visiting Scientist at the Center for Biodiversity & Conservation at the American Museum of Natural History.
-jamie +jamie Jamie M. Kass (developer) received his PhD at CUNY Graduate Center and City College of New York, did a Postdoc at Okinawa Institute of Science and Technology, and currently is Associate Professor at Tohoku University.
-gonzalo +gonzalo Gonzalo E. Pinilla-Buitrago (developer) received his PhD from the CUNY Graduate Center and City College of New York. He is currently a Postdoctoral Researcher at University of Connecticut.
-andrea +andrea Andrea Paz (developer) received her PhD at CUNY Graduate Center and City College of New York, after which she was a Postdoctoral Researcher at the Crowther Lab in ETH Zürich. She is now an Assistant Professor at the Biological Sciences Department, University of Montréal.
-dani +dani Daniel López Lozano (developer) is a Biodiversity Informatics Specialist at the American Museum of Natural History's Center for Biodiversity & Conservation.
-valentina +valentina Valentina Grisales-Betancur (developer) completed her undergraduate degree at EAFIT University in Colombia and visited the Anderson lab at CCNY and Blair lab at AMNH.
-dean +dean Dean Attali (developer, consultant) is an R-Shiny developer and consultant with a MSc in Bioinformatics from the University of British Columbia.
## Managers -rob +rob Robert P. Anderson (lead manager, developer) is a Professor of Biology at City College of New York, CUNY.
-matt +matt Matthew Aiello-Lammens (co-manager, developer) is an Associate Professor of Biology at Pace University.
-cory +cory Cory Merow (co-manager, developer) is currently a faculty researcher at the University of Connecticut.
-mary +mary -Mary E. Blair (co-manager) is the Director of Biodiversity Informatics Research at the Center for Biodiversity & Conservation at the American Museum of Natural History. +Mary E. Blair (co-manager) is the Associate Director at the Center for Biodiversity & Conservation at the American Museum of Natural History.
## Contributors -sarah +sarah Sarah Meenan (contributor) completed her undergraduate degree from the City College of New York and now works for the NYC Parks Department.
-olivier +olivier Olivier Broennimann (contributor, external partner) is a staff scientist in the Department of Ecology and Evolution at University of Lausanne.
-peteG +peteG Peter J. Galante (contributor) was formerly a Biodiversity Informatics Scientist at the Center for Biodiversity & Conservation at the American Museum of Natural History and now works as a Project Manager for Storke, LLC, a renewable energy company.
-brianM +brianM Brian S. Maitner (contributor, external partner) is a researcher at the Geography Department of the University at Buffalo, SUNY.
-hannahO +hannahO Hannah Owens (contributor, external partner) is an assistant professor at the Center for Global Mountain Biodiversity & the Center for Macroecology, Evolution, and Climate; Section for Biodiversity; Globe Institute; at the University of Copenhagen.
-saraV +saraV Sara Varela (contributor, external partner) is a Principal Investigator at the MAPAS Lab at the University of Vigo in Spain.
## Past Developers -bob +bob Robert Muscarella (contributor, past developer) is an Associate Professor at Uppsala University in Sweden.
-bruno +bruno Bruno Vilela (contributor, past developer) is a professor at Universidade Federal da Bahia in Brazil. From e277dd8120fdd4236066dd7fccbb42d6a33acecf Mon Sep 17 00:00:00 2001 From: Daniel Lopez Date: Tue, 16 Dec 2025 13:13:41 -0500 Subject: [PATCH 02/17] Fix: Enable running models with categorical variables when using ENMeval >2.0.5 --- R/model_bioclim.R | 8 ++-- R/model_maxent.R | 14 ++++-- R/penvs_bgMask.R | 18 +++---- R/penvs_bgSample.R | 2 +- R/vis_bioclimPlot.R | 2 +- R/xfer_area.R | 2 +- R/xfer_time.R | 2 +- R/xfer_userEnvs.R | 2 +- inst/shiny/modules/part_spat.Rmd | 2 +- inst/shiny/modules/penvs_bgExtent.R | 9 ++-- inst/shiny/modules/penvs_bgExtent.Rmd | 4 +- inst/shiny/modules/penvs_drawBgExtent.R | 9 ++-- inst/shiny/modules/penvs_drawBgExtent.Rmd | 4 +- inst/shiny/modules/penvs_userBgExtent.R | 9 ++-- inst/shiny/modules/penvs_userBgExtent.Rmd | 4 +- inst/shiny/modules/vis_mapPreds.R | 12 ++--- inst/shiny/modules/vis_mapPreds.Rmd | 12 ++--- inst/shiny/modules/xfer_mess.R | 4 +- inst/shiny/modules/xfer_mess.Rmd | 2 +- inst/shiny/modules/xfer_time.Rmd | 58 ++++++++++++++--------- inst/shiny/server.R | 8 ++-- tests/testthat/test_model_bioclim.R | 2 +- tests/testthat/test_model_maxent.R | 4 +- tests/testthat/test_penvs_bgMask.R | 18 +++---- tests/testthat/test_penvs_bgSample.R | 7 +-- tests/testthat/test_vis_bioclimPlot.R | 2 +- tests/testthat/test_xfer_area.R | 2 +- tests/testthat/test_xfer_time.R | 2 +- tests/testthat/test_xfer_userEnvs.R | 2 +- 29 files changed, 125 insertions(+), 101 deletions(-) diff --git a/R/model_bioclim.R b/R/model_bioclim.R index 85583084..2411ec2c 100644 --- a/R/model_bioclim.R +++ b/R/model_bioclim.R @@ -35,8 +35,8 @@ #' @param bg coordinates of background points to be used for modeling. #' @param user.grp a list of two vectors containing group assignments for #' occurrences (occs.grp) and background points (bg.grp). -#' @param bgMsk a RasterStack or a RasterBrick of environmental layers cropped -#' and masked to match the provided background extent. +#' @param bgMsk a SpatRaster of environmental layers cropped and masked to +#' match the provided background extent. #' @param logger Stores all notification messages to be displayed in the Log #' Window of Wallace GUI. Insert the logger reactive list here for running #' in shiny, otherwise leave the default NULL. @@ -55,7 +55,7 @@ #' bg <- read.csv(system.file("extdata/Bassaricyon_alleni_bgPoints.csv", #' package = "wallace")) #' partblock <- part_partitionOccs(occs, bg, method = 'block') -#' m <- model_bioclim(occs, bg, partblock, envs) +#' m <- model_bioclim(occs, bg, partblock, terra::rast(envs)) #' } #' #' @return Function returns an ENMevaluate object with all the evaluated models @@ -80,7 +80,7 @@ model_bioclim <- function(occs, bg, user.grp, bgMsk, logger = NULL, smartProgress(logger, message = paste0("Building/Evaluating BIOCLIM model for ", spName(spN), "..."), { - e <- ENMeval::ENMevaluate(occs = occs.xy, envs = terra::rast(bgMsk), bg = bg.xy, + e <- ENMeval::ENMevaluate(occs = occs.xy, envs = bgMsk, bg = bg.xy, algorithm = "bioclim", partitions = "user", user.grp = user.grp) }) diff --git a/R/model_maxent.R b/R/model_maxent.R index a1365dba..594dca46 100644 --- a/R/model_maxent.R +++ b/R/model_maxent.R @@ -36,8 +36,8 @@ #' @param bg coordinates of background points to be used for modeling. #' @param user.grp a list of two vectors containing group assignments for #' occurrences (occs.grp) and background points (bg.grp). -#' @param bgMsk a RasterStack or a RasterBrick of environmental layers cropped -#' and masked to match the provided background extent. +#' @param bgMsk a SpatRaster of environmental layers cropped and masked to +#' match the provided background extent. #' @param rms vector of range of regularization multipliers to be used in the #' ENMeval run. #' @param rmsStep step to be used when defining regularization multipliers to @@ -76,7 +76,7 @@ #' rmsStep <- 1 #' fcs <- c('L', 'LQ') #' m <- model_maxent(occs = occs, bg = bg, user.grp = partblock, -#' bgMsk = envs, rms = rms, rmsStep, fcs, +#' bgMsk = terra::rast(envs), rms = rms, rmsStep, fcs, #' clampSel = TRUE, algMaxent = "maxnet", #' parallel = FALSE) #' } @@ -192,12 +192,18 @@ model_maxent <- function(occs, bg, user.grp, bgMsk, rms, rmsStep, fcs, # get just coordinates occs.xy <- occs %>% dplyr::select("longitude", "latitude") bg.xy <- bg %>% dplyr::select("longitude", "latitude") + + # convert the categorical variables to factors + if (!is.null(catEnvs)) { + bgMsk[[catEnvs]] <- terra::as.factor(bgMsk[[catEnvs]]) + } + # run ENMeval e <- ENMeval::ENMevaluate(occs = as.data.frame(occs.xy), bg = as.data.frame(bg.xy), partitions = 'user', user.grp = user.grp, - envs = terra::rast(bgMsk), + envs = bgMsk, tune.args = tune.args, doClamp = clampSel, algorithm = algMaxent, diff --git a/R/penvs_bgMask.R b/R/penvs_bgMask.R index 7aca1b86..fbd2cfcb 100644 --- a/R/penvs_bgMask.R +++ b/R/penvs_bgMask.R @@ -31,8 +31,8 @@ #' environmental layers to be used in the modeling are cropped and masked #' to the provided background area. The background area is determined in #' the function penvs_bgExtent from the same component. The function returns -#' the provided environmental layers cropped and masked in the provided -#' format (either a rasterBrick or a rasterStack). +#' the provided environmental layers cropped and masked in the SpatRaster +#' format. #' #' @param occs data frame of cleaned or processed occurrences obtained from #' components occs: Obtain occurrence data or, poccs: Process occurrence data. @@ -59,10 +59,11 @@ #' bgMask <- penvs_bgMask(occs, envs, bgExt) #' } #' -#' @return A RasterStack or a RasterBrick of environmental layers cropped and +#' @return A SpatRaster of environmental layers cropped and #' masked to match the provided background extent. #' @author Jamie Kass #' @author Gonzalo E. Pinilla-Buitrago +#' @author Daniel Lopez-Lozano #' @seealso \code{\link{penvs_userBgExtent}}, #' \code{\link{penvs_drawBgExtent}}, \code{\link{penvs_bgExtent}}, #' \code{\link{penvs_bgSample}} @@ -81,18 +82,17 @@ penvs_bgMask <- function(occs, envs, bgExt, logger = NULL, spN = NULL) { message = paste0("Masking rasters for ", spName(spN), "..."), { - bgCrop <- raster::crop(envs, bgExt) - bgMask <- raster::mask(bgCrop, bgExt) + envs <- terra::rast(envs) + bgCrop <- terra::crop(envs, bgExt) + bgMask <- terra::mask(bgCrop, terra::vect(bgExt)) # GEPB: Workaround when raster alignment is changed after crop, which makes appears # new duplicated occs in the same grid cells. - occsEnvsVals <- as.data.frame(raster::extract(bgMask, + occsEnvsVals <- as.data.frame(terra::extract(bgMask, occs[, c('longitude', 'latitude')], cellnumbers = TRUE)) occs.dups <- duplicated(occsEnvsVals[, 1]) if (sum(occs.dups) > 0) { - bgMask <- terra::project(terra::rast(bgMask), - terra::rast(envs), method = 'near') - bgMask <- methods::as(bgMask, "Raster") + bgMask <- terra::project(bgMask, envs, method = 'near') } }) diff --git a/R/penvs_bgSample.R b/R/penvs_bgSample.R index e16523ac..709936eb 100644 --- a/R/penvs_bgSample.R +++ b/R/penvs_bgSample.R @@ -58,7 +58,7 @@ #' doBrick = TRUE) #' bgExt <- penvs_bgExtent(occs, bgSel = 'bounding box', bgBuf = 0.5) #' bgMask <- penvs_bgMask(occs, envs, bgExt) -#' bgsample <- penvs_bgSample(occs, bgMask, bgPtsNum = 1000) +#' bgsample <- penvs_bgSample(occs, raster::stack(bgMask), bgPtsNum = 1000) #' } #' #' @return a dataframe containing point coordinates (longitude and latitude). diff --git a/R/vis_bioclimPlot.R b/R/vis_bioclimPlot.R index d20a9479..cc0e59bd 100644 --- a/R/vis_bioclimPlot.R +++ b/R/vis_bioclimPlot.R @@ -56,7 +56,7 @@ #' bg <- read.csv(system.file("extdata/Bassaricyon_alleni_bgPoints.csv", #' package = "wallace")) #' partblock <- part_partitionOccs(occs, bg, method = 'block') -#' m <- model_bioclim(occs, bg, partblock, envs) +#' m <- model_bioclim(occs, bg, partblock, terra::rast(envs)) #' bioclimPlot <- vis_bioclimPlot(x = m@@models$bioclim, #' a = 1, b = 2, p = 1) #' } diff --git a/R/xfer_area.R b/R/xfer_area.R index 4821aa61..b62ff067 100644 --- a/R/xfer_area.R +++ b/R/xfer_area.R @@ -77,7 +77,7 @@ #' partblock <- part_partitionOccs(occs, bg, method = 'block') #' m <- model_maxent(occs, bg, #' user.grp = partblock, -#' bgMsk = envs, rms = c(1:2), +#' bgMsk = terra::rast(envs), rms = c(1:2), #' rmsStep = 1, fcs = c('L', 'LQ'), #' clampSel = TRUE, #' algMaxent = "maxnet", diff --git a/R/xfer_time.R b/R/xfer_time.R index d6f69121..00789725 100644 --- a/R/xfer_time.R +++ b/R/xfer_time.R @@ -78,7 +78,7 @@ #' occs <- read.csv(system.file("extdata/Bassaricyon_alleni.csv",package = "wallace")) #' bg <- read.csv(system.file("extdata/Bassaricyon_alleni_bgPoints.csv", package = "wallace")) #' partblock <- part_partitionOccs(occs, bg, method = 'block') -#' m <- model_maxent(occs, bg, user.grp = partblock, bgMsk = envs, rms = c(1:2), +#' m <- model_maxent(occs, bg, user.grp = partblock, bgMsk = terra::rast(envs), rms = c(1:2), #' rmsStep = 1, fcs = c('L', 'LQ'), #' clampSel = TRUE, algMaxent = "maxnet", parallel = FALSE) #' occsEnvs <- m@@occs diff --git a/R/xfer_userEnvs.R b/R/xfer_userEnvs.R index 3e361998..c94dd2dc 100644 --- a/R/xfer_userEnvs.R +++ b/R/xfer_userEnvs.R @@ -72,7 +72,7 @@ #' rasName = list.files(system.file("extdata/wc",package = "wallace"), #' pattern = ".tif$", full.names = FALSE)) #' partblock <- part_partitionOccs(occs, bg, method = 'block') -#' m <- model_maxent(occs, bg, user.grp = partblock, bgMsk = envs, rms = c(1:2), +#' m <- model_maxent(occs, bg, user.grp = partblock, bgMsk = terra::rast(envs), rms = c(1:2), #' rmsStep = 1, fcs = c('L', 'LQ'), clampSel = TRUE, algMaxent = "maxnet", parallel = FALSE) #' envsFut <- list.files(path = system.file('extdata/wc/future', #' package = "wallace"), diff --git a/inst/shiny/modules/part_spat.Rmd b/inst/shiny/modules/part_spat.Rmd index a6f37f0d..b8c8963b 100644 --- a/inst/shiny/modules/part_spat.Rmd +++ b/inst/shiny/modules/part_spat.Rmd @@ -22,6 +22,6 @@ groups_{{spAbr}} <- part_partitionOccs( occs = occs_{{spAbr}} , bg = bgSample_{{spAbr}}, method = "{{method_code_rmd}}", - bgMask = bgMask_{{spAbr}}, + bgMask = raster::stack(bgMask_{{spAbr}}), aggFact = {{aggFact_rmd}}) ``` diff --git a/inst/shiny/modules/penvs_bgExtent.R b/inst/shiny/modules/penvs_bgExtent.R index e27d0139..7e92341e 100644 --- a/inst/shiny/modules/penvs_bgExtent.R +++ b/inst/shiny/modules/penvs_bgExtent.R @@ -128,7 +128,8 @@ penvs_bgExtent_module_server <- function(input, output, session, common) { bgMask <- penvs_bgMask(spp[[sp]]$occs, envs.global[[spp[[sp]]$envs]], spp[[sp]]$procEnvs$bgExt, logger, spN = sp) req(bgMask) - bgNonNA <- raster::ncell(bgMask) - raster::freq(bgMask, value = NA)[[1]] + bgMaskR <- raster::stack(bgMask) + bgNonNA <- raster::ncell(bgMaskR) - raster::freq(bgMaskR, value = NA)[[1]] if ((bgNonNA + 1) < input$bgPtsNum) { logger %>% writeLog( @@ -138,15 +139,15 @@ penvs_bgExtent_module_server <- function(input, output, session, common) { "(n = ", bgNonNA, "). Please reduce the number of requested points.") return() } - bgPts <- penvs_bgSample(spp[[sp]]$occs, bgMask, input$bgPtsNum, logger, + bgPts <- penvs_bgSample(spp[[sp]]$occs, bgMaskR, input$bgPtsNum, logger, spN = sp) req(bgPts) withProgress( message = paste0("Extracting background values for ", spName(sp), "..."), { - bgEnvsVals <- as.data.frame(raster::extract(bgMask, bgPts)) + bgEnvsVals <- as.data.frame(terra::extract(bgMask, bgPts)) }) - NApoints <- sum(rowSums(is.na(raster::extract(bgMask, spp[[sp]]$occs[ , c("longitude", "latitude")])))) + NApoints <- sum(rowSums(is.na(terra::extract(bgMask, spp[[sp]]$occs[ , c("longitude", "latitude")])))) if (NApoints > 0) { logger %>% writeLog(type = "error", hlSpp(sp), diff --git a/inst/shiny/modules/penvs_bgExtent.Rmd b/inst/shiny/modules/penvs_bgExtent.Rmd index 6e025c15..81ed2e86 100644 --- a/inst/shiny/modules/penvs_bgExtent.Rmd +++ b/inst/shiny/modules/penvs_bgExtent.Rmd @@ -17,10 +17,10 @@ bgMask_{{spAbr}} <- penvs_bgMask( # Sample background points from the provided area bgSample_{{spAbr}} <- penvs_bgSample( occs = occs_{{spAbr}}, - bgMask = bgMask_{{spAbr}}, + bgMask = raster::stack(bgMask_{{spAbr}}), bgPtsNum = {{bgPtsNum_rmd}}) # Extract values of environmental layers for each background point -bgEnvsVals_{{spAbr}} <- as.data.frame(raster::extract(bgMask_{{spAbr}}, bgSample_{{spAbr}})) +bgEnvsVals_{{spAbr}} <- as.data.frame(terra::extract(bgMask_{{spAbr}}, bgSample_{{spAbr}})) ##Add extracted values to background points table bgEnvsVals_{{spAbr}} <- cbind(scientific_name = paste0("bg_", "{{spName}}"), bgSample_{{spAbr}}, occID = NA, year = NA, institution_code = NA, country = NA, diff --git a/inst/shiny/modules/penvs_drawBgExtent.R b/inst/shiny/modules/penvs_drawBgExtent.R index 364bffe8..06f066eb 100644 --- a/inst/shiny/modules/penvs_drawBgExtent.R +++ b/inst/shiny/modules/penvs_drawBgExtent.R @@ -127,7 +127,8 @@ penvs_drawBgExtent_module_server <- function(input, output, session, common) { logger, spN = sp) req(bgMask) - bgNonNA <- raster::ncell(bgMask) - raster::freq(bgMask, value = NA)[[1]] + bgMaskR <- raster::stack(bgMask) + bgNonNA <- raster::ncell(bgMaskR) - raster::freq(bgMaskR, value = NA)[[1]] if ((bgNonNA + 1) < input$bgPtsNum) { logger %>% writeLog( @@ -138,17 +139,17 @@ penvs_drawBgExtent_module_server <- function(input, output, session, common) { return() } bgPts <- penvs_bgSample(spp[[sp]]$occs, - bgMask, + bgMaskR, input$bgPtsNum, logger, spN = sp) req(bgPts) withProgress(message = paste0("Extracting background values for ", spName(sp), "..."), { - bgEnvsVals <- as.data.frame(raster::extract(bgMask, bgPts)) + bgEnvsVals <- as.data.frame(terra::extract(bgMask, bgPts)) }) - if (sum(rowSums(is.na(raster::extract(bgMask, spp[[sp]]$occs[ , c("longitude", "latitude")])))) > 0) { + if (sum(rowSums(is.na(terra::extract(bgMask, spp[[sp]]$occs[ , c("longitude", "latitude")])))) > 0) { logger %>% writeLog(type = "error", hlSpp(sp), "One or more occurrence points have NULL raster values.", diff --git a/inst/shiny/modules/penvs_drawBgExtent.Rmd b/inst/shiny/modules/penvs_drawBgExtent.Rmd index 514256ee..dae36fe2 100644 --- a/inst/shiny/modules/penvs_drawBgExtent.Rmd +++ b/inst/shiny/modules/penvs_drawBgExtent.Rmd @@ -18,10 +18,10 @@ bgMask_{{spAbr}} <- penvs_bgMask( # Sample background points from the provided area bgSample_{{spAbr}} <- penvs_bgSample( occs = occs_{{spAbr}}, - bgMask = bgMask_{{spAbr}}, + bgMask = raster::stack(bgMask_{{spAbr}}), bgPtsNum = {{bgPtsNum_rmd}}) # Extract values of environmental layers for each background point -bgEnvsVals_{{spAbr}} <- as.data.frame(raster::extract(bgMask_{{spAbr}}, bgSample_{{spAbr}})) +bgEnvsVals_{{spAbr}} <- as.data.frame(terra::extract(bgMask_{{spAbr}}, bgSample_{{spAbr}})) ##Add extracted values to background points table bgEnvsVals_{{spAbr}} <- cbind(scientific_name = paste0("bg_", "{{spName}}"), bgSample_{{spAbr}}, occID = NA, year = NA, institution_code = NA, country = NA, diff --git a/inst/shiny/modules/penvs_userBgExtent.R b/inst/shiny/modules/penvs_userBgExtent.R index bb9e4f8a..b0ea984d 100644 --- a/inst/shiny/modules/penvs_userBgExtent.R +++ b/inst/shiny/modules/penvs_userBgExtent.R @@ -148,7 +148,8 @@ penvs_userBgExtent_module_server <- function(input, output, session, common) { logger, spN = sp) req(bgMask) - bgNonNA <- raster::ncell(bgMask) - raster::freq(bgMask, value = NA)[[1]] + bgMaskR <- raster::stack(bgMask) + bgNonNA <- raster::ncell(bgMaskR) - raster::freq(bgMaskR, value = NA)[[1]] if ((bgNonNA + 1) < input$bgPtsNum) { logger %>% writeLog( @@ -159,17 +160,17 @@ penvs_userBgExtent_module_server <- function(input, output, session, common) { return() } bgPts <- penvs_bgSample(spp[[sp]]$occs, - bgMask, + bgMaskR, input$bgPtsNum, logger, spN = sp) req(bgPts) withProgress(message = paste0("Extracting background values for ", spName(sp), "..."), { - bgEnvsVals <- as.data.frame(raster::extract(bgMask, bgPts)) + bgEnvsVals <- as.data.frame(terra::extract(bgMask, bgPts)) }) - if (sum(rowSums(is.na(raster::extract(bgMask, spp[[sp]]$occs[ , c("longitude", "latitude")])))) > 0) { + if (sum(rowSums(is.na(terra::extract(bgMask, spp[[sp]]$occs[ , c("longitude", "latitude")])))) > 0) { logger %>% writeLog(type = "error", hlSpp(sp), "One or more occurrence points have NULL raster values.", diff --git a/inst/shiny/modules/penvs_userBgExtent.Rmd b/inst/shiny/modules/penvs_userBgExtent.Rmd index ff6d1b66..a2c2c386 100644 --- a/inst/shiny/modules/penvs_userBgExtent.Rmd +++ b/inst/shiny/modules/penvs_userBgExtent.Rmd @@ -21,10 +21,10 @@ bgMask_{{spAbr}} <- penvs_bgMask( # Sample background points from the provided area bgSample_{{spAbr}} <- penvs_bgSample( occs = occs_{{spAbr}}, - bgMask = bgMask_{{spAbr}}, + bgMask = raster::stack(bgMask_{{spAbr}}), bgPtsNum = {{bgPtsNum_rmd}}) # Extract values of environmental layers for each background point -bgEnvsVals_{{spAbr}} <- as.data.frame(raster::extract(bgMask_{{spAbr}}, bgSample_{{spAbr}})) +bgEnvsVals_{{spAbr}} <- as.data.frame(terra::extract(bgMask_{{spAbr}}, bgSample_{{spAbr}})) ##Add extracted values to background points table bgEnvsVals_{{spAbr}} <- cbind(scientific_name = paste0("bg_", "{{spName}}"), bgSample_{{spAbr}}, occID = NA, year = NA, institution_code = NA, country = NA, diff --git a/inst/shiny/modules/vis_mapPreds.R b/inst/shiny/modules/vis_mapPreds.R index 99e379e7..60db3006 100644 --- a/inst/shiny/modules/vis_mapPreds.R +++ b/inst/shiny/modules/vis_mapPreds.R @@ -87,7 +87,7 @@ vis_mapPreds_module_server <- function(input, output, session, common) { # pick the prediction that matches the model selected predSel <- evalOut()@predictions[[curModel()]] predSel <- raster::raster(predSel) - raster::crs(predSel) <- raster::crs(bgMask()) + raster::crs(predSel) <- raster::crs(raster::stack(bgMask())) if(is.na(raster::crs(predSel))) { logger %>% writeLog( type = "error", @@ -103,10 +103,10 @@ vis_mapPreds_module_server <- function(input, output, session, common) { if (spp[[curSp()]]$rmm$model$algorithms == "BIOCLIM") { predType <- "BIOCLIM" m <- evalOut()@models[[curModel()]] - predSel <- dismo::predict(m, terra::rast(bgMask())) + predSel <- dismo::predict(m, bgMask()) predSel <- raster::raster(predSel) # define crs - raster::crs(predSel) <- raster::crs(bgMask()) + raster::crs(predSel) <- raster::crs(raster::raster(bgMask())) # define predSel name names(predSel) <- curModel() } else if (spp[[curSp()]]$rmm$model$algorithms %in% c("maxent.jar", "maxnet")) { @@ -125,7 +125,7 @@ vis_mapPreds_module_server <- function(input, output, session, common) { clamping <- spp[[curSp()]]$rmm$model$algorithm$maxent$clamping if (spp[[curSp()]]$rmm$model$algorithms == "maxnet") { if (predType == "raw") predType <- "exponential" - predSel <- predictMaxnet(m, bgMask(), + predSel <- predictMaxnet(m, raster::stack(bgMask()), type = predType, clamp = FALSE) } else if (spp[[curSp()]]$rmm$model$algorithms == "maxent.jar") { @@ -135,7 +135,7 @@ vis_mapPreds_module_server <- function(input, output, session, common) { } else { doClamp <- "doclamp=false" } - predSel <- dismo::predict(m, terra::rast(bgMask()), + predSel <- dismo::predict(m, bgMask(), args = c(outputFormat, doClamp), #na.rm = TRUE ) @@ -143,7 +143,7 @@ vis_mapPreds_module_server <- function(input, output, session, common) { } }) # define crs - raster::crs(predSel) <- raster::crs(bgMask()) + raster::crs(predSel) <- raster::crs(raster::stack(bgMask())) # define predSel name names(predSel) <- curModel() diff --git a/inst/shiny/modules/vis_mapPreds.Rmd b/inst/shiny/modules/vis_mapPreds.Rmd index 5f67b862..beacf66f 100644 --- a/inst/shiny/modules/vis_mapPreds.Rmd +++ b/inst/shiny/modules/vis_mapPreds.Rmd @@ -7,7 +7,7 @@ Generate a map of the Bioclim generated model with no threshold # Select current model and obtain raster prediction m_{{spAbr}} <- model_{{spAbr}}@models[["{{curModel_rmd}}"]] -predSel_{{spAbr}} <- dismo::predict(m_{{spAbr}}, terra::rast(bgMask_{{spAbr}})) +predSel_{{spAbr}} <- dismo::predict(m_{{spAbr}}, bgMask_{{spAbr}}) predSel_{{spAbr}} <- raster::raster(predSel_{{spAbr}}) #Get values of prediction mapPredVals_{{spAbr}} <- getRasterVals(predSel_{{spAbr}}, "{{predType_rmd}}") @@ -43,7 +43,7 @@ Generate a map of the Bioclim generated model with a "{{thresholdRule_rmd}}" thr ```{r, echo = {{vis_mapPreds_knit & vis_map_bioclim_knit & vis_map_threshold_knit}}, include = {{vis_mapPreds_knit & vis_map_bioclim_knit & vis_map_threshold_knit}}} # Select current model and obtain raster prediction m_{{spAbr}} <- model_{{spAbr}}@models[["{{curModel_rmd}}"]] -predSel_{{spAbr}} <- dismo::predict(m_{{spAbr}}, terra::rast(bgMask_{{spAbr}})) +predSel_{{spAbr}} <- dismo::predict(m_{{spAbr}}, bgMask_{{spAbr}}) predSel_{{spAbr}} <- raster::raster(predSel_{{spAbr}}) # extract the suitability values for all occurrences occs_xy_{{spAbr}} <- occs_{{spAbr}}[c('longitude', 'latitude')] @@ -93,7 +93,7 @@ Generate a map of the Maxent generated model with no threshold # Select current model and obtain raster prediction m_{{spAbr}} <- model_{{spAbr}}@models[["{{curModel_rmd}}"]] predSel_{{spAbr}} <- dismo::predict( - m_{{spAbr}}, terra::rast(bgMask_{{spAbr}}), + m_{{spAbr}}, bgMask_{{spAbr}}, args = c(paste0("outputformat=", "{{predType_rmd}}"), paste0("doclamp=", tolower(as.character({{clamp_rmd}}))))) predSel_{{spAbr}} <- raster::raster(predSel_{{spAbr}}) @@ -132,7 +132,7 @@ Generate a map of the Maxent generated model with a "{{thresholdRule_rmd}}" thre # Select current model and obtain raster prediction m_{{spAbr}} <- model_{{spAbr}}@models[["{{curModel_rmd}}"]] predSel_{{spAbr}} <- dismo::predict( - m_{{spAbr}}, terra::rast(bgMask_{{spAbr}}), + m_{{spAbr}}, bgMask_{{spAbr}}, args = c(paste0("outputformat=", "{{predType_rmd}}"), paste0("doclamp=", tolower(as.character({{clamp_rmd}}))))) predSel_{{spAbr}} <- raster::raster(predSel_{{spAbr}}) @@ -181,7 +181,7 @@ Generate a map of the maxnet generated model with no threshold ```{r, echo = {{vis_mapPreds_knit & vis_map_maxnet_knit & !vis_map_threshold_knit}}, include = {{vis_mapPreds_knit & vis_map_maxnet_knit & !vis_map_threshold_knit}}} # Select current model and obtain raster prediction m_{{spAbr}} <- model_{{spAbr}}@models[["{{curModel_rmd}}"]] -predSel_{{spAbr}} <- predictMaxnet(m_{{spAbr}}, bgMask_{{spAbr}}, +predSel_{{spAbr}} <- predictMaxnet(m_{{spAbr}}, raster(bgMask_{{spAbr}}), type = "{{predType_rmd}}", clamp = {{clamp_rmd}}) #Get values of prediction @@ -218,7 +218,7 @@ Generate a map of the maxnet generated model with with a "{{thresholdRule_rmd}}" ```{r, echo = {{vis_mapPreds_knit & vis_map_maxnet_knit & vis_map_threshold_knit}}, include = {{vis_mapPreds_knit & vis_map_maxnet_knit & vis_map_threshold_knit}}} # Select current model and obtain raster prediction m_{{spAbr}} <- model_{{spAbr}}@models[["{{curModel_rmd}}"]] -predSel_{{spAbr}} <- predictMaxnet(m_{{spAbr}}, bgMask_{{spAbr}}, +predSel_{{spAbr}} <- predictMaxnet(m_{{spAbr}}, raster(bgMask_{{spAbr}}), type = "{{predType_rmd}}", clamp = {{clamp_rmd}}) # extract the suitability values for all occurrences diff --git a/inst/shiny/modules/xfer_mess.R b/inst/shiny/modules/xfer_mess.R index 4119c057..7cbee814 100644 --- a/inst/shiny/modules/xfer_mess.R +++ b/inst/shiny/modules/xfer_mess.R @@ -58,10 +58,10 @@ xfer_mess_module_server <- function(input, output, session, common) { # FUNCTION CALL #### xferYr <- spp[[curSp()]]$rmm$data$transfer$environment1$yearMax if (spp[[curSp()]]$rmm$model$algorithms == "BIOCLIM") { - mss <- xfer_mess(occs(), bg = NULL, bgMask(), spp[[curSp()]]$transfer$xfEnvs, + mss <- xfer_mess(occs(), bg = NULL, raster::stack(bgMask()), spp[[curSp()]]$transfer$xfEnvs, logger, spN = curSp()) } else { - mss <- xfer_mess(occs(), bg(), bgMask(), spp[[curSp()]]$transfer$xfEnvs, + mss <- xfer_mess(occs(), bg(), raster::stack(bgMask()), spp[[curSp()]]$transfer$xfEnvs, logger, spN = curSp()) } diff --git a/inst/shiny/modules/xfer_mess.Rmd b/inst/shiny/modules/xfer_mess.Rmd index 0630c96f..17217713 100644 --- a/inst/shiny/modules/xfer_mess.Rmd +++ b/inst/shiny/modules/xfer_mess.Rmd @@ -7,7 +7,7 @@ Generate a MESS map for the transferring variables given the variables used for xferMess_{{spAbr}} <- xfer_mess( occs = occs_{{spAbr}}, bg = bgEnvsVals_{{spAbr}} , - bgMsk = bgMask_{{spAbr}}, + bgMsk = raster::stack(bgMask_{{spAbr}}), xferExtRas = xferExt_{{spAbr}}) # Generate MESS map diff --git a/inst/shiny/modules/xfer_time.Rmd b/inst/shiny/modules/xfer_time.Rmd index e5b6ab3d..31041ebe 100644 --- a/inst/shiny/modules/xfer_time.Rmd +++ b/inst/shiny/modules/xfer_time.Rmd @@ -4,18 +4,19 @@ Transferring the model to the same modelling area with no threshold rule. New ti ``` ```{r, echo = {{xfer_time_knit & !xfer_time_user_knit & !xfer_time_drawn_knit & !xfer_time_threshold_knit & xfer_time_worldclim_knit}}, include = {{xfer_time_knit & !xfer_time_user_knit & !xfer_time_drawn_knit & !xfer_time_threshold_knit & xfer_time_worldclim_knit}}} -#Download variables for transferring +#Download variables for transferring +bgMaskR_{{spAbr}} <- raster::stack(bgMask_{{spAbr}}) xferTimeEnvs_{{spAbr}} <- geodata::cmip6_world( model = "{{model_rmd}}", ssp = "{{rcp_rmd}}", time = "{{year_rmd}}", var = "bio", - res = round((raster::res(bgMask_{{spAbr}}) * 60)[1],1), + res = round((raster::res(bgMaskR_{{spAbr}}) * 60)[1],1), path = tempdir()) names(xferTimeEnvs_{{spAbr}}) <- paste0('bio', c(paste0('0',1:9), 10:19)) # Select variables for transferring to match variables used for modelling -xferTimeEnvs_{{spAbr}} <- xferTimeEnvs_{{spAbr}}[[names(bgMask_{{spAbr}})]] +xferTimeEnvs_{{spAbr}} <- xferTimeEnvs_{{spAbr}}[[names(bgMaskR_{{spAbr}})]] # Convert to rasterstack xferTimeEnvs_{{spAbr}} <- raster::stack(xferTimeEnvs_{{spAbr}}) @@ -64,18 +65,19 @@ Transferring the model to the same modelling area with a "{{xfer_thresholdRule_r ``` ```{r, echo = {{xfer_time_knit & !xfer_time_user_knit & !xfer_time_drawn_knit & xfer_time_threshold_knit & xfer_time_worldclim_knit}}, include = {{xfer_time_knit & !xfer_time_user_knit & !xfer_time_drawn_knit & xfer_time_threshold_knit & xfer_time_worldclim_knit}}} -#Download variables for transferring +#Download variables for transferring +bgMaskR_{{spAbr}} <- raster::stack(bgMask_{{spAbr}}) xferTimeEnvs_{{spAbr}} <- geodata::cmip6_world( model = "{{model_rmd}}", ssp = "{{rcp_rmd}}", time = "{{year_rmd}}", var = "bio", - res = round((raster::res(bgMask_{{spAbr}}) * 60)[1],1), + res = round((raster::res(bgMaskR_{{spAbr}}) * 60)[1],1), path = tempdir()) names(xferTimeEnvs_{{spAbr}}) <- paste0('bio', c(paste0('0',1:9), 10:19)) # Select variables for transferring to match variables used for modelling -xferTimeEnvs_{{spAbr}} <- xferTimeEnvs_{{spAbr}}[[names(bgMask_{{spAbr}})]] +xferTimeEnvs_{{spAbr}} <- xferTimeEnvs_{{spAbr}}[[names(bgMaskR_{{spAbr}})]] # Convert to rasterstack xferTimeEnvs_{{spAbr}} <- raster::stack(xferTimeEnvs_{{spAbr}}) @@ -134,18 +136,19 @@ Transferring the model to a user drawn area with no threshold. New time based on ``` ```{r, echo = {{xfer_time_knit & !xfer_time_user_knit & xfer_time_drawn_knit & !xfer_time_threshold_knit & xfer_time_worldclim_knit}}, include = {{xfer_time_knit & !xfer_time_user_knit & xfer_time_drawn_knit & !xfer_time_threshold_knit & xfer_time_worldclim_knit}}} -#Download variables for transferring +#Download variables for transferring +bgMaskR_{{spAbr}} <- raster::stack(bgMask_{{spAbr}}) xferTimeEnvs_{{spAbr}} <- geodata::cmip6_world( model = "{{model_rmd}}", ssp = "{{rcp_rmd}}", time = "{{year_rmd}}", var = "bio", - res = round((raster::res(bgMask_{{spAbr}}) * 60)[1],1), + res = round((raster::res(bgMaskR_{{spAbr}}) * 60)[1],1), path = tempdir()) names(xferTimeEnvs_{{spAbr}}) <- paste0('bio', c(paste0('0',1:9), 10:19)) # Select variables for transferring to match variables used for modelling -xferTimeEnvs_{{spAbr}} <- xferTimeEnvs_{{spAbr}}[[names(bgMask_{{spAbr}})]] +xferTimeEnvs_{{spAbr}} <- xferTimeEnvs_{{spAbr}}[[names(bgMaskR_{{spAbr}})]] # Convert to rasterstack xferTimeEnvs_{{spAbr}} <- raster::stack(xferTimeEnvs_{{spAbr}}) @@ -201,18 +204,19 @@ Transferring the model to a user drawn area with a "{{xfer_thresholdRule_rmd}}" ``` ```{r, echo = {{xfer_time_knit & !xfer_time_user_knit & xfer_time_drawn_knit & xfer_time_threshold_knit & xfer_time_worldclim_knit}}, include = {{xfer_time_knit & !xfer_time_user_knit & xfer_time_drawn_knit & xfer_time_threshold_knit & xfer_time_worldclim_knit}}} -#Download variables for transferring +#Download variables for transferring +bgMaskR_{{spAbr}} <- raster::stack(bgMask_{{spAbr}}) xferTimeEnvs_{{spAbr}} <- geodata::cmip6_world( model = "{{model_rmd}}", ssp = "{{rcp_rmd}}", time = "{{year_rmd}}", var = "bio", - res = round((raster::res(bgMask_{{spAbr}}) * 60)[1],1), + res = round((raster::res(bgMaskR_{{spAbr}}) * 60)[1],1), path = tempdir()) names(xferTimeEnvs_{{spAbr}}) <- paste0('bio', c(paste0('0',1:9), 10:19)) # Select variables for transferring to match variables used for modelling -xferTimeEnvs_{{spAbr}} <- xferTimeEnvs_{{spAbr}}[[names(bgMask_{{spAbr}})]] +xferTimeEnvs_{{spAbr}} <- xferTimeEnvs_{{spAbr}}[[names(bgMaskR_{{spAbr}})]] # Convert to rasterstack xferTimeEnvs_{{spAbr}} <- raster::stack(xferTimeEnvs_{{spAbr}}) @@ -277,18 +281,19 @@ Transferring the model to a user provided area area with no threshold. New time ```{r, echo = {{xfer_time_knit & xfer_time_user_knit & !xfer_time_drawn_knit & !xfer_time_threshold_knit & xfer_time_worldclim_knit}}, include = {{xfer_time_knit & xfer_time_user_knit & !xfer_time_drawn_knit & !xfer_time_threshold_knit & xfer_time_worldclim_knit}}} -# Download variables for transferring +# Download variables for transferring +bgMaskR_{{spAbr}} <- raster::stack(bgMask_{{spAbr}}) xferTimeEnvs_{{spAbr}} <- geodata::cmip6_world( model = "{{model_rmd}}", ssp = "{{rcp_rmd}}", time = "{{year_rmd}}", var = "bio", - res = round((raster::res(bgMask_{{spAbr}}) * 60)[1],1), + res = round((raster::res(bgMaskR_{{spAbr}}) * 60)[1],1), path = tempdir()) names(xferTimeEnvs_{{spAbr}}) <- paste0('bio', c(paste0('0',1:9), 10:19)) # Select variables for transferring to match variables used for modelling -xferTimeEnvs_{{spAbr}} <- xferTimeEnvs_{{spAbr}}[[names(bgMask_{{spAbr}})]] +xferTimeEnvs_{{spAbr}} <- xferTimeEnvs_{{spAbr}}[[names(bgMaskR_{{spAbr}})]] # Convert to rasterstack xferTimeEnvs_{{spAbr}} <- raster::stack(xferTimeEnvs_{{spAbr}}) @@ -347,17 +352,18 @@ Transferring the model to a user provided area with a "{{xfer_thresholdRule_rmd} ```{r, echo = {{xfer_time_knit & xfer_time_user_knit & !xfer_time_drawn_knit & xfer_time_threshold_knit & xfer_time_worldclim_knit}}, include = {{xfer_time_knit & xfer_time_user_knit & !xfer_time_drawn_knit & xfer_time_threshold_knit & xfer_time_worldclim_knit}}} # Download variables for transferring from Worldclim +bgMaskR_{{spAbr}} <- raster::stack(bgMask_{{spAbr}}) xferTimeEnvs_{{spAbr}} <- geodata::cmip6_world( model = "{{model_rmd}}", ssp = "{{rcp_rmd}}", time = "{{year_rmd}}", var = "bio", - res = round((raster::res(bgMask_{{spAbr}}) * 60)[1],1), + res = round((raster::res(bgMaskR_{{spAbr}}) * 60)[1],1), path = tempdir()) names(xferTimeEnvs_{{spAbr}}) <- paste0('bio', c(paste0('0',1:9), 10:19)) # Select variables for transferring to match variables used for modelling -xferTimeEnvs_{{spAbr}} <- xferTimeEnvs_{{spAbr}}[[names(bgMask_{{spAbr}})]] +xferTimeEnvs_{{spAbr}} <- xferTimeEnvs_{{spAbr}}[[names(bgMaskR_{{spAbr}})]] # Convert to rasterstack xferTimeEnvs_{{spAbr}} <- raster::stack(xferTimeEnvs_{{spAbr}}) @@ -425,10 +431,11 @@ Transferring the model to the same modelling area with no threshold rule. New ti ```{r, echo = {{xfer_time_knit & !xfer_time_user_knit & !xfer_time_drawn_knit & !xfer_time_threshold_knit & !xfer_time_worldclim_knit}}, include = {{xfer_time_knit & !xfer_time_user_knit & !xfer_time_drawn_knit & !xfer_time_threshold_knit & !xfer_time_worldclim_knit}}} #Download data from ecoClimate for transferring +bgMaskR_{{spAbr}} <- raster::stack(bgMask_{{spAbr}}) xferTimeEnvs_{{spAbr}} <- envs_ecoClimate( "{{xfAOGCM_rmd}}", "{{xfScenario_rmd}}", - as.numeric(gsub("bio", "", names(bgMask_{{spAbr}})))) + as.numeric(gsub("bio", "", names(bgMaskR_{{spAbr}})))) # Generate a transfer of the model to the desired area and time xfer_time_{{spAbr}} <-xfer_time( @@ -477,10 +484,11 @@ Transferring the model to the same modelling area with a "{{xfer_thresholdRule_r ```{r, echo = {{xfer_time_knit & !xfer_time_user_knit & !xfer_time_drawn_knit & xfer_time_threshold_knit & !xfer_time_worldclim_knit}}, include = {{xfer_time_knit & !xfer_time_user_knit & !xfer_time_drawn_knit & xfer_time_threshold_knit & !xfer_time_worldclim_knit}}} #Download data from ecoClimate for transferring +bgMaskR_{{spAbr}} <- raster::stack(bgMask_{{spAbr}}) xferTimeEnvs_{{spAbr}} <- envs_ecoClimate( "{{xfAOGCM_rmd}}", "{{xfScenario_rmd}}", - as.numeric(gsub("bio", "", names(bgMask_{{spAbr}})))) + as.numeric(gsub("bio", "", names(bgMaskR_{{spAbr}})))) # Generate a transfer of the model to the desired area and time xfer_time_{{spAbr}} <-xfer_time( @@ -539,10 +547,11 @@ Transferring the model to a user drawn area with no threshold rule. New time bas ```{r, echo = {{xfer_time_knit & !xfer_time_user_knit & xfer_time_drawn_knit & !xfer_time_threshold_knit & !xfer_time_worldclim_knit}}, include = {{xfer_time_knit & !xfer_time_user_knit & xfer_time_drawn_knit & !xfer_time_threshold_knit & !xfer_time_worldclim_knit}}} #Download data from ecoClimate for transferring +bgMaskR_{{spAbr}} <- raster::stack(bgMask_{{spAbr}}) xferTimeEnvs_{{spAbr}} <- envs_ecoClimate( "{{xfAOGCM_rmd}}", "{{xfScenario_rmd}}", - as.numeric(gsub("bio", "", names(bgMask_{{spAbr}})))) + as.numeric(gsub("bio", "", names(bgMaskR_{{spAbr}})))) # Generate the area of transfer according to the drawn polygon in the GUI xfer_draw_{{spAbr}} <-xfer_draw( @@ -597,10 +606,11 @@ Transferring the model to a user drawn area with a "{{xfer_thresholdRule_rmd}}" ```{r, echo = {{xfer_time_knit & !xfer_time_user_knit & xfer_time_drawn_knit & xfer_time_threshold_knit & !xfer_time_worldclim_knit}}, include = {{xfer_time_knit & !xfer_time_user_knit & xfer_time_drawn_knit & xfer_time_threshold_knit & !xfer_time_worldclim_knit}}} #Download data from ecoClimate for transferring +bgMaskR_{{spAbr}} <- raster::stack(bgMask_{{spAbr}}) xferTimeEnvs_{{spAbr}} <- envs_ecoClimate( "{{xfAOGCM_rmd}}", "{{xfScenario_rmd}}", - as.numeric(gsub("bio", "", names(bgMask_{{spAbr}})))) + as.numeric(gsub("bio", "", names(bgMaskR_{{spAbr}})))) # Generate the area of transfer according to the drawn polygon in the GUI xfer_draw_{{spAbr}} <-xfer_draw( @@ -665,10 +675,11 @@ Transferring the model to a user provided area with no threshold rule. New time ```{r, echo = {{xfer_time_knit & xfer_time_user_knit & !xfer_time_drawn_knit & !xfer_time_threshold_knit & !xfer_time_worldclim_knit}}, include = {{xfer_time_knit & xfer_time_user_knit & !xfer_time_drawn_knit & !xfer_time_threshold_knit & !xfer_time_worldclim_knit}}} #Download data from ecoClimate for transferring +bgMaskR_{{spAbr}} <- raster::stack(bgMask_{{spAbr}}) xferTimeEnvs_{{spAbr}} <- envs_ecoClimate( "{{xfAOGCM_rmd}}", "{{xfScenario_rmd}}", - as.numeric(gsub("bio", "", names(bgMask_{{spAbr}})))) + as.numeric(gsub("bio", "", names(bgMaskR_{{spAbr}})))) # Generate the area of transfer based on user provided files ##User must input the path to shapefile or csv file and the file name @@ -726,10 +737,11 @@ Transferring the model to a user provided area with a "{{xfer_thresholdRule_rmd} ```{r, echo = {{xfer_time_knit & xfer_time_user_knit & !xfer_time_drawn_knit & xfer_time_threshold_knit & !xfer_time_worldclim_knit}}, include = {{xfer_time_knit & xfer_time_user_knit & !xfer_time_drawn_knit & xfer_time_threshold_knit & !xfer_time_worldclim_knit}}} #Download data from ecoClimate for transferring +bgMaskR_{{spAbr}} <- raster::stack(bgMask_{{spAbr}}) xferTimeEnvs_{{spAbr}} <- envs_ecoClimate( "{{xfAOGCM_rmd}}", "{{xfScenario_rmd}}", - as.numeric(gsub("bio", "", names(bgMask_{{spAbr}})))) + as.numeric(gsub("bio", "", names(bgMaskR_{{spAbr}})))) # Generate the area of transfer based on user provided files ##User must input the path to shapefile or csv file and the file name diff --git a/inst/shiny/server.R b/inst/shiny/server.R index b274e2ac..f849d35d 100644 --- a/inst/shiny/server.R +++ b/inst/shiny/server.R @@ -444,7 +444,7 @@ function(input, output, session) { type <- input$bgMskFileType nm <- names(envs()) - raster::writeRaster(bgMask(), nm, bylayer = TRUE, + raster::writeRaster(raster::stack(bgMask()), nm, bylayer = TRUE, format = type, overwrite = TRUE) ext <- switch(type, raster = 'grd', ascii = 'asc', GTiff = 'tif') @@ -801,7 +801,7 @@ function(input, output, session) { logger %>% writeLog(type = "error", "To download PNG prediction, you're required to", " install PhantomJS in your machine. You can use webshot::install_phantomjs()", - " in you are R console.") + " in your R console.") return() } if (!requireNamespace("mapview")) { @@ -954,7 +954,7 @@ function(input, output, session) { logger %>% writeLog(type = "error", "To download PNG prediction, you're required to", " install PhantomJS in your machine. You can use webshot::install_phantomjs()", - " in you are R console.") + " in your R console.") return() } if (!requireNamespace("mapview")) { @@ -1044,7 +1044,7 @@ function(input, output, session) { logger %>% writeLog(type = "error", "To download PNG prediction, you're required to", " install PhantomJS in your machine. You can use webshot::install_phantomjs()", - " in you are R console.") + " in your R console.") return() } if (!requireNamespace("mapview")) { diff --git a/tests/testthat/test_model_bioclim.R b/tests/testthat/test_model_bioclim.R index e1d546e5..a5b1ba09 100644 --- a/tests/testthat/test_model_bioclim.R +++ b/tests/testthat/test_model_bioclim.R @@ -14,7 +14,7 @@ occs <- read.csv(system.file("extdata/Bassaricyon_alleni.csv", bg <- read.csv(system.file("extdata/Bassaricyon_alleni_bgPoints.csv", package = "wallace")) partblock <- part_partitionOccs(occs, bg, method = 'block') -bioclimAlg <- model_bioclim(occs, bg, partblock, envs) +bioclimAlg <- model_bioclim(occs, bg, partblock, terra::rast(envs)) ### test output features test_that("output type checks", { diff --git a/tests/testthat/test_model_maxent.R b/tests/testthat/test_model_maxent.R index e9be364e..a5d22d1a 100644 --- a/tests/testthat/test_model_maxent.R +++ b/tests/testthat/test_model_maxent.R @@ -32,7 +32,7 @@ jar_f <- paste(system.file(package = "dismo"), "/maxent.jar", sep = '') ## test if the error messages appear when they are supposed to test_that("error checks", { # user has not partitioned occurrences - expect_error(model_maxent(occs, bg, bgMsk = envs, user.grp = NULL, + expect_error(model_maxent(occs, bg, bgMsk = terra::rest(envs), user.grp = NULL, rms, rmsStep, fcs, clampSel = TRUE, algMaxent = algorithm[1]), paste0("Before building a model, please partition occurrences ", @@ -47,7 +47,7 @@ for (i in algorithm) { ### run function maxentAlg <- model_maxent(occs = occs, bg = bg, user.grp = partblock, - bgMsk = envs, rms, rmsStep, fcs, clampSel = TRUE, + bgMsk = terra::rast(envs), rms, rmsStep, fcs, clampSel = TRUE, algMaxent = i, parallel = FALSE) test_that("output type checks", { diff --git a/tests/testthat/test_penvs_bgMask.R b/tests/testthat/test_penvs_bgMask.R index 1a7d1300..61b02f4b 100644 --- a/tests/testthat/test_penvs_bgMask.R +++ b/tests/testthat/test_penvs_bgMask.R @@ -11,7 +11,9 @@ envs <- envs_userEnvs(rasPath = list.files(system.file("extdata/wc", rasName = list.files(system.file("extdata/wc", package = "wallace"), pattern = ".tif$", full.names = FALSE)) +crs(envs) <- "EPSG:4326" bgExt <- penvs_bgExtent(occs, bgSel = 'minimum convex polygon', bgBuf = 0.5) +crs(bgExt) <- "EPSG:4326" bgMask <- penvs_bgMask(occs, envs, bgExt) @@ -24,21 +26,21 @@ test_that("error checks", { ### test output features test_that("output type checks", { - # the output is a RasterBrick - expect_is(bgMask, "RasterBrick") + # the output is a SpatRaster + expect_is(bgMask, "SpatRaster") # the amount of masked layers are the same as uploaded in the comp. 3 - expect_equal(raster::nlayers(envs), raster::nlayers(bgMask)) + expect_equal(raster::nlayers(envs), nlyr(bgMask)) # the masked layers are the same as uploaded in the comp. 3 expect_equal(names(bgMask), names(envs)) # all the environmental layers have the same amount of pixels - expect_equal(raster::cellStats(bgMask, sum), raster::cellStats(bgMask, sum)) + expect_equal(terra::ncell(bgMask), raster::ncell(envs)) # the original layers have more pixels than the masked ones expect_true( - raster::cellStats(bgMask$bio05, sum) < raster::cellStats(envs$bio05, sum)) + terra::global(bgMask$bio05, "notNA") < raster::ncell(envs$bio05)) expect_true( - raster::cellStats(bgMask$bio06, sum) < raster::cellStats(envs$bio06, sum)) + terra::global(bgMask$bio06, "notNA") < raster::ncell(envs$bio06)) expect_true( - raster::cellStats(bgMask$bio13, sum) < raster::cellStats(envs$bio13, sum)) + terra::global(bgMask$bio13, "notNA") < raster::ncell(envs$bio13)) expect_true( - raster::cellStats(bgMask$bio14, sum) < raster::cellStats(envs$bio14, sum)) + terra::global(bgMask$bio14, "notNA") < raster::ncell(envs$bio14)) }) diff --git a/tests/testthat/test_penvs_bgSample.R b/tests/testthat/test_penvs_bgSample.R index 16b78db8..54e6def3 100644 --- a/tests/testthat/test_penvs_bgSample.R +++ b/tests/testthat/test_penvs_bgSample.R @@ -17,14 +17,15 @@ envs <- envs_userEnvs(rasPath = list.files(system.file("extdata/wc", bgExt <- penvs_bgExtent(occs, bgSel = 'bounding box', bgBuf = 0.5) # background masked bgMask <- penvs_bgMask(occs, envs, bgExt) +bgMaskR <- raster::stack(bgMask) ## Number of background points to sample bgPtsNum <- 100 bgPtsNum_big <- 19525 ### run function -bgsample <- penvs_bgSample(occs, bgMask, bgPtsNum) -bgsample_big <- penvs_bgSample(occs, bgMask, bgPtsNum_big) +bgsample <- penvs_bgSample(occs, bgMaskR, bgPtsNum) +bgsample_big <- penvs_bgSample(occs, bgMaskR, bgPtsNum_big) ### test output features test_that("output type checks", { @@ -41,7 +42,7 @@ test_that("output type checks", { # bgPtsNum is bigger than available expect_equal( nrow(bgsample_big), - (raster::ncell(bgMask) - raster::freq(bgMask, value = NA)[[1]])) + (raster::ncell(bgMaskR) - raster::freq(bgMaskR, value = NA)[[1]])) # check if all the points sampled overlap with the study region # set longitude and latitude diff --git a/tests/testthat/test_vis_bioclimPlot.R b/tests/testthat/test_vis_bioclimPlot.R index 148e4008..b754d3f7 100644 --- a/tests/testthat/test_vis_bioclimPlot.R +++ b/tests/testthat/test_vis_bioclimPlot.R @@ -16,7 +16,7 @@ occs <- read.csv(system.file("extdata/Bassaricyon_alleni.csv", bg <- read.csv(system.file("extdata/Bassaricyon_alleni_bgPoints.csv", package = "wallace")) partblock <- part_partitionOccs(occs, bg, method = 'block') -m <- model_bioclim(occs, bg, partblock, envs) +m <- model_bioclim(occs, bg, partblock, terra::rast(envs)) ### run function bioclimPlot <- vis_bioclimPlot(x = m@models$bioclim, a = 2, b = 3, diff --git a/tests/testthat/test_xfer_area.R b/tests/testthat/test_xfer_area.R index 71256bca..cc3fa437 100644 --- a/tests/testthat/test_xfer_area.R +++ b/tests/testthat/test_xfer_area.R @@ -27,7 +27,7 @@ bg <- read.csv(system.file("extdata/Bassaricyon_alleni_bgPoints.csv", package = "wallace")) partblock <- part_partitionOccs(occs, bg, method = 'block') m <- model_maxent(occs, bg, user.grp = partblock, - bgMsk = envs, rms = c(1:2), rmsStep = 1, fcs = c('L', 'LQ'), + bgMsk = terra::rast(envs), rms = c(1:2), rmsStep = 1, fcs = c('L', 'LQ'), clampSel = TRUE, algMaxent = "maxnet", parallel = FALSE) diff --git a/tests/testthat/test_xfer_time.R b/tests/testthat/test_xfer_time.R index 836ff6b2..0fdd8b32 100644 --- a/tests/testthat/test_xfer_time.R +++ b/tests/testthat/test_xfer_time.R @@ -23,7 +23,7 @@ bg <- read.csv(system.file("extdata/Bassaricyon_alleni_bgPoints.csv", package = "wallace")) partblock <- part_partitionOccs(occs, bg, method = 'block') m <- model_maxent(occs, bg, user.grp = partblock, - bgMsk = envs, rms = c(1:2), rmsStep = 1, fcs = c('L', 'LQ'), + bgMsk = terra::rast(envs), rms = c(1:2), rmsStep = 1, fcs = c('L', 'LQ'), clampSel = TRUE, algMaxent = "maxnet", parallel = FALSE) diff --git a/tests/testthat/test_xfer_userEnvs.R b/tests/testthat/test_xfer_userEnvs.R index 675b739a..03e864fb 100644 --- a/tests/testthat/test_xfer_userEnvs.R +++ b/tests/testthat/test_xfer_userEnvs.R @@ -23,7 +23,7 @@ envs <- envs_userEnvs(rasPath = list.files(system.file("extdata/wc", pattern = ".tif$", full.names = FALSE)) partblock <- part_partitionOccs(occs, bg, method = 'block') m <- model_maxent(occs, bg, user.grp = partblock, - bgMsk = envs, rms = c(1:2), rmsStep = 1, fcs = c('L', 'LQ'), + bgMsk = terra::rast(envs), rms = c(1:2), rmsStep = 1, fcs = c('L', 'LQ'), clampSel = TRUE, algMaxent = "maxnet", parallel = FALSE) envsFut <- list.files(path = system.file('extdata/wc/future', From caecadccd8daec2c61955133d36ee2537dd4c69b Mon Sep 17 00:00:00 2001 From: Daniel Lopez Date: Thu, 15 Jan 2026 17:52:16 -0500 Subject: [PATCH 03/17] Fix issue when trying to xfer to new area or time when using BIOCLIM or Maxent.jar --- inst/shiny/modules/xfer_area.R | 3 ++- inst/shiny/modules/xfer_time.R | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/inst/shiny/modules/xfer_area.R b/inst/shiny/modules/xfer_area.R index 3f9ecedd..770f7baf 100644 --- a/inst/shiny/modules/xfer_area.R +++ b/inst/shiny/modules/xfer_area.R @@ -273,7 +273,8 @@ xfer_area_module_server <- function(input, output, session, common) { logger %>% writeLog(hlSpp(curSp()), "Transfer of model to new area with ", predType, ' output.') } - if(spp[[curSp()]]$rmm$model$algorithms == 'maxent.jar') + # SpatRaster to Raster + if(spp[[curSp()]]$rmm$model$algorithms %in% c('maxent.jar', 'BIOCLIM')) xferAreaThr <- raster::raster(xferAreaThr) raster::crs(xferAreaThr) <- raster::crs(envs()) # rename diff --git a/inst/shiny/modules/xfer_time.R b/inst/shiny/modules/xfer_time.R index dcde0dc7..1dcb0864 100644 --- a/inst/shiny/modules/xfer_time.R +++ b/inst/shiny/modules/xfer_time.R @@ -477,6 +477,9 @@ xfer_time_module_server <- function(input, output, session, common) { ' with ', predType, " output for GCM ", input$xfAOGCM, ".") } } + # SpatRaster to Raster + if(spp[[curSp()]]$rmm$model$algorithms %in% c('maxent.jar', 'BIOCLIM')) + xferTimeThr <- raster::raster(xferTimeThr) raster::crs(xferTimeThr) <- raster::crs(envs()) # rename names(xferTimeThr) <- paste0(curModel(), '_thresh_', predType) From eba28c97eb4cb948d7d9981472ffef3b4e7c9bf6 Mon Sep 17 00:00:00 2001 From: Daniel Lopez Date: Tue, 20 Jan 2026 16:54:53 -0500 Subject: [PATCH 04/17] Fix loading error for multiple species selected in EnvSpace --- inst/shiny/server.R | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/inst/shiny/server.R b/inst/shiny/server.R index b274e2ac..23c78573 100644 --- a/inst/shiny/server.R +++ b/inst/shiny/server.R @@ -248,7 +248,11 @@ function(input, output, session) { multSp <- reactive(sort(names(reactiveValuesToList(spp))[grepl("\\.", names(reactiveValuesToList(spp)))])) # convenience function for occurrence table for current species - occs <- reactive(spp[[curSp()]]$occs) + occs <- reactive({ + req(curSp()) + # Defaults to the first species if multiple are selected. + spp[[curSp()[1]]]$occs + }) # convenience function for metadata list for current species rmm <- reactive(spp[[curSp()]]$rmm) From f5cf1073a0c67e7c225bc83cd13630a1b36ee355 Mon Sep 17 00:00:00 2001 From: Daniel Lopez Date: Thu, 22 Jan 2026 15:26:18 -0500 Subject: [PATCH 05/17] Add test case for categorical variables in MaxEnt --- R/model_maxent.R | 1 + tests/testthat/test_model_maxent.R | 12 ++++++++++++ tests/testthat/test_penvs_bgMask.R | 6 ++++-- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/R/model_maxent.R b/R/model_maxent.R index 594dca46..a0914e56 100644 --- a/R/model_maxent.R +++ b/R/model_maxent.R @@ -86,6 +86,7 @@ #' @author Jamie M. Kass #' @author Gonzalo E. Pinilla-Buitrago #' @author Bethany A. Johnson +#' @author Daniel Lopez-Lozano # @note #' @seealso \code{\link[ENMeval]{ENMevaluate}} diff --git a/tests/testthat/test_model_maxent.R b/tests/testthat/test_model_maxent.R index a5d22d1a..c668fcab 100644 --- a/tests/testthat/test_model_maxent.R +++ b/tests/testthat/test_model_maxent.R @@ -9,6 +9,11 @@ envs <- envs_userEnvs(rasPath = list.files(system.file("extdata/wc", rasName = list.files(system.file("extdata/wc", package = "wallace"), pattern = ".tif$", full.names = FALSE)) + +# Includes one categorical variable +envsCat <- terra::rast(list.files(system.file('/ex', package='predicts'), + pattern='tif$', full.names=TRUE)) + occs <- read.csv(system.file("extdata/Bassaricyon_alleni.csv", package = "wallace")) bg <- read.csv(system.file("extdata/Bassaricyon_alleni_bgPoints.csv", @@ -49,10 +54,17 @@ for (i in algorithm) { maxentAlg <- model_maxent(occs = occs, bg = bg, user.grp = partblock, bgMsk = terra::rast(envs), rms, rmsStep, fcs, clampSel = TRUE, algMaxent = i, parallel = FALSE) + # run function with one categorical variable + maxentAlg2 <- model_maxent(occs = occs, bg = bg, user.grp = partblock, + bgMsk = envsCat, rms, rmsStep, fcs, clampSel = TRUE, + algMaxent = i, catEnvs = 'biome', parallel = FALSE) test_that("output type checks", { # the output is an ENMeval object expect_is(maxentAlg, "ENMevaluation") + # output using categorical variables is not null and is an ENMeval object + expect_true(!is.null(maxentAlg2)) + expect_is(maxentAlg2, "ENMevaluation") #the output has 9 slots with correct names expect_equal(length(slotNames(maxentAlg)), 20) expect_equal(slotNames(maxentAlg), diff --git a/tests/testthat/test_penvs_bgMask.R b/tests/testthat/test_penvs_bgMask.R index 61b02f4b..76024ea5 100644 --- a/tests/testthat/test_penvs_bgMask.R +++ b/tests/testthat/test_penvs_bgMask.R @@ -1,6 +1,7 @@ #### COMPONENT penvs: Process Environmental Data #### MODULE: Select Study Region context("bgMask") +library("terra") occs <- read.csv(system.file("extdata/Bassaricyon_alleni.csv", package = "wallace"))[, 2:3] @@ -11,9 +12,10 @@ envs <- envs_userEnvs(rasPath = list.files(system.file("extdata/wc", rasName = list.files(system.file("extdata/wc", package = "wallace"), pattern = ".tif$", full.names = FALSE)) -crs(envs) <- "EPSG:4326" + +raster::crs(envs) <- "EPSG:4326" bgExt <- penvs_bgExtent(occs, bgSel = 'minimum convex polygon', bgBuf = 0.5) -crs(bgExt) <- "EPSG:4326" +raster::crs(bgExt) <- "EPSG:4326" bgMask <- penvs_bgMask(occs, envs, bgExt) From d288568cf0a601517ac92b201bdd821206db3982 Mon Sep 17 00:00:00 2001 From: Daniel Lopez Date: Wed, 11 Mar 2026 16:12:52 -0400 Subject: [PATCH 06/17] Integrate leaflet.extras draw bindings (archived on CRAN) --- .gitignore | 1 + DESCRIPTION | 2 +- R/helper_functions.R | 1 - R/utils_leaflet_draw.R | 588 ++++++++++++++++++ .../htmlwidgets/bindings/lfx-draw-bindings.js | 338 ++++++++++ .../build/lfx-draw-drag/lfx-draw-drag-prod.js | 14 + .../lfx-draw-drag-prod.js.LICENSE.txt | 12 + .../build/lfx-draw/7ea3a6d428136b87ab95.png | Bin 0 -> 3581 bytes .../build/lfx-draw/a4e0eb7ad904a4858361.svg | 156 +++++ .../build/lfx-draw/ef32ea2bdf63ba132b4c.png | Bin 0 -> 1906 bytes .../build/lfx-draw/lfx-draw-bindings.js | 1 + .../build/lfx-draw/lfx-draw-prod.css | 1 + .../build/lfx-draw/lfx-draw-prod.js | 1 + inst/shiny/modules/penvs_drawBgExtent.R | 4 +- inst/shiny/modules/poccs_removeByID.R | 2 +- inst/shiny/modules/poccs_selectOccs.R | 4 +- inst/shiny/modules/poccs_thinOccs.R | 2 +- inst/shiny/modules/xfer_area.R | 4 +- inst/shiny/modules/xfer_time.R | 4 +- inst/shiny/modules/xfer_user.R | 4 +- 20 files changed, 1125 insertions(+), 14 deletions(-) create mode 100644 R/utils_leaflet_draw.R create mode 100644 inst/htmlwidgets/bindings/lfx-draw-bindings.js create mode 100644 inst/htmlwidgets/build/lfx-draw-drag/lfx-draw-drag-prod.js create mode 100644 inst/htmlwidgets/build/lfx-draw-drag/lfx-draw-drag-prod.js.LICENSE.txt create mode 100644 inst/htmlwidgets/build/lfx-draw/7ea3a6d428136b87ab95.png create mode 100644 inst/htmlwidgets/build/lfx-draw/a4e0eb7ad904a4858361.svg create mode 100644 inst/htmlwidgets/build/lfx-draw/ef32ea2bdf63ba132b4c.png create mode 100644 inst/htmlwidgets/build/lfx-draw/lfx-draw-bindings.js create mode 100644 inst/htmlwidgets/build/lfx-draw/lfx-draw-prod.css create mode 100644 inst/htmlwidgets/build/lfx-draw/lfx-draw-prod.js diff --git a/.gitignore b/.gitignore index 1f343d3f..e736c928 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ cmip5 # r proj files .Rproj.user .RData +.Rproj # .rmd files temp* # diff --git a/DESCRIPTION b/DESCRIPTION index 65458712..9db41cf8 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -43,7 +43,7 @@ Imports: geodata, knitcitations, leafem, - leaflet.extras (>= 1.0.0), + leafpm, magrittr, markdown, methods, diff --git a/R/helper_functions.R b/R/helper_functions.R index a1a7fc61..84c8a5b0 100644 --- a/R/helper_functions.R +++ b/R/helper_functions.R @@ -65,7 +65,6 @@ spurious <- function(x) { DT::renderDataTable(x) RColorBrewer::brewer.pal(x) leafem::addMouseCoordinates(x) - leaflet.extras::removeDrawToolbar(x) markdown::html_format() rmarkdown::github_document(x) shinyWidgets::pickerInput(x) diff --git a/R/utils_leaflet_draw.R b/R/utils_leaflet_draw.R new file mode 100644 index 00000000..37291171 --- /dev/null +++ b/R/utils_leaflet_draw.R @@ -0,0 +1,588 @@ +# ------------------------------------------------------------------------------ +# +# This file contains code adapted from the 'leaflet.extras' package (GPL-3) +# by Sebastian Gatscha, Bhaskar Karambelkar, Barret Schloerke, et al. +# Original Source: https://github.com/trafficonese/leaflet.extras +# +# This code was included because 'leaflet.extras' was archived on CRAN +# (on 2026-02-19), and its functionality represents a hard dependency for +# this package. +# +# It provides the Leaflet Draw features required to enable polygon drawing +# and shape editing on the map. +# +# ----------------------------------------------------------------------------- + +# Draw dependencies +drawDependencies <- function(drag = TRUE) { + draw_dep <- htmltools::htmlDependency( + "lfx-draw", + version = "1.0.4", + system.file(file.path("htmlwidgets", "build", "lfx-draw"), package = "wallace"), + script = c("lfx-draw-prod.js", "lfx-draw-bindings.js"), + stylesheet = "lfx-draw-prod.css", + all_files = TRUE + ) + + if (drag) { + drag_dep <- htmltools::htmlDependency( + "lfx-draw-drag", + version = "0.4.8", + system.file(file.path("htmlwidgets", "build", "lfx-draw-drag"), package = "wallace"), + script = "lfx-draw-drag-prod.js", + all_files = TRUE + ) + list(draw_dep, drag_dep) + } else { + list(draw_dep) + } +} + +#' Options for drawn shapes +#' @param stroke Whether to draw stroke along the path. Set it to false to disable borders on polygons or circles. +#' @param color Stroke color. +#' @param weight Stroke width in pixels. +#' @param opacity Stroke opacity. +#' @param fill Whether to fill the path with color. Set it to false to disable filling on polygons or circles. +#' @param fillColor Same as color. Fill color. +#' @param fillOpacity Fill opacity. +#' @param dashArray A string that defines the stroke dash pattern. Doesn't work on canvas-powered layers (e.g. Android 2). +#' @param lineCap A string that defines shape to be used at the end of the stroke. +#' @param lineJoin A string that defines shape to be used at the corners of the stroke. +#' @param clickable If false, the vector will not emit mouse events and will act as a part of the underlying map. +#' @param pointerEvents Sets the pointer-events attribute on the path if SVG backend is used. +#' @param smoothFactor How much to simplify the polyline on each zoom level. More means better performance and smoother look, and less means more accurate representation. +#' @param noClip Disabled polyline clipping. +#' @noRd +drawShapeOptions <- function( + stroke = TRUE, + color = "#03f", + weight = 1, + opacity = 1, + fill = TRUE, + fillColor = "#03f", + fillOpacity = 0.4, + dashArray = NULL, + lineCap = NULL, + lineJoin = NULL, + clickable = TRUE, + pointerEvents = NULL, + smoothFactor = 1.0, + noClip = TRUE +) { + leaflet::filterNULL(list( + stroke = stroke, + color = color, + weight = weight, + opacity = opacity, + fill = fill, + fillColor = fillColor, + fillOpacity = fillOpacity, + dashArray = dashArray, + lineCap = lineCap, + lineJoin = lineJoin, + clickable = clickable, + pointerEvents = pointerEvents, + smoothFactor = smoothFactor, + noClip = noClip + )) +} + +#' Options for drawing polylines +#' @param allowIntersection Determines if line segments can cross. +#' @param drawError Configuration options for the error that displays if an intersection is detected. +#' @param guidelineDistance Distance in pixels between each guide dash. +#' @param maxGuideLineLength Maximum length of the guide lines. +#' @param showLength Whether to display the distance in the tooltip. +#' @param metric Determines which measurement system (metric or imperial) is used. +#' @param feet When not metric, use feet instead of yards for display. +#' @param nautic When not metric, not feet, use nautic mile for display. +#' @param zIndexOffset This should be a high number to ensure that you can draw over all other layers on the map. +#' @param shapeOptions Leaflet Polyline options. See \code{\link{drawShapeOptions}}(). +#' @param repeatMode Determines if the draw tool remains enabled after drawing a shape. +#' @noRd +drawPolylineOptions <- function( + allowIntersection = TRUE, + drawError = list(color = "#b00b00", timeout = 2500), + guidelineDistance = 20, + maxGuideLineLength = 4000, + showLength = TRUE, + metric = TRUE, + feet = TRUE, + nautic = FALSE, + zIndexOffset = 2000, + shapeOptions = drawShapeOptions(fill = FALSE), + repeatMode = FALSE +) { + leaflet::filterNULL(list( + allowIntersection = allowIntersection, + drawError = drawError, + guidelineDistance = guidelineDistance, + maxGuideLineLength = maxGuideLineLength, + showLength = showLength, + metric = metric, + feet = feet, + nautic = nautic, + zIndexOffset = zIndexOffset, + shapeOptions = shapeOptions, + repeatMode = repeatMode + )) +} + +#' Options for drawing polygons +#' @param showArea Show the area of the drawn polygon in m², ha or km². The area is only approximate and become less accurate the larger the polygon is. +#' @param metric Determines which measurement system (metric or imperial) is used. +#' @param shapeOptions Shape options. See \code{\link{drawShapeOptions}}(). +#' @param repeatMode Determines if the draw tool remains enabled after drawing a shape. +#' @noRd +drawPolygonOptions <- function( + showArea = FALSE, + metric = TRUE, + shapeOptions = drawShapeOptions(), + repeatMode = FALSE +) { + leaflet::filterNULL(list( + showArea = showArea, + metric = metric, + shapeOptions = shapeOptions, + repeatMode = repeatMode + )) +} + +#' Options for drawing rectangles +#' @param showArea Show the area of the drawn rectangle in m², ha or km². +#' @param metric Determines which measurement system (metric or imperial) is used. +#' @param shapeOptions Shape options. See \code{\link{drawShapeOptions}}(). +#' @param repeatMode Determines if the draw tool remains enabled after drawing a shape. +#' @noRd +drawRectangleOptions <- function( + showArea = TRUE, + metric = TRUE, + shapeOptions = drawShapeOptions(), + repeatMode = FALSE +) { + leaflet::filterNULL(list( + showArea = showArea, + metric = metric, + shapeOptions = shapeOptions, + repeatMode = repeatMode + )) +} + +#' Options for drawing Circles +#' @param showRadius Show the radius of the drawn circle in m, km, ft (feet), or nm (nautical mile). +#' @param metric Determines which measurement system (metric or imperial) is used. +#' @param feet When not metric, use feet instead of yards for display. +#' @param nautic When not metric, not feet, use nautic mile for display. +#' @param shapeOptions Shape options. See \code{\link{drawShapeOptions}}(). +#' @param repeatMode Determines if the draw tool remains enabled after drawing a shape. +#' @noRd +drawCircleOptions <- function( + showRadius = TRUE, + metric = TRUE, + feet = TRUE, + nautic = FALSE, + shapeOptions = drawShapeOptions(), + repeatMode = FALSE +) { + leaflet::filterNULL(list( + shapeOptions = shapeOptions, + repeatMode = repeatMode, + showRadius = showRadius, + metric = metric, + feet = feet, + nautic = nautic + )) +} + +#' Options for drawing markers +#' @param markerIcon Can be either \code{\link[leaflet]{makeIcon}}() OR \code{\link[leaflet]{makeAwesomeIcon}}() +#' @param zIndexOffset This should be a high number to ensure that you can draw over all other layers on the map. +#' @param repeatMode Determines if the draw tool remains enabled after drawing a shape. +#' @noRd +drawMarkerOptions <- function( + markerIcon = NULL, + zIndexOffset = 2000, + repeatMode = FALSE +) { + leaflet::filterNULL(list( + markerIcon = markerIcon, + zIndexOffset = zIndexOffset, + repeatMode = repeatMode + )) +} + +#' Options for drawing circle markers +#' @param stroke Whether to draw stroke along the path. +#' @param color Stroke color. +#' @param weight Stroke width in pixels. +#' @param opacity Stroke opacity. +#' @param fill Whether to fill the path with color. +#' @param fillColor Fill color. +#' @param fillOpacity Fill opacity. +#' @param clickable If false, the vector will not emit mouse events. +#' @param zIndexOffset This should be a high number to ensure that you can draw over all other layers on the map. +#' @param repeatMode Determines if the draw tool remains enabled after drawing a shape. +#' @noRd +drawCircleMarkerOptions <- function( + stroke = TRUE, + color = "#3388ff", + weight = 4, + opacity = 0.5, + fill = TRUE, + fillColor = NULL, + fillOpacity = 0.2, + clickable = TRUE, + zIndexOffset = 2000, + repeatMode = FALSE +) { + leaflet::filterNULL(list( + stroke = stroke, + color = color, + weight = weight, + opacity = opacity, + fill = fill, + fillColor = fillColor, + fillOpacity = fillOpacity, + clickable = clickable, + zIndexOffset = zIndexOffset, + repeatMode = repeatMode + )) +} + +#' Options for path when in editMode +#' @param dashArray A string that defines the stroke dash pattern. +#' @param weight Stroke width in pixels. +#' @param color Stroke color. +#' @param fill Whether to fill the path with color. +#' @param fillColor Fill color. +#' @param fillOpacity Fill opacity. +#' @param maintainColor Whether to maintain shape's original color. +#' @noRd +selectedPathOptions <- function( + dashArray = c("10, 10"), + weight = 2, + color = "black", + fill = TRUE, + fillColor = "black", + fillOpacity = 0.6, + maintainColor = FALSE +) { + leaflet::filterNULL(list( + dashArray = dashArray, + weight = weight, + color = color, + fill = fill, + fillColor = fillColor, + fillOpacity = fillOpacity, + maintainColor = maintainColor + )) +} + +#' Options for editing shapes +#' @param edit Editing enabled by default. Set to false do disable editing. +#' @param remove Set to false to disable removing. +#' @param selectedPathOptions To customize shapes in editing mode pass \code{\link{selectedPathOptions}}(). +#' @param allowIntersection Determines if line segments can cross. +#' @noRd +editToolbarOptions <- function( + edit = TRUE, + remove = TRUE, + selectedPathOptions = NULL, + allowIntersection = TRUE +) { + leaflet::filterNULL(list( + edit = edit, + remove = remove, + selectedPathOptions = selectedPathOptions, + allowIntersection = allowIntersection + )) +} + +#' Options for editing handlers +#' @description Customize tooltips for \code{\link{addDrawToolbar}} +#' @param polyline List of options for polyline tooltips. +#' @param polygon List of options for polygon tooltips. +#' @param rectangle List of options for rectangle tooltips. +#' @param circle List of options for circle tooltips. +#' @param marker List of options for marker tooltips. +#' @param circlemarker List of options for circlemarker tooltips. +#' @param simpleshape List of options for simpleshape tooltips. +#' @noRd +handlersOptions <- function( + polyline = list( + error = "Error: shape edges cannot cross!", + tooltipStart = "Click to start drawing line.", + tooltipCont = "Click to start drawing line.", + tooltipEnd = "Click to start drawing line." + ), + polygon = list( + tooltipStart = "Click to start drawing shape.", + tooltipCont = "Click to start drawing shape.", + tooltipEnd = "Click to start drawing shape." + ), + rectangle = list( + tooltipStart = "Click and drag to draw rectangle." + ), + circle = list( + tooltipStart = "Click map to place circle marker.", + radius = "Radius" + ), + marker = list( + tooltipStart = "Click map to place marker." + ), + circlemarker = list( + tooltipStart = "Click and drag to draw circle." + ), + simpleshape = list( + tooltipEnd = "Release mouse to finish drawing." + ) +) { + leaflet::filterNULL(list( + polyline = list( + error = polyline$error, + tooltip = list( + start = polyline$tooltipStart, + cont = polyline$tooltipCont, + end = polyline$tooltipEnd + ) + ), + polygon = list( + tooltip = list( + start = polygon$tooltipStart, + cont = polygon$tooltipCont, + end = polygon$tooltipEnd + ) + ), + rectangle = list(tooltip = list(start = rectangle$tooltipStart)), + circle = list( + radius = circle$radius, + tooltip = list(start = circle$tooltipStart) + ), + marker = list(tooltip = list(start = marker$tooltipStart)), + circlemarker = list(tooltip = list(start = circlemarker$tooltipStart)), + simpleshape = list(tooltip = list(end = simpleshape$tooltipEnd)) + )) +} + +#' Options for editing the toolbar +#' @description Customize the toolbar for \code{\link{addDrawToolbar}} +#' @param actions List of options for actions toolbar button. +#' @param finish List of options for finish toolbar button. +#' @param undo List of options for undo toolbar button. +#' @param buttons List of options for buttons toolbar button. +#' @noRd +toolbarOptions <- function( + actions = list( + title = "Cancel drawing", + text = "Cancel" + ), + finish = list( + title = "Finish drawing", + text = "Finish" + ), + undo = list( + title = "Delete last point drawn", + text = "Delete last point" + ), + buttons = list( + polyline = "Draw a polyline", + polygon = "Draw a polygon", + rectangle = "Draw a rectangle", + circle = "Draw a circle", + marker = "Draw a marker", + circlemarker = "Draw a circlemarker" + ) +) { + leaflet::filterNULL(list( + actions = list( + title = actions$title, + text = actions$text + ), + finish = list( + title = finish$title, + text = finish$text + ), + undo = list( + title = undo$title, + text = undo$text + ), + buttons = list( + polyline = buttons$polyline, + polygon = buttons$polygon, + rectangle = buttons$rectangle, + circle = buttons$circle, + marker = buttons$marker, + circlemarker = buttons$circlemarker + ) + )) +} + +#' Options for editing edit handlers +#' @description Customize edit handlers for \code{\link{addDrawToolbar}} +#' @param edit List of options for editing tooltips. +#' @param remove List of options for removing tooltips. +#' @noRd +edithandlersOptions <- function( + edit = list( + tooltipText = "Drag handles or markers to edit features.", + tooltipSubtext = "Click cancel to undo changes." + ), + remove = list( + tooltipText = "Click on a feature to remove." + ) +) { + leaflet::filterNULL(list( + edit = list( + tooltip = list( + text = edit$tooltipText, + subtext = edit$tooltipSubtext + ) + ), + remove = list( + tooltip = list( + text = remove$tooltipText + ) + ) + )) +} + +#' Options for editing the toolbar +#' @description Customize the edit toolbar for \code{\link{addDrawToolbar}} +#' @param actions List of options for edit action tooltips. +#' @param buttons List of options for edit button tooltips. +#' @noRd +edittoolbarOptions <- function( + actions = list( + save = list( + title = "Save changes", + text = "Save" + ), + cancel = list( + title = "Cancel editing, discards all changes", + text = "Cancel" + ), + clearAll = list( + title = "Clear all layers", + text = "Clear All" + ) + ), + buttons = list( + edit = "Edit layers", + editDisabled = "No layers to edit", + remove = "Delete layers", + removeDisabled = "No layers to delete" + ) +) { + leaflet::filterNULL(list( + actions = actions, + buttons = buttons + )) +} + +#' Adds a Toolbar to draw shapes/points on the map +#' @param map The map widget. +#' @param targetLayerId An optional layerId of a GeoJSON/TopoJSON layer whose features need to be editable. +#' Used for adding a GeoJSON/TopoJSON layer and then editing the features using the draw plugin. +#' @param targetGroup An optional group name of a Feature Group whose features need to be editable. +#' Used for adding shapes(markers, lines, polygons) and then editing them using the draw plugin. +#' You can either set layerId or group or none but not both. +#' @param position The position where the toolbar should appear. +#' @param polylineOptions See \code{\link{drawPolylineOptions}}(). Set to FALSE to disable polyline drawing. +#' @param polygonOptions See \code{\link{drawPolygonOptions}}(). Set to FALSE to disable polygon drawing. +#' @param circleOptions See \code{\link{drawCircleOptions}}(). Set to FALSE to disable circle drawing. +#' @param rectangleOptions See \code{\link{drawRectangleOptions}}(). Set to FALSE to disable rectangle drawing. +#' @param markerOptions See \code{\link{drawMarkerOptions}}(). Set to FALSE to disable marker drawing. +#' @param circleMarkerOptions See \code{\link{drawCircleMarkerOptions}}(). Set to FALSE to disable circle marker drawing. +#' @param editOptions By default editing is disable. To enable editing pass \code{\link{editToolbarOptions}}(). +#' @param singleFeature When set to TRUE, only one feature can be drawn at a time, the previous ones being removed. +#' @param toolbar See \code{\link{toolbarOptions}}. Set to \code{NULL} to take Leaflets default values. +#' @param handlers See \code{\link{handlersOptions}}. Set to \code{NULL} to take Leaflets default values. +#' @param edittoolbar See \code{\link{edittoolbarOptions}}. Set to \code{NULL} to take Leaflets default values. +#' @param edithandlers See \code{\link{edithandlersOptions}}. Set to \code{NULL} to take Leaflets default values. +#' @param drag When set to \code{TRUE}, the drawn features will be draggable during editing, utilizing +#' the \code{Leaflet.Draw.Drag} plugin. Otherwise, this library will not be included. +#' +#' @details +#' The drawn features emit events upon mouse interaction. +#' Event names follow the pattern: \code{input$MAPID_LAYERCATEGORY_EVENTNAME}, +#' where \code{LAYERCATEGORY} can be one of: +#' \itemize{ +#' \item \code{marker} +#' \item \code{shape} +#' \item \code{polyline} +#' } +#' +#' Similarly, for \code{EVENTNAME}, valid values are: +#' \itemize{ +#' \item \code{click} +#' \item \code{mouseover} +#' \item \code{mouseout} +#' } +#' @noRd +addDrawToolbar <- function( + map, targetLayerId = NULL, targetGroup = NULL, + position = c("topleft", "topright", "bottomleft", "bottomright"), + polylineOptions = drawPolylineOptions(), + polygonOptions = drawPolygonOptions(), + circleOptions = drawCircleOptions(), + rectangleOptions = drawRectangleOptions(), + markerOptions = drawMarkerOptions(), + circleMarkerOptions = drawCircleMarkerOptions(), + editOptions = FALSE, + singleFeature = FALSE, + toolbar = NULL, + handlers = NULL, + edittoolbar = NULL, + edithandlers = NULL, + drag = TRUE +) { + if (!is.null(targetGroup) && !is.null(targetLayerId)) { + stop("To edit existing features either specify a targetGroup or a targetLayerId, but not both") + } + + if (!inherits(toolbar, "list")) toolbar <- NULL + if (!inherits(handlers, "list")) handlers <- NULL + if (!inherits(edittoolbar, "list")) edittoolbar <- NULL + if (!inherits(edithandlers, "list")) edithandlers <- NULL + + map$dependencies <- c(map$dependencies, drawDependencies(drag)) + + markerIconFunction <- NULL + if (inherits(markerOptions, "list") && !is.null(markerOptions$markerIcon)) { + stop("markerIcon is not supported in this version. Please use standard leaflet markers instead.") + } + + position <- match.arg(position) + + options <- list( + position = position, + draw = leaflet::filterNULL(list( + polyline = polylineOptions, + polygon = polygonOptions, + circle = circleOptions, + rectangle = rectangleOptions, + marker = markerOptions, + circlemarker = circleMarkerOptions, + singleFeature = singleFeature + )), + edit = editOptions, + toolbar = toolbar, + handlers = handlers, + edittoolbar = edittoolbar, + edithandlers = edithandlers + ) + + leaflet::invokeMethod( + map, leaflet::getMapData(map), "addDrawToolbar", + targetLayerId, targetGroup, options + ) +} + +#' Removes the draw toolbar +#' @param map The map widget. +#' @param clearFeatures Whether to clear the map of drawn features. +#' @noRd +removeDrawToolbar <- function(map, clearFeatures = FALSE) { + leaflet::invokeMethod(map, leaflet::getMapData(map), "removeDrawToolbar", clearFeatures) +} \ No newline at end of file diff --git a/inst/htmlwidgets/bindings/lfx-draw-bindings.js b/inst/htmlwidgets/bindings/lfx-draw-bindings.js new file mode 100644 index 00000000..f05ead51 --- /dev/null +++ b/inst/htmlwidgets/bindings/lfx-draw-bindings.js @@ -0,0 +1,338 @@ +/* global LeafletWidget, $, L, Shiny, HTMLWidgets */ + +LeafletWidget.methods.addDrawToolbar = function(targetLayerId, + targetGroup, options) { + + // Copied from: https://github.com/rstudio/leaflet/blob/8b20549eeca9b7b66019f098a380422377666bc6/javascript/src/methods.js#L17 + function mouseHandler(mapId, layerId, group, eventName, extraInfo) { + return function(e) { + if (!HTMLWidgets.shinyMode) return; + + var eventInfo = $.extend({ + id: layerId, + category: extraInfo, + '.nonce': Math.random() // force reactivity + }, + group !== null + ? {group: group} + : null, + e.target._latlngs + ? {latlngs: e.target._latlngs.flat()} + : e.target._latlng, + e.target._mRadius !== undefined + ? { radius: e.target._mRadius } + : {}); + + Shiny.onInputChange(mapId + '_' + eventName, eventInfo); + }; + } + + (function() { + + var map = this; + + if (map.drawToolbar) { + map.drawToolbar.remove(map); + delete map.drawToolbar; + } + + // FeatureGroup that will hold our drawn shapes/markers + // This can be an existing GeoJSON layer whose features can be edited/deleted or new ones added. + // OR an existing FeatureGroup whose features can be edited/deleted or new ones added. + // OR a new FeatureGroup to hold drawn shapes. + var editableFeatureGroup; + + if (targetLayerId) { + // If we're given an existing GeoJSON layer find it and use it + editableFeatureGroup = map.layerManager.getLayer('geojson', targetLayerId); + if (editableFeatureGroup) { + map._editableGeoJSONLayerId = targetLayerId; + } else { + // throw an error if we can't find the target GeoJSON layer + throw 'GeoJSON layer with ID ' + targetLayerId + ' not Found'; + } + } else { + // If we're given an existing FeatureLayer use that. + // In this case we don't throw an error if the specified FeatureGroup is not found, + // we silently create a new one. + if (!targetGroup) { + targetGroup = 'editableFeatureGroup'; + } + + editableFeatureGroup = map.layerManager.getLayerGroup(targetGroup, true); + map._editableFeatureGroupName = targetGroup; + } + + // Create appropriate Marker Icon. + if (options && options.draw && options.draw.marker) { + if (options.draw.marker.markerIcon && + options.draw.marker.markerIconFunction) { + options.draw.marker.icon = + options.draw.marker.markerIconFunction(options.draw.marker.markerIcon); + } + } + + // create appropriate options + if (!$.isEmptyObject(options.edit)) { + var editOptions = {}; + if (!options.edit.remove) { + editOptions.remove = false; + } + + if (!options.edit.edit) { + editOptions.edit = false; + } else if (!$.isEmptyObject(options.edit.selectedPathOptions)) { + editOptions.edit = {}; + editOptions.edit.selectedPathOptions = + options.edit.selectedPathOptions; + } + + if (!$.isEmptyObject(options.edit.poly)) { + editOptions.poly = options.edit.poly; + } + + editOptions.featureGroup = editableFeatureGroup; + options.edit = editOptions; + + if (options && options.edittoolbar) { + var edittool = options.edittoolbar; + var edittooldef = L.drawLocal.edit.toolbar; + L.drawLocal.edit.toolbar.buttons = Object.assign({}, edittooldef.buttons, edittool.buttons); + L.drawLocal.edit.toolbar.actions = Object.assign({}, edittooldef.actions, edittool.actions); + } + + if (options && options.edithandlers) { + var edithand = options.edithandlers; + var edithandledef = L.drawLocal.edit.handlers; + L.drawLocal.edit.handlers.edit = Object.assign({}, edithandledef.buttons, edithand.edit); + L.drawLocal.edit.handlers.remove = Object.assign({}, edithandledef.actions, edithand.remove); + } + } + + // Set Toolbar / Handlers options if provided. Changes the default values. + if (options && options.toolbar) { + var rtool = options.toolbar; + var tooldef = L.drawLocal.draw.toolbar; + L.drawLocal.draw.toolbar.buttons = Object.assign({}, tooldef.buttons, rtool.buttons); + L.drawLocal.draw.toolbar.actions = Object.assign({}, tooldef.actions, rtool.actions); + L.drawLocal.draw.toolbar.finish = Object.assign({}, tooldef.finish, rtool.finish); + L.drawLocal.draw.toolbar.undo = Object.assign({}, tooldef.undo, rtool.undo); + } + + if (options && options.handlers) { + var rhand = options.handlers; + var handldef = L.drawLocal.draw.handlers; + L.drawLocal.draw.handlers.circle = Object.assign({}, handldef.circle, rhand.circle); + L.drawLocal.draw.handlers.circlemarker = Object.assign({}, handldef.circlemarker, rhand.circlemarker); + L.drawLocal.draw.handlers.marker = Object.assign({}, handldef.marker, rhand.marker); + L.drawLocal.draw.handlers.polygon = Object.assign({}, handldef.polygon, rhand.polygon); + L.drawLocal.draw.handlers.polyline = Object.assign({}, handldef.polyline, rhand.polyline); + L.drawLocal.draw.handlers.rectangle = Object.assign({}, handldef.rectangle, rhand.rectangle); + L.drawLocal.draw.handlers.simpleshape = Object.assign({}, handldef.simpleshape, rhand.simpleshape); + } + + // Create new Drawing Control + map.drawToolbar = new L.Control.Draw(options); + // Store event handlers in map property + map.drawToolbar.eventHandler = {}; + map.drawToolbar.addTo(map); + + // Event Listeners + map.drawToolbar.eventHandler.onDrawStart = function(e) { + if (!HTMLWidgets.shinyMode) return; + Shiny.onInputChange(map.id + '_draw_start', {'feature_type': e.layerType, 'nonce': Math.random()}); + }; + map.on(L.Draw.Event.DRAWSTART, map.drawToolbar.eventHandler.onDrawStart); + + map.drawToolbar.eventHandler.onDrawStop = function(e) { + if (!HTMLWidgets.shinyMode) return; + Shiny.onInputChange(map.id + '_draw_stop', {'feature_type': e.layerType, 'nonce': Math.random()}); + }; + map.on(L.Draw.Event.DRAWSTOP, map.drawToolbar.eventHandler.onDrawStop); + + map.drawToolbar.eventHandler.onCreated = function(e) { + if (options.draw.singleFeature) { + if (editableFeatureGroup.getLayers().length > 0) { + editableFeatureGroup.clearLayers(); + } + } + + var layer = e.layer; + editableFeatureGroup.addLayer(layer); + + // assign a unique key to the newly created feature + var featureId = L.stamp(layer); + layer.feature = { + 'type': 'Feature', + 'properties': { + '_leaflet_id': featureId, + 'feature_type': e.layerType + } + }; + + // circles are just Points and toGeoJSON won't store radius by default + // so we store it inside the properties. + if (typeof layer.getRadius === 'function') { + layer.feature.properties.radius = layer.getRadius(); + } + + if (!HTMLWidgets.shinyMode) return; + + // Derive R leaflet layer category (shape, marker) from leaflet layer type + var layerCategory = e.layerType; + + if (['rectangle', 'polygon', 'circle'].includes(layerCategory)) { + layerCategory = 'shape'; + } else if (layerCategory === 'circlemarker') { + layerCategory = 'marker'; + } + + // Add R leaflet click, etc, handlers. + // Adjusted from: https://github.com/rstudio/leaflet/blob/8b20549eeca9b7b66019f098a380422377666bc6/javascript/src/methods.js#L355 + layer.on('click', mouseHandler(map.id, featureId, targetGroup, layerCategory + '_draw_click', layerCategory), map); + layer.on('mouseover', mouseHandler(map.id, featureId, targetGroup, layerCategory + '_draw_mouseover', layerCategory), map); + layer.on('mouseout', mouseHandler(map.id, featureId, targetGroup, layerCategory + '_draw_mouseout', layerCategory), map); + + Shiny.onInputChange(map.id + '_draw_new_feature', + layer.toGeoJSON(), {priority: 'event'}); + Shiny.onInputChange(map.id + '_draw_all_features', + editableFeatureGroup.toGeoJSON(), {priority: 'event'}); + }; + map.on(L.Draw.Event.CREATED, map.drawToolbar.eventHandler.onCreated); + + map.drawToolbar.eventHandler.onEditstart = function() { + if (!HTMLWidgets.shinyMode) return; + Shiny.onInputChange(map.id + '_draw_editstart', true, {priority: 'event'}); + }; + map.on(L.Draw.Event.EDITSTART, map.drawToolbar.eventHandler.onEditstart); + + map.drawToolbar.eventHandler.onEditstop = function() { + if (!HTMLWidgets.shinyMode) return; + Shiny.onInputChange(map.id + '_draw_editstop', true, {priority: 'event'}); + }; + map.on(L.Draw.Event.EDITSTOP, map.drawToolbar.eventHandler.onEditstop); + + map.drawToolbar.eventHandler.onEdited = function(e) { + var layers = e.layers; + layers.eachLayer(function(layer) { + var featureId = L.stamp(layer); + if (!layer.feature) { + layer.feature = {'type': 'Feature'}; + } + + if (!layer.feature.properties) { + layer.feature.properties = {}; + } + + layer.feature.properties._leaflet_id = featureId; + layer.feature.properties.layerId = layer.options.layerId; + if (typeof layer.getRadius === 'function') { + layer.feature.properties.radius = layer.getRadius(); + } + }); + + if (!HTMLWidgets.shinyMode) return; + + Shiny.onInputChange(map.id + '_draw_edited_features', + layers.toGeoJSON(), {priority: 'event'}); + Shiny.onInputChange(map.id + '_draw_all_features', + editableFeatureGroup.toGeoJSON(), {priority: 'event'}); + }; + map.on(L.Draw.Event.EDITED, map.drawToolbar.eventHandler.onEdited); + + map.drawToolbar.eventHandler.onDeletestart = function() { + if (!HTMLWidgets.shinyMode) return; + Shiny.onInputChange(map.id + '_draw_deletestart', true, {priority: 'event'}); + }; + map.on(L.Draw.Event.DELETESTART, map.drawToolbar.eventHandler.onDeletestart); + + map.drawToolbar.eventHandler.onDeletestop = function() { + if (!HTMLWidgets.shinyMode) return; + Shiny.onInputChange(map.id + '_draw_deletestop', true, {priority: 'event'}); + }; + map.on(L.Draw.Event.DELETESTOP, map.drawToolbar.eventHandler.onDeletestop); + + map.drawToolbar.eventHandler.onDeleted = function(e) { + var layers = e.layers; + layers.eachLayer(function(layer) { + var featureId = L.stamp(layer); + if (!layer.feature) { + layer.feature = {'type': 'Feature'}; + } + + if (!layer.feature.properties) { + layer.feature.properties = {}; + } + + layer.feature.properties._leaflet_id = featureId; + layer.feature.properties.layerId = layer.options.layerId; + if (typeof layer.getRadius === 'function') { + layer.feature.properties.radius = layer.getRadius(); + } + }); + + if (!HTMLWidgets.shinyMode) return; + Shiny.onInputChange(map.id + '_draw_deleted_features', + layers.toGeoJSON(), {priority: 'event'}); + Shiny.onInputChange(map.id + '_draw_all_features', + editableFeatureGroup.toGeoJSON(), {priority: 'event'}); + }; + map.on(L.Draw.Event.DELETED, map.drawToolbar.eventHandler.onDeleted); + + }).call(this); + +}; + +LeafletWidget.methods.removeDrawToolbar = function(clearFeatures) { + (function() { + + var map = this; + + if (map.drawToolbar) { + map.off(L.Draw.Event.DRAWSTART, map.drawToolbar.eventHandler.onDrawStart); + map.off(L.Draw.Event.DRAWSTOP, map.drawToolbar.eventHandler.onDrawStop); + map.off(L.Draw.Event.CREATED, map.drawToolbar.eventHandler.onCreated); + map.off(L.Draw.Event.EDITSTART, map.drawToolbar.eventHandler.onEditstart); + map.off(L.Draw.Event.EDITSTOP, map.drawToolbar.eventHandler.onEditstop); + map.off(L.Draw.Event.EDITED, map.drawToolbar.eventHandler.onEdited); + map.off(L.Draw.Event.DELETESTART, map.drawToolbar.eventHandler.onDeletestart); + map.off(L.Draw.Event.DELETESTOP, map.drawToolbar.eventHandler.onDeletestop); + map.off(L.Draw.Event.DELETED, map.drawToolbar.eventHandler.onDeleted); + map.drawToolbar.remove(map); + delete map.drawToolbar; + } + + if (map._editableFeatureGroupName && clearFeatures) { + var featureGroup = map.layerManager.getLayerGroup(map._editableFeatureGroupName, false); + featureGroup.clearLayers(); + } + + map._editableFeatureGroupName = null; + if (map._editableGeoJSONLayerId && clearFeatures) { + map.layerManager.removeLayer('geojson', map._editableGeoJSONLayerId); + } + + map._editableGeoJSONLayerId = null; + }).call(this); + +}; + + +// TODO - not used for now. Missing R-function..Is it working? +LeafletWidget.methods.getDrawnItems = function() { + var map = this; + + var featureGroup; + if (map._editableGeoJSONLayerId) { + featureGroup = map.layerManager.getLayer('geojson', map._editableGeoJSONLayerId); + } else if (map._editableFeatureGroupName) { + featureGroup = map.layerManager.getLayerGroup(map._editableFeatureGroupName, false); + } + + if (featureGroup) { + return featureGroup.toGeoJSON(); + } else { + return null; + } + +}; diff --git a/inst/htmlwidgets/build/lfx-draw-drag/lfx-draw-drag-prod.js b/inst/htmlwidgets/build/lfx-draw-drag/lfx-draw-drag-prod.js new file mode 100644 index 00000000..1f35c5c9 --- /dev/null +++ b/inst/htmlwidgets/build/lfx-draw-drag/lfx-draw-drag-prod.js @@ -0,0 +1,14 @@ +(()=>{var t={60:(t,e,i)=>{var o=globalThis.L||i(525);i(220),i(980),i(650),i(190),i(443),i(641),i(984),t.exports=o.Edit.Poly},220:()=>{var t,e;t=window,e=document,L.drawVersion="0.4.14",L.Draw={},L.drawLocal={draw:{toolbar:{actions:{title:"Cancel drawing",text:"Cancel"},finish:{title:"Finish drawing",text:"Finish"},undo:{title:"Delete last point drawn",text:"Delete last point"},buttons:{polyline:"Draw a polyline",polygon:"Draw a polygon",rectangle:"Draw a rectangle",circle:"Draw a circle",marker:"Draw a marker",circlemarker:"Draw a circlemarker"}},handlers:{circle:{tooltip:{start:"Click and drag to draw circle."},radius:"Radius"},circlemarker:{tooltip:{start:"Click map to place circle marker."}},marker:{tooltip:{start:"Click map to place marker."}},polygon:{tooltip:{start:"Click to start drawing shape.",cont:"Click to continue drawing shape.",end:"Click first point to close this shape."}},polyline:{error:"Error: shape edges cannot cross!",tooltip:{start:"Click to start drawing line.",cont:"Click to continue drawing line.",end:"Click last point to finish line."}},rectangle:{tooltip:{start:"Click and drag to draw rectangle."}},simpleshape:{tooltip:{end:"Release mouse to finish drawing."}}}},edit:{toolbar:{actions:{save:{title:"Save changes",text:"Save"},cancel:{title:"Cancel editing, discards all changes",text:"Cancel"},clearAll:{title:"Clear all layers",text:"Clear All"}},buttons:{edit:"Edit layers",editDisabled:"No layers to edit",remove:"Delete layers",removeDisabled:"No layers to delete"}},handlers:{edit:{tooltip:{text:"Drag handles or markers to edit features.",subtext:"Click cancel to undo changes."}},remove:{tooltip:{text:"Click on a feature to remove."}}}}},L.Draw.Event={},L.Draw.Event.CREATED="draw:created",L.Draw.Event.EDITED="draw:edited",L.Draw.Event.DELETED="draw:deleted",L.Draw.Event.DRAWSTART="draw:drawstart",L.Draw.Event.DRAWSTOP="draw:drawstop",L.Draw.Event.DRAWVERTEX="draw:drawvertex",L.Draw.Event.EDITSTART="draw:editstart",L.Draw.Event.EDITMOVE="draw:editmove",L.Draw.Event.EDITRESIZE="draw:editresize",L.Draw.Event.EDITVERTEX="draw:editvertex",L.Draw.Event.EDITSTOP="draw:editstop",L.Draw.Event.DELETESTART="draw:deletestart",L.Draw.Event.DELETESTOP="draw:deletestop",L.Draw.Event.TOOLBAROPENED="draw:toolbaropened",L.Draw.Event.TOOLBARCLOSED="draw:toolbarclosed",L.Draw.Event.MARKERCONTEXT="draw:markercontext",L.Draw=L.Draw||{},L.Draw.Feature=L.Handler.extend({initialize:function(t,e){this._map=t,this._container=t._container,this._overlayPane=t._panes.overlayPane,this._popupPane=t._panes.popupPane,e&&e.shapeOptions&&(e.shapeOptions=L.Util.extend({},this.options.shapeOptions,e.shapeOptions)),L.setOptions(this,e);var i=L.version.split(".");1===parseInt(i[0],10)&&parseInt(i[1],10)>=2?L.Draw.Feature.include(L.Evented.prototype):L.Draw.Feature.include(L.Mixin.Events)},enable:function(){this._enabled||(L.Handler.prototype.enable.call(this),this.fire("enabled",{handler:this.type}),this._map.fire(L.Draw.Event.DRAWSTART,{layerType:this.type}))},disable:function(){this._enabled&&(L.Handler.prototype.disable.call(this),this._map.fire(L.Draw.Event.DRAWSTOP,{layerType:this.type}),this.fire("disabled",{handler:this.type}))},addHooks:function(){var t=this._map;t&&(L.DomUtil.disableTextSelection(),t.getContainer().focus(),this._tooltip=new L.Draw.Tooltip(this._map),L.DomEvent.on(this._container,"keyup",this._cancelDrawing,this))},removeHooks:function(){this._map&&(L.DomUtil.enableTextSelection(),this._tooltip.dispose(),this._tooltip=null,L.DomEvent.off(this._container,"keyup",this._cancelDrawing,this))},setOptions:function(t){L.setOptions(this,t)},_fireCreatedEvent:function(t){this._map.fire(L.Draw.Event.CREATED,{layer:t,layerType:this.type})},_cancelDrawing:function(t){27===t.keyCode&&(this._map.fire("draw:canceled",{layerType:this.type}),this.disable())}}),L.Draw.Polyline=L.Draw.Feature.extend({statics:{TYPE:"polyline"},Poly:L.Polyline,options:{allowIntersection:!0,repeatMode:!1,drawError:{color:"#b00b00",timeout:2500},icon:new L.DivIcon({iconSize:new L.Point(8,8),className:"leaflet-div-icon leaflet-editing-icon"}),touchIcon:new L.DivIcon({iconSize:new L.Point(20,20),className:"leaflet-div-icon leaflet-editing-icon leaflet-touch-icon"}),guidelineDistance:20,maxGuideLineLength:4e3,shapeOptions:{stroke:!0,color:"#3388ff",weight:4,opacity:.5,fill:!1,clickable:!0},metric:!0,feet:!0,nautic:!1,showLength:!0,zIndexOffset:2e3,factor:1,maxPoints:0},initialize:function(t,e){L.Browser.touch&&(this.options.icon=this.options.touchIcon),this.options.drawError.message=L.drawLocal.draw.handlers.polyline.error,e&&e.drawError&&(e.drawError=L.Util.extend({},this.options.drawError,e.drawError)),this.type=L.Draw.Polyline.TYPE,L.Draw.Feature.prototype.initialize.call(this,t,e)},addHooks:function(){L.Draw.Feature.prototype.addHooks.call(this),this._map&&(this._markers=[],this._markerGroup=new L.LayerGroup,this._map.addLayer(this._markerGroup),this._poly=new L.Polyline([],this.options.shapeOptions),this._tooltip.updateContent(this._getTooltipText()),this._mouseMarker||(this._mouseMarker=L.marker(this._map.getCenter(),{icon:L.divIcon({className:"leaflet-mouse-marker",iconAnchor:[20,20],iconSize:[40,40]}),opacity:0,zIndexOffset:this.options.zIndexOffset})),this._mouseMarker.on("mouseout",this._onMouseOut,this).on("mousemove",this._onMouseMove,this).on("mousedown",this._onMouseDown,this).on("mouseup",this._onMouseUp,this).addTo(this._map),this._map.on("mouseup",this._onMouseUp,this).on("mousemove",this._onMouseMove,this).on("zoomlevelschange",this._onZoomEnd,this).on("touchstart",this._onTouch,this).on("zoomend",this._onZoomEnd,this))},removeHooks:function(){L.Draw.Feature.prototype.removeHooks.call(this),this._clearHideErrorTimeout(),this._cleanUpShape(),this._map.removeLayer(this._markerGroup),delete this._markerGroup,delete this._markers,this._map.removeLayer(this._poly),delete this._poly,this._mouseMarker.off("mousedown",this._onMouseDown,this).off("mouseout",this._onMouseOut,this).off("mouseup",this._onMouseUp,this).off("mousemove",this._onMouseMove,this),this._map.removeLayer(this._mouseMarker),delete this._mouseMarker,this._clearGuides(),this._map.off("mouseup",this._onMouseUp,this).off("mousemove",this._onMouseMove,this).off("zoomlevelschange",this._onZoomEnd,this).off("zoomend",this._onZoomEnd,this).off("touchstart",this._onTouch,this).off("click",this._onTouch,this)},deleteLastVertex:function(){if(!(this._markers.length<=1)){var t=this._markers.pop(),e=this._poly,i=e.getLatLngs(),o=i.splice(-1,1)[0];this._poly.setLatLngs(i),this._markerGroup.removeLayer(t),e.getLatLngs().length<2&&this._map.removeLayer(e),this._vertexChanged(o,!1)}},addVertex:function(t){this._markers.length>=2&&!this.options.allowIntersection&&this._poly.newLatLngIntersects(t)?this._showErrorTooltip():(this._errorShown&&this._hideErrorTooltip(),this._markers.push(this._createMarker(t)),this._poly.addLatLng(t),2===this._poly.getLatLngs().length&&this._map.addLayer(this._poly),this._vertexChanged(t,!0))},completeShape:function(){this._markers.length<=1||(this._fireCreatedEvent(),this.disable(),this.options.repeatMode&&this.enable())},_finishShape:function(){var t=this._poly._defaultShape?this._poly._defaultShape():this._poly.getLatLngs(),e=this._poly.newLatLngIntersects(t[t.length-1]);!this.options.allowIntersection&&e||!this._shapeIsValid()?this._showErrorTooltip():(this._fireCreatedEvent(),this.disable(),this.options.repeatMode&&this.enable())},_shapeIsValid:function(){return!0},_onZoomEnd:function(){null!==this._markers&&this._updateGuide()},_onMouseMove:function(t){var e=this._map.mouseEventToLayerPoint(t.originalEvent),i=this._map.layerPointToLatLng(e);this._currentLatLng=i,this._updateTooltip(i),this._updateGuide(e),this._mouseMarker.setLatLng(i),L.DomEvent.preventDefault(t.originalEvent)},_vertexChanged:function(t,e){this._map.fire(L.Draw.Event.DRAWVERTEX,{layers:this._markerGroup}),this._updateFinishHandler(),this._updateRunningMeasure(t,e),this._clearGuides(),this._updateTooltip()},_onMouseDown:function(t){if(!this._clickHandled&&!this._touchHandled&&!this._disableMarkers){this._onMouseMove(t),this._clickHandled=!0,this._disableNewMarkers();var e=t.originalEvent,i=e.clientX,o=e.clientY;this._startPoint.call(this,i,o)}},_startPoint:function(t,e){this._mouseDownOrigin=L.point(t,e)},_onMouseUp:function(t){var e=t.originalEvent,i=e.clientX,o=e.clientY;this._endPoint.call(this,i,o,t),this._clickHandled=null},_endPoint:function(e,i,o){if(this._mouseDownOrigin){var a=L.point(e,i).distanceTo(this._mouseDownOrigin),s=this._calculateFinishDistance(o.latlng);this.options.maxPoints>1&&this.options.maxPoints==this._markers.length+1?(this.addVertex(o.latlng),this._finishShape()):s<10&&L.Browser.touch?this._finishShape():Math.abs(a)<9*(t.devicePixelRatio||1)&&this.addVertex(o.latlng),this._enableNewMarkers()}this._mouseDownOrigin=null},_onTouch:function(t){var e,i,o=t.originalEvent;!o.touches||!o.touches[0]||this._clickHandled||this._touchHandled||this._disableMarkers||(e=o.touches[0].clientX,i=o.touches[0].clientY,this._disableNewMarkers(),this._touchHandled=!0,this._startPoint.call(this,e,i),this._endPoint.call(this,e,i,t),this._touchHandled=null),this._clickHandled=null},_onMouseOut:function(){this._tooltip&&this._tooltip._onMouseOut.call(this._tooltip)},_calculateFinishDistance:function(t){var e;if(this._markers.length>0){var i;if(this.type===L.Draw.Polyline.TYPE)i=this._markers[this._markers.length-1];else{if(this.type!==L.Draw.Polygon.TYPE)return 1/0;i=this._markers[0]}var o=this._map.latLngToContainerPoint(i.getLatLng()),a=new L.Marker(t,{icon:this.options.icon,zIndexOffset:2*this.options.zIndexOffset}),s=this._map.latLngToContainerPoint(a.getLatLng());e=o.distanceTo(s)}else e=1/0;return e},_updateFinishHandler:function(){var t=this._markers.length;t>1&&this._markers[t-1].on("click",this._finishShape,this),t>2&&this._markers[t-2].off("click",this._finishShape,this)},_createMarker:function(t){var e=new L.Marker(t,{icon:this.options.icon,zIndexOffset:2*this.options.zIndexOffset});return this._markerGroup.addLayer(e),e},_updateGuide:function(t){var e=this._markers?this._markers.length:0;e>0&&(t=t||this._map.latLngToLayerPoint(this._currentLatLng),this._clearGuides(),this._drawGuide(this._map.latLngToLayerPoint(this._markers[e-1].getLatLng()),t))},_updateTooltip:function(t){var e=this._getTooltipText();t&&this._tooltip.updatePosition(t),this._errorShown||this._tooltip.updateContent(e)},_drawGuide:function(t,e){var i,o,a,s=Math.floor(Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2))),n=this.options.guidelineDistance,r=this.options.maxGuideLineLength,h=s>r?s-r:n;for(this._guidesContainer||(this._guidesContainer=L.DomUtil.create("div","leaflet-draw-guides",this._overlayPane));h1&&this._markers[this._markers.length-1].off("click",this._finishShape,this)},_fireCreatedEvent:function(){var t=new this.Poly(this._poly.getLatLngs(),this.options.shapeOptions);L.Draw.Feature.prototype._fireCreatedEvent.call(this,t)}}),L.Draw.Polygon=L.Draw.Polyline.extend({statics:{TYPE:"polygon"},Poly:L.Polygon,options:{showArea:!1,showLength:!1,shapeOptions:{stroke:!0,color:"#3388ff",weight:4,opacity:.5,fill:!0,fillColor:null,fillOpacity:.2,clickable:!0},metric:!0,feet:!0,nautic:!1,precision:{}},initialize:function(t,e){L.Draw.Polyline.prototype.initialize.call(this,t,e),this.type=L.Draw.Polygon.TYPE},_updateFinishHandler:function(){var t=this._markers.length;1===t&&this._markers[0].on("click",this._finishShape,this),t>2&&(this._markers[t-1].on("dblclick",this._finishShape,this),t>3&&this._markers[t-2].off("dblclick",this._finishShape,this))},_getTooltipText:function(){var t,e;return 0===this._markers.length?t=L.drawLocal.draw.handlers.polygon.tooltip.start:this._markers.length<3?(t=L.drawLocal.draw.handlers.polygon.tooltip.cont,e=this._getMeasurementString()):(t=L.drawLocal.draw.handlers.polygon.tooltip.end,e=this._getMeasurementString()),{text:t,subtext:e}},_getMeasurementString:function(){var t=this._area,e="";return t||this.options.showLength?(this.options.showLength&&(e=L.Draw.Polyline.prototype._getMeasurementString.call(this)),t&&(e+="
"+L.GeometryUtil.readableArea(t,this.options.metric,this.options.precision)),e):null},_shapeIsValid:function(){return this._markers.length>=3},_vertexChanged:function(t,e){var i;!this.options.allowIntersection&&this.options.showArea&&(i=this._poly.getLatLngs(),this._area=L.GeometryUtil.geodesicArea(i)),L.Draw.Polyline.prototype._vertexChanged.call(this,t,e)},_cleanUpShape:function(){var t=this._markers.length;t>0&&(this._markers[0].off("click",this._finishShape,this),t>2&&this._markers[t-1].off("dblclick",this._finishShape,this))}}),L.SimpleShape={},L.Draw.SimpleShape=L.Draw.Feature.extend({options:{repeatMode:!1},initialize:function(t,e){this._endLabelText=L.drawLocal.draw.handlers.simpleshape.tooltip.end,L.Draw.Feature.prototype.initialize.call(this,t,e)},addHooks:function(){L.Draw.Feature.prototype.addHooks.call(this),this._map&&(this._mapDraggable=this._map.dragging.enabled(),this._mapDraggable&&this._map.dragging.disable(),this._container.style.cursor="crosshair",this._tooltip.updateContent({text:this._initialLabelText}),this._map.on("mousedown",this._onMouseDown,this).on("mousemove",this._onMouseMove,this).on("touchstart",this._onMouseDown,this).on("touchmove",this._onMouseMove,this),e.addEventListener("touchstart",L.DomEvent.preventDefault,{passive:!1}))},removeHooks:function(){L.Draw.Feature.prototype.removeHooks.call(this),this._map&&(this._mapDraggable&&this._map.dragging.enable(),this._container.style.cursor="",this._map.off("mousedown",this._onMouseDown,this).off("mousemove",this._onMouseMove,this).off("touchstart",this._onMouseDown,this).off("touchmove",this._onMouseMove,this),L.DomEvent.off(e,"mouseup",this._onMouseUp,this),L.DomEvent.off(e,"touchend",this._onMouseUp,this),e.removeEventListener("touchstart",L.DomEvent.preventDefault),this._shape&&(this._map.removeLayer(this._shape),delete this._shape)),this._isDrawing=!1},_getTooltipText:function(){return{text:this._endLabelText}},_onMouseDown:function(t){this._isDrawing=!0,this._startLatLng=t.latlng,L.DomEvent.on(e,"mouseup",this._onMouseUp,this).on(e,"touchend",this._onMouseUp,this).preventDefault(t.originalEvent)},_onMouseMove:function(t){var e=t.latlng;this._tooltip.updatePosition(e),this._isDrawing&&(this._tooltip.updateContent(this._getTooltipText()),this._drawShape(e))},_onMouseUp:function(){this._shape&&this._fireCreatedEvent(),this.disable(),this.options.repeatMode&&this.enable()}}),L.Draw.Rectangle=L.Draw.SimpleShape.extend({statics:{TYPE:"rectangle"},options:{shapeOptions:{stroke:!0,color:"#3388ff",weight:4,opacity:.5,fill:!0,fillColor:null,fillOpacity:.2,showArea:!0,clickable:!0},metric:!0},initialize:function(t,e){this.type=L.Draw.Rectangle.TYPE,this._initialLabelText=L.drawLocal.draw.handlers.rectangle.tooltip.start,L.Draw.SimpleShape.prototype.initialize.call(this,t,e)},disable:function(){this._enabled&&(this._isCurrentlyTwoClickDrawing=!1,L.Draw.SimpleShape.prototype.disable.call(this))},_onMouseUp:function(t){this._shape||this._isCurrentlyTwoClickDrawing?this._isCurrentlyTwoClickDrawing&&!function(t,e){for(;(t=t.parentElement)&&!t.classList.contains(e););return t}(t.target,"leaflet-pane")||L.Draw.SimpleShape.prototype._onMouseUp.call(this):this._isCurrentlyTwoClickDrawing=!0},_drawShape:function(t){this._shape?this._shape.setBounds(new L.LatLngBounds(this._startLatLng,t)):(this._shape=new L.Rectangle(new L.LatLngBounds(this._startLatLng,t),this.options.shapeOptions),this._map.addLayer(this._shape))},_fireCreatedEvent:function(){var t=new L.Rectangle(this._shape.getBounds(),this.options.shapeOptions);L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this,t)},_getTooltipText:function(){var t,e,i,o=L.Draw.SimpleShape.prototype._getTooltipText.call(this),a=this._shape,s=this.options.showArea;return a&&(t=this._shape._defaultShape?this._shape._defaultShape():this._shape.getLatLngs(),e=L.GeometryUtil.geodesicArea(t),i=s?L.GeometryUtil.readableArea(e,this.options.metric):""),{text:o.text,subtext:i}}}),L.Draw.Marker=L.Draw.Feature.extend({statics:{TYPE:"marker"},options:{icon:new L.Icon.Default,repeatMode:!1,zIndexOffset:2e3},initialize:function(t,e){this.type=L.Draw.Marker.TYPE,this._initialLabelText=L.drawLocal.draw.handlers.marker.tooltip.start,L.Draw.Feature.prototype.initialize.call(this,t,e)},addHooks:function(){L.Draw.Feature.prototype.addHooks.call(this),this._map&&(this._tooltip.updateContent({text:this._initialLabelText}),this._mouseMarker||(this._mouseMarker=L.marker(this._map.getCenter(),{icon:L.divIcon({className:"leaflet-mouse-marker",iconAnchor:[20,20],iconSize:[40,40]}),opacity:0,zIndexOffset:this.options.zIndexOffset})),this._mouseMarker.on("click",this._onClick,this).addTo(this._map),this._map.on("mousemove",this._onMouseMove,this),this._map.on("click",this._onTouch,this))},removeHooks:function(){L.Draw.Feature.prototype.removeHooks.call(this),this._map&&(this._map.off("click",this._onClick,this).off("click",this._onTouch,this),this._marker&&(this._marker.off("click",this._onClick,this),this._map.removeLayer(this._marker),delete this._marker),this._mouseMarker.off("click",this._onClick,this),this._map.removeLayer(this._mouseMarker),delete this._mouseMarker,this._map.off("mousemove",this._onMouseMove,this))},_onMouseMove:function(t){var e=t.latlng;this._tooltip.updatePosition(e),this._mouseMarker.setLatLng(e),this._marker?(e=this._mouseMarker.getLatLng(),this._marker.setLatLng(e)):(this._marker=this._createMarker(e),this._marker.on("click",this._onClick,this),this._map.on("click",this._onClick,this).addLayer(this._marker))},_createMarker:function(t){return new L.Marker(t,{icon:this.options.icon,zIndexOffset:this.options.zIndexOffset})},_onClick:function(){this._fireCreatedEvent(),this.disable(),this.options.repeatMode&&this.enable()},_onTouch:function(t){this._onMouseMove(t),this._onClick()},_fireCreatedEvent:function(){var t=new L.Marker.Touch(this._marker.getLatLng(),{icon:this.options.icon});L.Draw.Feature.prototype._fireCreatedEvent.call(this,t)}}),L.Draw.CircleMarker=L.Draw.Marker.extend({statics:{TYPE:"circlemarker"},options:{stroke:!0,color:"#3388ff",weight:4,opacity:.5,fill:!0,fillColor:null,fillOpacity:.2,clickable:!0,zIndexOffset:2e3},initialize:function(t,e){this.type=L.Draw.CircleMarker.TYPE,this._initialLabelText=L.drawLocal.draw.handlers.circlemarker.tooltip.start,L.Draw.Feature.prototype.initialize.call(this,t,e)},_fireCreatedEvent:function(){var t=new L.CircleMarker(this._marker.getLatLng(),this.options);L.Draw.Feature.prototype._fireCreatedEvent.call(this,t)},_createMarker:function(t){return new L.CircleMarker(t,this.options)}}),L.Draw.Circle=L.Draw.SimpleShape.extend({statics:{TYPE:"circle"},options:{shapeOptions:{stroke:!0,color:"#3388ff",weight:4,opacity:.5,fill:!0,fillColor:null,fillOpacity:.2,clickable:!0},showRadius:!0,metric:!0,feet:!0,nautic:!1},initialize:function(t,e){this.type=L.Draw.Circle.TYPE,this._initialLabelText=L.drawLocal.draw.handlers.circle.tooltip.start,L.Draw.SimpleShape.prototype.initialize.call(this,t,e)},_drawShape:function(t){if(L.GeometryUtil.isVersion07x())var e=this._startLatLng.distanceTo(t);else e=this._map.distance(this._startLatLng,t);this._shape?this._shape.setRadius(e):(this._shape=new L.Circle(this._startLatLng,e,this.options.shapeOptions),this._map.addLayer(this._shape))},_fireCreatedEvent:function(){var t=new L.Circle(this._startLatLng,this._shape.getRadius(),this.options.shapeOptions);L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this,t)},_onMouseMove:function(t){var e,i=t.latlng,o=this.options.showRadius,a=this.options.metric;if(this._tooltip.updatePosition(i),this._isDrawing){this._drawShape(i),e=this._shape.getRadius().toFixed(1);var s="";o&&(s=L.drawLocal.draw.handlers.circle.radius+": "+L.GeometryUtil.readableDistance(e,a,this.options.feet,this.options.nautic)),this._tooltip.updateContent({text:this._endLabelText,subtext:s})}}}),L.Edit=L.Edit||{},L.Edit.Marker=L.Handler.extend({initialize:function(t,e){this._marker=t,L.setOptions(this,e)},addHooks:function(){var t=this._marker;t.dragging.enable(),t.on("dragend",this._onDragEnd,t),this._toggleMarkerHighlight()},removeHooks:function(){var t=this._marker;t.dragging.disable(),t.off("dragend",this._onDragEnd,t),this._toggleMarkerHighlight()},_onDragEnd:function(t){var e=t.target;e.edited=!0,this._map.fire(L.Draw.Event.EDITMOVE,{layer:e})},_toggleMarkerHighlight:function(){var t=this._marker._icon;t&&(t.style.display="none",L.DomUtil.hasClass(t,"leaflet-edit-marker-selected")?(L.DomUtil.removeClass(t,"leaflet-edit-marker-selected"),this._offsetMarker(t,-4)):(L.DomUtil.addClass(t,"leaflet-edit-marker-selected"),this._offsetMarker(t,4)),t.style.display="")},_offsetMarker:function(t,e){var i=parseInt(t.style.marginTop,10)-e,o=parseInt(t.style.marginLeft,10)-e;t.style.marginTop=i+"px",t.style.marginLeft=o+"px"}}),L.Marker.addInitHook((function(){L.Edit.Marker&&(this.editing=new L.Edit.Marker(this),this.options.editable&&this.editing.enable())})),L.Edit=L.Edit||{},L.Edit.Poly=L.Handler.extend({initialize:function(t){this.latlngs=[t._latlngs],t._holes&&(this.latlngs=this.latlngs.concat(t._holes)),this._poly=t,this._poly.on("revert-edited",this._updateLatLngs,this)},_defaultShape:function(){return L.Polyline._flat?L.Polyline._flat(this._poly._latlngs)?this._poly._latlngs:this._poly._latlngs[0]:this._poly._latlngs},_eachVertexHandler:function(t){for(var e=0;et&&(i._index+=e)}))},_createMiddleMarker:function(t,e){var i,o,a,s=this._getMiddleLatLng(t,e),n=this._createMarker(s);n.setOpacity(.6),t._middleRight=e._middleLeft=n,o=function(){n.off("touchmove",o,this);var a=e._index;n._index=a,n.off("click",i,this).on("click",this._onMarkerClick,this),s.lat=n.getLatLng().lat,s.lng=n.getLatLng().lng,this._spliceLatLngs(a,0,s),this._markers.splice(a,0,n),n.setOpacity(1),this._updateIndexes(a,1),e._index++,this._updatePrevNext(t,n),this._updatePrevNext(n,e),this._poly.fire("editstart")},a=function(){n.off("dragstart",o,this),n.off("dragend",a,this),n.off("touchmove",o,this),this._createMiddleMarker(t,n),this._createMiddleMarker(n,e)},i=function(){o.call(this),a.call(this),this._fireEdit()},n.on("click",i,this).on("dragstart",o,this).on("dragend",a,this).on("touchmove",o,this),this._markerGroup.addLayer(n)},_updatePrevNext:function(t,e){t&&(t._next=e),e&&(e._prev=t)},_getMiddleLatLng:function(t,e){var i=this._poly._map,o=i.project(t.getLatLng()),a=i.project(e.getLatLng());return i.unproject(o._add(a)._divideBy(2))}}),L.Polyline.addInitHook((function(){this.editing||(L.Edit.Poly&&(this.editing=new L.Edit.Poly(this),this.options.editable&&this.editing.enable()),this.on("add",(function(){this.editing&&this.editing.enabled()&&this.editing.addHooks()})),this.on("remove",(function(){this.editing&&this.editing.enabled()&&this.editing.removeHooks()})))})),L.Edit=L.Edit||{},L.Edit.SimpleShape=L.Handler.extend({options:{moveIcon:new L.DivIcon({iconSize:new L.Point(8,8),className:"leaflet-div-icon leaflet-editing-icon leaflet-edit-move"}),resizeIcon:new L.DivIcon({iconSize:new L.Point(8,8),className:"leaflet-div-icon leaflet-editing-icon leaflet-edit-resize"}),touchMoveIcon:new L.DivIcon({iconSize:new L.Point(20,20),className:"leaflet-div-icon leaflet-editing-icon leaflet-edit-move leaflet-touch-icon"}),touchResizeIcon:new L.DivIcon({iconSize:new L.Point(20,20),className:"leaflet-div-icon leaflet-editing-icon leaflet-edit-resize leaflet-touch-icon"})},initialize:function(t,e){L.Browser.touch&&(this.options.moveIcon=this.options.touchMoveIcon,this.options.resizeIcon=this.options.touchResizeIcon),this._shape=t,L.Util.setOptions(this,e)},addHooks:function(){var t=this._shape;this._shape._map&&(this._map=this._shape._map,t.setStyle(t.options.editing),t._map&&(this._map=t._map,this._markerGroup||this._initMarkers(),this._map.addLayer(this._markerGroup)))},removeHooks:function(){var t=this._shape;if(t.setStyle(t.options.original),t._map){this._unbindMarker(this._moveMarker);for(var e=0,i=this._resizeMarkers.length;e"+L.drawLocal.edit.handlers.edit.tooltip.text,subtext:L.drawLocal.draw.handlers.circle.radius+": "+L.GeometryUtil.readableDistance(radius,!0,this.options.feet,this.options.nautic)}),this._shape.setRadius(radius),this._map.fire(L.Draw.Event.EDITRESIZE,{layer:this._shape})}}),L.Circle.addInitHook((function(){L.Edit.Circle&&(this.editing=new L.Edit.Circle(this),this.options.editable&&this.editing.enable()),this.on("add",(function(){this.editing&&this.editing.enabled()&&this.editing.addHooks()})),this.on("remove",(function(){this.editing&&this.editing.enabled()&&this.editing.removeHooks()}))})),L.Map.mergeOptions({touchExtend:!0}),L.Map.TouchExtend=L.Handler.extend({initialize:function(t){this._map=t,this._container=t._container,this._pane=t._panes.overlayPane},addHooks:function(){L.DomEvent.on(this._container,"touchstart",this._onTouchStart,this),L.DomEvent.on(this._container,"touchend",this._onTouchEnd,this),L.DomEvent.on(this._container,"touchmove",this._onTouchMove,this),this._detectIE()?(L.DomEvent.on(this._container,"MSPointerDown",this._onTouchStart,this),L.DomEvent.on(this._container,"MSPointerUp",this._onTouchEnd,this),L.DomEvent.on(this._container,"MSPointerMove",this._onTouchMove,this),L.DomEvent.on(this._container,"MSPointerCancel",this._onTouchCancel,this)):(L.DomEvent.on(this._container,"touchcancel",this._onTouchCancel,this),L.DomEvent.on(this._container,"touchleave",this._onTouchLeave,this))},removeHooks:function(){L.DomEvent.off(this._container,"touchstart",this._onTouchStart),L.DomEvent.off(this._container,"touchend",this._onTouchEnd),L.DomEvent.off(this._container,"touchmove",this._onTouchMove),this._detectIE()?(L.DomEvent.off(this._container,"MSPointerDowm",this._onTouchStart),L.DomEvent.off(this._container,"MSPointerUp",this._onTouchEnd),L.DomEvent.off(this._container,"MSPointerMove",this._onTouchMove),L.DomEvent.off(this._container,"MSPointerCancel",this._onTouchCancel)):(L.DomEvent.off(this._container,"touchcancel",this._onTouchCancel),L.DomEvent.off(this._container,"touchleave",this._onTouchLeave))},_touchEvent:function(t,e){var i={};if(void 0!==t.touches){if(!t.touches.length)return;i=t.touches[0]}else{if("touch"!==t.pointerType)return;if(i=t,!this._filterClick(t))return}var o=this._map.mouseEventToContainerPoint(i),a=this._map.mouseEventToLayerPoint(i),s=this._map.layerPointToLatLng(a);this._map.fire(e,{latlng:s,layerPoint:a,containerPoint:o,pageX:i.pageX,pageY:i.pageY,originalEvent:t})},_filterClick:function(t){var e=t.timeStamp||t.originalEvent.timeStamp,i=L.DomEvent._lastClick&&e-L.DomEvent._lastClick;return i&&i>100&&i<500||t.target._simulatedClick&&!t._simulated?(L.DomEvent.stop(t),!1):(L.DomEvent._lastClick=e,!0)},_onTouchStart:function(t){this._map._loaded&&this._touchEvent(t,"touchstart")},_onTouchEnd:function(t){this._map._loaded&&this._touchEvent(t,"touchend")},_onTouchCancel:function(t){if(this._map._loaded){var e="touchcancel";this._detectIE()&&(e="pointercancel"),this._touchEvent(t,e)}},_onTouchLeave:function(t){this._map._loaded&&this._touchEvent(t,"touchleave")},_onTouchMove:function(t){this._map._loaded&&this._touchEvent(t,"touchmove")},_detectIE:function(){var e=t.navigator.userAgent,i=e.indexOf("MSIE ");if(i>0)return parseInt(e.substring(i+5,e.indexOf(".",i)),10);if(e.indexOf("Trident/")>0){var o=e.indexOf("rv:");return parseInt(e.substring(o+3,e.indexOf(".",o)),10)}var a=e.indexOf("Edge/");return a>0&&parseInt(e.substring(a+5,e.indexOf(".",a)),10)}}),L.Map.addInitHook("addHandler","touchExtend",L.Map.TouchExtend),L.Marker.Touch=L.Marker.extend({_initInteraction:function(){return this.addInteractiveTarget?L.Marker.prototype._initInteraction.apply(this):this._initInteractionLegacy()},_initInteractionLegacy:function(){if(this.options.clickable){var t=this._icon,e=["dblclick","mousedown","mouseover","mouseout","contextmenu","touchstart","touchend","touchmove"];this._detectIE?e.concat(["MSPointerDown","MSPointerUp","MSPointerMove","MSPointerCancel"]):e.concat(["touchcancel"]),L.DomUtil.addClass(t,"leaflet-clickable"),L.DomEvent.on(t,"click",this._onMouseClick,this),L.DomEvent.on(t,"keypress",this._onKeyPress,this);for(var i=0;i0)return parseInt(e.substring(i+5,e.indexOf(".",i)),10);if(e.indexOf("Trident/")>0){var o=e.indexOf("rv:");return parseInt(e.substring(o+3,e.indexOf(".",o)),10)}var a=e.indexOf("Edge/");return a>0&&parseInt(e.substring(a+5,e.indexOf(".",a)),10)}}),L.LatLngUtil={cloneLatLngs:function(t){for(var e=[],i=0,o=t.length;i2){for(var n=0;n1&&(i=i+n+r[1])}return i},readableArea:function(e,i,o){var a,s;return o=L.Util.extend({},t,o),i?(s=["ha","m"],type=typeof i,"string"===type?s=[i]:"boolean"!==type&&(s=i),a=e>=1e6&&-1!==s.indexOf("km")?L.GeometryUtil.formattedNumber(1e-6*e,o.km)+" km²":e>=1e4&&-1!==s.indexOf("ha")?L.GeometryUtil.formattedNumber(1e-4*e,o.ha)+" ha":L.GeometryUtil.formattedNumber(e,o.m)+" m²"):a=(e/=.836127)>=3097600?L.GeometryUtil.formattedNumber(e/3097600,o.mi)+" mi²":e>=4840?L.GeometryUtil.formattedNumber(e/4840,o.ac)+" acres":L.GeometryUtil.formattedNumber(e,o.yd)+" yd²",a},readableDistance:function(e,i,o,a,s){var n;switch(s=L.Util.extend({},t,s),i?"string"==typeof i?i:"metric":o?"feet":a?"nauticalMile":"yards"){case"metric":n=e>1e3?L.GeometryUtil.formattedNumber(e/1e3,s.km)+" km":L.GeometryUtil.formattedNumber(e,s.m)+" m";break;case"feet":e*=3.28083,n=L.GeometryUtil.formattedNumber(e,s.ft)+" ft";break;case"nauticalMile":e*=.53996,n=L.GeometryUtil.formattedNumber(e/1e3,s.nm)+" nm";break;default:n=(e*=1.09361)>1760?L.GeometryUtil.formattedNumber(e/1760,s.mi)+" miles":L.GeometryUtil.formattedNumber(e,s.yd)+" yd"}return n},isVersion07x:function(){var t=L.version.split(".");return 0===parseInt(t[0],10)&&7===parseInt(t[1],10)}})}(),L.Util.extend(L.LineUtil,{segmentsIntersect:function(t,e,i,o){return this._checkCounterclockwise(t,i,o)!==this._checkCounterclockwise(e,i,o)&&this._checkCounterclockwise(t,e,i)!==this._checkCounterclockwise(t,e,o)},_checkCounterclockwise:function(t,e,i){return(i.y-t.y)*(e.x-t.x)>(e.y-t.y)*(i.x-t.x)}}),L.Polyline.include({intersects:function(){var t,e,i,o=this._getProjectedPoints(),a=o?o.length:0;if(this._tooFewPointsForIntersection())return!1;for(t=a-1;t>=3;t--)if(e=o[t-1],i=o[t],this._lineSegmentsIntersectsRange(e,i,t-2))return!0;return!1},newLatLngIntersects:function(t,e){return!!this._map&&this.newPointIntersects(this._map.latLngToLayerPoint(t),e)},newPointIntersects:function(t,e){var i=this._getProjectedPoints(),o=i?i.length:0,a=i?i[o-1]:null,s=o-2;return!this._tooFewPointsForIntersection(1)&&this._lineSegmentsIntersectsRange(a,t,s,e?1:0)},_tooFewPointsForIntersection:function(t){var e=this._getProjectedPoints(),i=e?e.length:0;return!e||(i+=t||0)<=3},_lineSegmentsIntersectsRange:function(t,e,i,o){var a,s,n=this._getProjectedPoints();o=o||0;for(var r=i;r>o;r--)if(a=n[r-1],s=n[r],L.LineUtil.segmentsIntersect(t,e,a,s))return!0;return!1},_getProjectedPoints:function(){if(!this._defaultShape)return this._originalPoints;for(var t=[],e=this._defaultShape(),i=0;i=2?L.Toolbar.include(L.Evented.prototype):L.Toolbar.include(L.Mixin.Events)},enabled:function(){return null!==this._activeMode},disable:function(){this.enabled()&&this._activeMode.handler.disable()},addToolbar:function(t){var e,i=L.DomUtil.create("div","leaflet-draw-section"),o=0,a=this._toolbarClass||"",s=this.getModeHandlers(t);for(this._toolbarContainer=L.DomUtil.create("div","leaflet-draw-toolbar leaflet-bar"),this._map=t,e=0;e0&&this._singleLineLabel&&(L.DomUtil.removeClass(this._container,"leaflet-draw-tooltip-single"),this._singleLineLabel=!1):(L.DomUtil.addClass(this._container,"leaflet-draw-tooltip-single"),this._singleLineLabel=!0),this._container.innerHTML=(t.subtext.length>0?''+t.subtext+"
":"")+""+t.text+"",t.text||t.subtext?(this._visible=!0,this._container.style.visibility="inherit"):(this._visible=!1,this._container.style.visibility="hidden"),this):this},updatePosition:function(t){var e=this._map.latLngToLayerPoint(t),i=this._container;return this._container&&(this._visible&&(i.style.visibility="inherit"),L.DomUtil.setPosition(i,e)),this},showAsError:function(){return this._container&&L.DomUtil.addClass(this._container,"leaflet-error-draw-tooltip"),this},removeError:function(){return this._container&&L.DomUtil.removeClass(this._container,"leaflet-error-draw-tooltip"),this},_onMouseOut:function(){this._container&&(this._container.style.visibility="hidden")}}),L.DrawToolbar=L.Toolbar.extend({statics:{TYPE:"draw"},options:{polyline:{},polygon:{},rectangle:{},circle:{},marker:{},circlemarker:{}},initialize:function(t){for(var e in this.options)this.options.hasOwnProperty(e)&&t[e]&&(t[e]=L.extend({},this.options[e],t[e]));this._toolbarClass="leaflet-draw-draw",L.Toolbar.prototype.initialize.call(this,t)},getModeHandlers:function(t){return[{enabled:this.options.polyline,handler:new L.Draw.Polyline(t,this.options.polyline),title:L.drawLocal.draw.toolbar.buttons.polyline},{enabled:this.options.polygon,handler:new L.Draw.Polygon(t,this.options.polygon),title:L.drawLocal.draw.toolbar.buttons.polygon},{enabled:this.options.rectangle,handler:new L.Draw.Rectangle(t,this.options.rectangle),title:L.drawLocal.draw.toolbar.buttons.rectangle},{enabled:this.options.circle,handler:new L.Draw.Circle(t,this.options.circle),title:L.drawLocal.draw.toolbar.buttons.circle},{enabled:this.options.marker,handler:new L.Draw.Marker(t,this.options.marker),title:L.drawLocal.draw.toolbar.buttons.marker},{enabled:this.options.circlemarker,handler:new L.Draw.CircleMarker(t,this.options.circlemarker),title:L.drawLocal.draw.toolbar.buttons.circlemarker}]},getActions:function(t){return[{enabled:t.completeShape,title:L.drawLocal.draw.toolbar.finish.title,text:L.drawLocal.draw.toolbar.finish.text,callback:t.completeShape,context:t},{enabled:t.deleteLastVertex,title:L.drawLocal.draw.toolbar.undo.title,text:L.drawLocal.draw.toolbar.undo.text,callback:t.deleteLastVertex,context:t},{title:L.drawLocal.draw.toolbar.actions.title,text:L.drawLocal.draw.toolbar.actions.text,callback:this.disable,context:this}]},setOptions:function(t){for(var e in L.setOptions(this,t),this._modes)this._modes.hasOwnProperty(e)&&t.hasOwnProperty(e)&&this._modes[e].handler.setOptions(t[e])}}),L.EditToolbar=L.Toolbar.extend({statics:{TYPE:"edit"},options:{edit:{selectedPathOptions:{dashArray:"10, 10",fill:!0,fillColor:"#fe57a1",fillOpacity:.1,maintainColor:!1}},remove:{},poly:null,featureGroup:null},initialize:function(t){t.edit&&(void 0===t.edit.selectedPathOptions&&(t.edit.selectedPathOptions=this.options.edit.selectedPathOptions),t.edit.selectedPathOptions=L.extend({},this.options.edit.selectedPathOptions,t.edit.selectedPathOptions)),t.remove&&(t.remove=L.extend({},this.options.remove,t.remove)),t.poly&&(t.poly=L.extend({},this.options.poly,t.poly)),this._toolbarClass="leaflet-draw-edit",L.Toolbar.prototype.initialize.call(this,t),this._selectedFeatureCount=0},getModeHandlers:function(t){var e=this.options.featureGroup;return[{enabled:this.options.edit,handler:new L.EditToolbar.Edit(t,{featureGroup:e,selectedPathOptions:this.options.edit.selectedPathOptions,poly:this.options.poly}),title:L.drawLocal.edit.toolbar.buttons.edit},{enabled:this.options.remove,handler:new L.EditToolbar.Delete(t,{featureGroup:e}),title:L.drawLocal.edit.toolbar.buttons.remove}]},getActions:function(t){var e=[{title:L.drawLocal.edit.toolbar.actions.save.title,text:L.drawLocal.edit.toolbar.actions.save.text,callback:this._save,context:this},{title:L.drawLocal.edit.toolbar.actions.cancel.title,text:L.drawLocal.edit.toolbar.actions.cancel.text,callback:this.disable,context:this}];return t.removeAllLayers&&e.push({title:L.drawLocal.edit.toolbar.actions.clearAll.title,text:L.drawLocal.edit.toolbar.actions.clearAll.text,callback:this._clearAllLayers,context:this}),e},addToolbar:function(t){var e=L.Toolbar.prototype.addToolbar.call(this,t);return this._checkDisabled(),this.options.featureGroup.on("layeradd layerremove",this._checkDisabled,this),e},removeToolbar:function(){this.options.featureGroup.off("layeradd layerremove",this._checkDisabled,this),L.Toolbar.prototype.removeToolbar.call(this)},disable:function(){this.enabled()&&(this._activeMode.handler.revertLayers(),L.Toolbar.prototype.disable.call(this))},_save:function(){this._activeMode.handler.save(),this._activeMode&&this._activeMode.handler.disable()},_clearAllLayers:function(){this._activeMode.handler.removeAllLayers(),this._activeMode&&this._activeMode.handler.disable()},_checkDisabled:function(){var t,e=0!==this.options.featureGroup.getLayers().length;this.options.edit&&(t=this._modes[L.EditToolbar.Edit.TYPE].button,e?L.DomUtil.removeClass(t,"leaflet-disabled"):L.DomUtil.addClass(t,"leaflet-disabled"),t.setAttribute("title",e?L.drawLocal.edit.toolbar.buttons.edit:L.drawLocal.edit.toolbar.buttons.editDisabled)),this.options.remove&&(t=this._modes[L.EditToolbar.Delete.TYPE].button,e?L.DomUtil.removeClass(t,"leaflet-disabled"):L.DomUtil.addClass(t,"leaflet-disabled"),t.setAttribute("title",e?L.drawLocal.edit.toolbar.buttons.remove:L.drawLocal.edit.toolbar.buttons.removeDisabled))}}),L.EditToolbar.Edit=L.Handler.extend({statics:{TYPE:"edit"},initialize:function(t,e){if(L.Handler.prototype.initialize.call(this,t),L.setOptions(this,e),this._featureGroup=e.featureGroup,!(this._featureGroup instanceof L.FeatureGroup))throw new Error("options.featureGroup must be a L.FeatureGroup");this._uneditedLayerProps={},this.type=L.EditToolbar.Edit.TYPE;var i=L.version.split(".");1===parseInt(i[0],10)&&parseInt(i[1],10)>=2?L.EditToolbar.Edit.include(L.Evented.prototype):L.EditToolbar.Edit.include(L.Mixin.Events)},enable:function(){!this._enabled&&this._hasAvailableLayers()&&(this.fire("enabled",{handler:this.type}),this._map.fire(L.Draw.Event.EDITSTART,{handler:this.type}),L.Handler.prototype.enable.call(this),this._featureGroup.on("layeradd",this._enableLayerEdit,this).on("layerremove",this._disableLayerEdit,this))},disable:function(){this._enabled&&(this._featureGroup.off("layeradd",this._enableLayerEdit,this).off("layerremove",this._disableLayerEdit,this),L.Handler.prototype.disable.call(this),this._map.fire(L.Draw.Event.EDITSTOP,{handler:this.type}),this.fire("disabled",{handler:this.type}))},addHooks:function(){var t=this._map;t&&(t.getContainer().focus(),this._featureGroup.eachLayer(this._enableLayerEdit,this),this._tooltip=new L.Draw.Tooltip(this._map),this._tooltip.updateContent({text:L.drawLocal.edit.handlers.edit.tooltip.text,subtext:L.drawLocal.edit.handlers.edit.tooltip.subtext}),t._editTooltip=this._tooltip,this._updateTooltip(),this._map.on("mousemove",this._onMouseMove,this).on("touchmove",this._onMouseMove,this).on("MSPointerMove",this._onMouseMove,this).on(L.Draw.Event.EDITVERTEX,this._updateTooltip,this))},removeHooks:function(){this._map&&(this._featureGroup.eachLayer(this._disableLayerEdit,this),this._uneditedLayerProps={},this._tooltip.dispose(),this._tooltip=null,this._map.off("mousemove",this._onMouseMove,this).off("touchmove",this._onMouseMove,this).off("MSPointerMove",this._onMouseMove,this).off(L.Draw.Event.EDITVERTEX,this._updateTooltip,this))},revertLayers:function(){this._featureGroup.eachLayer((function(t){this._revertLayer(t)}),this)},save:function(){var t=new L.LayerGroup;this._featureGroup.eachLayer((function(e){e.edited&&(t.addLayer(e),e.edited=!1)})),this._map.fire(L.Draw.Event.EDITED,{layers:t})},_backupLayer:function(t){var e=L.Util.stamp(t);this._uneditedLayerProps[e]||(t instanceof L.Polyline||t instanceof L.Polygon||t instanceof L.Rectangle?this._uneditedLayerProps[e]={latlngs:L.LatLngUtil.cloneLatLngs(t.getLatLngs())}:t instanceof L.Circle?this._uneditedLayerProps[e]={latlng:L.LatLngUtil.cloneLatLng(t.getLatLng()),radius:t.getRadius()}:(t instanceof L.Marker||t instanceof L.CircleMarker)&&(this._uneditedLayerProps[e]={latlng:L.LatLngUtil.cloneLatLng(t.getLatLng())}))},_getTooltipText:function(){return{text:L.drawLocal.edit.handlers.edit.tooltip.text,subtext:L.drawLocal.edit.handlers.edit.tooltip.subtext}},_updateTooltip:function(){this._tooltip.updateContent(this._getTooltipText())},_revertLayer:function(t){var e=L.Util.stamp(t);t.edited=!1,this._uneditedLayerProps.hasOwnProperty(e)&&(t instanceof L.Polyline||t instanceof L.Polygon||t instanceof L.Rectangle?t.setLatLngs(this._uneditedLayerProps[e].latlngs):t instanceof L.Circle?(t.setLatLng(this._uneditedLayerProps[e].latlng),t.setRadius(this._uneditedLayerProps[e].radius)):(t instanceof L.Marker||t instanceof L.CircleMarker)&&t.setLatLng(this._uneditedLayerProps[e].latlng),t.fire("revert-edited",{layer:t}))},_enableLayerEdit:function(t){var e,i,o=t.layer||t.target||t;this._backupLayer(o),this.options.poly&&(i=L.Util.extend({},this.options.poly),o.options.poly=i),this.options.selectedPathOptions&&((e=L.Util.extend({},this.options.selectedPathOptions)).maintainColor&&(e.color=o.options.color,e.fillColor=o.options.fillColor),o.options.original=L.extend({},o.options),o.options.editing=e),o instanceof L.Marker?(o.editing&&o.editing.enable(),o.dragging.enable(),o.on("dragend",this._onMarkerDragEnd).on("touchmove",this._onTouchMove,this).on("MSPointerMove",this._onTouchMove,this).on("touchend",this._onMarkerDragEnd,this).on("MSPointerUp",this._onMarkerDragEnd,this)):o.editing.enable()},_disableLayerEdit:function(t){var e=t.layer||t.target||t;e.edited=!1,e.editing&&e.editing.disable(),delete e.options.editing,delete e.options.original,this._selectedPathOptions&&(e instanceof L.Marker?this._toggleMarkerHighlight(e):(e.setStyle(e.options.previousOptions),delete e.options.previousOptions)),e instanceof L.Marker?(e.dragging.disable(),e.off("dragend",this._onMarkerDragEnd,this).off("touchmove",this._onTouchMove,this).off("MSPointerMove",this._onTouchMove,this).off("touchend",this._onMarkerDragEnd,this).off("MSPointerUp",this._onMarkerDragEnd,this)):e.editing.disable()},_onMouseMove:function(t){this._tooltip.updatePosition(t.latlng)},_onMarkerDragEnd:function(t){var e=t.target;e.edited=!0,this._map.fire(L.Draw.Event.EDITMOVE,{layer:e})},_onTouchMove:function(t){var e=t.originalEvent.changedTouches[0],i=this._map.mouseEventToLayerPoint(e),o=this._map.layerPointToLatLng(i);t.target.setLatLng(o)},_hasAvailableLayers:function(){return 0!==this._featureGroup.getLayers().length}}),L.EditToolbar.Delete=L.Handler.extend({statics:{TYPE:"remove"},initialize:function(t,e){if(L.Handler.prototype.initialize.call(this,t),L.Util.setOptions(this,e),this._deletableLayers=this.options.featureGroup,!(this._deletableLayers instanceof L.FeatureGroup))throw new Error("options.featureGroup must be a L.FeatureGroup");this.type=L.EditToolbar.Delete.TYPE;var i=L.version.split(".");1===parseInt(i[0],10)&&parseInt(i[1],10)>=2?L.EditToolbar.Delete.include(L.Evented.prototype):L.EditToolbar.Delete.include(L.Mixin.Events)},enable:function(){!this._enabled&&this._hasAvailableLayers()&&(this.fire("enabled",{handler:this.type}),this._map.fire(L.Draw.Event.DELETESTART,{handler:this.type}),L.Handler.prototype.enable.call(this),this._deletableLayers.on("layeradd",this._enableLayerDelete,this).on("layerremove",this._disableLayerDelete,this))},disable:function(){this._enabled&&(this._deletableLayers.off("layeradd",this._enableLayerDelete,this).off("layerremove",this._disableLayerDelete,this),L.Handler.prototype.disable.call(this),this._map.fire(L.Draw.Event.DELETESTOP,{handler:this.type}),this.fire("disabled",{handler:this.type}))},addHooks:function(){var t=this._map;t&&(t.getContainer().focus(),this._deletableLayers.eachLayer(this._enableLayerDelete,this),this._deletedLayers=new L.LayerGroup,this._tooltip=new L.Draw.Tooltip(this._map),this._tooltip.updateContent({text:L.drawLocal.edit.handlers.remove.tooltip.text}),this._map.on("mousemove",this._onMouseMove,this))},removeHooks:function(){this._map&&(this._deletableLayers.eachLayer(this._disableLayerDelete,this),this._deletedLayers=null,this._tooltip.dispose(),this._tooltip=null,this._map.off("mousemove",this._onMouseMove,this))},revertLayers:function(){this._deletedLayers.eachLayer((function(t){this._deletableLayers.addLayer(t),t.fire("revert-deleted",{layer:t})}),this)},save:function(){this._map.fire(L.Draw.Event.DELETED,{layers:this._deletedLayers})},removeAllLayers:function(){this._deletableLayers.eachLayer((function(t){this._removeLayer({layer:t})}),this),this.save()},_enableLayerDelete:function(t){(t.layer||t.target||t).on("click",this._removeLayer,this)},_disableLayerDelete:function(t){var e=t.layer||t.target||t;e.off("click",this._removeLayer,this),this._deletedLayers.removeLayer(e)},_removeLayer:function(t){var e=t.layer||t.target||t;this._deletableLayers.removeLayer(e),this._deletedLayers.addLayer(e),e.fire("deleted")},_onMouseMove:function(t){this._tooltip.updatePosition(t.latlng)},_hasAvailableLayers:function(){return 0!==this._deletableLayers.getLayers().length}})},443:()=>{L.Edit.Circle.include({addHooks:function(){this._shape._map&&(this._map=this._shape._map,this._shape.setStyle(this._shape.options.editing),this._markerGroup||(this._enableDragging(),this._initMarkers()),this._shape._map.addLayer(this._markerGroup))},removeHooks:function(){if(this._shape.setStyle(this._shape.options.original),this._shape._map){for(var t=0,e=this._resizeMarkers.length;t{L.Edit.PolyVerticesEdit.include({__createMarker:L.Edit.PolyVerticesEdit.prototype._createMarker,__removeMarker:L.Edit.PolyVerticesEdit.prototype._removeMarker,addHooks:function(){var t=this._poly;t instanceof L.Polygon||(t.options.fill=!1,t.options.editing&&(t.options.editing.fill=!1)),t.setStyle(t.options.editing),this._poly._map&&(this._map=this._poly._map,this._markerGroup||(this._enableDragging(),this._initMarkers(),this._createMoveMarker()),this._poly._map.addLayer(this._markerGroup))},_createMoveMarker:function(){L.EditToolbar.Edit.MOVE_MARKERS&&this._poly instanceof L.Polygon&&(this._moveMarker=new L.Marker(this._getShapeCenter(),{icon:this.options.moveIcon}),this._moveMarker.on("mousedown",this._delegateToShape,this),this._markerGroup.addLayer(this._moveMarker))},_delegateToShape:function(t){var e=this._shape||this._poly,i=t.target;e.fire("mousedown",L.Util.extend(t,{containerPoint:L.DomUtil.getPosition(i._icon).add(e._map._getMapPanePos())}))},_getShapeCenter:function(){return this._poly.getCenter()},removeHooks:function(){var t=this._poly;t.options.original&&t.setStyle(t.options.original),this._poly._map&&(this._poly._map.removeLayer(this._markerGroup),this._disableDragging(),delete this._markerGroup,delete this._markers),this._map=null},_enableDragging:function(){this._poly.dragging||(this._poly.dragging=new L.Handler.PathDrag(this._poly)),this._poly.dragging.enable(),this._poly.on("dragstart",this._onStartDragFeature,this).on("dragend",this._onStopDragFeature,this)},_disableDragging:function(){this._poly.dragging.disable(),this._poly.off("dragstart",this._onStartDragFeature,this).off("dragend",this._onStopDragFeature,this)},_onStartDragFeature:function(t){this._poly._map.removeLayer(this._markerGroup),this._poly.fire("editstart")},_onStopDragFeature:function(t){var e=this._poly._latlngs;L.Util.isArray(e[0])||(e=[e]);for(var i=0,o=e.length;i{L.Edit.Rectangle.include({addHooks:function(){this._shape._map&&(this._map=this._shape._map,this._shape.setStyle(this._shape.options.editing),this._markerGroup||(this._enableDragging(),this._initMarkers()),this._shape._map.addLayer(this._markerGroup))},removeHooks:function(){this._shape.setStyle(this._shape.options.original),this._shape._map&&(this._shape._map.removeLayer(this._markerGroup),this._disableDragging(),delete this._markerGroup,delete this._markers),this._map=null},_resize:function(t){this._shape.setBounds(L.latLngBounds(t,this._oppositeCorner)),this._updateMoveMarker(),this._shape._map.fire("draw:editresize",{layer:this._shape})},_onMarkerDragEnd:function(t){this._toggleCornerMarkers(1),this._repositionCornerMarkers(),L.Edit.SimpleShape.prototype._onMarkerDragEnd.call(this,t)},_enableDragging:function(){this._shape.dragging||(this._shape.dragging=new L.Handler.PathDrag(this._shape)),this._shape.dragging.enable(),this._shape.on("dragstart",this._onStartDragFeature,this).on("dragend",this._onStopDragFeature,this)},_disableDragging:function(){this._shape.dragging.disable(),this._shape.off("dragstart",this._onStartDragFeature,this).off("dragend",this._onStopDragFeature,this)},_onStartDragFeature:function(){this._shape._map.removeLayer(this._markerGroup),this._shape.fire("editstart")},_onStopDragFeature:function(){for(var t=this._shape,e=0,i=t._latlngs.length;e{L.Edit.SimpleShape.include({_updateMoveMarker:function(){this._moveMarker&&this._moveMarker.setLatLng(this._getShapeCenter())},_getShapeCenter:function(){return this._shape.getBounds().getCenter()},_createMoveMarker:function(){L.EditToolbar.Edit.MOVE_MARKERS&&(this._moveMarker=this._createMarker(this._getShapeCenter(),this.options.moveIcon))}}),L.Edit.SimpleShape.mergeOptions({moveMarker:!1})},650:()=>{ +/** + * Drag feature functionality for Leaflet.draw + * @preserve + * @license MIT + * @author Alexander Milevski + */ +L.EditToolbar.Edit.MOVE_MARKERS=!1,L.EditToolbar.Edit.include({initialize:function(t,e){e&&e.selectedPathOptions&&(L.EditToolbar.Edit.MOVE_MARKERS=!!e.selectedPathOptions.moveMarkers),this._initialize(t,e)},_initialize:L.EditToolbar.Edit.prototype.initialize})},525:t=>{"use strict";t.exports=L},980:(t,e,i)=>{"use strict";const o=i(525);function a(){return!0}o.SVG.include({_resetTransformPath:function(t){t._path.setAttributeNS(null,"transform","")},transformPath:function(t,e){t._path.setAttributeNS(null,"transform","matrix("+e.join(" ")+")")}}),o.SVG.include(o.Browser.vml?{_resetTransformPath:function(t){t._skew&&(t._skew.on=!1,t._path.removeChild(t._skew),t._skew=null)},transformPath:function(t,e){let i=t._skew;i||(i=o.SVG.create("skew"),t._path.appendChild(i),i.style.behavior="url(#default#VML)",t._skew=i);const a=e[0].toFixed(8)+" "+e[1].toFixed(8)+" "+e[2].toFixed(8)+" "+e[3].toFixed(8)+" 0 0",s=Math.floor(e[4]).toFixed()+", "+Math.floor(e[5]).toFixed(),n=this._path.style;let r=parseFloat(n.left),h=parseFloat(n.top),l=parseFloat(n.width),d=parseFloat(n.height);isNaN(r)&&(r=0),isNaN(h)&&(h=0),(isNaN(l)||!l)&&(l=1),(isNaN(d)||!d)&&(d=1);const _=(-r/l-.5).toFixed(8)+" "+(-h/d-.5).toFixed(8);i.on="f",i.matrix=a,i.origin=_,i.offset=s,i.on=!0}}:{}),o.Canvas.include({_resetTransformPath:function(t){this._containerCopy&&(delete this._containerCopy,t._containsPoint_&&(t._containsPoint=t._containsPoint_,delete t._containsPoint_,this._requestRedraw(t)))},transformPath:function(t,e){let i=this._containerCopy;const s=this._ctx;let n;const r=o.Browser.retina?2:1,h=this._bounds,l=h.getSize(),d=h.min;i||(i=this._containerCopy=document.createElement("canvas"),n=i.getContext("2d"),i.width=r*l.x,i.height=r*l.y,this._removePath(t),this._redraw(),n.translate(r*h.min.x,r*h.min.y),n.drawImage(this._container,0,0),this._initPath(t),t._containsPoint_=t._containsPoint,t._containsPoint=a),s.save(),s.clearRect(d.x,d.y,l.x*r,l.y*r),s.setTransform(1,0,0,1,0,0),s.restore(),s.save(),s.drawImage(this._containerCopy,0,0,l.x,l.y),s.transform.apply(s,e),this._drawing=!0,t._updatePath(),this._drawing=!1,s.restore()}}), +/** + * Leaflet vector features drag functionality + * @author Alexander Milevski + * @preserve + */ +o.Path.include({_transform:function(t){return this._renderer&&(t?this._renderer.transformPath(this,t):(this._renderer._resetTransformPath(this),this._update())),this},_onMouseClick:function(t){this.dragging&&this.dragging.moved()||this._map.dragging&&this._map.dragging.moved()||this._fireMouseEvent(t)}});const s={mousedown:"mouseup",touchstart:"touchend",pointerdown:"touchend",MSPointerDown:"touchend"},n={mousedown:"mousemove",touchstart:"touchmove",pointerdown:"touchmove",MSPointerDown:"touchmove"};function r(t,e){const i=t.x-e.x,o=t.y-e.y;return Math.sqrt(i*i+o*o)}o.Handler.PathDrag=o.Handler.extend({statics:{DRAGGING_CLS:"leaflet-path-draggable"},initialize:function(t){this._path=t,this._matrix=null,this._startPoint=null,this._dragStartPoint=null,this._mapDraggingWasEnabled=!1,this._path._dragMoved=!1},addHooks:function(){this._path.on("mousedown",this._onDragStart,this),this._path.options.className=this._path.options.className?this._path.options.className+" "+o.Handler.PathDrag.DRAGGING_CLS:o.Handler.PathDrag.DRAGGING_CLS,this._path._path&&o.DomUtil.addClass(this._path._path,o.Handler.PathDrag.DRAGGING_CLS)},removeHooks:function(){this._path.off("mousedown",this._onDragStart,this),this._path.options.className=this._path.options.className.replace(new RegExp("\\s+"+o.Handler.PathDrag.DRAGGING_CLS),""),this._path._path&&o.DomUtil.removeClass(this._path._path,o.Handler.PathDrag.DRAGGING_CLS)},moved:function(){return this._path._dragMoved},_onDragStart:function(t){const e=t.originalEvent._simulated?"touchstart":t.originalEvent.type;this._mapDraggingWasEnabled=!1,this._startPoint=t.containerPoint.clone(),this._dragStartPoint=t.containerPoint.clone(),this._matrix=[1,0,0,1,0,0],o.DomEvent.stop(t.originalEvent),o.DomUtil.addClass(this._path._renderer._container,"leaflet-interactive"),o.DomEvent.on(document,n[e],this._onDrag,this).on(document,s[e],this._onDragEnd,this),this._path._map.dragging.enabled()&&(this._path._map.dragging.disable(),this._mapDraggingWasEnabled=!0),this._path._dragMoved=!1,this._path._popup&&this._path._popup.close(),this._replaceCoordGetters(t)},_onDrag:function(t){o.DomEvent.stop(t);const e=t.touches&&t.touches.length>=1?t.touches[0]:t,i=this._path._map.mouseEventToContainerPoint(e);if("touchmove"===t.type&&!this._path._dragMoved&&this._dragStartPoint.distanceTo(i)<=this._path._map.options.tapTolerance)return;const a=i.x,s=i.y,n=a-this._startPoint.x,r=s-this._startPoint.y;(n||r)&&(this._path._dragMoved||(this._path._dragMoved=!0,this._path.options.interactive=!1,this._path._map.dragging._draggable._moved=!0,this._path.fire("dragstart",t),this._path.bringToFront()),this._matrix[4]+=n,this._matrix[5]+=r,this._startPoint.x=a,this._startPoint.y=s,this._path.fire("predrag",t),this._path._transform(this._matrix),this._path.fire("drag",t))},_onDragEnd:function(t){const e=this._path._map.mouseEventToContainerPoint(t),i=this.moved();if(i&&(this._transformPoints(this._matrix),this._path._updatePath(),this._path._project(),this._path._transform(null),o.DomEvent.stop(t)),o.DomEvent.off(document,"mousemove touchmove",this._onDrag,this),o.DomEvent.off(document,"mouseup touchend",this._onDragEnd,this),this._restoreCoordGetters(),i){this._path.fire("dragend",{distance:r(this._dragStartPoint,e)});const t=this._path._containsPoint;this._path._containsPoint=o.Util.falseFn,o.Util.requestAnimFrame((function(){this._path._dragMoved=!1,this._path.options.interactive=!0,this._path._containsPoint=t}),this)}this._mapDraggingWasEnabled&&this._path._map.dragging.enable()},_transformPoints:function(t,e){const i=this._path,a=L.point(t[4],t[5]),s=i._map.options.crs,n=s.transformation,r=s.scale(i._map.getZoom()),h=s.projection,l=n.untransform(a,r).subtract(n.untransform(o.point(0,0),r)),d=!e;if(i._bounds=new o.LatLngBounds,i._point)e=h.unproject(h.project(i._latlng)._add(l)),d&&(i._latlng=e,i._point._add(a));else if(i._rings||i._parts){const t=i._rings||i._parts;let s=i._latlngs;e=e||s,o.Util.isArray(s[0])||(s=[s],e=[e]);for(let o=0,n=t.length;o + */ + +/** + * Leaflet vector features drag functionality + * @author Alexander Milevski + * @preserve + */ diff --git a/inst/htmlwidgets/build/lfx-draw/7ea3a6d428136b87ab95.png b/inst/htmlwidgets/build/lfx-draw/7ea3a6d428136b87ab95.png new file mode 100644 index 0000000000000000000000000000000000000000..c45231aff86b1344333414cdfa7a2a74d38d5304 GIT binary patch literal 3581 zcmb_f`#;nBAOB2clxyUYb<>4Ynk1K8OH(Z&mzm3$Tyx3X$HJB_R+xiO7)ip%jA&_M zl!g=0DI?9L49i_)E<@k-{pI@)d|!{p`}%mjuaEcZ`Fy_bU9q_!y?ys~005+s7ZLUV zAm%Kpn@EU@^7{ODOi{5l!UE|iA+k6LAF`+|8G6wrLge4~CqXPU^I}Aus*#q?kq$V& zNc`1sUjUEC!vjKsBD}AL`oeMH{&}+|yG7mvB;u@NO#aLuF$Lxy3!R_SjbJ6a7tRHv zBz1~*mIVgf1VOjVip+7Z;f4r4FSwLrXXkj}z+=1B1NW~XOT>;Iw+;<}CA@r#lE0(? zd3i0IzewW74Wp>ECohsvqkk$E}Ms%B2GGcE&2IsxA>@@3X3m zDG^w?t(hOXlBT_cpS&2Dn|fFVwPcdu>G`b>)^_?a2;I}xzh~>4em_0}F`+6N=b))5 zLvEh|>p9CCdt9b&^Nrhjjm187ePKOPzjf=DLC&QTs(QE@DG$KNeuI&6ASNZCEEYc) z^=4&drTg>cr(@e`G~Lz)@iTjygDgj_!8H%>P9nM8^sHQdKsdqY=A2N?F-n=V2k=N+ zfT{pCVlg&|RwE;$y}1t`8uq<7bbQgI+mNCQf^{R4P0N-*8sv-e-K#wf)44&Bkv)x} z-`}1E>QMTXP8^-?#Rkp$tp3h{vasOc>3I`eGI`o2V^r3XUao;FTaV2oHxysdT<=-l zAV$OZEDRid@nB)L+Ah%PGbix1ApRKl<#nz!eQMlpHZo`z5XX)hB({%*YvdT{-Uudg z|FL-Ow6wHTIXgSMDMxC8REOLg)GnKxotT&~b#QRV$vM#IC?zAa>6dZn24tYVnVyz? zsRVDn(0*_jOKAvJ-MxD<_t7J!LNnKLR2t~j3T;nTuhEl8 zq+Wdb5#@StSLM2@pHQEk#jOkc{LH#SKlsbMi#)ToX5s;ufI`UB(}f$1pgvZdV7h-v zz2@Jrdp2$H9E>$~WyDvk zt*tp#xz&YWP<4(?*7(wbLLiYg>jn}mgiijyQ;=2i2Uj81L3JtlR_7o)yz zH)gs}C?qm!YZoO*2Jn&UCtw06YS4DCQRI&s0b_pLOIp=OH71JzkH&X$8vcHMA!cza z^MB>VmXx0ik5cSYLSKMfMtLklUNOh{e8;5^c8VLguc!6Bu3amua6D>ybIo{I0huvi zcKY<{xo?yCFrYi>>WXq%S?YUIgMLUlY^>Sz3K2r6EUfqcG`R2H*Ju0k6T5X8Wf(};_CV?wb+smDvO#Pu`cr_B=a3D zCk$?wcQ3w7ZodT(K(3?SKFfb+Ix;6dsd$?K-*h?4AfbsoAnVy>C)pQe>uB3hYcxFB z6gD?)^DsAU=Jm-H7O@oEJVr{UPYK4tnf}UZ#r7%tARkLX1v|w?wo`)j+`zahlb}W6 z&8)^H)VsuTygfZ1UqC!LjM2}vdIAcIHP;+=Jh!j?v|X!v(d?*tVYT8cuH{5Sn?0>Z z#oGk9`eA&A!A51x30pvdr$8UIhy3J6`it=32Rh?!fIfOC0WQ@d+Y>ub1sFl3s;P`l)3e7w zn%~@l5@{fQ3yB=pb3WmW!VVfu8J%@t^sB5_@kxV;Mr!%w9_N*iH|!txcrm5ynDR0$ z_6gxeV%?MEcAj2dFi}6)%`IO3?)NmMud$Sl^C;VUZLmwzk+Ssc%Z&olE*&`X$Zp!^8B9jJ~>PDhha_Ra+2GOEko*&K!KX z<$k*iSI40`>^Zb7aOPfYxxv;`E!vGw?Q#66*r98#lJ@7mu&%c*Z%AtndqyxTDk`Fl zS~461(~tn2EcKPloZjY?oqP;6ob$d9a~`K2w=xK4vaS> z>mxIMzF5SF_YGK&8+LtzC10-Z$dYqm3qH_wf&J%C8fkHkz>O~p%vP+jHDk-2wOk*?e6=`v!$6gD zXD08sxy5Q=GY-`Yuk}>`$mFcmkL60vGtL;unK8Al(&I^EV{dxB;}y>%T7%nOR4(|e zb{oizu%cIL9)pGU99zhW&y4TE_hv6~(WtM1+nWPXdiq{Y+Zoq^OB5cgMqZa;b$iAY zgL1EroM4GatZ8d#W@-%hUaW#mZa)VSwEaxQpPl8SD_uDY+mwJ;bm1NQ@?QxU>Sfm| zUHVjlPHKL(o1!IN&lq}1mTSOuTvAi7VTa|*LRPBI^X|0^9itfF_;u5?6It@KXl**+ z85lTO18x>q#!z_G&4D^1%13bqUyhVd4k{=p@PpY_4)C1Un(E23B*f@JCT7-a_i1@~ z)SqC0#2`_)pYcsa8W%($Rtye_f}Qt;a4NS^tV5-kV}8ftyMK&tSk%*silA!u`xW5O z&=3(ipI;KHRqgsw#4mdtd(Q`aRA77ktW_G(%G)n7dtsFsykw-Z^^G20@nsn{Klx~~ z5*+#AdCuPondXp zTH4~hI|Vabv^^)yZF`$o#WrheYxih(yfVo_`u6E^5bqP}k#WDhaEqZ`8>@80FafGH zIMg6TL1W7%Gtkqv89svd>uvnO^lUXQew###hytMi&4CjtGP`bZK|JwXxuX$j0i4%< z7q)Jbx9Xx@9a!k!W0SLuzti&H1YkJuM2bg4%+DnKg#*HH<^U2kNCR!*0%!SHBV@*L zJ4MF&f>eZL;x`)4@E{O~b1$6PNusXCZEAEp$d;DvBs8|E)c&yfqN1y=-V`H~3;Ox~ z3OVd4q)Y8?`}z7x!n+Txhmf0Gl?XHzJ3M(u=2tw?e=OgOv``~u99-C9kA+rHlPk9G zN^f%DTuX0bYhpd(&Z7$Rb93wSEkb;dik79qwY88bkxtrAO7r}FZ;o#{T3}H$|4530 zX?$CBE{VkxZkB-*NSSGvyHdKjOk#7ZZ;vmpepVzWKN&;T;iM&?HAROH^aw5aQCU3T z!H$W7f`UeYZb^w~pD@u`E)j{pub|O=@gha}NA2UAhE*1$A_luWw0n(Bp602PmQ(vEjZrBI>^%!!+W>I=~TZtF*Peo+n(heqw2o|zfU>y + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/htmlwidgets/build/lfx-draw/ef32ea2bdf63ba132b4c.png b/inst/htmlwidgets/build/lfx-draw/ef32ea2bdf63ba132b4c.png new file mode 100644 index 0000000000000000000000000000000000000000..97d71c68052d4dc47cfd05292616d63db451d1cd GIT binary patch literal 1906 zcmZuydpOg58~=_RJ5Wvw&m^aZm68f+J!Bcns|;C_Lu3->G^`j4kB5k|9F|j&*W=71 z=W_?-9ZS7Vt%W>>7E?d0Mf0xf{queA>-yf`?|on2KR(yzzCYI|^`awYx4gPM0ARPh z9U2D!?2!14kd+qaoK_uw@wFr3ti6k@IAUe5rHE^}P`k?!07!rSd0>c0^AqCce)2gy z**Szrj=mc12cn~+jRQ!55x!SL{ftAx{qt8&tBZg0?a|gQl!9fpN06qQip=Wv)D%Y- z+bg+w(aFW?lgyt1|9)ik>q%L+jrR?%LpEbnIpOt#PI47gtD8Xgkp?04z@vSC?o-yT zs)RLFQ&Mk)a7XZwPQ1fXhWF4FZ(?J55vMD5sr6?s^WZS?6c1xEs1ktlD89lbT4wr7L0U z?Ey%L8UfB3 z6Oo*h^2-)Lt*E^xr-tS4g2z6x4@=;H7tUP;O04r6_s<$_v*!eF15ymD~r9(3MS4qmRge#Kgp?GsW8{XrmeD>HT?B zYsDmcE>bm3*w)sDVF&?Dp~-{b&E1rimUhg+>dm#UI1$hEGX>wex1SLpMe9RhFR<5~;%m8wdW36OCQws-U? zbNLM@^k(Q8_sGqZ=lG#KD_po)L;L4B_Yzc8kXpSterHwlZVn25JZCp>)@&EDDq6c_LRD2YN?%BUQzu*j1W4nA z9ywrRV`C;B)G698=(YVU$kX1a9QR2JR1$cbY<8(uUpe?J6ROf%NZ|G8XPXUqoE5lY zN~-R9YVZdK2M5wk>hL*Djkt=fz&7)NFa!t{v(VA8eseemAE1UD2#Zo0-O;Xw1a{=e zCa0eZ)~<{M-duKg+j}+OpqI`5s~Sd>ra?76Qoz~SdD{hBC{sTGhb%y<${}DTVb1>W zy4h!1VdgNg+bn;0#2~Mc)9~QChvS926L9BiTz@yCOTe4GL4hYvyJ0y`n!2Cjg17|I z6Z|1UfGQFkCH9^uvf9jEue9D~IOsLR8WO&)?r#I#Jhs#P~1v1k(GWp2GNdUiy& zFkVOT{t}{T5Kh1T#hW`m6!3@N<=edW%?CKI=0nKdZO^E!Gp`B?xm+%y|Ks0XLrl`s z*)3Pl@rK#qbaIoDn!>Oc&yh2A)~7Fh7xYvnjD1O4b*Q;ydC#^%0o^lh2|yEs&m7VGd*3r@g*4vB#4OMn2LG^OP=)U z%Q2`;tltV!*h`;LiFz^LwGe6pF^+p_G;C@;AKtdqPD*7I-`(47h>#0xdIt$(MG`dk zj-;Zx30`0vhjSYitI?ZPm&*|5m*EUIG;JAAHMJ%<2 z>h;tap8Q9i@Ja!MZ@7fV8t!>s51Tn)nkiUcU-rtBOfOo<-aBWaoBOZeI|(OHD9+J- zO<4V#$CFY3^X8if#me{Q3N&4=al@ts2J{h`nPYpYt>>Bl03yB@A9L$QC!J0Y0hE1K zH_7C+ALn1T)^@1%3P9{($KnPze2vZH=3p&Ho=K>j1A6+-h~D9OBbsT9V5RH01*1Qf z5O1y6xoAFeQJ=owm&-n7Ts-hmyMyuc>jZCI1`=-7fR#HBJ!>a;3je&1G|Iqa;~@OU zFU2V&e{Ld(%d-`&j|a|4(L^E^+csfMgU~=v&b-S#u zudmR(Iq2qsS45tW+~pasy_~(gwzkGDEiENAPH9TgYTR28V$BxX8nc^wOf4;U9y~>R z=_cYoV~U4^X$~Ghm30w3SbBPl>WTiHVD#iOn_Aw7-7~XdSf_VAR{)g66#YCQJ%5%~ i{eP_FzxHA`84t@4c6%tZ%}0&&o.clearLayers();var d=e.layer;o.addLayer(d);var i=L.stamp(d);if(d.feature={type:"Feature",properties:{_leaflet_id:i,feature_type:e.layerType}},"function"==typeof d.getRadius&&(d.feature.properties.radius=d.getRadius()),HTMLWidgets.shinyMode){var l=e.layerType;["rectangle","polygon","circle"].includes(l)?l="shape":"circlemarker"===l&&(l="marker"),d.on("click",t(n.id,i,a,l+"_draw_click",l),n),d.on("mouseover",t(n.id,i,a,l+"_draw_mouseover",l),n),d.on("mouseout",t(n.id,i,a,l+"_draw_mouseout",l),n),Shiny.onInputChange(n.id+"_draw_new_feature",d.toGeoJSON(),{priority:"event"}),Shiny.onInputChange(n.id+"_draw_all_features",o.toGeoJSON(),{priority:"event"})}},n.on(L.Draw.Event.CREATED,n.drawToolbar.eventHandler.onCreated),n.drawToolbar.eventHandler.onEditstart=function(){HTMLWidgets.shinyMode&&Shiny.onInputChange(n.id+"_draw_editstart",!0,{priority:"event"})},n.on(L.Draw.Event.EDITSTART,n.drawToolbar.eventHandler.onEditstart),n.drawToolbar.eventHandler.onEditstop=function(){HTMLWidgets.shinyMode&&Shiny.onInputChange(n.id+"_draw_editstop",!0,{priority:"event"})},n.on(L.Draw.Event.EDITSTOP,n.drawToolbar.eventHandler.onEditstop),n.drawToolbar.eventHandler.onEdited=function(e){var a=e.layers;a.eachLayer((function(e){var a=L.stamp(e);e.feature||(e.feature={type:"Feature"}),e.feature.properties||(e.feature.properties={}),e.feature.properties._leaflet_id=a,e.feature.properties.layerId=e.options.layerId,"function"==typeof e.getRadius&&(e.feature.properties.radius=e.getRadius())})),HTMLWidgets.shinyMode&&(Shiny.onInputChange(n.id+"_draw_edited_features",a.toGeoJSON(),{priority:"event"}),Shiny.onInputChange(n.id+"_draw_all_features",o.toGeoJSON(),{priority:"event"}))},n.on(L.Draw.Event.EDITED,n.drawToolbar.eventHandler.onEdited),n.drawToolbar.eventHandler.onDeletestart=function(){HTMLWidgets.shinyMode&&Shiny.onInputChange(n.id+"_draw_deletestart",!0,{priority:"event"})},n.on(L.Draw.Event.DELETESTART,n.drawToolbar.eventHandler.onDeletestart),n.drawToolbar.eventHandler.onDeletestop=function(){HTMLWidgets.shinyMode&&Shiny.onInputChange(n.id+"_draw_deletestop",!0,{priority:"event"})},n.on(L.Draw.Event.DELETESTOP,n.drawToolbar.eventHandler.onDeletestop),n.drawToolbar.eventHandler.onDeleted=function(e){var a=e.layers;a.eachLayer((function(e){var a=L.stamp(e);e.feature||(e.feature={type:"Feature"}),e.feature.properties||(e.feature.properties={}),e.feature.properties._leaflet_id=a,e.feature.properties.layerId=e.options.layerId,"function"==typeof e.getRadius&&(e.feature.properties.radius=e.getRadius())})),HTMLWidgets.shinyMode&&(Shiny.onInputChange(n.id+"_draw_deleted_features",a.toGeoJSON(),{priority:"event"}),Shiny.onInputChange(n.id+"_draw_all_features",o.toGeoJSON(),{priority:"event"}))},n.on(L.Draw.Event.DELETED,n.drawToolbar.eventHandler.onDeleted)}).call(this)},LeafletWidget.methods.removeDrawToolbar=function(e){(function(){var a=this;a.drawToolbar&&(a.off(L.Draw.Event.DRAWSTART,a.drawToolbar.eventHandler.onDrawStart),a.off(L.Draw.Event.DRAWSTOP,a.drawToolbar.eventHandler.onDrawStop),a.off(L.Draw.Event.CREATED,a.drawToolbar.eventHandler.onCreated),a.off(L.Draw.Event.EDITSTART,a.drawToolbar.eventHandler.onEditstart),a.off(L.Draw.Event.EDITSTOP,a.drawToolbar.eventHandler.onEditstop),a.off(L.Draw.Event.EDITED,a.drawToolbar.eventHandler.onEdited),a.off(L.Draw.Event.DELETESTART,a.drawToolbar.eventHandler.onDeletestart),a.off(L.Draw.Event.DELETESTOP,a.drawToolbar.eventHandler.onDeletestop),a.off(L.Draw.Event.DELETED,a.drawToolbar.eventHandler.onDeleted),a.drawToolbar.remove(a),delete a.drawToolbar),a._editableFeatureGroupName&&e&&a.layerManager.getLayerGroup(a._editableFeatureGroupName,!1).clearLayers(),a._editableFeatureGroupName=null,a._editableGeoJSONLayerId&&e&&a.layerManager.removeLayer("geojson",a._editableGeoJSONLayerId),a._editableGeoJSONLayerId=null}).call(this)},LeafletWidget.methods.getDrawnItems=function(){var e,a=this;return a._editableGeoJSONLayerId?e=a.layerManager.getLayer("geojson",a._editableGeoJSONLayerId):a._editableFeatureGroupName&&(e=a.layerManager.getLayerGroup(a._editableFeatureGroupName,!1)),e?e.toGeoJSON():null}; \ No newline at end of file diff --git a/inst/htmlwidgets/build/lfx-draw/lfx-draw-prod.css b/inst/htmlwidgets/build/lfx-draw/lfx-draw-prod.css new file mode 100644 index 00000000..3aa0462d --- /dev/null +++ b/inst/htmlwidgets/build/lfx-draw/lfx-draw-prod.css @@ -0,0 +1 @@ +.leaflet-draw-section{position:relative}.leaflet-draw-toolbar{margin-top:12px}.leaflet-draw-toolbar-top{margin-top:0}.leaflet-draw-toolbar-notop a:first-child{border-top-right-radius:0}.leaflet-draw-toolbar-nobottom a:last-child{border-bottom-right-radius:0}.leaflet-draw-toolbar a{background-clip:padding-box;background-image:url(ef32ea2bdf63ba132b4c.png);background-image:linear-gradient(transparent,transparent),url(a4e0eb7ad904a4858361.svg);background-repeat:no-repeat;background-size:300px 30px}.leaflet-retina .leaflet-draw-toolbar a{background-image:url(7ea3a6d428136b87ab95.png);background-image:linear-gradient(transparent,transparent),url(a4e0eb7ad904a4858361.svg)}.leaflet-draw a{display:block;text-align:center;text-decoration:none}.leaflet-draw a .sr-only{height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;clip:rect(0,0,0,0);border:0}.leaflet-draw-actions{display:none;left:26px;list-style:none;margin:0;padding:0;position:absolute;top:0;white-space:nowrap}.leaflet-touch .leaflet-draw-actions{left:32px}.leaflet-right .leaflet-draw-actions{left:auto;right:26px}.leaflet-touch .leaflet-right .leaflet-draw-actions{left:auto;right:32px}.leaflet-draw-actions li{display:inline-block}.leaflet-draw-actions li:first-child a{border-left:0}.leaflet-draw-actions li:last-child a{-webkit-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.leaflet-right .leaflet-draw-actions li:last-child a{-webkit-border-radius:0;border-radius:0}.leaflet-right .leaflet-draw-actions li:first-child a{-webkit-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.leaflet-draw-actions a{background-color:#919187;border-left:1px solid #aaa;color:#fff;font:11px/19px Helvetica Neue,Arial,Helvetica,sans-serif;height:28px;line-height:28px;padding-left:10px;padding-right:10px;text-decoration:none}.leaflet-touch .leaflet-draw-actions a{font-size:12px;height:30px;line-height:30px}.leaflet-draw-actions-bottom{margin-top:0}.leaflet-draw-actions-top{margin-top:1px}.leaflet-draw-actions-bottom a,.leaflet-draw-actions-top a{height:27px;line-height:27px}.leaflet-draw-actions a:hover{background-color:#a0a098}.leaflet-draw-actions-top.leaflet-draw-actions-bottom a{height:26px;line-height:26px}.leaflet-draw-toolbar .leaflet-draw-draw-polyline{background-position:-2px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-polyline{background-position:0 -1px}.leaflet-draw-toolbar .leaflet-draw-draw-polygon{background-position:-31px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-polygon{background-position:-29px -1px}.leaflet-draw-toolbar .leaflet-draw-draw-rectangle{background-position:-62px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-rectangle{background-position:-60px -1px}.leaflet-draw-toolbar .leaflet-draw-draw-circle{background-position:-92px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-circle{background-position:-90px -1px}.leaflet-draw-toolbar .leaflet-draw-draw-marker{background-position:-122px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-marker{background-position:-120px -1px}.leaflet-draw-toolbar .leaflet-draw-draw-circlemarker{background-position:-273px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-circlemarker{background-position:-271px -1px}.leaflet-draw-toolbar .leaflet-draw-edit-edit{background-position:-152px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-edit{background-position:-150px -1px}.leaflet-draw-toolbar .leaflet-draw-edit-remove{background-position:-182px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-remove{background-position:-180px -1px}.leaflet-draw-toolbar .leaflet-draw-edit-edit.leaflet-disabled{background-position:-212px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-edit.leaflet-disabled{background-position:-210px -1px}.leaflet-draw-toolbar .leaflet-draw-edit-remove.leaflet-disabled{background-position:-242px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-remove.leaflet-disabled{background-position:-240px -2px}.leaflet-mouse-marker{background-color:#fff;cursor:crosshair}.leaflet-draw-tooltip{background:#363636;background:rgba(0,0,0,.5);border:1px solid transparent;-webkit-border-radius:4px;border-radius:4px;color:#fff;font:12px/18px Helvetica Neue,Arial,Helvetica,sans-serif;margin-left:20px;margin-top:-21px;padding:4px 8px;position:absolute;visibility:hidden;white-space:nowrap;z-index:6}.leaflet-draw-tooltip:before{border-bottom:6px solid transparent;border-right:6px solid rgba(0,0,0,.5);border-top:6px solid transparent;content:"";left:-7px;position:absolute;top:7px}.leaflet-error-draw-tooltip{background-color:#f2dede;border:1px solid #e6b6bd;color:#b94a48}.leaflet-error-draw-tooltip:before{border-right-color:#e6b6bd}.leaflet-draw-tooltip-single{margin-top:-12px}.leaflet-draw-tooltip-subtext{color:#f8d5e4}.leaflet-draw-guide-dash{font-size:1%;height:5px;opacity:.6;position:absolute;width:5px}.leaflet-edit-marker-selected{background-color:rgba(254,87,161,.1);border:4px dashed rgba(254,87,161,.6);-webkit-border-radius:4px;border-radius:4px;box-sizing:content-box}.leaflet-edit-move{cursor:move}.leaflet-edit-resize{cursor:pointer}.leaflet-oldie .leaflet-draw-toolbar{border:1px solid #999} \ No newline at end of file diff --git a/inst/htmlwidgets/build/lfx-draw/lfx-draw-prod.js b/inst/htmlwidgets/build/lfx-draw/lfx-draw-prod.js new file mode 100644 index 00000000..a148381c --- /dev/null +++ b/inst/htmlwidgets/build/lfx-draw/lfx-draw-prod.js @@ -0,0 +1 @@ +(()=>{var t,e;t=window,e=document,L.drawVersion="1.0.4",L.Draw={},L.drawLocal={draw:{toolbar:{actions:{title:"Cancel drawing",text:"Cancel"},finish:{title:"Finish drawing",text:"Finish"},undo:{title:"Delete last point drawn",text:"Delete last point"},buttons:{polyline:"Draw a polyline",polygon:"Draw a polygon",rectangle:"Draw a rectangle",circle:"Draw a circle",marker:"Draw a marker",circlemarker:"Draw a circlemarker"}},handlers:{circle:{tooltip:{start:"Click and drag to draw circle."},radius:"Radius"},circlemarker:{tooltip:{start:"Click map to place circle marker."}},marker:{tooltip:{start:"Click map to place marker."}},polygon:{tooltip:{start:"Click to start drawing shape.",cont:"Click to continue drawing shape.",end:"Click first point to close this shape."}},polyline:{error:"Error: shape edges cannot cross!",tooltip:{start:"Click to start drawing line.",cont:"Click to continue drawing line.",end:"Click last point to finish line."}},rectangle:{tooltip:{start:"Click and drag to draw rectangle."}},simpleshape:{tooltip:{end:"Release mouse to finish drawing."}}}},edit:{toolbar:{actions:{save:{title:"Save changes",text:"Save"},cancel:{title:"Cancel editing, discards all changes",text:"Cancel"},clearAll:{title:"Clear all layers",text:"Clear All"}},buttons:{edit:"Edit layers",editDisabled:"No layers to edit",remove:"Delete layers",removeDisabled:"No layers to delete"}},handlers:{edit:{tooltip:{text:"Drag handles or markers to edit features.",subtext:"Click cancel to undo changes."}},remove:{tooltip:{text:"Click on a feature to remove."}}}}},L.Draw.Event={},L.Draw.Event.CREATED="draw:created",L.Draw.Event.EDITED="draw:edited",L.Draw.Event.DELETED="draw:deleted",L.Draw.Event.DRAWSTART="draw:drawstart",L.Draw.Event.DRAWSTOP="draw:drawstop",L.Draw.Event.DRAWVERTEX="draw:drawvertex",L.Draw.Event.EDITSTART="draw:editstart",L.Draw.Event.EDITMOVE="draw:editmove",L.Draw.Event.EDITRESIZE="draw:editresize",L.Draw.Event.EDITVERTEX="draw:editvertex",L.Draw.Event.EDITSTOP="draw:editstop",L.Draw.Event.DELETESTART="draw:deletestart",L.Draw.Event.DELETESTOP="draw:deletestop",L.Draw.Event.TOOLBAROPENED="draw:toolbaropened",L.Draw.Event.TOOLBARCLOSED="draw:toolbarclosed",L.Draw.Event.MARKERCONTEXT="draw:markercontext",L.Draw=L.Draw||{},L.Draw.Feature=L.Handler.extend({initialize:function(t,e){this._map=t,this._container=t._container,this._overlayPane=t._panes.overlayPane,this._popupPane=t._panes.popupPane,e&&e.shapeOptions&&(e.shapeOptions=L.Util.extend({},this.options.shapeOptions,e.shapeOptions)),L.setOptions(this,e);var i=L.version.split(".");1===parseInt(i[0],10)&&parseInt(i[1],10)>=2?L.Draw.Feature.include(L.Evented.prototype):L.Draw.Feature.include(L.Mixin.Events)},enable:function(){this._enabled||(L.Handler.prototype.enable.call(this),this.fire("enabled",{handler:this.type}),this._map.fire(L.Draw.Event.DRAWSTART,{layerType:this.type}))},disable:function(){this._enabled&&(L.Handler.prototype.disable.call(this),this._map.fire(L.Draw.Event.DRAWSTOP,{layerType:this.type}),this.fire("disabled",{handler:this.type}))},addHooks:function(){var t=this._map;t&&(L.DomUtil.disableTextSelection(),t.getContainer().focus(),this._tooltip=new L.Draw.Tooltip(this._map),L.DomEvent.on(this._container,"keyup",this._cancelDrawing,this))},removeHooks:function(){this._map&&(L.DomUtil.enableTextSelection(),this._tooltip.dispose(),this._tooltip=null,L.DomEvent.off(this._container,"keyup",this._cancelDrawing,this))},setOptions:function(t){L.setOptions(this,t)},_fireCreatedEvent:function(t){this._map.fire(L.Draw.Event.CREATED,{layer:t,layerType:this.type})},_cancelDrawing:function(t){27===t.keyCode&&(this._map.fire("draw:canceled",{layerType:this.type}),this.disable())}}),L.Draw.Polyline=L.Draw.Feature.extend({statics:{TYPE:"polyline"},Poly:L.Polyline,options:{allowIntersection:!0,repeatMode:!1,drawError:{color:"#b00b00",timeout:2500},icon:new L.DivIcon({iconSize:new L.Point(8,8),className:"leaflet-div-icon leaflet-editing-icon"}),touchIcon:new L.DivIcon({iconSize:new L.Point(20,20),className:"leaflet-div-icon leaflet-editing-icon leaflet-touch-icon"}),guidelineDistance:20,maxGuideLineLength:4e3,shapeOptions:{stroke:!0,color:"#3388ff",weight:4,opacity:.5,fill:!1,clickable:!0},metric:!0,feet:!0,nautic:!1,showLength:!0,zIndexOffset:2e3,factor:1,maxPoints:0},initialize:function(t,e){L.Browser.touch&&(this.options.icon=this.options.touchIcon),this.options.drawError.message=L.drawLocal.draw.handlers.polyline.error,e&&e.drawError&&(e.drawError=L.Util.extend({},this.options.drawError,e.drawError)),this.type=L.Draw.Polyline.TYPE,L.Draw.Feature.prototype.initialize.call(this,t,e)},addHooks:function(){L.Draw.Feature.prototype.addHooks.call(this),this._map&&(this._markers=[],this._markerGroup=new L.LayerGroup,this._map.addLayer(this._markerGroup),this._poly=new L.Polyline([],this.options.shapeOptions),this._tooltip.updateContent(this._getTooltipText()),this._mouseMarker||(this._mouseMarker=L.marker(this._map.getCenter(),{icon:L.divIcon({className:"leaflet-mouse-marker",iconAnchor:[20,20],iconSize:[40,40]}),opacity:0,zIndexOffset:this.options.zIndexOffset})),this._mouseMarker.on("mouseout",this._onMouseOut,this).on("mousemove",this._onMouseMove,this).on("mousedown",this._onMouseDown,this).on("mouseup",this._onMouseUp,this).addTo(this._map),this._map.on("mouseup",this._onMouseUp,this).on("mousemove",this._onMouseMove,this).on("zoomlevelschange",this._onZoomEnd,this).on("touchstart",this._onTouch,this).on("zoomend",this._onZoomEnd,this))},removeHooks:function(){L.Draw.Feature.prototype.removeHooks.call(this),this._clearHideErrorTimeout(),this._cleanUpShape(),this._map.removeLayer(this._markerGroup),delete this._markerGroup,delete this._markers,this._map.removeLayer(this._poly),delete this._poly,this._mouseMarker.off("mousedown",this._onMouseDown,this).off("mouseout",this._onMouseOut,this).off("mouseup",this._onMouseUp,this).off("mousemove",this._onMouseMove,this),this._map.removeLayer(this._mouseMarker),delete this._mouseMarker,this._clearGuides(),this._map.off("mouseup",this._onMouseUp,this).off("mousemove",this._onMouseMove,this).off("zoomlevelschange",this._onZoomEnd,this).off("zoomend",this._onZoomEnd,this).off("touchstart",this._onTouch,this).off("click",this._onTouch,this)},deleteLastVertex:function(){if(!(this._markers.length<=1)){var t=this._markers.pop(),e=this._poly,i=e.getLatLngs(),o=i.splice(-1,1)[0];this._poly.setLatLngs(i),this._markerGroup.removeLayer(t),e.getLatLngs().length<2&&this._map.removeLayer(e),this._vertexChanged(o,!1)}},addVertex:function(t){this._markers.length>=2&&!this.options.allowIntersection&&this._poly.newLatLngIntersects(t)?this._showErrorTooltip():(this._errorShown&&this._hideErrorTooltip(),this._markers.push(this._createMarker(t)),this._poly.addLatLng(t),2===this._poly.getLatLngs().length&&this._map.addLayer(this._poly),this._vertexChanged(t,!0))},completeShape:function(){this._markers.length<=1||!this._shapeIsValid()||(this._fireCreatedEvent(),this.disable(),this.options.repeatMode&&this.enable())},_finishShape:function(){var t=this._poly._defaultShape?this._poly._defaultShape():this._poly.getLatLngs(),e=this._poly.newLatLngIntersects(t[t.length-1]);!this.options.allowIntersection&&e||!this._shapeIsValid()?this._showErrorTooltip():(this._fireCreatedEvent(),this.disable(),this.options.repeatMode&&this.enable())},_shapeIsValid:function(){return!0},_onZoomEnd:function(){null!==this._markers&&this._updateGuide()},_onMouseMove:function(t){var e=this._map.mouseEventToLayerPoint(t.originalEvent),i=this._map.layerPointToLatLng(e);this._currentLatLng=i,this._updateTooltip(i),this._updateGuide(e),this._mouseMarker.setLatLng(i),L.DomEvent.preventDefault(t.originalEvent)},_vertexChanged:function(t,e){this._map.fire(L.Draw.Event.DRAWVERTEX,{layers:this._markerGroup}),this._updateFinishHandler(),this._updateRunningMeasure(t,e),this._clearGuides(),this._updateTooltip()},_onMouseDown:function(t){if(!this._clickHandled&&!this._touchHandled&&!this._disableMarkers){this._onMouseMove(t),this._clickHandled=!0,this._disableNewMarkers();var e=t.originalEvent,i=e.clientX,o=e.clientY;this._startPoint.call(this,i,o)}},_startPoint:function(t,e){this._mouseDownOrigin=L.point(t,e)},_onMouseUp:function(t){var e=t.originalEvent,i=e.clientX,o=e.clientY;this._endPoint.call(this,i,o,t),this._clickHandled=null},_endPoint:function(e,i,o){if(this._mouseDownOrigin){var a=L.point(e,i).distanceTo(this._mouseDownOrigin),n=this._calculateFinishDistance(o.latlng);this.options.maxPoints>1&&this.options.maxPoints==this._markers.length+1?(this.addVertex(o.latlng),this._finishShape()):n<10&&L.Browser.touch?this._finishShape():Math.abs(a)<9*(t.devicePixelRatio||1)&&this.addVertex(o.latlng),this._enableNewMarkers()}this._mouseDownOrigin=null},_onTouch:function(t){var e,i,o=t.originalEvent;!o.touches||!o.touches[0]||this._clickHandled||this._touchHandled||this._disableMarkers||(e=o.touches[0].clientX,i=o.touches[0].clientY,this._disableNewMarkers(),this._touchHandled=!0,this._startPoint.call(this,e,i),this._endPoint.call(this,e,i,t),this._touchHandled=null),this._clickHandled=null},_onMouseOut:function(){this._tooltip&&this._tooltip._onMouseOut.call(this._tooltip)},_calculateFinishDistance:function(t){var e;if(this._markers.length>0){var i;if(this.type===L.Draw.Polyline.TYPE)i=this._markers[this._markers.length-1];else{if(this.type!==L.Draw.Polygon.TYPE)return 1/0;i=this._markers[0]}var o=this._map.latLngToContainerPoint(i.getLatLng()),a=new L.Marker(t,{icon:this.options.icon,zIndexOffset:2*this.options.zIndexOffset}),n=this._map.latLngToContainerPoint(a.getLatLng());e=o.distanceTo(n)}else e=1/0;return e},_updateFinishHandler:function(){var t=this._markers.length;t>1&&this._markers[t-1].on("click",this._finishShape,this),t>2&&this._markers[t-2].off("click",this._finishShape,this)},_createMarker:function(t){var e=new L.Marker(t,{icon:this.options.icon,zIndexOffset:2*this.options.zIndexOffset});return this._markerGroup.addLayer(e),e},_updateGuide:function(t){var e=this._markers?this._markers.length:0;e>0&&(t=t||this._map.latLngToLayerPoint(this._currentLatLng),this._clearGuides(),this._drawGuide(this._map.latLngToLayerPoint(this._markers[e-1].getLatLng()),t))},_updateTooltip:function(t){var e=this._getTooltipText();t&&this._tooltip.updatePosition(t),this._errorShown||this._tooltip.updateContent(e)},_drawGuide:function(t,e){var i,o,a,n=Math.floor(Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2))),s=this.options.guidelineDistance,r=this.options.maxGuideLineLength,l=n>r?n-r:s;for(this._guidesContainer||(this._guidesContainer=L.DomUtil.create("div","leaflet-draw-guides",this._overlayPane));l1&&this._markers[this._markers.length-1].off("click",this._finishShape,this)},_fireCreatedEvent:function(){var t=new this.Poly(this._poly.getLatLngs(),this.options.shapeOptions);L.Draw.Feature.prototype._fireCreatedEvent.call(this,t)}}),L.Draw.Polygon=L.Draw.Polyline.extend({statics:{TYPE:"polygon"},Poly:L.Polygon,options:{showArea:!1,showLength:!1,shapeOptions:{stroke:!0,color:"#3388ff",weight:4,opacity:.5,fill:!0,fillColor:null,fillOpacity:.2,clickable:!0},metric:!0,feet:!0,nautic:!1,precision:{}},initialize:function(t,e){L.Draw.Polyline.prototype.initialize.call(this,t,e),this.type=L.Draw.Polygon.TYPE},_updateFinishHandler:function(){var t=this._markers.length;1===t&&this._markers[0].on("click",this._finishShape,this),t>2&&(this._markers[t-1].on("dblclick",this._finishShape,this),t>3&&this._markers[t-2].off("dblclick",this._finishShape,this))},_getTooltipText:function(){var t,e;return 0===this._markers.length?t=L.drawLocal.draw.handlers.polygon.tooltip.start:this._markers.length<3?(t=L.drawLocal.draw.handlers.polygon.tooltip.cont,e=this._getMeasurementString()):(t=L.drawLocal.draw.handlers.polygon.tooltip.end,e=this._getMeasurementString()),{text:t,subtext:e}},_getMeasurementString:function(){var t=this._area,e="";return t||this.options.showLength?(this.options.showLength&&(e=L.Draw.Polyline.prototype._getMeasurementString.call(this)),t&&(e+="
"+L.GeometryUtil.readableArea(t,this.options.metric,this.options.precision)),e):null},_shapeIsValid:function(){return this._markers.length>=3},_vertexChanged:function(t,e){var i;!this.options.allowIntersection&&this.options.showArea&&(i=this._poly.getLatLngs(),this._area=L.GeometryUtil.geodesicArea(i)),L.Draw.Polyline.prototype._vertexChanged.call(this,t,e)},_cleanUpShape:function(){var t=this._markers.length;t>0&&(this._markers[0].off("click",this._finishShape,this),t>2&&this._markers[t-1].off("dblclick",this._finishShape,this))}}),L.SimpleShape={},L.Draw.SimpleShape=L.Draw.Feature.extend({options:{repeatMode:!1},initialize:function(t,e){this._endLabelText=L.drawLocal.draw.handlers.simpleshape.tooltip.end,L.Draw.Feature.prototype.initialize.call(this,t,e)},addHooks:function(){L.Draw.Feature.prototype.addHooks.call(this),this._map&&(this._mapDraggable=this._map.dragging.enabled(),this._mapDraggable&&this._map.dragging.disable(),this._container.style.cursor="crosshair",this._tooltip.updateContent({text:this._initialLabelText}),this._map.on("mousedown",this._onMouseDown,this).on("mousemove",this._onMouseMove,this).on("touchstart",this._onMouseDown,this).on("touchmove",this._onMouseMove,this),e.addEventListener("touchstart",L.DomEvent.preventDefault,{passive:!1}))},removeHooks:function(){L.Draw.Feature.prototype.removeHooks.call(this),this._map&&(this._mapDraggable&&this._map.dragging.enable(),this._container.style.cursor="",this._map.off("mousedown",this._onMouseDown,this).off("mousemove",this._onMouseMove,this).off("touchstart",this._onMouseDown,this).off("touchmove",this._onMouseMove,this),L.DomEvent.off(e,"mouseup",this._onMouseUp,this),L.DomEvent.off(e,"touchend",this._onMouseUp,this),e.removeEventListener("touchstart",L.DomEvent.preventDefault),this._shape&&(this._map.removeLayer(this._shape),delete this._shape)),this._isDrawing=!1},_getTooltipText:function(){return{text:this._endLabelText}},_onMouseDown:function(t){this._isDrawing=!0,this._startLatLng=t.latlng,L.DomEvent.on(e,"mouseup",this._onMouseUp,this).on(e,"touchend",this._onMouseUp,this).preventDefault(t.originalEvent)},_onMouseMove:function(t){var e=t.latlng;this._tooltip.updatePosition(e),this._isDrawing&&(this._tooltip.updateContent(this._getTooltipText()),this._drawShape(e))},_onMouseUp:function(){this._shape&&this._fireCreatedEvent(),this.disable(),this.options.repeatMode&&this.enable()}}),L.Draw.Rectangle=L.Draw.SimpleShape.extend({statics:{TYPE:"rectangle"},options:{shapeOptions:{stroke:!0,color:"#3388ff",weight:4,opacity:.5,fill:!0,fillColor:null,fillOpacity:.2,clickable:!0},showArea:!0,metric:!0},initialize:function(t,e){this.type=L.Draw.Rectangle.TYPE,this._initialLabelText=L.drawLocal.draw.handlers.rectangle.tooltip.start,L.Draw.SimpleShape.prototype.initialize.call(this,t,e)},disable:function(){this._enabled&&(this._isCurrentlyTwoClickDrawing=!1,L.Draw.SimpleShape.prototype.disable.call(this))},_onMouseUp:function(t){this._shape||this._isCurrentlyTwoClickDrawing?this._isCurrentlyTwoClickDrawing&&!function(t,e){for(;(t=t.parentElement)&&!t.classList.contains(e););return t}(t.target,"leaflet-pane")||L.Draw.SimpleShape.prototype._onMouseUp.call(this):this._isCurrentlyTwoClickDrawing=!0},_drawShape:function(t){this._shape?this._shape.setBounds(new L.LatLngBounds(this._startLatLng,t)):(this._shape=new L.Rectangle(new L.LatLngBounds(this._startLatLng,t),this.options.shapeOptions),this._map.addLayer(this._shape))},_fireCreatedEvent:function(){var t=new L.Rectangle(this._shape.getBounds(),this.options.shapeOptions);L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this,t)},_getTooltipText:function(){var t,e,i,o=L.Draw.SimpleShape.prototype._getTooltipText.call(this),a=this._shape,n=this.options.showArea;return a&&(t=this._shape._defaultShape?this._shape._defaultShape():this._shape.getLatLngs(),e=L.GeometryUtil.geodesicArea(t),i=n?L.GeometryUtil.readableArea(e,this.options.metric):""),{text:o.text,subtext:i}}}),L.Draw.Marker=L.Draw.Feature.extend({statics:{TYPE:"marker"},options:{icon:new L.Icon.Default,repeatMode:!1,zIndexOffset:2e3},initialize:function(t,e){this.type=L.Draw.Marker.TYPE,this._initialLabelText=L.drawLocal.draw.handlers.marker.tooltip.start,L.Draw.Feature.prototype.initialize.call(this,t,e)},addHooks:function(){L.Draw.Feature.prototype.addHooks.call(this),this._map&&(this._tooltip.updateContent({text:this._initialLabelText}),this._mouseMarker||(this._mouseMarker=L.marker(this._map.getCenter(),{icon:L.divIcon({className:"leaflet-mouse-marker",iconAnchor:[20,20],iconSize:[40,40]}),opacity:0,zIndexOffset:this.options.zIndexOffset})),this._mouseMarker.on("click",this._onClick,this).addTo(this._map),this._map.on("mousemove",this._onMouseMove,this),this._map.on("click",this._onTouch,this))},removeHooks:function(){L.Draw.Feature.prototype.removeHooks.call(this),this._map&&(this._map.off("click",this._onClick,this).off("click",this._onTouch,this),this._marker&&(this._marker.off("click",this._onClick,this),this._map.removeLayer(this._marker),delete this._marker),this._mouseMarker.off("click",this._onClick,this),this._map.removeLayer(this._mouseMarker),delete this._mouseMarker,this._map.off("mousemove",this._onMouseMove,this))},_onMouseMove:function(t){var e=t.latlng;this._tooltip.updatePosition(e),this._mouseMarker.setLatLng(e),this._marker?(e=this._mouseMarker.getLatLng(),this._marker.setLatLng(e)):(this._marker=this._createMarker(e),this._marker.on("click",this._onClick,this),this._map.on("click",this._onClick,this).addLayer(this._marker))},_createMarker:function(t){return new L.Marker(t,{icon:this.options.icon,zIndexOffset:this.options.zIndexOffset})},_onClick:function(){this._fireCreatedEvent(),this.disable(),this.options.repeatMode&&this.enable()},_onTouch:function(t){this._onMouseMove(t),this._onClick()},_fireCreatedEvent:function(){var t=new L.Marker.Touch(this._marker.getLatLng(),{icon:this.options.icon});L.Draw.Feature.prototype._fireCreatedEvent.call(this,t)}}),L.Draw.CircleMarker=L.Draw.Marker.extend({statics:{TYPE:"circlemarker"},options:{stroke:!0,color:"#3388ff",weight:4,opacity:.5,fill:!0,fillColor:null,fillOpacity:.2,clickable:!0,zIndexOffset:2e3},initialize:function(t,e){this.type=L.Draw.CircleMarker.TYPE,this._initialLabelText=L.drawLocal.draw.handlers.circlemarker.tooltip.start,L.Draw.Feature.prototype.initialize.call(this,t,e)},_fireCreatedEvent:function(){var t=new L.CircleMarker(this._marker.getLatLng(),this.options);L.Draw.Feature.prototype._fireCreatedEvent.call(this,t)},_createMarker:function(t){return new L.CircleMarker(t,this.options)}}),L.Draw.Circle=L.Draw.SimpleShape.extend({statics:{TYPE:"circle"},options:{shapeOptions:{stroke:!0,color:"#3388ff",weight:4,opacity:.5,fill:!0,fillColor:null,fillOpacity:.2,clickable:!0},showRadius:!0,metric:!0,feet:!0,nautic:!1},initialize:function(t,e){this.type=L.Draw.Circle.TYPE,this._initialLabelText=L.drawLocal.draw.handlers.circle.tooltip.start,L.Draw.SimpleShape.prototype.initialize.call(this,t,e)},_drawShape:function(t){if(L.GeometryUtil.isVersion07x())var e=this._startLatLng.distanceTo(t);else e=this._map.distance(this._startLatLng,t);this._shape?this._shape.setRadius(e):(this._shape=new L.Circle(this._startLatLng,e,this.options.shapeOptions),this._map.addLayer(this._shape))},_fireCreatedEvent:function(){var t=new L.Circle(this._startLatLng,this._shape.getRadius(),this.options.shapeOptions);L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this,t)},_onMouseMove:function(t){var e,i=t.latlng,o=this.options.showRadius,a=this.options.metric;if(this._tooltip.updatePosition(i),this._isDrawing){this._drawShape(i),e=this._shape.getRadius().toFixed(1);var n="";o&&(n=L.drawLocal.draw.handlers.circle.radius+": "+L.GeometryUtil.readableDistance(e,a,this.options.feet,this.options.nautic)),this._tooltip.updateContent({text:this._endLabelText,subtext:n})}}}),L.Edit=L.Edit||{},L.Edit.Marker=L.Handler.extend({initialize:function(t,e){this._marker=t,L.setOptions(this,e)},addHooks:function(){var t=this._marker;t.dragging.enable(),t.on("dragend",this._onDragEnd,t),this._toggleMarkerHighlight()},removeHooks:function(){var t=this._marker;t.dragging.disable(),t.off("dragend",this._onDragEnd,t),this._toggleMarkerHighlight()},_onDragEnd:function(t){var e=t.target;e.edited=!0,this._map.fire(L.Draw.Event.EDITMOVE,{layer:e})},_toggleMarkerHighlight:function(){var t=this._marker._icon;t&&(t.style.display="none",L.DomUtil.hasClass(t,"leaflet-edit-marker-selected")?(L.DomUtil.removeClass(t,"leaflet-edit-marker-selected"),this._offsetMarker(t,-4)):(L.DomUtil.addClass(t,"leaflet-edit-marker-selected"),this._offsetMarker(t,4)),t.style.display="")},_offsetMarker:function(t,e){var i=parseInt(t.style.marginTop,10)-e,o=parseInt(t.style.marginLeft,10)-e;t.style.marginTop=i+"px",t.style.marginLeft=o+"px"}}),L.Marker.addInitHook((function(){L.Edit.Marker&&(this.editing=new L.Edit.Marker(this),this.options.editable&&this.editing.enable())})),L.Edit=L.Edit||{},L.Edit.Poly=L.Handler.extend({initialize:function(t){this.latlngs=[t._latlngs],t._holes&&(this.latlngs=this.latlngs.concat(t._holes)),this._poly=t,this._poly.on("revert-edited",this._updateLatLngs,this)},_defaultShape:function(){return L.Polyline._flat?L.Polyline._flat(this._poly._latlngs)?this._poly._latlngs:this._poly._latlngs[0]:this._poly._latlngs},_eachVertexHandler:function(t){for(var e=0;et&&(i._index+=e)}))},_createMiddleMarker:function(t,e){var i,o,a,n=this._getMiddleLatLng(t,e),s=this._createMarker(n);s.setOpacity(.6),t._middleRight=e._middleLeft=s,o=function(){s.off("touchmove",o,this);var a=e._index;s._index=a,s.off("click",i,this).on("click",this._onMarkerClick,this),n.lat=s.getLatLng().lat,n.lng=s.getLatLng().lng,this._spliceLatLngs(a,0,n),this._markers.splice(a,0,s),s.setOpacity(1),this._updateIndexes(a,1),e._index++,this._updatePrevNext(t,s),this._updatePrevNext(s,e),this._poly.fire("editstart")},a=function(){s.off("dragstart",o,this),s.off("dragend",a,this),s.off("touchmove",o,this),this._createMiddleMarker(t,s),this._createMiddleMarker(s,e)},i=function(){o.call(this),a.call(this),this._fireEdit()},s.on("click",i,this).on("dragstart",o,this).on("dragend",a,this).on("touchmove",o,this),this._markerGroup.addLayer(s)},_updatePrevNext:function(t,e){t&&(t._next=e),e&&(e._prev=t)},_getMiddleLatLng:function(t,e){var i=this._poly._map,o=i.project(t.getLatLng()),a=i.project(e.getLatLng());return i.unproject(o._add(a)._divideBy(2))}}),L.Polyline.addInitHook((function(){this.editing||(L.Edit.Poly&&(this.editing=new L.Edit.Poly(this),this.options.editable&&this.editing.enable()),this.on("add",(function(){this.editing&&this.editing.enabled()&&this.editing.addHooks()})),this.on("remove",(function(){this.editing&&this.editing.enabled()&&this.editing.removeHooks()})))})),L.Edit=L.Edit||{},L.Edit.SimpleShape=L.Handler.extend({options:{moveIcon:new L.DivIcon({iconSize:new L.Point(8,8),className:"leaflet-div-icon leaflet-editing-icon leaflet-edit-move"}),resizeIcon:new L.DivIcon({iconSize:new L.Point(8,8),className:"leaflet-div-icon leaflet-editing-icon leaflet-edit-resize"}),touchMoveIcon:new L.DivIcon({iconSize:new L.Point(20,20),className:"leaflet-div-icon leaflet-editing-icon leaflet-edit-move leaflet-touch-icon"}),touchResizeIcon:new L.DivIcon({iconSize:new L.Point(20,20),className:"leaflet-div-icon leaflet-editing-icon leaflet-edit-resize leaflet-touch-icon"})},initialize:function(t,e){L.Browser.touch&&(this.options.moveIcon=this.options.touchMoveIcon,this.options.resizeIcon=this.options.touchResizeIcon),this._shape=t,L.Util.setOptions(this,e)},addHooks:function(){var t=this._shape;this._shape._map&&(this._map=this._shape._map,t.setStyle(t.options.editing),t._map&&(this._map=t._map,this._markerGroup||this._initMarkers(),this._map.addLayer(this._markerGroup)))},removeHooks:function(){var t=this._shape;if(t.setStyle(t.options.original),t._map){this._unbindMarker(this._moveMarker);for(var e=0,i=this._resizeMarkers.length;e"+L.drawLocal.edit.handlers.edit.tooltip.text,subtext:L.drawLocal.draw.handlers.circle.radius+": "+L.GeometryUtil.readableDistance(radius,!0,this.options.feet,this.options.nautic)}),this._shape.setRadius(radius),this._map.fire(L.Draw.Event.EDITRESIZE,{layer:this._shape})}}),L.Circle.addInitHook((function(){L.Edit.Circle&&(this.editing=new L.Edit.Circle(this),this.options.editable&&this.editing.enable())})),L.Map.mergeOptions({touchExtend:!0}),L.Map.TouchExtend=L.Handler.extend({initialize:function(t){this._map=t,this._container=t._container,this._pane=t._panes.overlayPane},addHooks:function(){L.DomEvent.on(this._container,"touchstart",this._onTouchStart,this),L.DomEvent.on(this._container,"touchend",this._onTouchEnd,this),L.DomEvent.on(this._container,"touchmove",this._onTouchMove,this),this._detectIE()?(L.DomEvent.on(this._container,"MSPointerDown",this._onTouchStart,this),L.DomEvent.on(this._container,"MSPointerUp",this._onTouchEnd,this),L.DomEvent.on(this._container,"MSPointerMove",this._onTouchMove,this),L.DomEvent.on(this._container,"MSPointerCancel",this._onTouchCancel,this)):(L.DomEvent.on(this._container,"touchcancel",this._onTouchCancel,this),L.DomEvent.on(this._container,"touchleave",this._onTouchLeave,this))},removeHooks:function(){L.DomEvent.off(this._container,"touchstart",this._onTouchStart,this),L.DomEvent.off(this._container,"touchend",this._onTouchEnd,this),L.DomEvent.off(this._container,"touchmove",this._onTouchMove,this),this._detectIE()?(L.DomEvent.off(this._container,"MSPointerDown",this._onTouchStart,this),L.DomEvent.off(this._container,"MSPointerUp",this._onTouchEnd,this),L.DomEvent.off(this._container,"MSPointerMove",this._onTouchMove,this),L.DomEvent.off(this._container,"MSPointerCancel",this._onTouchCancel,this)):(L.DomEvent.off(this._container,"touchcancel",this._onTouchCancel,this),L.DomEvent.off(this._container,"touchleave",this._onTouchLeave,this))},_touchEvent:function(t,e){var i={};if(void 0!==t.touches){if(!t.touches.length)return;i=t.touches[0]}else{if("touch"!==t.pointerType)return;if(i=t,!this._filterClick(t))return}var o=this._map.mouseEventToContainerPoint(i),a=this._map.mouseEventToLayerPoint(i),n=this._map.layerPointToLatLng(a);this._map.fire(e,{latlng:n,layerPoint:a,containerPoint:o,pageX:i.pageX,pageY:i.pageY,originalEvent:t})},_filterClick:function(t){var e=t.timeStamp||t.originalEvent.timeStamp,i=L.DomEvent._lastClick&&e-L.DomEvent._lastClick;return i&&i>100&&i<500||t.target._simulatedClick&&!t._simulated?(L.DomEvent.stop(t),!1):(L.DomEvent._lastClick=e,!0)},_onTouchStart:function(t){this._map._loaded&&this._touchEvent(t,"touchstart")},_onTouchEnd:function(t){this._map._loaded&&this._touchEvent(t,"touchend")},_onTouchCancel:function(t){if(this._map._loaded){var e="touchcancel";this._detectIE()&&(e="pointercancel"),this._touchEvent(t,e)}},_onTouchLeave:function(t){this._map._loaded&&this._touchEvent(t,"touchleave")},_onTouchMove:function(t){this._map._loaded&&this._touchEvent(t,"touchmove")},_detectIE:function(){var e=t.navigator.userAgent,i=e.indexOf("MSIE ");if(i>0)return parseInt(e.substring(i+5,e.indexOf(".",i)),10);if(e.indexOf("Trident/")>0){var o=e.indexOf("rv:");return parseInt(e.substring(o+3,e.indexOf(".",o)),10)}var a=e.indexOf("Edge/");return a>0&&parseInt(e.substring(a+5,e.indexOf(".",a)),10)}}),L.Map.addInitHook("addHandler","touchExtend",L.Map.TouchExtend),L.Marker.Touch=L.Marker.extend({_initInteraction:function(){return this.addInteractiveTarget?L.Marker.prototype._initInteraction.apply(this):this._initInteractionLegacy()},_initInteractionLegacy:function(){if(this.options.clickable){var t=this._icon,e=["dblclick","mousedown","mouseover","mouseout","contextmenu","touchstart","touchend","touchmove"];this._detectIE?e.concat(["MSPointerDown","MSPointerUp","MSPointerMove","MSPointerCancel"]):e.concat(["touchcancel"]),L.DomUtil.addClass(t,"leaflet-clickable"),L.DomEvent.on(t,"click",this._onMouseClick,this),L.DomEvent.on(t,"keypress",this._onKeyPress,this);for(var i=0;i0)return parseInt(e.substring(i+5,e.indexOf(".",i)),10);if(e.indexOf("Trident/")>0){var o=e.indexOf("rv:");return parseInt(e.substring(o+3,e.indexOf(".",o)),10)}var a=e.indexOf("Edge/");return a>0&&parseInt(e.substring(a+5,e.indexOf(".",a)),10)}}),L.LatLngUtil={cloneLatLngs:function(t){for(var e=[],i=0,o=t.length;i2){for(var s=0;s1&&(i=i+s+r[1])}return i},readableArea:function(e,i,o){var a,n;return o=L.Util.extend({},t,o),i?(n=["ha","m"],type=typeof i,"string"===type?n=[i]:"boolean"!==type&&(n=i),a=e>=1e6&&-1!==n.indexOf("km")?L.GeometryUtil.formattedNumber(1e-6*e,o.km)+" km²":e>=1e4&&-1!==n.indexOf("ha")?L.GeometryUtil.formattedNumber(1e-4*e,o.ha)+" ha":L.GeometryUtil.formattedNumber(e,o.m)+" m²"):a=(e/=.836127)>=3097600?L.GeometryUtil.formattedNumber(e/3097600,o.mi)+" mi²":e>=4840?L.GeometryUtil.formattedNumber(e/4840,o.ac)+" acres":L.GeometryUtil.formattedNumber(e,o.yd)+" yd²",a},readableDistance:function(e,i,o,a,n){var s;switch(n=L.Util.extend({},t,n),i?"string"==typeof i?i:"metric":o?"feet":a?"nauticalMile":"yards"){case"metric":s=e>1e3?L.GeometryUtil.formattedNumber(e/1e3,n.km)+" km":L.GeometryUtil.formattedNumber(e,n.m)+" m";break;case"feet":e*=3.28083,s=L.GeometryUtil.formattedNumber(e,n.ft)+" ft";break;case"nauticalMile":e*=.53996,s=L.GeometryUtil.formattedNumber(e/1e3,n.nm)+" nm";break;default:s=(e*=1.09361)>1760?L.GeometryUtil.formattedNumber(e/1760,n.mi)+" miles":L.GeometryUtil.formattedNumber(e,n.yd)+" yd"}return s},isVersion07x:function(){var t=L.version.split(".");return 0===parseInt(t[0],10)&&7===parseInt(t[1],10)}})}(),L.Util.extend(L.LineUtil,{segmentsIntersect:function(t,e,i,o){return this._checkCounterclockwise(t,i,o)!==this._checkCounterclockwise(e,i,o)&&this._checkCounterclockwise(t,e,i)!==this._checkCounterclockwise(t,e,o)},_checkCounterclockwise:function(t,e,i){return(i.y-t.y)*(e.x-t.x)>(e.y-t.y)*(i.x-t.x)}}),L.Polyline.include({intersects:function(){var t,e,i,o=this._getProjectedPoints(),a=o?o.length:0;if(this._tooFewPointsForIntersection())return!1;for(t=a-1;t>=3;t--)if(e=o[t-1],i=o[t],this._lineSegmentsIntersectsRange(e,i,t-2))return!0;return!1},newLatLngIntersects:function(t,e){return!!this._map&&this.newPointIntersects(this._map.latLngToLayerPoint(t),e)},newPointIntersects:function(t,e){var i=this._getProjectedPoints(),o=i?i.length:0,a=i?i[o-1]:null,n=o-2;return!this._tooFewPointsForIntersection(1)&&this._lineSegmentsIntersectsRange(a,t,n,e?1:0)},_tooFewPointsForIntersection:function(t){var e=this._getProjectedPoints(),i=e?e.length:0;return!e||(i+=t||0)<=3},_lineSegmentsIntersectsRange:function(t,e,i,o){var a,n,s=this._getProjectedPoints();o=o||0;for(var r=i;r>o;r--)if(a=s[r-1],n=s[r],L.LineUtil.segmentsIntersect(t,e,a,n))return!0;return!1},_getProjectedPoints:function(){if(!this._defaultShape)return this._originalPoints;for(var t=[],e=this._defaultShape(),i=0;i=2?L.Toolbar.include(L.Evented.prototype):L.Toolbar.include(L.Mixin.Events)},enabled:function(){return null!==this._activeMode},disable:function(){this.enabled()&&this._activeMode.handler.disable()},addToolbar:function(t){var e,i=L.DomUtil.create("div","leaflet-draw-section"),o=0,a=this._toolbarClass||"",n=this.getModeHandlers(t);for(this._toolbarContainer=L.DomUtil.create("div","leaflet-draw-toolbar leaflet-bar"),this._map=t,e=0;e0&&this._singleLineLabel&&(L.DomUtil.removeClass(this._container,"leaflet-draw-tooltip-single"),this._singleLineLabel=!1):(L.DomUtil.addClass(this._container,"leaflet-draw-tooltip-single"),this._singleLineLabel=!0),this._container.innerHTML=(t.subtext.length>0?''+t.subtext+"
":"")+""+t.text+"",t.text||t.subtext?(this._visible=!0,this._container.style.visibility="inherit"):(this._visible=!1,this._container.style.visibility="hidden"),this):this},updatePosition:function(t){var e=this._map.latLngToLayerPoint(t),i=this._container;return this._container&&(this._visible&&(i.style.visibility="inherit"),L.DomUtil.setPosition(i,e)),this},showAsError:function(){return this._container&&L.DomUtil.addClass(this._container,"leaflet-error-draw-tooltip"),this},removeError:function(){return this._container&&L.DomUtil.removeClass(this._container,"leaflet-error-draw-tooltip"),this},_onMouseOut:function(){this._container&&(this._container.style.visibility="hidden")}}),L.DrawToolbar=L.Toolbar.extend({statics:{TYPE:"draw"},options:{polyline:{},polygon:{},rectangle:{},circle:{},marker:{},circlemarker:{}},initialize:function(t){for(var e in this.options)this.options.hasOwnProperty(e)&&t[e]&&(t[e]=L.extend({},this.options[e],t[e]));this._toolbarClass="leaflet-draw-draw",L.Toolbar.prototype.initialize.call(this,t)},getModeHandlers:function(t){return[{enabled:this.options.polyline,handler:new L.Draw.Polyline(t,this.options.polyline),title:L.drawLocal.draw.toolbar.buttons.polyline},{enabled:this.options.polygon,handler:new L.Draw.Polygon(t,this.options.polygon),title:L.drawLocal.draw.toolbar.buttons.polygon},{enabled:this.options.rectangle,handler:new L.Draw.Rectangle(t,this.options.rectangle),title:L.drawLocal.draw.toolbar.buttons.rectangle},{enabled:this.options.circle,handler:new L.Draw.Circle(t,this.options.circle),title:L.drawLocal.draw.toolbar.buttons.circle},{enabled:this.options.marker,handler:new L.Draw.Marker(t,this.options.marker),title:L.drawLocal.draw.toolbar.buttons.marker},{enabled:this.options.circlemarker,handler:new L.Draw.CircleMarker(t,this.options.circlemarker),title:L.drawLocal.draw.toolbar.buttons.circlemarker}]},getActions:function(t){return[{enabled:t.completeShape,title:L.drawLocal.draw.toolbar.finish.title,text:L.drawLocal.draw.toolbar.finish.text,callback:t.completeShape,context:t},{enabled:t.deleteLastVertex,title:L.drawLocal.draw.toolbar.undo.title,text:L.drawLocal.draw.toolbar.undo.text,callback:t.deleteLastVertex,context:t},{title:L.drawLocal.draw.toolbar.actions.title,text:L.drawLocal.draw.toolbar.actions.text,callback:this.disable,context:this}]},setOptions:function(t){for(var e in L.setOptions(this,t),this._modes)this._modes.hasOwnProperty(e)&&t.hasOwnProperty(e)&&this._modes[e].handler.setOptions(t[e])}}),L.EditToolbar=L.Toolbar.extend({statics:{TYPE:"edit"},options:{edit:{selectedPathOptions:{dashArray:"10, 10",fill:!0,fillColor:"#fe57a1",fillOpacity:.1,maintainColor:!1}},remove:{},poly:null,featureGroup:null},initialize:function(t){t.edit&&(void 0===t.edit.selectedPathOptions&&(t.edit.selectedPathOptions=this.options.edit.selectedPathOptions),t.edit.selectedPathOptions=L.extend({},this.options.edit.selectedPathOptions,t.edit.selectedPathOptions)),t.remove&&(t.remove=L.extend({},this.options.remove,t.remove)),t.poly&&(t.poly=L.extend({},this.options.poly,t.poly)),this._toolbarClass="leaflet-draw-edit",L.Toolbar.prototype.initialize.call(this,t),this._selectedFeatureCount=0},getModeHandlers:function(t){var e=this.options.featureGroup;return[{enabled:this.options.edit,handler:new L.EditToolbar.Edit(t,{featureGroup:e,selectedPathOptions:this.options.edit.selectedPathOptions,poly:this.options.poly}),title:L.drawLocal.edit.toolbar.buttons.edit},{enabled:this.options.remove,handler:new L.EditToolbar.Delete(t,{featureGroup:e}),title:L.drawLocal.edit.toolbar.buttons.remove}]},getActions:function(t){var e=[{title:L.drawLocal.edit.toolbar.actions.save.title,text:L.drawLocal.edit.toolbar.actions.save.text,callback:this._save,context:this},{title:L.drawLocal.edit.toolbar.actions.cancel.title,text:L.drawLocal.edit.toolbar.actions.cancel.text,callback:this.disable,context:this}];return t.removeAllLayers&&e.push({title:L.drawLocal.edit.toolbar.actions.clearAll.title,text:L.drawLocal.edit.toolbar.actions.clearAll.text,callback:this._clearAllLayers,context:this}),e},addToolbar:function(t){var e=L.Toolbar.prototype.addToolbar.call(this,t);return this._checkDisabled(),this.options.featureGroup.on("layeradd layerremove",this._checkDisabled,this),e},removeToolbar:function(){this.options.featureGroup.off("layeradd layerremove",this._checkDisabled,this),L.Toolbar.prototype.removeToolbar.call(this)},disable:function(){this.enabled()&&(this._activeMode.handler.revertLayers(),L.Toolbar.prototype.disable.call(this))},_save:function(){this._activeMode.handler.save(),this._activeMode&&this._activeMode.handler.disable()},_clearAllLayers:function(){this._activeMode.handler.removeAllLayers(),this._activeMode&&this._activeMode.handler.disable()},_checkDisabled:function(){var t,e=0!==this.options.featureGroup.getLayers().length;this.options.edit&&(t=this._modes[L.EditToolbar.Edit.TYPE].button,e?L.DomUtil.removeClass(t,"leaflet-disabled"):L.DomUtil.addClass(t,"leaflet-disabled"),t.setAttribute("title",e?L.drawLocal.edit.toolbar.buttons.edit:L.drawLocal.edit.toolbar.buttons.editDisabled)),this.options.remove&&(t=this._modes[L.EditToolbar.Delete.TYPE].button,e?L.DomUtil.removeClass(t,"leaflet-disabled"):L.DomUtil.addClass(t,"leaflet-disabled"),t.setAttribute("title",e?L.drawLocal.edit.toolbar.buttons.remove:L.drawLocal.edit.toolbar.buttons.removeDisabled))}}),L.EditToolbar.Edit=L.Handler.extend({statics:{TYPE:"edit"},initialize:function(t,e){if(L.Handler.prototype.initialize.call(this,t),L.setOptions(this,e),this._featureGroup=e.featureGroup,!(this._featureGroup instanceof L.FeatureGroup))throw new Error("options.featureGroup must be a L.FeatureGroup");this._uneditedLayerProps={},this.type=L.EditToolbar.Edit.TYPE;var i=L.version.split(".");1===parseInt(i[0],10)&&parseInt(i[1],10)>=2?L.EditToolbar.Edit.include(L.Evented.prototype):L.EditToolbar.Edit.include(L.Mixin.Events)},enable:function(){!this._enabled&&this._hasAvailableLayers()&&(this.fire("enabled",{handler:this.type}),this._map.fire(L.Draw.Event.EDITSTART,{handler:this.type}),L.Handler.prototype.enable.call(this),this._featureGroup.on("layeradd",this._enableLayerEdit,this).on("layerremove",this._disableLayerEdit,this))},disable:function(){this._enabled&&(this._featureGroup.off("layeradd",this._enableLayerEdit,this).off("layerremove",this._disableLayerEdit,this),L.Handler.prototype.disable.call(this),this._map.fire(L.Draw.Event.EDITSTOP,{handler:this.type}),this.fire("disabled",{handler:this.type}))},addHooks:function(){var t=this._map;t&&(t.getContainer().focus(),this._featureGroup.eachLayer(this._enableLayerEdit,this),this._tooltip=new L.Draw.Tooltip(this._map),this._tooltip.updateContent({text:L.drawLocal.edit.handlers.edit.tooltip.text,subtext:L.drawLocal.edit.handlers.edit.tooltip.subtext}),t._editTooltip=this._tooltip,this._updateTooltip(),this._map.on("mousemove",this._onMouseMove,this).on("touchmove",this._onMouseMove,this).on("MSPointerMove",this._onMouseMove,this).on(L.Draw.Event.EDITVERTEX,this._updateTooltip,this))},removeHooks:function(){this._map&&(this._featureGroup.eachLayer(this._disableLayerEdit,this),this._uneditedLayerProps={},this._tooltip.dispose(),this._tooltip=null,this._map.off("mousemove",this._onMouseMove,this).off("touchmove",this._onMouseMove,this).off("MSPointerMove",this._onMouseMove,this).off(L.Draw.Event.EDITVERTEX,this._updateTooltip,this))},revertLayers:function(){this._featureGroup.eachLayer((function(t){this._revertLayer(t)}),this)},save:function(){var t=new L.LayerGroup;this._featureGroup.eachLayer((function(e){e.edited&&(t.addLayer(e),e.edited=!1)})),this._map.fire(L.Draw.Event.EDITED,{layers:t})},_backupLayer:function(t){var e=L.Util.stamp(t);this._uneditedLayerProps[e]||(t instanceof L.Polyline||t instanceof L.Polygon||t instanceof L.Rectangle?this._uneditedLayerProps[e]={latlngs:L.LatLngUtil.cloneLatLngs(t.getLatLngs())}:t instanceof L.Circle?this._uneditedLayerProps[e]={latlng:L.LatLngUtil.cloneLatLng(t.getLatLng()),radius:t.getRadius()}:(t instanceof L.Marker||t instanceof L.CircleMarker)&&(this._uneditedLayerProps[e]={latlng:L.LatLngUtil.cloneLatLng(t.getLatLng())}))},_getTooltipText:function(){return{text:L.drawLocal.edit.handlers.edit.tooltip.text,subtext:L.drawLocal.edit.handlers.edit.tooltip.subtext}},_updateTooltip:function(){this._tooltip.updateContent(this._getTooltipText())},_revertLayer:function(t){var e=L.Util.stamp(t);t.edited=!1,this._uneditedLayerProps.hasOwnProperty(e)&&(t instanceof L.Polyline||t instanceof L.Polygon||t instanceof L.Rectangle?t.setLatLngs(this._uneditedLayerProps[e].latlngs):t instanceof L.Circle?(t.setLatLng(this._uneditedLayerProps[e].latlng),t.setRadius(this._uneditedLayerProps[e].radius)):(t instanceof L.Marker||t instanceof L.CircleMarker)&&t.setLatLng(this._uneditedLayerProps[e].latlng),t.fire("revert-edited",{layer:t}))},_enableLayerEdit:function(t){var e,i,o=t.layer||t.target||t;this._backupLayer(o),this.options.poly&&(i=L.Util.extend({},this.options.poly),o.options.poly=i),this.options.selectedPathOptions&&((e=L.Util.extend({},this.options.selectedPathOptions)).maintainColor&&(e.color=o.options.color,e.fillColor=o.options.fillColor),o.options.original=L.extend({},o.options),o.options.editing=e),o instanceof L.Marker?(o.editing&&o.editing.enable(),o.dragging.enable(),o.on("dragend",this._onMarkerDragEnd).on("touchmove",this._onTouchMove,this).on("MSPointerMove",this._onTouchMove,this).on("touchend",this._onMarkerDragEnd,this).on("MSPointerUp",this._onMarkerDragEnd,this)):o.editing.enable()},_disableLayerEdit:function(t){var e=t.layer||t.target||t;e.edited=!1,e.editing&&e.editing.disable(),delete e.options.editing,delete e.options.original,this._selectedPathOptions&&(e instanceof L.Marker?this._toggleMarkerHighlight(e):(e.setStyle(e.options.previousOptions),delete e.options.previousOptions)),e instanceof L.Marker?(e.dragging.disable(),e.off("dragend",this._onMarkerDragEnd,this).off("touchmove",this._onTouchMove,this).off("MSPointerMove",this._onTouchMove,this).off("touchend",this._onMarkerDragEnd,this).off("MSPointerUp",this._onMarkerDragEnd,this)):e.editing.disable()},_onMouseMove:function(t){this._tooltip.updatePosition(t.latlng)},_onMarkerDragEnd:function(t){var e=t.target;e.edited=!0,this._map.fire(L.Draw.Event.EDITMOVE,{layer:e})},_onTouchMove:function(t){var e=t.originalEvent.changedTouches[0],i=this._map.mouseEventToLayerPoint(e),o=this._map.layerPointToLatLng(i);t.target.setLatLng(o)},_hasAvailableLayers:function(){return 0!==this._featureGroup.getLayers().length}}),L.EditToolbar.Delete=L.Handler.extend({statics:{TYPE:"remove"},initialize:function(t,e){if(L.Handler.prototype.initialize.call(this,t),L.Util.setOptions(this,e),this._deletableLayers=this.options.featureGroup,!(this._deletableLayers instanceof L.FeatureGroup))throw new Error("options.featureGroup must be a L.FeatureGroup");this.type=L.EditToolbar.Delete.TYPE;var i=L.version.split(".");1===parseInt(i[0],10)&&parseInt(i[1],10)>=2?L.EditToolbar.Delete.include(L.Evented.prototype):L.EditToolbar.Delete.include(L.Mixin.Events)},enable:function(){!this._enabled&&this._hasAvailableLayers()&&(this.fire("enabled",{handler:this.type}),this._map.fire(L.Draw.Event.DELETESTART,{handler:this.type}),L.Handler.prototype.enable.call(this),this._deletableLayers.on("layeradd",this._enableLayerDelete,this).on("layerremove",this._disableLayerDelete,this))},disable:function(){this._enabled&&(this._deletableLayers.off("layeradd",this._enableLayerDelete,this).off("layerremove",this._disableLayerDelete,this),L.Handler.prototype.disable.call(this),this._map.fire(L.Draw.Event.DELETESTOP,{handler:this.type}),this.fire("disabled",{handler:this.type}))},addHooks:function(){var t=this._map;t&&(t.getContainer().focus(),this._deletableLayers.eachLayer(this._enableLayerDelete,this),this._deletedLayers=new L.LayerGroup,this._tooltip=new L.Draw.Tooltip(this._map),this._tooltip.updateContent({text:L.drawLocal.edit.handlers.remove.tooltip.text}),this._map.on("mousemove",this._onMouseMove,this))},removeHooks:function(){this._map&&(this._deletableLayers.eachLayer(this._disableLayerDelete,this),this._deletedLayers=null,this._tooltip.dispose(),this._tooltip=null,this._map.off("mousemove",this._onMouseMove,this))},revertLayers:function(){this._deletedLayers.eachLayer((function(t){this._deletableLayers.addLayer(t),t.fire("revert-deleted",{layer:t})}),this)},save:function(){this._map.fire(L.Draw.Event.DELETED,{layers:this._deletedLayers})},removeAllLayers:function(){this._deletableLayers.eachLayer((function(t){this._removeLayer({layer:t})}),this),this.save()},_enableLayerDelete:function(t){(t.layer||t.target||t).on("click",this._removeLayer,this)},_disableLayerDelete:function(t){var e=t.layer||t.target||t;e.off("click",this._removeLayer,this),this._deletedLayers.removeLayer(e)},_removeLayer:function(t){var e=t.layer||t.target||t;this._deletableLayers.removeLayer(e),this._deletedLayers.addLayer(e),e.fire("deleted")},_onMouseMove:function(t){this._tooltip.updatePosition(t.latlng)},_hasAvailableLayers:function(){return 0!==this._deletableLayers.getLayers().length}})})(); \ No newline at end of file diff --git a/inst/shiny/modules/penvs_drawBgExtent.R b/inst/shiny/modules/penvs_drawBgExtent.R index 06f066eb..31fdd059 100644 --- a/inst/shiny/modules/penvs_drawBgExtent.R +++ b/inst/shiny/modules/penvs_drawBgExtent.R @@ -207,14 +207,14 @@ penvs_drawBgExtent_module_map <- function(map, common) { curSp <- common$curSp occs <- common$occs - map %>% leaflet.extras::addDrawToolbar( + map %>% addDrawToolbar( targetGroup = 'draw', polylineOptions = FALSE, rectangleOptions = FALSE, circleOptions = FALSE, markerOptions = FALSE, circleMarkerOptions = FALSE, - editOptions = leaflet.extras::editToolbarOptions() + editOptions = editToolbarOptions() ) if (is.null(spp[[curSp()]]$procEnvs$bgExt)) { diff --git a/inst/shiny/modules/poccs_removeByID.R b/inst/shiny/modules/poccs_removeByID.R index 1fbf814d..00e40a4f 100644 --- a/inst/shiny/modules/poccs_removeByID.R +++ b/inst/shiny/modules/poccs_removeByID.R @@ -94,7 +94,7 @@ poccs_removeByID_module_server <- function(input, output, session, common) { poccs_removeByID_module_map <- function(map, common) { occs <- common$occs # Map logic - map %>% leaflet.extras::removeDrawToolbar() %>% + map %>% removeDrawToolbar() %>% clearAll() %>% addCircleMarkers(data = occs(), lat = ~latitude, lng = ~longitude, radius = 5, color = 'red', fill = TRUE, fillColor = "red", diff --git a/inst/shiny/modules/poccs_selectOccs.R b/inst/shiny/modules/poccs_selectOccs.R index 21655cd4..14715d7b 100644 --- a/inst/shiny/modules/poccs_selectOccs.R +++ b/inst/shiny/modules/poccs_selectOccs.R @@ -85,10 +85,10 @@ poccs_selectOccs_module_map <- function(map, common) { radius = 5, color = 'red', fill = TRUE, fillColor = "red", fillOpacity = 0.2, weight = 2, popup = ~pop) %>% zoom2Occs(occs()) %>% - leaflet.extras::addDrawToolbar(targetGroup='draw', polylineOptions = FALSE, + addDrawToolbar(targetGroup='draw', polylineOptions = FALSE, rectangleOptions = FALSE, circleOptions = FALSE, markerOptions = FALSE, circleMarkerOptions = FALSE, - editOptions = leaflet.extras::editToolbarOptions()) + editOptions = editToolbarOptions()) } poccs_selectOccs_module_rmd <- function(species) { diff --git a/inst/shiny/modules/poccs_thinOccs.R b/inst/shiny/modules/poccs_thinOccs.R index 118c5d13..02a06949 100644 --- a/inst/shiny/modules/poccs_thinOccs.R +++ b/inst/shiny/modules/poccs_thinOccs.R @@ -127,7 +127,7 @@ poccs_thinOccs_module_map <- function(map, common) { radius = 5, color = 'red', fill = TRUE, fillColor = "red", fillOpacity = 0.2, weight = 2, popup = ~pop) %>% zoom2Occs(occs()) %>% - leaflet.extras::removeDrawToolbar(clearFeatures = TRUE) + removeDrawToolbar(clearFeatures = TRUE) } } diff --git a/inst/shiny/modules/xfer_area.R b/inst/shiny/modules/xfer_area.R index 770f7baf..fc452494 100644 --- a/inst/shiny/modules/xfer_area.R +++ b/inst/shiny/modules/xfer_area.R @@ -368,10 +368,10 @@ xfer_area_module_map <- function(map, common) { mapXfer <- common$mapXfer # Map logic - map %>% leaflet.extras::addDrawToolbar( + map %>% addDrawToolbar( targetGroup = 'draw', polylineOptions = FALSE, rectangleOptions = FALSE, circleOptions = FALSE, markerOptions = FALSE, circleMarkerOptions = FALSE, - editOptions = leaflet.extras::editToolbarOptions() + editOptions = editToolbarOptions() ) # Add just transfer Polygon req(spp[[curSp()]]$transfer$xfExt) diff --git a/inst/shiny/modules/xfer_time.R b/inst/shiny/modules/xfer_time.R index 1dcb0864..2537a337 100644 --- a/inst/shiny/modules/xfer_time.R +++ b/inst/shiny/modules/xfer_time.R @@ -622,10 +622,10 @@ xfer_time_module_map <- function(map, common) { mapXfer <- common$mapXfer # Map logic - map %>% leaflet.extras::addDrawToolbar( + map %>% addDrawToolbar( targetGroup = 'draw', polylineOptions = FALSE, rectangleOptions = FALSE, circleOptions = FALSE, markerOptions = FALSE, circleMarkerOptions = FALSE, - editOptions = leaflet.extras::editToolbarOptions() + editOptions = editToolbarOptions() ) # Add just transfer Polygon req(spp[[curSp()]]$transfer$xfExt) diff --git a/inst/shiny/modules/xfer_user.R b/inst/shiny/modules/xfer_user.R index ab118993..2ebecaa8 100644 --- a/inst/shiny/modules/xfer_user.R +++ b/inst/shiny/modules/xfer_user.R @@ -393,10 +393,10 @@ xfer_user_module_map <- function(map, common) { mapXfer <- common$mapXfer # Map logic - map %>% leaflet.extras::addDrawToolbar( + map %>% addDrawToolbar( targetGroup = 'draw', polylineOptions = FALSE, rectangleOptions = FALSE, circleOptions = FALSE, markerOptions = FALSE, circleMarkerOptions = FALSE, - editOptions = leaflet.extras::editToolbarOptions() + editOptions = editToolbarOptions() ) # Add just Polygon of transfer req(spp[[curSp()]]$transfer$xfExt) From ae63de37ff0343cd185ab8e56cd70c238a3c0865 Mon Sep 17 00:00:00 2001 From: Daniel Lopez Date: Fri, 13 Mar 2026 14:20:30 -0400 Subject: [PATCH 07/17] Set Namespace with new draw functions and update documentation --- DESCRIPTION | 6 +-- NAMESPACE | 3 ++ R/utils_leaflet_draw.R | 39 +++++++------- inst/shiny/Rmd/text_intro_tab.Rmd | 2 +- man/addDrawToolbar.Rd | 86 +++++++++++++++++++++++++++++++ man/model_bioclim.Rd | 6 +-- man/model_maxent.Rd | 8 +-- man/penvs_bgMask.Rd | 8 +-- man/penvs_bgSample.Rd | 2 +- man/removeDrawToolbar.Rd | 17 ++++++ man/vis_bioclimPlot.Rd | 2 +- man/wallace-package.Rd | 37 +++++++++++++ man/xfer_area.Rd | 2 +- man/xfer_time.Rd | 2 +- man/xfer_userEnvs.Rd | 2 +- 15 files changed, 186 insertions(+), 36 deletions(-) create mode 100644 man/addDrawToolbar.Rd create mode 100644 man/removeDrawToolbar.Rd diff --git a/DESCRIPTION b/DESCRIPTION index 9db41cf8..88ba1533 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,5 +1,5 @@ Package: wallace -Version: 2.2.0 +Version: 2.2.1 Date: 2025-03-06 Title: A Modular Platform for Reproducible Modeling of Species Niches and Distributions @@ -41,9 +41,9 @@ Imports: ecospat (>= 4.0.0), ENMeval (>= 2.0.5), geodata, + htmltools, knitcitations, leafem, - leafpm, magrittr, markdown, methods, @@ -79,4 +79,4 @@ Suggests: License: GPL-3 URL: http://wallaceecomod.github.io/wallace/, Encoding: UTF-8 -RoxygenNote: 7.2.3 +RoxygenNote: 7.3.3 diff --git a/NAMESPACE b/NAMESPACE index 074f9abb..e4216cbf 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,10 +1,12 @@ # Generated by roxygen2: do not edit by hand +export(addDrawToolbar) export(clearAll) export(create_module) export(ecoClimate_getdata) export(ecoClimate_select) export(ecospat.plot.nicheDEV) +export(editToolbarOptions) export(envs_ecoClimate) export(envs_userEnvs) export(envs_worldclim) @@ -34,6 +36,7 @@ export(predictMaxnet) export(printVecAsis) export(register_module) export(remEnvsValsNA) +export(removeDrawToolbar) export(reverseLabel) export(run_wallace) export(smartProgress) diff --git a/R/utils_leaflet_draw.R b/R/utils_leaflet_draw.R index 37291171..92ccedf1 100644 --- a/R/utils_leaflet_draw.R +++ b/R/utils_leaflet_draw.R @@ -1,14 +1,14 @@ # ------------------------------------------------------------------------------ -# -# This file contains code adapted from the 'leaflet.extras' package (GPL-3) +# +# This file contains code adapted from the 'leaflet.extras' package (GPL-3) # by Sebastian Gatscha, Bhaskar Karambelkar, Barret Schloerke, et al. # Original Source: https://github.com/trafficonese/leaflet.extras # -# This code was included because 'leaflet.extras' was archived on CRAN -# (on 2026-02-19), and its functionality represents a hard dependency for -# this package. -# -# It provides the Leaflet Draw features required to enable polygon drawing +# This code was included because 'leaflet.extras' was archived on CRAN +# (on 2026-02-19), and its functionality represents a hard dependency for +# this package. +# +# It provides the Leaflet Draw features required to enable polygon drawing # and shape editing on the map. # # ----------------------------------------------------------------------------- @@ -23,7 +23,7 @@ drawDependencies <- function(drag = TRUE) { stylesheet = "lfx-draw-prod.css", all_files = TRUE ) - + if (drag) { drag_dep <- htmltools::htmlDependency( "lfx-draw-drag", @@ -284,7 +284,8 @@ selectedPathOptions <- function( #' @param remove Set to false to disable removing. #' @param selectedPathOptions To customize shapes in editing mode pass \code{\link{selectedPathOptions}}(). #' @param allowIntersection Determines if line segments can cross. -#' @noRd +#' @export +#' @keywords internal editToolbarOptions <- function( edit = TRUE, remove = TRUE, @@ -519,7 +520,8 @@ edittoolbarOptions <- function( #' \item \code{mouseover} #' \item \code{mouseout} #' } -#' @noRd +#' @export +#' @keywords internal addDrawToolbar <- function( map, targetLayerId = NULL, targetGroup = NULL, position = c("topleft", "topright", "bottomleft", "bottomright"), @@ -540,21 +542,21 @@ addDrawToolbar <- function( if (!is.null(targetGroup) && !is.null(targetLayerId)) { stop("To edit existing features either specify a targetGroup or a targetLayerId, but not both") } - + if (!inherits(toolbar, "list")) toolbar <- NULL if (!inherits(handlers, "list")) handlers <- NULL if (!inherits(edittoolbar, "list")) edittoolbar <- NULL if (!inherits(edithandlers, "list")) edithandlers <- NULL - + map$dependencies <- c(map$dependencies, drawDependencies(drag)) - + markerIconFunction <- NULL if (inherits(markerOptions, "list") && !is.null(markerOptions$markerIcon)) { stop("markerIcon is not supported in this version. Please use standard leaflet markers instead.") } - + position <- match.arg(position) - + options <- list( position = position, draw = leaflet::filterNULL(list( @@ -572,7 +574,7 @@ addDrawToolbar <- function( edittoolbar = edittoolbar, edithandlers = edithandlers ) - + leaflet::invokeMethod( map, leaflet::getMapData(map), "addDrawToolbar", targetLayerId, targetGroup, options @@ -582,7 +584,8 @@ addDrawToolbar <- function( #' Removes the draw toolbar #' @param map The map widget. #' @param clearFeatures Whether to clear the map of drawn features. -#' @noRd +#' @export +#' @keywords internal removeDrawToolbar <- function(map, clearFeatures = FALSE) { leaflet::invokeMethod(map, leaflet::getMapData(map), "removeDrawToolbar", clearFeatures) -} \ No newline at end of file +} diff --git a/inst/shiny/Rmd/text_intro_tab.Rmd b/inst/shiny/Rmd/text_intro_tab.Rmd index 554b7fba..f06081a2 100644 --- a/inst/shiny/Rmd/text_intro_tab.Rmd +++ b/inst/shiny/Rmd/text_intro_tab.Rmd @@ -5,7 +5,7 @@ output: html_document #### WORKFLOW -*Wallace* (v2.2.0) currently includes ten components, or steps of a possible workflow. Each component includes two or more modules, which are possible analyses for that step. +*Wallace* (v2.2.1) currently includes ten components, or steps of a possible workflow. Each component includes two or more modules, which are possible analyses for that step. **Components:** diff --git a/man/addDrawToolbar.Rd b/man/addDrawToolbar.Rd new file mode 100644 index 00000000..3eaab200 --- /dev/null +++ b/man/addDrawToolbar.Rd @@ -0,0 +1,86 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils_leaflet_draw.R +\name{addDrawToolbar} +\alias{addDrawToolbar} +\title{Adds a Toolbar to draw shapes/points on the map} +\usage{ +addDrawToolbar( + map, + targetLayerId = NULL, + targetGroup = NULL, + position = c("topleft", "topright", "bottomleft", "bottomright"), + polylineOptions = drawPolylineOptions(), + polygonOptions = drawPolygonOptions(), + circleOptions = drawCircleOptions(), + rectangleOptions = drawRectangleOptions(), + markerOptions = drawMarkerOptions(), + circleMarkerOptions = drawCircleMarkerOptions(), + editOptions = FALSE, + singleFeature = FALSE, + toolbar = NULL, + handlers = NULL, + edittoolbar = NULL, + edithandlers = NULL, + drag = TRUE +) +} +\arguments{ +\item{map}{The map widget.} + +\item{targetLayerId}{An optional layerId of a GeoJSON/TopoJSON layer whose features need to be editable. +Used for adding a GeoJSON/TopoJSON layer and then editing the features using the draw plugin.} + +\item{targetGroup}{An optional group name of a Feature Group whose features need to be editable. +Used for adding shapes(markers, lines, polygons) and then editing them using the draw plugin. +You can either set layerId or group or none but not both.} + +\item{position}{The position where the toolbar should appear.} + +\item{polylineOptions}{See \code{\link{drawPolylineOptions}}(). Set to FALSE to disable polyline drawing.} + +\item{polygonOptions}{See \code{\link{drawPolygonOptions}}(). Set to FALSE to disable polygon drawing.} + +\item{circleOptions}{See \code{\link{drawCircleOptions}}(). Set to FALSE to disable circle drawing.} + +\item{rectangleOptions}{See \code{\link{drawRectangleOptions}}(). Set to FALSE to disable rectangle drawing.} + +\item{markerOptions}{See \code{\link{drawMarkerOptions}}(). Set to FALSE to disable marker drawing.} + +\item{circleMarkerOptions}{See \code{\link{drawCircleMarkerOptions}}(). Set to FALSE to disable circle marker drawing.} + +\item{editOptions}{By default editing is disable. To enable editing pass \code{\link{editToolbarOptions}}().} + +\item{singleFeature}{When set to TRUE, only one feature can be drawn at a time, the previous ones being removed.} + +\item{toolbar}{See \code{\link{toolbarOptions}}. Set to \code{NULL} to take Leaflets default values.} + +\item{handlers}{See \code{\link{handlersOptions}}. Set to \code{NULL} to take Leaflets default values.} + +\item{edittoolbar}{See \code{\link{edittoolbarOptions}}. Set to \code{NULL} to take Leaflets default values.} + +\item{edithandlers}{See \code{\link{edithandlersOptions}}. Set to \code{NULL} to take Leaflets default values.} + +\item{drag}{When set to \code{TRUE}, the drawn features will be draggable during editing, utilizing +the \code{Leaflet.Draw.Drag} plugin. Otherwise, this library will not be included.} +} +\description{ +Adds a Toolbar to draw shapes/points on the map +} +\details{ +The drawn features emit events upon mouse interaction. +Event names follow the pattern: \code{input$MAPID_LAYERCATEGORY_EVENTNAME}, +where \code{LAYERCATEGORY} can be one of: +\itemize{ + \item \code{marker} + \item \code{shape} + \item \code{polyline} +} + +Similarly, for \code{EVENTNAME}, valid values are: +\itemize{ + \item \code{click} + \item \code{mouseover} + \item \code{mouseout} +} +} +\keyword{internal} diff --git a/man/model_bioclim.Rd b/man/model_bioclim.Rd index 9147402a..c422bdb3 100644 --- a/man/model_bioclim.Rd +++ b/man/model_bioclim.Rd @@ -15,8 +15,8 @@ components occs: Obtain occurrence data or, poccs: Process occurrence data.} \item{user.grp}{a list of two vectors containing group assignments for occurrences (occs.grp) and background points (bg.grp).} -\item{bgMsk}{a RasterStack or a RasterBrick of environmental layers cropped -and masked to match the provided background extent.} +\item{bgMsk}{a SpatRaster of environmental layers cropped and masked to +match the provided background extent.} \item{logger}{Stores all notification messages to be displayed in the Log Window of Wallace GUI. Insert the logger reactive list here for running @@ -49,7 +49,7 @@ occs <- read.csv(system.file("extdata/Bassaricyon_alleni.csv", bg <- read.csv(system.file("extdata/Bassaricyon_alleni_bgPoints.csv", package = "wallace")) partblock <- part_partitionOccs(occs, bg, method = 'block') -m <- model_bioclim(occs, bg, partblock, envs) +m <- model_bioclim(occs, bg, partblock, terra::rast(envs)) } } diff --git a/man/model_maxent.Rd b/man/model_maxent.Rd index 0f5ad1e5..aa8f3958 100644 --- a/man/model_maxent.Rd +++ b/man/model_maxent.Rd @@ -30,8 +30,8 @@ components occs: Obtain occurrence data or, poccs: Process occurrence data.} \item{user.grp}{a list of two vectors containing group assignments for occurrences (occs.grp) and background points (bg.grp).} -\item{bgMsk}{a RasterStack or a RasterBrick of environmental layers cropped -and masked to match the provided background extent.} +\item{bgMsk}{a SpatRaster of environmental layers cropped and masked to +match the provided background extent.} \item{rms}{vector of range of regularization multipliers to be used in the ENMeval run.} @@ -91,7 +91,7 @@ rms <- c(1:2) rmsStep <- 1 fcs <- c('L', 'LQ') m <- model_maxent(occs = occs, bg = bg, user.grp = partblock, - bgMsk = envs, rms = rms, rmsStep, fcs, + bgMsk = terra::rast(envs), rms = rms, rmsStep, fcs, clampSel = TRUE, algMaxent = "maxnet", parallel = FALSE) } @@ -106,4 +106,6 @@ Jamie M. Kass Gonzalo E. Pinilla-Buitrago Bethany A. Johnson + +Daniel Lopez-Lozano } diff --git a/man/penvs_bgMask.Rd b/man/penvs_bgMask.Rd index 24bf1664..6081df1c 100644 --- a/man/penvs_bgMask.Rd +++ b/man/penvs_bgMask.Rd @@ -23,7 +23,7 @@ in shiny, otherwise leave the default NULL.} \item{spN}{species name to be used for all logger messages} } \value{ -A RasterStack or a RasterBrick of environmental layers cropped and +A SpatRaster of environmental layers cropped and masked to match the provided background extent. } \description{ @@ -35,8 +35,8 @@ This function is used in the select study region component. Here, the environmental layers to be used in the modeling are cropped and masked to the provided background area. The background area is determined in the function penvs_bgExtent from the same component. The function returns - the provided environmental layers cropped and masked in the provided - format (either a rasterBrick or a rasterStack). + the provided environmental layers cropped and masked in the SpatRaster + format. } \examples{ \dontrun{ @@ -63,4 +63,6 @@ bgMask <- penvs_bgMask(occs, envs, bgExt) Jamie Kass Gonzalo E. Pinilla-Buitrago + +Daniel Lopez-Lozano } diff --git a/man/penvs_bgSample.Rd b/man/penvs_bgSample.Rd index 3c2f8b06..1b00c281 100644 --- a/man/penvs_bgSample.Rd +++ b/man/penvs_bgSample.Rd @@ -54,7 +54,7 @@ envs <- envs_worldclim(bcRes = 10, doBrick = TRUE) bgExt <- penvs_bgExtent(occs, bgSel = 'bounding box', bgBuf = 0.5) bgMask <- penvs_bgMask(occs, envs, bgExt) -bgsample <- penvs_bgSample(occs, bgMask, bgPtsNum = 1000) +bgsample <- penvs_bgSample(occs, raster::stack(bgMask), bgPtsNum = 1000) } } diff --git a/man/removeDrawToolbar.Rd b/man/removeDrawToolbar.Rd new file mode 100644 index 00000000..35b34ba0 --- /dev/null +++ b/man/removeDrawToolbar.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils_leaflet_draw.R +\name{removeDrawToolbar} +\alias{removeDrawToolbar} +\title{Removes the draw toolbar} +\usage{ +removeDrawToolbar(map, clearFeatures = FALSE) +} +\arguments{ +\item{map}{The map widget.} + +\item{clearFeatures}{Whether to clear the map of drawn features.} +} +\description{ +Removes the draw toolbar +} +\keyword{internal} diff --git a/man/vis_bioclimPlot.Rd b/man/vis_bioclimPlot.Rd index 2e055e1a..56a64506 100644 --- a/man/vis_bioclimPlot.Rd +++ b/man/vis_bioclimPlot.Rd @@ -51,7 +51,7 @@ occs <- read.csv(system.file("extdata/Bassaricyon_alleni.csv", bg <- read.csv(system.file("extdata/Bassaricyon_alleni_bgPoints.csv", package = "wallace")) partblock <- part_partitionOccs(occs, bg, method = 'block') -m <- model_bioclim(occs, bg, partblock, envs) +m <- model_bioclim(occs, bg, partblock, terra::rast(envs)) bioclimPlot <- vis_bioclimPlot(x = m@models$bioclim, a = 1, b = 2, p = 1) } diff --git a/man/wallace-package.Rd b/man/wallace-package.Rd index c616cf55..9157f79a 100644 --- a/man/wallace-package.Rd +++ b/man/wallace-package.Rd @@ -23,3 +23,40 @@ Please see the official website (\url{https://wallaceecomod.github.io/}) for in the \href{https://groups.google.com/forum/#!forum/wallaceecomod}{Google Group}, or email the team directly: \email{wallaceEcoMod@gmail.com}. } +\seealso{ +Useful links: +\itemize{ + \item \url{http://wallaceecomod.github.io/wallace/,} +} + +} +\author{ +\strong{Maintainer}: Mary E. Blair \email{mblair1@amnh.org} + +Authors: +\itemize{ + \item Bethany A. Johnson \email{bjohnso005@citymail.cuny.edu} + \item Daniel F. Lopez-Lozano \email{dlopezlozano@amnh.org} + \item Jamie M. Kass \email{jamie.m.kass@gmail.com} + \item Gonzalo E. Pinilla-Buitrago \email{gepinillab@gmail.com} + \item Andrea Paz \email{paz.andreita@gmail.com} + \item Valentina Grisales-Betancur \email{vgrisale@eafit.edu.co} + \item Dean Attali \email{daattali@gmail.com} + \item Matthew E. Aiello-Lammens \email{matt.lammens@gmail.com} + \item Cory Merow \email{corymerow@gmail.com} + \item Robert P. Anderson \email{randerson@ccny.cuny.edu} +} + +Other contributors: +\itemize{ + \item Sarah I. Meenan \email{sarah.meenan@gmail.com} [contributor] + \item Olivier Broennimann \email{olivier.broennimann@unil.ch} [contributor] + \item Peter J. Galante \email{pgalante@amnh.org} [contributor] + \item Brian S. Maitner \email{bmaitner@gmail.com} [contributor] + \item Hannah L. Owens \email{hannah.owens@gmail.com} [contributor] + \item Sara Varela \email{sara_varela@yahoo.com} [contributor] + \item Bruno Vilela \email{bvilela@wustl.edu} [contributor] + \item Robert Muscarella \email{bob.muscarella@gmail.com} [contributor] +} + +} diff --git a/man/xfer_area.Rd b/man/xfer_area.Rd index b58e97fd..d526ff34 100644 --- a/man/xfer_area.Rd +++ b/man/xfer_area.Rd @@ -88,7 +88,7 @@ bg <- read.csv(system.file("extdata/Bassaricyon_alleni_bgPoints.csv", partblock <- part_partitionOccs(occs, bg, method = 'block') m <- model_maxent(occs, bg, user.grp = partblock, - bgMsk = envs, rms = c(1:2), + bgMsk = terra::rast(envs), rms = c(1:2), rmsStep = 1, fcs = c('L', 'LQ'), clampSel = TRUE, algMaxent = "maxnet", diff --git a/man/xfer_time.Rd b/man/xfer_time.Rd index 62434ae8..96609d6f 100644 --- a/man/xfer_time.Rd +++ b/man/xfer_time.Rd @@ -88,7 +88,7 @@ polyExt <- sp::SpatialPolygons(list(sp::Polygons(list(sp::Polygon(selCoords)), occs <- read.csv(system.file("extdata/Bassaricyon_alleni.csv",package = "wallace")) bg <- read.csv(system.file("extdata/Bassaricyon_alleni_bgPoints.csv", package = "wallace")) partblock <- part_partitionOccs(occs, bg, method = 'block') -m <- model_maxent(occs, bg, user.grp = partblock, bgMsk = envs, rms = c(1:2), +m <- model_maxent(occs, bg, user.grp = partblock, bgMsk = terra::rast(envs), rms = c(1:2), rmsStep = 1, fcs = c('L', 'LQ'), clampSel = TRUE, algMaxent = "maxnet", parallel = FALSE) occsEnvs <- m@occs diff --git a/man/xfer_userEnvs.Rd b/man/xfer_userEnvs.Rd index 5e493059..dcc2e978 100644 --- a/man/xfer_userEnvs.Rd +++ b/man/xfer_userEnvs.Rd @@ -78,7 +78,7 @@ package = "wallace"), pattern = ".tif$", full.names = TRUE), rasName = list.files(system.file("extdata/wc",package = "wallace"), pattern = ".tif$", full.names = FALSE)) partblock <- part_partitionOccs(occs, bg, method = 'block') -m <- model_maxent(occs, bg, user.grp = partblock, bgMsk = envs, rms = c(1:2), +m <- model_maxent(occs, bg, user.grp = partblock, bgMsk = terra::rast(envs), rms = c(1:2), rmsStep = 1, fcs = c('L', 'LQ'), clampSel = TRUE, algMaxent = "maxnet", parallel = FALSE) envsFut <- list.files(path = system.file('extdata/wc/future', package = "wallace"), From 4445879614b50cea73e0089b8ac1eb389a573ab9 Mon Sep 17 00:00:00 2001 From: Daniel Lopez Date: Fri, 13 Mar 2026 14:31:52 -0400 Subject: [PATCH 08/17] Adjust the draw toolbar removal logic --- R/helper_functions.R | 2 +- inst/shiny/modules/poccs_removeByID.R | 5 ++--- inst/shiny/modules/poccs_thinOccs.R | 5 ++--- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/R/helper_functions.R b/R/helper_functions.R index 84c8a5b0..2ed46dc0 100644 --- a/R/helper_functions.R +++ b/R/helper_functions.R @@ -186,7 +186,7 @@ writeLog <- function(logger, ..., type = 'default') { #' @export clearAll <- function(map) { map %>% clearMarkers() %>% clearShapes() %>% clearImages() %>% - clearControls() %>% removeLayersControl() + clearControls() %>% removeLayersControl() %>% removeDrawToolbar() } #' @title polyZoom diff --git a/inst/shiny/modules/poccs_removeByID.R b/inst/shiny/modules/poccs_removeByID.R index 00e40a4f..0429784d 100644 --- a/inst/shiny/modules/poccs_removeByID.R +++ b/inst/shiny/modules/poccs_removeByID.R @@ -1,6 +1,6 @@ # Wallace EcoMod: a flexible platform for reproducible modeling of # species niches and distributions. -# +# # poccs_removeByID.R # File author: Wallace EcoMod Dev Team. 2023. # -------------------------------------------------------------------------- @@ -94,8 +94,7 @@ poccs_removeByID_module_server <- function(input, output, session, common) { poccs_removeByID_module_map <- function(map, common) { occs <- common$occs # Map logic - map %>% removeDrawToolbar() %>% - clearAll() %>% + map %>% clearAll() %>% addCircleMarkers(data = occs(), lat = ~latitude, lng = ~longitude, radius = 5, color = 'red', fill = TRUE, fillColor = "red", fillOpacity = 0.2, weight = 2, popup = ~pop) %>% diff --git a/inst/shiny/modules/poccs_thinOccs.R b/inst/shiny/modules/poccs_thinOccs.R index 02a06949..d706587a 100644 --- a/inst/shiny/modules/poccs_thinOccs.R +++ b/inst/shiny/modules/poccs_thinOccs.R @@ -1,6 +1,6 @@ # Wallace EcoMod: a flexible platform for reproducible modeling of # species niches and distributions. -# +# # poccs_thinOccs.R # File author: Wallace EcoMod Dev Team. 2023. # -------------------------------------------------------------------------- @@ -126,8 +126,7 @@ poccs_thinOccs_module_map <- function(map, common) { addCircleMarkers(data = occs(), lat = ~latitude, lng = ~longitude, radius = 5, color = 'red', fill = TRUE, fillColor = "red", fillOpacity = 0.2, weight = 2, popup = ~pop) %>% - zoom2Occs(occs()) %>% - removeDrawToolbar(clearFeatures = TRUE) + zoom2Occs(occs()) } } From 6abc3e05092db2daae1ea1489ffbb5eb5b4ac144 Mon Sep 17 00:00:00 2001 From: Daniel Lopez Date: Fri, 20 Mar 2026 11:13:38 -0400 Subject: [PATCH 09/17] Prevent toolbar from disappearing when moving to different modules --- R/helper_functions.R | 2 +- inst/shiny/modules/penvs_drawBgExtent.R | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/R/helper_functions.R b/R/helper_functions.R index 2ed46dc0..84c8a5b0 100644 --- a/R/helper_functions.R +++ b/R/helper_functions.R @@ -186,7 +186,7 @@ writeLog <- function(logger, ..., type = 'default') { #' @export clearAll <- function(map) { map %>% clearMarkers() %>% clearShapes() %>% clearImages() %>% - clearControls() %>% removeLayersControl() %>% removeDrawToolbar() + clearControls() %>% removeLayersControl() } #' @title polyZoom diff --git a/inst/shiny/modules/penvs_drawBgExtent.R b/inst/shiny/modules/penvs_drawBgExtent.R index 31fdd059..9023e391 100644 --- a/inst/shiny/modules/penvs_drawBgExtent.R +++ b/inst/shiny/modules/penvs_drawBgExtent.R @@ -1,6 +1,6 @@ # Wallace EcoMod: a flexible platform for reproducible modeling of # species niches and distributions. -# +# # penvs_drawBgExtent.R # File author: Wallace EcoMod Dev Team. 2023. # -------------------------------------------------------------------------- @@ -207,16 +207,6 @@ penvs_drawBgExtent_module_map <- function(map, common) { curSp <- common$curSp occs <- common$occs - map %>% addDrawToolbar( - targetGroup = 'draw', - polylineOptions = FALSE, - rectangleOptions = FALSE, - circleOptions = FALSE, - markerOptions = FALSE, - circleMarkerOptions = FALSE, - editOptions = editToolbarOptions() - ) - if (is.null(spp[[curSp()]]$procEnvs$bgExt)) { map %>% clearAll() %>% addCircleMarkers(data = occs(), lat = ~latitude, lng = ~longitude, @@ -239,6 +229,16 @@ penvs_drawBgExtent_module_map <- function(map, common) { group = 'bgShp') } } + + map %>% addDrawToolbar( + targetGroup = 'draw', + polylineOptions = FALSE, + rectangleOptions = FALSE, + circleOptions = FALSE, + markerOptions = FALSE, + circleMarkerOptions = FALSE, + editOptions = editToolbarOptions() + ) } penvs_drawBgExtent_module_rmd <- function(species) { From 808d7bcd3e22ea11f43666c64fdad02d2fe8f2b3 Mon Sep 17 00:00:00 2001 From: "Bethany A. Johnson" Date: Thu, 23 Apr 2026 16:23:59 -0400 Subject: [PATCH 10/17] raster to stack (#462) * Raster to Stack when visualizing the prediction in the Rmd. * "define threshold value" missing from the xfer_time Rmd. --- inst/shiny/modules/vis_mapPreds.Rmd | 4 ++-- inst/shiny/modules/xfer_time.Rmd | 30 +++++++++++++++++++++++------ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/inst/shiny/modules/vis_mapPreds.Rmd b/inst/shiny/modules/vis_mapPreds.Rmd index beacf66f..ed9f987e 100644 --- a/inst/shiny/modules/vis_mapPreds.Rmd +++ b/inst/shiny/modules/vis_mapPreds.Rmd @@ -181,7 +181,7 @@ Generate a map of the maxnet generated model with no threshold ```{r, echo = {{vis_mapPreds_knit & vis_map_maxnet_knit & !vis_map_threshold_knit}}, include = {{vis_mapPreds_knit & vis_map_maxnet_knit & !vis_map_threshold_knit}}} # Select current model and obtain raster prediction m_{{spAbr}} <- model_{{spAbr}}@models[["{{curModel_rmd}}"]] -predSel_{{spAbr}} <- predictMaxnet(m_{{spAbr}}, raster(bgMask_{{spAbr}}), +predSel_{{spAbr}} <- predictMaxnet(m_{{spAbr}}, stack(bgMask_{{spAbr}}), type = "{{predType_rmd}}", clamp = {{clamp_rmd}}) #Get values of prediction @@ -218,7 +218,7 @@ Generate a map of the maxnet generated model with with a "{{thresholdRule_rmd}}" ```{r, echo = {{vis_mapPreds_knit & vis_map_maxnet_knit & vis_map_threshold_knit}}, include = {{vis_mapPreds_knit & vis_map_maxnet_knit & vis_map_threshold_knit}}} # Select current model and obtain raster prediction m_{{spAbr}} <- model_{{spAbr}}@models[["{{curModel_rmd}}"]] -predSel_{{spAbr}} <- predictMaxnet(m_{{spAbr}}, raster(bgMask_{{spAbr}}), +predSel_{{spAbr}} <- predictMaxnet(m_{{spAbr}}, stack(bgMask_{{spAbr}}), type = "{{predType_rmd}}", clamp = {{clamp_rmd}}) # extract the suitability values for all occurrences diff --git a/inst/shiny/modules/xfer_time.Rmd b/inst/shiny/modules/xfer_time.Rmd index 31041ebe..bd2e2028 100644 --- a/inst/shiny/modules/xfer_time.Rmd +++ b/inst/shiny/modules/xfer_time.Rmd @@ -101,8 +101,11 @@ occPredVals_{{spAbr}} <- raster::extract(predSel_{{spAbr}}, occs_xy_{{spAbr}}) # Define probability of quantile based on selected threshold xfer_thresProb_{{spAbr}} <- switch("{{xfer_thresholdRule_rmd}}", "mtp" = 0, "p10" = 0.1, "qtp" = {{xfer_probQuantile_rmd}}) +# Define threshold value +xfer_thres_{{spAbr}} <- stats::quantile(occPredVals_{{spAbr}}, + probs = xfer_thresProb_{{spAbr}}) # Add threshold if specified -xfer_time_{{spAbr}} <- xfer_time_{{spAbr}}$xferTime > xfer_thresProb_{{spAbr}} +xfer_time_{{spAbr}} <- xfer_time_{{spAbr}}$xferTime > xfer_thres_{{spAbr}} ##Make map ###Make map of transfer @@ -246,8 +249,11 @@ occPredVals_{{spAbr}} <- raster::extract(predSel_{{spAbr}}, occs_xy_{{spAbr}}) # Define probability of quantile based on selected threshold xfer_thresProb_{{spAbr}} <- switch("{{xfer_thresholdRule_rmd}}", "mtp" = 0, "p10" = 0.1, "qtp" = {{xfer_probQuantile_rmd}}) +# Define threshold value +xfer_thres_{{spAbr}} <- stats::quantile(occPredVals_{{spAbr}}, + probs = xfer_thresProb_{{spAbr}}) # Add threshold if specified -xfer_time_{{spAbr}} <- xfer_time_{{spAbr}}$xferTime > xfer_thresProb_{{spAbr}} +xfer_time_{{spAbr}} <- xfer_time_{{spAbr}}$xferTime > xfer_thres_{{spAbr}} ##Make map ###Make map of transfer @@ -393,8 +399,11 @@ occPredVals_{{spAbr}} <- raster::extract(predSel_{{spAbr}}, occs_xy_{{spAbr}}) # Define probability of quantile based on selected threshold xfer_thresProb_{{spAbr}} <- switch("{{xfer_thresholdRule_rmd}}", "mtp" = 0, "p10" = 0.1, "qtp" = {{xfer_probQuantile_rmd}}) +# Define threshold value +xfer_thres_{{spAbr}} <- stats::quantile(occPredVals_{{spAbr}}, + probs = xfer_thresProb_{{spAbr}}) # Add threshold if specified -xfer_time_{{spAbr}} <- xfer_time_{{spAbr}}$xferTime > xfer_thresProb_{{spAbr}} +xfer_time_{{spAbr}} <- xfer_time_{{spAbr}}$xferTime > xfer_thres_{{spAbr}} ##Make map ###Make map of transfer @@ -509,8 +518,11 @@ occPredVals_{{spAbr}} <- raster::extract(predSel_{{spAbr}}, occs_xy_{{spAbr}}) # Define probability of quantile based on selected threshold xfer_thresProb_{{spAbr}} <- switch("{{xfer_thresholdRule_rmd}}", "mtp" = 0, "p10" = 0.1, "qtp" = {{xfer_probQuantile_rmd}}) +# Define threshold value +xfer_thres_{{spAbr}} <- stats::quantile(occPredVals_{{spAbr}}, + probs = xfer_thresProb_{{spAbr}}) # Add threshold if specified -xfer_time_{{spAbr}} <- xfer_time_{{spAbr}}$xferTime > xfer_thresProb_{{spAbr}} +xfer_time_{{spAbr}} <- xfer_time_{{spAbr}}$xferTime > xfer_thres_{{spAbr}} ##Make map ###Make map of transfer @@ -638,8 +650,11 @@ occPredVals_{{spAbr}} <- raster::extract(predSel_{{spAbr}}, occs_xy_{{spAbr}}) # Define probability of quantile based on selected threshold xfer_thresProb_{{spAbr}} <- switch("{{xfer_thresholdRule_rmd}}", "mtp" = 0, "p10" = 0.1, "qtp" = {{xfer_probQuantile_rmd}}) +# Define threshold value +xfer_thres_{{spAbr}} <- stats::quantile(occPredVals_{{spAbr}}, + probs = xfer_thresProb_{{spAbr}}) # Add threshold if specified -xfer_time_{{spAbr}} <- xfer_time_{{spAbr}}$xferTime > xfer_thresProb_{{spAbr}} +xfer_time_{{spAbr}} <- xfer_time_{{spAbr}}$xferTime > xfer_thres_{{spAbr}} ##Make map ###Make map of transfer @@ -769,8 +784,11 @@ occPredVals_{{spAbr}} <- raster::extract(predSel_{{spAbr}}, occs_xy_{{spAbr}}) # Define probability of quantile based on selected threshold xfer_thresProb_{{spAbr}} <- switch("{{xfer_thresholdRule_rmd}}", "mtp" = 0, "p10" = 0.1, "qtp" = {{xfer_probQuantile_rmd}}) +# Define threshold value +xfer_thres_{{spAbr}} <- stats::quantile(occPredVals_{{spAbr}}, + probs = xfer_thresProb_{{spAbr}}) # Add threshold if specified -xfer_time_{{spAbr}} <- xfer_time_{{spAbr}}$xferTime > xfer_thresProb_{{spAbr}} +xfer_time_{{spAbr}} <- xfer_time_{{spAbr}}$xferTime > xfer_thres_{{spAbr}} ##Make map ###Make map of transfer From 96669b8621c66ffe54a7b7232ea702f7ed24ca74 Mon Sep 17 00:00:00 2001 From: Daniel Lopez Date: Tue, 28 Apr 2026 11:24:57 -0400 Subject: [PATCH 11/17] Fix links and added new code documentation --- R/utils_leaflet_draw.R | 40 +++++++++++++++++++-------------------- man/addDrawToolbar.Rd | 22 ++++++++++----------- man/editToolbarOptions.Rd | 26 +++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 31 deletions(-) create mode 100644 man/editToolbarOptions.Rd diff --git a/R/utils_leaflet_draw.R b/R/utils_leaflet_draw.R index 92ccedf1..333c6dba 100644 --- a/R/utils_leaflet_draw.R +++ b/R/utils_leaflet_draw.R @@ -98,7 +98,7 @@ drawShapeOptions <- function( #' @param feet When not metric, use feet instead of yards for display. #' @param nautic When not metric, not feet, use nautic mile for display. #' @param zIndexOffset This should be a high number to ensure that you can draw over all other layers on the map. -#' @param shapeOptions Leaflet Polyline options. See \code{\link{drawShapeOptions}}(). +#' @param shapeOptions Leaflet Polyline options. See \code{drawShapeOptions()}. #' @param repeatMode Determines if the draw tool remains enabled after drawing a shape. #' @noRd drawPolylineOptions <- function( @@ -132,7 +132,7 @@ drawPolylineOptions <- function( #' Options for drawing polygons #' @param showArea Show the area of the drawn polygon in m², ha or km². The area is only approximate and become less accurate the larger the polygon is. #' @param metric Determines which measurement system (metric or imperial) is used. -#' @param shapeOptions Shape options. See \code{\link{drawShapeOptions}}(). +#' @param shapeOptions Shape options. See \code{drawShapeOptions()}. #' @param repeatMode Determines if the draw tool remains enabled after drawing a shape. #' @noRd drawPolygonOptions <- function( @@ -152,7 +152,7 @@ drawPolygonOptions <- function( #' Options for drawing rectangles #' @param showArea Show the area of the drawn rectangle in m², ha or km². #' @param metric Determines which measurement system (metric or imperial) is used. -#' @param shapeOptions Shape options. See \code{\link{drawShapeOptions}}(). +#' @param shapeOptions Shape options. See \code{drawShapeOptions()}. #' @param repeatMode Determines if the draw tool remains enabled after drawing a shape. #' @noRd drawRectangleOptions <- function( @@ -174,7 +174,7 @@ drawRectangleOptions <- function( #' @param metric Determines which measurement system (metric or imperial) is used. #' @param feet When not metric, use feet instead of yards for display. #' @param nautic When not metric, not feet, use nautic mile for display. -#' @param shapeOptions Shape options. See \code{\link{drawShapeOptions}}(). +#' @param shapeOptions Shape options. See \code{drawShapeOptions()}. #' @param repeatMode Determines if the draw tool remains enabled after drawing a shape. #' @noRd drawCircleOptions <- function( @@ -282,7 +282,7 @@ selectedPathOptions <- function( #' Options for editing shapes #' @param edit Editing enabled by default. Set to false do disable editing. #' @param remove Set to false to disable removing. -#' @param selectedPathOptions To customize shapes in editing mode pass \code{\link{selectedPathOptions}}(). +#' @param selectedPathOptions To customize shapes in editing mode pass \code{selectedPathOptions()}. #' @param allowIntersection Determines if line segments can cross. #' @export #' @keywords internal @@ -301,7 +301,7 @@ editToolbarOptions <- function( } #' Options for editing handlers -#' @description Customize tooltips for \code{\link{addDrawToolbar}} +#' @description Customize tooltips for \code{addDrawToolbar()} #' @param polyline List of options for polyline tooltips. #' @param polygon List of options for polygon tooltips. #' @param rectangle List of options for rectangle tooltips. @@ -367,7 +367,7 @@ handlersOptions <- function( } #' Options for editing the toolbar -#' @description Customize the toolbar for \code{\link{addDrawToolbar}} +#' @description Customize the toolbar for \code{addDrawToolbar()} #' @param actions List of options for actions toolbar button. #' @param finish List of options for finish toolbar button. #' @param undo List of options for undo toolbar button. @@ -420,7 +420,7 @@ toolbarOptions <- function( } #' Options for editing edit handlers -#' @description Customize edit handlers for \code{\link{addDrawToolbar}} +#' @description Customize edit handlers for \code{addDrawToolbar()} #' @param edit List of options for editing tooltips. #' @param remove List of options for removing tooltips. #' @noRd @@ -449,7 +449,7 @@ edithandlersOptions <- function( } #' Options for editing the toolbar -#' @description Customize the edit toolbar for \code{\link{addDrawToolbar}} +#' @description Customize the edit toolbar for \code{addDrawToolbar()} #' @param actions List of options for edit action tooltips. #' @param buttons List of options for edit button tooltips. #' @noRd @@ -489,18 +489,18 @@ edittoolbarOptions <- function( #' Used for adding shapes(markers, lines, polygons) and then editing them using the draw plugin. #' You can either set layerId or group or none but not both. #' @param position The position where the toolbar should appear. -#' @param polylineOptions See \code{\link{drawPolylineOptions}}(). Set to FALSE to disable polyline drawing. -#' @param polygonOptions See \code{\link{drawPolygonOptions}}(). Set to FALSE to disable polygon drawing. -#' @param circleOptions See \code{\link{drawCircleOptions}}(). Set to FALSE to disable circle drawing. -#' @param rectangleOptions See \code{\link{drawRectangleOptions}}(). Set to FALSE to disable rectangle drawing. -#' @param markerOptions See \code{\link{drawMarkerOptions}}(). Set to FALSE to disable marker drawing. -#' @param circleMarkerOptions See \code{\link{drawCircleMarkerOptions}}(). Set to FALSE to disable circle marker drawing. -#' @param editOptions By default editing is disable. To enable editing pass \code{\link{editToolbarOptions}}(). +#' @param polylineOptions See \code{drawPolylineOptions()}. Set to FALSE to disable polyline drawing. +#' @param polygonOptions See \code{drawPolygonOptions()}. Set to FALSE to disable polygon drawing. +#' @param circleOptions See \code{drawCircleOptions()}. Set to FALSE to disable circle drawing. +#' @param rectangleOptions See \code{drawRectangleOptions()}. Set to FALSE to disable rectangle drawing. +#' @param markerOptions See \code{drawMarkerOptions()}. Set to FALSE to disable marker drawing. +#' @param circleMarkerOptions See \code{drawCircleMarkerOptions()}. Set to FALSE to disable circle marker drawing. +#' @param editOptions By default editing is disable. To enable editing pass \code{editToolbarOptions()}. #' @param singleFeature When set to TRUE, only one feature can be drawn at a time, the previous ones being removed. -#' @param toolbar See \code{\link{toolbarOptions}}. Set to \code{NULL} to take Leaflets default values. -#' @param handlers See \code{\link{handlersOptions}}. Set to \code{NULL} to take Leaflets default values. -#' @param edittoolbar See \code{\link{edittoolbarOptions}}. Set to \code{NULL} to take Leaflets default values. -#' @param edithandlers See \code{\link{edithandlersOptions}}. Set to \code{NULL} to take Leaflets default values. +#' @param toolbar See \code{toolbarOptions()}. Set to \code{NULL} to take Leaflets default values. +#' @param handlers See \code{handlersOptions()}. Set to \code{NULL} to take Leaflets default values. +#' @param edittoolbar See \code{edittoolbarOptions()}. Set to \code{NULL} to take Leaflets default values. +#' @param edithandlers See \code{edithandlersOptions()}. Set to \code{NULL} to take Leaflets default values. #' @param drag When set to \code{TRUE}, the drawn features will be draggable during editing, utilizing #' the \code{Leaflet.Draw.Drag} plugin. Otherwise, this library will not be included. #' diff --git a/man/addDrawToolbar.Rd b/man/addDrawToolbar.Rd index 3eaab200..82b56c7a 100644 --- a/man/addDrawToolbar.Rd +++ b/man/addDrawToolbar.Rd @@ -36,29 +36,29 @@ You can either set layerId or group or none but not both.} \item{position}{The position where the toolbar should appear.} -\item{polylineOptions}{See \code{\link{drawPolylineOptions}}(). Set to FALSE to disable polyline drawing.} +\item{polylineOptions}{See \code{drawPolylineOptions()}. Set to FALSE to disable polyline drawing.} -\item{polygonOptions}{See \code{\link{drawPolygonOptions}}(). Set to FALSE to disable polygon drawing.} +\item{polygonOptions}{See \code{drawPolygonOptions()}. Set to FALSE to disable polygon drawing.} -\item{circleOptions}{See \code{\link{drawCircleOptions}}(). Set to FALSE to disable circle drawing.} +\item{circleOptions}{See \code{drawCircleOptions()}. Set to FALSE to disable circle drawing.} -\item{rectangleOptions}{See \code{\link{drawRectangleOptions}}(). Set to FALSE to disable rectangle drawing.} +\item{rectangleOptions}{See \code{drawRectangleOptions()}. Set to FALSE to disable rectangle drawing.} -\item{markerOptions}{See \code{\link{drawMarkerOptions}}(). Set to FALSE to disable marker drawing.} +\item{markerOptions}{See \code{drawMarkerOptions()}. Set to FALSE to disable marker drawing.} -\item{circleMarkerOptions}{See \code{\link{drawCircleMarkerOptions}}(). Set to FALSE to disable circle marker drawing.} +\item{circleMarkerOptions}{See \code{drawCircleMarkerOptions()}. Set to FALSE to disable circle marker drawing.} -\item{editOptions}{By default editing is disable. To enable editing pass \code{\link{editToolbarOptions}}().} +\item{editOptions}{By default editing is disable. To enable editing pass \code{editToolbarOptions()}.} \item{singleFeature}{When set to TRUE, only one feature can be drawn at a time, the previous ones being removed.} -\item{toolbar}{See \code{\link{toolbarOptions}}. Set to \code{NULL} to take Leaflets default values.} +\item{toolbar}{See \code{toolbarOptions()}. Set to \code{NULL} to take Leaflets default values.} -\item{handlers}{See \code{\link{handlersOptions}}. Set to \code{NULL} to take Leaflets default values.} +\item{handlers}{See \code{handlersOptions()}. Set to \code{NULL} to take Leaflets default values.} -\item{edittoolbar}{See \code{\link{edittoolbarOptions}}. Set to \code{NULL} to take Leaflets default values.} +\item{edittoolbar}{See \code{edittoolbarOptions()}. Set to \code{NULL} to take Leaflets default values.} -\item{edithandlers}{See \code{\link{edithandlersOptions}}. Set to \code{NULL} to take Leaflets default values.} +\item{edithandlers}{See \code{edithandlersOptions()}. Set to \code{NULL} to take Leaflets default values.} \item{drag}{When set to \code{TRUE}, the drawn features will be draggable during editing, utilizing the \code{Leaflet.Draw.Drag} plugin. Otherwise, this library will not be included.} diff --git a/man/editToolbarOptions.Rd b/man/editToolbarOptions.Rd new file mode 100644 index 00000000..919299ca --- /dev/null +++ b/man/editToolbarOptions.Rd @@ -0,0 +1,26 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils_leaflet_draw.R +\name{editToolbarOptions} +\alias{editToolbarOptions} +\title{Options for editing shapes} +\usage{ +editToolbarOptions( + edit = TRUE, + remove = TRUE, + selectedPathOptions = NULL, + allowIntersection = TRUE +) +} +\arguments{ +\item{edit}{Editing enabled by default. Set to false do disable editing.} + +\item{remove}{Set to false to disable removing.} + +\item{selectedPathOptions}{To customize shapes in editing mode pass \code{selectedPathOptions()}.} + +\item{allowIntersection}{Determines if line segments can cross.} +} +\description{ +Options for editing shapes +} +\keyword{internal} From 71034a05eba3acfede55b6b87175aa4e49592a26 Mon Sep 17 00:00:00 2001 From: Daniel Lopez Date: Wed, 29 Apr 2026 14:54:49 -0400 Subject: [PATCH 12/17] Release prep: version 2.2.1 --- DESCRIPTION | 3 +-- NEWS.md | 9 +++++++++ R/wallace-package.R | 20 +++++++++----------- README.md | 8 ++++---- man/wallace-package.Rd | 13 +++++++------ 5 files changed, 30 insertions(+), 23 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 88ba1533..01709ce2 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: wallace Version: 2.2.1 -Date: 2025-03-06 +Date: 2026-04-29 Title: A Modular Platform for Reproducible Modeling of Species Niches and Distributions Description: The 'shiny' application Wallace is a modular platform for @@ -46,7 +46,6 @@ Imports: leafem, magrittr, markdown, - methods, RColorBrewer, rJava, rlang, diff --git a/NEWS.md b/NEWS.md index 4a3adb34..ee5f96a5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,12 @@ +wallace 2.2.1 +============= +- Fixed image paths in the Team section. +- Enabled support for models with categorical variables when using ENMeval > 2.0.5. +- Fixed an issue occurring during model transfer to new areas or time periods when using BIOCLIM or Maxent.jar. +- Fixed a loading error triggered by selecting multiple species in Environmental Space. +- Integrated the drawing functionality of leaflet.extras directly into wallace to streamline dependencies. +- Fixed an issue with session code generation when visualizing map predictions and transferring to a new time period. + wallace 2.2.0 ============= - updated for ENMeval v2.0.5 functionality diff --git a/R/wallace-package.R b/R/wallace-package.R index 234b483b..acaaa426 100644 --- a/R/wallace-package.R +++ b/R/wallace-package.R @@ -1,6 +1,6 @@ # Wallace EcoMod: a flexible platform for reproducible modeling of # species niches and distributions. -# +# # wallace-package.R # File author: Wallace EcoMod Dev Team. 2023. # -------------------------------------------------------------------------- @@ -21,21 +21,18 @@ # along with Wallace. If not, see . # -------------------------------------------------------------------------- # -#' @name wallace-package -#' @aliases wallace -#' @aliases wallace-package -#' @docType package #' @title \emph{Wallace}: A modular platform for reproducible ecological modeling +#' #' @description \emph{Wallace} is a \code{shiny} app that guides users through a complete #' species niche/distributional modeling analysis, from the acquisition of #' species occurrence and environmental data to visualizing model predictions #' on an interactive map (\code{rleaflet}), thus bundling complex workflows #' into a single, streamlined GUI interface. New functionality, in the form of -#' modules, can be added to \emph{Wallace} via contributions from the user -#' community. In addition, executable session code (R Markdown format) can -#' be downloaded to share with others or use as supplementary information -#' for scientific papers and reports. The application is run via the -#' function \code{\link{run_wallace}}. +#' modules, can be added to \emph{Wallace} via contributions from the user +#' community. In addition, executable session code (R Markdown format) can +#' be downloaded to share with others or use as supplementary information +#' for scientific papers and reports. The application is run via the +#' function \code{\link{run_wallace}}. #' #' @details Please see the official website (\url{https://wallaceecomod.github.io/}) for #' more details. If you have questions about the application, please participate @@ -44,4 +41,5 @@ #' #' @import leaflet shiny #' @importFrom magrittr "%>%" -NULL +#' @keywords internal +"_PACKAGE" diff --git a/README.md b/README.md index 44b28880..956e0197 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![R-CMD-check](https://github.com/wallaceEcoMod/wallace/workflows/R-CMD-check/badge.svg)](https://github.com/wallaceEcoMod/wallace/actions) [![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) [![CRAN version](http://www.r-pkg.org/badges/version/wallace)](https://CRAN.R-project.org/package=wallace) [![downloads](https://cranlogs.r-pkg.org:443/badges/grand-total/wallace?color=orange)](https://cranlogs.r-pkg.org:443/badges/grand-total/wallace?color=orange) -# Wallace (v2.2.0) +# Wallace (v2.2.1) *Wallace* is a modular platform for reproducible modeling of species niches and distributions, written in R. The application guides users through a complete analysis, from the acquisition of data to visualizing model predictions on an interactive map, thus bundling complex workflows into a single, streamlined interface. @@ -15,8 +15,8 @@ run_wallace() Development versions can be downloaded from Github with the following R code. ```R -install.packages("devtools") -devtools::install_github("wallaceEcoMod/wallace") +install.packages("remotes") +remotes::install_github("wallaceEcoMod/wallace") library(wallace) run_wallace() ``` @@ -27,7 +27,7 @@ run_wallace() Please make sure you have installed the latest versions of both R (Mac OS, Windows) and RStudio (Mac OS / Windows: choose the free version). #### How to run Maxent with maxent.jar -*Wallace* v2.2.0 includes two options to run Maxent models: maxnet and maxent.jar. The former, which is an R implementation and fits the model with the package `glmnet`, is now the default and does not require the package `rJava` (see Phillips et al. 2017). The latter, which is the Java implementation, runs the `maxent()` function in the package `dismo`. This function requires the user to place the `maxent.jar` file in the `/java` directory of the `dismo` package root folder. You can download Maxent here, and locate `maxent.jar`, which is the Maxent program itself, in the downloaded folder. You can find the directory path to `dismo/java` by running `system.file('java', package="dismo")` at the R console. Simply copy `maxent.jar` and paste it into this folder. If you try to run Maxent in *Wallace* without the file in place, you will get a warning message in the log window and Maxent will not run. +*Wallace* v2.2.1 includes two options to run Maxent models: maxnet and maxent.jar. The former, which is an R implementation and fits the model with the package `glmnet`, is now the default and does not require the package `rJava` (see Phillips et al. 2017). ### Potential Issues diff --git a/man/wallace-package.Rd b/man/wallace-package.Rd index 9157f79a..651beea5 100644 --- a/man/wallace-package.Rd +++ b/man/wallace-package.Rd @@ -2,8 +2,8 @@ % Please edit documentation in R/wallace-package.R \docType{package} \name{wallace-package} -\alias{wallace-package} \alias{wallace} +\alias{wallace-package} \title{\emph{Wallace}: A modular platform for reproducible ecological modeling} \description{ \emph{Wallace} is a \code{shiny} app that guides users through a complete @@ -11,11 +11,11 @@ species occurrence and environmental data to visualizing model predictions on an interactive map (\code{rleaflet}), thus bundling complex workflows into a single, streamlined GUI interface. New functionality, in the form of - modules, can be added to \emph{Wallace} via contributions from the user - community. In addition, executable session code (R Markdown format) can - be downloaded to share with others or use as supplementary information - for scientific papers and reports. The application is run via the - function \code{\link{run_wallace}}. + modules, can be added to \emph{Wallace} via contributions from the user + community. In addition, executable session code (R Markdown format) can + be downloaded to share with others or use as supplementary information + for scientific papers and reports. The application is run via the + function \code{\link{run_wallace}}. } \details{ Please see the official website (\url{https://wallaceecomod.github.io/}) for @@ -60,3 +60,4 @@ Other contributors: } } +\keyword{internal} From a3522c02d743456d372afd0cffcc31a365e37f2f Mon Sep 17 00:00:00 2001 From: Daniel Lopez Date: Wed, 29 Apr 2026 15:21:23 -0400 Subject: [PATCH 13/17] Update version number to 2.2.1 --- inst/shiny/Rmd/text_about.Rmd | 2 +- inst/shiny/Rmd/userReport_intro.Rmd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/inst/shiny/Rmd/text_about.Rmd b/inst/shiny/Rmd/text_about.Rmd index c7d35b68..a2e1bd7f 100644 --- a/inst/shiny/Rmd/text_about.Rmd +++ b/inst/shiny/Rmd/text_about.Rmd @@ -7,7 +7,7 @@ output: html_document logo -Welcome to *Wallace*, a flexible application for reproducible ecological modeling, built for community expansion. The current version of *Wallace* (v2.2.0) steps the user through a full niche/distribution modeling analysis, from data acquisition to visualizing results. +Welcome to *Wallace*, a flexible application for reproducible ecological modeling, built for community expansion. The current version of *Wallace* (v2.2.1) steps the user through a full niche/distribution modeling analysis, from data acquisition to visualizing results. The application is written in `R` with the web app development package `shiny`. Please find the stable version of *Wallace* on CRAN, and the development version on Github. We also maintain a *Wallace* website that has some basic info, links, and will be updated with tutorial materials in the near future. diff --git a/inst/shiny/Rmd/userReport_intro.Rmd b/inst/shiny/Rmd/userReport_intro.Rmd index 47b20b55..305243a2 100644 --- a/inst/shiny/Rmd/userReport_intro.Rmd +++ b/inst/shiny/Rmd/userReport_intro.Rmd @@ -10,7 +10,7 @@ knit_engines$set(asis = function(options) { knitr::opts_chunk$set(message = FALSE, warning = FALSE, eval = FALSE) ``` -Please find below the R code history from your *Wallace* v2.2.0 session. +Please find below the R code history from your *Wallace* v2.2.1 session. You can reproduce your session results by running this R Markdown file in RStudio. From 92e152d28eef97f72051a13aceaba7c6ef4884fa Mon Sep 17 00:00:00 2001 From: Daniel Lopez Date: Thu, 30 Apr 2026 13:12:28 -0400 Subject: [PATCH 14/17] Fix power-off icon visibility on macOS --- inst/shiny/ui.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/inst/shiny/ui.R b/inst/shiny/ui.R index bbffa924..f4207683 100644 --- a/inst/shiny/ui.R +++ b/inst/shiny/ui.R @@ -1,6 +1,6 @@ # Wallace EcoMod: a flexible platform for reproducible modeling of # species niches and distributions. -# +# # ui.R # File author: Wallace EcoMod Dev Team. 2023. # -------------------------------------------------------------------------- @@ -57,7 +57,7 @@ tagList( HTML('Google Group'), HTML('GitHub Issues'), HTML('Send Email')), - tabPanel(NULL, icon = icon("power-off"), value = "_stopapp") + tabPanel(HTML(" "), icon = icon("power-off"), value = "_stopapp") ), tags$div( class = "container-fluid", From 003e78d546aedb7787e9990c42a9ba7f655fb95e Mon Sep 17 00:00:00 2001 From: Daniel Lopez Date: Tue, 5 May 2026 14:36:19 -0400 Subject: [PATCH 15/17] CRAN checks for version 2.2.1 --- .Rbuildignore | 4 ++-- DESCRIPTION | 6 +++--- NEWS.md | 1 + README.md | 4 ++-- man/wallace-package.Rd | 4 ++-- tests/testthat/test_penvs_bgSample.R | 2 ++ 6 files changed, 12 insertions(+), 9 deletions(-) diff --git a/.Rbuildignore b/.Rbuildignore index 689873b2..c48cd3bc 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -1,8 +1,7 @@ ^.*\.Rproj$ # Automatically added by RStudio, ^\.Rproj\.user$ # used for temporary files. ^README\.Rmd$ # An Rmarkdown file used to generate README.md -^cran-comments\.md$ # Comments for CRAN submission -^NEWS\.md$ # A news file written in Markdown +^cran-comments\.md$ # Comments for CRAN submissionS \.DS_Store$ # for macs cmip5* # worldclim files ^wallace.Rproj @@ -13,3 +12,4 @@ cmip5* # worldclim files ^docs$ ^pkgdown$ ^LICENSE$ +^CRAN-SUBMISSION$ diff --git a/DESCRIPTION b/DESCRIPTION index 01709ce2..6ca52974 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -12,8 +12,8 @@ Description: The 'shiny' application Wallace is a modular platform for most package functionality can be found on the package's GitHub Pages website: . Authors@R: - c(person("Bethany A.", "Johnson", email = "bjohnso005@citymail.cuny.edu", role = "aut"), - person("Daniel F.", "Lopez-Lozano", email = "dlopezlozano@amnh.org", role = "aut"), + c(person("Daniel F.", "Lopez-Lozano", email = "dlopezlozano@amnh.org", role = "aut"), + person("Bethany A.", "Johnson", email = "bjohnso005@citymail.cuny.edu", role = "aut"), person("Jamie M.", "Kass", email = "jamie.m.kass@gmail.com", role = "aut"), person("Gonzalo E.", "Pinilla-Buitrago", email = "gepinillab@gmail.com", role = "aut"), person("Andrea", "Paz", email = "paz.andreita@gmail.com", role = "aut"), @@ -76,6 +76,6 @@ Suggests: testthat, tools License: GPL-3 -URL: http://wallaceecomod.github.io/wallace/, +URL: https://wallaceecomod.github.io/wallace/, Encoding: UTF-8 RoxygenNote: 7.3.3 diff --git a/NEWS.md b/NEWS.md index ee5f96a5..fc3bbc33 100644 --- a/NEWS.md +++ b/NEWS.md @@ -6,6 +6,7 @@ wallace 2.2.1 - Fixed a loading error triggered by selecting multiple species in Environmental Space. - Integrated the drawing functionality of leaflet.extras directly into wallace to streamline dependencies. - Fixed an issue with session code generation when visualizing map predictions and transferring to a new time period. +- Fixed UI rendering issue where the power-off icon was hidden on macOS. wallace 2.2.0 ============= diff --git a/README.md b/README.md index 956e0197..6ebb5699 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ run_wallace() ### Before using *Wallace* #### Update R and RStudio versions -Please make sure you have installed the latest versions of both R (Mac OS, Windows) and RStudio (Mac OS / Windows: choose the free version). +Please make sure you have installed the latest versions of both R (Mac OS, Windows) and RStudio (Mac OS / Windows: choose the free version). #### How to run Maxent with maxent.jar *Wallace* v2.2.1 includes two options to run Maxent models: maxnet and maxent.jar. The former, which is an R implementation and fits the model with the package `glmnet`, is now the default and does not require the package `rJava` (see Phillips et al. 2017). @@ -32,7 +32,7 @@ Please make sure you have installed the latest versions of both R (here, and 64-bit Windows users should make sure to select "Windows Offline (64-bit)". There is currently only a 64-bit download for Mac OS. For Mac users running OSX Yosemite and above with problems, see this StackOverflow post for some tips on how to get `rJava` working again. If you need to install Java for the first time, you can follow these instructions for Mac and Windows. +*Wallace* uses the `rJava` package only to run the program `maxent.jar`. The package `rJava` will not load properly if the version of Java on your computer (32-bit or 64-bit) does not match that of the R installation you are using. For example, if you are running 64-bit R, please make sure your Java is also 64-bit, or else `rJava` will be unable to load. Install the latest version of Java here, and 64-bit Windows users should make sure to select "Windows Offline (64-bit)". There is currently only a 64-bit download for Mac OS. For Mac users running OSX Yosemite and above with problems, see this StackOverflow post for some tips on how to get `rJava` working again. If you need to install Java for the first time, you can follow these instructions for Mac and Windows. #### Problems viewing tables If for some reason you are unable to view the tables in *Wallace*, please install (force if necessary) the development version of `htmlwidgets` by running this code: `devtools::install_github("ramnathv/htmlwidgets")`. You should be able to view tables now. diff --git a/man/wallace-package.Rd b/man/wallace-package.Rd index 651beea5..1ed1cddc 100644 --- a/man/wallace-package.Rd +++ b/man/wallace-package.Rd @@ -26,7 +26,7 @@ Please see the official website (\url{https://wallaceecomod.github.io/}) for \seealso{ Useful links: \itemize{ - \item \url{http://wallaceecomod.github.io/wallace/,} + \item \url{https://wallaceecomod.github.io/wallace/,} } } @@ -35,8 +35,8 @@ Useful links: Authors: \itemize{ - \item Bethany A. Johnson \email{bjohnso005@citymail.cuny.edu} \item Daniel F. Lopez-Lozano \email{dlopezlozano@amnh.org} + \item Bethany A. Johnson \email{bjohnso005@citymail.cuny.edu} \item Jamie M. Kass \email{jamie.m.kass@gmail.com} \item Gonzalo E. Pinilla-Buitrago \email{gepinillab@gmail.com} \item Andrea Paz \email{paz.andreita@gmail.com} diff --git a/tests/testthat/test_penvs_bgSample.R b/tests/testthat/test_penvs_bgSample.R index 54e6def3..ae80c573 100644 --- a/tests/testthat/test_penvs_bgSample.R +++ b/tests/testthat/test_penvs_bgSample.R @@ -16,6 +16,8 @@ envs <- envs_userEnvs(rasPath = list.files(system.file("extdata/wc", # background extent bgExt <- penvs_bgExtent(occs, bgSel = 'bounding box', bgBuf = 0.5) # background masked +raster::crs(envs) <- "EPSG:4326" +raster::crs(bgExt) <- "EPSG:4326" bgMask <- penvs_bgMask(occs, envs, bgExt) bgMaskR <- raster::stack(bgMask) From ecce2cb007cd7c382192638aa7c1c519cec96612 Mon Sep 17 00:00:00 2001 From: Daniel Lopez Date: Tue, 5 May 2026 16:02:12 -0400 Subject: [PATCH 16/17] Fix Maxent compatibility issue with terra v1.9.11 --- DESCRIPTION | 4 ++-- NEWS.md | 1 + R/model_maxent.R | 14 +++++++++----- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 6ca52974..28acc00a 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: wallace Version: 2.2.1 -Date: 2026-04-29 +Date: 2026-05-05 Title: A Modular Platform for Reproducible Modeling of Species Niches and Distributions Description: The 'shiny' application Wallace is a modular platform for @@ -72,7 +72,7 @@ Suggests: raster, rgbif (>= 3.3.0), sp, - terra, + terra (>= 1.9-11), testthat, tools License: GPL-3 diff --git a/NEWS.md b/NEWS.md index fc3bbc33..06a119ad 100644 --- a/NEWS.md +++ b/NEWS.md @@ -7,6 +7,7 @@ wallace 2.2.1 - Integrated the drawing functionality of leaflet.extras directly into wallace to streamline dependencies. - Fixed an issue with session code generation when visualizing map predictions and transferring to a new time period. - Fixed UI rendering issue where the power-off icon was hidden on macOS. +- Fixed an issue affecting Maxent models when using terra > '1.9.11'. wallace 2.2.0 ============= diff --git a/R/model_maxent.R b/R/model_maxent.R index a0914e56..9ecdcf6f 100644 --- a/R/model_maxent.R +++ b/R/model_maxent.R @@ -36,7 +36,7 @@ #' @param bg coordinates of background points to be used for modeling. #' @param user.grp a list of two vectors containing group assignments for #' occurrences (occs.grp) and background points (bg.grp). -#' @param bgMsk a SpatRaster of environmental layers cropped and masked to +#' @param bgMsk a SpatRaster of environmental layers cropped and masked to #' match the provided background extent. #' @param rms vector of range of regularization multipliers to be used in the #' ENMeval run. @@ -193,12 +193,12 @@ model_maxent <- function(occs, bg, user.grp, bgMsk, rms, rmsStep, fcs, # get just coordinates occs.xy <- occs %>% dplyr::select("longitude", "latitude") bg.xy <- bg %>% dplyr::select("longitude", "latitude") - + # convert the categorical variables to factors if (!is.null(catEnvs)) { bgMsk[[catEnvs]] <- terra::as.factor(bgMsk[[catEnvs]]) - } - + } + # run ENMeval e <- ENMeval::ENMevaluate(occs = as.data.frame(occs.xy), bg = as.data.frame(bg.xy), @@ -226,7 +226,11 @@ model_maxent <- function(occs, bg, user.grp, bgMsk, rms, rmsStep, fcs, updateProgress = updateProgress, quiet = FALSE) - occPredVals <- raster::extract(e@predictions, occs.xy) + # Make sure e@predictions has an assigned CRS + if(terra::crs(e@predictions) == ""){ + terra::crs(e@predictions) <- terra::crs(bgMsk) + } + occPredVals <- terra::extract(e@predictions, occs.xy) endTxt <- paste("]), using", algMaxent, "with clamping", ifelse(clampSel, "on.", "off.")) From 7cdd97c15a3e6aa829f072baac54d44ce71cdff1 Mon Sep 17 00:00:00 2001 From: Daniel Lopez Date: Thu, 7 May 2026 12:59:28 -0400 Subject: [PATCH 17/17] Fix minor typo --- DESCRIPTION | 2 +- man/model_maxent.Rd | 2 +- man/wallace-package.Rd | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 28acc00a..bbaa3a47 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -76,6 +76,6 @@ Suggests: testthat, tools License: GPL-3 -URL: https://wallaceecomod.github.io/wallace/, +URL: https://wallaceecomod.github.io/wallace/ Encoding: UTF-8 RoxygenNote: 7.3.3 diff --git a/man/model_maxent.Rd b/man/model_maxent.Rd index aa8f3958..368423b4 100644 --- a/man/model_maxent.Rd +++ b/man/model_maxent.Rd @@ -30,7 +30,7 @@ components occs: Obtain occurrence data or, poccs: Process occurrence data.} \item{user.grp}{a list of two vectors containing group assignments for occurrences (occs.grp) and background points (bg.grp).} -\item{bgMsk}{a SpatRaster of environmental layers cropped and masked to +\item{bgMsk}{a SpatRaster of environmental layers cropped and masked to match the provided background extent.} \item{rms}{vector of range of regularization multipliers to be used in the diff --git a/man/wallace-package.Rd b/man/wallace-package.Rd index 1ed1cddc..87138b6d 100644 --- a/man/wallace-package.Rd +++ b/man/wallace-package.Rd @@ -26,7 +26,7 @@ Please see the official website (\url{https://wallaceecomod.github.io/}) for \seealso{ Useful links: \itemize{ - \item \url{https://wallaceecomod.github.io/wallace/,} + \item \url{https://wallaceecomod.github.io/wallace/} } }