knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)

local({
  hook_output <- knitr::knit_hooks$get('output')
  knitr::knit_hooks$set(output = function(x, options) {
    if (!is.null(options$max.height)) options$attr.output <- c(
      options$attr.output,
      sprintf('style="max-height: %s;"', options$max.height)
    )
    hook_output(x, options)
  })
})

When mapping with simple feature objects, it can be helpful to create a list of related objects. A list could be:

To ease the use of simple feature lists, overedge includes several dedicated functions for creating or modifying these lists along with support for sf lists in most of the key functions.

library(overedge)
library(ggplot2)
library(dplyr)
library(patchwork)

Utility functions for working with sf lists

To start, as_sf_list can turn a sf object into a list and optionally use a grouping column to define how the list is structured.

nc <- sf::st_read(system.file("shape/nc.shp", package = "sf"), quiet = TRUE)

nc_list_name <-
  as_sf_list(x = nc, col = "NAME")

str(nc_list_name)

If you use a grouping column, the list names are taken from the value of the grouping column:

# List names match names from grouping column 
names(nc_list_name)

Alternatively, a character string with the same length as the list can be provided to the "nm" parameter:

nc_list <-
  as_sf_list(nc, nm = "North Carolina")

str(nc_list)

You can check if an object is an sf list using is_sf_list.

is_sf_list(nc_list)

Both as_sf and as_bbox can convert simple feature lists to either a single simple feature data frame or a single bounding box.

as_sf(nc_list_name) %>%
  slice_head(n = 4)

as_bbox(nc_list)

Modifying and mapping sf lists

Other key functions work with sf list objects by applying the function to each object in the list and maintain the list format, including:

Some of these functions can also convert a sf list into a list of bbox objects or the reverse. When using the ext = TRUE parameter, is_sf_list returns TRUE for sfc and bbox lists as well as sf lists.

nc_list_bbox <- st_bbox_ext(nc_list_name, asp = 1, class = "bbox")[1:4]

is_sf_list(nc_list_bbox)

is_sf_list(nc_list_bbox, ext = TRUE)
nc_list_name_buffered <-
  st_buffer_ext(x = nc_list_name, dist = 5, unit = "mi")

Lists can be visualized with ggplot by creating lists of layers. This approach illustrates the results of the st_buffer_ext function above:

ggplot() +
  purrr::map(
    nc_list_name_buffered,
    ~ geom_sf(data = .x, aes(fill = NAME), alpha = 0.2)
  ) +
  guides(fill = "none")

get_location also supports the creation of sf lists using the name_col, id_col, or name_col (used by get_esri_data) as a grouping column.

park_list <-
  get_location(
    type = "parks",
    name_col = "park_district",
    name = c("Patterson", "Clifton", "Carroll"),
    package = "mapbaltimore",
    class = "list"
  )

str(park_list)

The make_location_data_list function is useful for combining locations and related data in paired named lists. For example, the prior list of parks grouped by park district can be combined with a list of park districts to make two lists of 3 sf objects.

district_list <-
  get_location(
    type = "park_districts",
    name_col = "name",
    name = c("Patterson", "Clifton", "Carroll"),
    package = "mapbaltimore",
    class = "list"
  )

park_district_list <-
  make_location_data_list(data = park_list, location = district_list)

str(park_district_list)

This combined object can be used to create similar maps for each area using purrr::map2() as the following example illustrates.

park_district_maps <-
  purrr::map2(
    park_district_list$data,
    park_district_list$location,
    ~ ggplot() +
      layer_location_data(
        data = "baltimore_city_detailed",
        package = "mapbaltimore",
        fill = "gray95",
        color = "gray55"
      ) +
      layer_location_data(
        data = .x,
        location = .y,
        fill = "forestgreen",
        color = "lightgreen",
        alpha = 0.6
      ) +
      layer_neatline(
        data = .y,
        asp = 1
      )
  )

park_district_maps[[1]] + park_district_maps[[2]] + park_district_maps[[3]]

get_location_data works with sf lists but in a somewhat limited way that does not support all available features of the function. This currently works best when data is a character string which can also be used to name each sf object in the list (in combination with the label parameter).

map_layers <-
  map_location_data(
    data = c("streets", "mta_bus_lines", "neighborhoods"),
    package = "mapbaltimore",
    asp = 1,
    location = district_list$carroll,
    label = "Carroll District",
    class = "list"
  )

The named lists making assembling layered maps with ggplot2 especially convenient:

ggplot() +
  layer_location_data(
    data = map_layers$carroll_district_streets,
    location = district_list$carroll,
    fn = ~ filter(.x, (!is.na(sha_class) & (sha_class != ""))),
    trim = TRUE,
 #   mapping = aes(size = sha_class),
    color = "gray40",
    size = 0.2,
    fill = NA
  ) +
  #  sha_class_scale +
  layer_location_data(
    data = map_layers$carroll_district_neighborhoods,
    alpha = 0.4,
    color = NA,
    mapping = aes(fill = name)
  ) +
  guides(fill = "none") +
  layer_location_data(
    data = map_layers$carroll_district_mta_bus_lines,
    mapping = aes(color = frequent),
    size = 0.3,
    alpha = 0.3
  ) +
  guides(color = "none") +
  layer_neatline(
    data = district_list$carroll,
    asp = 1,
    expand = TRUE
  )

Working with sf lists in combination with data and location index lists

params <- list(
  index = list(
    data = list(
      "cama" = "https://geodata.md.gov/imap/rest/services/PlanningCadastre/MD_ComputerAssistedMassAppraisal/MapServer/1",
      "cama_bldg" = "https://geodata.md.gov/imap/rest/services/PlanningCadastre/MD_ComputerAssistedMassAppraisal/MapServer/0",
      "parcels" = "https://geodata.md.gov/imap/rest/services/PlanningCadastre/MD_ParcelBoundaries/MapServer/0",
      "edge_of_pavement" = "https://gisdata.baltimorecity.gov/egis/rest/services/OpenBaltimore/Edge_of_Pavement/FeatureServer/0"
      # "property" = "https://opendata.baltimorecity.gov/egis/rest/services/NonSpatialTables/RealProperty/FeatureServer/0"
    ),
    type = "named_intersections",
    package = "mapbaltimore"
  ),
  location = list(
    name = "EASTERN AVE & S CONKLING ST",
    label = "Conkling Plaza"
  ),
  data = list(
    dist = 150,
    crs = 3857
  )
)

location <-
  get_location(
    type = params$index$type,
    package = params$index$package,
    name = params$location$name,
    label = params$location$label
  )

# TODO: map_location_data may not be passing the location bounding box correctly
# FIXME: When this hit an error from the nonspatial data in the FeatureServer link indexed to property
property_data <-
  map_location_data(
    data = names(params$index$data),
    index = params$index$data,
    location = location,
    dist = params$data$dist,
    class = "list",
    crs = params$data$crs
  )


elipousson/overedge documentation built on Aug. 13, 2022, 7:41 p.m.