r-spatial / mapedit

Interactive editing of spatial data in R
https://www.r-spatial.org/r/2019/03/31/mapedit_leafpm.html
Other
218 stars 33 forks source link

map_bounds and map_marker_click in leaflet under editMod #69

Closed cywhale closed 6 years ago

cywhale commented 6 years ago

It may be a simple question, but I cannot make it work as I call edit module: server <- function(input, output) {
ns <- shiny::NS("eview") emapx <- callModule(editMod, "eview", leaflet(...)) ... } The leaflet map correctly show, and leafletProxy(ns("map")) also works, including the popup when clicking marker. But I cannot use input$map_bounds or input$map_marker_click to detect map bounds and to get triggered by marker click through the leaflet id "map". The original usage under only shiny+leaflet is ok. Do I misuse the id of leaflet (in edit module of mapedit) for input$'MAPID'_bounds (and _marker_click)? Is there any suggestion to do so? Thanks.

cywhale commented 6 years ago

I write a small app.R to test my problems for input$map_bounds and input$map_marker_click (leaflet under editMod), and compare the results just in shiny+leaflet. Append my sessionInfo() in the end.

library(shiny)
library(sf)
library(leaflet)
library(mapview)
library(mapedit)
library(magrittr)

ui <- fluidPage(
  fluidRow(
    column(4, 
           fluidRow(
             uiOutput("control_ui")
           ),
           fluidRow(
             textOutput("outText")
           )
    ),
    column(7,
           fluidRow(
             uiOutput("emod_ui")
           ),
           fluidRow(
             uiOutput("leaflet_ui")
           )
    )
  )
)

server <- function(input, output) {

  ns <- shiny::NS("eview") 

  lf0 <- reactive ({ leaflet(breweries91) %>%
      addProviderTiles("Esri.WorldTopoMap",options = providerTileOptions(maxZoom=13,minZoom=0,continuousWorld=FALSE,noWrap=TRUE)) %>%
      setView(10.5, 49.5, zoom=8) %>%
      addCircleMarkers(weight = 1, layerId = 1:nrow(breweries91),
                       popup = breweries91@data$brewery)
  })

  output$control_ui <- renderUI({
    selectizeInput(inputId="basemapsel", label= "Basemap:",
                   choices=list("Esri Topo"= 1, "Esri Ocean"=2), multiple=FALSE, 
                   selected = 1, options = list(allowEmptyOption=FALSE)
    )
  })

  output$emod_ui <- renderUI({
    editModUI("eview", width="100%", height="400px")
  })

  output$leaflet_ui <- renderUI({
    leafletOutput("lview", width="100%", height="400px")
  })

  lfx<- isolate({lf0()})
  editmapx <- callModule(editMod, "eview", lfx)

  observe({ editmapx() })

  observeEvent(input$basemapsel, {

    lf <- leafletProxy(ns("map")) 

    if (input$basemapsel == 1) {
      lf %>% addProviderTiles("Esri.WorldTopoMap",options = providerTileOptions(maxZoom=13,minZoom=0,continuousWorld=FALSE,noWrap=TRUE)) 

    } else { 
      lf %>% addProviderTiles("Esri.OceanBasemap",options = providerTileOptions(maxZoom=13,minZoom=0,continuousWorld=FALSE,noWrap=TRUE)) 
    }
  })   

  output$lview <- renderLeaflet({
    lfx
  })

######## Detect event from leaflet under editMod: Marker click  

  em_idx <- eventReactive(input$map_marker_click, {  
    cevent <- input$map_marker_click 

    if (is.null(cevent)) {
      return("click_by_emod_NONE")
    }
    paste0("click_by_emod: ",as.character(cevent$id))
  })

#### Map bounds  
  em_boundx <- eventReactive(input$map_bounds, { 
    if (is.null(input$map_bounds)) {
      return("bounds_by_emod_NONE")
    }
    paste0("bounds_by_emod: ",paste(input$map_bounds, collapse = ","))
  }) 

######## Detect event from only leaflet  

  lf_idx <- eventReactive (input$lview_marker_click, {  
    cevent <- input$lview_marker_click 

    if (is.null(cevent)) {
      return("click_by_lf_NONE")
    }
    paste0("click_by_lf: ",as.character(cevent$id))
  })

  lf_boundx <- eventReactive(input$lview_bounds, { 
    if (is.null(input$lview_bounds)) {
      return("bounds_by_lf_NONE")
    }
    paste0("bounds_by_lf: ",paste(input$lview_bounds, collapse = ","))
  }) 

#### Output Result, click the markers on leaflet
  output$outText <- renderText({

    paste(#"From emMod: ", em_idx(), em_boundx(), #### Comment the two text outputs because it cannot work and make renderText failed.
      "From LF: ", lf_idx(), lf_boundx(), sep=";\n")## Only the two text outputs from leaflet "lview" works!
  })

}

shinyApp(ui = ui, server = server)
sessionInfo()
R version 3.4.1 (2017-06-30)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 16.04.3 LTS

