Add Moving Marker to a shiny leaflet - r

I am trying to visualise a random walk. Not its path, but actually see the marker moving as it wanders around. Something like this.
I have come with this workaround in which I clear all markers and add them again with the new positions at every step.
library(shiny)
library(leaflet)
df <- data.frame(latitude = 10, longitude = 0)
ui <- fluidPage(
sliderInput("time", "date", 0,
1e2,
value = 1,
step = 1,
animate = TRUE
),
leafletOutput("mymap")
)
server <- function(input, output, session) {
points <- eventReactive(input$time, {
df$latitude <- df$latitude + rnorm(1)
df$longitude <- df$longitude + rnorm(1)
df
})
output$mymap <- renderLeaflet({
leaflet() %>%
addTiles()
})
observe({
leafletProxy("mymap") %>%
clearMarkers() %>%
addMarkers(data = points())
})
}
shinyApp(ui, server)
But I found a much more neat solution in this method movingMarker. I was wondering if there's a way to implement it using that javascript code.

Related

R Leaflet : detect on which polygon is the map bounds center

I would like to automatically detect which polygon is at the center of the map. And it should update dynamically when the user is moving through the map.
For the moment I could not find a way to reverse find on which polygon are some coordinates.
I think I could simulate a input$map_shape_click with shinyjs or javascript and so get input$map_shape_click$id, but before I go to this solution, I would like to make sure there is no other way.
Here is a minimal example
library(leaflet)
library(shiny)
# data source : https://biogeo.ucdavis.edu/data/gadm3.6/Rsp/gadm36_FRA_2_sp.rds
cities <- readRDS(file = "../gadm36_FRA_2_sf.rds")
ui <- fluidPage(leafletOutput("map"))
server <- function(input, output, session) {
rv <- reactiveValues()
output$map <- renderLeaflet({
leaflet() %>%
addProviderTiles(provider = providers$CartoDB.Positron) %>%
setView(lng = 1, lat = 45, zoom = 8) %>%
addPolygons(data = cities,layerId = ~NAME_2,label = ~NAME_2)
})
observeEvent(input$map_bounds,{
rv$center <- c(mean(input$map_bounds$north, input$map_bounds$south), mean(input$map_bounds$east, input$map_bounds$west))
# how can I detect on which polygon the center is ?
})
}
shinyApp(ui = ui, server = server)
library(leaflet)
library(shiny)
library(sf)
cities <- readRDS(file = "gadm36_FRA_2_sp.rds") %>%
st_as_sf()
ui <- fluidPage(leafletOutput("map"))
server <- function(input, output, session) {
rv <- reactiveValues()
output$map <- renderLeaflet({
leaflet() %>%
addProviderTiles(provider = providers$CartoDB.Positron) %>%
setView(lng = 1, lat = 45, zoom = 8) %>%
addPolygons(data = cities, layerId = ~NAME_2, label = ~NAME_2)
})
observeEvent(input$map_bounds, {
rv$center <- c(mean(input$map_bounds$north, input$map_bounds$south), mean(input$map_bounds$east,
input$map_bounds$west))
pnt <- st_point(c(rv$center[2], rv$center[1]))
rslt <- cities[which(st_intersects(pnt, cities, sparse = FALSE)),]$NAME_1
print(rslt)
})
}
shinyApp(ui = ui, server = server)
So I found a way to do it with the function sf::st_intersects
observeEvent(input$map_bounds,{
rv$center <- data.frame(x = mean(c(input$map_bounds$north, input$map_bounds$south)),
y = mean(c(input$map_bounds$east, input$map_bounds$west)))
res <- sf::st_as_sf(rv$center, coords=c("y","x"), crs=st_crs(cities$geometry))
intersection <- as.integer(st_intersects(res, cities$geometry))
print(if_else(is.na(intersection), '', cities$NAME_2[intersection]))
})

R shiny checkboxGroup to plot data on map

I am very new to shiny, and I have a question.
I have a simple dataset with observations (Number_Total) of species (Species), in a certain location (X,Y).
I would like to generate a map, that enables you to select the species in a dropdown menu. Shiny then shows you were the species occurs on the map.
I got pretty far (for my experience), but selecting species in the menu does not do anything...
ui <- (fluidPage(titlePanel("Species Checker"),
sidebarLayout(
sidebarPanel(
selectizeInput('species', 'Choose species',
choices = df$Species, multiple = TRUE)
),
mainPanel(
leafletOutput("CountryMap",
width = 1000, height = 500))
)
))
The server side
server <- function(input, output, session){
output$CountryMap <- renderLeaflet({
leaflet() %>% addTiles() %>%
setView(lng = 10, lat = 40, zoom = 5) %>%
addCircles(lng = df$Y, lat = df$X, weight = 10,
radius =sqrt(df$Number_Total)*15000, popup = df$Species)
})
observeEvent(input$species, {
if(input$species != "")
{
leafletProxy("CountryMap") %>% clearShapes()
index = which(df$Species == input$species)
leafletProxy("CountryMap")%>% addCircles(lng = df$X[index],
lat = df$Y[index],
weight = 1,
radius =sqrt(df$Number_Total[index])*30, popup = df$Species[index])
}
})
}
And finally plot it
shinyApp(ui = ui, server = server)
I know my code is probably messy, but again, I blaim my experience =)
I did not manage to get an example dataset in here right away, so here it comes as picture
This is the result of the above code (with slightly different data)
enter image description here
Here's what you need. I think you are skilled enough to understand this but comment if you have any questions.
server <- function(input, output, session) {
# map_data <- reactive({
# req(input$species)
# df[df$Species %in% input$species, ]
# })
output$CountryMap <- renderLeaflet({
leaflet() %>% addTiles() %>%
setView(lng = 10, lat = 40, zoom = 5)
})
map_proxy <- leafletProxy("CountryMap")
observe({
md <- df[df$Species %in% input$species, ]
map_proxy %>%
addCircles(lng = md$Y, lat = md$X, weight = 10,
radius = sqrt(md$Number_Total)*15000, popup = md$Species)
})
}

