Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding tmap layers to Leaflet Proxy map #865

Open
James-G-Hill opened this issue Apr 18, 2024 · 5 comments
Open

Adding tmap layers to Leaflet Proxy map #865

James-G-Hill opened this issue Apr 18, 2024 · 5 comments
Labels

Comments

@James-G-Hill
Copy link

I was working on integrating tmap and leaflet this week in a package so that I can use tmap to create a static map then convert to leaflet when necessary & add any extra interactive elements on top. This is achievable with tmap_leaflet (although there were some annoyances such as the default converted Leaflet always showing 3 basemaps and then having to define the 1 basemap I want separately with tmap_options(basemaps).

I thought it would be great to define any layers that could be static in tmap and then convert to leaflet when I need to. This would reduce duplication of effort across tmap and leaflet.

However, one problem I came across is that I want to be able to use the layers I defined in leaflet with a proxy map. If I convert with tmap_leaflet I get a full leaflet map, not layers I can then add to a proxy map.

Is there any way to convert individual layers from tmap to leaflet format? Or extract layers from the output of tmap_leaflet and add them to another leaflet map.

I'm guessing this is not possible to do with the current version of tmap but maybe such an ability could be added in future?

Ultimately, I'd like to be able to define a tm_lines then convert to leaflet and attach it so that the lines and legend appear correctly on the leaflet map.

@mtennekes
Copy link
Member

I still have to migrate the tmapProxy/shiny integration in v4.

Your input is welcome, so thanks for that. However, I don't understand exactly what you mean yet. Are you looking for something like tmap_leaflet(tm, layers = c(1,3,5))? If so, why don't you just create a tmap with those layers?

Could you perhaps share a minimal use case / example, of something you want to achieve?

@mtennekes
Copy link
Member

shiny functions implemented for v4 :-) Please check if you have time @Nowosad @nickbearman @James-G-Hill @olivroy

If anything is missing or you have feature requests, please let me know

@Nowosad
Copy link
Member

Nowosad commented Jul 19, 2024

I've tried to modify the app from geocompr (https://github.com/geocompx/geocompr/blob/main/apps/CycleHireApp/app.R) to use the shiny features of tmap and it seems to be working fine (except the issue mentioned at #904):

library(shiny)
library(sf)
library(spData)
library(spDataLarge)
library(leaflet)
library(tmap)
library(units)
library(dplyr)
library(stringr)
tmap_mode("view")

# Based on input coordinates finding the nearest bicycle points
ui = fluidPage(
  # Application title
  titlePanel("CycleHireApp"),
  # Numeric Input from User
  bootstrapPage(
    div(style = "display:inline-block", 
        numericInput("x", ("Enter x-coordinate of your location"), value = 51.5, step = 0.001)),
    div(style = "display:inline-block", 
        numericInput("y", ("Enter y-coordinate of your location"), value = -0.1, step = 0.001)),
    div(style = "display:inline-block", 
        numericInput("num", "How many cycles are you looking for?", value = 1, step = 1))
  ),
  # Where leaflet map will be rendered
  fluidRow(
    tmapOutput("map", height = 300)
  )
)

server = function(input, output, session) {
  #Centering the leaflet map onto London - use if needed
  map_centre = matrix(c(-0.2574846, 51.4948089), nrow = 1, ncol = 2, 
                      dimnames = list(c("r1"), c("X", "Y")))
  
  #Based on input coords calculating top 5 closest stations to be displayed 
  
  #Making reactive object of input location coordinates
  input_pt = reactive({
    matrix(c(input$y, input$x), nrow = 1, ncol = 2,
           dimnames = list(c("r1"), c("X", "Y")))
  })
  input_pt_sf = reactive({
    st_as_sf(as.data.frame(input_pt()), coords = c("X", "Y"), crs = "EPSG:4326")
  })
  #Rendering the output map showing the input coordinates
  output$map = renderTmap({
    tm_shape() +
      tm_basemap() +
      tm_view(set.view = c(input_pt()[, "X"], input_pt()[, "Y"], 15)) 
  })
  #Finding the top distance between input coordinates and all other cycle stations, then sorting them.
  data = reactive({
    cycle_hire$dist = st_point(input_pt()) |>  
      st_sfc() |> 
      st_set_crs("EPSG:4326") |> 
      st_distance(cycle_hire$geometry) |>
      t() |> 
      set_units("km")
    
    cycle_hire[order(cycle_hire$dist), ]
  })
  #Filtering the distance data from above to show top 5 closest stations meeting requirement of # of bikes needed  
  filteredData = reactive({
    data() |> 
      filter(nbikes >= input$num) |> 
      head(5) |> 
      mutate(popup = str_c(str_c("Station:", name, sep = " "),
                           str_c("Available bikes:", nbikes, sep = " "), sep = "<br/>"))
    
  })
  #Making changes to the output leaflet map reflecting the cycle stations found above
  icons = tmap_icons(system.file("img/airplane.png", package = "tmap"))
  
  observe({
    proxy = tmapProxy("map", session, x = {
        tm_remove_layer(402) +
        tm_shape(input_pt_sf()) +
        tm_markers(size = 4, zindex = 402) +
        tm_remove_layer(401) +
        tm_shape(filteredData()) +
        tm_symbols(shape = icons, zindex = 401)
    })

    
  })
}
# Run the application
shinyApp(ui = ui, server = server)  

@mtennekes
Copy link
Member

#904 is fixed, let me know if there are any other issues

@James-G-Hill
Copy link
Author

@mtennekes Sorry, I have been away from this for a long time; when I look at my maps again soon I'll see if what you've done has fixed what I was trying to do.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants