Creating an interactive 3D map of tree density in Portland, Oregon

Blog Image

Portland is one of my favorite US cities. I love its unique vibe, food cart pod scene, alternative transportation options, and many parks. Before giving a promotional presentation for a soon-to-be-taught Geography Field Seminar course to Oregon, I stumbled upon the pdxTrees R package and at the same time had been experimenting with 3D visualizations through deck.gl. I decided to put these two together for my presentation and was satisfied at how quickly and easily I could produce an interactive of map 200,000+ trees.

There are several R packages available for 3D visualization, and here I use the deckgl package which connects to Uber’s JavaScript library deck.gl. The pdxTrees package is super simple and contains two functions for retrieving data on tree locations (among other variables) in the city of Portland: one for trees within the 174 public parks (get_pdxTrees_parks) and another along streets of all 96 neighborhoods (get_pdxTrees_streets). The approach demonstrated here would work for any point geometry data, however, as long as x and y are appropriately specified.

library(pdxTrees)
library(dplyr)
library(deckgl)

I decided to visualize the density of street trees, and the function below simply reads a .csv currently hosted on github:

df <- get_pdxTrees_streets()

There’s an impressive number of tree locations in here (over 200k) which were collected as a part of the Urban Forestry Tree Inventory Project.

nrow(df)
## [1] 218602

Since there are that many rows however (and 23 columns), we can improve performance by simplifying data a bit:

df_small <- df %>%
  select(Longitude, Latitude)

Next, we’ll create list of properties that will be passed to the add_grid_layer function later on. These include options for how deck.gl will handle visual style.

props <- list(
  extruded = TRUE,
  cellSize = 200,
  elevationScale = 4,
  getPosition = ~Longitude + Latitude
)

The extruded option simply makes the visualization three dimensional. The cell size is the size of each grid cell in meters. The other options are self-explanatory.

Then, the deckgl function initializes the deck.gl instance with some options that are common to all visualizations (e.g., zoom, pitch, bearing, and lng/lat). The add_grid_layer function is where our data is actually plotted with a color range and set of properties (specified above).

deckgl(zoom = 11,
       pitch = 75,
       bearing = -30,
       longitude = -122.6138,
       latitude = 45.5163,
       width = "100%") %>%
  add_basemap() %>%
  add_grid_layer(
    data = df_small,
    colorRange = RColorBrewer::brewer.pal(8, "Greens"),
    properties = props
  )