How to update shiny reactive only if threshold is crossed?

In the code below, the leaflet addCircles get drawn twice after a change in zoom. I think this double plotting occurs because the reactive to create a dataframe always updates with a change in zoom. However, I only want the reactive dataframe (race.dots.all.r) to update when a zoom threshold is crossed. Any ideas?
EDIT: I removed even more code to simplify and made it reproducible by adding a dropbox link to the data.
library(shiny)
library(leaflet)
library(dplyr)
load(url("https://www.dropbox.com/s/umhqvoqvbhlkrc6/shiny_app_seg_gap_stackoverflow.RData?dl=1"))
ui <- shinyUI(fluidPage(
leafletOutput("map"),
checkboxInput("togglewhite", "White", value = TRUE)
))
server <- shinyServer(function(input, output, session) {
per.person <- eventReactive(input$map_zoom,{
new_zoom <- 12
if (!is.null(input$map_zoom)) {
new_zoom <- input$map_zoom}
if ( new_zoom < 13 ) {
per.person <- "1000"
} else {
per.person <- "250"
}
return(per.person)
})
race.dots.all.r <- eventReactive(per.person(),{
race.dots.all <- race.dots.all[[per.person()]]
return(race.dots.all)
})
values <- reactiveValues(school = NULL)
output$map <- renderLeaflet({
leaflet(options = leafletOptions(preferCanvas = TRUE)) %>%
addProviderTiles("CartoDB") %>%
setView(lat=40.73771, lng=-74.18958, zoom = 8)
})
observeEvent(c(input$togglewhite, race.dots.all.r()), {
proxy <- leafletProxy('map')
proxy %>% clearGroup(group = "White")
if (input$togglewhite){
race.dots.all.selected.race <- dplyr::filter( race.dots.all.r(), group == "White")
proxy %>% addCircles(group = race.dots.all.selected.race$group,
race.dots.all.selected.race$lng,
race.dots.all.selected.race$lat)
}
},ignoreInit = TRUE)
}) # close server
shinyApp(ui, server)
If I understand your requirement correctly then here's one way to do it. I have set the zoom threshold at 0 for this example just so that it is easy to demo. You can change it to 4 for your app.
library(shiny)
library(leaflet)
shinyApp(
ui = fluidPage(
leafletOutput("map")
),
server = function(input, output, session) {
output$map <- renderLeaflet({
leaflet() %>%
addTiles()
})
map_proxy <- leafletProxy("map")
observeEvent(input$map_zoom, {
if(input$map_zoom > 0) {
map_proxy %>%
addCircleMarkers(lng = 74.0060, lat = 40.7128,
group = "high_zoom_in", radius = 50, color = "red")
} else {
map_proxy %>%
clearGroup("high_zoom_in")
}
})
}
)

R Leaflet GeoJSON Coloring

