2
votes

In my shiny app, I would like to click on a polygon of my mapview map and be able to extract the layerId attribute to a variable using input$map_shape_click. In the following code, when you click a polygon, it prints out the id, but it is set to null by default in mapview.

library(shiny)
library(tmap)
library(leaflet)
library(mapview)

ui <- bootstrapPage(
  title = "Standardized Crop Production Index",
  tags$style(type = "text/css", "html, body {width:100%;height:100%}"),
  mapview::mapviewOutput("map", width = "100%", height = "100%")
)

data("World") #from the tmap library

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

  out_plot <- observeEvent(input$map_shape_click, {
    p <- input$map_shape_click
    print(p)
  })

  output$map <- renderLeaflet({

    test <- mapview(World)
    test@map
  })
}

shinyApp(ui = ui, server = server)

Is there a way to set the layerId of a mapview object? I know that I could set it using addPolygons() if I were to just use Leaflet instead of mapview. However, I would eventually like to use the data gathered from clicking the polygon as input into mapview's popupGraph().

Even better would be if there is a way to retrieve the attributes from the table that pops up when you click a polygon. For example, if I click on Antarctica, the following attribute table pops up: Antarctica Attribute Table. Is there any way to retrieve the "name" attribute and store it as a variable when a polygon clicked?

Thanks!

2

2 Answers

1
votes

Not entirely sure about your first question, as I'm not aware of any way that can be assigned using mapview(). However, here is a reproducible solution using addPolygons():

library(dplyr)
library(shiny)
library(leaflet)
library(leaflet.extras)
library(rgdal)
library(sp)
library(tigris)
library(htmltools)

setwd(dirname(rstudioapi::getActiveDocumentContext()$path))  # set your working directory

philly <- tracts(state = 'PA', county = c('Philadelphia'))

ui <- fluidPage(
  title = "Test Map",
  leafletOutput("mymap", width = 600)
)


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

  RV <- reactiveValues(Clicks=list()) # used for storing leaflet variables

  tract_labels <- sprintf( # labels for mouseover tooltip
    "<strong>%s</strong>, <strong>%s</strong>
    <br/><b>Land Area:</b> %s",
    philly$COUNTYFP,
    philly$STATEFP,
    philly$ALAND
  ) %>% lapply(htmltools::HTML)


  output$mymap <- renderLeaflet({  # leaflet map
    leaflet(data = philly) %>%
      setView(-75.16, 39.9523, zoom = 10) %>%
      addTiles(urlTemplate = "https://{s}.tile.openstreetmap.se/hydda/full/{z}/{x}/{y}.png",
               attribution = NULL) %>%
      addPolygons(data = philly,
                  layerId = philly@data$ALAND,
                  group = "regions",
                  fillColor = "#bdd7e7",
                  weight = 1,
                  opacity = 1.0, 
                  fillOpacity = 0.5,
                  smoothFactor = 0.5,
                  label = tract_labels,
                  labelOptions = labelOptions(
                    style = list("font-weight" = "normal", padding = "3px 8px"),
                    textsize = "12px",
                    direction = "auto"),
                  highlightOptions = highlightOptions(color = "white",
                                                      weight = 2,
                                                      bringToFront = TRUE))
  })


  observeEvent({input$mymap_shape_click}, {

    #create object for clicked polygon
    click <- input$mymap_shape_click
    RV$Clicks <- c(RV$Clicks,click$id)

    #define leaflet proxy for second regional level map
    proxy <- leafletProxy("mymap")

    #subset regions shapefile by the clicked on polygons
    selectedReg <- philly[philly@data$ALAND == click$id,]

    #map clicked on polygons
    proxy %>% addPolygons(data = selectedReg,
                          fillColor = "red",
                          fillOpacity = 1,
                          weight = 1,
                          color = "black",
                          stroke = T,
                          group = "selected",
                          layerId = selectedReg@data$ALAND)

    # remove polygon group that are clicked twice
    if(click$group == "selected"){
      proxy %>%
        clearGroup(group = "selected")

      RV$Clicks <- 0  # resets values if polygons are clicked twice
    }

    mean.land <- mean(as.numeric(RV$Clicks))  # stores the values of polygons that are clicked
    print(mean.land)

  })


}

shinyApp(ui, server)

Essentially the map has two layers: the base tracts layer, and another tracts polygon that highlights what you click. You can click on each polygon to 'retrieve' a value (in this case it's land area, or variable ALAND) from each polygon and make calculations on it. Here I have selected three polygons, and I have used mean.land variable to display the average land area of all three.

enter image description here

The reactiveValues RV object is used to store a numeric value of the layerId variable on any polygon that you click. This allows you to store and 'retrieve' it for other calculations you may want to make.

[1] 717210  # first click, first value
[1] 571940  # second click, averaged value
[1] 488678.3  # third click, averaged value

You can change extract the layerId attribute by changing any references to variable ALAND in the code.

1
votes

A little late, but it is possible to set the layerId in mapview. mapview will pass any additional (valid) arguments on to the respective leaflet functions via the ... argument including layerId. The only difference is that you cannot use the formula notation with mapview as it does only pass the geometry part to (in this case) addPolygons. So at the time of evaluating the formula, there is no data attached to the geometries and hence it fails. What you can do, though, is pass the respective vector to layerId.

To sum it up, the following should return the layerId on input$map_shape_click:

library(shiny)
library(tmap)
library(leaflet)
library(mapview)

ui <- bootstrapPage(
  title = "Standardized Crop Production Index",
  tags$style(type = "text/css", "html, body {width:100%;height:100%}"),
  mapview::mapviewOutput("map", width = "100%", height = "100%")
)

data("World") #from the tmap library

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

  out_plot <- observeEvent(input$map_shape_click, {
    p <- input$map_shape_click
    print(p)
  })

  output$map <- renderLeaflet({

    test <- mapview(World, layerId = World$iso_a3)
    test@map
  })
}

shinyApp(ui = ui, server = server)