Getting started with ‘cartographer’

Cartographer comes with one example dataset registered (the map of North Carolina provided by the {sf} package where the feature names correspond to counties). If you install the {maps} package, some additional maps are available.

library(cartographer)

# list registered datasets
feature_types()
#>  [1] "maps.italy"                    "rnaturalearth.countries_hires"
#>  [3] "maps.lakes"                    "rnaturalearth.countries"      
#>  [5] "rnaturalearth.australia"       "maps.nz"                      
#>  [7] "maps.world"                    "maps.state"                   
#>  [9] "sf.nc"                         "maps.france"

# list feature names in one of the datasets
head(feature_names("sf.nc"))
#> [1] "Ashe"        "Alleghany"   "Surry"       "Currituck"   "Northampton"
#> [6] "Hertford"

Our example dataset has a column called county with the name of the county and a column type that is one of two values. We’d like to draw a map that shows us where the entries are by type.

head(nc_type_example_1)
#>    county type
#> 1  BERTIE    B
#> 2  BERTIE    A
#> 3  BERTIE    A
#> 4 PAMLICO    B
#> 5  WARREN    B
#> 6 PAMLICO    B

We want to convert our data frame into a spatial data frame, then we’ll be able to use standard {ggplot2} tools to draw our map. Cartographer just needs to know which of its maps we’re using ("sf.nc", although it’s able to guess that if we don’t specify it), and which column has the feature names (county):

add_geometry(nc_type_example_1, county, feature_type = "sf.nc")
#> Error in `resolve_feature_names()`:
#> ! location contains unexpected values
#> ✖ The unknown values are PAMILCO.
#> ℹ Expected sf.nc names like Ashe, Alleghany, and Surry.
#> ℹ See feature_names('sf.nc') for the full list.

What happened? The matching of county names was case-insensitive, so that wasn’t the issue; it looks like our data contained a typo. One of the rows contains "PAMILCO" instead of "PAMLICO" in the county column.

library(dplyr, warn.conflicts = FALSE)

# cleaned dataset
nc_type_example_1_fixed <-
  nc_type_example_1 |>
  mutate(county = case_match(county, "PAMILCO" ~ "PAMLICO", .default = county))

add_geometry(nc_type_example_1_fixed, county, feature_type = "sf.nc")
#> Simple feature collection with 50 features and 2 fields
#> Geometry type: MULTIPOLYGON
#> Dimension:     XY
#> Bounding box:  xmin: -82.07776 ymin: 34.98222 xmax: -76.50713 ymax: 36.55629
#> Geodetic CRS:  NAD27
#> First 10 features:
#>         county type                       geometry
#> 1       BERTIE    B MULTIPOLYGON (((-76.78307 3...
#> 2       BERTIE    A MULTIPOLYGON (((-76.78307 3...
#> 3       BERTIE    A MULTIPOLYGON (((-76.78307 3...
#> 4      PAMLICO    B MULTIPOLYGON (((-76.94324 3...
#> 5       WARREN    B MULTIPOLYGON (((-78.30876 3...
#> 6      PAMLICO    B MULTIPOLYGON (((-76.94324 3...
#> 7  NORTHAMPTON    B MULTIPOLYGON (((-77.21767 3...
#> 8      PAMLICO    A MULTIPOLYGON (((-76.94324 3...
#> 9      PAMLICO    B MULTIPOLYGON (((-76.94324 3...
#> 10     PAMLICO    B MULTIPOLYGON (((-76.94324 3...

Now that we have our spatial geometry added, we can go ahead and plot it, adding a layer that uses the full "sf.nc" dataset to give us the base map:

library(ggplot2)

nc_type_example_1_fixed |>
  count(county, type) |>
  add_geometry(county, feature_type = "sf.nc") |>
  ggplot() +
  geom_sf(data = map_sf("sf.nc")) +
  geom_sf(aes(fill = n)) +
  facet_wrap(vars(type))

The package {ggautomap} uses cartographer to provide some new {ggplot2} geoms that transparently attach the map data.