Skip to content

Commit

Permalink
Merge pull request #13 from munterfinger/reverse-geocode
Browse files Browse the repository at this point in the history
Reverse geocode
  • Loading branch information
munterfi authored Nov 13, 2019
2 parents 9dad82b + 7282ef4 commit 00a80f6
Show file tree
Hide file tree
Showing 45 changed files with 706 additions and 3,717 deletions.
4 changes: 2 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
Package: hereR
Type: Package
Title: 'sf'-Based Interface to the 'HERE' REST APIs
Version: 0.1.1
Version: 0.2.0
Authors@R: c(
person("Merlin", "Unterfinger", email = "[email protected]", role = c("aut", "cre")))
Maintainer: Merlin Unterfinger <[email protected]>
URL: https://munterfinger.github.io/hereR/, https://github.com/munterfinger/hereR/
BugReports: https://github.com/munterfinger/hereR/issues/
Description: Interface to the 'HERE' REST APIs <https://developer.here.com/develop/rest-apis>:
(1) geocode and autocomplete addresses using the 'Geocoder' API;
(1) geocode and autocomplete addresses or reverse geocode POIs using the 'Geocoder' API;
(2) routing directions, travel distance or time matrices and isolines using the 'Routing' API;
(3) traffic flow and incident information from the 'Traffic' API;
(4) weather forecasts, reports on current weather conditions, astronomical
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
export(autocomplete)
export(geocode)
export(isoline)
export(reverse_geocode)
export(route)
export(route_matrix)
export(set_auth)
Expand Down
7 changes: 4 additions & 3 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# hereR 0.1.1
# version 0.2.0

* Enhanced `geocode()`: In the case of empty responses the row names match the index of the geocoded addresses. Improved input checks. Option to use autocomplete by setting `autocomplete = TRUE`.
* **Geocoder Autocompete API:** The new feature `autocomplete()` allows autocompleting addresses.
* **Geocoder API: Autocomplete** The new feature `autocomplete()` allows autocompleting addresses.
* **Geocoder API: Reverse geocode** The new feature `reverse_geocode()` implements reverse geocoding POIs in order to retrieve suggestions for addresses or landmarks.

# hereR 0.1.0
# version 0.1.0

