tmap
tmap copied to clipboard
Adding `tmap` layers to Leaflet Proxy map
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.
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?
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
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 https://github.com/r-tmap/tmap/issues/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)
#904 is fixed, let me know if there are any other issues
@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.