Skip to content

Instantly share code, notes, and snippets.

@ericrobskyhuntley
Created March 15, 2019 20:01
Show Gist options
  • Save ericrobskyhuntley/9a5decf7f1a8de4ea0a46998ee8cd655 to your computer and use it in GitHub Desktop.
Save ericrobskyhuntley/9a5decf7f1a8de4ea0a46998ee8cd655 to your computer and use it in GitHub Desktop.
R Shiny with Mapdeck
#
# In this global.R field, we create all objects and run all code
# that we only want to run once when we start our app and that we
# want accessible to all parts of our app. This includes database
# queries (in this case at least), API keys, etc.
#
# First, we load and attach packages. A package must be installed before
# loading; for example, to install RPostgreSQL, we simply type
# install.packages("RPostgreSQL").
#
# RPostgreSQL allows us to connect to our database.
library(RPostgreSQL)
# sf (Simple Features) is our spatial package of choice. It uses Simple
# Features lists to store geometries. SF is a standard storage model
# maintained by the Open Geospatial Consortium (OGS) and the
# International Organization for Standardization (ISO), so it's like
# reaaaal offish. Incidentally, PostGIS also implements Simple Features,
# so SF plays very nicely with SF.
library(sf)
# Shiny is a web application Framework for R that provides both
# server-side scripting and a browser user interface API.
library(shiny)
# Mapdeck is an R interface for MapboxGL and Deck.gl; this is the library
# that we'll be using to build our map.
library(mapdeck)
# Here, we create and initialize a PostgreSQL client.
# We could also write driver <- dbDriver('PostgreSQL')
# I discovered digging into the docs that dbDriver methods are deprecated,
# meaning that users are encouraged to instead use database-specific
# functions to initialize drivers.
driver <- PostgreSQL()
# Here, we initiate a connection to our database using parameters that
# should by now look pretty familiar!
con <- dbConnect(driver,
dbname='class521_s19',
host='cronpgsql.mit.edu',
user='',
password='')
# Storing a SQL query as a character string.
evic_rate_q <- "SELECT
t.geoid10,
t.geom,
e.eviction_rate,
e.year
FROM
somerville.gb_bg_2010 AS t
INNER JOIN somerville.evic_bg AS e ON t.geoid10 = e.geoid10;"
# st_read comes from the SF package, and allows us to read a file or
# query a database whose results are stored as a data frame with a
# simple feature column. The ST_ prefix should look familiar from our
# PostGIS work! It stands for Spatial Type.
evic_rate <- st_read(dsn = con, query = evic_rate_q)
# We now disconnect our database instance to ensure that we don't open
# multiple unnecessary connections to our database (this is especially
# relevant in development, as you'll likely be stopping and starting
# your application frequently).
dbDisconnect(con)
# This is your Mapbox API key (or 'token'), available on the dashboard of your account.
mb_token = ''
#
# This is the server logic of our Mapdeck web application.
#
# Things that we expect to respond to user and dynamically update
# go here. Our map will be responding to user inputs.
#
#
# The shinyServer function wraps our server-side code. Note
# that it uses both input and output objects. These are inputs
# to the code in our server.R file (e.g., the value selected
# from a dropdown menu or using a slider) and outputs from our
# dynamic objects (e.g., our rendered map).
shinyServer(function(input, output) {
# We want to dynamically filter data based on user input.
# So! We create a reactive expression, which simply wraps a normal
# expression, allowing it to change over time. When a reactive
# value changes, other things that depend on it (for example, our
# observe expression below) will be reevaluated.
filtered_data <- reactive({
# Filter by date (note our use of input$date, which matches the inputID on sliderInput in our ui.R)
# and exclude outliers (eviction rates greater than 6%).
evic_rate[evic_rate$year==input$date & evic_rate$eviction_rate <= 6 ,]
})
# Render a Mapdeck map and send it to the output of our server logic.
# This is subsequently rendered in the mapdeckOutput element on our
# page. Again, see ui.R
output$map <- renderMapdeck({
# Note that our mb_token value is read in from global.R
mapdeck(token = mb_token,
style = mapdeck_style("dark"),
# Angle camera down.
pitch = 55,
# 55 degrees off of north.
bearing = -55,
# Center on Somerville.
location = c(-71.099615, 42.387760),
zoom = 10)
})
# Create a reactive observer.
observe({
# Here, we establish a dependency on filtered_data(), which
# means that every time we change our input year this observer
# will be reevaluated.
data <- filtered_data()
# Base an elevation on ourupdated data.
data$elev <- data$eviction_rate * 800
# Create a color ramp between fully transparent white and
# fully opaque magenta. add_polygon expects values between 1
# and 255, so we scale the range of values. 1:256 is non-inclusive
# which is why we add 1 to the right end of the range.
m <- colorRamp(c(rgb(1,1,1, 0.0), rgb(1,0,1, 1)), alpha = TRUE)( (1:256) / 256)
# update our map...
mapdeck_update("map") %>%
# adding polygons based on filtered data.
add_polygon(data,
fill_colour = "eviction_rate",
update_view = FALSE,
elevation = "elev",
palette = m,
# opacity must be between 1 and 255, so this is
# 75% scaled.
fill_opacity = 0.75 * 255,
# transitions! Neat.
transitions = list(
fill_colour = 1500,
elevation = 1500
))
})
})
#
# This is the user-interface definition of a Shiny web application.
#
# This is where you place elements that appear in your browser. If
# a plot, input, or map is meant to be visible to the user, you need
# to create a space here.
# The shinyUI function wraps our user interface.
shinyUI(
# Create a bootstrap page. Bootstrap is a library of ready-made
# web site components, which also makes it exceedingly simple to
# lay out a website.
bootstrapPage(
# Create a style that is applied to our app's body. Here, we want
# it to occupy 100% of a browser's horizontal and vertical space.
tags$style(type = "text/css", "html, body {width:100%;height:100%}"),
# This mapdeckOutput creates a place for our mapdeck map. Note that
# the first argument is "map" - this means that we'll need to send
# the renderMapdeck to a variable called output$map in our server.R
# function.
mapdeckOutput("map", width = "100%", height = "100%"),
# Still within our bootstrapPage, we create an absolutePanel; this
# is a shiny panel with absolute dimensions; i.e., these elements
# render based on their absolute position on the page, not based on
# their location relative to other elements on the page (our map
# for example). We position it 10 pixels from the top, 10 pixels
# from the right.
absolutePanel(top = 10, right = 10,
# We create a sliderInput with the inputID "date", meaning that
# its values are accessible within server.R using input$date.
# We set a minimum value, a maximum value, a default value, and
# what should be used as a thousands separator (nuttin').
sliderInput("date",
label = "Date of interest:",
min = 2008, max = 2016, value = 2012,
sep=''
)
)
)
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment