Created
March 15, 2019 20:01
-
-
Save ericrobskyhuntley/9a5decf7f1a8de4ea0a46998ee8cd655 to your computer and use it in GitHub Desktop.
R Shiny with Mapdeck
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# 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 file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# 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 file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# 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