8.5 Mapping applications
The interactive web maps demonstrated in Section 8.4 can go far.Careful selection of layers to display, base-maps and pop-ups can be used to communicate the main results of many projects involving geocomputation.But the web mapping approach to interactivity has limitations:
- Although the map is interactive in terms of panning, zooming and clicking, the code is static, meaning the user interface is fixed.
- All map content is generally static in a web map, meaning that web maps cannot scale to handle large datasets easily.
- Additional layers of interactivity, such a graphs showing relationships between variables and ‘dashboards’ are difficult to create using the web-mapping approach.
Overcoming these limitations involves going beyond static web mapping and towards geospatial frameworks and map servers.Products in this field include GeoDjango (which extends the Django web framework and is written in Python), MapGuide (a framework for developing web applications, largely written in C++) and GeoServer (a mature and powerful map server written in Java).Each of these (particularly GeoServer) is scalable, enabling maps to be served to thousands of people daily — assuming there is sufficient public interest in your maps!The bad news is that such server-side solutions require much skilled developer time to set-up and maintain, often involving teams of people with roles such as a dedicated geospatial database administrator (DBA).
The good news is that web mapping applications can now be rapidly created using shiny, a package for converting R code into interactive web applications.This is thanks to its support for interactive maps via functions such as renderLeaflet()
, documented on the Shiny integration section of RStudio’s leaflet website.This section gives some context, teaches the basics of shiny from a web mapping perspective and culminates in a full-screen mapping application in less than 100 lines of code.
The way shiny works is well documented at shiny.rstudio.com.The two key elements of a shiny app reflect the duality common to most web application development: ‘front end’ (the bit the user sees) and ‘back end’ code.In shiny apps, these elements are typically created in objects named ui
and server
within an R script named app.R
, which lives in an ‘app folder’.This allows web mapping applications to be represented in a single file, such as the coffeeApp/app.R
file in the book’s GitHub repo.
In shiny apps these are often split into ui.R
(short for user interface) and server.R
files, naming conventions used by shiny-server
, a server-side Linux application for serving shiny apps on public-facing websites.shiny-server
also serves apps defined by a single app.R
file in an ‘app folder’.Learn more at: https://github.com/rstudio/shiny-server.
Before considering large apps, it is worth seeing a minimal example, named ‘lifeApp’, in action.39The code below defines and launches — with the command shinyApp()
— a lifeApp, which provides an interactive slider allowing users to make countries appear with progressively lower levels of life expectancy (see Figure 8.24):
library(shiny) # for shiny apps
library(leaflet) # renderLeaflet function
library(spData) # loads the world dataset
ui = fluidPage(
sliderInput(inputId = "life", "Life expectancy", 49, 84, value = 80),
leafletOutput(outputId = "map")
)
server = function(input, output) {
output$map = renderLeaflet({
leaflet() %>% addProviderTiles("OpenStreetMap.BlackAndWhite") %>%
addPolygons(data = world[world$lifeExp < input$life, ])})
}
shinyApp(ui, server)
Figure 8.24: Screenshot showing minimal example of a web mapping application created with shiny.
The user interface (ui
) of lifeApp is created by fluidPage()
.This contains input and output ‘widgets’ — in this case, a sliderInput()
(many other *Input()
functions are available) and a leafletOutput()
.These are arranged row-wise by default, explaining why the slider interface is placed directly above the map in Figure 8.24 (see ?column
for adding content column-wise).
The server side (server
) is a function with input
and output
arguments.output
is a list of objects containing elements generated by render*()
function — renderLeaflet()
which in this example generates output$map
.Input elements such as input$life
referred to in the server must relate to elements that exist in the ui
— defined by inputId = "life"
in the code above.The function shinyApp()
combines both the ui
and server
elements and serves the results interactively via a new R process.When you move the slider in the map shown in Figure 8.24, you are actually causing R code to re-run, although this is hidden from view in the user interface.
Building on this basic example and knowing where to find help (see ?shiny
), the best way forward now may be to stop reading and start programming!The recommended next step is to open the previously mentioned coffeeApp/app.R
script in an IDE of choice, modify it and re-run it repeatedly.The example contains some of the components of a web mapping application implemented in shiny and should ‘shine’ a light on how they behave.
The coffeeApp/app.R
script contains shiny functions that go beyond those demonstrated in the simple ‘lifeApp’ example.These include reactive()
and observe()
(for creating outputs that respond to the user interface — see ?reactive
) and leafletProxy()
(for modifying a leaflet
object that has already been created).Such elements are critical to the creation of web mapping applications implemented in shiny.A range of ‘events’ can be programmed including advanced functionality such as drawing new layers or subsetting data, as described in the shiny section of RStudio’s leafletwebsite.
There are a number of ways to run a shiny app.For RStudio users, the simplest way is probably to click on the ‘Run App’ button located in the top right of the source pane when an app.R
, ui.R
or server.R
script is open.shiny apps can also be initiated by using runApp()
with the first argument being the folder containing the app code and data: runApp("coffeeApp")
in this case (which assumes a folder named coffeeApp
containing the app.R
script is in your working directory).You can also launch apps from a Unix command line with the command Rscript -e 'shiny::runApp("coffeeApp")'
.
Experimenting with apps such as coffeeApp
will build not only your knowledge of web mapping applications in R, but also your practical skills.Changing the contents of setView()
, for example, will change the starting bounding box that the user sees when the app is initiated.Such experimentation should not be done at random, but with reference to relevant documentation, starting with ?shiny
, and motivated by a desire to solve problems such as those posed in the exercises.
shiny used in this way can make prototyping mapping applications faster and more accessible than ever before (deploying shiny apps is a separate topic beyond the scope of this chapter).Even if your applications are eventually deployed using different technologies, shiny undoubtedly allows web mapping applications to be developed in relatively few lines of code (60 in the case of coffeeApp).That does not stop shiny apps getting rather large.The Propensity to Cycle Tool (PCT) hosted at pct.bike, for example, is a national mapping tool funded by the UK’s Department for Transport.The PCT is used by dozens of people each day and has multiple interactive elements based on more than 1000 lines of code(Lovelace et al. 2017).
While such apps undoubtedly take time and effort to develop, shiny provides a framework for reproducible prototyping that should aid the development process.One potential problem with the ease of developing prototypes with shiny is the temptation to start programming too early, before the purpose of the mapping application has been envisioned in detail.For that reason, despite advocating shiny, we recommend starting with the longer established technology of a pen and paper as the first stage for interactive mapping projects.This way your prototype web applications should be limited not by technical considerations, but by your motivations and imagination.