I am building a shiny application which contains a leaflet map with markers on it, and a table with information about each marker next to it. As I zoom in the leaflet map, how would I update the table to only show markers still visible on the leaflet map?
# Minimum Viable Example
library(shiny)
library(leaflet)
library(DT)
data(quakes)
# Define UI
ui <- fluidPage(
# leaflet box
column(
leafletOutput("mymap"),
width = 8
),
#data table box
column(
DT::dataTableOutput("table"),
width = 4
)
)
# Define server logic
server <- function(input, output) {
# leaflet map
output$mymap <- renderLeaflet({
# Show first 20 rows from the `quakes` dataset
leaflet(data = quakes[1:20,]) %>% addTiles() %>%
addMarkers(~long, ~lat, popup = ~as.character(mag), label = ~as.character(mag))
})
# data table
output$table = DT::renderDataTable({
quakes
})
}
# Run the application
shinyApp(ui = ui, server = server)
Use the input$mymap_bounds event to filter the data. In your example, add library(dplyr) and change output$table to
output$table = DT::renderDataTable({
if (isTruthy(input$mymap_bounds)) {
bounds = input$mymap_bounds
quakes %>% filter(
between(long, bounds$west, bounds$east),
between(lat, bounds$south, bounds$north)
)
} else
quakes
})
Note that this filters the entire quakes table, not the 20 items you show. Modify to suit. See the Events section of this page for details:
https://rstudio.github.io/leaflet/shiny.html
Related
I'm currently using addSearchOSM() from the leaflet.extras package to search addresses:
How can I change the colour of the circle marker? Will accept CSS solutions as well - I attempted to manually update the .leaflet-interactive{} css, but that changes all interactive elements, including polygons.
Reproducible example here:
library(shiny)
library(leaflet)
library(tidyverse)
library(leaflet.extras)
ui <- fluidPage(
fluidRow(
column(
width = 12,
leafletOutput("map")
)
)
)
server <- function(input, output) {
output$map <- renderLeaflet({
leaflet() %>%
addTiles() %>%
setView(
lng = -73.9888,
lat = 40.72905,
zoom = 12
) %>%
addSearchOSM()
})
}
# Run the application
shinyApp(ui = ui, server = server)
I am building a Shiny app with a Leaflet map based on a PostgreSQL spatialdatabase.
I succeeded to import spatial data into SpatialPolygonDataFrame and to display it on Leaflet widget.
I am trying to display the data from the SpatialDataFrame with a RenderTable output, but its not working, even by converting it with as.data.frame(spatialdataframe).
Therefore this conversion is enough to dispay the table with view(), kable() or other display functions, but not in Shiny.
Should I make another conversion? Anyone got an idea?
ui <- fluidPage(
titlePanel("AgriPAG"),
sidebarLayout(
mainPanel(
tabsetPanel(
tabPanel(leafletOutput("m",width = "100%", height = 1000)),
tabPanel(tableOutput(as.data.frame(sample_test1)))
)
),
sidebarPanel("curseur")
)
)
server <- function(input,output, session){
data <- reactive({
x <- test1
})
output$mymap <- renderLeaflet({
test1 <- data()
m <- leaflet(data = sample_test1) %>%
addTiles() %>%
setView(lng=-52.3333300, lat=4.9333300 , zoom=10) %>%
addPolygons(data=sample_test1, weight=2, col="black", opacity=0.5)
m
})
output$table <- renderDataTable(as.data.frame(sample_test1))
}
shinyApp(ui = ui, server = server)
renderDataTable does not work with tableOutput. You have to use dataTableOutput instead. In addition, you should add the correct inputId for dataTableOutput.
To get everything to work change: tableOutput(as.data.frame(sample_test1)) in your ui to dataTableOutput('table')
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))
})
})
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)
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.