First release of the `hereR` package, an `sf`-based interface to the **HERE REST APIs**.
The packages binds to the following HERE APIs:
Expand Down
2 changes: 1 addition & 1 deletion R/autocomplete.R
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ autocomplete <- function(addresses, results = 5, url_only = FALSE) {

# Check addresses
.check_addresses(addresses)
.check_autocomplete_results(results)
.check_max_results(results)

# Add authentication
url <- .add_auth(
Expand Down
9 changes: 5 additions & 4 deletions R/checks.R
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
}

.check_points <- function(points) {
if (!"sf" %in% class(points) |
any(sf::st_geometry_type(points) != "POINT"))
stop("'points' must be an sf object with geometry type 'POINT'.")
if (!"sf" %in% class(points))
stop("'points' must be an sf object.")
if (any(sf::st_geometry_type(points) != "POINT"))
stop("'points' must be an sf object with geometry type 'POINT'.")
}

.check_polygon <- function(polygon) {
Expand Down Expand Up @@ -135,7 +136,7 @@
stop(sprintf("'product' must be '%s'.", paste(traffic_product_types, collapse = "', '")))
}

.check_autocomplete_results <- function(results) {
.check_max_results <- function(results) {
if (!is.numeric(results))
stop("'results' must be of type 'numeric'.")
if (results < 1 | results > 20)
Expand Down
145 changes: 145 additions & 0 deletions R/reverse.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
#' HERE Geocoder API: Reverse Geocode
#'
#' Get addresses or landmarks from locations using the 'Geocoder' API.
#' The return value is an \code{sf} object, containing point geometries
#' with suggestions for addresses or landmarks near the provided POIs.
#'
#' @references
#' \href{https://developer.here.com/documentation/geocoder/topics/resource-geocode.html}{HERE Geocoder API: Geocode}
#'
#' @param poi \code{sf} object, Points of Interest (POIs) of geometry type \code{POINT}.
#' @param results numeric, maximum number of results (Valid range: 1 and 20).
#' @param landmarks boolean, retrieve landmarks instead of addresses (\code{default = FALSE})?.
#' @param url_only boolean, only return the generated URLs (\code{default = FALSE})?
#'
#' @return
#' An \code{sf} object, containing the suggested addresses or landmark names of the reverse geocoded POIs.
#' @export
#'
#' @note If no addresses or landmarks are found near a POI, \code{NULL} for this POI is returned.
#' In this case the rows corresponding to this particular POI are missing and merging the POIs by row is not possible.
#' However, in the returned \code{sf} object, the column \code{"id"} matches the rows of the input POIs.
#' The \code{"id"} column can be used to join the original POIs.
#'
#' @examples
#' # Authentication
#' set_auth(
#' app_id = "<YOUR APP ID>",
#' app_code = "<YOUR APP CODE>"
#' )
#'
#' # Get addresses
#' addresses <- reverse_geocode(poi = poi, results = 3, landmarks = FALSE, url_only = TRUE)
#'
#' # Get landmarks
#' landmarks <- reverse_geocode(poi = poi, results = 3, landmarks = TRUE, url_only = TRUE)
reverse_geocode <- function(poi, results = 1, landmarks = FALSE, url_only = FALSE) {

# Check and preprocess addresses
.check_points(poi)
.check_max_results(results)

# Add authentication
url <- .add_auth(
url = "https://reverse.geocoder.api.here.com/6.2/reversegeocode.json?"
)

# Add point coords
poi <- sf::st_coordinates(
sf::st_transform(poi, 4326)
)
url = paste0(
url,
"&prox=", poi[, 2], ",", poi[, 1]
)

# Add RevGeo mode
url = paste0(
url,
"&mode=",
if (landmarks) {"retrieveLandmarks"} else {"retrieveAddresses"}
)

# Add max results
url = paste0(
url,
"&maxresults=",
results
)

# Return urls if chosen
if (url_only) return(url)

# Request and get content
data <- .get_content(
url = url
)
if (length(data) == 0) return(NULL)

# Extract information
if (landmarks) {
reverse <- .extract_landmarks(data)
} else {
reverse <- .extract_addresses(data)
}

# Create sf, data.table, data.frame
if (nrow(reverse) > 0) {
return(
sf::st_set_crs(
sf::st_as_sf(reverse, coords = c("lng", "lat")),
4326)
)
} else {
return(NULL)
}
}

.extract_addresses <- function(data) {
count <- 0
result <- data.table::rbindlist(lapply(data, function(con) {
count <<- count + 1
df <- jsonlite::fromJSON(con)
if (length(df$Response$View$Result) == 0) return(NULL)
data.table::data.table(
id = count,
rank = seq(1, nrow(df$Response$View$Result[[1]])),
distance = df$Response$View$Result[[1]]$Distance,
level = df$Response$View$Result[[1]]$MatchLevel,
lng = df$Response$View$Result[[1]]$Location$DisplayPosition$Longitude,
lat = df$Response$View$Result[[1]]$Location$DisplayPosition$Latitude,
label = df$Response$View$Result[[1]]$Location$Address$Label,
country = df$Response$View$Result[[1]]$Location$Address$Country,
state = df$Response$View$Result[[1]]$Location$Address$State,
county = df$Response$View$Result[[1]]$Location$Address$County,
city = df$Response$View$Result[[1]]$Location$Address$City,
district = df$Response$View$Result[[1]]$Location$Address$District,
street = df$Response$View$Result[[1]]$Location$Address$Street,
houseNumber = df$Response$View$Result[[1]]$Location$Address$HouseNumber,
postalCode = df$Response$View$Result[[1]]$Location$Address$PostalCode
)
}), fill = TRUE)
result
}

.extract_landmarks <- function(data) {
count <- 0
result <- data.table::rbindlist(lapply(data, function(con) {
count <<- count + 1
df <- jsonlite::fromJSON(con)
if (length(df$Response$View$Result) == 0) return(NULL)
data.table::data.table(
id = count,
rank = seq(1, nrow(df$Response$View$Result[[1]])),
distance = df$Response$View$Result[[1]]$Distance,
level = df$Response$View$Result[[1]]$MatchLevel,
lng = df$Response$View$Result[[1]]$Location$DisplayPosition$Longitude,
lat = df$Response$View$Result[[1]]$Location$DisplayPosition$Latitude,
name = df$Response$View$Result[[1]]$Location$Name,
label = df$Response$View$Result[[1]]$Location$Address$Label,
country = df$Response$View$Result[[1]]$Location$Address$Country,
state = df$Response$View$Result[[1]]$Location$Address$State
)
}), fill = TRUE)
result
}
Binary file modified R/sysdata.rda
Binary file not shown.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<!-- badges: end -->

R interface to the **HERE REST APIs**:
(1) geocode and autocomplete addresses using the **Geocoder API**;
(1) geocode and autocomplete addresses or reverse geocode POIs using the **Geocoder API**;
(2) routing directions, travel distance or time matrices and isolines using the **Routing API**;
(3) traffic flow and incident information from the **Traffic API**;
(4) weather forecasts, reports on current weather conditions and astronomical information at a specific location from the **Destination Weather API**.
Expand Down Expand Up @@ -46,6 +46,7 @@ No login yet? Get your free login here: [developer.here.com](https://developer.h

* **Geocode** addresses:<br>`locs <- geocode(addresses = c("Schweighofstrasse 190, Zürich, Schweiz", "Hardstrasse 48, Zürich, Schweiz"))`<br><br>
* **Autocomplete** addresses:<br>`suggestions <- autocomplete(addresses = c("Schweighofstrasse", "Hardstrasse"))`<br><br>
* **Reverse geocode** POIs:<br>`addresses <- reverse_geocode(poi = locs)`<br><br>
* Construct a **route** between points:<br>`routes <- route(start = locs_start, destination = locs_dest, mode = "car")`<br><br>
* Create a **route matrix** between points:<br>`route_matrix <- route_matrix(start = locs, mode = "car")`<br><br>
* Request **weather observations** at specific locations:<br>`observations <- weather(poi = locs, product = "observation")`<br><br>
Expand Down
8 changes: 8 additions & 0 deletions data-raw/Internal.R
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ url_geocode <-
geocode(addresses = poi$city, url_only = TRUE)
url_autocomplete <-
autocomplete(addresses = poi$city, results = 3, url_only = TRUE)
url_reverse_geocode_addresses <-
reverse_geocode(poi = poi, landmarks = FALSE, results = 3, url_only = TRUE)
url_reverse_geocode_landmarks <-
reverse_geocode(poi = poi, landmarks = TRUE, results = 3, url_only = TRUE)
url_route <-
route(start = poi[1:2, ], destination = poi[3:4, ], url_only = TRUE)
url_route_matrix <-
Expand All @@ -29,6 +33,8 @@ url_traffic_incidents <-
mock <- list(
geocode_response = hereR:::.get_content(url_geocode),
autocomplete_response = hereR:::.get_content(url_autocomplete),
reverse_geocode_addresses = hereR:::.get_content(url_reverse_geocode_addresses),
reverse_geocode_landmarks = hereR:::.get_content(url_reverse_geocode_landmarks),
route_response = hereR:::.get_content(url_route),
route_matrix_response = hereR:::.get_content(url_route_matrix),
isoline_response = hereR:::.get_content(url_isoline),
Expand All @@ -44,6 +50,8 @@ mock <- list(
example <- list(
geocode = geocode(addresses = poi$city),
autocomplete = autocomplete(addresses = poi$city, results = 3),
reverse_geocode_addresses = reverse_geocode(poi = poi, results = 3, landmarks = FALSE),
reverse_geocode_landmarks = reverse_geocode(poi = poi, results = 3, landmarks = TRUE),
route = route(start = poi[1:2, ], destination = poi[3:4, ]),
route_matrix = route_matrix(start = poi),
isoline = isoline(poi = poi),
Expand Down
2 changes: 1 addition & 1 deletion docs/404.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion docs/LICENSE.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion docs/articles/authentication.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 28 additions & 5 deletions docs/articles/geocoder.html

Large diffs are not rendered by default.

Loading

0 comments on commit 00a80f6

Please sign in to comment.