Matrix products: default
BLAS/LAPACK: /opt/openblas/lib/libopenblasp-r0.2.20.dev.so

locale:
 [1] LC_CTYPE=zh_TW.UTF-8    LC_NUMERIC=C            LC_TIME=lzh_TW         
 [4] LC_COLLATE=zh_TW.UTF-8  LC_MONETARY=lzh_TW      LC_MESSAGES=zh_TW.UTF-8
 [7] LC_PAPER=lzh_TW         LC_NAME=C               LC_ADDRESS=C           
[10] LC_TELEPHONE=C          LC_MEASUREMENT=lzh_TW   LC_IDENTIFICATION=C    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] magrittr_1.5  mapedit_0.3.2 mapview_2.3.0 leaflet_1.1.0 sf_0.6-0     
[6] shiny_1.0.5  

loaded via a namespace (and not attached):
 [1] Rcpp_0.12.15       plyr_1.8.4         pillar_1.1.0       compiler_3.4.1    
 [5] R.methodsS3_1.7.1  base64enc_0.1-3    R.utils_2.6.0      class_7.3-14      
 [9] iterators_1.0.9    tools_3.4.1        gdalUtils_2.0.1.7  digest_0.6.15     
[13] jsonlite_1.5       viridisLite_0.3.0  satellite_1.0.1    lattice_0.20-35   
[17] png_0.1-7          rlang_0.1.6        foreach_1.4.4      DBI_0.7           
[21] crosstalk_1.0.0    yaml_2.1.16        rgdal_1.2-16       e1071_1.6-8       
[25] raster_2.6-7       htmlwidgets_1.0    webshot_0.5.0      classInt_0.1-24   
[29] stats4_3.4.1       grid_3.4.1         R6_2.2.2           sp_1.2-7          
[33] udunits2_0.13      leaflet.extras_0.2 scales_0.5.0       codetools_0.2-15  
[37] htmltools_0.3.6    units_0.5-1        rsconnect_0.8.5    colorspace_1.3-2  
[41] mime_0.5           xtable_1.8-2       httpuv_1.3.5       munsell_0.4.3     
[45] R.oo_1.21.0 
timelyportfolio commented 6 years ago

@cywhale I think I understand the objective. I'll try to post a demo tonight. Thanks for using mapedit.

timelyportfolio commented 6 years ago

@cywhale, I simplified your example a bit to focus on the Shiny module event. Please let me know if my simplification lost important aspects of your question.

Shiny Module Events

Shiny Modules pass messages back and forth between JS and R just like regular old Shiny, but the messages are namespaced. In your example, input$map_bounds would become input[["eview-map_bounds]] or using the ns() function input[[ns("map_bounds")]]. options(shiny.trace=TRUE) can be very helpful here.

image

Example

mapedit_module

library(shiny)
library(sf)
library(leaflet)
library(mapview)
library(mapedit)
library(magrittr)

ui <- fluidPage(
  fluidRow(
    column(7,
      editModUI("eview", width="100%", height="400px")
    ),
    column(5,
      verbatimTextOutput("textbounds")
    )
  )
)

server <- function(input, output) {

  ns <- shiny::NS("eview") 

  lf0 <- leaflet(breweries91) %>%
      addProviderTiles("Esri.WorldTopoMap",options = providerTileOptions(maxZoom=13,minZoom=0,continuousWorld=FALSE,noWrap=TRUE)) %>%
      setView(10.5, 49.5, zoom=8) %>%
      addCircleMarkers(weight = 1, layerId = 1:nrow(breweries91),
                      popup = breweries91@data$brewery)

  editmapx <- callModule(editMod, "eview", lf0)

  # events within module will be namespaced
  #   so we will need to wrap them in our ns() function
  # show bounds in textOutput
  output$textbounds <- renderPrint({
    str(input[[ns("map_bounds")]])
  })
  # print structure in console to demonstrate
  observeEvent(
    input[[ns("map_bounds")]],
    {
      str(input[[ns("map_bounds")]])
    }
  )
}

shinyApp(ui = ui, server = server)
cywhale commented 6 years ago

@timelyportfolio Your response is exactly what I needed. I know I misuse the id, but was not aware of the alternative way by using input[[ns(..)]]. Thanks for the timely help, also the trick options(shiny.trace=TRUE). I appreciate the progress in mapview/mapedit, and try to integrate it with my rshiny work.

timelyportfolio commented 6 years ago

@cywhale, fantastic, glad it was helpful. I think Shiny modules are unfortunately under-documented. Please let me know how I can help as you progress with mapedit. Feedback will be critical for the success of the project. Also, look forward to another round of features soon once the newest leaflet is complete.

cywhale commented 6 years ago

@timelyportfolio Sure. I will feedback my rshiny work after integrating with mapedit. I have designed an API that can query some biodiversity information through polygonal regions. So now I just try to extract the polygons that users draw on leaflet by using mapedit, and then feed the polygons into the API. I'll keep watching the new features of leaflet/mapedit. Thanks!