\(~\)

1 Introduction

The City of EPA is developing its local ADU ordinance and needs to decide how much “local discretion” to apply on top of the new state ADU laws. There is a range of “ADU eligibility” from the “least restrictive”, where the state law is adopted without any local discrtion“, to the”most restrictive" where the most local discretion is applied that is allowed under state law. This document presents ADU eligibility in EPA under these different scenarios to help inform staff recommendations, community perspective, Planning Commission recommendations, and the City Council’s final decision.

See EPA Planning’s latest staff report and Powerpoint slides which will be shared in the 4/27 Planning Commission meeting. This analysis reflects the current staff recommendations.

In summary, the key local discretionary choices being considered are:

  • Street setback (none or 10/12ft for front/side street edge)
  • Min rear yard open space (none or 50% existing, up to 750 sqft)
  • Max ADU size (1200 sqft or 1000 sqft)
  • Property setback (4ft or 6ft)
  • Max site coverage & floor area ratio (none or 50%/55%)

Keep in mind that none of the above local discretionary add-ons can prevent a 800 SF ADU. They can only create a maximum size restriction on ADUs that would have been in the 800-1200 sqft range.

Given the above choices, we have prepared a sequence of scenarios starting from least restrictive (the state law with no local discretion) then increasing in restrictiveness on ADUs through different likely combinations of the above choices. The primary measure of “restrictiveness” is the number of ADUs that can be built in the 800+ (greater than 800) sqft range. Four analysis results are shown in this summary table (with much more detail in the specific sections for each scenario): the # of parcels that can build only up to 800 sqft ADUs, attached and detached, and the # of parcels that can build 800+ sqft ADUs, attached and detached (note there is overlap between attached/detached categories). The total number of ADU-eligible parcels is always 4150 as a result of state law.

1.1 Illustration of Scenarios

To illustrate the seven different scenarios, we will focus on one specific address, 2279 Clarke Ave. It was chosen because is very close to the median lot size (5,600 sqft) and median floor area (1,110 sqft) for single-family parcels in EPA.

The first scenario (A) mapped below shows the buildable area for this parcel if no local discretion was applied, meaning that it would be free to build in the way that state law allows with no other restrictions.

The following instructions on interpreting the map will apply to all other detailed maps too:

  • Hover over the white outline of the parcel to see the address. Only residential parcels will have any other color fields.
  • The beige region is the existing building footprint, based on OpenStreetMap. Hover over to see the gross building square footage, which would account for second floors as well based on Assessor data.
  • The green region is detached ADU buildable area. For Scenario A, it’s 4ft from side/rear, 4ft from existing buildings, and can fit at least an 8ft x 20ft structure. Hover over to see the sqft of the buildable area. This is capped at 1,200 sqft per state regulations, even if the green space is much larger.
  • The blue shape is an example detached ADU. For now, this just shows three possible sizes: 160 sqft, 448 sqft (HEART’s studio), or 640 sqft (HEART’s 1BR), generally set as far as possible away from existing structures (sometimes for other parcels this is shown in the front yard, which may be realistic; you can imagine placing another blue rectangle anywhere there is green). Hover over to see the sqft of the example ADU.
  • The yellow region (including green) is attached ADU buildable area. It doesn’t have the setback from existing buildings. Hover over to see the sqft of the buildable area. This is usually different from the max detached ADU because attached ADUs are capped at 50% of existing gross building sqft.
data <- readRDS("C:/Users/derek/Google Drive/City Systems/EPA/GIS/epa_parcels_adus.rds")

example <- 
  data %>% 
  filter(ADDRESS == "2279 CLARKE AVE") %>% 
  mutate(
    furthest_adu = 
      ifelse(
        !is.na(st_dimension(detached_heart16x40_4s4h_furthest_adu)),
        detached_heart16x40_4s4h_furthest_adu,
        ifelse(
          !is.na(st_dimension(detached_heart16x28_4s4h_furthest_adu)),
          detached_heart16x28_4s4h_furthest_adu,
          detached_4s4h_furthest_adu
        )
      ) %>% st_sfc() %>% st_set_crs(projection),
    furthest_adu_area = st_area(furthest_adu) %>% as.numeric() %>% round(),
    max_attached_4s_buildable_area =
      ifelse(
        is.na(max_attached_4s_buildable_area),
        NA,
        pmin(
          ifelse(
            is.na(`GROSS BUILDING SQFT`),
            building_geometry %>% st_area(.)/2,
            `GROSS BUILDING SQFT`/2
          ),
          1200,
          max_attached_4s_buildable_area
        )
      ),
    max_attached_4s_buildable_area =
      floor(max_attached_4s_buildable_area/10)*10,
    max_detached_4s4h_buildable_area =
      pmin(1200,max_detached_4s4h_buildable_area)
  )

example_A <- leaflet() %>%
  addTiles(group = "Default") %>%
  addProviderTiles(providers$Esri.WorldImagery, group = "Satellite") %>%
  addPolygons(
    data =
      example %>%
      st_set_geometry("parcel_geometry") %>%
      dplyr::select(ADDRESS) %>%
      st_transform(4326),
    fill = F,
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Parcels"
  ) %>%
  addPolygons(
    data =
      example %>%
      st_set_geometry("attached_4s_buildable_area_geometry") %>%
      dplyr::select(max_attached_4s_buildable_area) %>%
      st_transform(4326) %>%
      filter(!is.na(st_dimension(.))),
    fillColor = "yellow",
    stroke = F,
    fillOpacity = 0.5,
    highlightOptions = highlightOptions(
      fillOpacity = 1
    ),
    label = ~max_attached_4s_buildable_area,
    group = "Attached ADU Buildable Area"
  ) %>%
  addPolygons(
    data =
      example %>%
      st_set_geometry("detached_4s4h_buildable_area_geometry") %>%
      dplyr::select(max_detached_4s4h_buildable_area) %>%
      st_transform(4326) %>%
      filter(!is.na(st_dimension(.))),
    fillColor = "green",
    stroke = F,
    fillOpacity = 0.5,
    highlightOptions = highlightOptions(
      fillOpacity = 1
    ),
    label = ~max_detached_4s4h_buildable_area,
    group = "Detached ADU Buildable Area"
  ) %>%
  addPolygons(
    data =
      example %>%
      st_set_geometry("building_geometry") %>%
      dplyr::select(`GROSS BUILDING SQFT`) %>%
      st_transform(4326) %>%
      filter(!is.na(st_dimension(.))),
    fillColor = "tan",
    stroke = F,
    fillOpacity = 0.5,
    highlightOptions = highlightOptions(
      fillOpacity = 1
    ),
    label = ~`GROSS BUILDING SQFT`,
    group = "Existing Building"
  ) %>%
  addPolygons(
    data =
      example %>%
      st_set_geometry("furthest_adu") %>%
      st_transform(4326) %>%
      filter(!is.na(st_dimension(.))),
    fillColor = "blue",
    stroke = F,
    fillOpacity = 0.5,
    highlightOptions = highlightOptions(
      fillOpacity = 1
    ),
    label = ~furthest_adu_area,
    group = "Example Detached ADU"
  ) %>%
  addLayersControl(
    baseGroups = c("Default", "Satellite"),
    overlayGroups = c("Parcels", "Existing Building", "Attached ADU<br>Buildable Area", "Detached ADU<br>Buildable Area", "Example<br>Detached ADU"),
    options = layersControlOptions(collapsed = T)
  )

Example Parcel, Scenario A

\(~\)

In Scenario A, our example parcel can build a maximum 1,200 sqft detached ADU (an example 640 1BR ADU is shown in blue) or a 580 sqft attached ADU (which, if shown, would likely be attached to the back of the main structure).

Now for Scenario B, let’s imagine that a 12ft setback were to be applied to the parcel from the street edge. The City is considering this setback for side street edges. While they’re considering a 10ft setback on the front street edge, our analysis will consider a shared 12ft setback from all street edges, which is a conservative simplification. The following map shows the resultant buildable area.

example <-
  data %>% 
  filter(ADDRESS == "2279 CLARKE AVE") %>% 
  mutate(
    furthest_adu = 
      ifelse(
        !is.na(st_dimension(detached_heart16x40_4s4h12f_furthest_adu)),
        detached_heart16x40_4s4h12f_furthest_adu,
        ifelse(
          !is.na(st_dimension(detached_heart16x28_4s4h12f_furthest_adu)),
          detached_heart16x28_4s4h12f_furthest_adu,
          detached_4s4h12f_furthest_adu
        )
      ) %>% st_sfc() %>% st_set_crs(projection),
    furthest_adu_area = st_area(furthest_adu) %>% as.numeric() %>% round(),
    max_attached_4s12f_buildable_area =
      ifelse(
        is.na(max_attached_4s12f_buildable_area),
        NA,
        pmin(
          ifelse(
            is.na(`GROSS BUILDING SQFT`),
            building_geometry %>% st_area(.)/2,
            `GROSS BUILDING SQFT`/2
          ),
          1200,
          max_attached_4s12f_buildable_area
        )
      ),
    max_attached_4s12f_buildable_area =
      floor(max_attached_4s12f_buildable_area/10)*10,
    max_detached_4s4h12f_buildable_area =
      pmin(1200,max_detached_4s4h12f_buildable_area)
  )

example_B <- leaflet() %>%
  addTiles(group = "Default") %>%
  addProviderTiles(providers$Esri.WorldImagery, group = "Satellite") %>%
  addPolygons(
    data =
      example %>%
      st_set_geometry("parcel_geometry") %>%
      dplyr::select(ADDRESS) %>%
      st_transform(4326),
    fill = F,
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Parcels"
  ) %>%
  addPolygons(
    data =
      example %>%
      st_set_geometry("attached_4s12f_buildable_area_geometry") %>%
      dplyr::select(max_attached_4s12f_buildable_area) %>%
      st_transform(4326) %>%
      filter(!is.na(st_dimension(.))),
    fillColor = "yellow",
    stroke = F,
    fillOpacity = 0.5,
    highlightOptions = highlightOptions(
      fillOpacity = 1
    ),
    label = ~max_attached_4s12f_buildable_area,
    group = "Attached ADU Buildable Area"
  ) %>%
  addPolygons(
    data =
      example %>%
      st_set_geometry("detached_4s4h12f_buildable_area_geometry") %>%
      dplyr::select(max_detached_4s4h12f_buildable_area) %>%
      st_transform(4326) %>%
      filter(!is.na(st_dimension(.))),
    fillColor = "green",
    stroke = F,
    fillOpacity = 0.5,
    highlightOptions = highlightOptions(
      fillOpacity = 1
    ),
    label = ~max_detached_4s4h12f_buildable_area,
    group = "Detached ADU Buildable Area"
  ) %>%
  addPolygons(
    data =
      example %>%
      st_set_geometry("building_geometry") %>%
      dplyr::select(`GROSS BUILDING SQFT`) %>%
      st_transform(4326) %>%
      filter(!is.na(st_dimension(.))),
    fillColor = "tan",
    stroke = F,
    fillOpacity = 0.5,
    highlightOptions = highlightOptions(
      fillOpacity = 1
    ),
    label = ~`GROSS BUILDING SQFT`,
    group = "Existing Building"
  ) %>%
  addPolygons(
    data =
      example %>%
      st_set_geometry("furthest_adu") %>%
      st_transform(4326) %>%
      filter(!is.na(st_dimension(.))),
    fillColor = "blue",
    stroke = F,
    fillOpacity = 0.5,
    highlightOptions = highlightOptions(
      fillOpacity = 1
    ),
    label = ~furthest_adu_area,
    group = "Example Detached ADU"
  ) %>%
  addLayersControl(
    baseGroups = c("Default", "Satellite"),
    overlayGroups = c("Parcels", "Existing Building", "Attached ADU<br>Buildable Area", "Detached ADU<br>Buildable Area", "Example<br>Detached ADU"),
    options = layersControlOptions(collapsed = T)
  )

Example Parcel, Scenario B

\(~\)

Notice the green area in the front yard has disappeared, and a yellow area still exists in the front yard but has shortened. This new front edge to the yellow area represents the edge of the new 12ft setback. A 8x20ft attached ADU would still fit in this front yard yellow area, but the green region would have been too thin for a detached ADU so has been removed altogether. The backyard condition remains the same.

Scenario C builds on Scenario B and increases the property setback from 4ft to 6ft, which is the existing property setback in EPA.

example <-
  data %>% 
  filter(ADDRESS == "2279 CLARKE AVE") %>% 
  mutate(
    furthest_adu = 
      ifelse(
        !is.na(st_dimension(detached_heart16x40_4s6h12f_furthest_adu)),
        detached_heart16x40_4s6h12f_furthest_adu,
        ifelse(
          !is.na(st_dimension(detached_heart16x28_4s6h12f_furthest_adu)),
          detached_heart16x28_4s6h12f_furthest_adu,
          detached_4s6h12f_furthest_adu
        )
      ) %>% st_sfc() %>% st_set_crs(projection),
    furthest_adu_area = st_area(furthest_adu) %>% as.numeric() %>% round(),
    max_attached_4s12f_buildable_area =
      ifelse(
        is.na(max_attached_4s12f_buildable_area),
        NA,
        pmin(
          ifelse(
            is.na(`GROSS BUILDING SQFT`),
            building_geometry %>% st_area(.)/2,
            `GROSS BUILDING SQFT`/2
          ),
          1200,
          max_attached_4s12f_buildable_area
        )
      ),
    max_attached_4s12f_buildable_area =
      floor(max_attached_4s12f_buildable_area/10)*10,
    max_detached_4s6h12f_buildable_area =
      pmin(1200,max_detached_4s6h12f_buildable_area)
  )

example_C <- leaflet() %>%
  addTiles(group = "Default") %>%
  addProviderTiles(providers$Esri.WorldImagery, group = "Satellite") %>%
  addPolygons(
    data =
      example %>%
      st_set_geometry("parcel_geometry") %>%
      dplyr::select(ADDRESS) %>%
      st_transform(4326),
    fill = F,
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Parcels"
  ) %>%
  addPolygons(
    data =
      example %>%
      st_set_geometry("attached_4s12f_buildable_area_geometry") %>%
      dplyr::select(max_attached_4s12f_buildable_area) %>%
      st_transform(4326) %>%
      filter(!is.na(st_dimension(.))),
    fillColor = "yellow",
    stroke = F,
    fillOpacity = 0.5,
    highlightOptions = highlightOptions(
      fillOpacity = 1
    ),
    label = ~max_attached_4s12f_buildable_area,
    group = "Attached ADU Buildable Area"
  ) %>%
  addPolygons(
    data =
      example %>%
      st_set_geometry("detached_4s6h12f_buildable_area_geometry") %>%
      dplyr::select(max_detached_4s6h12f_buildable_area) %>%
      st_transform(4326) %>%
      filter(!is.na(st_dimension(.))),
    fillColor = "green",
    stroke = F,
    fillOpacity = 0.5,
    highlightOptions = highlightOptions(
      fillOpacity = 1
    ),
    label = ~max_detached_4s6h12f_buildable_area,
    group = "Detached ADU Buildable Area"
  ) %>%
  addPolygons(
    data =
      example %>%
      st_set_geometry("building_geometry") %>%
      dplyr::select(`GROSS BUILDING SQFT`) %>%
      st_transform(4326) %>%
      filter(!is.na(st_dimension(.))),
    fillColor = "tan",
    stroke = F,
    fillOpacity = 0.5,
    highlightOptions = highlightOptions(
      fillOpacity = 1
    ),
    label = ~`GROSS BUILDING SQFT`,
    group = "Existing Building"
  ) %>%
  addPolygons(
    data =
      example %>%
      st_set_geometry("furthest_adu") %>%
      st_transform(4326) %>%
      filter(!is.na(st_dimension(.))),
    fillColor = "blue",
    stroke = F,
    fillOpacity = 0.5,
    highlightOptions = highlightOptions(
      fillOpacity = 1
    ),
    label = ~furthest_adu_area,
    group = "Example Detached ADU"
  ) %>%
  addLayersControl(
    baseGroups = c("Default", "Satellite"),
    overlayGroups = c("Parcels", "Existing Building", "Attached ADU<br>Buildable Area", "Detached ADU<br>Buildable Area", "Example<br>Detached ADU"),
    options = layersControlOptions(collapsed = T)
  )

Example Parcel, Scenario C

\(~\)

The change is almost imperceptible from the previous scenario, but the green region has been trimmed 2ft on the side facing the existing residence. It’s still large enough to technically allow for a 1,200 sqft ADU.

In Scenario D, we add on top of Scenario C a max ADU size of 1,000 sqft. This wouldn’t change the previous map, but less of the green area would be usable for large ADUs.

In Scenario E, we add on top of Scenario F a minimum 50% rear space, up to 750 sqft. To model this, we take the original backyard buildable area for each parcel and assume that to be “rear space” (shown in orange below).

example <-
  data %>% 
  filter(ADDRESS == "2279 CLARKE AVE") %>% 
  mutate(
    rear_max =
      detached_4s4hXf_buildable_area_geometry %>% st_buffer(4, joinStyle = "BEVEL"),
    rear_max_area = rear_max %>% st_area(.) %>% as.numeric(),
    rear_max_area = floor(rear_max_area/10)*10,
    furthest_adu = 
      ifelse(
        !is.na(st_dimension(detached_heart16x40_4s6h12f_furthest_adu)),
        detached_heart16x40_4s6h12f_furthest_adu,
        ifelse(
          !is.na(st_dimension(detached_heart16x28_4s6h12f_furthest_adu)),
          detached_heart16x28_4s6h12f_furthest_adu,
          detached_4s6h12f_furthest_adu
        )
      ) %>% st_sfc() %>% st_set_crs(projection),
    furthest_adu_area = st_area(furthest_adu) %>% as.numeric() %>% round(),
    max_attached_4s12f_buildable_area =
      ifelse(
        is.na(max_attached_4s12f_buildable_area),
        NA,
        pmin(
          ifelse(
            is.na(`GROSS BUILDING SQFT`),
            building_geometry %>% st_area(.)/2,
            `GROSS BUILDING SQFT`/2
          ),
          1200,
          max_attached_4s12f_buildable_area
        )
      )
  )

example_E <- leaflet() %>%
  addTiles(group = "Default") %>%
  addProviderTiles(providers$Esri.WorldImagery, group = "Satellite") %>%
  addPolygons(
    data =
      example %>%
      st_set_geometry("parcel_geometry") %>%
      dplyr::select(ADDRESS) %>%
      st_transform(4326),
    fill = F,
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Parcels"
  ) %>%
  addPolygons(
    data =
      example %>%
      st_set_geometry("rear_max") %>%
      dplyr::select(rear_max_area) %>%
      st_transform(4326) %>%
      filter(!is.na(st_dimension(.))),
    fillColor = "orange",
    stroke = F,
    fillOpacity = 0.5,
    highlightOptions = highlightOptions(
      fillOpacity = 1
    ),
    label = ~rear_max_area,
    group = "Rear Space"
  ) %>%
  addPolygons(
    data =
      example %>%
      st_set_geometry("building_geometry") %>%
      dplyr::select(`GROSS BUILDING SQFT`) %>%
      st_transform(4326) %>%
      filter(!is.na(st_dimension(.))),
    fillColor = "tan",
    stroke = F,
    fillOpacity = 0.5,
    highlightOptions = highlightOptions(
      fillOpacity = 1
    ),
    label = ~`GROSS BUILDING SQFT`,
    group = "Existing Building"
  ) %>%
  addPolygons(
    data =
      example %>%
      st_set_geometry("furthest_adu") %>%
      st_transform(4326) %>%
      filter(!is.na(st_dimension(.))),
    fillColor = "blue",
    stroke = F,
    fillOpacity = 0.5,
    highlightOptions = highlightOptions(
      fillOpacity = 1
    ),
    label = ~furthest_adu_area,
    group = "Example Detached ADU"
  ) %>%
  addLayersControl(
    baseGroups = c("Default", "Satellite"),
    overlayGroups = c("Parcels", "Rear Space", "Existing Building", "Example<br>Detached ADU"),
    options = layersControlOptions(collapsed = T)
  )

Example Parcel, Scenario E

\(~\)

For our example parcel, this rear space is over 2,000 sqft, so the minimum 50% rear space requirement would not add any further constraint than the existing 1,000 sqft max ADU limit. But for other parcels, this may reduce the max ADU size further.

Finally, for Scenario F, we add on top of Scenario F limitations on site coverage (50%) and floor area ratio (55%). This represents City staff’s current recommendation.

For our example parcel, the floor area ratio limitation governs, and reduces the max ADU size down to just over 640 sqft. Our blue example ADU just barely would be allowed and represents the largest possible , but for many other parcels, such an ADU may no longer be allowed.

example <-
  data %>% 
  filter(ADDRESS == "2279 CLARKE AVE") %>% 
  mutate(
    max_attached_4s12f_buildable_area =
      ifelse(
        is.na(max_attached_4s12f_buildable_area),
        NA,
        pmin(
          ifelse(
            is.na(`GROSS BUILDING SQFT`),
            building_geometry %>% st_area(.)/2,
            `GROSS BUILDING SQFT`/2
          ),
          1200,
          max_attached_4s12f_buildable_area
        )
      ),
    max_attached_4s12f_buildable_area = 
      pmin(
        parcel_area/2,
        `GROSS BUILDING SQFT`*0.55,
        max_attached_4s12f_buildable_area
      ),
    max_detached_4s6h12f_buildable_area =
      pmin(
        parcel_area/2,
        `GROSS BUILDING SQFT`*0.55,
        max_detached_4s6h12f_buildable_area
      ),
    max_detached_4s6h12f_buildable_area =
      floor(max_detached_4s6h12f_buildable_area/10)*10
  )

\(~\)

1.2 Summary Table

The following table summarizes the features of the six scenarios. It also summarizes four performance measures for each scenario:

  • The number of attached ADUs that can only be built up to 800 sqft. For Scenario A, 2930 ADUs are in this category because they are limited by physical site geometries, even with the least restrictive rules.
  • The number of attached ADUs that are free to be built in the 800-1,200 sqft range. This is the pool of ADU possibilities that can be reduced in size by local discretion.
  • The same two measures but for detached ADUs.

Keep in mind that no matter the scenario, 4150 parcels are eligible for some kind of ADU, at least a JADU. This is the result of state law and isn’t affected by local discretion.

The scenarios are ordered from least to most restrictive, so the 800+ sqft categories will steadily decrease in quantity as ADUs become constrained in size flexibility and join the other category of ADUs that can build only up to 800 sqft.

Scenario Street Set Back House Set Back Max ADU Min Rear Max SC/ FAR Up to 800 sqft Attached ADUs 800+ Sqft Attached ADUs Up to 800 sqft Detached ADUs 800+ Sqft Detached ADUs
A (Least Restrictive): State law without any local discretion 4ft 4ft 1200 sqft None None 2930 540 670 2750
B: 12ft from street 12ft 4ft 1200 sqft None None 3030 430 1250 2170
C: 12ft from street + Maintain EPA property setback 12ft 6ft 1200 sqft None None 3030 430 1590 1830
D: 12ft from street + Maintain EPA property setback + Limit max size 12ft 6ft 1000 sqft None None 3030 430 1590 1830
E: 12ft from street + Maintain EPA property setback + Limit max size + Maintain rear space 12ft 6ft 1000 sqft 50% None 3160 310 1740 1680
F: 12ft from street + Maintain EPA property setback + Limit max size + Maintain rear space + Limit SC/FAR To house 6ft 1000 sqft 50% 50%/55% 3170 300 3040 380

The following charts show the distribution of max ADU sizes for the seven scenarios.

\(~\)

The pink region represents the attached ADUs that can be built up to 800 sqft. Note that the local discretion only acts to reduce the size of the other color bars, which are a minority of cases compared to the ~ 3,000 parcels that can build an 800 sqft attached ADU no matter what.

\(~\)

For detached ADUs, the impacts of local discretion are much more apparent. In Scenario A, a majority of parcels that can build detached ADUs can build 800+ sqft ADUs. But by Scenario F, a majority of these detached ADUs have become constrained to only up to 800 sqft.

2 Parcel Statistics

Here’s the breakdown of parcels by zoning type in General Plan 2035. The parcel shapes and zoning were pulled from ArcGISOnline. Note there were some inconsistencies in APN designation between this zoning file and the County’s Assessor record. 6 residential parcels were manually re-coded from Assessor APN to EPA General Plan APN.

2.1 Zoning Summary

zoning_summary <-
  data %>%
  st_set_geometry(NULL) %>% 
  group_by(Zoning) %>% 
  summarize(
    Count = n(),
    `Median Lot Size` = median(parcel_area, na.rm=T) %>% round()
  ) %>% 
  filter(!is.na(Zoning))
Zoning summary
Zoning Count Median Lot Size
4 Corners 41 9813
Bay Road Central 28 10184
C-G (Commercial General) 9 17606
C-N (Commercial Neighborhood) 15 8573
C-O (Commercial Office) 12 16029
Industrial Flex Overlay 12 13883
Industrial Transition 17 13654
MUC-1 (Mixed Use Corridor) 172 9994
MUC-2 (Mixed Use Corridor) 8 12986
MUH (Mixed Use High) 12 38825
MUL (Mixed Use Low) 31 8114
PI (Public Institutional) 19 81278
PR (Parks and Recreation) 34 23476
PUD (Planned Unit Development) 219 3102
R-HD (Multi-Family High Density Residential) 284 30208
R-LD (Single-Family Residential) 3518 5603
R-MD-1 (Multi-Family Medium Density Residential) 300 6244
R-MD-2 (Multi-Family Medium Density Residential) 155 6667
R-UHD (Multi-Family Urban High Density Residential) 46 30978
REC (Ravenswood Employment Center) 30 40473
RM (Resource Management) 5 228188
Urban Residential 81 10151
Waterfront Office 11 85561

\(~\)

New state regs require ADUs to be allowed in all residentially zoned parcels. We select: R-HD, R-LD, R-MD-1, R-MD-2, R-UHD, & Urban Residential.

The following table is the same as above but just for residential zones, and adding existing structure size and special flood hazard area status.

2.2 Residential Zoning Summary

residential_summary <-
  data %>%
  st_set_geometry(NULL) %>% 
  filter(
    Zoning %in% 
      c(
        "R-HD (Multi-Family High Density Residential)",
        "R-LD (Single-Family Residential)",
        "R-MD-1 (Multi-Family Medium Density Residential)",
        "R-MD-2 (Multi-Family Medium Density Residential)",
        "R-UHD (Multi-Family Urban High Density Residential)",
        "Urban Residential"
      )
  ) %>% 
  group_by(Zoning) %>% 
  summarize(
    Count = n(),
    `Median Lot Size` = median(parcel_area, na.rm=T) %>% round(),
    `Median Gross Bldg Sqft` = median(`GROSS BUILDING SQFT`, na.rm=T) %>% round(),
    `In Flood Zone` = sum(!is.na(FLD_ZONE))
  ) %>% 
  filter(!is.na(Zoning))

# hist(epa_parcels_adus %>% filter(Zoning == "R-LD (Single-Family Residential)") %>% pull(parcel_area), breaks=c(seq(0,1000000,500)),xlim = c(0,10000))
Residential zoning summary
Zoning Count Median Lot Size Median Gross Bldg Sqft In Flood Zone
R-HD (Multi-Family High Density Residential) 284 30208 10658 35
R-LD (Single-Family Residential) 3518 5603 1110 1171
R-MD-1 (Multi-Family Medium Density Residential) 300 6244 2072 101
R-MD-2 (Multi-Family Medium Density Residential) 155 6667 1340 0
R-UHD (Multi-Family Urban High Density Residential) 46 30978 26408 0
Urban Residential 81 10151 1690 7

\(~\)

Of residentially zoned parcels, below is the breakdown of property use code according to the County Assessor, which should represent as of early 2019 the existing use. The table also notes which PUCs we considered to be “ADU eligible” for our analysis, and completed the buildable area analysis for. Note the following key caveats:

  • PUC 91 and 92 are likely existing permitted ADU parcels. There are likely many other unpermitted ADUs under PUC 1.
  • If a residentially zoned lot is currently vacant (PUC: 0), buildable area analysis wasn’t performed. But if the property owner were to build a residential building, then it’s likely they would also subsequently be able to build an ADU. So our ADU numbers will be underestimates because they don’t include currently vacant residential lots.
  • Larger multifamily buildings (5+ units) are not included in the buildable area analysis, although they are technically ADU eligible. Such buildings are typically very differently designed than low-density housing, and have their own special state ADU regulations. We would assume that most of the ADU opportunities for such buildings are internal ADU conversions.

The table includes some additional statistics. % owner occupied is the number for which there is a listed homeowner tax exemption OR the owner’s mailing address matches the property address.

2.3 Residential Property Use Code Summary

residential_puc_summary <-
  data %>%
  st_set_geometry(NULL) %>% 
  filter(
    Zoning %in% 
      c(
        "R-HD (Multi-Family High Density Residential)",
        "R-LD (Single-Family Residential)",
        "R-MD-1 (Multi-Family Medium Density Residential)",
        "R-MD-2 (Multi-Family Medium Density Residential)",
        "R-UHD (Multi-Family Urban High Density Residential)",
        "Urban Residential"
      )
  ) %>% 
  group_by(PUC) %>% 
  summarize(
    Count = n(),
    `Median Lot Size` = median(parcel_area, na.rm=T) %>% round(),
    `Median Gross Bldg Sqft` = median(`GROSS BUILDING SQFT`, na.rm=T) %>% round(),
    `In Flood Zone` = sum(!is.na(FLD_ZONE)),
    `# of Units` = sum(UNITS,na.rm=T),
    `% Owner Occupied` = sum(`EXEMPTION CODE`=="HO" | ADDRESS != substr(`OWNER MAILING ADDRESS`,1,nchar(ADDRESS)),na.rm=T)/n()*100,
    `% Owner Occupied` = `% Owner Occupied` %>% round()
  ) %>% 
  filter(!is.na(PUC)) %>% 
  filter(PUC <= 5 | PUC >= 89) %>% 
  left_join(read_csv("smc_puc.csv"), by="PUC") %>% 
  dplyr::select(PUC, `PUC Description`, Categorization = type, everything()) %>% 
  mutate(
    Categorization = 
      case_when(
        Categorization == "other" ~ "Other",
        Categorization == "adu eligible" ~ "ADU Eligible",
        TRUE ~ "Other Residential"
      )
  )
Residential PUC summary
PUC PUC Description Categorization Count Median Lot Size Median Gross Bldg Sqft In Flood Zone # of Units % Owner Occupied
0 VACANT LAND Other 69 6112 NA 13 0 99
1 SINGLE FAMILY RES ADU Eligible 3603 5561 1110 1157 0 78
2 DUPLEX ADU Eligible 49 5996 1990 12 86 94
3 TRIPLEX ADU Eligible 3 4997 1895 0 9 100
4 FOURPLEX ADU Eligible 9 7129 2836 5 40 100
5 FIVE OR MORE UNITS Other Residential 85 19993 18018 22 1854 99
89 RESIDENTIAL MISC. ADU Eligible 2 7583 0 0 0 50
91 MORE THAN 1 DETACHED LIVING UNITS ADU Eligible 27 7524 2181 5 27 85
92 SFR CONVERTED TO 2 UNITS ADU Eligible 38 6582 2210 12 28 89
93 SFR & DUPLEX OR TRIPLEX ADU Eligible 4 9001 2950 3 7 100
94 TWO DUPLEXES ADU Eligible 6 7033 2388 3 24 100
95 RESIDENTIAL; COMBINATION OF UNIT TYPES ADU Eligible 7 8211 2699 4 25 86
96 FOURPLEX PLUS A RESIDENCE, DUPLEX OR TRI ADU Eligible 6 9593 4810 4 39 100
97 RESIDENTIAL CONDO Other Residential 279 154490 NA 51 0 85

\(~\)

3 Scenario A (Least Restrictive): State regs without local discretion

The following map shows all buildable area for attached and detached ADUs in East Palo Alto in the least restrictive scenario, which essentially allows attached and detached ADUs in the 800-1200 sqft range. Note we have assumed the state’s ministerial 4ft rear/side setback is applied to the house and front as well; these are variables that can be changed with local discretion.

3.1 Scenario A: Detailed Map

load("epa_sfha.Rdata")

epa_boundary <-
  places("CA", cb=FALSE) %>% 
  filter(NAME == "East Palo Alto") %>% 
  st_transform(projection)

epa_sfha_clip <-
  st_intersection(epa_sfha, epa_boundary)

map_center <- data %>% filter(ADDRESS == "2279 CLARKE AVE") %>% st_centroid() %>% st_transform(4326) %>% st_coordinates()
long <- map_center[1]
lat <- map_center[2]

data_4s4h <-
  data %>% 
  mutate(
    furthest_adu = 
      ifelse(
        !is.na(st_dimension(detached_heart16x40_4s4h_furthest_adu)),
        detached_heart16x40_4s4h_furthest_adu,
        ifelse(
          !is.na(st_dimension(detached_heart16x28_4s4h_furthest_adu)),
          detached_heart16x28_4s4h_furthest_adu,
          detached_4s4h_furthest_adu
        )
      ) %>% st_sfc() %>% st_set_crs(projection),
    furthest_adu_area = st_area(furthest_adu) %>% as.numeric() %>% round(),
    max_attached_4s_buildable_area =
      ifelse(
        is.na(max_attached_4s_buildable_area),
        NA,
        pmin(
          ifelse(
            is.na(`GROSS BUILDING SQFT`),
            building_geometry %>% st_area(.)/2,
            `GROSS BUILDING SQFT`/2
          ),
          1200,
          max_attached_4s_buildable_area
        )
      ),
    max_attached_4s_buildable_area =
      floor(max_attached_4s_buildable_area/10)*10,
    max_detached_4s4h_buildable_area =
      pmin(1200,max_detached_4s4h_buildable_area)
  )

map <- leaflet() %>%
  addTiles(group = "Default") %>%
  addProviderTiles(providers$Esri.WorldImagery, group = "Satellite") %>%
  addPolygons(
    data =
      data_4s4h %>%
      st_set_geometry("parcel_geometry") %>%
      dplyr::select(ADDRESS) %>%
      st_transform(4326),
    fill = F,
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Parcels"
  ) %>%
  addPolygons(
    data =
      data_4s4h %>%
      st_set_geometry("attached_4s_buildable_area_geometry") %>%
      dplyr::select(max_attached_4s_buildable_area) %>%
      st_transform(4326) %>%
      filter(!is.na(st_dimension(.))),
    fillColor = "yellow",
    stroke = F,
    fillOpacity = 0.5,
    highlightOptions = highlightOptions(
      fillOpacity = 1
    ),
    label = ~max_attached_4s_buildable_area,
    group = "Attached ADU Buildable Area"
  ) %>%
  addPolygons(
    data =
      data_4s4h %>%
      st_set_geometry("detached_4s4h_buildable_area_geometry") %>%
      dplyr::select(max_detached_4s4h_buildable_area) %>%
      st_transform(4326) %>%
      filter(!is.na(st_dimension(.))),
    fillColor = "green",
    stroke = F,
    fillOpacity = 0.5,
    highlightOptions = highlightOptions(
      fillOpacity = 1
    ),
    label = ~max_detached_4s4h_buildable_area,
    group = "Detached ADU Buildable Area"
  ) %>%
  addPolygons(
    data =
      data_4s4h %>%
      st_set_geometry("building_geometry") %>%
      dplyr::select(`GROSS BUILDING SQFT`) %>%
      st_transform(4326) %>%
      filter(!is.na(st_dimension(.))),
    fillColor = "tan",
    stroke = F,
    fillOpacity = 0.5,
    highlightOptions = highlightOptions(
      fillOpacity = 1
    ),
    label = ~`GROSS BUILDING SQFT`,
    group = "Existing Building"
  ) %>%
  addPolygons(
    data =
      data_4s4h %>%
      st_set_geometry("furthest_adu") %>%
      st_transform(4326) %>%
      filter(!is.na(st_dimension(.))),
    fillColor = "blue",
    stroke = F,
    fillOpacity = 0.5,
    highlightOptions = highlightOptions(
      fillOpacity = 1
    ),
    label = ~furthest_adu_area,
    group = "Example Detached ADU"
  ) %>%
  addLayersControl(
    baseGroups = c("Default", "Satellite"),
    overlayGroups = c("Parcels", "Existing Building", "Attached ADU<br>Buildable Area", "Detached ADU<br>Buildable Area", "Example<br>Detached ADU"),
    options = layersControlOptions(collapsed = T)
  ) %>% 
  setView(long,lat, zoom = 14)

saveWidget(map, "4s4h_map_detailed.html", selfcontained=FALSE)

Least Restrictive Scenario: Buildable Area Map

\(~\)

The following shows ADU statistics for the residential parcels.

3.2 Scenario A: Summary Table

epa_parcels_adus_residential <-
  data_4s4h %>%
  st_set_geometry(NULL) %>%
  filter(
    Zoning %in%
      c(
        "R-HD (Multi-Family High Density Residential)",
        "R-LD (Single-Family Residential)",
        "R-MD-1 (Multi-Family Medium Density Residential)",
        "R-MD-2 (Multi-Family Medium Density Residential)",
        "R-UHD (Multi-Family Urban High Density Residential)",
        "Urban Residential"
      )
    & ADDRESS != "NA"
  )

epa_parcels_adus_residential_no9192 <-
  epa_parcels_adus_residential %>%
  filter(!PUC %in% c(91,92)) %>%
  filter(PUC <= 5 | PUC >= 89)

residential_adu_summary_A <-
  rbind(
    data.frame(
      Description = "Total # of Residential Parcels",
      Amount = epa_parcels_adus_residential %>% nrow(),
      `Amount in Flood Zone` = epa_parcels_adus_residential %>% filter(!is.na(FLD_ZONE)) %>% nrow()
    ),
    data.frame(
      Description = "Total # of ADU-Eligible Parcels",
      Amount = epa_parcels_adus_residential %>% filter(PUC <= 5 | PUC >= 89) %>%  nrow(),
      `Amount in Flood Zone` = epa_parcels_adus_residential %>% filter(PUC <= 5 | PUC >= 89) %>% filter(!is.na(FLD_ZONE)) %>% nrow()
    ),
    data.frame(
      Description = "Estimated # of Existing Permitted ADUs",
      Amount = epa_parcels_adus_residential %>% filter(PUC %in% c(91,92)) %>% nrow(),
      `Amount in Flood Zone` = epa_parcels_adus_residential %>% filter(PUC %in% c(91,92)) %>% filter(!is.na(FLD_ZONE)) %>% nrow()
    ),
    data.frame(
      Description = "Estimated # of Ministerial JADUs",
      Amount = epa_parcels_adus_residential_no9192 %>% nrow(),
      `Amount in Flood Zone` = epa_parcels_adus_residential_no9192 %>% filter(!is.na(FLD_ZONE)) %>% nrow()
    ),
    data.frame(
      Description = "Estimated # of Parcels that can build up to 800 sqft ADUs",
      Amount = epa_parcels_adus_residential_no9192 %>% filter(max_attached_4s_buildable_area <= 800 | max_detached_4s4h_buildable_area <= 800) %>% nrow(),
      `Amount in Flood Zone` = epa_parcels_adus_residential_no9192 %>% filter(max_attached_4s_buildable_area <= 800 | max_detached_4s4h_buildable_area <= 800) %>% filter(!is.na(FLD_ZONE)) %>% nrow()
    ),
    data.frame(
      Description = "Estimated # of Parcels that can build 800+ sqft ADUs",
      Amount = epa_parcels_adus_residential_no9192 %>% filter(max_attached_4s_buildable_area > 800 | max_detached_4s4h_buildable_area > 800) %>% nrow(),
      `Amount in Flood Zone` = epa_parcels_adus_residential_no9192 %>% filter(max_attached_4s_buildable_area > 800 | max_detached_4s4h_buildable_area > 800) %>% filter(!is.na(FLD_ZONE)) %>% nrow()
    ),
    data.frame(
      Description = "Estimated # of Parcels that can build only up to 800 sqft Attached ADUs",
      Amount = epa_parcels_adus_residential_no9192 %>% filter(max_attached_4s_buildable_area <= 800) %>% nrow(),
      `Amount in Flood Zone` = epa_parcels_adus_residential_no9192 %>% filter(max_attached_4s_buildable_area <= 800) %>% filter(!is.na(FLD_ZONE)) %>% nrow()
    ),
    data.frame(
      Description = "Estimated # of Parcels that can build 800+ sqft Attached ADUs",
      Amount = epa_parcels_adus_residential_no9192 %>% filter(max_attached_4s_buildable_area > 800) %>% nrow(),
      `Amount in Flood Zone` = epa_parcels_adus_residential_no9192 %>% filter(max_attached_4s_buildable_area > 800) %>% filter(!is.na(FLD_ZONE)) %>% nrow()
    ),
    data.frame(
      Description = "Estimated # of Parcels that can build only up to 800 sqft Detached ADUs",
      Amount = epa_parcels_adus_residential_no9192 %>% filter(max_detached_4s4h_buildable_area <= 800) %>% nrow(),
      `Amount in Flood Zone` = epa_parcels_adus_residential_no9192 %>% filter(max_detached_4s4h_buildable_area <= 800) %>% filter(!is.na(FLD_ZONE)) %>% nrow()
    ),
    data.frame(
      Description = "Estimated # of Parcels that can build 800+ sqft Detached ADUs",
      Amount = epa_parcels_adus_residential_no9192 %>% filter(max_detached_4s4h_buildable_area > 800) %>% nrow(),
      `Amount in Flood Zone` = epa_parcels_adus_residential_no9192 %>% filter(max_detached_4s4h_buildable_area > 800) %>% filter(!is.na(FLD_ZONE)) %>% nrow()
    )
  ) %>%
  rename(`Amount in Flood Zone` = Amount.in.Flood.Zone)

saveRDS(residential_adu_summary_A, "residential_adu_summary_A.rds")
residential_adu_summary_A <- readRDS("residential_adu_summary_A.rds") 

least_restrictive_data <-
  residential_adu_summary_A[c(2,7,8,9,10),] %>% 
  mutate(
    Description = c("Total # of ADU-Eligible Parcels", "Min # of Parcels that can build only up to 800 sqft Attached ADUs", "Max # of Parcels that can build 800+ sqft Attached ADUs", "Min # of Parcels that can build only up to 800 sqft Detached ADUs", "Max # of Parcels that can build 800+ sqft Detached ADUs")
  )

flexible_APNS <-
  epa_parcels_adus_residential_no9192 %>% filter(max_attached_4s_buildable_area > 800 | max_detached_4s4h_buildable_area > 800) %>% 
  pull(APN)

eligible_detached <-
  epa_parcels_adus_residential_no9192 %>% 
  filter(!is.na(max_detached_4s4h_buildable_area)) %>% 
  pull(APN)

eligible_attached <-
  epa_parcels_adus_residential_no9192 %>% 
  filter(!is.na(max_attached_4s_buildable_area)) %>% 
  pull(APN)
Least Restrictive Scenario: ADU summary
Description Amount Amount in Flood Zone
Total # of Residential Parcels 4257 1291
Total # of ADU-Eligible Parcels 4157 1282
Estimated # of Existing Permitted ADUs 65 17
Estimated # of Ministerial JADUs 4092 1265
Estimated # of Parcels that can build up to 800 sqft ADUs 3048 1026
Estimated # of Parcels that can build 800+ sqft ADUs 2870 1022
Estimated # of Parcels that can build only up to 800 sqft Attached ADUs 2934 990
Estimated # of Parcels that can build 800+ sqft Attached ADUs 543 164
Estimated # of Parcels that can build only up to 800 sqft Detached ADUs 675 165
Estimated # of Parcels that can build 800+ sqft Detached ADUs 2755 986

\(~\)

Here’s a map showing some of the different circumstances from the table above. You can turn the flood layer on and off.

  • Yellow: Parcels that can build 800+ sqft detached ADUs (some can also build 800+ sqft attached ADUs)
  • Orange: Parcels that can build 800+ sqft attached ADUs (but can’t build 800+ sqft detached ADUs)
  • Green: Parcels that can build only up to 800 sqft ADUs
  • Purple: Existing Permitted ADU

3.3 Scenario A: Summary Map

adu_summary_map <-
  data_4s4h %>%
  mutate(
    Category =
      case_when(
        PUC %in% c(91,92) & 
          Zoning %in% 
          c(
            "R-HD (Multi-Family High Density Residential)",
            "R-LD (Single-Family Residential)",
            "R-MD-1 (Multi-Family Medium Density Residential)",
            "R-MD-2 (Multi-Family Medium Density Residential)",
            "R-UHD (Multi-Family Urban High Density Residential)",
            "Urban Residential"
          ) ~ "Existing Permitted ADU",
        ADDRESS %in% epa_parcels_adus_residential_no9192$ADDRESS & max_detached_4s4h_buildable_area > 800 ~ "Parcels that can build 800+ sqft Detached ADUs",
        ADDRESS %in% epa_parcels_adus_residential_no9192$ADDRESS & max_attached_4s_buildable_area > 800 ~ "Parcels that can build 800+ sqft Attached ADUs",
        ADDRESS %in% epa_parcels_adus_residential_no9192$ADDRESS & max_detached_4s4h_buildable_area <= 800 ~ "Parcels that can build only up to 800 sqft Detached ADUs",
        ADDRESS %in% epa_parcels_adus_residential_no9192$ADDRESS & max_attached_4s_buildable_area <= 800 ~ "Parcels that can build only up to 800 sqft Attached ADUs",
        ADDRESS %in% epa_parcels_adus_residential_no9192$ADDRESS ~ "Ministerial JADUs",
        TRUE ~ "Other"
      )
  ) %>% 
  mutate(
    Category = ifelse(Category == "Other", NA, Category)
  ) %>% 
  dplyr::select(ADDRESS,Category)

map <- leaflet() %>%
  addTiles(group = "Default") %>%
  addProviderTiles(providers$Esri.WorldImagery, group = "Satellite") %>%
  addPolygons(
    data = adu_summary_map %>% filter(Category == "Existing Permitted ADU") %>% st_transform(4326),
    fillColor = "purple",
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    fillOpacity = 0.75,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Existing Permitted ADU"
  ) %>%
  addPolygons(
    data = adu_summary_map %>% filter(Category == "Parcels that can build 800+ sqft Detached ADUs") %>% st_transform(4326),
    fillColor = "yellow",
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    fillOpacity = 0.5,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Parcels that can build 800+ sqft Detached ADUs"
  ) %>%
  addPolygons(
    data = adu_summary_map %>% filter(Category == "Parcels that can build 800+ sqft Attached ADUs") %>% st_transform(4326),
    fillColor = "orange",
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    fillOpacity = 0.75,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Parcels that can build 800+ sqft Attached ADUs"
  ) %>%
  addPolygons(
    data = adu_summary_map %>% filter(Category == "Parcels that can build only up to 800 sqft Detached ADUs") %>% st_transform(4326),
    fillColor = "green",
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    fillOpacity = 0.75,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Parcels that can build only up to 800 sqft Detached ADUs"
  ) %>%
  addPolygons(
    data = adu_summary_map %>% filter(Category == "Parcels that can build only up to 800 sqft Attached ADUs") %>% st_transform(4326),
    fillColor = "green",
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    fillOpacity = 0.75,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Parcels that can build only up to 800 sqft Attached ADUs"
  ) %>%
  addPolygons(
    data = adu_summary_map %>% filter(Category == "Ministerial JADUs") %>% st_transform(4326),
    fillColor = "green",
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    fillOpacity = 0.75,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Ministerial JADUs"
  ) %>%
  addPolygons(
    data = epa_sfha_clip %>% st_transform(4326),
    fillColor = "blue",
    stroke = F,
    fillOpacity = 0.25,
    group = "Special Flood Hazard Area"
  ) %>%
  addLayersControl(
    baseGroups = c("Default", "Satellite"),
    overlayGroups = c("Existing Permitted ADU","Parcels that have been restricted to<br>only up to 800 sqft ADUs","Parcels that can build<br>800+ sqft Detached ADUs", "Parcels that can build<br>800+ sqft Attached ADUs","Parcels that can build only<br>up to 800 sqft Detached ADUs","Parcels that can build only<br>up to 800 sqft Attached ADUs", "Ministerial JADUs","Special Flood Hazard Area"),
    options = layersControlOptions(collapsed = T)
  ) %>% 
  setView(long,lat, zoom = 14)

saveWidget(map, "4s4h_map_summary.html", selfcontained=FALSE)

Least Restrictive Scenario: Parcel Summary

\(~\)

The following graphs focus only on parcels that can build 800+ sqft ADUs. The bars show how many ADUs can be built in increments of 50 (e.g. the first bar is 90+ attached ADUs between 800-850 sqft).

3.4 Scenario A: 800+ sqft ADU Distribution

hist_4s <-
  data_4s4h %>% 
  dplyr::select(area = max_attached_4s_buildable_area) %>% 
  st_set_geometry(NULL) %>% 
  filter(area > 800) %>% 
  mutate(
    bin = floor(area/50)*50,
    scenario = "A"
  )

\(~\)

The last bar in the following graph is detached ADUs that can max out at 1200 sqft exactly.

hist_4s4h <-
  data_4s4h %>% 
  dplyr::select(area = max_detached_4s4h_buildable_area) %>% 
  st_set_geometry(NULL) %>% 
  filter(area > 800) %>% 
  mutate(
    bin = floor(area/50)*50,
    scenario = "A"
  )

\(~\)

4 Scenario B: 12ft from street

Scenario B removes 12ft of setback from all street edges, including front yard and street sides on corner lots, from ADU development. As noted before, although EPA is considering just a 10ft setback from the front street edge, our analysis applies 12ft to all street edges for the sake of conservative simplicity.

Note that if the parcel could build an 800 sqft ADU in the front yard in the least restrictive scenario, then the Casita Coalition’s interpretation of the state law through HCD correspondence is that a local discretionary rule cannot prevent a homeowner from choosing to build a detached or attached ADU in the front yard. Our analysis currently doesn’t specifically identify whether an 800 sqft ADU can be built in a front yard; besides, we suspect this will not ultimately be desired by the City or its residents. But this analysis can be added in the future.

4.1 Scenario B: Detailed Map

data_4s4h12f <-
  data %>% 
  mutate(
    furthest_adu = 
      ifelse(
        !is.na(st_dimension(detached_heart16x40_4s4h12f_furthest_adu)),
        detached_heart16x40_4s4h12f_furthest_adu,
        ifelse(
          !is.na(st_dimension(detached_heart16x28_4s4h12f_furthest_adu)),
          detached_heart16x28_4s4h12f_furthest_adu,
          detached_4s4h12f_furthest_adu
        )
      ) %>% st_sfc() %>% st_set_crs(projection),
    furthest_adu_area = st_area(furthest_adu) %>% as.numeric() %>% round(),
    max_attached_4s12f_buildable_area =
      ifelse(
        is.na(max_attached_4s12f_buildable_area),
        NA,
        pmin(
          ifelse(
            is.na(`GROSS BUILDING SQFT`),
            building_geometry %>% st_area(.)/2,
            `GROSS BUILDING SQFT`/2
          ),
          1200,
          max_attached_4s12f_buildable_area
        )
      ),
    max_attached_4s12f_buildable_area =
      floor(max_attached_4s12f_buildable_area/10)*10,
    max_detached_4s4h12f_buildable_area =
      pmin(1200,max_detached_4s4h12f_buildable_area)
  )

map <- leaflet() %>%
  addTiles(group = "Default") %>%
  addProviderTiles(providers$Esri.WorldImagery, group = "Satellite") %>%
  addPolygons(
    data =
      data_4s4h12f %>%
      st_set_geometry("parcel_geometry") %>%
      dplyr::select(ADDRESS) %>%
      st_transform(4326),
    fill = F,
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Parcels"
  ) %>%
  addPolygons(
    data =
      data_4s4h12f %>%
      st_set_geometry("attached_4s12f_buildable_area_geometry") %>%
      dplyr::select(max_attached_4s12f_buildable_area) %>%
      st_transform(4326) %>%
      filter(!is.na(st_dimension(.))),
    fillColor = "yellow",
    stroke = F,
    fillOpacity = 0.5,
    highlightOptions = highlightOptions(
      fillOpacity = 1
    ),
    label = ~max_attached_4s12f_buildable_area,
    group = "Attached ADU Buildable Area"
  ) %>%
  addPolygons(
    data =
      data_4s4h12f %>%
      st_set_geometry("detached_4s4h12f_buildable_area_geometry") %>%
      dplyr::select(max_detached_4s4h12f_buildable_area) %>%
      st_transform(4326) %>%
      filter(!is.na(st_dimension(.))),
    fillColor = "green",
    stroke = F,
    fillOpacity = 0.5,
    highlightOptions = highlightOptions(
      fillOpacity = 1
    ),
    label = ~max_detached_4s4h12f_buildable_area,
    group = "Detached ADU Buildable Area"
  ) %>%
  addPolygons(
    data =
      data_4s4h12f %>%
      st_set_geometry("building_geometry") %>%
      dplyr::select(`GROSS BUILDING SQFT`) %>%
      st_transform(4326) %>%
      filter(!is.na(st_dimension(.))),
    fillColor = "tan",
    stroke = F,
    fillOpacity = 0.5,
    highlightOptions = highlightOptions(
      fillOpacity = 1
    ),
    label = ~`GROSS BUILDING SQFT`,
    group = "Existing Building"
  ) %>%
  addPolygons(
    data =
      data_4s4h12f %>%
      st_set_geometry("furthest_adu") %>%
      st_transform(4326) %>%
      filter(!is.na(st_dimension(.))),
    fillColor = "blue",
    stroke = F,
    fillOpacity = 0.5,
    highlightOptions = highlightOptions(
      fillOpacity = 1
    ),
    label = ~furthest_adu_area,
    group = "Example Detached ADU"
  ) %>%
  addLayersControl(
    baseGroups = c("Default", "Satellite"),
    overlayGroups = c("Parcels", "Existing Building", "Attached ADU<br>Buildable Area", "Detached ADU<br>Buildable Area", "Example<br>Detached ADU"),
    options = layersControlOptions(collapsed = T)
  ) %>% 
  setView(long,lat, zoom = 14)

