Skip to content

Commit

Permalink
reverse geocoding
Browse files Browse the repository at this point in the history
  • Loading branch information
JosiahParry committed Jun 6, 2024
1 parent 15d89d2 commit 77c995d
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 0 deletions.
15 changes: 15 additions & 0 deletions _freeze/docs/geocode/reverse-geocoding/execute-results/html.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"hash": "95d056b87bd42bf8914c34b4f1f45f6b",
"result": {
"engine": "knitr",
"markdown": "---\ntitle: \"Reverse Geocoding\"\n---\n\n\nOften you may coordinates of locations but need their addresses as well. **Reverse geocoding** finds an address associated with a location on earth. \n\nUse `arcgisgeocode::reverse_geocode()` to perform reverse geocoding. Using this fuction you can\n\n- reverse geocode an `sfc_POINT` geometry column from the `{sf}` package\n- reverse geocode a matrix of coordinates\n- reverse geocode a single location as a length 2 vector e.g. `c(-117, 34)`\n\n\n# Reverse geocode a single point\n\nFirst, load the R package. \n\n\n::: {.cell}\n\n```{.r .cell-code}\nlibrary(arcgisgeocode)\n```\n:::\n\n\nYou can reverse geocode a single longitude/latitude pair as a length 2 vector with `reverse_geocode()`. \n\n\n::: {.cell}\n\n```{.r .cell-code}\n# Find addresses from locations\nres <- reverse_geocode(c(-117.172, 34.052))\ndplyr::glimpse(res)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\nRows: 1\nColumns: 23\n$ match_addr <chr> \"600-620 Home Pl, Redlands, California, 92374\"\n$ long_label <chr> \"600-620 Home Pl, Redlands, CA, 92374, USA\"\n$ short_label <chr> \"600-620 Home Pl\"\n$ addr_type <chr> \"StreetAddress\"\n$ type_field <chr> \"\"\n$ place_name <chr> \"\"\n$ add_num <chr> \"608\"\n$ address <chr> \"608 Home Pl\"\n$ block <chr> \"\"\n$ sector <chr> \"\"\n$ neighborhood <chr> \"South Redlands\"\n$ district <chr> \"\"\n$ city <chr> \"Redlands\"\n$ metro_area <chr> \"\"\n$ subregion <chr> \"San Bernardino County\"\n$ region <chr> \"California\"\n$ region_abbr <chr> \"CA\"\n$ territory <chr> \"\"\n$ postal <chr> \"92374\"\n$ postal_ext <chr> \"\"\n$ country_name <chr> \"United States\"\n$ country_code <chr> \"USA\"\n$ geometry <POINT [°]> POINT (-117.172 34.05204)\n```\n\n\n:::\n:::\n\n\n:::{.callout-important}\nIt is important to note that when you are not using an `sfc_POINT` object, the coordinate reference system is not known. So it is assumed to be `EPSG:4326`. If you provide values outside of [-180, 180] and [-90, 90] for longitude or latitude, an error will occur. \n:::\n\n# Reverse geocode from an sf object\n\nMore commonly, you may have an sf object that you want to reverse geocode their locations. To demonstrate this, you will reverse geocode a csv of state capitals. \n\nFirst, read the csv file into a data.frame and convert it to an sf object.\n\n\n::: {.cell}\n\n```{.r .cell-code}\nlibrary(sf)\nlibrary(dplyr)\n\n# USA State Capitals\nfp <- \"https://analysis-1.maps.arcgis.com/sharing/rest/content/items/85bcfca158d641b99e7579b47cfee91e/data\"\n\n# read the csv\ncapitals <- readr::read_csv(fp) |>\n # convert to an sf object with EPSG:4326\n st_as_sf(\n coords = c(\"longitude\", \"latitude\"),\n crs = 4326\n )\n\ncapitals\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\nSimple feature collection with 50 features and 2 fields\nGeometry type: POINT\nDimension: XY\nBounding box: xmin: -157.8574 ymin: 21.30744 xmax: -69.78169 ymax: 58.3016\nGeodetic CRS: WGS 84\n# A tibble: 50 × 3\n name description geometry\n * <chr> <chr> <POINT [°]>\n 1 Alabama Montgomery (-86.30057 32.37772)\n 2 Alaska Juneau (-134.4202 58.3016)\n 3 Arizona Phoenix (-112.097 33.44814)\n 4 Arkansas Little Rock (-92.28899 34.74661)\n 5 California Sacramento (-121.4936 38.57667)\n 6 Colorado Denver (-104.9849 39.73923)\n 7 Connecticut Hartford<br> (-72.6822 41.76405)\n 8 Delaware Dover (-75.51972 39.15731)\n 9 Hawaii Honolulu (-157.8574 21.30744)\n10 Florida Tallahassee (-84.2813 30.43812)\n# ℹ 40 more rows\n```\n\n\n:::\n:::\n\n\nUse `reverse_geocode()` with the geometry column from the capitals to create a new sf object with the address information.\n\n\n::: {.cell}\n\n```{.r .cell-code}\ngeocoded <- reverse_geocode(st_geometry(capitals))\nglimpse(geocoded)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\nRows: 50\nColumns: 23\n$ match_addr <chr> \"Alabama State Capitol\", \"Juneau Post Office - Federal St…\n$ long_label <chr> \"Alabama State Capitol, Montgomery, AL, USA\", \"Juneau Pos…\n$ short_label <chr> \"Alabama State Capitol\", \"Juneau Post Office - Federal St…\n$ addr_type <chr> \"POI\", \"POI\", \"POI\", \"POI\", \"POI\", \"POI\", \"POI\", \"POI\", \"…\n$ type_field <chr> \"Building\", \"Post Office\", \"Museum\", \"Government Office\",…\n$ place_name <chr> \"Alabama State Capitol\", \"Juneau Post Office - Federal St…\n$ add_num <chr> \"\", \"\", \"1700\", \"500\", \"1315\", \"101\", \"210\", \"411\", \"304\"…\n$ address <chr> \"\", \"\", \"1700 W Washington St\", \"500 Woodlane Ave\", \"1315…\n$ block <chr> \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"…\n$ sector <chr> \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"…\n$ neighborhood <chr> \"\", \"\", \"Central City\", \"\", \"Downtown Sacramento\", \"\", \"D…\n$ district <chr> \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"…\n$ city <chr> \"Montgomery\", \"Juneau\", \"Phoenix\", \"Little Rock\", \"Sacram…\n$ metro_area <chr> \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"…\n$ subregion <chr> \"Montgomery County\", \"Juneau City and Borough\", \"Maricopa…\n$ region <chr> \"Alabama\", \"Alaska\", \"Arizona\", \"Arkansas\", \"California\",…\n$ region_abbr <chr> \"AL\", \"AK\", \"AZ\", \"AR\", \"CA\", \"CO\", \"CT\", \"DE\", \"HI\", \"FL…\n$ territory <chr> \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"…\n$ postal <chr> \"\", \"\", \"85007\", \"72201\", \"95814\", \"80203\", \"06105\", \"199…\n$ postal_ext <chr> \"\", \"\", \"\", \"\", \"4801\", \"\", \"\", \"\", \"2420\", \"\", \"\", \"\", \"…\n$ country_name <chr> \"United States\", \"United States\", \"United States\", \"Unite…\n$ country_code <chr> \"USA\", \"USA\", \"USA\", \"USA\", \"USA\", \"USA\", \"USA\", \"USA\", \"…\n$ geometry <POINT [°]> POINT (-86.30049 32.37775), POINT (-134.4203 58.301…\n```\n\n\n:::\n:::\n\n\nThen, you can use dplyr (or base R via `cbind()`) to combine the two datasets. In this example, the geometry column from the reverse geocoding results is drops. This prevents dplyr from renaming the duplicate columns and preserves the sf class.\n\n\n::: {.cell}\n\n```{.r .cell-code}\nbind_cols(\n capitals,\n st_drop_geometry(geocoded)\n)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\nSimple feature collection with 50 features and 24 fields\nGeometry type: POINT\nDimension: XY\nBounding box: xmin: -157.8574 ymin: 21.30744 xmax: -69.78169 ymax: 58.3016\nGeodetic CRS: WGS 84\n# A tibble: 50 × 25\n name description geometry match_addr long_label short_label\n * <chr> <chr> <POINT [°]> <chr> <chr> <chr> \n 1 Alab… Montgomery (-86.30057 32.37772) Alabama State … Alabama S… Alabama St…\n 2 Alas… Juneau (-134.4202 58.3016) Juneau Post Of… Juneau Po… Juneau Pos…\n 3 Ariz… Phoenix (-112.097 33.44814) Arizona Capito… Arizona C… Arizona Ca…\n 4 Arka… Little Rock (-92.28899 34.74661) Arkansas State… Arkansas … Arkansas S…\n 5 Cali… Sacramento (-121.4936 38.57667) California Sta… Californi… California…\n 6 Colo… Denver (-104.9849 39.73923) Credit Repair Credit Re… Credit Rep…\n 7 Conn… Hartford<b… (-72.6822 41.76405) Connecticut St… Connectic… Connecticu…\n 8 Dela… Dover (-75.51972 39.15731) Delaware Senat… Delaware … Delaware S…\n 9 Hawa… Honolulu (-157.8574 21.30744) Eternal Flame … Eternal F… Eternal Fl…\n10 Flor… Tallahassee (-84.2813 30.43812) Florida Capito… Florida C… Florida Ca…\n# ℹ 40 more rows\n# ℹ 19 more variables: addr_type <chr>, type_field <chr>, place_name <chr>,\n# add_num <chr>, address <chr>, block <chr>, sector <chr>,\n# neighborhood <chr>, district <chr>, city <chr>, metro_area <chr>,\n# subregion <chr>, region <chr>, region_abbr <chr>, territory <chr>,\n# postal <chr>, postal_ext <chr>, country_name <chr>, country_code <chr>\n```\n\n\n:::\n:::\n\n\n\nAlternatively, you can accomplish this using a more esoteric and tidyverse-centric approach. The below is an option that uses `mutate()` to create a new column which is an sf object. Then, it uses `tidyr::unnest()` to unnest the newly created sf column.\n\n\n::: {.cell}\n\n```{.r .cell-code}\ncapitals |>\n mutate(\n address_info = st_drop_geometry(\n reverse_geocode(geometry)\n )\n ) |>\n tidyr::unnest(address_info)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\nSimple feature collection with 50 features and 24 fields\nGeometry type: POINT\nDimension: XY\nBounding box: xmin: -157.8574 ymin: 21.30744 xmax: -69.78169 ymax: 58.3016\nGeodetic CRS: WGS 84\n# A tibble: 50 × 25\n name description geometry match_addr long_label short_label\n <chr> <chr> <POINT [°]> <chr> <chr> <chr> \n 1 Alab… Montgomery (-86.30057 32.37772) Alabama State … Alabama S… Alabama St…\n 2 Alas… Juneau (-134.4202 58.3016) Juneau Post Of… Juneau Po… Juneau Pos…\n 3 Ariz… Phoenix (-112.097 33.44814) Arizona Capito… Arizona C… Arizona Ca…\n 4 Arka… Little Rock (-92.28899 34.74661) Arkansas State… Arkansas … Arkansas S…\n 5 Cali… Sacramento (-121.4936 38.57667) California Sta… Californi… California…\n 6 Colo… Denver (-104.9849 39.73923) Credit Repair Credit Re… Credit Rep…\n 7 Conn… Hartford<b… (-72.6822 41.76405) Connecticut St… Connectic… Connecticu…\n 8 Dela… Dover (-75.51972 39.15731) Delaware Senat… Delaware … Delaware S…\n 9 Hawa… Honolulu (-157.8574 21.30744) Eternal Flame … Eternal F… Eternal Fl…\n10 Flor… Tallahassee (-84.2813 30.43812) Florida Capito… Florida C… Florida Ca…\n# ℹ 40 more rows\n# ℹ 19 more variables: addr_type <chr>, type_field <chr>, place_name <chr>,\n# add_num <chr>, address <chr>, block <chr>, sector <chr>,\n# neighborhood <chr>, district <chr>, city <chr>, metro_area <chr>,\n# subregion <chr>, region <chr>, region_abbr <chr>, territory <chr>,\n# postal <chr>, postal_ext <chr>, country_name <chr>, country_code <chr>\n```\n\n\n:::\n:::\n\n\n\n# Reverse geocoding a matrix of coordinates\n\nThere are other times where you may have your coordinates stored as a matrix with two columns. `reverse_geocode()` accepts a 2 column numeric matrix as an input to its `location` argument. \n\nFor the sake of example, the coordinates are extracted as a matrix using `sf::st_coordinates()`.\n\n::: {.cell}\n\n```{.r .cell-code}\ncoords <- st_coordinates(capitals)\nhead(coords)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n X Y\n[1,] -86.30057 32.37772\n[2,] -134.42021 58.30160\n[3,] -112.09696 33.44814\n[4,] -92.28899 34.74661\n[5,] -121.49363 38.57667\n[6,] -104.98486 39.73923\n```\n\n\n:::\n:::\n\n\nPass this matrix directly into `reverse_geocode()`.\n\n::: {.cell}\n\n```{.r .cell-code}\ngeocoded <- reverse_geocode(coords)\nglimpse(geocoded)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\nRows: 50\nColumns: 23\n$ match_addr <chr> \"Alabama State Capitol\", \"Juneau Post Office - Federal St…\n$ long_label <chr> \"Alabama State Capitol, Montgomery, AL, USA\", \"Juneau Pos…\n$ short_label <chr> \"Alabama State Capitol\", \"Juneau Post Office - Federal St…\n$ addr_type <chr> \"POI\", \"POI\", \"POI\", \"POI\", \"POI\", \"POI\", \"POI\", \"POI\", \"…\n$ type_field <chr> \"Building\", \"Post Office\", \"Museum\", \"Government Office\",…\n$ place_name <chr> \"Alabama State Capitol\", \"Juneau Post Office - Federal St…\n$ add_num <chr> \"\", \"\", \"1700\", \"500\", \"1315\", \"101\", \"210\", \"411\", \"304\"…\n$ address <chr> \"\", \"\", \"1700 W Washington St\", \"500 Woodlane Ave\", \"1315…\n$ block <chr> \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"…\n$ sector <chr> \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"…\n$ neighborhood <chr> \"\", \"\", \"Central City\", \"\", \"Downtown Sacramento\", \"\", \"D…\n$ district <chr> \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"…\n$ city <chr> \"Montgomery\", \"Juneau\", \"Phoenix\", \"Little Rock\", \"Sacram…\n$ metro_area <chr> \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"…\n$ subregion <chr> \"Montgomery County\", \"Juneau City and Borough\", \"Maricopa…\n$ region <chr> \"Alabama\", \"Alaska\", \"Arizona\", \"Arkansas\", \"California\",…\n$ region_abbr <chr> \"AL\", \"AK\", \"AZ\", \"AR\", \"CA\", \"CO\", \"CT\", \"DE\", \"HI\", \"FL…\n$ territory <chr> \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"…\n$ postal <chr> \"\", \"\", \"85007\", \"72201\", \"95814\", \"80203\", \"06105\", \"199…\n$ postal_ext <chr> \"\", \"\", \"\", \"\", \"4801\", \"\", \"\", \"\", \"2420\", \"\", \"\", \"\", \"…\n$ country_name <chr> \"United States\", \"United States\", \"United States\", \"Unite…\n$ country_code <chr> \"USA\", \"USA\", \"USA\", \"USA\", \"USA\", \"USA\", \"USA\", \"USA\", \"…\n$ geometry <POINT [°]> POINT (-86.30049 32.37775), POINT (-134.4203 58.301…\n```\n\n\n:::\n:::\n",
"supporting": [],
"filters": [
"rmarkdown/pagebreak.lua"
],
"includes": {},
"engineDependencies": {},
"preserve": {},
"postProcess": true
}
}
2 changes: 2 additions & 0 deletions _quarto.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ website:
- docs/geocode/forward-geocoding.qmd
- href: docs/geocode/bulk-geocoding.qmd
text: "Bulk Geocoding"
- href: docs/geocode/reverse-geocoding.qmd
text: "Reverse Geocoding"
- section: Places


