Skip to content

Instantly share code, notes, and snippets.

@aoles
Last active April 1, 2021 23:53
Show Gist options
  • Save aoles/3ced6b5c073bcb1e8c7e599e4b9d376d to your computer and use it in GitHub Desktop.
Save aoles/3ced6b5c073bcb1e8c7e599e4b9d376d to your computer and use it in GitHub Desktop.
ORS isochrones demo Shiny app https://aoles.shinyapps.io/isochrones
library(shiny)
library(leaflet)
library(openrouteservice)
ui <- bootstrapPage(
includeScript("busy_indicator.js"),
includeScript("lazy_sliders.js"),
includeCSS("style.css"),
leafletOutput("map", width = "100%", height = "100%"),
absolutePanel(top = 10, right = 10, id="controls",
selectInput("profile", "Profile", c(driving = "driving-car",
cycling = "cycling-regular",
walking = "foot-walking")),
sliderInput("range", "Range [minutes]", 1, 60, 20, 1),
sliderInput("interval", "Interval [minutes]", 1, 60, 20, 1)
),
div(id = 'loader')
)
server <- function(input, output, session) {
observe({
updateSliderInput(session, "interval", max = input$range)
})
point <- eventReactive(input$map_click, {
data.frame(lng = input$map_click$lng, lat = input$map_click$lat)
})
output$map <- renderLeaflet({
leaflet() %>%
addTiles() %>%
setView(8.675581, 49.418579, zoom = 9)
})
observeEvent(point(), {
leafletProxy("map") %>%
clearMarkers() %>%
addMarkers(point()$lng, point()$lat)
})
isochrones <- reactive({
req(point())
x <- ors_isochrones(point(),
profile = input$profile,
range = input$range * 60,
interval = input$interval * 60)
# set isochrone colors
ranges <- sapply(x$features, function(y) y$properties$value)
pal <- setNames(heat.colors(length(ranges)), ranges)
x$features <- lapply(1:length(x$features), function(i) {
feature <- x$features[[i]]
range <- feature$properties$value
## set style
col <- unname(pal[as.character(range)])
feature$properties$style <- list(color = col, fillColor = col, fillOpacity=0.5)
## restrict polygon to current level only
if (range > ranges[1])
feature$geometry$coordinates <-
c(feature$geometry$coordinates, x$features[[i-1]]$geometry$coordinates)
feature
})
x
})
observe({
leafletProxy("map") %>%
clearGeoJSON() %>%
addGeoJSON(isochrones())
})
}
shinyApp(ui, server)
$(function() {
$(document).on({
'shiny:busy': function(event) {
$('#loader').show();
},
'shiny:idle': function(event) {
$('#loader').hide();
}
});
});
/* https://stackoverflow.com/a/47726369/2792099 */
(function() {
var sliderInputBinding = Shiny.inputBindings.bindingNames['shiny.sliderInput'].binding;
var lazySliderInputBinding = $.extend({}, sliderInputBinding, {
subscribe: function(el, callback) {
var $el = $(el);
var slider = $el.data('ionRangeSlider');
var handleChange = function() {
if (!inputsInitialized) return;
callback(!$el.data('immediate') && !$el.data('animating'));
};
slider.update({
onUpdate: handleChange,
onFinish: handleChange
});
},
unsubscribe: function(el, callback) {
var slider = $(el).data('ionRangeSlider');
slider.update({
onUpdate: null,
onFinish: null
});
}
});
Shiny.inputBindings.register(lazySliderInputBinding, 'shiny.lazySliderInput');
var inputsInitialized = false;
$(document).one('shiny:connected', function() {
inputsInitialized = true;
});
})();
html, body {
width: 100%;
height: 100%;
}
#controls {
background: rgba(255, 255, 255, 0.8);
padding: 10px;
border-radius: 5px;
}
/* Center the loader */
#loader {
position: absolute;
left: 50%;
top: 50%;
z-index: 1;
width: 150px;
height: 150px;
margin: -75px 0 0 -75px;
border: 16px solid #f3f3f3;
border-radius: 50%;
border-top: 16px solid #3498db;
-webkit-animation: spin 2s linear infinite;
animation: spin 2s linear infinite;
}
@-webkit-keyframes spin {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment