tract_choropleth throws error when used with reference_path=TRUE; similar code works at state, county and zip level

27 Views Asked by At

I am attempting to create a basic census tract choropleth with map overlay through setting parameter reference_map=TRUE:

library(choroplethr)
library(choroplethrMaps)
library(choroplethrZip)
library(ggmap)

data(df_pop_ny_tract)
tract_choropleth(df_pop_ny_tract,state_name="new york",num_colors=1
                             ,reference_map=TRUE)

Which when run throws this error:

Error in if (lon < -180 || lon > 180) cli::cli_abort("Longitude of center must be between -180 and 180 degrees. Note {.pkg ggmap} uses lon/lat, not lat/lon."): missing value where TRUE/FALSE needed

reference_map=TRUE works fine with other levels of choropleths, from state all the way to zip, once I registered with Google and followed the solution posted here: bug when creating reference maps with choroplethr. For example, the analogous choroplethr call at the zip level produces the choropleth with map overlay:

library(choroplethr)
library(choroplethrMaps)
library(choroplethrZip)
library(ggmap)

data(df_pop_zip)
 zip_choropleth(df_pop_zip,state_zoom=c("new york")
                ,num_colors=1
                 ,reference_map=TRUE)

output map from zip_choropleth with reference_map=TRUE

The tract_choropleth also works fine where reference_map=FALSE

library(choroplethr)
library(choroplethrMaps)
library(choroplethrZip)
library(ggmap)

data(df_pop_ny_tract)
tract_choropleth(df_pop_ny_tract,state_name="new york",num_colors=1
                             ,reference_map=FALSE)

output map from tract_choropleth with reference_map=FALSE

My environment is:

## R version 4.3.2 (2023-10-31)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 22.04.3 LTS
## 
## Matrix products: default
## BLAS:   /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.10.0 
## LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.10.0
## 
## locale:
##  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
##  [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
##  [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
##  [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
##  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
## [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       
## 
## time zone: America/New_York
## tzcode source: system (glibc)
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] ggmap_4.0.0           ggplot2_3.4.4         choroplethrZip_1.5.0 
## [4] choroplethrMaps_1.0.1 choroplethr_3.7.2     acs_2.1.4            
## [7] XML_3.99-0.16.1       stringr_1.5.1         knitr_1.45           
## 
1

There are 1 best solutions below

1
cristian-vargas On

So it appears that upon examining the source code of choroplethr, the problem is likely caused by differing map projections between df_pop_ny_tract (which has a geodetic CRS = NAD 83 / EPSG: 4269) and the reference_map called inside of the tract_choropleth() function (which uses ggmaps in the background to return a basemap that has a geodetic CRS = WGS 84 & EPSG: 3857, aka Psuedo-Mercator).

All of this is complicated by the fact that df_pop_ny_tract is purely an object of the data.frame class. It's only during the call to the tract_choropleth() function that it's joined by region column to the GEOID column of the TIGER shapefile called internally by the tigris package, at which point it inherits that CRS. I think it may be easier to directly use many of the underlying packages to achieve your desired result instead of the choroplethr family of packages. Here's how I recreated your desired output using sf, tidycensus, ggplot2, and ggmap.

library(ggplot2)
library(ggmap)
library(sf)
library(tidycensus)

# Recreate NY tract population
# Requires registering for free Census API key
df_ny_pop_tract <- tidycensus::get_decennial(
  geography = "tract",
  state = "NY",
  variables = c("Total Population" = "P1_001N"),
  geometry = TRUE,
  output = "tidy",
  sumfile = "pl"
)

# Get bounding box for use in ggmap function
# st_bbox(df_ny_pop_tract)

# Get reference map (from Google or Stadia Maps)
# Use get_googlemap() for Google
# I don't have a Google Maps API key so I use Stadia Maps
ny_reference_map <- ggmap::get_stadiamap(
  bbox = c(left = -79.76215, bottom = 40.49610, right = -71.85621, top = 45.01585),
  zoom = 10,
  maptype = "alidade_smooth"
)

# Draw map
ggmap(ny_reference_map) +
  geom_sf(data = df_ny_pop_tract, aes(fill = value), inherit.aes = FALSE) +
  theme_void()

Choropleth of New York with reference map

A key part of the code at the end is to specify inherit.aes=FALSE to deal with the different map projections. It's a different basemap since I don't have a Google Maps API key anymore, but you should be able to substitute a Google Maps basemap with ggmap::get_googlemap(). Hope this helps!