Search code examples
rshinyleafletandroid-mapviewr-leaflet

How to change circle marker attributes (e.g. opacity & fillOpacity) within mapview::addFeatures


I've been able to generate a map in a R-Shiny app that allows multi-selection of markers by combining the mapview, mapedit and leaflet packages. Functionally everything is fine except I wish to modify the marker attributes, more specifically increase fillOpacity.

The documentation for mapview AddFeatures suggests it should accept the same arguments used by leaflet for addCircleMarkers. I have also tried using the function addCircleMarkers instead of addFeatures without any success.

See addFeatures doco

... Further arguments passed to the respective leaflet::add* functions. See addCircleMarkers, addPolylines and addPolygons.

But it seems to ignore the arguments; weight, opacity and fillOpacity. I have marked in the standalone code below which arguments work or not.

Am I doing something wrong or do you think this is a bug?

# devtools::install_github("r-spatial/sf")
# devtools::install_github("r-spatial/mapview@develop")
# devtools::install_github("bhaskarvk/leaflet.extras")
# devtools::install_github("r-spatial/mapedit")
library(tidyverse)
library(sf)
library(leaflet)
library(mapedit)
library(mapview)
library(shiny)
library(shinyjs)

locnCoord <-
  data.frame(location = c('Sit1','Site2','Site3'),
             lat=c(-18.1, -18.3, -18.4),
             lon=c(145.8, 145.9, 145.9)) %>%
  mutate(depth = runif(3))

locnSF <- st_as_sf(locnCoord, coords = c('lon','lat'), crs="+proj=longlat +datum=WGS84 +no_defs")

#### User input

ui <- fluidPage(
  shinyjs::useShinyjs(),
  shinyjs::extendShinyjs(text = "shinyjs.refresh = function() { location.reload(); }"),
  fluidRow(
    # edit module ui
    column(6,
           selectModUI("selectmap"),
           actionButton("refresh", "Refresh Map")
           ),
    column(6,
           h3("Point of Depth"),
           plotOutput("selectstat")
           )
    )
  )

#### Server

server <- function(input, output, session) {

  observeEvent(input$refresh, {
    shinyjs::js$refresh()
  })

  g_sel <- callModule(
    selectMod,
    "selectmap",
    leaflet() %>%
      addTiles() %>%
      addFeatures(
      # addCircleMarkers(
        data = locnSF,
        layerId = ~location,
        stroke = TRUE, # This is effective
        color = 'red', # This is effective
        weight = 150, # This is ignored    ####
        opacity = 0, # This is ignored     ####
        fill = TRUE, # This is effective
        fillColor = 'blue', # This is effective
        fillOpacity=1, # This is ignored   ####
        radius=20) # This is effective
  )

  rv <- reactiveValues(selected=NULL)

  observe({
    gs <- g_sel()

    if(length(gs$id) > 0) {
      rv$selected <- locnSF %>% filter(location %in% gs$id)
    } else {
      rv$selected <- NULL
    }
  })

  output$selectstat <- renderPlot({
    ggplot()
    if(!is.null(rv$selected) && nrow(rv$selected) > 0) {
      ggplot(data=rv$selected, aes(location, depth))+
        geom_point(color='red', size=5)
    } else {
      ggplot()
    }
  })
}
shinyApp(ui, server)

Solution

  • They are not ignored. If you try as a stand-alone mapview call you will see that they work. In your app they are being masked by the mapedit module selectMod which allows you set opacity, fillOpacity and weight separately for the case when features are selected and not selected via arguments styleTrue and styleFalse, respectively. When you set those, you will get the desired behaviour. Thus, change the part where you call the selectMod module like below:

    g_sel <- callModule(
        selectMod,
        "selectmap",
        leaflet() %>%
          addTiles() %>%
          addFeatures(
            # addCircleMarkers(
            data = locnSF,
            layerId = ~location,
            stroke = TRUE, # This is effective
            color = 'red', # This is effective
            # weight = 150, # This is ignored    ####
            # opacity = 0, # This is ignored     ####
            fill = TRUE, # This is effective
            fillColor = 'blue', # This is effective
            # fillOpacity=1, # This is ignored   ####
            radius=20), # This is effective
        styleFalse = list(fillOpacity = 0.5, weight = 0, opacity = 0), 
        styleTrue = list(fillOpacity = 1, weight = 5, opacity = 1)
      )