Expand Down
102 changes: 102 additions & 0 deletions docs/geocode/reverse-geocoding.qmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
---
title: "Reverse Geocoding"
---

Often you may coordinates of locations but need their addresses as well. **Reverse geocoding** finds an address associated with a location on earth.

Use `arcgisgeocode::reverse_geocode()` to perform reverse geocoding. Using this fuction you can

- reverse geocode an `sfc_POINT` geometry column from the `{sf}` package
- reverse geocode a matrix of coordinates
- reverse geocode a single location as a length 2 vector e.g. `c(-117, 34)`


# Reverse geocode a single point

First, load the R package.

```{r}
library(arcgisgeocode)
```

You can reverse geocode a single longitude/latitude pair as a length 2 vector with `reverse_geocode()`.

```{r message=FALSE}
# Find addresses from locations
res <- reverse_geocode(c(-117.172, 34.052))
dplyr::glimpse(res)
```

:::{.callout-important}
It is important to note that when you are not using an `sfc_POINT` object, the coordinate reference system is not known. So it is assumed to be `EPSG:4326`. If you provide values outside of [-180, 180] and [-90, 90] for longitude or latitude, an error will occur.
:::

# Reverse geocode from an sf object

More commonly, you may have an sf object that you want to reverse geocode their locations. To demonstrate this, you will reverse geocode a csv of state capitals.

