I would like to add local tiles for leaflet to render them offline in a shiny application.
Although there are solutions to this on SO for example here and here , I am still ending up with grey map with no tiles. It would really help me to see some reproducible example.
Thanks.
My example code:
library(shiny)
library(dplyr)
library(RgoogleMaps)
#downloads tiles for a given regions, saves it to C:/Users/.../mapTiles/OSM
for (zoom in 0:16)
GetMapTiles(center = c(lat = 52.431635, lon = 13.194773),
zoom = zoom,
nTiles = round(c(20,20)/(17-zoom)))
#shiny ui
ui = fluidPage(leafletOutput("map"))
#create basic map, load tiles from directory and set view to centre of downloaded tiles
server = function(input, output, server){
addResourcePath(prefix = "OSM", "C:/Users/.../mapTiles")
output$map = renderLeaflet({
leaflet() %>%
addTiles( urlTemplate = "/OSM/{z}_{x}_{y}.png") %>%
setView(52.431635, 13.194773 , zoom = 10) %>% #set to the location with tiles
addMarkers(52.431635, 13.194773 )
}
)
}
shinyApp(ui, server)
In my case, I create my own tiles via gdal2tiles, which takes your data and automatically creates a {z}/{x}/{y}.png folder structure. Please see this link for a nice tutorial and what i mean about the file structure;
+---14
| +---8185
| +---5460.png
| +---5461.png
| +---etc.png
| \---8186
# I use the following server (see how my addTiles has a folder structure)
server <- function(input, output,session) {
addResourcePath("mytiles", "C:/.../tiles")
output$tilemap <- renderLeaflet({
leaflet() %>%
setView(lng = -4.4, lat = 52, zoom = 12) %>%
addTiles(urlTemplate = "mytiles/{z}/{x}/{y}.png")
})
}
Now, as you are downloading tiles from Google Maps to your hard drive, you'll want a slightly different approach as the files are downloaded in a {z}_{x}_{y}.png format, and not produced into a file structure like gdal creates;
+---11_1098_671.png
etc.
so you need to adjust your addTiles code to reflect this, using underscores, like the Google filenames;
server <- function(input, output,session) {
addResourcePath("mytiles", "C:/.../OSM")
output$tilemap <- renderLeaflet({
leaflet() %>%
setView(lng = 13.194773, lat = 52.431635, zoom = 11) %>%
addTiles(urlTemplate = "mytiles/{z}_{x}_{y}.png")
})
}
Lastly, my setView arguments are in a different order to yours but i'm not sure whether that makes a difference or not.
i tried this solution but it could not work,the topic is old but it really helped me to achieve what i wanted to do, i found another solution for those of you in the same case by creating two ports :
just define two differents ports for your shiny server( 3838) and for the server hosting the tiles (8000)
servr::httd(dir="C:/TestApp/data_hydrepat/tiles_hydrepat/mapTiles/mytiles",daemon=TRUE,port=8000)
options(shiny.port = 3838)
to close the server hosting the tiles, just put a reactive on an input or something.. and close
(servr::daemon_stop(which = daemon_list())
hope it'll help !
Related
I have a requirement where I have some cities' longitude and latitude.
Bangalore - 12.9539974,77.6309395
Chennai - 13.0473748,79.9288083
Delhi - 28.6921165,76.8079346
Mumbai - 19.0821978,72.741098
I have created an input box that has some cities listed down
Image
Based on this input I need to zoom into these cities.
How can I do this?
You can do this with leaflet
library(dplyr)
library(shiny)
library(leaflet)
data_cities = data.frame(
city = c('Bangalore', 'Chennai', 'Delhi', 'Mumbai'),
lat = c(12.9539974, 13.0473748, 28.6921165, 19.0821978),
lng = c(77.6309395, 79.9288083, 76.8079346, 72.741098)
)
ui <- fluidPage(
selectInput("select_city", label = "Select city", choices = data_cities$city),
leafletOutput("map")
)
server <- function(input, output) {
# Initiate the map
output$map <- renderLeaflet({
leaflet() %>%
addTiles(options = providerTileOptions(noWrap = TRUE))
})
#Zoom when select city
observeEvent(input$select_city, {
selected_city_data <- data_cities %>%
filter(city == input$select_city)
leafletProxy("map") %>%
setView(lng = selected_city_data$lng, lat = selected_city_data$lat, zoom=8)
})
}
shinyApp(ui = ui, server = server)
Well, you can use a simple API for pinning out the location on selection.
There are several API's available on net.
The one I would suggest is to use a free API's like - IPAPI.
All you need to do is to go through the documentation on their official website, in-order to get a close picture of how the API works.
In that case, you can use a package 'ggmap' and its function named qmap() within it.
or you can also check for package named 'maps'. Install this package and use map.cities() function.
There's one more lib for the same, its called rMaps. This package is available on github.
I think there are a lot of ways to implement this, maybe it should work somehow. In any case, it is necessary to read the documentation.
HTML: <div id="buttons"></div>
JS:
var Balganore = L.map('Bangalore').setView([12.9539974,77.6309395], 8);
Jquery:
$('#buttons').append(
$('<div>').text('City choose:'),
$('<label>').append(
$('<input>').prop({'type':'checkbox', 'checked':true}).click(function(){
$(Bangalore).toggle();
}),
'Bangalore'
),)
I'm using the leaftlet.extras R package to add Gps control inside a map.
I'm using the extension addControlGPS inside my code :
... %>%
addControlGPS(options = gpsOptions(position = "topleft", activate = TRUE,
autoCenter = TRUE, maxZoom = 60,
setView = TRUE)) %>%
...
The controller works ok.
I need to extract the Gps coordinates to re-use in my code as arguments for other functions. Is there any way to do that ?
Every time the gps location updates, the coordinates are written to map.id+'_gps_located'. You can find all leaflet.extras bindings in the htmlwidgets/bindings folder in their git.
Working example
library(leaflet)
library(leaflet.extras)
library(shiny)
ui <- fluidPage(
leafletOutput('map')
)
server <- function(input, output, session) {
output$map <- renderLeaflet({ leaflet()%>%addTiles() %>%
addControlGPS(options = gpsOptions(position = "topleft", activate = TRUE,
autoCenter = TRUE, maxZoom = 60,
setView = TRUE))})
observe(
print(input$map_gps_located)
)
}
shinyApp(ui, server)
I've recently had a similar problem with an app I was working on.
You can extract the gps coordinates from a leaflet map by using the _marker_click feature, where is the map label you specify as an output for the leaflet rendering statement.
In my case here's the chunk of code I used to retrieve the coords. In my case the output name of the map object was parksMap therefore the full input to consider in the event observation was parksMap_marker_click. This statement can be saved in a variable (in my case pin), that stores the coordinates data. Finally you need to wrap it all in a reactive expression to be able to save every coordinate when clicking on a point in the leaflet map.
# code to load the park card once the click event on a marker is intercepted
observeEvent(input$parksMap_marker_click, {
pin <- input$parksMap_marker_click
#print(Sys.time()) #uncomment to log coords
#print(pin) #uncomment to log coords
selectedPoint <- reactive(parks[parks$Latitude == pin$lat & parks$Longitude == pin$lng,])
leafletProxy("parksMap", data = selectedPoint()) %>% clearPopups() %>%
addPopups(~Longitude,
~Latitude,
popup = ~park_card(selectedPoint()$ParkName, selectedPoint()$ParkCode, selectedPoint()$State, selectedPoint()$Acres, selectedPoint()$Latitude, selectedPoint()$Longitude)
)
})
The full github repo of the app is available here.
I have been working on my first little project in R and have run into an issue with a Leaflet map. It will render properly with the data and design I have specified thus far, but once I move the map in browser or the R viewer in RStudio it will no longer react to clicks/drags/etc. and will not react even if it is left alone for several minutes.
I have also had an issue with the zoom functionality, I am not sure if this is due to something that I missed or something to do with the above issue.
Example of the data:
Data_example
# Libraries ---------------------------------------------------------------
library("shiny")
library("tidyverse")
library("leaflet")
library("leaflet.minicharts")
# UI ----------------------------------------------------------------------
ui <- fluidPage(
titlePanel("Wiersma Sale Iceland Trip"),
mainPanel(
leafletOutput(outputId = "Map_1", height = 1080, width = 1920)
)
)
# Server ------------------------------------------------------------------
server <- function(input, output) {
sheets_data <- read.csv("Iceland_Mark2 - Data.csv")
output$Map_1 <- renderLeaflet({
m <- leaflet(data = sheets_data) %>%
addTiles() %>%
addMinicharts(
sheets_data$Long,
sheets_data$Lat,
type = "pie",
popup = popupArgs(
labels = c("A", "B", "C"),
html = paste0(
"<div>",
"<h3>",
sheets_data$Name,
"</h3>",
"Description: ",
sheets_data$Description,
"<br>",
"Media_1: ",
sheets_data$Media_1,
"</div>"
)
)
)
})
}
# Run_App -----------------------------------------------------------------
shinyApp(ui = ui, server = server)
The output:
Output_of_app
It needn't be pretty, nor unique, but it does need to react to zooming and movement and I can't for the life of me figure out why it behaves this way.
I had the same problem suddenly come up after having already produced a number of maps with no issues. So I figured it most likely was to do with the data I was feeding it.
I had one row in my chartdata that had NAs. Deleting this row and remapping fixed the problem.
Here is some reproducible R/Shiny code that I whipped up to illustrate the issue I am having... Basically, if I try to embed a numeric input into a leaflet map, as illustrated below, I am unable to call the value input specified in the numeric input.
After scouring the web, I have a hunch that a JS-based solution is required because of the way shiny and leaflet render maps. I tried creating a UI output, then a render a UI numeric input, attempted various strategies using reactive functions but did not get very far. Any help would be appreciated as I have been banging my head on the wall trying to figure this out.
library(shiny)
library(leaflet)
library(dplyr)
content <- paste(sep = "<br/>",
"<b>Change number below:</b>",
numericInput("numeroUno", label = NULL, value = 1)
)
ui <- fluidPage(
"This is a map",
leafletOutput("myMap"),
numericInput('numeroDos', label = NULL, value = 5),
textOutput("mapPopupLink"))
server <- function(input, output, session) {
output$myMap <- renderLeaflet({
leaflet() %>%
setView(lng = 25.0343, lat = 77.3963, zoom = 5) %>%
addPopups(-122.327298, 47.597131, content, options = popupOptions(closeButton = FALSE)) %>%
addTiles() %>%
addProviderTiles(providers$OpenTopoMap)
})
output$mapPopupLink <- renderText({
paste("The ui-based widget prints: ", input$numeroDos, ". But the server-based widget does not: ", input$numeroUno)
})
}
shinyApp(ui, server)
Please help!
Let me know if you need additional info.
I have a Shiny App that inserts a circle on a map based on the lat lng associated with the zip code input. The map renders when I load it; however, when I attempt to change the value of the zip code via a selectInput object, the map renders a blank window - i.e. the selectedZip variable.
Any help addressing this issue will be appreciated:
library(shiny)
library(leaflet)
# Data
data <- read.csv('VENDOR_PERFORMANCE_EX.csv')
ui <- fluidPage(
titlePanel("VPD"),
sidebarLayout(
sidebarPanel("Inputs"),
mainPanel("Results")),
selectInput("zipInput", "Select Zip Code", data$Zip),
selectInput("vendorInput", "Select Vendor", as.character(data$Vendor)),
leafletOutput("CLEmap", width = "75%", height = 600)
)
server <- function(input, output, session) {
selectedZip <- reactive({
data[data$Zip == input$zipInput, ]
})
output$CLEmap <- renderLeaflet({
leaflet() %>%
addTiles() %>%
setView(-81.730844, 41.430102, zoom = 11) %>%
addCircles(data = selectedZip(), lng = ~ Y, lat = ~ X, radius = 1069)
})
}
shinyApp(ui=ui, server = server)
This works, although there is something very strange going on. And although I can't be sure it fixes the same problem you have because I don't have your data, it seems likely.
Once I added data and got something that sounded like your error I hunted around a bit. The only change I made in the end is adding a unique statement to your zipInput instance of selectInput, I was clued by the fact that that selectInput was not initializing correctly, although it was actually working other than the initial value being blank.
I think that the selectInput control was not correctly able to deal with duplicate entries in the choices vector, and was causing the shiny control to behave strange in some way, and thereby corrupting ... something. Not really sure what.
Weird. And not sure of what was really going on. Anyway this works. And if you take out the unique it does not work and gets an error like you describe.
The code:
library(shiny)
library(leaflet)
# Data
#data <- read.csv('VENDOR_PERFORMANCE_EX.csv')
data <- data.frame(Zip=c("44102","44102","44109"),
Vendor=c("Vendor1","Vendor2","vendor3"),
X=c(41.475,41.477,41.467),Y=c(-81.742,-81.748,-81.697))
ui <- fluidPage(
titlePanel("VPD"),
sidebarLayout(
sidebarPanel("Inputs"),
mainPanel("Results")),
selectInput("vendorInput", "Select Vendor", as.character(data$Vendor)),
selectInput("zipInput", "Select Zip Code", unique(as.character(data$Zip)) ),
leafletOutput("CLEmap", width = "75%", height = 600)
)
server <- function(input, output, session) {
selectedZips <- reactive({
data[data$Zip == input$zipInput, ]
})
output$CLEmap <- renderLeaflet({
leaflet() %>%
addTiles() %>%
setView(-81.730844, 41.430102, zoom = 11) %>%
addCircles(data=selectedZips(),lng = ~Y, lat = ~X,radius = 300 )
})
}
shinyApp(ui=ui, server = server)
The output: