Skip to contents

Get location

Supported location types

The “type” of location data that you can access with get_location() is very flexible. You can provide:

  • A simple feature object
  • A url to a spatial data file compatible with sf::read_sf()
  • A url for an ArcGIS MapServer or FeatureServer
  • A file path

You can also provide the name of a simple feature object that is either already loaded to the global environment or available to load through any installed package.

Data in packages include exported data that is available when a package is loaded, data that is available in the system files but not loaded (and may sometimes be undocumented), and data that is in the default cache folder path from rappdirs::user_cache_dir(). If you want to use package data that is cached in a different location, you need to provide the file path (or filename and directory) in order to use it with get_location().

All of these parameters are passed from get_location() to get_location_data() so the following section of this vignette includes more details on how the more general function works.

Finally, you can alternatively pass a named list to index that serves as a look-up table for matching the “type” to a an sf object.

Supported parameters to accessing a location

The most basic parameters for accessing a specific location are:

  • name (with name_col matching column to filter)
  • id (with id_col matching column to filter)

The passed value for name is coerced to character and id is coerced to numeric so, if the “id” column for this type of location is in a character format, you need to treat it as a “name” instead.

Alternatively you can use the location parameter which supports either:

  • a valid street address (geocoded with tidygeocoder::geo())
  • a simple feature object or bounding box

If you provide a sf or bbox object, the object is used as a spatial filter either when reading in the data (using the bbox parameter for read_sf_path or read_sf_url) or before returning the data using the sf::st_filter() function.

Working with union and label parameters

Location types that are administrative boundaries can often be grouped together into larger areas. The union parameter makes it easy to do this.

What is get_location() good for?

Building custom look-up functions

Here is are a few examples showing how you can use get_location() to create new custom look-up functions.

If your location data is from another package, you can use the package parameters passed to get_location_data() to access that data even without loading the package. This approach works best when the package data uses a consistent naming scheme for columns and data to support. This makes it easy to turn the names into parameters as the example below using the nycgeo package.

# remotes::install_github("mfherman/nycgeo")

get_nyc_borough <- function(name = NULL, id = NULL, type = "simple") {
  data <- "borough_sf"
  if (type == "simple") {
    data <- paste(data, type, sep = "_")
  }

  package <- "nycgeo"

  get_location(
    type = data,
    package = package,
    name = name,
    id = id,
    name_col = "borough_name",
    id_col = "borough_id"
  )
}

get_nyc_borough(name = "Manhattan")
#> old-style crs object detected; please recreate object with a recent sf::st_crs()
#> old-style crs object detected; please recreate object with a recent sf::st_crs()
#> old-style crs object detected; please recreate object with a recent sf::st_crs()
#> Simple feature collection with 1 feature and 6 fields
#> Geometry type: MULTIPOLYGON
#> Dimension:     XY
#> Bounding box:  xmin: 971160 ymin: 188438.4 xmax: 1010065 ymax: 259550.8
#> Projected CRS: NAD83 / New York Long Island (ftUS)
#> # A tibble: 1 × 7
#>   geoid state_fips county_fips county_name borough_name borough_id
#>   <chr> <chr>      <chr>       <chr>       <chr>        <chr>     
#> 1 36061 36         061         New York    Manhattan    1         
#> # … with 1 more variable: geometry <MULTIPOLYGON [US_survey_foot]>

If your location data is sourced from an ArcGIS MapServer or FeatureServer, you can use the name and name_col parameters from the underlying get_esri_data() function. This avoids the need to download the full dataset every time.

# get_anc looks up the Advisory Neighborhood Council
# Data from Open Data DC https://opendata.dc.gov/datasets/fcfbf29074e549d8aff9b9c708179291

get_anc <- function(name) {
  url <- "https://maps2.dcgis.dc.gov/dcgis/rest/services/DCGIS_DATA/Administrative_Other_Boundaries_WebMercator/MapServer/1"

  get_location(
    type = url,
    name = name,
    name_col = "name"
  )
}

get_anc(name = "5B")
#> Layer Type: Feature Layer
#> Geometry Type: esriGeometryPolygon
#> Service Coordinate Reference System: 3857
#> Output Coordinate Reference System: 4326
#> Simple feature collection with 1 feature and 11 fields
#> Geometry type: MULTIPOLYGON
#> Dimension:     XY
#> Bounding box:  xmin: -76.99753 ymin: 38.92135 xmax: -76.96666 ymax: 38.95024
#> Geodetic CRS:  WGS 84
#>   objectid anc_id                                                    web_url
#> 1        9 ANC 5B http://anc.dc.gov/page/advisory-neighborhood-commission-5b
#>   name shape_area shape_len                               globalid creator
#> 1   5B          0         0 {6DBCF9C2-E269-41C6-AE4D-AC645FFC101D}      NA
#>   created editor       edited                          geoms
#> 1      NA  DCGIS 1.573036e+12 MULTIPOLYGON (((-76.98239 3...