saveWidget(map, "4s4h12f_map_detailed.html", selfcontained=FALSE)

B: Buildable Area Map

\(~\)

The following table estimates the number of parcels that were under local influence that have been restricted to build only up to 800 sqft ADUs. Note that of the parcels that can still build 800+ sqft ADUs, many of these are also affected by a reduction in maximum possible size.

4.2 Scenario B: Summary Table

epa_parcels_adus_residential <-
  data_4s4h12f %>%
  st_set_geometry(NULL) %>%
  filter(
    Zoning %in%
      c(
        "R-HD (Multi-Family High Density Residential)",
        "R-LD (Single-Family Residential)",
        "R-MD-1 (Multi-Family Medium Density Residential)",
        "R-MD-2 (Multi-Family Medium Density Residential)",
        "R-UHD (Multi-Family Urban High Density Residential)",
        "Urban Residential"
      )
    & ADDRESS != "NA"
  )

epa_parcels_adus_residential_no9192 <-
  epa_parcels_adus_residential %>%
  filter(!PUC %in% c(91,92)) %>%
  filter(PUC <= 5 | PUC >= 89)

residential_adu_summary_B <-
  rbind(
    least_restrictive_data %>% rename(Amount.in.Flood.Zone = `Amount in Flood Zone`),
    data.frame(
      Description = "B: Estimated # of Parcels that can build only up to 800 sqft Attached ADUs",
      Amount = epa_parcels_adus_residential_no9192 %>% filter((APN %in% eligible_attached) & (is.na(max_attached_4s12f_buildable_area) | (max_attached_4s12f_buildable_area <= 800))) %>% nrow(),
      `Amount in Flood Zone` = epa_parcels_adus_residential_no9192 %>% filter((APN %in% eligible_attached) & (is.na(max_attached_4s12f_buildable_area) | (max_attached_4s12f_buildable_area <= 800))) %>% filter(!is.na(FLD_ZONE)) %>% nrow()
    ),
    data.frame(
      Description = "B: Estimated # of Parcels that can build 800+ sqft Attached ADUs",
      Amount = epa_parcels_adus_residential_no9192 %>% filter(max_attached_4s12f_buildable_area > 800) %>% nrow(),
      `Amount in Flood Zone` = epa_parcels_adus_residential_no9192 %>% filter(max_attached_4s12f_buildable_area > 800) %>% filter(!is.na(FLD_ZONE)) %>% nrow()
    ),
    data.frame(
      Description = "B: Estimated # of Parcels that can build only up to 800 sqft Detached ADUs",
      Amount = epa_parcels_adus_residential_no9192 %>% filter((APN %in% eligible_detached) & (is.na(max_detached_4s4h12f_buildable_area) | (max_detached_4s4h12f_buildable_area <= 800))) %>% nrow(),
      `Amount in Flood Zone` = epa_parcels_adus_residential_no9192 %>% filter((APN %in% eligible_detached) & (is.na(max_detached_4s4h12f_buildable_area) | (max_detached_4s4h12f_buildable_area <= 800))) %>% filter(!is.na(FLD_ZONE)) %>% nrow()
    ),
    data.frame(
      Description = "B: Estimated # of Parcels that can build 800+ sqft Detached ADUs",
      Amount = epa_parcels_adus_residential_no9192 %>% filter(max_detached_4s4h12f_buildable_area > 800) %>% nrow(),
      `Amount in Flood Zone` = epa_parcels_adus_residential_no9192 %>% filter(max_detached_4s4h12f_buildable_area > 800) %>% filter(!is.na(FLD_ZONE)) %>% nrow()
    )
  ) %>%
  rename(`Amount in Flood Zone` = Amount.in.Flood.Zone)

residential_adu_summary_B[10:11,1] <-
  c(
    "B: Estimated # Parcels that have been restricted to only up to 800 sqft Attached ADUs",
    "B: Estimated # Parcels that have been restricted to only up to 800 sqft Detached ADUs"
  )

residential_adu_summary_B[10:11,2:3] <-
  residential_adu_summary_B[c(6,8),2:3]-
  residential_adu_summary_B[c(2,4),2:3]

saveRDS(residential_adu_summary_B, "residential_adu_summary_B.rds")
# residential_adu_summary_B <- readRDS("residential_adu_summary_B.rds") 
B: ADU summary
Description Amount Amount in Flood Zone
Total # of ADU-Eligible Parcels 4157 1282
Min # of Parcels that can build only up to 800 sqft Attached ADUs 2934 990
Max # of Parcels that can build 800+ sqft Attached ADUs 543 164
Min # of Parcels that can build only up to 800 sqft Detached ADUs 675 165
Max # of Parcels that can build 800+ sqft Detached ADUs 2755 986
B: Estimated # of Parcels that can build only up to 800 sqft Attached ADUs 3039 1028
B: Estimated # of Parcels that can build 800+ sqft Attached ADUs 438 126
B: Estimated # of Parcels that can build only up to 800 sqft Detached ADUs 1259 375
B: Estimated # of Parcels that can build 800+ sqft Detached ADUs 2171 776
B: Estimated # Parcels that have been restricted to only up to 800 sqft Attached ADUs 105 38
B: Estimated # Parcels that have been restricted to only up to 800 sqft Detached ADUs 584 210

\(~\)

  • Red: Parcels that have been restricted to only up to 800 sqft ADUs
  • Yellow: Parcels that can build 800+ sqft detached ADUs (some can also build 800+ sqft attached ADUs)
  • Orange: Parcels that can build 800+ sqft attached ADUs (but can’t build 800+ sqft detached ADUs)
  • Green: Parcels that can build only up to 800 sqft ADUs
  • Purple: Existing Permitted ADU

4.3 Scenario B: Summary Map

adu_summary_map <-
  data_4s4h12f %>%
  mutate(
    Category =
      case_when(
        PUC %in% c(91,92) ~ "Existing Permitted ADU",
        max_detached_4s4h12f_buildable_area > 800 ~ "Parcels that can build 800+ sqft Detached ADUs",
        max_attached_4s12f_buildable_area > 800 ~ "Parcels that can build 800+ sqft Attached ADUs",
        APN %in% flexible_APNS ~ "Parcels that have been restricted to only up to 800 sqft ADUs",
        max_detached_4s4h12f_buildable_area <= 800 ~ "Parcels that can build only up to 800 sqft Detached ADUs",
        max_attached_4s12f_buildable_area <= 800 ~ "Parcels that can build only up to 800 sqft Attached ADUs",
        Zoning %in% 
      c(
        "R-HD (Multi-Family High Density Residential)",
        "R-LD (Single-Family Residential)",
        "R-MD-1 (Multi-Family Medium Density Residential)",
        "R-MD-2 (Multi-Family Medium Density Residential)",
        "R-UHD (Multi-Family Urban High Density Residential)",
        "Urban Residential"
      ) & (PUC <= 5 | PUC >= 89) ~ "Ministerial JADUs",
        TRUE ~ "Other"
      )
  ) %>% 
  mutate(
    Category = ifelse(Category == "Other", NA, Category)
  ) %>% 
  dplyr::select(ADDRESS,Category)

map <- leaflet() %>%
  addTiles(group = "Default") %>%
  addProviderTiles(providers$Esri.WorldImagery, group = "Satellite") %>%
  addPolygons(
    data = adu_summary_map %>% filter(Category == "Existing Permitted ADU") %>% st_transform(4326),
    fillColor = "purple",
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    fillOpacity = 0.75,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Existing Permitted ADU"
  ) %>%
  addPolygons(
    data = adu_summary_map %>% filter(Category == "Parcels that have been restricted to only up to 800 sqft ADUs") %>% st_transform(4326),
    fillColor = "red",
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    fillOpacity = 0.5,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Parcels that have been restricted to only up to 800 sqft ADUs"
  ) %>%
  addPolygons(
    data = adu_summary_map %>% filter(Category == "Parcels that can build 800+ sqft Detached ADUs") %>% st_transform(4326),
    fillColor = "yellow",
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    fillOpacity = 0.5,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Parcels that can build 800+ sqft Detached ADUs"
  ) %>%
  addPolygons(
    data = adu_summary_map %>% filter(Category == "Parcels that can build 800+ sqft Attached ADUs") %>% st_transform(4326),
    fillColor = "orange",
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    fillOpacity = 0.75,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Parcels that can build 800+ sqft Attached ADUs"
  ) %>%
  addPolygons(
    data = adu_summary_map %>% filter(Category == "Parcels that can build only up to 800 sqft Detached ADUs") %>% st_transform(4326),
    fillColor = "green",
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    fillOpacity = 0.75,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Parcels that can build only up to 800 sqft Detached ADUs"
  ) %>%
  addPolygons(
    data = adu_summary_map %>% filter(Category == "Parcels that can build only up to 800 sqft Attached ADUs") %>% st_transform(4326),
    fillColor = "green",
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    fillOpacity = 0.75,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Parcels that can build only up to 800 sqft Attached ADUs"
  ) %>%
  addPolygons(
    data = adu_summary_map %>% filter(Category == "Ministerial JADUs") %>% st_transform(4326),
    fillColor = "green",
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    fillOpacity = 0.75,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Ministerial JADUs"
  ) %>%
  addPolygons(
    data = epa_sfha_clip %>% st_transform(4326),
    fillColor = "blue",
    stroke = F,
    fillOpacity = 0.25,
    group = "Special Flood Hazard Area"
  ) %>%
  addLayersControl(
    baseGroups = c("Default", "Satellite"),
    overlayGroups = c("Existing Permitted ADU","Parcels that have been restricted to<br>only up to 800 sqft ADUs","Parcels that can build<br>800+ sqft Detached ADUs", "Parcels that can build<br>800+ sqft Attached ADUs","Parcels that can build only<br>up to 800 sqft Detached ADUs","Parcels that can build only<br>up to 800 sqft Attached ADUs", "Ministerial JADUs","Special Flood Hazard Area"),
    options = layersControlOptions(collapsed = T)
  ) %>% 
  setView(long,lat, zoom = 14)

saveWidget(map, "4s4h12f_map_summary.html", selfcontained=FALSE)

B: Parcel Summary

\(~\)

4.4 Scenario B: 800+ sqft ADU Distribution

hist_4s12f <-
  data_4s4h12f %>% 
  dplyr::select(area = max_attached_4s12f_buildable_area) %>% 
  st_set_geometry(NULL) %>% 
  filter(area > 800) %>% 
  mutate(
    bin = floor(area/50)*50,
    scenario = "B"
  )

\(~\)

hist_4s4h12f <-
  data_4s4h12f %>% 
  dplyr::select(area = max_detached_4s4h12f_buildable_area) %>% 
  st_set_geometry(NULL) %>% 
  filter(area > 800) %>% 
  mutate(
    bin = floor(area/50)*50,
    scenario = "B"
  )

\(~\)

5 Scenario C: 12ft from street + Maintain EPA property setback

Scenario C creates a 6ft setback from existing structures on the parcel.

5.1 Scenario C: Detailed Map

data_4s6h12f <-
  data %>% 
  mutate(
    furthest_adu = 
      ifelse(
        !is.na(st_dimension(detached_heart16x40_4s6h12f_furthest_adu)),
        detached_heart16x40_4s6h12f_furthest_adu,
        ifelse(
          !is.na(st_dimension(detached_heart16x28_4s6h12f_furthest_adu)),
          detached_heart16x28_4s6h12f_furthest_adu,
          detached_4s6h12f_furthest_adu
        )
      ) %>% st_sfc() %>% st_set_crs(projection),
    furthest_adu_area = st_area(furthest_adu) %>% as.numeric() %>% round(),
    max_attached_4s12f_buildable_area =
      ifelse(
        is.na(max_attached_4s12f_buildable_area),
        NA,
        pmin(
          ifelse(
            is.na(`GROSS BUILDING SQFT`),
            building_geometry %>% st_area(.)/2,
            `GROSS BUILDING SQFT`/2
          ),
          1200,
          max_attached_4s12f_buildable_area
        )
      ),
    max_attached_4s12f_buildable_area =
      floor(max_attached_4s12f_buildable_area/10)*10,
    max_detached_4s6h12f_buildable_area =
      pmin(1200,max_detached_4s6h12f_buildable_area)
  )

