Search code examples
rshinygeospatialtmap

Add location to interactive map with a click using tmap package and r shiny


I am creating a shiny application that displays locations of public fruit trees around Bellingham, WA. So far I have made it so users can input lat/long coords and tree type and plot the location on the map. eventually I would like to make it so the user can click on the interactive map view and add a point to the map. I have no idea if this even possible, and if it is, how to create this functionality. I have included some code from my application.

library(dplyr)
library(shiny)
library(leaflet)
library(tmap)
library(sf)




dat <- data.frame(lat = c(48.7323,48.7308,
                          48.7301,48.7276,
                         48.7246,48.7323,
                          48.7211),
                  long = c(-122.4928,-122.4940,
                           -122.4942,-122.4939,
                           -122.4958,-122.4975,
                           -122.4946),
                  species = c("Apple", "Apple",
                              "Pear", "Plum",
                              "Fig", "Plum",
                              "Pear"),
                  status = rep("Confirmed",7))



ui <- fluidPage(

  titlePanel("Public Fruit Trees"),

  sidebarLayout(
    sidebarPanel(
      numericInput(inputId = "lat",
                   label = "Latitude DD",
                   value = 48.7211,step = 1e-3),
      numericInput(inputId = "long",
                   label = "Longitude DD",
                   value=-122.4942,step = 1e-3),
      selectInput(inputId = "species", label = "Species",
                  choices = c("Apple","Pear","Fig","Peach","Other")),
      actionButton(inputId = "addObservation",
                   label = "Add tree to database")
    ),
    mainPanel(
      
      tmapOutput(outputId = "tmapMap"),
      
      DT::dataTableOutput("updatedData"),
    )
  )
)

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

  
  theData <- reactiveValues()
  theData$dat <- dat

 
  output$tmapMap <- renderTmap({
    dat2plot <- theData$dat
    
    dat2plot <- st_as_sf(dat2plot, coords = c("long","lat"), crs=st_crs("EPSG:4326"))

    map1 <- tm_shape(dat2plot) +
      tm_dots(col= "species", alpha=0.8,size = 0.1) +
      tm_legend(show = TRUE) +
      tm_view(set.zoom.limits = c(14,16))
    map1
  })

  observeEvent(input$addObservation,{
    to_add <- data.frame(lat = input$lat,
                         long = input$long,
                         species = input$species,
                         status = "Uncomfirmed"
    )
    theData$dat <- rbind(theData$dat,to_add) # adding new data
  })

  output$updatedData <- DT::renderDataTable(
    theData$dat
  )

}


shinyApp(ui = ui, server = server)

Like I said before I am wondering if it's even possible. Is there some function that will allow the observation of a click on the map and then return the lat/long coords, allowing me to add these to the data set? Thanks!


Solution

  • It can be done and you are in the rigth path to do it. This map is a leaflet map and there are event listerners asociated to it, like an on click event that updates an input with the lng and lat of the click coordinates.

    Just add this line to your app server, with this when you click the lng and lat inputs will be updated with the coordinates of the clicked place.

    observeEvent(input$tmapMap_click, {
        updateNumericInput(
          inputId = "long",
          value = input$tmapMap_click$lng
        )
        
        updateNumericInput(
          inputId = "lat",
          value = input$tmapMap_click$lat
        )
      })
    

    To read more about this, check the leaflet with shiny documentation