---
title: "Reverse geocoding and feature types"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Reverse geocoding and feature types}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

<!-- feature-types.Rmd is generated from feature-types.Rmd.orig. Please edit that file -->

*Adapted from
<https://developers.arcgis.com/rest/geocode/api-reference/geocoding-reverse-geocode.htm>*

## Reverse geocode details

The purpose of reverse geocoding is to answer the question: What's near this
location? To answer this question, the `reverseGeocode` operation provided by
the **ArcGIS REST API** returns the most relevant feature near an input location
based on a prioritized hierarchy of feature types.

The hierarchy is summarized in the table below, ordered by descending priority.
Unless otherwise noted, each feature type is only returned when the distance
between the input location and the feature is within the tolerance specified in
the *Search Tolerance* column.

::: {#table1}
:::

| Feature type | Search tolerance | Comments |
|----|----|----|
| `StreetInt` | 10 meters | Intersections are only returned when `featuretypes = "StreetInt"` is included in the request. |
| `StreetAddress` (near), `DistanceMarker`, or `StreetName` | 3 meters | Candidates of type `StreetName` are only returned if `featureTypes = "streetName"` is included in the request. |
| `POI` centroid | 25 meters | A business or landmark that can be represented by a point. |
| `Subaddress` | 10 meters | `Subaddress` candidates, which can be features such as apartments or floors in a building, maybe not being returned under certain conditions. |
| `PointAddress` | 50 meters | A `PointAddress` match is not returned if it is on the opposite side of the street as the input location, even if it is within 50 meters of the location. |
| `StreetAddress` (distant), `DistanceMarker`, or `StreetName` | 100 meters | Candidates of type `StreetName` are only returned if `featuretypes = "StreetName"` is included in the request. |
| `POI` area | within boundary | A business or landmark that can be represented by an area, such as a large park or university. Not available in all countries. |
| `Postal` or `Locality` area | within boundary | If the input location intersects multiple boundaries, the feature with the smallest area is returned. |

: **Table 1**: Adapted from ArcGIS REST API `reverseGeocode`

In **arcgeocoder**, this hierarchy is implemented in `arc_reverse_geo()`,
specifically through the `featuretypes` argument. The default value
(`featuretypes = NULL`) does not include the argument in the API call. In this
case, the hierarchy presented in the previous table would apply.

It is possible to narrow down the output of the query to a specific feature type
or a list of feature types. The possible values supported for this argument
are:

-   `"StreetInt"`
-   `"DistanceMarker"`
-   `"StreetAddress"`
-   `"StreetName"`
-   `"POI"`
-   `"Subaddress"`
-   `"PointAddress"`
-   `"Postal"`
-   `"Locality"`

As mentioned, is to possible to include several feature types. If more than one
value is specified for the argument, the values must be separated by a comma,
with no spaces after the comma.

### single `featuretypes` value

``` r
arc_reverse_geo(..., featuretypes = "PointAddress")
```

### multiple `featuretypes` value

``` r
arc_reverse_geo(..., featuretypes = c("PointAddress", "StreetAddress"))
```

## Examples

In the following examples, we would provide different examples for better
understanding.

``` r
library(arcgeocoder)
library(dplyr)
```

### Example 1: Match to `POI` centroid returned

In this example, we do not provide any value to the `featuretypes` argument.
This input location is within the search tolerance of both `POI` and
`PointAddress` features, but a match to the `POI` centroid is returned because
it has a higher priority (see [**Table 1**](#table1)). Note that the output
field `Addr_type` indicates the type of feature.

``` r
example_x <- -117.203741
example_y <- 40.95029

api_poi <- arc_reverse_geo(
  x = example_x, y = example_y,
  langcode = "EN", full_results = TRUE, verbose = TRUE
)

api_poi |>
  select(x, y, address, lon, lat, Addr_type) |>
  knitr::kable()
```

|         x |        y | address                  |       lon |      lat | Addr_type |
|----------:|---------:|:-------------------------|----------:|---------:|:----------|
| -117.2037 | 40.95029 | 89414, Golconda, NV, USA | -117.2037 | 40.95029 | Postal    |

### Example 2: `Locality` match returned

``` r
api_local <- arc_reverse_geo(
  x = example_x, y = example_y,
  featuretypes = "Locality",
  langcode = "EN", full_results = TRUE, verbose = TRUE
)

api_local |>
  select(x, y, address, lon, lat, Addr_type) |>
  knitr::kable()
```

|         x |        y | address                  |       lon |      lat | Addr_type |
|----------:|---------:|:-------------------------|----------:|---------:|:----------|
| -117.2037 | 40.95029 | Humboldt County, NV, USA | -117.2037 | 40.95029 | Locality  |

### Example 3: multiple values

When multiple values are included in the API call, the hierarchy explained in
[**Table 1**](#table1) would still be applied on the requested `featuretypes`.

``` r
api_multiple <- arc_reverse_geo(
  x = example_x, y = example_y,
  featuretypes = c("Locality", "StreetInt", "StreetAddress"),
  langcode = "EN", full_results = TRUE, verbose = TRUE
)

api_multiple |>
  select(x, y, address, lon, lat, Addr_type) |>
  knitr::kable()
```

|         x |        y | address                  |       lon |      lat | Addr_type |
|----------:|---------:|:-------------------------|----------:|---------:|:----------|
| -117.2037 | 40.95029 | Humboldt County, NV, USA | -117.2037 | 40.95029 | Locality  |

### Example 4: No results for specific `featuretypes`

In the following example we present a case where only certain `featuretypes` are
near the requested location. In this case, when reverse geocoding the North Pole
the API would return a `Locality` but no `StreetAddress` is found.

When it is not possible to return results, `arc_reverse_geo()` returns an empty
**tibble**.

``` r
# North Pole

npole <- arc_reverse_geo(x = 0, y = 90, langcode = "EN", full_results = TRUE)

npole |>
  select(x, y, address, lon, lat, Addr_type) |>
  knitr::kable()
```

|   x |   y | address        | lon | lat | Addr_type |
|----:|----:|:---------------|----:|----:|:----------|
|   0 |  90 | Ozero Shybyndy |   0 |  90 | POI       |

``` r
# But no StreetAddress
npole2 <- arc_reverse_geo(
  x = 0, y = 90, langcode = "EN", full_results = TRUE,
  featuretypes = "StreetAddress"
)

npole2 |>
  knitr::kable()
```

|   x |   y | address |
|----:|----:|:--------|
|   0 |  90 | NA      |

## Conclusion

The API would return different results for the same `x,y` values depending on
the value of `featuretypes`. By using `featuretypes = NULL` the feature type
returned would depend on the hierarchy explained in [**Table 1**](#table1).

Depending on the location, the `featuretypes` filter may not return results,
hence for general purposes using `featuretypes = NULL` is safer.
