Examination of Building Permit data for Seattle, WA for 2023

Introduction

The purpose of the following analysis was to explore some of the characteristics of residential construction permits for the City of Seattle that have been issued so far in 2023. This initial overview was done to find some key factors related to location and type of permits issued, the number of new units to be added, along with the associated permit value of the projects. There were four different maps created, each looking at the data in a different manner.

Load packages

library(sf)  
library(tidyverse)
library(rgdal)        
library(scales)       
library(RColorBrewer) 
library(units)
library(cowplot)
library(here)
library(leaflet)
library(leafem)
library(leafpop)

Data Preparation

The first steps taken were related to data acquisition and preparation. The data set I used contains data for residential construction permits since 2010, that can be found through the Seattle GeoData portal as Built Units Since 2010. In addition to the associated coordinates and addresses for each permit, there are numerous attributes related to property type, units to be added or demolished, permit value, neighborhood, and several others. After obtaining the CSV files, I selected only permits issued in 2013 with a net addition of units. I also only selected a few variables to be used for further analysis and renamed some columns. Once complete, the coordinate variables were used to create a simple feature object. The following steps outline the process.

Import data
permits <- read_csv(here("Data","Seattle_Permits.csv")) %>% # Read in file
  drop_na(LONGITUDE) %>% # Remove rows with N/A for coordinates
  filter(YEAR_ISSUED >= 2023 & NET_UNITS >0) %>% # Only select permits for current year with net new units added
  select(OBJECTID, LOT_SIZE, PRMT_NR,NET_UNITS,VALUE,TYPE_OCC,YEAR_ISSUED,CRA,NEIGHBORHOOD,LONGITUDE,LATITUDE) %>% # Only retain relevant columns
  rename("Permit" = PRMT_NR, "New_Units" = NET_UNITS,"Unit_Type" = TYPE_OCC, "Neigborhood" = CRA, "District" = NEIGHBORHOOD, "value" = VALUE) # Rename some columns
glimpse(permits)
## Rows: 446
## Columns: 11
## $ OBJECTID    <dbl> 5150, 5168, 5331, 5369, 5378, 5385, 5386, 5389, 5390, 5487…
## $ LOT_SIZE    <dbl> 6924, 7333, 51124, 7571, 13348, 3436, 3436, 3416, 3416, 50…
## $ Permit      <dbl> 6930747, 6888945, 6857761, 6786325, 6735916, 6884794, 6884…
## $ New_Units   <dbl> 1, 1, 238, 1, 89, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1…
## $ value       <dbl> 235100, 761427, 47400135, 193281, 11285988, 447194, 447194…
## $ Unit_Type   <chr> "DADU", "ADU", "MIXED", "SF", "MIXED", "SF", "ADU", "SF", …
## $ YEAR_ISSUED <dbl> 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023…
## $ Neigborhood <chr> "Madison Park", "Cedar Park/Meadowbrook", "Ballard", "Nort…
## $ District    <chr> "East", "North", "Ballard", "Ballard", "North", "Central",…
## $ LONGITUDE   <dbl> -122.2906, -122.2778, -122.3871, -122.3603, -122.3211, -12…
## $ LATITUDE    <dbl> 47.62869, 47.70718, 47.66884, 47.70810, 47.71032, 47.61201…

Create sf object

permit_pts <- st_as_sf(permits, coords = c("LONGITUDE","LATITUDE"), crs = 3690) # Convert to sf 

Get unit types

unit_type <- permits$Unit_Type # Select Unit type columns
types <- as.data.frame(unique(unit_type)) # Obtain list of different unit types
types # Display results
##   unique(unit_type)
## 1              DADU
## 2               ADU
## 3             MIXED
## 4                SF
## 5                MF
color_pal <- colorFactor(c("lightgreen","darkgreen","darkblue","orange","lightblue"),
                         domain = c("DADU","ADU","MIXED","SF","MF")) # Create color palette to assign colors based on unit type

Results

Map One - Location of Permits with popup table

The first map shown in Figure 1 below provides the location of each permit in the data set, along with a popup table containing related attribute information.

leaflet(data = permit_pts) %>%
  setView(lng = -122.335167, lat = 47.608013, zoom = 11) %>% # Center on city center
  addProviderTiles(providers$CartoDB.DarkMatter) %>% # Add basemap
  addProviderTiles(providers$Stamen.TonerLines, options = providerTileOptions(opacity = .20)) %>% # Add roads as separate layer 
  addCircleMarkers(color = "darkred", fillOpacity = .75, stroke = F,
                   popup = leafpop::popupTable(st_drop_geometry(permit_pts[,2:9]),feature.id = F,
                                               row.numbers = F), radius = 4) %>% # Add markers with popup table
  addScaleBar(position = "bottomright") %>% # Add scale bar
  addMiniMap() # Add mini-map

Figure 1: Location of constuction permits issued in 2023 with popup tables

Map Two - Permit location with unit types

The second map shows the location of issued permits with colors based on unit type and can be seen in Figure 2. Green colors are for Accessory Dwelling Units (ADUs), blue colors for single-family (sf) and multi-family (mf) residences, and orange for mixed-use (mixed). The location of the different unit types is more easily observed on this map. ADUs appear to be the most prominent unit type for which permits have been obtained. Multi-family permits also seem to be prominent, along with mixed-use permits. These unit types appear to be found predominantly near major roadways. There are also single-family permits scattered throughout.

