I am trying to figure out how to zoom into a map based on user inputs. I have started with the map of the US and then trying zooming into a specific location based on zip code and number of miles:
shinyUI(fluidPage(
# Application title
titlePanel("Starbucks Locator"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
textInput("zip","Zip Code:", value = "18101"),
sliderInput("radius",
"Radius",
min = 1,
max = 25,
value = 15),
actionButton("go", "Submit")
),
# Show a plot of the generated distribution
mainPanel(
leafletOutput("myMap")
)
)
))
This is the Server Code:
library(shiny)
library(ZipRadius)
library(leaflet)
shinyServer(function(input, output) {
data <- reactive({x <- readRDS("Starbucks.rds")})
output$myMap <- renderLeaflet({
df <- data()
m <- leaflet(data = df) %>%
addTiles() %>%
addMarkers(lng = ~Lon, lat = ~Lat)
})
eventReactive(input$go, {
zip_include <- zipRadius(input$zip, input$radius)
})
leafletProxy("myMap") %>% fitBounds(~min(zip_include$longitude), ~min(zip_include$latitude),
~max(zip_include$longitude), ~max(zip_include$latitude))
})
I am using the ZipRadius package to find the which zip codes are input$distance away from input$zip. Then I want to zoom into the input zip code as the center and the bounds defined by the radius (input$distance). The original leaflet map works, but the leafletProxy is where I am having my issue. Any thoughts on how to do this? I am trying to use fitbounds since it can take into account minimum and maximum latitudes and longitudes. I think the issue may be with the eventReactive, but not sure how to test it. The dataset is available here at github.
There are a few issues with your server code.
You don't need to create your data in a reactive expression. As you only upload it once (I guess you won't change the coords or add another row).
Your renderLeaflet function can be simplified
Instead of using eventReactive use observeEvent. This function is triggered when you press the go button. In the observer you have to add all the actions that should be triggered by the button click. So you also have to insert the leafletProxy function here.
Remove the ~ symbol in the fitBounds function. You are already pointing to the zip_include dataset in fitBounds.
Working server (with provided csv)
shinyServer(function(input, output) {
data <- read.csv("c:/starbucks_us_locations.csv")
names(data) <- c('Lon', 'Lat')
output$myMap <- renderLeaflet({leaflet(data) %>%
addTiles() %>%
addMarkers(lng = ~Lon, lat = ~Lat)
})
observeEvent(input$go, {
zip_include <- zipRadius(input$zip, input$radius)
print(zip_include)
leafletProxy("myMap") %>% fitBounds(min(zip_include$longitude), min(zip_include$latitude),
max(zip_include$longitude), max(zip_include$latitude))
})
})
Related
I am creating an app that allows the user to drop markers on the map. What I would like to do is show the user what markers they have dropped, and give them the option of downloading them. Whilst I could build another object to record historic map click information, shiny must be storing this information already. So, my question is fundamentally:
How do I extract the lat lon information from the marker_map object in the code?
OR
Where is all that marker lat lon information stored and how can I get it?
library(leaflet)
library(shiny)
ui <- fluidPage(
leafletOutput("mymap")
)
server <- function(input, output, session){
output$mymap <- renderLeaflet({
leaflet(options = leafletOptions(doubleClickZoom= FALSE)) %>%
setView(lng = 2, lat = 55, zoom = 5) %>%
addTiles()
})
observeEvent(input$mymap_click, {
click = input$mymap_click
marker_map <- leafletProxy('mymap') %>% addMarkers(lng = click$lng, lat = click$lat)
marker_map
})
}
shinyApp(ui, server)
I have data frame df which has two variables lat and lon, Now I need to create a Shinydashboard which updates the map by taking next row value in from the data frame after every 10 seconds.
df
df <- data.frame("Lat" = c(12.8882, 12.890, 12.891), "Lon" = c(77.58195,77.58190,77.581958))
Ui.R
library(shiny)
library(leaflet)
shinyUI( fluidPage(
leafletOutput("map1")
)
)
server.R
library(shiny)
shinyServer(function(input, output, session) {
output$mymap <- renderLeaflet({
leaflet() %>%
addTiles() %>% # Add default OpenStreetMap map tiles
addMarkers(lng=df$lon, lat=df$lat)})
})
Only thing I know is I can use invalidateLater() to call the timer but I do not know how to implement that for the incremental reading of the rows in the data frame.
Expected Result
I need a map where the marker moves to the next position after every 10 Seconds, The coordinates for moving the marker is given through the data frame df.
You can use a reactiveVal() to keep track of the currently displayed marker, and use observe() in combination with invalidateLater() and leafletProxy() to remove the previous marker and add the new one. To do so we can give the layer a layerId everytime we add our marker, which we can then use to remove the marker again when plotting the next marker.
A working example is given below, I added some comments to illustrate what is happening. Hope this helps!
library(shiny)
library(leaflet)
set.seed(1)
df <- cbind(rnorm(40) * 2 + 13, rnorm(40) + 48)
ui <- fluidPage(
leafletOutput("mymap")
)
server <- function(input, output, session) {
# Create the base map
output$mymap <- renderLeaflet({
leaflet() %>%
addProviderTiles(providers$Stamen.TonerLite,
options = providerTileOptions(noWrap = TRUE)
) %>%
setView(lng = mean(rnorm(1000) * 2 + 13), lat = mean(rnorm(1000) + 48), zoom = 7)
})
# Initialize a reactiveVal to keep track of which point is currently selected
point_to_plot <- reactiveVal(1)
observe({
# invalidate every 2 seconds
invalidateLater(2000)
isolate({
# update point_to_plot() to next value. If next value is higher than the amount of rows
# in df, set it to 1.
point_to_plot(ifelse(point_to_plot()+1<=nrow(df),point_to_plot()+1,1))
# Use leafletProxy to remove our previous marker, and add the new one.
leafletProxy('mymap') %>%
removeMarker('my_marker') %>%
addMarkers(layerId = 'my_marker',data = df[point_to_plot(),,drop=F])
})
})
}
shinyApp(ui, server)
EDIT: Working example with your data:
library(shiny)
library(leaflet)
set.seed(1)
df <- data.frame("Lat" = c(12.8882, 12.890, 12.891), "Lon" = c(77.58195,77.58190,77.581958))
ui <- fluidPage(
leafletOutput("mymap")
)
server <- function(input, output, session) {
# Create the base map
output$mymap <- renderLeaflet({
leaflet() %>%
addProviderTiles(providers$Stamen.TonerLite,
options = providerTileOptions(noWrap = TRUE)
) %>%
setView(lat = 12.89, lng = 77.58195, zoom = 14)
})
# Initialize a reactieVal to keep trakc of which point is currently selected
point_to_plot <- reactiveVal(1)
observe({
# invalidate every 2 seconds
invalidateLater(2000)
isolate({
# update point_to_plot() to next value. If next value is higher than the amount of rows
# in df, set it to 1.
point_to_plot(ifelse(point_to_plot()+1<=nrow(df),point_to_plot()+1,1))
# Use leafletProxy to remove our previous marker, and add the new one.
leafletProxy('mymap') %>%
removeMarker('my_marker') %>%
addMarkers(layerId = 'my_marker',data = df[point_to_plot(),,drop=F])
})
})
}
shinyApp(ui, server)
The minimal example below renders a leaflet map with 3 markets, and a DT table with 3 records. When a market on the map is selected, so to is the matching record on the table. However, what I cannot do, is to also have the reverse of that, where a clicked row on the table also shows the related popup on the map.
I have been unable to find an example R shiny leaflet app that does something similar.
CODE tweaked to reflect initial comments
library(shiny)
library(leaflet)
library(DT)
library(tidyverse)
# Define UI for application that draws a histogram
ui <- fluidPage(
leafletOutput("opsMap"),
DT::dataTableOutput('ranksDT')
)
# Define server logic required to draw a histogram
server <- function(input, output) {
lats <- c(21.608889,21.693056, 24.04)
longs <- c(-74.650833, -73.095,-74.341944)
popups <- c('a','b','c')
layerids <- c('a','b','c')
iconNames <- c('cog','cog','cog')
iconColors <- c('red','red','red')
sampleData <- tibble(lats,longs, popups,layerids,iconNames,iconColors)
score <- c(7,3,9)
locationRanks <- tibble(popups, score)
output$opsMap <- renderLeaflet({
leaflet() %>%
addTiles() %>%
addAwesomeMarkers(lat = sampleData$lats,
lng = sampleData$longs,
popup = sampleData$popups,
layerId = sampleData$layerids,
icon = makeAwesomeIcon(icon=sampleData$iconNames,
markerColor=sampleData$iconColors))
})
output$ranksDT <- DT::renderDataTable({
d1 <- datatable(locationRanks,
selection = 'single',
rownames=FALSE,
options = list(dom = 'tpi',
pageLength =5,
paging=FALSE,
searching=FALSE
)
)
d1
})
# create a reactive value that will store the click position
mapClick <- reactiveValues(clickedMarker=NULL)
mapClick <- reactiveValues(clickedGroup=NULL)
# create a reactive for the DT table
locationClick <-reactiveValues(clickedRow = NULL)
# observe click events
observe({
mapClick$clickedMarker <- paste(input$opsMap_marker_click$id)
mapClick$clickedGroup <- paste(input$opsMap_marker_click$group)
locationClick$clickedRow <- input$ranksDT_rows_selected
})
# define a proxy variable for the plant rank table
proxy1 = dataTableProxy('ranksDT')
# when map is clicked, make the same table row selection - need row number
observeEvent(input$opsMap_marker_click$id, {
a <- which(locationRanks[1] == input$opsMap_marker_click$id)
proxy1 %>% selectRows(a)
})
proxy2 = leafletProxy('opsMap', session = shiny::getDefaultReactiveDomain())
# if table is clicked, select the same market from the map
observeEvent(locationClick$clickedRow, {
a <- as.character(locationRanks[locationClick$clickedRow,1])
cat(file=stderr(),"clicked row", locationClick$clickedRow, a,'\n')
#proxy2 %>% opsMap_marker_click$id <- a
})
}
# Run the application
shinyApp(ui = ui, server = server)
A solution could be to use input$map01_marker_click$id together with dataTableProxy(), selectRows() and selectPage() if you want to highlight rows in the datatable.
In order to highlight markers, i think you could either use some javascript to simulate a click on the marker. But i would also go for the easier way to adding a highlighted marker and removing it afterwards.
Basically your question was partly answered in this question: Shiny - how to highlight an object on a leaflet map when selecting a record in a datatable? and the remaining part was in one of the answers. -> credits to them.
As the code was quity lengthy, i made the effort to reduce it towards a minimal reproducible example.
Minimal reproducible example:
library(shiny)
library(leaflet)
library(DT)
qDat <- quakes[1:10, ]
qDat$id <- seq.int(nrow(qDat))
ui <- fluidPage(
mainPanel(
leafletOutput('map01'),
dataTableOutput('table01')
)
)
server <- function(input,output){
output$table01 <- renderDataTable({
DT::datatable(qDat, selection = "single", options = list(stateSave = TRUE))
})
# to keep track of previously selected row
prev_row <- reactiveVal()
# new icon style
highlight_icon = makeAwesomeIcon(icon = 'flag', markerColor = 'green', iconColor = 'white')
observeEvent(input$table01_rows_selected, {
row_selected = qDat[input$table01_rows_selected, ]
proxy <- leafletProxy('map01')
proxy %>%
addAwesomeMarkers(popup = as.character(row_selected$mag),
layerId = as.character(row_selected$id),
lng = row_selected$long,
lat = row_selected$lat,
icon = highlight_icon)
# Reset previously selected marker
if(!is.null(prev_row())){
proxy %>%
addMarkers(popup = as.character(prev_row()$mag),
layerId = as.character(prev_row()$id),
lng = prev_row()$long,
lat = prev_row()$lat)
}
# set new value to reactiveVal
prev_row(row_selected)
})
output$map01 <- renderLeaflet({
leaflet(data = qDat) %>%
addTiles() %>%
addMarkers(popup = ~as.character(mag), layerId = as.character(qDat$id))
})
observeEvent(input$map01_marker_click, {
clickId <- input$map01_marker_click$id
dataTableProxy("table01") %>%
selectRows(which(qDat$id == clickId)) %>%
selectPage(which(input$table01_rows_all == clickId) %/% input$table01_state$length + 1)
})
}
shinyApp(ui = ui, server = server)
This is not a solution, just some things that I found out about the code when looking at it.
The ID plantRanks only appears once in your code.
That is in input$plantRanksDT_rows_selected. Such things are easy to find and easy to fix. The correct id should be the output id of the datatable, so ranksDT. Once you replace that, you will see a second issue
proxy2 %>% opsMap_marker_click$id <- a makes no sense.
input$opsMap_marker_click$id exists but can obviously not be written. I don't exactly know how leaflet proxys work, but
leaflet::addMarkers()
looks promising. Good luck!
I'm creating a shiny leaflet map to record where I have been. I have a dataset contains coordinates and time. In my shiny app I've got 2 widgets-- a sliderbard for time line, a dropdown box to show the current countries that I have been. The country choices in the dropdown box is based on the time line sliderbar. Say for example: before 2016 all coordinates on the map are in country A then in the dropdown box there will be only one option in the dropdown box (country A). After 2016-01-01, the number of countries that I have been increased to 2 then in the dropdown box there will be 2 options (country A and country B) and currently this function works well.
Now I want to further develop my shiny app, the function I want is when I have multiple countries in the dropdown box, the app should allow me to choose one of the countries and when the country is chosen, the leaflet map will focus on the country I choose. I think using if else in setview() should solve the problem.
Then I created a (partially) workable shiny script below:
global.R
df <-read.csv("https://dl.dropbox.com/s/5w09dayyeav7hzy/Coordinatestest.csv",
header = T,
stringsAsFactors = F)
df$Time <- as.Date(df$Time, "%m/%d/%Y")
countriesSP <- getMap(resolution='low')
and
ui.R
library(devtools)
library(leaflet)
library(htmlwidgets)
library(shiny)
library(shinydashboard)
library(sp)
library(rworldmap)
library(RCurl)
header <- dashboardHeader(
title = 'Shiny Memery'
)
body <- dashboardBody(
fluidRow(
tabBox(
tabPanel("My Map", leafletOutput("mymap",height = 550)),
width = 700
))
)
dashboardPage(
header,
dashboardSidebar(
sliderInput('Timeline Value','Time line',min = min(df$Time),max = max(df$Time), value = min(df$Time)),
selectInput("select_country", label = "Select Country",
choices = NULL,
selected = NULL)
),
body
)
and
server.R
shinyServer(function(input, output, session) {
output$mymap <- renderLeaflet({
df <- subset(df, df$Time <= input$`Timeline Value`)
observe({
pointsSP <- SpatialPoints(df[,c("lon", "lat")], proj4string=CRS(proj4string(countriesSP)))
indices <- over(pointsSP, countriesSP)
part_choices <- as.list(c("All", na.omit(unique(as.character(indices$ADMIN)))))
updateSelectInput(session, "select_country", choices=part_choices)
})
lng <- ifelse(input$select_country == "All", mean(df$lon), 0)
lat <- ifelse(input$select_country == "All", mean(df$lat), 0)
m <- leaflet(df) %>%
addTiles(
#urlTemplate = "http://otile4.mqcdn.com/tiles/1.0.0/sat/{z}/{x}/{y}.png"
) %>% # Add default OpenStreetMap map tiles
#setView(mean(df$lon), mean(df$lat), zoom = 5) %>%
setView(lng, lat, zoom = 5) %>%
addMarkers(~lon, ~lat,
clusterOptions = markerClusterOptions())
})
})
Please copy and paste the script into Rstudio and run it. You will see as you drag the time line till the end, the country option will increase but default is always All. Ideally when I select one country and as you see based on some simple logic as long as the selection is not All, the coordinates in setview() function should be (0,0) (this can be dynamic later, currently I just want setview() to change the focus of the map). This function is not really working currently, i.e. when I select other country, the focus of the map will change to (0,0) but again change back to the default focus (mean(df$lon), mean(df$lat)) immediately and the selection will change back to All as well.
So any idea on how to alter my code to make this work?
Hope you are clear about my situation in this example.
Much appreciate for the help
I have changed the server.R part how I think this should be done. Let me know if this helps.
server.R
shinyServer(function(input, output, session) {
dfs <- reactive({
tmp <- subset(df, df$Time <= input$`Timeline Value`)
tmp
})
part_choices <- reactive({
tmp <- dfs()
pointsSP <- SpatialPoints(tmp[,c("lon", "lat")], proj4string=CRS(proj4string(countriesSP)))
indices <- over(pointsSP, countriesSP)
as.list(c("All", na.omit(unique(as.character(indices$ADMIN)))))
})
observe({
updateSelectInput(session, "select_country", choices=part_choices())
})
output$mymap <- renderLeaflet({
lng <- ifelse(input$select_country == "All", mean(df$lon), 0)
lat <- ifelse(input$select_country == "All", mean(df$lat), 0)
m <- leaflet(dfs()) %>%
addTiles(
#urlTemplate = "http://otile4.mqcdn.com/tiles/1.0.0/sat/{z}/{x}/{y}.png"
) %>% # Add default OpenStreetMap map tiles
#setView(mean(dfs()$lon), mean(dfs()$lat), zoom = 5) %>%
setView(lng, lat, zoom = 5) %>%
addMarkers(~lon, ~lat,
clusterOptions = markerClusterOptions())
})
})
I am trying to create an interactive webmap in R to display locations using Shiny and Leaflet
The idea is that the user selects one input and the markers corresponding to that input(lat/long which are to be fetched from data set of the corresponding input) are displayed in a Leaflet map (with zoom in/out function).
Any help/advice would be greatly appreciated!
(sample data file uploaded here):
enter code here
Server.R
library(shiny)
library(rpart.plot)
library(leaflet)
shinyServer(
function(input, output) {
output$dtmplot <- renderPlot({
dtmplot <- rpart.plot(dtm, type=4, extra=101)
})
observe({
output$map <- renderLeaflet( {
for(j in 1:nrow(df))
{
if(df[j, "col1"]==input$input1) {
map <- leaflet() %>%
addTiles() %>%
addMarkers(lng=df[j,"Longitude"], lat=df[j,"Latitude)
}
}
})
})
}
)
enter code here
UI.R
library(shiny)
library(leaflet)
shinyUI(
pageWithSidebar(
headerPanel("Sample project"),
sidebarPanel(
plotOutput("dtmplot"),
selectInput("input1",
label = "label1:",
choices = c(“choice1”,”choice2”),
selected = " choice1"),
sliderInput("slider","Please select slider input", min=1,max=100,value=20,step=10)
),
mainPanel(
leafletOutput("map")
)
))
The basic code to handle custom points in a leaflet map is available below. The code utilises the official example available on the leaflet GitHub and provided end-user with the functionality to display custom location on the map.
app.R
library(shiny)
library(leaflet)
r_colors <- rgb(t(col2rgb(colors()) / 255))
names(r_colors) <- colors()
ui <- fluidPage(
leafletOutput("mymap"),
p(),
h1("Added example to add more points here:"),
p(),
numericInput("long", label = h3("Longitude:"), value = 11.242828),
numericInput("lat", label = h3("Latitude:"), value = 30.51470),
actionButton("recalc", "Show point")
)
server <- function(input, output, session) {
points <- eventReactive(input$recalc, {
cbind(input$long, input$lat)
}, ignoreNULL = FALSE)
output$mymap <- renderLeaflet({
leaflet() %>%
setView(lat = 30, lng = 11, zoom = 4) %>%
addProviderTiles("Stamen.TonerLite",
options = providerTileOptions(noWrap = TRUE)
) %>%
addMarkers(data = points())
})
}
shinyApp(ui, server)
Results
The obtained map looks like that:
Explanation
The mechanics is fairly simple and can be summarised in the following steps:
You need to pas lat and lon to your map to addMarkers. In my example this is done via primitive input files but it can be done in a number of ways.
You have to decide on the logic of dynamically adding markers to your map; in the presented case this is done with use of an actionButton.
Side notes
As at the time of drafting this answer there was no clarity with respect to the actual data that should be represented on the map, I found it more informative to generate the desired functionality following the official example instead of trying to modify the provided code.
The thing worth noting is that the lat/lon values have to be of correct format to appear on the map.
The map setView to make the example more presentable but in an actual solution, default lat/lon values should be generated dynamically.