Reputation: 2026
I have a shiny app which involves drawing a large number of lines on a map. I would like to use a spinner to show the user that rendering is underway. Most shiny approaches don't work because they only show the spinner while the data is being sent to leaflet, not when leaflet is rendering. The Leaflet.Spin plugin looks promising but I have been struggling to get it to work. The examples I have been following are
https://gist.github.com/jcheng5/c084a59717f18e947a17955007dc5f92
leaflet plugin and leafletProxy with polylineDecorator as Example
How do I get the js events to fire properly and show Leaflet.Spin when the lines (circles in this example) are rendering?
library(shiny)
library(leaflet)
library(htmltools) # for htmlDependency
library(htmlwidgets) # for onRender
# https://gist.github.com/jcheng5/c084a59717f18e947a17955007dc5f92
# https://stackoverflow.com/questions/52846472/leaflet-plugin-and-leafletproxy-with-polylinedecorator-as-example
spinPlugin <- htmlDependency(
"spin.js",
"2.3.2",
src = c(href = "https://cdnjs.cloudflare.com/ajax/libs/spin.js/2.3.2"),
script = "spin.min.js") # there's no spin.css
leafletspinPlugin <- htmlDependency(
"Leaflet.Spin",
"1.1.2",
src = c(href = "https://cdnjs.cloudflare.com/ajax/libs/Leaflet.Spin/1.1.2"),
script = "leaflet.spin.min.js")
registerPlugin <- function(map, plugin) {
map$dependencies <- c(map$dependencies, list(plugin))
map
}
# Note: Ctrl-Shift-J opens the javascript console in the browser
spin_event <- "function(el, x) {
console.log('spin event added');
var mymap = this;
mymap.on('layerremove', function(e) {
console.log('layerremove fired');
mymap.spin(true);
});
mymap.on('layeradd', function(e) {
console.log('layeradd fired');
mymap.spin(false);
});
}"
dlat <- 1 / 111000 * 100 # degrees per metre
ui <- fluidRow(
tags$h2("Using Leaflet.Spin in Shiny"),
actionButton("plotbutton", label = "Show Spinner While Adding Markers"),
leafletOutput("map")
)
server <- function(input, output, session) {
output$map <- renderLeaflet({
cat("renderLeaflet\n")
leaflet() %>%
addTiles() %>%
setView(175.322, -37.789, zoom = 17) %>%
registerPlugin(spinPlugin) %>%
registerPlugin(leafletspinPlugin) %>%
onRender(spin_event) %>%
clearShapes() %>% # initialise spinner
addCircles(
lng = 175.322,
lat = -37.789,
radius = 0,
opacity = 0
)
})
observeEvent(input$plotbutton, {
cat("input$plotbutton\n")
n <- ceiling(runif(1) * 10000)
leafletProxy("map") %>%
clearShapes() %>%
addCircles(
lng = 175.322 + (runif(n) * 2 - 1) * dlat * 6,
lat = -37.789 + (runif(n) * 2 - 1) * dlat * 1.5,
radius = dlat * runif(n) * dlat
)
})
}
shinyApp(ui = ui, server = server)
Upvotes: 3
Views: 1044
Reputation: 462
One year late, but I was looking for a way to have a busy spinner for leaflet.
I realized that your original code would create x number of spin instances on clearShapes()
. If the following addCircles()
has less than x number of circles, there would be spin instances left running.
My solution is to use a dummy layer to watch for from Javascript. In the example below, a circle with layerId = 'spinnerMarker'
. To start the leafletproxy
update, that layer is removed with removeShape(layerId = 'spinnerMarker')
which triggers JS layerremove
. The data circles are then added. The update is ended by adding a circle with layerId = 'spinnerMarker'
to trigger the JS layeradd
.
In JS, the layer being added or removed is checked with e.layer.options.layerId == 'spinnerMarker'
to run mymap.spin()
.
This way there is only one spin instance running.
library(shiny)
library(leaflet)
library(htmltools) # for htmlDependency
library(htmlwidgets) # for onRender
# https://gist.github.com/jcheng5/c084a59717f18e947a17955007dc5f92
# https://stackoverflow.com/questions/52846472/leaflet-plugin-and-leafletproxy-with-polylinedecorator-as-example
spinPlugin <- htmlDependency(
"spin.js",
"2.3.2",
src = c(href = "https://cdnjs.cloudflare.com/ajax/libs/spin.js/2.3.2"),
script = "spin.min.js") # there's no spin.css
leafletspinPlugin <- htmlDependency(
"Leaflet.Spin",
"1.1.2",
src = c(href = "https://cdnjs.cloudflare.com/ajax/libs/Leaflet.Spin/1.1.2"),
script = "leaflet.spin.min.js")
registerPlugin <- function(map, plugin) {
map$dependencies <- c(map$dependencies, list(plugin))
map
}
# Note: Ctrl-Shift-J opens the javascript console in the browser
spin_event <- "function(el, x) {
console.log('spin event added');
var mymap = this;
mymap.on('layerremove', function(e) {
console.log('layerremove fired');
if (e.layer.options.layerId == 'spinnerMarker') {
console.log(e.layer.options.layerId);
mymap.spin(true);
}
});
mymap.on('layeradd', function(e) {
console.log('layeradd fired');
if (e.layer.options.layerId == 'spinnerMarker') {
console.log(e.layer.options.layerId);
mymap.spin(false);
}
});
}"
dlat <- 1 / 111000 * 100 # degrees per metre
ui <- fluidRow(
tags$h2("Using Leaflet.Spin in Shiny"),
actionButton("plotbutton", label = "Show Spinner While Adding Markers"),
leafletOutput("map")
)
server <- function(input, output, session) {
output$map <- renderLeaflet({
cat("renderLeaflet\n")
leaflet() %>%
addTiles() %>%
setView(175.322, -37.789, zoom = 17) %>%
registerPlugin(spinPlugin) %>%
registerPlugin(leafletspinPlugin) %>%
onRender(spin_event) %>%
clearShapes() %>% # initialise spinner
addCircles( # invisible placeholder
lng = 175.322,
lat = -37.789,
radius = 0,
opacity = 0,
layerId = 'spinnerMarker' # identifier, can be found in js: e.layer.options.layerId
)
})
observeEvent(input$plotbutton, {
cat("input$plotbutton\n")
n <- ceiling(runif(1) * 10000)
leafletProxy("map") %>%
removeShape(layerId = 'spinnerMarker') %>% # this triggers mymap.spin(true)
clearShapes() %>%
addCircles(
lng = 175.322 + (runif(n) * 2 - 1) * dlat * 6,
lat = -37.789 + (runif(n) * 2 - 1) * dlat * 1.5,
radius = dlat * runif(n) * dlat
) %>%
addCircles( # invisible placeholder to trigger the mymap.spin(false)
lng = 175.322,
lat = -37.789,
radius = 0,
opacity = 0,
layerId = 'spinnerMarker' # identifier, can be found in js: e.layer.options.layerId
)
})
}
shinyApp(ui = ui, server = server)
Upvotes: 5
Reputation: 84529
The URLs you provide are not valid. Try
spinPlugin <- htmlDependency(
"spin.js",
"4.1.0",
src = c(href = "https://cdnjs.cloudflare.com/ajax/libs/spin.js/2.3.2"),
script = "spin.min.js") # there's no spin.css
leafletspinPlugin <- htmlDependency(
"Leaflet.Spin",
"1.1.2",
src = c(href = "https://cdnjs.cloudflare.com/ajax/libs/Leaflet.Spin/1.1.2"),
script = "leaflet.spin.min.js")
Upvotes: 2