diff --git a/NEWS.md b/NEWS.md index 264c0b04..887aa0db 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,26 +1,36 @@ # nominatimlite 0.1.3 -- Skip API query tests on CRAN to avoid false positives. +- Skip API query tests on CRAN to avoid false positives. + +- Centralize API queries on (internal) function `api_call()`. + +- Queries fully honors now the [Nominatim Usage + Policy](https://operations.osmfoundation.org/policies/nominatim/). Queries + may be slower now. # nominatimlite 0.1.2 -- New internal: `nominatim_check_access()`. +- New internal: `nominatim_check_access()`. -- Adapt tests to `testthat` v3.1.0. +- Adapt tests to `testthat` v3.1.0. # nominatimlite 0.1.1 -- Adapt tests to **CRAN** checks. +- Adapt tests to **CRAN** checks. # nominatimlite 0.1.0 -- **CRAN** release. -- Adjust query rate limits to Nominatim policy. -- New `strict` argument on `geo_amenity()` and `geo_amenity_sf()`. -- Parameter `polygon` changed to `points_only` - ([#8](https://github.com/dieghernan/nominatimlite/issues/8)) thanks to - @jlacko. -- Package now falls gracefully if url not reachable. +- **CRAN** release. + +- Adjust query rate limits to Nominatim policy. + +- New `strict` argument on `geo_amenity()` and `geo_amenity_sf()`. + +- Parameter `polygon` changed to `points_only` + ([#8](https://github.com/dieghernan/nominatimlite/issues/8)) thanks to + @jlacko. + +- Package now falls gracefully if url not reachable. # nominatimlite 0.0.1 diff --git a/R/geo_address_lookup.R b/R/geo_address_lookup.R index af61e5bb..1f74403e 100644 --- a/R/geo_address_lookup.R +++ b/R/geo_address_lookup.R @@ -66,22 +66,14 @@ geo_address_lookup <- function(osm_ids, json <- tempfile(fileext = ".json") - # nocov start - res <- tryCatch( - download.file(url, json, mode = "wb", quiet = isFALSE(verbose)), - warning = function(e) { - return(NULL) - }, - error = function(e) { - return(NULL) - } - ) + res <- api_call(url, json, isFALSE(verbose)) - if (is.null(res)) { - message(url, " not reachable.", nodes) + # nocov start + if (isFALSE(res)) { + message(url, " not reachable.") result_out <- tibble::tibble(query = paste0(type, osm_ids), a = NA, b = NA) names(result_out) <- c("query", lat, long) - return(result_out) + return(invisible(result_out)) } # nocov end @@ -101,7 +93,7 @@ geo_address_lookup <- function(osm_ids, message("No results for query ", nodes) result_out <- tibble::tibble(query = paste0(type, osm_ids), a = NA, b = NA) names(result_out) <- c("query", lat, long) - return(result_out) + return(invisible(result_out)) } # Rename diff --git a/R/geo_address_lookup_sf.R b/R/geo_address_lookup_sf.R index e4d39441..30d7a9b2 100644 --- a/R/geo_address_lookup_sf.R +++ b/R/geo_address_lookup_sf.R @@ -81,21 +81,13 @@ geo_address_lookup_sf <- function(osm_ids, json <- tempfile(fileext = ".geojson") - # nocov start - res <- tryCatch( - download.file(url, json, mode = "wb", quiet = isFALSE(verbose)), - warning = function(e) { - return(NULL) - }, - error = function(e) { - return(NULL) - } - ) + res <- api_call(url, json, quiet = isFALSE(verbose)) - if (is.null(res)) { - message(url, " not reachable.", nodes) + # nocov start + if (isFALSE(res)) { + message(url, " not reachable.") result_out <- data.frame(query = paste0(type, osm_ids)) - return(result_out) + return(invisible(result_out)) } # nocov end @@ -109,7 +101,7 @@ geo_address_lookup_sf <- function(osm_ids, if (length(names(sfobj)) == 1) { message("No results for query ", nodes) result_out <- data.frame(query = paste0(type, osm_ids)) - return(result_out) + return(invisible(result_out)) } diff --git a/R/geo_amenity.R b/R/geo_amenity.R index 332f0804..e7b09c54 100644 --- a/R/geo_amenity.R +++ b/R/geo_amenity.R @@ -92,10 +92,6 @@ geo_amenity <- function(bbox, all_res <- NULL for (i in seq_len(length(amenity))) { - if (i > 1) { - Sys.sleep(1) - } - res_single <- geo_amenity_single( bbox = bbox, amenity = amenity[i], @@ -179,22 +175,15 @@ geo_amenity_single <- function(bbox, json <- tempfile(fileext = ".json") - # nocov start - res <- tryCatch( - download.file(url, json, mode = "wb", quiet = isFALSE(verbose)), - warning = function(e) { - return(NULL) - }, - error = function(e) { - return(NULL) - } - ) - if (is.null(res)) { + res <- api_call(url, json, isFALSE(verbose)) + + # nocov start + if (isFALSE(res)) { message(url, " not reachable.") result_out <- tibble::tibble(query = amenity, a = NA, b = NA) names(result_out) <- c("query", lat, long) - return(result_out) + return(invisible(result_out)) } # nocov end @@ -214,7 +203,7 @@ geo_amenity_single <- function(bbox, message("No results for query ", amenity) result_out <- tibble::tibble(query = amenity, a = NA, b = NA) names(result_out) <- c("query", lat, long) - return(result_out) + return(invisible(result_out)) } # Rename diff --git a/R/geo_amenity_sf.R b/R/geo_amenity_sf.R index f25b4a12..5e086162 100644 --- a/R/geo_amenity_sf.R +++ b/R/geo_amenity_sf.R @@ -83,10 +83,6 @@ geo_amenity_sf <- function(bbox, all_res <- NULL for (i in seq_len(length(amenity))) { - if (i > 1) { - Sys.sleep(1) - } - res_single <- geo_amenity_sf_single( bbox = bbox, amenity = amenity[i], @@ -163,21 +159,13 @@ geo_amenity_sf_single <- function(bbox, json <- tempfile(fileext = ".geojson") - # nocov start - res <- tryCatch( - download.file(url, json, mode = "wb", quiet = isFALSE(verbose)), - warning = function(e) { - return(NULL) - }, - error = function(e) { - return(NULL) - } - ) + res <- api_call(url, json, isFALSE(verbose)) - if (is.null(res)) { + # nocov start + if (isFALSE(res)) { message(url, " not reachable.") result_out <- data.frame(query = amenity) - return(result_out) + return(invisible(result_out)) } # nocov end @@ -193,7 +181,7 @@ geo_amenity_sf_single <- function(bbox, if (length(names(sfobj)) == 1) { message("No results for query ", amenity) result_out <- data.frame(query = amenity) - return(result_out) + return(invisible(result_out)) } diff --git a/R/geo_lite.R b/R/geo_lite.R index 5532fb22..ad07db4c 100644 --- a/R/geo_lite.R +++ b/R/geo_lite.R @@ -64,10 +64,6 @@ geo_lite <- function(address, all_res <- NULL for (i in seq_len(length(address))) { - if (i > 1) { - Sys.sleep(1) - } - res_single <- geo_lite_single( address = address[i], lat, @@ -124,22 +120,14 @@ geo_lite_single <- function(address, json <- tempfile(fileext = ".json") - # nocov start - res <- tryCatch( - download.file(url, json, mode = "wb", quiet = isFALSE(verbose)), - warning = function(e) { - return(NULL) - }, - error = function(e) { - return(NULL) - } - ) + res <- api_call(url, json, quiet = isFALSE(verbose)) - if (is.null(res)) { + # nocov start + if (isFALSE(res)) { message(url, " not reachable.") result_out <- tibble::tibble(query = address, a = NA, b = NA) names(result_out) <- c("query", lat, long) - return(result_out) + return(invisible(result_out)) } # nocov end @@ -160,7 +148,7 @@ geo_lite_single <- function(address, message("No results for query ", address) result_out <- tibble::tibble(query = address, a = NA, b = NA) names(result_out) <- c("query", lat, long) - return(result_out) + return(invisible(result_out)) } # Rename diff --git a/R/geo_lite_sf.R b/R/geo_lite_sf.R index 095efc76..d0c276a9 100644 --- a/R/geo_lite_sf.R +++ b/R/geo_lite_sf.R @@ -96,10 +96,6 @@ geo_lite_sf <- function(address, all_res <- NULL for (i in seq_len(length(address))) { - if (i > 1) { - Sys.sleep(1) - } - res_single <- geo_lite_sf_single( address = address[i], limit, @@ -159,21 +155,15 @@ geo_lite_sf_single <- function(address, json <- tempfile(fileext = ".geojson") + res <- api_call(url, json, quiet = isFALSE(verbose)) + + # nocov start - res <- tryCatch( - download.file(url, json, mode = "wb", quiet = isFALSE(verbose)), - warning = function(e) { - return(NULL) - }, - error = function(e) { - return(NULL) - } - ) - if (is.null(res)) { + if (isFALSE(res)) { message(url, " not reachable.") result_out <- data.frame(query = address) - return(result_out) + return(invisible(result_out)) } # nocov end @@ -187,7 +177,7 @@ geo_lite_sf_single <- function(address, if (length(names(sfobj)) == 1) { message("No results for query ", address) result_out <- data.frame(query = address) - return(result_out) + return(invisible(result_out)) } # Prepare output diff --git a/R/nominatim_check_access.R b/R/nominatim_check_access.R index f7194e4a..75139e05 100644 --- a/R/nominatim_check_access.R +++ b/R/nominatim_check_access.R @@ -1,6 +1,6 @@ #' Check access to Nominatim API #' -#' @concept helper +#' @family api_management #' #' @description #' Check if R has access to resources at @@ -8,29 +8,33 @@ #' #' @return a logical. #' -#' @examples +#' @seealso +#' #' +#' @examples +#' \donttest{ #' nominatim_check_access() +#' } #' @keywords internal #' @export nominatim_check_access <- function() { - url <- paste0( - "https://nominatim.openstreetmap.org/search?q=", - "Madrid&format=json&limit=1" - ) - # nocov start - access <- - tryCatch( - download.file(url, destfile = tempfile(), quiet = TRUE), - warning = function(e) { - return(FALSE) - } - ) + url <- "https://nominatim.openstreetmap.org/status.php?format=json" + destfile <- tempfile(fileext = ".json") + + api_res <- api_call(url, destfile, TRUE) - if (isFALSE(access)) { + # nocov start + if (isFALSE(api_res)) { return(FALSE) - } else { + } + # nocov end + result <- tibble::as_tibble(jsonlite::fromJSON(destfile, flatten = TRUE)) + + # nocov start + if (result$status == 0 || result$message == "OK") { return(TRUE) + } else { + return(FALSE) } # nocov end } @@ -47,3 +51,61 @@ skip_if_api_server <- function() { return(invisible()) # nocov end } + + +#' Helper function for centralize API queries +#' +#' @description +#' A wrapper of [utils::download.file()]. On warning on error it will +#' retry the call. Requests are adjusted to the rate of 1 query per second. +#' +#' See [Nominatim Usage +#' Policy](https://operations.osmfoundation.org/policies/nominatim/). +#' +#' @family api_management +#' +#' @inheritParams utils::download.file +#' @return A logical `TRUE/FALSE` +#' +#' @keywords internal +#' +api_call <- function(url, destfile, quiet) { + # nocov start + dwn_res <- + tryCatch( + download.file(url, destfile = destfile, quiet = quiet, mode = "wb"), + warning = function(e) { + return(FALSE) + }, + error = function(e) { + return(FALSE) + } + ) + # nocov end + # Always sleep to make 1 call per sec + Sys.sleep(1) + + # nocov start + if (isFALSE(dwn_res)) { + if (isFALSE(quiet)) message("Retrying query") + Sys.sleep(1) + + dwn_res <- + tryCatch( + download.file(url, destfile = destfile, quiet = quiet, mode = "wb"), + warning = function(e) { + return(FALSE) + }, + error = function(e) { + return(FALSE) + } + ) + } + + if (isFALSE(dwn_res)) { + return(FALSE) + } else { + return(TRUE) + } + # nocov end +} diff --git a/R/reverse_geo_lite.R b/R/reverse_geo_lite.R index 18017707..5a7d7be7 100644 --- a/R/reverse_geo_lite.R +++ b/R/reverse_geo_lite.R @@ -103,10 +103,6 @@ reverse_geo_lite <- function(lat, all_res <- NULL for (i in seq_len(length(long_cap))) { - if (i > 1) { - Sys.sleep(1) - } - res_single <- reverse_geo_lite_single( lat_cap[i], long_cap[i], @@ -163,22 +159,15 @@ reverse_geo_lite_single <- function(lat_cap, json <- tempfile(fileext = ".json") - # nocov start - res <- tryCatch( - download.file(url, json, mode = "wb", quiet = isFALSE(verbose)), - warning = function(e) { - return(NULL) - }, - error = function(e) { - return(NULL) - } - ) + res <- api_call(url, json, quiet = isFALSE(verbose)) + - if (is.null(res)) { + # nocov start + if (isFALSE(res)) { message(url, " not reachable.") result_out <- tibble::tibble(ad = NA) names(result_out) <- address - return(result_out) + return(invisible(result_out)) } # nocov end @@ -192,7 +181,7 @@ reverse_geo_lite_single <- function(lat_cap, ) result_out <- tibble::tibble(ad = NA) names(result_out) <- address - return(result_out) + return(invisible(result_out)) } diff --git a/R/reverse_geo_lite_sf.R b/R/reverse_geo_lite_sf.R index 9a584eba..06aaf144 100644 --- a/R/reverse_geo_lite_sf.R +++ b/R/reverse_geo_lite_sf.R @@ -124,11 +124,6 @@ reverse_geo_lite_sf <- function(lat, all_res <- NULL for (i in seq_len(length(long_cap))) { - if (i > 1) { - Sys.sleep(1) - } - - res_single <- reverse_geo_lite_sf_single( lat_cap[i], long_cap[i], @@ -191,22 +186,15 @@ reverse_geo_lite_sf_single <- function(lat_cap, json <- tempfile(fileext = ".geojson") - # nocov start - res <- tryCatch( - download.file(url, json, mode = "wb", quiet = isFALSE(verbose)), - warning = function(e) { - return(NULL) - }, - error = function(e) { - return(NULL) - } - ) + res <- api_call(url, json, quiet = isFALSE(verbose)) - if (is.null(res)) { + + # nocov start + if (isFALSE(res)) { message(url, " not reachable.") result_out <- tibble::tibble(ad = NA) names(result_out) <- address - return(result_out) + return(invisible(result_out)) } # nocov end @@ -243,7 +231,7 @@ reverse_geo_lite_sf_single <- function(lat_cap, result_out <- cbind(result_out, coords) } - return(result_out) + return(invisible(result_out)) } # Prepare output diff --git a/README.md b/README.md index 86409b01..7fdd75e6 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,7 @@ data from the geocoder service. |:-------------------------------------------|---------:|-----------:|:-------------------------------------------------------------------------------------------------------------------------------------------------------| | 1600 Pennsylvania Ave NW, Washington, DC | 38.89770 | -77.03655 | White House, 1600, Pennsylvania Avenue Northwest, Washington, District of Columbia, 20500, United States | | 600 Montgomery St, San Francisco, CA 94111 | 37.79520 | -122.40279 | Transamerica Pyramid, 600, Montgomery Street, Chinatown, San Francisco, San Francisco City and County, San Francisco, California, 94111, United States | -| 233 S Wacker Dr, Chicago, IL 60606 | 41.87535 | -87.63576 | South Wacker Drive, Printer’s Row, Loop, Chicago, Cook County, Illinois, 60606, United States | +| 233 S Wacker Dr, Chicago, IL 60606 | 41.87887 | -87.63591 | Willis Tower, 233, South Wacker Drive, Printer’s Row, Loop, Chicago, Cook County, Illinois, 60606, United States | To perform reverse geocoding (obtaining addresses from geographic coordinates), we can use the `reverse_geo_lite()` function. The @@ -185,7 +185,7 @@ reverse <- reverse_geo_lite( |:-------------------------------------------------------------------------------------------------------------------------------------------------------|---------:|-----------:| | White House, 1600, Pennsylvania Avenue Northwest, Washington, District of Columbia, 20500, United States | 38.89770 | -77.03655 | | Transamerica Pyramid, 600, Montgomery Street, Chinatown, San Francisco, San Francisco City and County, San Francisco, California, 94111, United States | 37.79520 | -122.40279 | -| South Wacker Drive, Printer’s Row, Loop, Chicago, Cook County, Illinois, 60606, United States | 41.87535 | -87.63576 | +| Willis Tower, 233, South Wacker Drive, Printer’s Row, Loop, Chicago, Cook County, Illinois, 60606, United States | 41.87887 | -87.63591 | For more advance users, see [Nominatim docs](https://nominatim.org/release-docs/latest/api/Search/) to check diff --git a/codemeta.json b/codemeta.json index 7e333fce..a243224e 100644 --- a/codemeta.json +++ b/codemeta.json @@ -199,7 +199,7 @@ }, "applicationCategory": "cartography", "keywords": ["r", "geocoding", "openstreetmap", "address", "nominatim", "reverse-geocoding", "rstats", "shapefile", "r-package", "spatial", "cran", "api-wrapper"], - "fileSize": "183.874KB", + "fileSize": "192.098KB", "citation": [ { "@type": "SoftwareSourceCode", diff --git a/man/api_call.Rd b/man/api_call.Rd new file mode 100644 index 00000000..96e8ee3f --- /dev/null +++ b/man/api_call.Rd @@ -0,0 +1,35 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/nominatim_check_access.R +\name{api_call} +\alias{api_call} +\title{Helper function for centralize API queries} +\usage{ +api_call(url, destfile, quiet) +} +\arguments{ +\item{url}{a \code{\link{character}} string (or longer vector e.g., + for the \code{"libcurl"} method) naming the URL of a resource to be + downloaded.} + +\item{destfile}{a character string (or vector, see the \code{url} + argument) with the file path where the downloaded file is to be + saved. Tilde-expansion is performed.} + +\item{quiet}{If \code{TRUE}, suppress status messages (if any), and + the progress bar.} +} +\value{ +A logical \code{TRUE/FALSE} +} +\description{ +A wrapper of \code{\link[utils:download.file]{utils::download.file()}}. On warning on error it will +retry the call. Requests are adjusted to the rate of 1 query per second. + +See \href{https://operations.osmfoundation.org/policies/nominatim/}{Nominatim Usage Policy}. +} +\seealso{ +Other api_management: +\code{\link{nominatim_check_access}()} +} +\concept{api_management} +\keyword{internal} diff --git a/man/figures/README-McDonalds-1.png b/man/figures/README-McDonalds-1.png index c0e4c1b8..86518ab8 100644 Binary files a/man/figures/README-McDonalds-1.png and b/man/figures/README-McDonalds-1.png differ diff --git a/man/figures/README-line-object-1.png b/man/figures/README-line-object-1.png index 068ad5dd..c8b341d8 100644 Binary files a/man/figures/README-line-object-1.png and b/man/figures/README-line-object-1.png differ diff --git a/man/figures/README-penta-1.png b/man/figures/README-penta-1.png index 4e5d1f61..6fa13c5f 100644 Binary files a/man/figures/README-penta-1.png and b/man/figures/README-penta-1.png differ diff --git a/man/nominatim_check_access.Rd b/man/nominatim_check_access.Rd index 4e1bea74..79f4594c 100644 --- a/man/nominatim_check_access.Rd +++ b/man/nominatim_check_access.Rd @@ -14,8 +14,15 @@ Check if R has access to resources at \url{https://nominatim.openstreetmap.org}. } \examples{ - +\donttest{ nominatim_check_access() } -\concept{helper} +} +\seealso{ +\url{https://nominatim.org/release-docs/latest/api/Status/} + +Other api_management: +\code{\link{api_call}()} +} +\concept{api_management} \keyword{internal} diff --git a/vignettes/McDonalds-1.png b/vignettes/McDonalds-1.png index 3a0365e7..99703bf4 100644 Binary files a/vignettes/McDonalds-1.png and b/vignettes/McDonalds-1.png differ diff --git a/vignettes/nominatimlite.Rmd b/vignettes/nominatimlite.Rmd index a7ab4a32..ee6bca6e 100644 --- a/vignettes/nominatimlite.Rmd +++ b/vignettes/nominatimlite.Rmd @@ -113,7 +113,7 @@ the geocoder service. |:------------------------------------------|--------:|----------:|:------------------------------------------------------------------------------------------------------------------------------------------------------| |1600 Pennsylvania Ave NW, Washington, DC | 38.89770| -77.03655|White House, 1600, Pennsylvania Avenue Northwest, Washington, District of Columbia, 20500, United States | |600 Montgomery St, San Francisco, CA 94111 | 37.79520| -122.40279|Transamerica Pyramid, 600, Montgomery Street, Chinatown, San Francisco, San Francisco City and County, San Francisco, California, 94111, United States | -|233 S Wacker Dr, Chicago, IL 60606 | 41.87535| -87.63576|South Wacker Drive, Printer's Row, Loop, Chicago, Cook County, Illinois, 60606, United States | +|233 S Wacker Dr, Chicago, IL 60606 | 41.87887| -87.63591|Willis Tower, 233, South Wacker Drive, Printer's Row, Loop, Chicago, Cook County, Illinois, 60606, United States | To perform reverse geocoding (obtaining addresses from geographic coordinates), we can use the `reverse_geo_lite()` function. The arguments are similar to the @@ -134,7 +134,7 @@ reverse <- reverse_geo_lite( |:------------------------------------------------------------------------------------------------------------------------------------------------------|--------:|----------:| |White House, 1600, Pennsylvania Avenue Northwest, Washington, District of Columbia, 20500, United States | 38.89770| -77.03655| |Transamerica Pyramid, 600, Montgomery Street, Chinatown, San Francisco, San Francisco City and County, San Francisco, California, 94111, United States | 37.79520| -122.40279| -|South Wacker Drive, Printer's Row, Loop, Chicago, Cook County, Illinois, 60606, United States | 41.87535| -87.63576| +|Willis Tower, 233, South Wacker Drive, Printer's Row, Loop, Chicago, Cook County, Illinois, 60606, United States | 41.87887| -87.63591| For more advance users, see [Nominatim docs](https://nominatim.org/release-docs/latest/api/Search/) to check the diff --git a/vignettes/penta-1.png b/vignettes/penta-1.png index 15b1c28a..452505e3 100644 Binary files a/vignettes/penta-1.png and b/vignettes/penta-1.png differ