map <- leaflet() %>%
  addTiles(group = "Default") %>%
  addProviderTiles(providers$Esri.WorldImagery, group = "Satellite") %>%
  addPolygons(
    data =
      data_4s6h12f %>%
      st_set_geometry("parcel_geometry") %>%
      dplyr::select(ADDRESS) %>%
      st_transform(4326),
    fill = F,
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Parcels"
  ) %>%
  addPolygons(
    data =
      data_4s6h12f %>%
      st_set_geometry("attached_4s12f_buildable_area_geometry") %>%
      dplyr::select(max_attached_4s12f_buildable_area) %>%
      st_transform(4326) %>%
      filter(!is.na(st_dimension(.))),
    fillColor = "yellow",
    stroke = F,
    fillOpacity = 0.5,
    highlightOptions = highlightOptions(
      fillOpacity = 1
    ),
    label = ~max_attached_4s12f_buildable_area,
    group = "Attached ADU Buildable Area"
  ) %>%
  addPolygons(
    data =
      data_4s6h12f %>%
      st_set_geometry("detached_4s6h12f_buildable_area_geometry") %>%
      dplyr::select(max_detached_4s6h12f_buildable_area) %>%
      st_transform(4326) %>%
      filter(!is.na(st_dimension(.))),
    fillColor = "green",
    stroke = F,
    fillOpacity = 0.5,
    highlightOptions = highlightOptions(
      fillOpacity = 1
    ),
    label = ~max_detached_4s6h12f_buildable_area,
    group = "Detached ADU Buildable Area"
  ) %>%
  addPolygons(
    data =
      data_4s6h12f %>%
      st_set_geometry("building_geometry") %>%
      dplyr::select(`GROSS BUILDING SQFT`) %>%
      st_transform(4326) %>%
      filter(!is.na(st_dimension(.))),
    fillColor = "tan",
    stroke = F,
    fillOpacity = 0.5,
    highlightOptions = highlightOptions(
      fillOpacity = 1
    ),
    label = ~`GROSS BUILDING SQFT`,
    group = "Existing Building"
  ) %>%
  addPolygons(
    data =
      data_4s6h12f %>%
      st_set_geometry("furthest_adu") %>%
      st_transform(4326) %>%
      filter(!is.na(st_dimension(.))),
    fillColor = "blue",
    stroke = F,
    fillOpacity = 0.5,
    highlightOptions = highlightOptions(
      fillOpacity = 1
    ),
    label = ~furthest_adu_area,
    group = "Example Detached ADU"
  ) %>%
  addLayersControl(
    baseGroups = c("Default", "Satellite"),
    overlayGroups = c("Parcels", "Existing Building", "Attached ADU<br>Buildable Area", "Detached ADU<br>Buildable Area", "Example<br>Detached ADU"),
    options = layersControlOptions(collapsed = T)
  ) %>% 
  setView(long,lat, zoom = 14)

saveWidget(map, "4s6h12f_map_detailed.html", selfcontained=FALSE)

C: Buildable Area Map

\(~\)

5.2 Scenario C: Summary Table

epa_parcels_adus_residential <-
  data_4s6h12f %>%
  st_set_geometry(NULL) %>%
  filter(
    Zoning %in%
      c(
        "R-HD (Multi-Family High Density Residential)",
        "R-LD (Single-Family Residential)",
        "R-MD-1 (Multi-Family Medium Density Residential)",
        "R-MD-2 (Multi-Family Medium Density Residential)",
        "R-UHD (Multi-Family Urban High Density Residential)",
        "Urban Residential"
      )
    & ADDRESS != "NA"
  )

epa_parcels_adus_residential_no9192 <-
  epa_parcels_adus_residential %>%
  filter(!PUC %in% c(91,92)) %>%
  filter(PUC <= 5 | PUC >= 89)

residential_adu_summary_C <-
  rbind(
    least_restrictive_data %>% rename(Amount.in.Flood.Zone = `Amount in Flood Zone`),
    data.frame(
      Description = "C: Estimated # of Parcels that can build only up to 800 sqft Attached ADUs",
      Amount = epa_parcels_adus_residential_no9192 %>% filter((APN %in% eligible_attached) & (is.na(max_attached_4s12f_buildable_area) | (max_attached_4s12f_buildable_area <= 800))) %>% nrow(),
      `Amount in Flood Zone` = epa_parcels_adus_residential_no9192 %>% filter((APN %in% eligible_attached) & (is.na(max_attached_4s12f_buildable_area) | (max_attached_4s12f_buildable_area <= 800))) %>%  filter(!is.na(FLD_ZONE)) %>% nrow()
    ),
    data.frame(
      Description = "C: Estimated # of Parcels that can build 800+ sqft Attached ADUs",
      Amount = epa_parcels_adus_residential_no9192 %>% filter(max_attached_4s12f_buildable_area > 800) %>% nrow(),
      `Amount in Flood Zone` = epa_parcels_adus_residential_no9192 %>% filter(max_attached_4s12f_buildable_area > 800) %>% filter(!is.na(FLD_ZONE)) %>% nrow()
    ),
    data.frame(
      Description = "C: Estimated # of Parcels that can build only up to 800 sqft Detached ADUs",
      Amount = epa_parcels_adus_residential_no9192 %>% filter((APN %in% eligible_detached) & (is.na(max_detached_4s6h12f_buildable_area) | (max_detached_4s6h12f_buildable_area <= 800))) %>% nrow(),
      `Amount in Flood Zone` = epa_parcels_adus_residential_no9192 %>% filter((APN %in% eligible_detached) & (is.na(max_detached_4s6h12f_buildable_area) | (max_detached_4s6h12f_buildable_area <= 800))) %>%  filter(!is.na(FLD_ZONE)) %>% nrow()
    ),
    data.frame(
      Description = "C: Estimated # of Parcels that can build 800+ sqft Detached ADUs",
      Amount = epa_parcels_adus_residential_no9192 %>% filter(max_detached_4s6h12f_buildable_area > 800) %>% nrow(),
      `Amount in Flood Zone` = epa_parcels_adus_residential_no9192 %>% filter(max_detached_4s6h12f_buildable_area > 800) %>% filter(!is.na(FLD_ZONE)) %>% nrow()
    )
  ) %>%
  rename(`Amount in Flood Zone` = Amount.in.Flood.Zone)

residential_adu_summary_C[10:11,1] <-
  c(
    "C: Estimated # Parcels that have been restricted to only up to 800 sqft Attached ADUs",
    "C: Estimated # Parcels that have been restricted to only up to 800 sqft Detached ADUs"
  )

residential_adu_summary_C[10:11,2:3] <-
  residential_adu_summary_C[c(6,8),2:3]-
  residential_adu_summary_C[c(2,4),2:3]

saveRDS(residential_adu_summary_C, "residential_adu_summary_C.rds")
# residential_adu_summary_C <- readRDS("residential_adu_summary_C.rds") 
C: ADU summary
Description Amount Amount in Flood Zone
Total # of ADU-Eligible Parcels 4157 1282
Min # of Parcels that can build only up to 800 sqft Attached ADUs 2934 990
Max # of Parcels that can build 800+ sqft Attached ADUs 543 164
Min # of Parcels that can build only up to 800 sqft Detached ADUs 675 165
Max # of Parcels that can build 800+ sqft Detached ADUs 2755 986
C: Estimated # of Parcels that can build only up to 800 sqft Attached ADUs 3039 1028
C: Estimated # of Parcels that can build 800+ sqft Attached ADUs 438 126
C: Estimated # of Parcels that can build only up to 800 sqft Detached ADUs 1599 486
C: Estimated # of Parcels that can build 800+ sqft Detached ADUs 1831 665
C: Estimated # Parcels that have been restricted to only up to 800 sqft Attached ADUs 105 38
C: Estimated # Parcels that have been restricted to only up to 800 sqft Detached ADUs 924 321

\(~\)

  • Red: ADUs Restricted by Local Discretion
  • Yellow: Detached ADUs with Flexibility Remaining (800-1200sqft)
  • Orange: Attached ADUs with Flexibility Remaining (800-1200sqft)
  • Purple: Existing Permitted ADU
  • Green: Ministerial ADU under Least Restrictive Scenario

5.3 Scenario C: Summary Map

adu_summary_map <-
  data_4s6h12f %>%
  mutate(
    Category =
      case_when(
        PUC %in% c(91,92) ~ "Existing Permitted ADU",
        max_detached_4s6h12f_buildable_area > 800 ~ "Parcels that can build 800+ sqft Detached ADUs",
        max_attached_4s12f_buildable_area > 800 ~ "Parcels that can build 800+ sqft Attached ADUs",
        APN %in% flexible_APNS ~ "Parcels that have been restricted to only up to 800 sqft ADUs",
        max_detached_4s6h12f_buildable_area <= 800 ~ "Parcels that can build only up to 800 sqft Detached ADUs",
        max_attached_4s12f_buildable_area <= 800 ~ "Parcels that can build only up to 800 sqft Attached ADUs",
        Zoning %in% 
      c(
        "R-HD (Multi-Family High Density Residential)",
        "R-LD (Single-Family Residential)",
        "R-MD-1 (Multi-Family Medium Density Residential)",
        "R-MD-2 (Multi-Family Medium Density Residential)",
        "R-UHD (Multi-Family Urban High Density Residential)",
        "Urban Residential"
      ) & (PUC <= 5 | PUC >= 89) ~ "Ministerial JADUs",
        TRUE ~ "Other"
      )
  ) %>% 
  mutate(
    Category = ifelse(Category == "Other", NA, Category)
  ) %>% 
  dplyr::select(ADDRESS,Category)

map <- leaflet() %>%
  addTiles(group = "Default") %>%
  addProviderTiles(providers$Esri.WorldImagery, group = "Satellite") %>%
  addPolygons(
    data = adu_summary_map %>% filter(Category == "Existing Permitted ADU") %>% st_transform(4326),
    fillColor = "purple",
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    fillOpacity = 0.75,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Existing Permitted ADU"
  ) %>%
  addPolygons(
    data = adu_summary_map %>% filter(Category == "Parcels that have been restricted to only up to 800 sqft ADUs") %>% st_transform(4326),
    fillColor = "red",
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    fillOpacity = 0.5,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Parcels that have been restricted to only up to 800 sqft ADUs"
  ) %>%
  addPolygons(
    data = adu_summary_map %>% filter(Category == "Parcels that can build 800+ sqft Detached ADUs") %>% st_transform(4326),
    fillColor = "yellow",
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    fillOpacity = 0.5,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Parcels that can build 800+ sqft Detached ADUs"
  ) %>%
  addPolygons(
    data = adu_summary_map %>% filter(Category == "Parcels that can build 800+ sqft Attached ADUs") %>% st_transform(4326),
    fillColor = "orange",
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    fillOpacity = 0.75,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Parcels that can build 800+ sqft Attached ADUs"
  ) %>%
  addPolygons(
    data = adu_summary_map %>% filter(Category == "Parcels that can build only up to 800 sqft Detached ADUs") %>% st_transform(4326),
    fillColor = "green",
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    fillOpacity = 0.75,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Parcels that can build only up to 800 sqft Detached ADUs"
  ) %>%
  addPolygons(
    data = adu_summary_map %>% filter(Category == "Parcels that can build only up to 800 sqft Attached ADUs") %>% st_transform(4326),
    fillColor = "green",
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    fillOpacity = 0.75,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Parcels that can build only up to 800 sqft Attached ADUs"
  ) %>%
  addPolygons(
    data = adu_summary_map %>% filter(Category == "Ministerial JADUs") %>% st_transform(4326),
    fillColor = "green",
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    fillOpacity = 0.75,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Ministerial JADUs"
  ) %>%
  addPolygons(
    data = epa_sfha_clip %>% st_transform(4326),
    fillColor = "blue",
    stroke = F,
    fillOpacity = 0.25,
    group = "Special Flood Hazard Area"
  ) %>%
  addLayersControl(
    baseGroups = c("Default", "Satellite"),
    overlayGroups = c("Existing Permitted ADU","Parcels that have been restricted to<br>only up to 800 sqft ADUs","Parcels that can build<br>800+ sqft Detached ADUs", "Parcels that can build<br>800+ sqft Attached ADUs","Parcels that can build only<br>up to 800 sqft Detached ADUs","Parcels that can build only<br>up to 800 sqft Attached ADUs", "Ministerial JADUs","Special Flood Hazard Area"),
    options = layersControlOptions(collapsed = T)
  ) %>% 
  setView(long,lat, zoom = 14)

saveWidget(map, "4s6h12f_map_summary.html", selfcontained=FALSE)

C: Parcel Summary

\(~\)

5.4 Scenario C: 800+ sqft ADU Distribution

hist_4s12f_2 <-
  data_4s6h12f %>% 
  dplyr::select(area = max_attached_4s12f_buildable_area) %>% 
  st_set_geometry(NULL) %>% 
  filter(area > 800) %>% 
  mutate(
    bin = floor(area/50)*50,
    scenario = "C"
  )

\(~\)

hist_4s6h12f <-
  data_4s6h12f %>% 
  dplyr::select(area = max_detached_4s6h12f_buildable_area) %>% 
  st_set_geometry(NULL) %>% 
  filter(area > 800) %>% 
  mutate(
    bin = floor(area/50)*50,
    scenario = "C"
  )

\(~\)

6 Scenario D: 12ft from street + Maintain EPA property setback + Limit max size

This scenario is exactly the same as C, except for a cap of 1,000 sqft. So the only change in our summary outputs will be in the distribution of 800+ sqft ADUs, shown below.

6.1 Scenario D: 800+ sqft ADU Distribution

scenario_D <-
  data_4s6h12f %>% 
  mutate(
    max_attached_4s12f_buildable_area = 
      pmin(
        1000,
        max_attached_4s12f_buildable_area
      ),
    max_detached_4s6h12f_buildable_area = 
      pmin(
        1000,
        max_detached_4s6h12f_buildable_area
      )
  )
hist_4s12f_3 <-
  scenario_D %>% 
  dplyr::select(area = max_attached_4s12f_buildable_area) %>% 
  st_set_geometry(NULL) %>% 
  filter(area > 800) %>% 
  mutate(
    bin = floor(area/50)*50,
    scenario = "D"
  )

\(~\)

hist_4s6h12f_2 <-
  scenario_D %>% 
  dplyr::select(area = max_detached_4s6h12f_buildable_area) %>% 
  st_set_geometry(NULL) %>% 
  filter(area > 800) %>% 
  mutate(
    bin = floor(area/50)*50,
    scenario = "D"
  )