I am still working on this R Leaflet self project to learn and I'm trying to color in some Polygons in the Wake County area of Raleigh, NC. Below is the image of what I am trying to color.
https://imgur.com/a/xdvNLvM
Basically I am trying to get each of those polygons colored differently. I've tried addPolygons but I guess I didn't have correct Polygon data. I've looked at color binning but I seem to be out of ideas. Below is my code. I even tried to unnest the GeoJSON data and create a factor palette but that hasn't seemed to work.
library(shiny)
library(leaflet.extras)
library(geojsonio)
library(rgdal)
dataurl <- 'https://opendata.arcgis.com/datasets/f5c3b84a6fcc499d8f9ece78602258eb_0.geojson'
data <- geojson_read(dataurl, method = 'web', parse = FALSE, what = 'list')
wake <- readOGR(dataurl)
wake$zips <- factor(sample.int(39L, nrow(wake), TRUE))
#bikedata <- 'D:/bicycle-crash-data-chapel-hill-region.geojson'
#bike <- geojson_read(bikedata)
vtdata <- 'http://geodata.vermont.gov/datasets/4c206846699947429df59c8cb552ab5c_11.geojson'
vt <- geojson_read(vtdata)
factpal <- colorFactor(topo.colors(39), wake$zips)
ui <- shinyUI(
fluidPage(
leafletOutput("map", width = "100%", height = "900px")
)
)
server <- function(input, output) {
wakegeojson <- reactive({
data
})
#bikegeojson <- reactive({
# bike
#})
vtgeojson <- reactive({
vt
})
output$map <- renderLeaflet({
leaflet() %>%
addTiles() %>%
setView(-93.65, 42.0285, zoom = 4)
})
observe({
leafletProxy("map") %>%
addWMSTiles("http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi",
layers = "nexrad-n0r-900913",
options = WMSTileOptions(format = "image/png", transparent = TRUE),
attribution = "") %>%
addGeoJSON(wakegeojson(), weight = 3, fill = factpal) %>%
#addGeoJSON(bikegeojson()) %>%
addGeoJSON(vtgeojson(), fill = FALSE, color = "black")
})
}
app <- shinyApp(ui = ui, server = server)
runApp(app, launch.browser = TRUE)
I think I need to explore the addPolygons feature more but I'm not exactly sure how to do that or how to parse/unnest my GeoJSON data in order to accomplish filling in the Wake County Zipcodes with different colors. Any help is always appreciated. Thank you.
I would switch to sf. You can directly load the geojson and produce a Multipolygon and a Multilinestring object which will also read much faster than readOGR.
Then you can just put those objects in addPolygons and addPolylines.
The following example should work:
library(shiny)
library(leaflet.extras)
library(geojsonio)
library(rgdal)
library(sf)
dataurl <- 'https://opendata.arcgis.com/datasets/f5c3b84a6fcc499d8f9ece78602258eb_0.geojson'
wake <- st_read(dataurl)
wake$zips <- factor(sample.int(39L, nrow(wake), TRUE))
vtdata <- 'http://geodata.vermont.gov/datasets/4c206846699947429df59c8cb552ab5c_11.geojson'
vt <- st_read(vtdata)
factpal <- colorFactor(topo.colors(39), wake$zips)
ui <- shinyUI(
fluidPage(
leafletOutput("map", width = "100%", height = "900px")
)
)
server <- function(input, output) {
wakegeojson <- reactive({
wake
})
vtgeojson <- reactive({
vt
})
output$map <- renderLeaflet({
leaflet() %>%
addTiles() %>%
addPolygons(data=wakegeojson(), color=factpal(wake$zips)) %>%
addPolylines(data=vtgeojson(), color="red")
})
}
app <- shinyApp(ui = ui, server = server)
runApp(app, launch.browser = TRUE)

r shiny leaflet - creating popups one by one 'on demand'

Inserting popups in addCirclemarkers caused lengthy calculation time for data of thousands of points to be mapped. I am assuming all popups have to be calculated before showing the map.
I searched online for a way to only add/create the popup if a point/circle/marker is clicked. Currently, I am at the below code. If you run this code, you will see that the popup is created, but the string to extract from the data is not shown. What am I doing wrong?
library(shiny)
library(leaflet)
library(htmltools)
library(sp)
data <- data.frame(
"name"=c("Place 1","Place 2","Place 3"),
"lat"=c(50,51,52),
"lng"=c(3,4,5), stringsAsFactors = FALSE)
ui = fluidPage(
fluidRow(column(8, offset = 2, leafletOutput("map", width = "100%", height = "650px")))
)
server = function(input, output, session) {
pts <- reactive({
pts <- data
coordinates(pts) <- ~lng+lat
pts
})
output$map <- renderLeaflet({
leaflet(pts()) %>%
addTiles(group="OSM") %>%
addCircleMarkers()
})
observeEvent(input$map_marker_click, {
leafletProxy("map") %>% clearPopups()
event <- input$map_marker_click
if (is.null(event))
return()
isolate({
pts2 <- pts()
sgh <- pts2[row.names(pts2) == event$id,]
# sgh <- pts2[pts2$name == event$id,]
content <- htmlEscape(paste("This place is",as.character(sgh$name)))
leafletProxy("map") %>% addPopups(event$lng, event$lat, content, layerId = event$id)
})
})
}
shinyApp(ui = ui, server = server, options = list(launch.browser=TRUE))
With you code event$id is NULL, so the sgh <- pts2[row.names(pts2) == event$id,] line return NULL as well.
You have to add the layerId to the CircleMarkers (and is not necessary to add it to the Popup.
This also let access it wothout needing to 'merge' it with the original data:
output$map <- renderLeaflet({
leaflet(pts()) %>%
addTiles(group="OSM") %>%
addCircleMarkers(layerId = ~name)
})
observeEvent(input$map_marker_click, {
leafletProxy("map") %>%
clearPopups()
event <- input$map_marker_click
if (is.null(event))
return()
isolate({
content <- htmlEscape(paste("This place is", event$id))
leafletProxy("map") %>%
addPopups(event$lng, event$lat, content)
})
})

Resources