Location data can also be loaded from a URL with a GeoJSON or other spatial data file.

Accessing overlapping geographies

Since location supports both addresses and sf objects, you can pull individual locations without knowing the name or id of an area. For example, you can use an address:

# remotes::install_github("elipousson/mapbaltimore")

lookup_tract <- function(address) {
  get_location(
    type = "baltimore_tracts",
    package = "mapbaltimore",
    location = address
  )
}

lookup_tract("100 Holliday St, Baltimore, MD 21202")
#> Passing 1 address to the Nominatim single address geocoder
#> Query completed in: 1 seconds
#> Simple feature collection with 1 feature and 8 fields
#> Geometry type: POLYGON
#> Dimension:     XY
#> Bounding box:  xmin: 432552.6 ymin: 179852.2 xmax: 434068 ymax: 180839.8
#> Projected CRS: NAD83(HARN) / Maryland
#>   tractce       geoid name         namelsad   aland awater    intptlat
#> 1  040100 24510040100  401 Census Tract 401 1191002   1430 +39.2902267
#>       intptlon                       geometry
#> 1 -076.6145019 POLYGON ((432552.6 180801.8...

You can also combine multiple approaches to create a look-up function that returns overlapping geography for a named characteristic. For example, the following function takes the name of a school and returns any Baltimore City neighborhoods that overlap with that school’s attendance zone.


lookup_school_neighborhoods <- function(school_name) {
  school <-
    get_location(
      type = "bcps_es_zones_SY2021",
      package = "bcpss",
      name = school_name,
      name_col = "program_name_short",
      crs = 2804
    )

  school_neighborhoods <-
    get_location(
    type = "neighborhoods",
    package = "mapbaltimore",
    location = school
  )

  return(school_neighborhoods)
}

lookup_school_neighborhoods(school_name = "Eutaw-Marshburn E")
#> Simple feature collection with 7 features and 5 fields
#> Geometry type: MULTIPOLYGON
#> Dimension:     XY
#> Bounding box:  xmin: 430345 ymin: 180859.9 xmax: 432760.6 ymax: 183281.7
#> Projected CRS: NAD83(HARN) / Maryland
#> # A tibble: 7 × 6
#>   name                type       acres osm_id wikidata                  geometry
#> * <chr>               <chr>      <dbl> <chr>  <chr>           <MULTIPOLYGON [m]>
#> 1 Bolton Hill         Residenti… 146.  12792… Q4940186 (((432349.2 182587.8, 43…
#> 2 Druid Heights       Residenti…  57.9 12792… NA       (((431569.6 181988, 4315…
#> 3 Madison Park        Residenti… 102.  12792… NA       (((432337.1 181728.9, 43…
#> 4 Penn North          Residenti…  86.5 12799… Q181584… (((430510.3 182832.8, 43…
#> 5 Reservoir Hill      Residenti… 195.  12792… Q7315317 (((432026 183146.6, 4320…
#> 6 Sandtown-Winchester Residenti… 260.  12800… Q7416984 (((430729.9 182481.6, 43…
#> 7 Upton               Residenti… 188.  12792… Q7899346 (((432361 181396.1, 4323…

Get location data

As noted in the previous section, get_location() is largely built around get_location_data(). But, get_location_data() is also a helpful function just by itself.

While the data parameter supports all of the options for the type parameter described in the prior section, get_location_data() also supports named parameters for path or url.

Note: This vignette is a work in progress with additional details to be added soon.

params <- list(
  type = "council_districts",
  id = 12,
  dist = 0.125,
  diag_ratio = NULL,
  unit = "mi",
  package = "mapbaltimore",
  layers = c("streets", "neighborhoods", "trees")
)

district <-
  get_location(
    type = params$type,
    package = params$package,
    id = params$id
  )

district
#> Simple feature collection with 1 feature and 2 fields
#> Geometry type: MULTIPOLYGON
#> Dimension:     XY
#> Bounding box:  xmin: 432071 ymin: 179871.4 xmax: 436036.4 ymax: 184134.6
#> Projected CRS: NAD83(HARN) / Maryland
#>    id                       geometry        name
#> 13 12 MULTIPOLYGON (((433676 1838... District 12

Selecting a page size

You can use the get_paper function to look up one or more common paper sizes or standard image sizes:

paper_options <- get_paper(paper = NULL, standard = c("Twitter", "Instagram"))
knitr::kable(paper_options)
name series size standard units width height asp cols col_width rows row_height gutter section_asp orientation type
Instagram post NA post Instagram px 1080 1080 1.000000 1 1080 1 1080 0 1.000000 square social
Instagram story NA story Instagram px 1080 1920 0.562500 1 1080 1 1920 0 0.562500 portrait social
Twitter cover photo NA cover Twitter px 1500 500 3.000000 1 1500 1 500 0 3.000000 landscape social
Twitter image and link post NA post Twitter px 1200 628 1.910828 1 1200 1 628 0 1.910828 landscape social
Twitter single image post NA post Twitter px 1200 675 1.777778 1 1200 1 675 0 1.777778 landscape social
Twitter multiple image post NA post Twitter px 700 800 0.875000 1 700 1 800 0 0.875000 portrait social