\(~\)

7 Scenario E: 12ft from street + Maintain EPA property setback + Limit max size + Maintain rear space

Scenario E adds a minimum rear space of 50%, up to 750 sqft. To model this, we took the original backyard buildable area for each parcel and assume that to be “rear space”. For each parcel, we add a limitation that the attached or detached ADU can be no larger than 50% of the backyard buildable area or the backyard buildable area minus 750 sqft, whichever is greater.

The detailed map will be the same as Scenario C (except for some example ADUs which may be too big in this scenario). We will now show the summary table, summary map, and histograms.

7.1 Scenario E: Summary Table

scenario_E <-
  scenario_D %>% 
  mutate(
    rear_max =
      detached_4s4hXf_buildable_area_geometry %>% st_buffer(4, joinStyle = "BEVEL") %>% st_area(.) %>% as.numeric(),
    max_attached_4s12f_buildable_area = 
      pmin(
        pmax(
          rear_max/2,
          rear_max-750
        ),
        max_attached_4s12f_buildable_area
      ),
    max_detached_4s6h12f_buildable_area =
      pmin(
        pmax(
          rear_max/2,
          rear_max-750
        ),
        max_detached_4s6h12f_buildable_area
      )
  )
epa_parcels_adus_residential <-
  scenario_E %>%
  st_set_geometry(NULL) %>%
  filter(
    Zoning %in%
      c(
        "R-HD (Multi-Family High Density Residential)",
        "R-LD (Single-Family Residential)",
        "R-MD-1 (Multi-Family Medium Density Residential)",
        "R-MD-2 (Multi-Family Medium Density Residential)",
        "R-UHD (Multi-Family Urban High Density Residential)",
        "Urban Residential"
      )
    & ADDRESS != "NA"
  )

epa_parcels_adus_residential_no9192 <-
  epa_parcels_adus_residential %>%
  filter(!PUC %in% c(91,92)) %>%
  filter(PUC <= 5 | PUC >= 89)

residential_adu_summary_E <-
  rbind(
    least_restrictive_data %>% rename(Amount.in.Flood.Zone = `Amount in Flood Zone`),
    data.frame(
      Description = "E: Estimated # of Parcels that can build only up to 800 sqft Attached ADUs",
      Amount = epa_parcels_adus_residential_no9192 %>% filter((APN %in% eligible_attached) & (is.na(max_attached_4s12f_buildable_area) | (max_attached_4s12f_buildable_area <= 800))) %>% nrow(),
      `Amount in Flood Zone` = epa_parcels_adus_residential_no9192 %>% filter((APN %in% eligible_attached) & (is.na(max_attached_4s12f_buildable_area) | (max_attached_4s12f_buildable_area <= 800))) %>%  filter(!is.na(FLD_ZONE)) %>% nrow()
    ),
    data.frame(
      Description = "E: Estimated # of Parcels that can build 800+ sqft Attached ADUs",
      Amount = epa_parcels_adus_residential_no9192 %>% filter(max_attached_4s12f_buildable_area > 800) %>% nrow(),
      `Amount in Flood Zone` = epa_parcels_adus_residential_no9192 %>% filter(max_attached_4s12f_buildable_area > 800) %>% filter(!is.na(FLD_ZONE)) %>% nrow()
    ),
    data.frame(
      Description = "E: Estimated # of Parcels that can build only up to 800 sqft Detached ADUs",
      Amount = epa_parcels_adus_residential_no9192 %>% filter((APN %in% eligible_detached) & (is.na(max_detached_4s6h12f_buildable_area) | (max_detached_4s6h12f_buildable_area <= 800))) %>% nrow(),
      `Amount in Flood Zone` = epa_parcels_adus_residential_no9192 %>% filter((APN %in% eligible_detached) & (is.na(max_detached_4s6h12f_buildable_area) | (max_detached_4s6h12f_buildable_area <= 800))) %>%  filter(!is.na(FLD_ZONE)) %>% nrow()
    ),
    data.frame(
      Description = "E: Estimated # of Parcels that can build 800+ sqft Detached ADUs",
      Amount = epa_parcels_adus_residential_no9192 %>% filter(max_detached_4s6h12f_buildable_area > 800) %>% nrow(),
      `Amount in Flood Zone` = epa_parcels_adus_residential_no9192 %>% filter(max_detached_4s6h12f_buildable_area > 800) %>% filter(!is.na(FLD_ZONE)) %>% nrow()
    )
  ) %>%
  rename(`Amount in Flood Zone` = Amount.in.Flood.Zone)

residential_adu_summary_E[10:11,1] <-
  c(
    "E: Estimated # Parcels that have been restricted to only up to 800 sqft Attached ADUs",
    "E: Estimated # Parcels that have been restricted to only up to 800 sqft Detached ADUs"
  )

residential_adu_summary_E[10:11,2:3] <-
  residential_adu_summary_E[c(6,8),2:3]-
  residential_adu_summary_E[c(2,4),2:3]

saveRDS(residential_adu_summary_E, "residential_adu_summary_E.rds")
# residential_adu_summary_E <- readRDS("residential_adu_summary_E.rds") 
E: ADU summary
Description Amount Amount in Flood Zone
Total # of ADU-Eligible Parcels 4157 1282
Min # of Parcels that can build only up to 800 sqft Attached ADUs 2934 990
Max # of Parcels that can build 800+ sqft Attached ADUs 543 164
Min # of Parcels that can build only up to 800 sqft Detached ADUs 675 165
Max # of Parcels that can build 800+ sqft Detached ADUs 2755 986
E: Estimated # of Parcels that can build only up to 800 sqft Attached ADUs 3165 1058
E: Estimated # of Parcels that can build 800+ sqft Attached ADUs 312 96
E: Estimated # of Parcels that can build only up to 800 sqft Detached ADUs 1742 533
E: Estimated # of Parcels that can build 800+ sqft Detached ADUs 1688 618
E: Estimated # Parcels that have been restricted to only up to 800 sqft Attached ADUs 231 68
E: Estimated # Parcels that have been restricted to only up to 800 sqft Detached ADUs 1067 368

\(~\)

  • Red: ADUs Restricted by Local Discretion
  • Yellow: Detached ADUs with Flexibility Remaining (800-1200sqft)
  • Orange: Attached ADUs with Flexibility Remaining (800-1200sqft)
  • Purple: Existing Permitted ADU
  • Green: Ministerial ADU under Least Restrictive Scenario

7.2 Scenario E: Summary Map

adu_summary_map <-
  scenario_E %>%
  mutate(
    Category =
      case_when(
        PUC %in% c(91,92) ~ "Existing Permitted ADU",
        max_detached_4s6h12f_buildable_area > 800 ~ "Parcels that can build 800+ sqft Detached ADUs",
        max_attached_4s12f_buildable_area > 800 ~ "Parcels that can build 800+ sqft Attached ADUs",
        APN %in% flexible_APNS ~ "Parcels that have been restricted to only up to 800 sqft ADUs",
        max_detached_4s6h12f_buildable_area <= 800 ~ "Parcels that can build only up to 800 sqft Detached ADUs",
        max_attached_4s12f_buildable_area <= 800 ~ "Parcels that can build only up to 800 sqft Attached ADUs",
        Zoning %in% 
      c(
        "R-HD (Multi-Family High Density Residential)",
        "R-LD (Single-Family Residential)",
        "R-MD-1 (Multi-Family Medium Density Residential)",
        "R-MD-2 (Multi-Family Medium Density Residential)",
        "R-UHD (Multi-Family Urban High Density Residential)",
        "Urban Residential"
      ) & (PUC <= 5 | PUC >= 89) ~ "Ministerial JADUs",
        TRUE ~ "Other"
      )
  ) %>% 
  mutate(
    Category = ifelse(Category == "Other", NA, Category)
  ) %>% 
  dplyr::select(ADDRESS,Category)

map <- leaflet() %>%
  addTiles(group = "Default") %>%
  addProviderTiles(providers$Esri.WorldImagery, group = "Satellite") %>%
  addPolygons(
    data = adu_summary_map %>% filter(Category == "Existing Permitted ADU") %>% st_transform(4326),
    fillColor = "purple",
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    fillOpacity = 0.75,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Existing Permitted ADU"
  ) %>%
  addPolygons(
    data = adu_summary_map %>% filter(Category == "Parcels that have been restricted to only up to 800 sqft ADUs") %>% st_transform(4326),
    fillColor = "red",
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    fillOpacity = 0.5,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Parcels that have been restricted to only up to 800 sqft ADUs"
  ) %>%
  addPolygons(
    data = adu_summary_map %>% filter(Category == "Parcels that can build 800+ sqft Detached ADUs") %>% st_transform(4326),
    fillColor = "yellow",
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    fillOpacity = 0.5,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Parcels that can build 800+ sqft Detached ADUs"
  ) %>%
  addPolygons(
    data = adu_summary_map %>% filter(Category == "Parcels that can build 800+ sqft Attached ADUs") %>% st_transform(4326),
    fillColor = "orange",
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    fillOpacity = 0.75,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Parcels that can build 800+ sqft Attached ADUs"
  ) %>%
  addPolygons(
    data = adu_summary_map %>% filter(Category == "Parcels that can build only up to 800 sqft Detached ADUs") %>% st_transform(4326),
    fillColor = "green",
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    fillOpacity = 0.75,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Parcels that can build only up to 800 sqft Detached ADUs"
  ) %>%
  addPolygons(
    data = adu_summary_map %>% filter(Category == "Parcels that can build only up to 800 sqft Attached ADUs") %>% st_transform(4326),
    fillColor = "green",
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    fillOpacity = 0.75,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Parcels that can build only up to 800 sqft Attached ADUs"
  ) %>%
  addPolygons(
    data = adu_summary_map %>% filter(Category == "Ministerial JADUs") %>% st_transform(4326),
    fillColor = "green",
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    fillOpacity = 0.75,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Ministerial JADUs"
  ) %>%
  addPolygons(
    data = epa_sfha_clip %>% st_transform(4326),
    fillColor = "blue",
    stroke = F,
    fillOpacity = 0.25,
    group = "Special Flood Hazard Area"
  ) %>%
  addLayersControl(
    baseGroups = c("Default", "Satellite"),
    overlayGroups = c("Existing Permitted ADU","Parcels that have been restricted to<br>only up to 800 sqft ADUs","Parcels that can build<br>800+ sqft Detached ADUs", "Parcels that can build<br>800+ sqft Attached ADUs","Parcels that can build only<br>up to 800 sqft Detached ADUs","Parcels that can build only<br>up to 800 sqft Attached ADUs", "Ministerial JADUs","Special Flood Hazard Area"),
    options = layersControlOptions(collapsed = T)
  ) %>% 
  setView(long,lat, zoom = 14)

saveWidget(map, "scenario_E_map_summary.html", selfcontained=FALSE)

E: Parcel Summary

\(~\)

7.3 Scenario E: 800+ sqft ADU Distribution

hist_4s12f_4 <-
  scenario_E %>% 
  dplyr::select(area = max_attached_4s12f_buildable_area) %>% 
  st_set_geometry(NULL) %>% 
  filter(area > 800) %>% 
  mutate(
    bin = floor(area/50)*50,
    scenario = "E"
  )

\(~\)

hist_4s6h12f_3 <-
  scenario_E %>% 
  dplyr::select(area = max_detached_4s6h12f_buildable_area) %>% 
  st_set_geometry(NULL) %>% 
  filter(area > 800) %>% 
  mutate(
    bin = floor(area/50)*50,
    scenario = "E"
  )

\(~\)

8 Scenario F: 12ft from street + Maintain EPA property setback + Limit max size + Maintain rear space + Limit SC/FAR

Scenario F adds a maximum site coverage of 50% and floor area ratio 55%. We will model both effects at the same time. Site coverage is straightforward because we have easy access to site area for each parcel. Floor area ratio is based on the best information we have about the gross building square footage which comes from the San Mateo County Tax Assessor’s building characteristics data (but we have less confidence about this data).

The detailed map will be the same as Scenario C (except for some example ADUs which may be too big in this scenario). We will now show the summary table, summary map, and histograms.

8.1 Scenario F: Summary Table

scenario_F <-
  scenario_E %>% 
  mutate(
    max_attached_4s12f_buildable_area = 
      pmin(
        parcel_area/2,
        `GROSS BUILDING SQFT`*0.55,
        max_attached_4s12f_buildable_area
      ),
    max_detached_4s6h12f_buildable_area =
      pmin(
        parcel_area/2,
        `GROSS BUILDING SQFT`*0.55,
        max_detached_4s6h12f_buildable_area
      )
  )
epa_parcels_adus_residential <-
  scenario_F %>%
  st_set_geometry(NULL) %>%
  filter(
    Zoning %in%
      c(
        "R-HD (Multi-Family High Density Residential)",
        "R-LD (Single-Family Residential)",
        "R-MD-1 (Multi-Family Medium Density Residential)",
        "R-MD-2 (Multi-Family Medium Density Residential)",
        "R-UHD (Multi-Family Urban High Density Residential)",
        "Urban Residential"
      )
    & ADDRESS != "NA"
  )

epa_parcels_adus_residential_no9192 <-
  epa_parcels_adus_residential %>%
  filter(!PUC %in% c(91,92)) %>%
  filter(PUC <= 5 | PUC >= 89)