pop <- paste0(permit_pts$New_Units," New Units Added") # popup message with associated value for number of units
leaflet() %>%
  setView(lng = -122.335167, lat = 47.608013, zoom = 11) %>% # Center on city center
  addProviderTiles(providers$CartoDB.Voyager) %>% # Add basemap
  addProviderTiles(providers$Stamen.TonerLines, options = providerTileOptions(opacity = .20)) %>% # Add roads as separate layer
  addCircleMarkers(data = permit_pts, color = ~color_pal(Unit_Type),label = ~Unit_Type, popup = pop, radius = 5) %>% # Add markers that are color coded based on unit type with popup displaying unit count
  addScaleBar(position = "bottomright") %>%  # Add scale bar
  addLegend("bottomleft",
            colors = c("lightgreen","darkgreen","orange","lightblue","darkblue"),
            labels =c("ADU","DADU","MIXED","SF","MF"),
            title = "Unit Type") %>% # Add legend that displays unit type and corresponding color
  addMiniMap() # Add mini-map

Figure 2: Location of permits issued in 2023, color coded based on unit type

Map Three - Permits scaled by net units added

Figure 3 scales the symbols based on new units added. Mixed-use permits are responsible for the greatest number of new units added, at least on a per permit basis, with a multi-family permit also adding a substantial number of new units. These properties are also found predominantly in two main locations, in the northwest section of the City, and the southeast section.

leaflet() %>%
  setView(lng = -122.335167, lat = 47.608013, zoom = 11) %>% # Center on city center
  addProviderTiles(providers$CartoDB) %>% # Add basemap
  addProviderTiles(providers$Stamen.TonerLines, options = providerTileOptions(opacity = .20)) %>% # Add roads as separate layer
  addCircles(data = permit_pts, fillColor = ~color_pal(Unit_Type),
             color = NA, fillOpacity = 0.75, radius = ~New_Units*5, label = ~Unit_Type, popup = pop) %>% # Add circles that are scaled based on new units added with popup displaying new units added
  addScaleBar(position = "bottomright") %>%  # Add scale bar
  addLegend("bottomleft",
            colors = c("lightgreen","darkgreen","orange","lightblue","darkblue"),
            labels =c("ADU","DADU","MIXED","SF","MF"),
            title = "Unit Type") %>% # Add legend that displays unit type and corresponding color
  addMiniMap() # Add mini-map

Figure 3: Location of permits issued in 2023 with symbols scaled based on number of new units per permit

Map Four - Permits scaled by permit value

The final map created, and can be found in Figure 4, scales the symbols based on permit value. The main takeaways are like what was found with the unit count map, but one permit stands out as truly unique in this map. The largest symbol by far, shown in dark red, is significantly greater in size and darker in color than any other symbol. If not for the size, it would be hard to tell a difference in values between all the other permits and the largest one shown. This mixed-use property is really in a league of its own, at least based on permits issued this year.

pop2 <- paste0("Value of Project - $ ",permit_pts$value) # popup message
pal <- colorNumeric(palette = "YlOrRd",domain = permit_pts$value) # Color scale to be used
leaflet(data = permit_pts) %>%
  setView(lng = -122.335167, lat = 47.608013, zoom = 11) %>% # Center on city center
  addProviderTiles(providers$CartoDB.DarkMatter) %>% # Add basemap
  addProviderTiles(providers$Stamen.TonerLines, options = providerTileOptions(opacity = .20)) %>% # Add roads as separate layer
  addCircles(fillColor = ~colorNumeric("YlOrRd", value)(value),
             color = NA, fillOpacity = 0.75, radius = ~value/25000,
             label = ~Unit_Type, popup = pop2) %>% # Add circles that scale based on permit value with popup displaying permit value
  addScaleBar(position = "bottomright") %>%  # Add scale bar
  addLegend("bottomleft", pal = pal, values = ~value, title = "Permit Values", opacity = 1,labFormat = labelFormat(prefix = "$ ")) %>% # Add legend that shows range of permit values and corresponding colors
  addMiniMap() # Add mini-map

Figure 4: Location of permits issued in 2023 with symbols scaled based on permit value

Conclusion

Based on the analysis, there are several main summary results. One, is that accessory dwelling units seem to be the most common unit type for permits issued this year, and that the multi-family and mixed-use buildings appear to be concentrated near main transportation nodes. Another takeaway is that in terms of new units to be added on a per permit basis, mixed-use properties are responsible for the highest number. Finally, the value of the projects varies significantly from the highest value to the lowest value. There is one project that has a value that is so much greater than the others, it is hard to know whether that property is that much larger or if there is a data error. Further follow-up steps would be to clarify whether that one permit value is correct or not, and to determine if there are multiple permits for one project. The data used was based on permit numbers, not addresses. It is possible that one project contains multiple permits. If that is the case, then the scale of some of the properties in relation to one another would change. These are the suggested follow-up steps based on the conclusion through this previously discussed analysis.