You can also use the geometry of any specified location to set the orientation of the page or the suggested aspect ratio using the sf_bbox_asp function:

sf_bbox_asp(sf::st_bbox(district))
#> [1] 0.930126

sf_bbox_asp((sf::st_bbox(district)), orientation = TRUE)
#> [1] "square"

Based on this information, it looks like this map way work well as an Instagram post.

params$paper <- "Instagram post"

# Get a paper by name 
paper <- 
  get_paper(
    paper = params$paper
  )

# Move width, height, aspect ratio (asp), and units (for map) into params
params$width <- paper$width
params$height <- paper$height
params$asp <- paper$asp
# NOTE: px units are not fully supported by all functions
# NOTE: units supports map-scale options ("in") rather than the unit parameter used by st_buffer_ext
params$units <- paper$units

There is one additional consideration in setting the aspect ratio for the map area: the aspect ratio of the content block after marings have been applied. While this is less significant for a Instagram post where full bleed images are appropriate, an example below shows how to get the aspect ratio of a plot, text, or map block after margins have been added to a page. The “px” units associated with the Instagram post paper cannot currently be used with the header and footer option so I passed “npc” units to manually set the margin rather than using the preset options.

get_asp(
  paper = "letter",
  block_asp = FALSE)
#> [1] 0.7727273

get_asp(
  paper = "letter",
  margin = c(1, 1, 1.5, 1),
  block_asp = TRUE)
#> [1] 0.7647059

Finally, the final selections can be used to create a bounding box that reduces the need to pass these same variables to all similar functions. This can also works as a way of effectively saving different “zoom levels” for a map based on different buffer units.

# This information 
district_bbox <-
  st_bbox_ext(
    x = district,
    dist = params$dist,
    diag_ratio = params$diag_ratio,
    asp = params$asp
  )
district_trees <-
  get_location_data(
    location = district_bbox,
    data = "trees",
    package = "mapbaltimore",
    filetype = "gpkg"
  )

gt::gt(dplyr::slice_head(district_trees, n = 10))
street_number street_name on_street location_type side condition dbh height multi_stem spp cultivar common genus x_coord y_coord geom
36 S CHARLES ST UNASSIGNED Street Side Fair 12.5 35 1 Platanus x acerifolia planetree, London Platanus -76.61534 39.28780 c(433185.393072185, 180025.527207589)
20 S CHARLES ST UNASSIGNED Street Front Good 6.0 25 3 Betula nigra birch, river Betula -76.61531 39.28864 c(433187.446373906, 180117.789288718)
20 S CHARLES ST UNASSIGNED Street Front Good 7.0 2 2 Betula nigra birch, river Betula -76.61531 39.28866 c(433187.476673959, 180120.3217882)
20 S CHARLES ST UNASSIGNED Street Front Good 5.0 20 3 Betula nigra birch, river Betula -76.61528 39.28869 c(433190.498673416, 180123.357087508)
20 S CHARLES ST UNASSIGNED Street Front Dead 3.0 15 2 Betula nigra birch, river Betula -76.61528 39.28866 c(433190.633873321, 180120.457688098)
20 S CHARLES ST UNASSIGNED Street Front Good 2.3 20 1 Betula nigra birch, river Betula -76.61527 39.28863 c(433191.055573171, 180117.722788649)
20 S CHARLES ST UNASSIGNED Street Front Good 8.0 25 3 Betula nigra birch, river Betula -76.61524 39.28869 c(433193.697172768, 180123.411387426)
20 S CHARLES ST UNASSIGNED Street Front Good 7.0 25 3 Betula nigra birch, river Betula -76.61524 39.28866 c(433193.955672647, 180120.47168802)
20 S CHARLES ST UNASSIGNED Street Front Fair 12.8 15 1 Tilia americana linden, American Tilia -76.61518 39.28851 c(433199.02187122, 180103.449791372)
20 S CHARLES ST UNASSIGNED Street Front Good 13.8 20 1 Tilia cordata linden, littleleaf Tilia -76.61517 39.28842 c(433199.756870866, 180094.466893189)
ggplot() +
  layer_location_data(
    data = district_trees,
    mapping = aes(color = condition),
    size = 0.3,
    alpha = 0.4
  ) +
  theme_legend(
    position = "bottomleft"
  ) +
  theme_text(
    font_family = "Fira Code"
  ) +
  labs(
    color = "Condition"
  ) +
  scale_color_brewer(palette = "RdYlGn") +
  layer_mask(
    data = district,
    dist = 100,
    color = "gray70",
    neatline = TRUE
  )