residential_adu_summary_F <-
  rbind(
    least_restrictive_data %>% rename(Amount.in.Flood.Zone = `Amount in Flood Zone`),
    data.frame(
      Description = "F: Estimated # of Parcels that can build only up to 800 sqft Attached ADUs",
      Amount = epa_parcels_adus_residential_no9192 %>% filter((APN %in% eligible_attached) & (is.na(max_attached_4s12f_buildable_area) | (max_attached_4s12f_buildable_area <= 800))) %>% nrow(),
      `Amount in Flood Zone` = epa_parcels_adus_residential_no9192 %>% filter((APN %in% eligible_attached) & (is.na(max_attached_4s12f_buildable_area) | (max_attached_4s12f_buildable_area <= 800))) %>%  filter(!is.na(FLD_ZONE)) %>% nrow()
    ),
    data.frame(
      Description = "F: Estimated # of Parcels that can build 800+ sqft Attached ADUs",
      Amount = epa_parcels_adus_residential_no9192 %>% filter(max_attached_4s12f_buildable_area > 800) %>% nrow(),
      `Amount in Flood Zone` = epa_parcels_adus_residential_no9192 %>% filter(max_attached_4s12f_buildable_area > 800) %>% filter(!is.na(FLD_ZONE)) %>% nrow()
    ),
    data.frame(
      Description = "F: Estimated # of Parcels that can build only up to 800 sqft Detached ADUs",
      Amount = epa_parcels_adus_residential_no9192 %>% filter((APN %in% eligible_detached) & (is.na(max_detached_4s6h12f_buildable_area) | (max_detached_4s6h12f_buildable_area <= 800))) %>% nrow(),
      `Amount in Flood Zone` = epa_parcels_adus_residential_no9192 %>% filter((APN %in% eligible_detached) & (is.na(max_detached_4s6h12f_buildable_area) | (max_detached_4s6h12f_buildable_area <= 800))) %>%  filter(!is.na(FLD_ZONE)) %>% nrow()
    ),
    data.frame(
      Description = "F: Estimated # of Parcels that can build 800+ sqft Detached ADUs",
      Amount = epa_parcels_adus_residential_no9192 %>% filter(max_detached_4s6h12f_buildable_area > 800) %>% nrow(),
      `Amount in Flood Zone` = epa_parcels_adus_residential_no9192 %>% filter(max_detached_4s6h12f_buildable_area > 800) %>% filter(!is.na(FLD_ZONE)) %>% nrow()
    )
  ) %>%
  rename(`Amount in Flood Zone` = Amount.in.Flood.Zone)

residential_adu_summary_F[10:11,1] <-
  c(
    "F: Estimated # Parcels that have been restricted to only up to 800 sqft Attached ADUs",
    "F: Estimated # Parcels that have been restricted to only up to 800 sqft Detached ADUs"
  )

residential_adu_summary_F[10:11,2:3] <-
  residential_adu_summary_F[c(6,8),2:3]-
  residential_adu_summary_F[c(2,4),2:3]

saveRDS(residential_adu_summary_F, "residential_adu_summary_F.rds")
# residential_adu_summary_F <- readRDS("residential_adu_summary_F.rds") 
F: ADU summary
Description Amount Amount in Flood Zone
Total # of ADU-Eligible Parcels 4157 1282
Min # of Parcels that can build only up to 800 sqft Attached ADUs 2934 990
Max # of Parcels that can build 800+ sqft Attached ADUs 543 164
Min # of Parcels that can build only up to 800 sqft Detached ADUs 675 165
Max # of Parcels that can build 800+ sqft Detached ADUs 2755 986
F: Estimated # of Parcels that can build only up to 800 sqft Attached ADUs 3172 1060
F: Estimated # of Parcels that can build 800+ sqft Attached ADUs 305 94
F: Estimated # of Parcels that can build only up to 800 sqft Detached ADUs 3047 1029
F: Estimated # of Parcels that can build 800+ sqft Detached ADUs 383 122
F: Estimated # Parcels that have been restricted to only up to 800 sqft Attached ADUs 238 70
F: Estimated # Parcels that have been restricted to only up to 800 sqft Detached ADUs 2372 864

\(~\)

  • Red: ADUs Restricted by Local Discretion
  • Yellow: Detached ADUs with Flexibility Remaining (800-1200sqft)
  • Orange: Attached ADUs with Flexibility Remaining (800-1200sqft)
  • Purple: Existing Permitted ADU
  • Green: Ministerial ADU under Least Restrictive Scenario

8.2 Scenario F: Summary Map

adu_summary_map <-
  scenario_F %>%
  mutate(
    Category =
      case_when(
        PUC %in% c(91,92) ~ "Existing Permitted ADU",
        max_detached_4s6h12f_buildable_area > 800 ~ "Parcels that can build 800+ sqft Detached ADUs",
        max_attached_4s12f_buildable_area > 800 ~ "Parcels that can build 800+ sqft Attached ADUs",
        APN %in% flexible_APNS ~ "Parcels that have been restricted to only up to 800 sqft ADUs",
        max_detached_4s6h12f_buildable_area <= 800 ~ "Parcels that can build only up to 800 sqft Detached ADUs",
        max_attached_4s12f_buildable_area <= 800 ~ "Parcels that can build only up to 800 sqft Attached ADUs",
        Zoning %in% 
      c(
        "R-HD (Multi-Family High Density Residential)",
        "R-LD (Single-Family Residential)",
        "R-MD-1 (Multi-Family Medium Density Residential)",
        "R-MD-2 (Multi-Family Medium Density Residential)",
        "R-UHD (Multi-Family Urban High Density Residential)",
        "Urban Residential"
      ) & (PUC <= 5 | PUC >= 89) ~ "Ministerial JADUs",
        TRUE ~ "Other"
      )
  ) %>% 
  mutate(
    Category = ifelse(Category == "Other", NA, Category)
  ) %>% 
  dplyr::select(ADDRESS,Category)

map <- leaflet() %>%
  addTiles(group = "Default") %>%
  addProviderTiles(providers$Esri.WorldImagery, group = "Satellite") %>%
  addPolygons(
    data = adu_summary_map %>% filter(Category == "Existing Permitted ADU") %>% st_transform(4326),
    fillColor = "purple",
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    fillOpacity = 0.75,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Existing Permitted ADU"
  ) %>%
  addPolygons(
    data = adu_summary_map %>% filter(Category == "Parcels that have been restricted to only up to 800 sqft ADUs") %>% st_transform(4326),
    fillColor = "red",
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    fillOpacity = 0.5,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Parcels that have been restricted to only up to 800 sqft ADUs"
  ) %>%
  addPolygons(
    data = adu_summary_map %>% filter(Category == "Parcels that can build 800+ sqft Detached ADUs") %>% st_transform(4326),
    fillColor = "yellow",
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    fillOpacity = 0.5,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Parcels that can build 800+ sqft Detached ADUs"
  ) %>%
  addPolygons(
    data = adu_summary_map %>% filter(Category == "Parcels that can build 800+ sqft Attached ADUs") %>% st_transform(4326),
    fillColor = "orange",
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    fillOpacity = 0.75,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Parcels that can build 800+ sqft Attached ADUs"
  ) %>%
  addPolygons(
    data = adu_summary_map %>% filter(Category == "Parcels that can build only up to 800 sqft Detached ADUs") %>% st_transform(4326),
    fillColor = "green",
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    fillOpacity = 0.75,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Parcels that can build only up to 800 sqft Detached ADUs"
  ) %>%
  addPolygons(
    data = adu_summary_map %>% filter(Category == "Parcels that can build only up to 800 sqft Attached ADUs") %>% st_transform(4326),
    fillColor = "green",
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    fillOpacity = 0.75,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Parcels that can build only up to 800 sqft Attached ADUs"
  ) %>%
  addPolygons(
    data = adu_summary_map %>% filter(Category == "Ministerial JADUs") %>% st_transform(4326),
    fillColor = "green",
    color = "white",
    weight = 0.5,
    opacity = 0.5,
    fillOpacity = 0.75,
    highlightOptions = highlightOptions(
      weight = 2,
      opacity = 1
    ),
    label = ~ADDRESS,
    group = "Ministerial JADUs"
  ) %>%
  addPolygons(
    data = epa_sfha_clip %>% st_transform(4326),
    fillColor = "blue",
    stroke = F,
    fillOpacity = 0.25,
    group = "Special Flood Hazard Area"
  ) %>%
  addLayersControl(
    baseGroups = c("Default", "Satellite"),
    overlayGroups = c("Existing Permitted ADU","Parcels that have been restricted to<br>only up to 800 sqft ADUs","Parcels that can build<br>800+ sqft Detached ADUs", "Parcels that can build<br>800+ sqft Attached ADUs","Parcels that can build only<br>up to 800 sqft Detached ADUs","Parcels that can build only<br>up to 800 sqft Attached ADUs", "Ministerial JADUs","Special Flood Hazard Area"),
    options = layersControlOptions(collapsed = T)
  ) %>% 
  setView(long,lat, zoom = 14)

saveWidget(map, "scenario_F_map_summary.html", selfcontained=FALSE)

F: Parcel Summary

\(~\)

8.3 Scenario F: 800+ sqft ADU Distribution

hist_4s12f_5 <-
  scenario_F %>% 
  dplyr::select(area = max_attached_4s12f_buildable_area) %>% 
  st_set_geometry(NULL) %>% 
  filter(area > 800) %>% 
  mutate(
    bin = floor(area/50)*50,
    scenario = "F"
  )

\(~\)

hist_4s6h12f_4 <-
  scenario_F %>% 
  dplyr::select(area = max_detached_4s6h12f_buildable_area) %>% 
  st_set_geometry(NULL) %>% 
  filter(area > 800) %>% 
  mutate(
    bin = floor(area/50)*50,
    scenario = "F"
  )

summary_hist_attached <-
  rbind(
    data_4s4h %>% 
      dplyr::select(APN,area = max_attached_4s_buildable_area) %>% 
      st_set_geometry(NULL) %>%
      mutate(
        area = pmax(750,area),
        bin = floor(area/50)*50,
        scenario = "A"
      ),
    data_4s4h12f %>% 
      dplyr::select(APN,area = max_attached_4s12f_buildable_area) %>% 
      st_set_geometry(NULL) %>%
      mutate(
        area = pmax(750,area),
        bin = floor(area/50)*50,
        scenario = "B"
      ),
    data_4s6h12f %>% 
      dplyr::select(APN,area = max_attached_4s12f_buildable_area) %>% 
      st_set_geometry(NULL) %>%
      mutate(
        area = pmax(750,area),
        bin = floor(area/50)*50,
        scenario = "C"
      ),
    scenario_D %>% 
      dplyr::select(APN,area = max_attached_4s12f_buildable_area) %>% 
      st_set_geometry(NULL) %>%
      mutate(
        area = pmax(750,area),
        bin = floor(area/50)*50,
        scenario = "D"
      ),
    scenario_E %>% 
      dplyr::select(APN,area = max_attached_4s12f_buildable_area) %>% 
      st_set_geometry(NULL) %>%
      mutate(
        area = pmax(750,area),
        bin = floor(area/50)*50,
        scenario = "E"
      ),
    scenario_F %>% 
      dplyr::select(APN,area = max_attached_4s12f_buildable_area) %>% 
      st_set_geometry(NULL) %>%
      mutate(
        area = pmax(750,area),
        bin = floor(area/50)*50,
        scenario = "F"
      )
  ) %>% 
  filter(APN %in% eligible_attached) %>% 
  mutate(
    bin = 
      ifelse(
        is.na(bin),
        750,
        bin
      )
  )

summary_hist_detached <-
  rbind(
    data_4s4h %>% 
      dplyr::select(APN,area = max_detached_4s4h_buildable_area) %>% 
      st_set_geometry(NULL) %>%
      mutate(
        area = pmax(750,area),
        bin = floor(area/50)*50,
        scenario = "A"
      ),
    data_4s4h12f %>% 
      dplyr::select(APN,area = max_detached_4s4h12f_buildable_area) %>% 
      st_set_geometry(NULL) %>%
      mutate(
        area = pmax(750,area),
        bin = floor(area/50)*50,
        scenario = "B"
      ),
    data_4s6h12f %>% 
      dplyr::select(APN,area = max_detached_4s6h12f_buildable_area) %>% 
      st_set_geometry(NULL) %>%
      mutate(
        area = pmax(750,area),
        bin = floor(area/50)*50,
        scenario = "C"
      ),
    scenario_D %>% 
      dplyr::select(APN,area = max_detached_4s6h12f_buildable_area) %>% 
      st_set_geometry(NULL) %>%
      mutate(
        area = pmax(750,area),
        bin = floor(area/50)*50,
        scenario = "D"
      ),
    scenario_E %>% 
      dplyr::select(APN,area = max_detached_4s6h12f_buildable_area) %>% 
      st_set_geometry(NULL) %>%
      mutate(
        area = pmax(750,area),
        bin = floor(area/50)*50,
        scenario = "E"
      ),
    scenario_F %>% 
      dplyr::select(APN,area = max_detached_4s6h12f_buildable_area) %>% 
      st_set_geometry(NULL) %>%
      mutate(
        area = pmax(750,area),
        bin = floor(area/50)*50,
        scenario = "F"
      )
  ) %>% 
  filter(APN %in% eligible_detached) %>% 
  mutate(
    bin = 
      ifelse(
        is.na(bin),
        750,
        bin
      )
  )
  
  

final_summary <-
  rbind(
    least_restrictive_data[,1:2],
    residential_adu_summary_B[6:9,1:2],
    residential_adu_summary_C[6:9,1:2],
    residential_adu_summary_C[6:9,1:2],
    residential_adu_summary_E[6:9,1:2],
    residential_adu_summary_F[6:9,1:2]
  ) %>% 
  mutate(
    Amount = 
      floor(Amount/10)*10
  )

\(~\)