First, read the csv file into a data.frame and convert it to an sf object.

```{r message = FALSE}
library(sf)
library(dplyr)
# USA State Capitals
fp <- "https://analysis-1.maps.arcgis.com/sharing/rest/content/items/85bcfca158d641b99e7579b47cfee91e/data"
# read the csv
capitals <- readr::read_csv(fp) |>
# convert to an sf object with EPSG:4326
st_as_sf(
coords = c("longitude", "latitude"),
crs = 4326
)
capitals
```

Use `reverse_geocode()` with the geometry column from the capitals to create a new sf object with the address information.

```{r message = FALSE}
geocoded <- reverse_geocode(st_geometry(capitals))
glimpse(geocoded)
```

Then, you can use dplyr (or base R via `cbind()`) to combine the two datasets. In this example, the geometry column from the reverse geocoding results is drops. This prevents dplyr from renaming the duplicate columns and preserves the sf class.

```{r message= FALSE}
bind_cols(
capitals,
st_drop_geometry(geocoded)
)
```


Alternatively, you can accomplish this using a more esoteric and tidyverse-centric approach. The below is an option that uses `mutate()` to create a new column which is an sf object. Then, it uses `tidyr::unnest()` to unnest the newly created sf column.

```{r}
capitals |>
mutate(
address_info = st_drop_geometry(
reverse_geocode(geometry)
)
) |>
tidyr::unnest(address_info)
```


# Reverse geocoding a matrix of coordinates

There are other times where you may have your coordinates stored as a matrix with two columns. `reverse_geocode()` accepts a 2 column numeric matrix as an input to its `location` argument.

For the sake of example, the coordinates are extracted as a matrix using `sf::st_coordinates()`.
```{r}
coords <- st_coordinates(capitals)
head(coords)
```

Pass this matrix directly into `reverse_geocode()`.
```{r}
geocoded <- reverse_geocode(coords)
glimpse(geocoded)
```

0 comments on commit 77c995d

Please sign in to comment.