diff --git a/.Rbuildignore b/.Rbuildignore index 4074d72c..f18b3f78 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -29,3 +29,4 @@ ^CODE_OF_CONDUCT\.md$ ^CONTRIBUTING\.md$ ^Rplots\.pdf$ +^vignettes/articles$ diff --git a/CITATION.cff b/CITATION.cff index e0ef5ed0..ae31f4c6 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -109,21 +109,6 @@ references: - type: url value: https://arxiv.org/abs/1403.2805 version: '>= 1.7.0' -- type: software - title: lifecycle - abstract: 'lifecycle: Manage the Life Cycle of your Package Functions' - notes: Imports - url: https://lifecycle.r-lib.org/ - repository: https://CRAN.R-project.org/package=lifecycle - authors: - - family-names: Henry - given-names: Lionel - email: lionel@posit.co - - family-names: Wickham - given-names: Hadley - email: hadley@posit.co - orcid: https://orcid.org/0000-0003-4757-117X - year: '2024' - type: software title: sf abstract: 'sf: Simple Features for R' @@ -211,6 +196,21 @@ references: email: xie@yihui.name orcid: https://orcid.org/0000-0003-0645-5666 year: '2024' +- type: software + title: lifecycle + abstract: 'lifecycle: Manage the Life Cycle of your Package Functions' + notes: Suggests + url: https://lifecycle.r-lib.org/ + repository: https://CRAN.R-project.org/package=lifecycle + authors: + - family-names: Henry + given-names: Lionel + email: lionel@posit.co + - family-names: Wickham + given-names: Hadley + email: hadley@posit.co + orcid: https://orcid.org/0000-0003-4757-117X + year: '2024' - type: software title: rmarkdown abstract: 'rmarkdown: Dynamic Documents for R' @@ -266,6 +266,21 @@ references: email: hadley@posit.co year: '2024' version: '>= 3.0.0' +- type: software + title: tibble + abstract: 'tibble: Simple Data Frames' + notes: Suggests + url: https://tibble.tidyverse.org/ + repository: https://CRAN.R-project.org/package=tibble + authors: + - family-names: Müller + given-names: Kirill + email: kirill@cynkra.com + orcid: https://orcid.org/0000-0002-1416-3412 + - family-names: Wickham + given-names: Hadley + email: hadley@rstudio.com + year: '2024' - type: software title: tidygeocoder abstract: 'tidygeocoder: Geocoding Made Easy' diff --git a/DESCRIPTION b/DESCRIPTION index 0d83e8ed..b881c96b 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -24,24 +24,27 @@ Depends: Imports: dplyr (>= 1.0.0), jsonlite (>= 1.7.0), - lifecycle, sf (>= 0.9.0), utils Suggests: arcgeocoder, ggplot2 (>= 3.0.0), knitr, + lifecycle, rmarkdown, testthat (>= 3.0.0), + tibble, tidygeocoder VignetteBuilder: knitr -Config/Needs/website: dieghernan/gitdevr, remotes, devtools, tidyverse +Config/Needs/website: dieghernan/gitdevr, remotes, devtools, tidyverse, + leaflet, reactable, crosswalk, tidyr Config/testthat/edition: 3 Config/testthat/parallel: true Copyright: Data © OpenStreetMap contributors, ODbL 1.0. Encoding: UTF-8 +LazyData: true Roxygen: list(markdown = TRUE) RoxygenNote: 7.3.1 X-schema.org-applicationCategory: cartography diff --git a/NAMESPACE b/NAMESPACE index 6c64dc25..a199ffdb 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -7,6 +7,8 @@ export(geo_amenity) export(geo_amenity_sf) export(geo_lite) export(geo_lite_sf) +export(geo_lite_struct) +export(geo_lite_struct_sf) export(nominatim_check_access) export(reverse_geo_lite) export(reverse_geo_lite_sf) diff --git a/NEWS.md b/NEWS.md index ae47982d..dd17ee91 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,9 +1,39 @@ # nominatimlite (development version) +- New functions: + + - `geo_lite_struct()` and `geo_lite_struct_sf()` for performing structured + queries. + - Bring back `geo_amenity()` and `geo_amenity_sf()` as a wrapper of + `geo_lite_struct()` / `geo_lite_struct_sf(),` so now are more robust and + compatible with **sf** objects. + +- Improve unnesting of fields when requiring `extratags`, i.e. + `custom_query = list(extratags = TRUE)`. + - It is possible to use **nominatimlite** with local server thanks to the new argument `nominatim_server` (#42 \@alexwhitedatamine). + - Adapt endpoints to **Nominatim v4.4.0** `[Python-only]`. +- `nominatimlite::osm_amenities` data set re-introduced, updated and with + additional description fields. + +- API call for non-spatial function uses now JSONV2 format (`&format=jsonv2`). + This implies the following changes in the output: + + - `class` renamed to `category`. + - additional field `place_rank` with the search rank of the object. + +- `custom_query` argument can use vectors and `logical`: + + ``` r + geo_lite(address = "New York", + custom_query = list(addressdetails = TRUE, + viewbox = c(-60, -20, 60, 20)) + ) + ``` + # nominatimlite 0.3.0 - Add a `progressbar` parameter to `geo_lite()`, `geo_lite_sf()`, diff --git a/R/bbox_to_poly.R b/R/bbox_to_poly.R index fde22808..c5a0a3f2 100644 --- a/R/bbox_to_poly.R +++ b/R/bbox_to_poly.R @@ -5,9 +5,9 @@ #' #' @family spatial #' -#' @param bbox numeric vector of 4 elements representing the coordinates of the +#' @param bbox Numeric vector of 4 elements representing the coordinates of the #' bounding box. Values should be `c(xmin, ymin, xmax, ymax)`. -#' @param xmin,ymin,xmax,ymax alternatively, you can use these named parameters +#' @param xmin,ymin,xmax,ymax Alternatively, you can use these named parameters #' instead of `bbox`. #' #' @inheritParams sf::st_sf diff --git a/R/data.R b/R/data.R new file mode 100644 index 00000000..9a8b1ee4 --- /dev/null +++ b/R/data.R @@ -0,0 +1,35 @@ +#' OpenStreetMap amenity database +#' +#' @description +#' Database with the list of amenities available on OpenStreetMap. +#' +#' @family datasets +#' @family amenity +#' +#' @encoding UTF-8 +#' +#' @name osm_amenities +#' +#' @docType data +#' +#' @format +#' A [`tibble`][tibble::tibble] with with +#' `r prettyNum(nrow(nominatimlite::osm_amenities), big.mark=",")` rows and +#' fields: +#' \describe{ +#' \item{category}{The category of the amenity.} +#' \item{amenity}{The value of the amenity.} +#' \item{comment}{A brief description of the type of amenity.} +#' } +#' +#' +#' @source +#' +#' @note Data extracted on **03 April 2024**. +#' +#' @examples +#' +#' data("osm_amenities") +#' +#' osm_amenities +NULL diff --git a/R/deprecated.R b/R/deprecated.R deleted file mode 100644 index 63a0b11b..00000000 --- a/R/deprecated.R +++ /dev/null @@ -1,66 +0,0 @@ -#' Geocode amenities -#' -#' @description -#' `r lifecycle::badge("defunct")` -#' -#' This operation is not supported any more. Use -#' [arcgeocoder::arc_geo_categories()] instead. -#' -#' -#' @keywords internal -#' @name geo_amenity -#' @rdname geo_amenity -#' -#' @param bbox,... Deprecated -#' -#' @return An error. -#' -#' -#' @export -#' @examples -#' \donttest{ -#' # Madrid, Spain -#' -#' library(arcgeocoder) -#' library(ggplot2) -#' -#' bbox <- c(-3.888954, 40.311977, -3.517916, 40.643729) -#' -#' # Food -#' rest_pub <- arc_geo_categories( -#' bbox = bbox, category = "Bakery,Bar or Pub", -#' full_results = TRUE, -#' limit = 50 -#' ) -#' -#' rest_pub -#' } -geo_amenity <- function(bbox = NULL, ...) { - if (requireNamespace("lifecycle", quietly = TRUE)) { - lifecycle::deprecate_stop("0.3.0", "geo_amenity()", - with = "arcgeocoder::arc_geo_categories()", - details = paste( - "Operation not supported any", - "more by the Nominatim API." - ) - ) - } -} - -#' -#' @name geo_amenity_sf -#' @rdname geo_amenity -#' -#' @keywords internal -#' @export -geo_amenity_sf <- function(bbox = NULL, ...) { - if (requireNamespace("lifecycle", quietly = TRUE)) { - lifecycle::deprecate_stop("0.3.0", "geo_amenity_sf()", - with = "arcgeocoder::arc_geo_categories()", - details = paste( - "Operation not supported any", - "more by the Nominatim API." - ) - ) - } -} diff --git a/R/geo_address_lookup.R b/R/geo_address_lookup.R index 8b7b83c2..f2469648 100644 --- a/R/geo_address_lookup.R +++ b/R/geo_address_lookup.R @@ -10,9 +10,9 @@ #' @family lookup #' @family geocoding #' -#' @param osm_ids vector of OSM identifiers as **numeric** +#' @param osm_ids Vector of OSM identifiers as **numeric** #' (`c(00000, 11111, 22222)`). -#' @param type vector character of the type of the OSM type associated to each +#' @param type Vector character of the type of the OSM type associated to each #' `osm_ids`. Possible values are node (`"N"`), way (`"W"`) or relation #' (`"R"`). If a single value is provided it would be recycled. #' @@ -64,7 +64,7 @@ geo_address_lookup <- function(osm_ids, nodes <- paste0(type, osm_ids, collapse = ",") # Compose url - url <- paste0(api, "osm_ids=", nodes, "&format=json") + url <- paste0(api, "osm_ids=", nodes, "&format=jsonv2") if (full_results) url <- paste0(url, "&addressdetails=1") @@ -81,14 +81,12 @@ geo_address_lookup <- function(osm_ids, # Keep a tbl with the query tbl_query <- dplyr::tibble(query = paste0(type, osm_ids)) - # nocov start # If no response... if (isFALSE(res)) { message(url, " not reachable.") out <- empty_tbl(tbl_query, lat, long) return(invisible(out)) } - # nocov end result <- dplyr::as_tibble(jsonlite::fromJSON(json, flatten = TRUE)) diff --git a/R/geo_address_lookup_sf.R b/R/geo_address_lookup_sf.R index 91c13429..6dc4ef68 100644 --- a/R/geo_address_lookup_sf.R +++ b/R/geo_address_lookup_sf.R @@ -97,14 +97,12 @@ geo_address_lookup_sf <- function(osm_ids, # Keep a tbl with the query tbl_query <- dplyr::tibble(query = paste0(type, osm_ids)) - # nocov start # If no response... if (isFALSE(res)) { message(url, " not reachable.") out <- empty_sf(tbl_query) return(invisible(out)) } - # nocov end # Read sfobj <- sf::read_sf(json, stringsAsFactors = FALSE) diff --git a/R/geo_amenity.R b/R/geo_amenity.R new file mode 100644 index 00000000..0c1e7470 --- /dev/null +++ b/R/geo_amenity.R @@ -0,0 +1,144 @@ +#' Geocode amenities +#' +#' @description +#' This function search [amenities][osm_amenities] as defined by OpenStreetMap +#' on a restricted area defined by a bounding box in the form +#' `(, , , )`. This function returns the +#' [`tibble`][tibble::tibble] associated with the query, see [geo_amenity_sf()] +#' for retrieving the data as a spatial object ([`sf`][sf::st_sf] format). +#' +#' @family amenity +#' @family geocoding +#' +#' @param bbox The bounding box (viewbox) used to limit the search. It could be: +#' - A numeric vector of **longitude** (`x`) and **latitude** (`y`) +#' `(xmin, ymin, xmax, ymax)`. See **Details**. +#' - A [`sf`][sf::st_sf] or [`sfc`][sf::st_sfc] object. +#' @param amenity A `character` (or a vector of `character`s) with the +#' amenities to be geolocated (i.e. `c("pub", "restaurant")`). See +#' [nominatimlite::osm_amenities]. +#' @param strict Logical `TRUE/FALSE`. Force the results to be included inside +#' the `bbox`. Note that Nominatim default behavior may return results located +#' outside the provided bounding box. +#' @inheritParams geo_lite_struct +#' @inheritParams geo_lite +#' +#' @details +#' +#' Bounding boxes can be located using different online tools, as +#' [Bounding Box Tool](https://boundingbox.klokantech.com/). +#' +#' For a full list of valid amenities see +#' and [osm_amenities]. +#' +#' See for additional +#' parameters to be passed to `custom_query`. +#' +#' +#' @return +#' +#' ```{r child = "man/chunks/tibbleout.Rmd"} +#' ``` +#' +#' @export +#' +#' @examplesIf nominatim_check_access() +#' \donttest{ +#' # Times Square, NY, USA +#' bbox <- c( +#' -73.9894467311, 40.75573629, +#' -73.9830630737, 40.75789245 +#' ) +#' +#' geo_amenity( +#' bbox = bbox, +#' amenity = "restaurant" +#' ) +#' +#' # Several amenities +#' geo_amenity( +#' bbox = bbox, +#' amenity = c("restaurant", "pub") +#' ) +#' +#' # Increase limit and use with strict +#' geo_amenity( +#' bbox = bbox, +#' amenity = c("restaurant", "pub"), +#' limit = 10, +#' strict = TRUE +#' ) +#' } +geo_amenity <- function( + bbox, amenity, lat = "lat", long = "lon", limit = 1, full_results = FALSE, + return_addresses = TRUE, verbose = FALSE, + nominatim_server = "https://nominatim.openstreetmap.org/", + progressbar = TRUE, custom_query = list(), strict = FALSE) { + if (limit > 50) { + message(paste( + "Nominatim provides 50 results as a maximum. ", + "Your query may be incomplete" + )) + limit <- min(50, limit) + } + + # bbox types + if (any(inherits(bbox, "sf"), inherits(bbox, "sfc"))) { + tolonlat <- sf::st_transform(bbox, 4326) + bbox <- as.vector(sf::st_bbox(tolonlat)) + } + bbox <- as.vector(bbox) + + # Overwrite custom query + custom_query <- as.list(custom_query) + custom_query$viewbox <- bbox + custom_query$bounded <- TRUE + + # Dedupe for query + key <- unique(amenity) + + # Set progress bar + ntot <- length(key) + # Set progress bar if n > 1 + progressbar <- all(progressbar, ntot > 1) + if (progressbar) { + pb <- txtProgressBar(min = 0, max = ntot, width = 50, style = 3) + } + seql <- seq(1, ntot, 1) + + seql <- seq(1, ntot, 1) + + all_res <- lapply(seql, function(x) { + ad <- key[x] + if (progressbar) { + setTxtProgressBar(pb, x) + } + + geo_lite_struct( + amenity = ad, lat = lat, long = long, limit = limit, + full_results = full_results, + return_addresses = return_addresses, verbose = verbose, + nominatim_server = nominatim_server, + custom_query = custom_query + ) + }) + if (progressbar) close(pb) + + all_res <- dplyr::bind_rows(all_res) + + # Clean columns and names + nm <- names(all_res) + nm[nm == "q_amenity"] <- "query" + names(all_res) <- nm + all_res <- all_res[, !grepl("^q_", nm)] + + if (strict) { + bbox_sf <- bbox_to_poly(bbox) + all_res_sf <- sf::st_as_sf(all_res, coords = c("lon", "lat"), crs = 4326) + + int <- as.vector(sf::st_intersects(all_res_sf, bbox_sf, sparse = FALSE)) + all_res <- all_res[int, ] + } + + return(all_res) +} diff --git a/R/geo_amenity_sf.R b/R/geo_amenity_sf.R new file mode 100644 index 00000000..c9215c37 --- /dev/null +++ b/R/geo_amenity_sf.R @@ -0,0 +1,130 @@ +#' Geocode amenities in \CRANpkg{sf} format +#' +#' @description +#' This function search [amenities][osm_amenities] as defined by OpenStreetMap +#' on a restricted area defined by a bounding box in the form +#' `(, , , )`. This function returns the spatial +#' object associated with the query using \CRANpkg{sf}, see [geo_amenity()] for +#' retrieving the data in [`tibble`][tibble::tibble] format. +#' +#' @family amenity +#' @family geocoding +#' @family spatial +#' +#' @inheritParams geo_amenity +#' @inheritParams geo_lite_sf +#' +#' @details +#' +#' Bounding boxes can be located using different online tools, as +#' [Bounding Box Tool](https://boundingbox.klokantech.com/). +#' +#' For a full list of valid amenities see +#' and [osm_amenities]. +#' +#' See for additional +#' parameters to be passed to `custom_query`. +#' +#' +#' @inheritSection geo_lite_sf About Geometry Types +#' +#' @return +#' +#' ```{r child = "man/chunks/sfout.Rmd"} +#' ``` +#' +#' @export +#' +#' @examplesIf nominatim_check_access() +#' \donttest{ +#' # Usera, Madrid +#' +#' library(ggplot2) +#' mad <- geo_lite_sf("Usera, Madrid, Spain", points_only = FALSE) +#' +#' +#' # Restaurants, pubs and schools +#' +#' rest_pub <- geo_amenity_sf(mad, c("restaurant", "pub", "school"), +#' limit = 50 +#' ) +#' +#' if (any(!sf::st_is_empty(rest_pub))) { +#' ggplot(mad) + +#' geom_sf() + +#' geom_sf(data = rest_pub, aes(color = query, shape = query)) +#' } +#' } +geo_amenity_sf <- function( + bbox, amenity, limit = 1, full_results = FALSE, + return_addresses = TRUE, verbose = FALSE, + nominatim_server = "https://nominatim.openstreetmap.org/", + progressbar = TRUE, custom_query = list(), strict = FALSE, + points_only = TRUE) { + if (limit > 50) { + message(paste( + "Nominatim provides 50 results as a maximum. ", + "Your query may be incomplete" + )) + limit <- min(50, limit) + } + + # bbox types + if (any(inherits(bbox, "sf"), inherits(bbox, "sfc"))) { + tolonlat <- sf::st_transform(bbox, 4326) + bbox <- as.vector(sf::st_bbox(tolonlat)) + } + bbox <- as.vector(bbox) + + # Overwrite custom query + custom_query <- as.list(custom_query) + custom_query$viewbox <- bbox + custom_query$bounded <- TRUE + + # Dedupe for query + key <- unique(amenity) + + # Set progress bar + ntot <- length(key) + # Set progress bar if n > 1 + progressbar <- all(progressbar, ntot > 1) + if (progressbar) { + pb <- txtProgressBar(min = 0, max = ntot, width = 50, style = 3) + } + seql <- seq(1, ntot, 1) + + seql <- seq(1, ntot, 1) + + all_res <- lapply(seql, function(x) { + ad <- key[x] + if (progressbar) { + setTxtProgressBar(pb, x) + } + + geo_lite_struct_sf( + amenity = ad, limit = limit, full_results = full_results, + return_addresses = return_addresses, verbose = verbose, + nominatim_server = nominatim_server, + custom_query = custom_query, points_only = points_only + ) + }) + if (progressbar) close(pb) + + all_res <- dplyr::bind_rows(all_res) + + # Clean columns and names + nm <- names(all_res) + nm[nm == "q_amenity"] <- "query" + names(all_res) <- nm + all_res <- all_res[, !grepl("^q_", nm)] + all_res <- sf_to_tbl(all_res) + + if (strict) { + bbox_sf <- bbox_to_poly(bbox) + int <- as.vector(sf::st_intersects(all_res, bbox_sf, sparse = FALSE)) + all_res <- all_res[int, ] + } + + + return(all_res) +} diff --git a/R/geo_lite.R b/R/geo_lite.R index 082ee702..4e8f6634 100644 --- a/R/geo_lite.R +++ b/R/geo_lite.R @@ -10,18 +10,18 @@ #' #' @family geocoding #' -#' @param address character with single line address, e.g. +#' @param address `character` with single line address, e.g. #' (`"1600 Pennsylvania Ave NW, Washington"`) or a vector of addresses #' (`c("Madrid", "Barcelona")`). -#' @param lat latitude column name in the output data (default `"lat"`). -#' @param long longitude column name in the output data (default `"long"`). -#' @param limit maximum number of results to return per input address. Note +#' @param lat Latitude column name in the output data (default `"lat"`). +#' @param long Longitude column name in the output data (default `"long"`). +#' @param limit Maximum number of results to return per input address. Note #' that each query returns a maximum of 50 results. -#' @param full_results returns all available data from the API service. +#' @param full_results Returns all available data from the API service. #' If `FALSE` (default) only latitude, longitude and address columns are #' returned. See also `return_addresses`. -#' @param return_addresses return input addresses with results if `TRUE`. -#' @param verbose if `TRUE` then detailed logs are output to the console. +#' @param return_addresses Return input addresses with results if `TRUE`. +#' @param verbose If `TRUE` then detailed logs are output to the console. #' @param nominatim_server The URL of the Nominatim server to use. #' Defaults to `"https://nominatim.openstreetmap.org/"`. #' @param progressbar Logical. If `TRUE` displays a progress bar to indicate @@ -136,7 +136,7 @@ geo_lite_single <- function(address, address2 <- gsub(" ", "+", address) # Compose url - url <- paste0(api, address2, "&format=json&limit=", limit) + url <- paste0(api, address2, "&format=jsonv2&limit=", limit) if (full_results) url <- paste0(url, "&addressdetails=1") @@ -153,13 +153,11 @@ geo_lite_single <- function(address, tbl_query <- dplyr::tibble(query = address) - # nocov start if (isFALSE(res)) { message(url, " not reachable.") out <- empty_tbl(tbl_query, lat, long) return(invisible(out)) } - # nocov end result <- dplyr::as_tibble(jsonlite::fromJSON(json, flatten = TRUE)) diff --git a/R/geo_lite_sf.R b/R/geo_lite_sf.R index 07fe0ac8..b95bbf0a 100644 --- a/R/geo_lite_sf.R +++ b/R/geo_lite_sf.R @@ -1,9 +1,9 @@ #' Address search API for OSM elements in \CRANpkg{sf} format (free-form query) #' #' @description -#' This function allows you to geocode addresses and return the corresponding +#' This function allows you to geocode addresses and returns the corresponding #' spatial object. This function returns the spatial object associated with the -#' query using \CRANpkg{sf}, see [geo_lite_sf()] for retrieving the data in +#' query using \CRANpkg{sf}, see [geo_lite()] for retrieving the data in #' [`tibble`][tibble::tibble] format. #' #' This function correspond to the **free-form query** search described in the @@ -13,10 +13,9 @@ #' @family geocoding #' @family spatial #' -#' @param full_results returns all available data from the API service. +#' @param full_results Returns all available data from the API service. #' If `FALSE` (default) only address columns are returned. See also #' `return_addresses`. -#' #' @param points_only Logical `TRUE/FALSE`. Whether to return only spatial #' points (`TRUE`, which is the default) or potentially other shapes as #' provided by the Nominatim API (`FALSE`). See **About Geometry Types**. @@ -76,13 +75,13 @@ #' } #' # Several results #' -#' Madrid <- geo_lite_sf("Comunidad de Madrid, Spain", +#' madrid <- geo_lite_sf("Comunidad de Madrid, Spain", #' limit = 2, #' points_only = FALSE, full_results = TRUE #' ) #' -#' if (any(!sf::st_is_empty(Madrid))) { -#' ggplot(Madrid) + +#' if (any(!sf::st_is_empty(madrid))) { +#' ggplot(madrid) + #' geom_sf(fill = NA) #' } #' } @@ -195,13 +194,11 @@ geo_lite_sf_single <- function(address, # Keep a tbl with the query tbl_query <- dplyr::tibble(query = address) - # nocov start if (isFALSE(res)) { message(url, " not reachable.") out <- empty_sf(tbl_query) return(invisible(out)) } - # nocov end # Read sfobj <- sf::read_sf(json, stringsAsFactors = FALSE) diff --git a/R/geo_lite_struct.R b/R/geo_lite_struct.R new file mode 100644 index 00000000..8d8b4772 --- /dev/null +++ b/R/geo_lite_struct.R @@ -0,0 +1,171 @@ +#' Address search API for OSM elements (structured query) +#' +#' @description +#' Geocodes addresses already split into components. This function returns the +#' [`tibble`][tibble::tibble] associated with the query, see +#' [geo_lite_struct_sf()] for retrieving the data as a spatial object +#' ([`sf`][sf::st_sf] format). +#' +#' This function correspond to the **structured query** search described in the +#' [API endpoint](https://nominatim.org/release-docs/develop/api/Search/). For +#' performing a free-form search use [geo_lite()]. +#' +#' @family geocoding +#' +#' @param amenity Name and/or type of POI, see also [geo_amenity]. +#' @param street House number and street name. +#' @param city City. +#' @param county County. +#' @param state State. +#' @param country Country. +#' @param postalcode Postal Code. +#' @inheritParams geo_lite +#' +#' +#' @details +#' +#' The structured form of the search query allows to look up up an address that +#' is already split into its components. Each parameter represents a field of +#' the address. All parameters are optional. You should only use the ones that +#' are relevant for the address you want to geocode. +#' +#' +#' See for additional +#' parameters to be passed to `custom_query`. +#' +#' @return +#' +#' ```{r child = "man/chunks/tibbleout.Rmd"} +#' ``` +#' +#' +#' @seealso +#' [geo_lite_struct_sf()], [tidygeocoder::geo()]. +#' +#' @export +#' +#' @examplesIf nominatim_check_access() +#' \donttest{ +#' pl_mayor <- geo_lite_struct( +#' street = "Plaza Mayor", country = "Spain", +#' limit = 50, full_results = TRUE +#' ) +#' +#' +#' dplyr::glimpse(pl_mayor) +#' } +geo_lite_struct <- function( + amenity = NULL, street = NULL, city = NULL, county = NULL, state = NULL, + country = NULL, postalcode = NULL, lat = "lat", long = "lon", limit = 1, + full_results = FALSE, return_addresses = TRUE, verbose = FALSE, + nominatim_server = "https://nominatim.openstreetmap.org/", + custom_query = list()) { + if (limit > 50) { + message(paste( + "Nominatim provides 50 results as a maximum. ", + "Your query may be incomplete" + )) + limit <- min(50, limit) + } + + # Check params, not vectorized + pars <- list( + amenity = amenity[1], + street = street[1], + city = city[1], + county = county[1], + state = state[1], + country = country[1], + postalcode = postalcode[1] + ) + + pars <- lapply(pars, function(x) { + if (is.null(x)) { + return(NA_character_) + } + a_char <- as.character(x) + a_char + }) + + tbl_query <- dplyr::as_tibble(pars) + names(tbl_query) <- paste0("q_", names(tbl_query)) + + if (all(is.na(pars))) { + message("Nothing to search for.") + out <- empty_tbl(tbl_query, lat, long) + return(invisible(out)) + } + + # Paste + + pars <- lapply(pars, function(x) { + gsub(" ", "+", x) + }) + + # First build the api address. If the passed nominatim_server does not end + # with a trailing forward-slash, add one + api <- prepare_api_url(nominatim_server, "search?") + # Compose url + url <- paste0(api, "format=jsonv2&limit=", limit) + + if (full_results) url <- paste0(url, "&addressdetails=1") + + # Clean and add options + newopts <- c(pars, custom_query) + + logis <- vapply(newopts, function(x) { + any(is.na(x), is.null(x)) + }, FUN.VALUE = logical(1)) + + + newopts <- newopts[!logis] + url <- add_custom_query(newopts, url) + + # Download to temp file + json <- tempfile(fileext = ".json") + res <- api_call(url, json, isFALSE(verbose)) + + # Step 2: Read and parse results ---- + if (isFALSE(res)) { + message(url, " not reachable.") + out <- empty_tbl(tbl_query, lat, long) + return(invisible(out)) + } + + result <- dplyr::as_tibble(jsonlite::fromJSON(json, flatten = TRUE)) + + # Rename lat and lon + nmes <- names(result) + nmes[nmes == "lat"] <- lat + nmes[nmes == "lon"] <- long + + names(result) <- nmes + + # Empty query + if (nrow(result) == 0) { + message("No results for query") + out <- empty_tbl(tbl_query, lat, long) + return(invisible(out)) + } + + + # Coords as double + result[lat] <- as.double(result[[lat]]) + result[long] <- as.double(result[[long]]) + + + # Add query + result_clean <- dplyr::bind_cols( + tbl_query[rep(1, nrow(result)), ], + result + ) + + # Keep names + result_out <- keep_names(result_clean, return_addresses, full_results, + colstokeep = c(names(tbl_query), lat, long) + ) + + # As tibble + result_out <- dplyr::as_tibble(result_out) + + result_out +} diff --git a/R/geo_lite_struct_sf.R b/R/geo_lite_struct_sf.R new file mode 100644 index 00000000..ca6cacc5 --- /dev/null +++ b/R/geo_lite_struct_sf.R @@ -0,0 +1,177 @@ +#' Address search API for OSM elements in \CRANpkg{sf} format (structured query) +#' +#' @description +#' Geocodes addresses already split into components and return the corresponding +#' spatial object. This function returns the spatial object associated with the +#' query using \CRANpkg{sf}, see [geo_lite_struct()] for retrieving the data in +#' [`tibble`][tibble::tibble] format. +#' +#' This function correspond to the **structured query** search described in the +#' [API endpoint](https://nominatim.org/release-docs/develop/api/Search/). For +#' performing a free-form search use [geo_lite_sf()]. +#' +#' @family geocoding +#' @family spatial +#' +#' @inheritParams geo_lite_struct +#' @inheritParams geo_lite_sf +#' +#' @details +#' +#' The structured form of the search query allows to look up up an address that +#' is already split into its components. Each parameter represents a field of +#' the address. All parameters are optional. You should only use the ones that +#' are relevant for the address you want to geocode. +#' +#' +#' See for additional +#' parameters to be passed to `custom_query`. +#' +#' @inheritSection geo_lite_sf About Geometry Types +#' +#' @return +#' +#' ```{r child = "man/chunks/sfout.Rmd"} +#' ``` +#' +#' @seealso +#' [geo_lite_struct()]. +#' +#' @export +#' +#' @examplesIf nominatim_check_access() +#' \donttest{ +#' # Map +#' +#' pl_mayor <- geo_lite_struct_sf( +#' street = "Plaza Mayor", +#' county = "Comunidad de Madrid", +#' country = "Spain", limit = 50, +#' full_results = TRUE, verbose = TRUE +#' ) +#' +#' # Outline +#' ccaa <- geo_lite_sf("Comunidad de Madrid, Spain", points_only = FALSE) +#' +#' library(ggplot2) +#' +#' if (any(!sf::st_is_empty(pl_mayor), !sf::st_is_empty(ccaa))) { +#' ggplot(ccaa) + +#' geom_sf() + +#' geom_sf(data = pl_mayor, aes(shape = addresstype, color = addresstype)) +#' } +#' } +geo_lite_struct_sf <- function( + amenity = NULL, street = NULL, city = NULL, county = NULL, state = NULL, + country = NULL, postalcode = NULL, limit = 1, full_results = FALSE, + return_addresses = TRUE, verbose = FALSE, + nominatim_server = "https://nominatim.openstreetmap.org/", + custom_query = list(), points_only = TRUE) { + if (limit > 50) { + message(paste( + "Nominatim provides 50 results as a maximum. ", + "Your query may be incomplete" + )) + limit <- min(50, limit) + } + + # Check params, not vectorized + pars <- list( + amenity = amenity[1], + street = street[1], + city = city[1], + county = county[1], + state = state[1], + country = country[1], + postalcode = postalcode[1] + ) + + pars <- lapply(pars, function(x) { + if (is.null(x)) { + return(NA_character_) + } + a_char <- as.character(x) + a_char + }) + + tbl_query <- dplyr::as_tibble(pars) + names(tbl_query) <- paste0("q_", names(tbl_query)) + + if (all(is.na(pars))) { + message("Nothing to search for.") + out <- empty_sf(tbl_query) + return(invisible(out)) + } + + # Paste + + pars <- lapply(pars, function(x) { + gsub(" ", "+", x) + }) + + # First build the api address. If the passed nominatim_server does not end + # with a trailing forward-slash, add one + api <- prepare_api_url(nominatim_server, "search?") + # Compose url + url <- paste0(api, "format=geojson&limit=", limit) + + if (full_results) url <- paste0(url, "&addressdetails=1") + if (!isTRUE(points_only)) url <- paste0(url, "&polygon_geojson=1") + + # Clean and add options + newopts <- c(pars, custom_query) + + logis <- vapply(newopts, function(x) { + any(is.na(x), is.null(x)) + }, FUN.VALUE = logical(1)) + + + newopts <- newopts[!logis] + url <- add_custom_query(newopts, url) + + # Download to temp file + json <- tempfile(fileext = ".geojson") + res <- api_call(url, json, isFALSE(verbose)) + + # Step 2: Read and parse results ---- + if (isFALSE(res)) { + message(url, " not reachable.") + out <- empty_sf(tbl_query) + return(invisible(out)) + } + + # Read + sfobj <- sf::read_sf(json, stringsAsFactors = FALSE) + + # Empty query + if (length(names(sfobj)) == 1) { + message("No results for query") + out <- empty_sf(tbl_query) + return(invisible(out)) + } + + # Prepare output + + # Unnest address + sfobj <- unnest_sf(sfobj) + + + + # Prepare output + sf_clean <- sfobj + + # Naming order + sf_clean <- dplyr::bind_cols( + sf_clean, + tbl_query[rep(1, nrow(sf_clean)), ] + ) + + # Keep names + result_out <- keep_names(sf_clean, return_addresses, full_results, + colstokeep = names(tbl_query) + ) + + # Attach as tibble + result_out <- sf_to_tbl(result_out) + + result_out +} diff --git a/R/reverse_geo_lite.R b/R/reverse_geo_lite.R index 95ad95a4..c5a89e2d 100644 --- a/R/reverse_geo_lite.R +++ b/R/reverse_geo_lite.R @@ -11,12 +11,12 @@ #' #' @family reverse #' -#' @param lat latitude values in numeric format. Must be in the range +#' @param lat Latitude values in numeric format. Must be in the range #' \eqn{\left[-90, 90 \right]}. -#' @param long longitude values in numeric format. Must be in the range +#' @param long Longitude values in numeric format. Must be in the range #' \eqn{\left[-180, 180 \right]}. -#' @param address address column name in the output data (default `"address"`). -#' @param return_coords return input coordinates with results if `TRUE`. +#' @param address Address column name in the output data (default `"address"`). +#' @param return_coords Return input coordinates with results if `TRUE`. #' @param custom_query API-specific parameters to be used, passed as a named #' list (ie. `list(zoom = 3)`). See **Details**. #' @@ -74,7 +74,7 @@ #' # With options: zoom to country level #' sev <- reverse_geo_lite( #' lat = c(40.75728, 55.95335), long = c(-73.98586, -3.188375), -#' custom_query = list(zoom = 0, extratags = 1), +#' custom_query = list(zoom = 0, extratags = TRUE), #' verbose = TRUE, full_results = TRUE #' ) #' @@ -181,7 +181,7 @@ reverse_geo_lite_single <- function(lat_cap, api <- prepare_api_url(nominatim_server, "reverse?") # Compose url - url <- paste0(api, "lat=", lat_cap, "&lon=", long_cap, "&format=json") + url <- paste0(api, "lat=", lat_cap, "&lon=", long_cap, "&format=jsonv2") if (isFALSE(full_results)) { url <- paste0(url, "&addressdetails=0") @@ -201,13 +201,11 @@ reverse_geo_lite_single <- function(lat_cap, - # nocov start if (isFALSE(res)) { message(url, " not reachable.") - out <- empty_tbl(tbl_query, address) + out <- empty_tbl_rev(tbl_query, address) return(invisible(out)) } - # nocov end result_init <- jsonlite::fromJSON(json, flatten = TRUE) diff --git a/R/reverse_geo_lite_sf.R b/R/reverse_geo_lite_sf.R index 650fe1cb..02387d4f 100644 --- a/R/reverse_geo_lite_sf.R +++ b/R/reverse_geo_lite_sf.R @@ -213,13 +213,11 @@ reverse_geo_lite_sf_single <- function(lat_cap, # Step 2: Read and parse results ---- tbl_query <- dplyr::tibble(lat = lat_cap, lon = long_cap) - # nocov start if (isFALSE(res)) { message(url, " not reachable.") out <- empty_sf(empty_tbl_rev(tbl_query, address)) return(invisible(out)) } - # nocov end # Empty query diff --git a/R/utils.R b/R/utils.R index 35de1689..00468c57 100644 --- a/R/utils.R +++ b/R/utils.R @@ -4,6 +4,14 @@ add_custom_query <- function(custom_query = list(), url) { return(url) } + custom_query <- lapply(custom_query, function(x) { + if (is.logical(x)) { + x <- ifelse(isTRUE(x), 1, 0) + } + x <- paste0(x, collapse = ",") + x + }) + opts <- paste0(names(custom_query), "=", custom_query, collapse = "&") end_url <- paste0(url, "&", opts) @@ -24,15 +32,23 @@ is_named <- function(x) { return(FALSE) } - return(TRUE) + TRUE } keep_names <- function(x, return_addresses, full_results, colstokeep = "query") { - names(x) <- gsub("address.", "", names(x)) - names(x) <- gsub("namedetails.", "", names(x)) - names(x) <- gsub("display_name", "address", names(x)) + x$address <- x$display_name + if ("boundingbox" %in% names(x)) { + bbun <- lapply(x$boundingbox, function(y) { + unl <- unlist(y) + bb <- dplyr::tibble(boundingbox = list(as.double(unl))) + bb + }) + bbun <- dplyr::bind_rows(bbun) + cln <- x[, names(x) != "boundingbox"] + x <- dplyr::bind_cols(cln, bbun) + } out_cols <- colstokeep if (return_addresses) out_cols <- c(out_cols, "address") @@ -47,8 +63,10 @@ keep_names <- function(x, return_addresses, full_results, keep_names_rev <- function(x, address = "address", return_coords = FALSE, full_results = FALSE, colstokeep = address) { - names(x) <- gsub("display_name", address, names(x)) - + x$xxxyyyzzz <- x$display_name + nm <- names(x) + nm <- gsub("xxxyyyzzz", address, nm, fixed = TRUE) + names(x) <- nm out_cols <- colstokeep if (return_coords) out_cols <- c(out_cols, "lat", "lon") if (full_results) out_cols <- c(out_cols, "lat", "lon", names(x)) @@ -102,11 +120,14 @@ unnest_reverse <- function(x) { # OSM address if ("address" %in% names(lngths)) { ad <- dplyr::as_tibble(x$address)[1, ] + names(ad) <- paste0("address.", names(ad)) + endobj <- dplyr::bind_cols(endobj, ad) } if ("extratags" %in% names(lngths)) { xtra <- dplyr::as_tibble(x$extratags)[1, ] + names(xtra) <- paste0("extratags.", names(xtra)) endobj <- dplyr::bind_cols(endobj, xtra) } @@ -145,6 +166,48 @@ sf_to_tbl <- function(x) { unnest_sf <- function(x) { # Unnest + if ("address" %in% names(x)) { + # Need to unnest + add <- as.character(x$address) + newadd <- lapply(add, function(x) { + df <- jsonlite::fromJSON(x, simplifyVector = TRUE) + dplyr::as_tibble(df) + }) + + newadd <- dplyr::bind_rows(newadd) + names(newadd) <- paste0("address.", names(newadd)) + + newsfobj <- x + newsfobj <- x[, setdiff(names(x), "address")] + x <- dplyr::bind_cols(newsfobj, newadd) + } + + if ("extratags" %in% names(x)) { + # Need to unnest + xtra <- as.character(x$extratags) + + newxtra <- lapply(xtra, function(x) { + if (any(is.na(x), is.null(x))) { + return(dplyr::tibble(xxx_empty_remove = NA)) + } + df <- jsonlite::fromJSON(x, simplifyVector = TRUE) + dplyr::as_tibble(df) + }) + + newxtra <- dplyr::bind_rows(newxtra) + names(newxtra) <- paste0("extratags.", names(newxtra)) + + newsfobj <- x + newsfobj <- x[, setdiff(names(x), "extratags")] + x <- dplyr::bind_cols(newsfobj, newxtra) + x <- x[, setdiff(names(x), "extratags.xxx_empty_remove")] + } + + + x <- sf_to_tbl(x) + + x + if (!("address" %in% names(x))) { return(x) } @@ -178,6 +241,7 @@ unnest_sf_reverse <- function(x) { }) newadd <- dplyr::bind_rows(newadd)[1, ] + names(newadd) <- paste0("address.", names(newadd)) newsfobj <- x newsfobj <- x[, setdiff(names(x), "address")] @@ -194,6 +258,7 @@ unnest_sf_reverse <- function(x) { }) newxtra <- dplyr::bind_rows(newxtra)[1, ] + names(newxtra) <- paste0("extratags.", names(newxtra)) newsfobj <- x newsfobj <- x[, setdiff(names(x), "extratags")] diff --git a/README.Rmd b/README.Rmd index 71a4b227..0630df2a 100644 --- a/README.Rmd +++ b/README.Rmd @@ -91,7 +91,7 @@ install.packages("nominatimlite") You can install the developing version of **nominatimlite** with: ```{r, eval=FALSE} -devtools::install_github("dieghernan/nominatimlite") +remotes::install_github("dieghernan/nominatimlite") ``` Alternatively, you can install **nominatimlite** using the diff --git a/README.md b/README.md index de723649..145f89fb 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ install.packages("nominatimlite") You can install the developing version of **nominatimlite** with: ``` r -devtools::install_github("dieghernan/nominatimlite") +remotes::install_github("dieghernan/nominatimlite") ``` Alternatively, you can install **nominatimlite** using the @@ -223,8 +223,7 @@ A BibTeX entry for LaTeX users is ## References -
+
diff --git a/codemeta.json b/codemeta.json index 6db7e447..1c3af936 100644 --- a/codemeta.json +++ b/codemeta.json @@ -14,7 +14,7 @@ "name": "R", "url": "https://r-project.org" }, - "runtimePlatform": "R version 4.3.3 (2024-02-29 ucrt)", + "runtimePlatform": "R version 4.3.3 (2024-02-29)", "provider": { "@id": "https://cran.r-project.org", "@type": "Organization", @@ -103,6 +103,18 @@ }, "sameAs": "https://CRAN.R-project.org/package=knitr" }, + { + "@type": "SoftwareApplication", + "identifier": "lifecycle", + "name": "lifecycle", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=lifecycle" + }, { "@type": "SoftwareApplication", "identifier": "rmarkdown", @@ -128,6 +140,18 @@ }, "sameAs": "https://CRAN.R-project.org/package=testthat" }, + { + "@type": "SoftwareApplication", + "identifier": "tibble", + "name": "tibble", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=tibble" + }, { "@type": "SoftwareApplication", "identifier": "tidygeocoder", @@ -175,18 +199,6 @@ "sameAs": "https://CRAN.R-project.org/package=jsonlite" }, "4": { - "@type": "SoftwareApplication", - "identifier": "lifecycle", - "name": "lifecycle", - "provider": { - "@id": "https://cran.r-project.org", - "@type": "Organization", - "name": "Comprehensive R Archive Network (CRAN)", - "url": "https://cran.r-project.org" - }, - "sameAs": "https://CRAN.R-project.org/package=lifecycle" - }, - "5": { "@type": "SoftwareApplication", "identifier": "sf", "name": "sf", @@ -199,7 +211,7 @@ }, "sameAs": "https://CRAN.R-project.org/package=sf" }, - "6": { + "5": { "@type": "SoftwareApplication", "identifier": "utils", "name": "utils" @@ -208,7 +220,7 @@ }, "applicationCategory": "cartography", "keywords": ["r", "geocoding", "openstreetmap", "address", "nominatim", "reverse-geocoding", "rstats", "shapefile", "r-package", "spatial", "cran", "api-wrapper", "api", "gis"], - "fileSize": "193.155KB", + "fileSize": "244.777KB", "citation": [ { "@type": "SoftwareSourceCode", diff --git a/data-raw/amenities.csv b/data-raw/amenities.csv deleted file mode 100644 index afcdd534..00000000 --- a/data-raw/amenities.csv +++ /dev/null @@ -1,101 +0,0 @@ -category,amenity -Sustenance,bar -Sustenance,biergarten -Sustenance,cafe -Sustenance,fast_food -Sustenance,food_court -Sustenance,ice_cream -Sustenance,pub -Sustenance,restaurant -Education,college -Education,driving_school -Education,kindergarten -Education,language_school -Education,library -Education,toy_library -Education,music_school -Education,school -Education,university -Transportation,bicycle_parking -Transportation,bicycle_repair_station -Transportation,bicycle_rental -Transportation,boat_rental -Transportation,boat_sharing -Transportation,bus_station -Transportation,car_rental -Transportation,car_sharing -Transportation,car_wash -Transportation,vehicle_inspection -Transportation,charging_station -Transportation,ferry_terminal -Transportation,fuel -Transportation,grit_bin -Transportation,motorcycle_parking -Transportation,parking -Transportation,parking_entrance -Transportation,parking_space -Transportation,taxi -Financial,atm -Financial,bank -Financial,bureau_de_change -Healthcare,baby_hatch -Healthcare,clinic -Healthcare,dentist -Healthcare,doctors -Healthcare,hospital -Healthcare,nursing_home -Healthcare,pharmacy -Healthcare,social_facility -Healthcare,veterinary -Entertainment-Arts-Culture,arts_centre -Entertainment-Arts-Culture,brothel -Entertainment-Arts-Culture,casino -Entertainment-Arts-Culture,cinema -Entertainment-Arts-Culture,community_centre -Entertainment-Arts-Culture,conference_centre -Entertainment-Arts-Culture,events_venue -Entertainment-Arts-Culture,fountain -Entertainment-Arts-Culture,gambling -Entertainment-Arts-Culture,love_hotel -Entertainment-Arts-Culture,nightclub -Entertainment-Arts-Culture,planetarium -Entertainment-Arts-Culture,public_bookcase -Entertainment-Arts-Culture,social_centre -Entertainment-Arts-Culture,stripclub -Entertainment-Arts-Culture,studio -Entertainment-Arts-Culture,swingerclub -Entertainment-Arts-Culture,theatre -Public Service,courthouse -Public Service,embassy -Public Service,fire_station -Public Service,police -Public Service,post_box -Public Service,post_depot -Public Service,post_office -Public Service,prison -Public Service,ranger_station -Public Service,townhall -Facilities,bbq -Facilities,bench -Facilities,dog_toilet -Facilities,drinking_water -Facilities,give_box -Facilities,shelter -Facilities,shower -Facilities,telephone -Facilities,toilets -Facilities,water_point -Facilities,watering_place -Waste Management,sanitary_dump_station -Waste Management,recycling -Waste Management,waste_basket -Waste Management,waste_disposal -Waste Management,waste_transfer_station -Others,animal_boarding -Others,animal_breeding -Others,animal_shelter -Others,baking_oven -Others,childcare -Others,clock -Others,crematorium -Others,dive_centre diff --git a/data-raw/osm_amenities.R b/data-raw/osm_amenities.R index f065b478..39c9abb8 100644 --- a/data-raw/osm_amenities.R +++ b/data-raw/osm_amenities.R @@ -1,8 +1,28 @@ ## code to prepare `osm_amenities` dataset goes here -library(readr) +# https://www.r-bloggers.com/2021/07/politely-scraping-wikipedia-tables-2/ -osm_amenities <- readr::read_csv("./data-raw/amenities.csv") +# To clean data +library(tidyverse) +# To scrape data +library(rvest) +url <- "https://wiki.openstreetmap.org/wiki/Key:amenity" + +osm_amenities <- rvest::read_html(url) %>% # scrape web page + rvest::html_nodes("table.wikitable") %>% # pull out specific table + rvest::html_table() %>% + pluck(1) %>% + as_tibble(.name_repair = "unique") %>% + mutate(Element = ifelse(Element == "", NA, Element)) %>% + fill(Element, .direction = "down") %>% + select(category = Element, amenity = Value, comment = Comment) %>% + mutate( + category = str_trim(category), + amenity = str_trim(amenity), + comment = str_trim(comment) + ) %>% + filter(category != amenity) %>% + as_tibble() usethis::use_data(osm_amenities, overwrite = TRUE) diff --git a/data/osm_amenities.rda b/data/osm_amenities.rda new file mode 100644 index 00000000..dc89a2d5 Binary files /dev/null and b/data/osm_amenities.rda differ diff --git a/inst/WORDLIST b/inst/WORDLIST index d26cd2a3..7d0c4a82 100644 --- a/inst/WORDLIST +++ b/inst/WORDLIST @@ -4,9 +4,12 @@ CMD Cambon CodeFactor DOI +Flaticon +Freepik Geocode Geocodes Geocoding +JSONV Mapbox Maëlle Nominatim @@ -20,20 +23,27 @@ Transamerica al api arcgeocoder +browsable codecov +crosstalk de et +geo geocode geocoded geocoder geocoding +geolocated ie json lon +osm osmdata +reactable rlang testthat tibble tidygeocoder unnesting vectorized +viewbox diff --git a/inst/schemaorg.json b/inst/schemaorg.json index 86b4a3eb..8310f396 100644 --- a/inst/schemaorg.json +++ b/inst/schemaorg.json @@ -47,7 +47,7 @@ "name": "Comprehensive R Archive Network (CRAN)", "url": "https://cran.r-project.org" }, - "runtimePlatform": "R version 4.3.3 (2024-02-29 ucrt)", + "runtimePlatform": "R version 4.3.3 (2024-02-29)", "version": "0.3.0.9000" }, { diff --git a/man/bbox_to_poly.Rd b/man/bbox_to_poly.Rd index 1505de4f..018dae27 100644 --- a/man/bbox_to_poly.Rd +++ b/man/bbox_to_poly.Rd @@ -7,10 +7,10 @@ bbox_to_poly(bbox = NA, xmin = NA, ymin = NA, xmax = NA, ymax = NA, crs = 4326) } \arguments{ -\item{bbox}{numeric vector of 4 elements representing the coordinates of the +\item{bbox}{Numeric vector of 4 elements representing the coordinates of the bounding box. Values should be \code{c(xmin, ymin, xmax, ymax)}.} -\item{xmin, ymin, xmax, ymax}{alternatively, you can use these named parameters +\item{xmin, ymin, xmax, ymax}{Alternatively, you can use these named parameters instead of \code{bbox}.} \item{crs}{coordinate reference system, something suitable as input to \link[sf]{st_crs}} @@ -63,7 +63,9 @@ if (any(!sf::st_is_empty(sfobj))) { Get \code{\link[sf:sf]{sf}} objects: \code{\link{geo_address_lookup_sf}()}, +\code{\link{geo_amenity_sf}()}, \code{\link{geo_lite_sf}()}, +\code{\link{geo_lite_struct_sf}()}, \code{\link{reverse_geo_lite_sf}()} } \concept{spatial} diff --git a/man/figures/README-line-object-1.png b/man/figures/README-line-object-1.png index eb1860db..a6f7078e 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-pizzahut-1.png b/man/figures/README-pizzahut-1.png index cbacabd9..aadb3340 100644 Binary files a/man/figures/README-pizzahut-1.png and b/man/figures/README-pizzahut-1.png differ diff --git a/man/figures/README-statue_liberty-1.png b/man/figures/README-statue_liberty-1.png index 6dc7fe45..be12c007 100644 Binary files a/man/figures/README-statue_liberty-1.png and b/man/figures/README-statue_liberty-1.png differ diff --git a/man/geo_address_lookup.Rd b/man/geo_address_lookup.Rd index 423340b6..e2e6aa25 100644 --- a/man/geo_address_lookup.Rd +++ b/man/geo_address_lookup.Rd @@ -17,24 +17,24 @@ geo_address_lookup( ) } \arguments{ -\item{osm_ids}{vector of OSM identifiers as \strong{numeric} +\item{osm_ids}{Vector of OSM identifiers as \strong{numeric} (\code{c(00000, 11111, 22222)}).} -\item{type}{vector character of the type of the OSM type associated to each +\item{type}{Vector character of the type of the OSM type associated to each \code{osm_ids}. Possible values are node (\code{"N"}), way (\code{"W"}) or relation (\code{"R"}). If a single value is provided it would be recycled.} -\item{lat}{latitude column name in the output data (default \code{"lat"}).} +\item{lat}{Latitude column name in the output data (default \code{"lat"}).} -\item{long}{longitude column name in the output data (default \code{"long"}).} +\item{long}{Longitude column name in the output data (default \code{"long"}).} -\item{full_results}{returns all available data from the API service. +\item{full_results}{Returns all available data from the API service. If \code{FALSE} (default) only latitude, longitude and address columns are returned. See also \code{return_addresses}.} -\item{return_addresses}{return input addresses with results if \code{TRUE}.} +\item{return_addresses}{Return input addresses with results if \code{TRUE}.} -\item{verbose}{if \code{TRUE} then detailed logs are output to the console.} +\item{verbose}{If \code{TRUE} then detailed logs are output to the console.} \item{nominatim_server}{The URL of the Nominatim server to use. Defaults to \code{"https://nominatim.openstreetmap.org/"}.} @@ -76,8 +76,12 @@ Address Lookup API: Geocoding: \code{\link{geo_address_lookup_sf}()}, +\code{\link{geo_amenity}()}, +\code{\link{geo_amenity_sf}()}, \code{\link{geo_lite}()}, -\code{\link{geo_lite_sf}()} +\code{\link{geo_lite_sf}()}, +\code{\link{geo_lite_struct}()}, +\code{\link{geo_lite_struct_sf}()} } \concept{geocoding} \concept{lookup} diff --git a/man/geo_address_lookup_sf.Rd b/man/geo_address_lookup_sf.Rd index b39714b0..67f6cc9f 100644 --- a/man/geo_address_lookup_sf.Rd +++ b/man/geo_address_lookup_sf.Rd @@ -16,20 +16,20 @@ geo_address_lookup_sf( ) } \arguments{ -\item{osm_ids}{vector of OSM identifiers as \strong{numeric} +\item{osm_ids}{Vector of OSM identifiers as \strong{numeric} (\code{c(00000, 11111, 22222)}).} -\item{type}{vector character of the type of the OSM type associated to each +\item{type}{Vector character of the type of the OSM type associated to each \code{osm_ids}. Possible values are node (\code{"N"}), way (\code{"W"}) or relation (\code{"R"}). If a single value is provided it would be recycled.} -\item{full_results}{returns all available data from the API service. +\item{full_results}{Returns all available data from the API service. If \code{FALSE} (default) only address columns are returned. See also \code{return_addresses}.} -\item{return_addresses}{return input addresses with results if \code{TRUE}.} +\item{return_addresses}{Return input addresses with results if \code{TRUE}.} -\item{verbose}{if \code{TRUE} then detailed logs are output to the console.} +\item{verbose}{If \code{TRUE} then detailed logs are output to the console.} \item{nominatim_server}{The URL of the Nominatim server to use. Defaults to \code{"https://nominatim.openstreetmap.org/"}.} @@ -116,12 +116,18 @@ Address Lookup API: Geocoding: \code{\link{geo_address_lookup}()}, +\code{\link{geo_amenity}()}, +\code{\link{geo_amenity_sf}()}, \code{\link{geo_lite}()}, -\code{\link{geo_lite_sf}()} +\code{\link{geo_lite_sf}()}, +\code{\link{geo_lite_struct}()}, +\code{\link{geo_lite_struct_sf}()} Get \code{\link[sf:sf]{sf}} objects: \code{\link{bbox_to_poly}()}, +\code{\link{geo_amenity_sf}()}, \code{\link{geo_lite_sf}()}, +\code{\link{geo_lite_struct_sf}()}, \code{\link{reverse_geo_lite_sf}()} } \concept{geocoding} diff --git a/man/geo_amenity.Rd b/man/geo_amenity.Rd index f6166ff0..088d7cb5 100644 --- a/man/geo_amenity.Rd +++ b/man/geo_amenity.Rd @@ -1,43 +1,127 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/deprecated.R +% Please edit documentation in R/geo_amenity.R \name{geo_amenity} \alias{geo_amenity} -\alias{geo_amenity_sf} \title{Geocode amenities} \usage{ -geo_amenity(bbox = NULL, ...) - -geo_amenity_sf(bbox = NULL, ...) +geo_amenity( + bbox, + amenity, + lat = "lat", + long = "lon", + limit = 1, + full_results = FALSE, + return_addresses = TRUE, + verbose = FALSE, + nominatim_server = "https://nominatim.openstreetmap.org/", + progressbar = TRUE, + custom_query = list(), + strict = FALSE +) } \arguments{ -\item{bbox, ...}{Deprecated} +\item{bbox}{The bounding box (viewbox) used to limit the search. It could be: +\itemize{ +\item A numeric vector of \strong{longitude} (\code{x}) and \strong{latitude} (\code{y}) +\verb{(xmin, ymin, xmax, ymax)}. See \strong{Details}. +\item A \code{\link[sf:sf]{sf}} or \code{\link[sf:sfc]{sfc}} object. +}} + +\item{amenity}{A \code{character} (or a vector of \code{character}s) with the +amenities to be geolocated (i.e. \code{c("pub", "restaurant")}). See +\link{osm_amenities}.} + +\item{lat}{Latitude column name in the output data (default \code{"lat"}).} + +\item{long}{Longitude column name in the output data (default \code{"long"}).} + +\item{limit}{Maximum number of results to return per input address. Note +that each query returns a maximum of 50 results.} + +\item{full_results}{Returns all available data from the API service. +If \code{FALSE} (default) only latitude, longitude and address columns are +returned. See also \code{return_addresses}.} + +\item{return_addresses}{Return input addresses with results if \code{TRUE}.} + +\item{verbose}{If \code{TRUE} then detailed logs are output to the console.} + +\item{nominatim_server}{The URL of the Nominatim server to use. +Defaults to \code{"https://nominatim.openstreetmap.org/"}.} + +\item{progressbar}{Logical. If \code{TRUE} displays a progress bar to indicate +the progress of the function.} + +\item{custom_query}{A named list with API-specific parameters to be used +(i.e. \code{list(countrycodes = "US")}). See \strong{Details}.} + +\item{strict}{Logical \code{TRUE/FALSE}. Force the results to be included inside +the \code{bbox}. Note that Nominatim default behavior may return results located +outside the provided bounding box.} } \value{ -An error. +A \code{\link[tibble:tibble]{tibble}} with the results found by the query. } \description{ -\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#defunct}{\figure{lifecycle-defunct.svg}{options: alt='[Defunct]'}}}{\strong{[Defunct]}} +This function search \link[=osm_amenities]{amenities} as defined by OpenStreetMap +on a restricted area defined by a bounding box in the form +\verb{(, , , )}. This function returns the +\code{\link[tibble:tibble]{tibble}} associated with the query, see \code{\link[=geo_amenity_sf]{geo_amenity_sf()}} +for retrieving the data as a spatial object (\code{\link[sf:sf]{sf}} format). +} +\details{ +Bounding boxes can be located using different online tools, as +\href{https://boundingbox.klokantech.com/}{Bounding Box Tool}. -This operation is not supported any more. Use -\code{\link[arcgeocoder:arc_geo_categories]{arcgeocoder::arc_geo_categories()}} instead. +For a full list of valid amenities see +\url{https://wiki.openstreetmap.org/wiki/Key:amenity} and \link{osm_amenities}. + +See \url{https://nominatim.org/release-docs/latest/api/Search/} for additional +parameters to be passed to \code{custom_query}. } \examples{ +\dontshow{if (nominatim_check_access()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} \donttest{ -# Madrid, Spain - -library(arcgeocoder) -library(ggplot2) +# Times Square, NY, USA +bbox <- c( + -73.9894467311, 40.75573629, + -73.9830630737, 40.75789245 +) -bbox <- c(-3.888954, 40.311977, -3.517916, 40.643729) +geo_amenity( + bbox = bbox, + amenity = "restaurant" +) -# Food -rest_pub <- arc_geo_categories( - bbox = bbox, category = "Bakery,Bar or Pub", - full_results = TRUE, - limit = 50 +# Several amenities +geo_amenity( + bbox = bbox, + amenity = c("restaurant", "pub") ) -rest_pub +# Increase limit and use with strict +geo_amenity( + bbox = bbox, + amenity = c("restaurant", "pub"), + limit = 10, + strict = TRUE +) } +\dontshow{\}) # examplesIf} +} +\seealso{ +Other amenity: +\code{\link{geo_amenity_sf}()}, +\code{\link{osm_amenities}} + +Geocoding: +\code{\link{geo_address_lookup}()}, +\code{\link{geo_address_lookup_sf}()}, +\code{\link{geo_amenity_sf}()}, +\code{\link{geo_lite}()}, +\code{\link{geo_lite_sf}()}, +\code{\link{geo_lite_struct}()}, +\code{\link{geo_lite_struct_sf}()} } -\keyword{internal} +\concept{amenity} +\concept{geocoding} diff --git a/man/geo_amenity_sf.Rd b/man/geo_amenity_sf.Rd new file mode 100644 index 00000000..2c4a45bc --- /dev/null +++ b/man/geo_amenity_sf.Rd @@ -0,0 +1,147 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/geo_amenity_sf.R +\name{geo_amenity_sf} +\alias{geo_amenity_sf} +\title{Geocode amenities in \CRANpkg{sf} format} +\usage{ +geo_amenity_sf( + bbox, + amenity, + limit = 1, + full_results = FALSE, + return_addresses = TRUE, + verbose = FALSE, + nominatim_server = "https://nominatim.openstreetmap.org/", + progressbar = TRUE, + custom_query = list(), + strict = FALSE, + points_only = TRUE +) +} +\arguments{ +\item{bbox}{The bounding box (viewbox) used to limit the search. It could be: +\itemize{ +\item A numeric vector of \strong{longitude} (\code{x}) and \strong{latitude} (\code{y}) +\verb{(xmin, ymin, xmax, ymax)}. See \strong{Details}. +\item A \code{\link[sf:sf]{sf}} or \code{\link[sf:sfc]{sfc}} object. +}} + +\item{amenity}{A \code{character} (or a vector of \code{character}s) with the +amenities to be geolocated (i.e. \code{c("pub", "restaurant")}). See +\link{osm_amenities}.} + +\item{limit}{Maximum number of results to return per input address. Note +that each query returns a maximum of 50 results.} + +\item{full_results}{Returns all available data from the API service. +If \code{FALSE} (default) only latitude, longitude and address columns are +returned. See also \code{return_addresses}.} + +\item{return_addresses}{Return input addresses with results if \code{TRUE}.} + +\item{verbose}{If \code{TRUE} then detailed logs are output to the console.} + +\item{nominatim_server}{The URL of the Nominatim server to use. +Defaults to \code{"https://nominatim.openstreetmap.org/"}.} + +\item{progressbar}{Logical. If \code{TRUE} displays a progress bar to indicate +the progress of the function.} + +\item{custom_query}{A named list with API-specific parameters to be used +(i.e. \code{list(countrycodes = "US")}). See \strong{Details}.} + +\item{strict}{Logical \code{TRUE/FALSE}. Force the results to be included inside +the \code{bbox}. Note that Nominatim default behavior may return results located +outside the provided bounding box.} + +\item{points_only}{Logical \code{TRUE/FALSE}. Whether to return only spatial +points (\code{TRUE}, which is the default) or potentially other shapes as +provided by the Nominatim API (\code{FALSE}). See \strong{About Geometry Types}.} +} +\value{ +A \code{\link[sf:sf]{sf}} object with the results. +} +\description{ +This function search \link[=osm_amenities]{amenities} as defined by OpenStreetMap +on a restricted area defined by a bounding box in the form +\verb{(, , , )}. This function returns the spatial +object associated with the query using \CRANpkg{sf}, see \code{\link[=geo_amenity]{geo_amenity()}} for +retrieving the data in \code{\link[tibble:tibble]{tibble}} format. +} +\details{ +Bounding boxes can be located using different online tools, as +\href{https://boundingbox.klokantech.com/}{Bounding Box Tool}. + +For a full list of valid amenities see +\url{https://wiki.openstreetmap.org/wiki/Key:amenity} and \link{osm_amenities}. + +See \url{https://nominatim.org/release-docs/latest/api/Search/} for additional +parameters to be passed to \code{custom_query}. +} +\section{About Geometry Types}{ + + +The parameter \code{points_only} specifies whether the function results will be +points (all Nominatim results are guaranteed to have at least point +geometry) or possibly other spatial objects. + +Note that the type of geometry returned in case of \code{points_only = FALSE} +will depend on the object being geocoded: +\itemize{ +\item Administrative areas, major buildings and the like will be +returned as polygons. +\item Rivers, roads and their like as lines. +\item Amenities may be points even in case of a \code{points_only = FALSE} call. +} + +The function is vectorized, allowing for multiple addresses to be geocoded; +in case of \code{points_only = FALSE} multiple geometry types may be returned. +} + +\examples{ +\dontshow{if (nominatim_check_access()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} +\donttest{ +# Usera, Madrid + +library(ggplot2) +mad <- geo_lite_sf("Usera, Madrid, Spain", points_only = FALSE) + + +# Restaurants, pubs and schools + +rest_pub <- geo_amenity_sf(mad, c("restaurant", "pub", "school"), + limit = 50 +) + +if (any(!sf::st_is_empty(rest_pub))) { + ggplot(mad) + + geom_sf() + + geom_sf(data = rest_pub, aes(color = query, shape = query)) +} +} +\dontshow{\}) # examplesIf} +} +\seealso{ +Other amenity: +\code{\link{geo_amenity}()}, +\code{\link{osm_amenities}} + +Geocoding: +\code{\link{geo_address_lookup}()}, +\code{\link{geo_address_lookup_sf}()}, +\code{\link{geo_amenity}()}, +\code{\link{geo_lite}()}, +\code{\link{geo_lite_sf}()}, +\code{\link{geo_lite_struct}()}, +\code{\link{geo_lite_struct_sf}()} + +Get \code{\link[sf:sf]{sf}} objects: +\code{\link{bbox_to_poly}()}, +\code{\link{geo_address_lookup_sf}()}, +\code{\link{geo_lite_sf}()}, +\code{\link{geo_lite_struct_sf}()}, +\code{\link{reverse_geo_lite_sf}()} +} +\concept{amenity} +\concept{geocoding} +\concept{spatial} diff --git a/man/geo_lite.Rd b/man/geo_lite.Rd index a00f53d5..0c2f452b 100644 --- a/man/geo_lite.Rd +++ b/man/geo_lite.Rd @@ -18,24 +18,24 @@ geo_lite( ) } \arguments{ -\item{address}{character with single line address, e.g. +\item{address}{\code{character} with single line address, e.g. (\code{"1600 Pennsylvania Ave NW, Washington"}) or a vector of addresses (\code{c("Madrid", "Barcelona")}).} -\item{lat}{latitude column name in the output data (default \code{"lat"}).} +\item{lat}{Latitude column name in the output data (default \code{"lat"}).} -\item{long}{longitude column name in the output data (default \code{"long"}).} +\item{long}{Longitude column name in the output data (default \code{"long"}).} -\item{limit}{maximum number of results to return per input address. Note +\item{limit}{Maximum number of results to return per input address. Note that each query returns a maximum of 50 results.} -\item{full_results}{returns all available data from the API service. +\item{full_results}{Returns all available data from the API service. If \code{FALSE} (default) only latitude, longitude and address columns are returned. See also \code{return_addresses}.} -\item{return_addresses}{return input addresses with results if \code{TRUE}.} +\item{return_addresses}{Return input addresses with results if \code{TRUE}.} -\item{verbose}{if \code{TRUE} then detailed logs are output to the console.} +\item{verbose}{If \code{TRUE} then detailed logs are output to the console.} \item{nominatim_server}{The URL of the Nominatim server to use. Defaults to \code{"https://nominatim.openstreetmap.org/"}.} @@ -83,6 +83,10 @@ geo_lite(c("Madrid", "Barcelona"), Geocoding: \code{\link{geo_address_lookup}()}, \code{\link{geo_address_lookup_sf}()}, -\code{\link{geo_lite_sf}()} +\code{\link{geo_amenity}()}, +\code{\link{geo_amenity_sf}()}, +\code{\link{geo_lite_sf}()}, +\code{\link{geo_lite_struct}()}, +\code{\link{geo_lite_struct_sf}()} } \concept{geocoding} diff --git a/man/geo_lite_sf.Rd b/man/geo_lite_sf.Rd index 09011736..432f8b43 100644 --- a/man/geo_lite_sf.Rd +++ b/man/geo_lite_sf.Rd @@ -17,20 +17,20 @@ geo_lite_sf( ) } \arguments{ -\item{address}{character with single line address, e.g. +\item{address}{\code{character} with single line address, e.g. (\code{"1600 Pennsylvania Ave NW, Washington"}) or a vector of addresses (\code{c("Madrid", "Barcelona")}).} -\item{limit}{maximum number of results to return per input address. Note +\item{limit}{Maximum number of results to return per input address. Note that each query returns a maximum of 50 results.} -\item{return_addresses}{return input addresses with results if \code{TRUE}.} +\item{return_addresses}{Return input addresses with results if \code{TRUE}.} -\item{full_results}{returns all available data from the API service. +\item{full_results}{Returns all available data from the API service. If \code{FALSE} (default) only address columns are returned. See also \code{return_addresses}.} -\item{verbose}{if \code{TRUE} then detailed logs are output to the console.} +\item{verbose}{If \code{TRUE} then detailed logs are output to the console.} \item{progressbar}{Logical. If \code{TRUE} displays a progress bar to indicate the progress of the function.} @@ -49,9 +49,9 @@ provided by the Nominatim API (\code{FALSE}). See \strong{About Geometry Types}. A \code{\link[sf:sf]{sf}} object with the results. } \description{ -This function allows you to geocode addresses and return the corresponding +This function allows you to geocode addresses and returns the corresponding spatial object. This function returns the spatial object associated with the -query using \CRANpkg{sf}, see \code{\link[=geo_lite_sf]{geo_lite_sf()}} for retrieving the data in +query using \CRANpkg{sf}, see \code{\link[=geo_lite]{geo_lite()}} for retrieving the data in \code{\link[tibble:tibble]{tibble}} format. This function correspond to the \strong{free-form query} search described in the @@ -104,13 +104,13 @@ if (any(!sf::st_is_empty(sol_poly))) { } # Several results -Madrid <- geo_lite_sf("Comunidad de Madrid, Spain", +madrid <- geo_lite_sf("Comunidad de Madrid, Spain", limit = 2, points_only = FALSE, full_results = TRUE ) -if (any(!sf::st_is_empty(Madrid))) { - ggplot(Madrid) + +if (any(!sf::st_is_empty(madrid))) { + ggplot(madrid) + geom_sf(fill = NA) } } @@ -122,11 +122,17 @@ if (any(!sf::st_is_empty(Madrid))) { Geocoding: \code{\link{geo_address_lookup}()}, \code{\link{geo_address_lookup_sf}()}, -\code{\link{geo_lite}()} +\code{\link{geo_amenity}()}, +\code{\link{geo_amenity_sf}()}, +\code{\link{geo_lite}()}, +\code{\link{geo_lite_struct}()}, +\code{\link{geo_lite_struct_sf}()} Get \code{\link[sf:sf]{sf}} objects: \code{\link{bbox_to_poly}()}, \code{\link{geo_address_lookup_sf}()}, +\code{\link{geo_amenity_sf}()}, +\code{\link{geo_lite_struct_sf}()}, \code{\link{reverse_geo_lite_sf}()} } \concept{geocoding} diff --git a/man/geo_lite_struct.Rd b/man/geo_lite_struct.Rd new file mode 100644 index 00000000..e10154d7 --- /dev/null +++ b/man/geo_lite_struct.Rd @@ -0,0 +1,108 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/geo_lite_struct.R +\name{geo_lite_struct} +\alias{geo_lite_struct} +\title{Address search API for OSM elements (structured query)} +\usage{ +geo_lite_struct( + amenity = NULL, + street = NULL, + city = NULL, + county = NULL, + state = NULL, + country = NULL, + postalcode = NULL, + lat = "lat", + long = "lon", + limit = 1, + full_results = FALSE, + return_addresses = TRUE, + verbose = FALSE, + nominatim_server = "https://nominatim.openstreetmap.org/", + custom_query = list() +) +} +\arguments{ +\item{amenity}{Name and/or type of POI, see also \link{geo_amenity}.} + +\item{street}{House number and street name.} + +\item{city}{City.} + +\item{county}{County.} + +\item{state}{State.} + +\item{country}{Country.} + +\item{postalcode}{Postal Code.} + +\item{lat}{Latitude column name in the output data (default \code{"lat"}).} + +\item{long}{Longitude column name in the output data (default \code{"long"}).} + +\item{limit}{Maximum number of results to return per input address. Note +that each query returns a maximum of 50 results.} + +\item{full_results}{Returns all available data from the API service. +If \code{FALSE} (default) only latitude, longitude and address columns are +returned. See also \code{return_addresses}.} + +\item{return_addresses}{Return input addresses with results if \code{TRUE}.} + +\item{verbose}{If \code{TRUE} then detailed logs are output to the console.} + +\item{nominatim_server}{The URL of the Nominatim server to use. +Defaults to \code{"https://nominatim.openstreetmap.org/"}.} + +\item{custom_query}{A named list with API-specific parameters to be used +(i.e. \code{list(countrycodes = "US")}). See \strong{Details}.} +} +\value{ +A \code{\link[tibble:tibble]{tibble}} with the results found by the query. +} +\description{ +Geocodes addresses already split into components. This function returns the +\code{\link[tibble:tibble]{tibble}} associated with the query, see +\code{\link[=geo_lite_struct_sf]{geo_lite_struct_sf()}} for retrieving the data as a spatial object +(\code{\link[sf:sf]{sf}} format). + +This function correspond to the \strong{structured query} search described in the +\href{https://nominatim.org/release-docs/develop/api/Search/}{API endpoint}. For +performing a free-form search use \code{\link[=geo_lite]{geo_lite()}}. +} +\details{ +The structured form of the search query allows to look up up an address that +is already split into its components. Each parameter represents a field of +the address. All parameters are optional. You should only use the ones that +are relevant for the address you want to geocode. + +See \url{https://nominatim.org/release-docs/latest/api/Search/} for additional +parameters to be passed to \code{custom_query}. +} +\examples{ +\dontshow{if (nominatim_check_access()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} +\donttest{ +pl_mayor <- geo_lite_struct( + street = "Plaza Mayor", country = "Spain", + limit = 50, full_results = TRUE +) + + +dplyr::glimpse(pl_mayor) +} +\dontshow{\}) # examplesIf} +} +\seealso{ +\code{\link[=geo_lite_struct_sf]{geo_lite_struct_sf()}}, \code{\link[tidygeocoder:geo]{tidygeocoder::geo()}}. + +Geocoding: +\code{\link{geo_address_lookup}()}, +\code{\link{geo_address_lookup_sf}()}, +\code{\link{geo_amenity}()}, +\code{\link{geo_amenity_sf}()}, +\code{\link{geo_lite}()}, +\code{\link{geo_lite_sf}()}, +\code{\link{geo_lite_struct_sf}()} +} +\concept{geocoding} diff --git a/man/geo_lite_struct_sf.Rd b/man/geo_lite_struct_sf.Rd new file mode 100644 index 00000000..00423a11 --- /dev/null +++ b/man/geo_lite_struct_sf.Rd @@ -0,0 +1,147 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/geo_lite_struct_sf.R +\name{geo_lite_struct_sf} +\alias{geo_lite_struct_sf} +\title{Address search API for OSM elements in \CRANpkg{sf} format (structured query)} +\usage{ +geo_lite_struct_sf( + amenity = NULL, + street = NULL, + city = NULL, + county = NULL, + state = NULL, + country = NULL, + postalcode = NULL, + limit = 1, + full_results = FALSE, + return_addresses = TRUE, + verbose = FALSE, + nominatim_server = "https://nominatim.openstreetmap.org/", + custom_query = list(), + points_only = TRUE +) +} +\arguments{ +\item{amenity}{Name and/or type of POI, see also \link{geo_amenity}.} + +\item{street}{House number and street name.} + +\item{city}{City.} + +\item{county}{County.} + +\item{state}{State.} + +\item{country}{Country.} + +\item{postalcode}{Postal Code.} + +\item{limit}{Maximum number of results to return per input address. Note +that each query returns a maximum of 50 results.} + +\item{full_results}{Returns all available data from the API service. +If \code{FALSE} (default) only latitude, longitude and address columns are +returned. See also \code{return_addresses}.} + +\item{return_addresses}{Return input addresses with results if \code{TRUE}.} + +\item{verbose}{If \code{TRUE} then detailed logs are output to the console.} + +\item{nominatim_server}{The URL of the Nominatim server to use. +Defaults to \code{"https://nominatim.openstreetmap.org/"}.} + +\item{custom_query}{A named list with API-specific parameters to be used +(i.e. \code{list(countrycodes = "US")}). See \strong{Details}.} + +\item{points_only}{Logical \code{TRUE/FALSE}. Whether to return only spatial +points (\code{TRUE}, which is the default) or potentially other shapes as +provided by the Nominatim API (\code{FALSE}). See \strong{About Geometry Types}.} +} +\value{ +A \code{\link[sf:sf]{sf}} object with the results. +} +\description{ +Geocodes addresses already split into components and return the corresponding +spatial object. This function returns the spatial object associated with the +query using \CRANpkg{sf}, see \code{\link[=geo_lite_struct]{geo_lite_struct()}} for retrieving the data in +\code{\link[tibble:tibble]{tibble}} format. + +This function correspond to the \strong{structured query} search described in the +\href{https://nominatim.org/release-docs/develop/api/Search/}{API endpoint}. For +performing a free-form search use \code{\link[=geo_lite_sf]{geo_lite_sf()}}. +} +\details{ +The structured form of the search query allows to look up up an address that +is already split into its components. Each parameter represents a field of +the address. All parameters are optional. You should only use the ones that +are relevant for the address you want to geocode. + +See \url{https://nominatim.org/release-docs/latest/api/Search/} for additional +parameters to be passed to \code{custom_query}. +} +\section{About Geometry Types}{ + + +The parameter \code{points_only} specifies whether the function results will be +points (all Nominatim results are guaranteed to have at least point +geometry) or possibly other spatial objects. + +Note that the type of geometry returned in case of \code{points_only = FALSE} +will depend on the object being geocoded: +\itemize{ +\item Administrative areas, major buildings and the like will be +returned as polygons. +\item Rivers, roads and their like as lines. +\item Amenities may be points even in case of a \code{points_only = FALSE} call. +} + +The function is vectorized, allowing for multiple addresses to be geocoded; +in case of \code{points_only = FALSE} multiple geometry types may be returned. +} + +\examples{ +\dontshow{if (nominatim_check_access()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} +\donttest{ +# Map + +pl_mayor <- geo_lite_struct_sf( + street = "Plaza Mayor", + county = "Comunidad de Madrid", + country = "Spain", limit = 50, + full_results = TRUE, verbose = TRUE +) + +# Outline +ccaa <- geo_lite_sf("Comunidad de Madrid, Spain", points_only = FALSE) + +library(ggplot2) + +if (any(!sf::st_is_empty(pl_mayor), !sf::st_is_empty(ccaa))) { + ggplot(ccaa) + + geom_sf() + + geom_sf(data = pl_mayor, aes(shape = addresstype, color = addresstype)) +} +} +\dontshow{\}) # examplesIf} +} +\seealso{ +\code{\link[=geo_lite_struct]{geo_lite_struct()}}. + +Geocoding: +\code{\link{geo_address_lookup}()}, +\code{\link{geo_address_lookup_sf}()}, +\code{\link{geo_amenity}()}, +\code{\link{geo_amenity_sf}()}, +\code{\link{geo_lite}()}, +\code{\link{geo_lite_sf}()}, +\code{\link{geo_lite_struct}()} + +Get \code{\link[sf:sf]{sf}} objects: +\code{\link{bbox_to_poly}()}, +\code{\link{geo_address_lookup_sf}()}, +\code{\link{geo_amenity_sf}()}, +\code{\link{geo_lite_sf}()}, +\code{\link{reverse_geo_lite_sf}()} +} +\concept{geocoding} +\concept{spatial} diff --git a/man/osm_amenities.Rd b/man/osm_amenities.Rd new file mode 100644 index 00000000..414261d9 --- /dev/null +++ b/man/osm_amenities.Rd @@ -0,0 +1,39 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/data.R +\docType{data} +\encoding{UTF-8} +\name{osm_amenities} +\alias{osm_amenities} +\title{OpenStreetMap amenity database} +\format{ +A \code{\link[tibble:tibble]{tibble}} with with +136 rows and +fields: +\describe{ +\item{category}{The category of the amenity.} +\item{amenity}{The value of the amenity.} +\item{comment}{A brief description of the type of amenity.} +} +} +\source{ +\url{https://wiki.openstreetmap.org/wiki/Key:amenity} +} +\description{ +Database with the list of amenities available on OpenStreetMap. +} +\note{ +Data extracted on \strong{03 April 2024}. +} +\examples{ + +data("osm_amenities") + +osm_amenities +} +\seealso{ +Other amenity: +\code{\link{geo_amenity}()}, +\code{\link{geo_amenity_sf}()} +} +\concept{amenity} +\concept{datasets} diff --git a/man/reverse_geo_lite.Rd b/man/reverse_geo_lite.Rd index 7847d4c0..90c8af42 100644 --- a/man/reverse_geo_lite.Rd +++ b/man/reverse_geo_lite.Rd @@ -17,21 +17,21 @@ reverse_geo_lite( ) } \arguments{ -\item{lat}{latitude values in numeric format. Must be in the range +\item{lat}{Latitude values in numeric format. Must be in the range \eqn{\left[-90, 90 \right]}.} -\item{long}{longitude values in numeric format. Must be in the range +\item{long}{Longitude values in numeric format. Must be in the range \eqn{\left[-180, 180 \right]}.} -\item{address}{address column name in the output data (default \code{"address"}).} +\item{address}{Address column name in the output data (default \code{"address"}).} -\item{full_results}{returns all available data from the API service. +\item{full_results}{Returns all available data from the API service. If \code{FALSE} (default) only latitude, longitude and address columns are returned. See also \code{return_addresses}.} -\item{return_coords}{return input coordinates with results if \code{TRUE}.} +\item{return_coords}{Return input coordinates with results if \code{TRUE}.} -\item{verbose}{if \code{TRUE} then detailed logs are output to the console.} +\item{verbose}{If \code{TRUE} then detailed logs are output to the console.} \item{nominatim_server}{The URL of the Nominatim server to use. Defaults to \code{"https://nominatim.openstreetmap.org/"}.} @@ -86,7 +86,7 @@ reverse_geo_lite(lat = c(40.75728, 55.95335), long = c(-73.98586, -3.188375)) # With options: zoom to country level sev <- reverse_geo_lite( lat = c(40.75728, 55.95335), long = c(-73.98586, -3.188375), - custom_query = list(zoom = 0, extratags = 1), + custom_query = list(zoom = 0, extratags = TRUE), verbose = TRUE, full_results = TRUE ) diff --git a/man/reverse_geo_lite_sf.Rd b/man/reverse_geo_lite_sf.Rd index 2e9f585d..a4cf8b4a 100644 --- a/man/reverse_geo_lite_sf.Rd +++ b/man/reverse_geo_lite_sf.Rd @@ -18,21 +18,21 @@ reverse_geo_lite_sf( ) } \arguments{ -\item{lat}{latitude values in numeric format. Must be in the range +\item{lat}{Latitude values in numeric format. Must be in the range \eqn{\left[-90, 90 \right]}.} -\item{long}{longitude values in numeric format. Must be in the range +\item{long}{Longitude values in numeric format. Must be in the range \eqn{\left[-180, 180 \right]}.} -\item{address}{address column name in the output data (default \code{"address"}).} +\item{address}{Address column name in the output data (default \code{"address"}).} -\item{full_results}{returns all available data from the API service. +\item{full_results}{Returns all available data from the API service. If \code{FALSE} (default) only latitude, longitude and address columns are returned. See also \code{return_addresses}.} -\item{return_coords}{return input coordinates with results if \code{TRUE}.} +\item{return_coords}{Return input coordinates with results if \code{TRUE}.} -\item{verbose}{if \code{TRUE} then detailed logs are output to the console.} +\item{verbose}{If \code{TRUE} then detailed logs are output to the console.} \item{nominatim_server}{The URL of the Nominatim server to use. Defaults to \code{"https://nominatim.openstreetmap.org/"}.} @@ -148,7 +148,9 @@ Reverse geocoding coordinates: Get \code{\link[sf:sf]{sf}} objects: \code{\link{bbox_to_poly}()}, \code{\link{geo_address_lookup_sf}()}, -\code{\link{geo_lite_sf}()} +\code{\link{geo_amenity_sf}()}, +\code{\link{geo_lite_sf}()}, +\code{\link{geo_lite_struct_sf}()} } \concept{reverse} \concept{spatial} diff --git a/pkgdown/_pkgdown.yml b/pkgdown/_pkgdown.yml index c3cf7c27..79f3d080 100644 --- a/pkgdown/_pkgdown.yml +++ b/pkgdown/_pkgdown.yml @@ -42,6 +42,10 @@ reference: These functions return sf objects. contents: has_concept("spatial") +- title: Datasets + desc: Helper datasets. + contents: + - has_concept("datasets") - title: About the package contents: nominatimlite-package diff --git a/tests/testthat/_snaps/deprecated.md b/tests/testthat/_snaps/deprecated.md deleted file mode 100644 index 3b55f5a7..00000000 --- a/tests/testthat/_snaps/deprecated.md +++ /dev/null @@ -1,20 +0,0 @@ -# Deprecated geo_amenity_sf - - Code - geo_amenity_sf() - Condition - Error: - ! `geo_amenity_sf()` was deprecated in nominatimlite 0.3.0 and is now defunct. - i Please use `arcgeocoder::arc_geo_categories()` instead. - i Operation not supported any more by the Nominatim API. - -# Deprecated geo_amenity - - Code - geo_amenity() - Condition - Error: - ! `geo_amenity()` was deprecated in nominatimlite 0.3.0 and is now defunct. - i Please use `arcgeocoder::arc_geo_categories()` instead. - i Operation not supported any more by the Nominatim API. - diff --git a/tests/testthat/_snaps/geo_address_lookup.md b/tests/testthat/_snaps/geo_address_lookup.md new file mode 100644 index 00000000..461e6b00 --- /dev/null +++ b/tests/testthat/_snaps/geo_address_lookup.md @@ -0,0 +1,8 @@ +# Fail + + Code + several <- geo_address_lookup(vector_ids, vector_type, full_results = TRUE, + nominatim_server = "https://xyz.com/") + Message + https://xyz.com/lookup?osm_ids=R146656,N240109189&format=jsonv2&addressdetails=1 not reachable. + diff --git a/tests/testthat/_snaps/geo_address_lookup_sf.md b/tests/testthat/_snaps/geo_address_lookup_sf.md new file mode 100644 index 00000000..5423f23e --- /dev/null +++ b/tests/testthat/_snaps/geo_address_lookup_sf.md @@ -0,0 +1,8 @@ +# Fail + + Code + several <- geo_address_lookup_sf(vector_ids, vector_type, full_results = TRUE, + nominatim_server = "https://xyz.com/") + Message + https://xyz.com/lookup?osm_ids=R146656,N240109189&format=geojson&addressdetails=1 not reachable. + diff --git a/tests/testthat/_snaps/geo_lite.md b/tests/testthat/_snaps/geo_lite.md new file mode 100644 index 00000000..633ef0a5 --- /dev/null +++ b/tests/testthat/_snaps/geo_lite.md @@ -0,0 +1,7 @@ +# Fail + + Code + several <- geo_lite("Madrid", full_results = TRUE, nominatim_server = "https://xyz.com/") + Message + https://xyz.com/search?q=Madrid&format=jsonv2&limit=1&addressdetails=1 not reachable. + diff --git a/tests/testthat/_snaps/geo_lite_sf.md b/tests/testthat/_snaps/geo_lite_sf.md new file mode 100644 index 00000000..c532968c --- /dev/null +++ b/tests/testthat/_snaps/geo_lite_sf.md @@ -0,0 +1,7 @@ +# Fail + + Code + several <- geo_lite_sf("madrid", full_results = TRUE, nominatim_server = "https://xyz.com/") + Message + https://xyz.com/search?q=madrid&format=geojson&limit=1&addressdetails=1 not reachable. + diff --git a/tests/testthat/_snaps/geo_lite_struct.md b/tests/testthat/_snaps/geo_lite_struct.md new file mode 100644 index 00000000..75314f71 --- /dev/null +++ b/tests/testthat/_snaps/geo_lite_struct.md @@ -0,0 +1,7 @@ +# Fail + + Code + several <- geo_lite_struct("Madrid", full_results = TRUE, nominatim_server = "https://xyz.com/") + Message + https://xyz.com/search?format=jsonv2&limit=1&addressdetails=1&amenity=Madrid not reachable. + diff --git a/tests/testthat/_snaps/geo_lite_struct_sf.md b/tests/testthat/_snaps/geo_lite_struct_sf.md new file mode 100644 index 00000000..a33c66e7 --- /dev/null +++ b/tests/testthat/_snaps/geo_lite_struct_sf.md @@ -0,0 +1,7 @@ +# Fail + + Code + several <- geo_lite_struct_sf("madrid", full_results = TRUE, nominatim_server = "https://xyz.com/") + Message + https://xyz.com/search?format=geojson&limit=1&addressdetails=1&amenity=madrid not reachable. + diff --git a/tests/testthat/_snaps/reverse_geo_lite.md b/tests/testthat/_snaps/reverse_geo_lite.md new file mode 100644 index 00000000..e0acc520 --- /dev/null +++ b/tests/testthat/_snaps/reverse_geo_lite.md @@ -0,0 +1,8 @@ +# Fail + + Code + several <- reverse_geo_lite(40.75728, -73.98, full_results = TRUE, + nominatim_server = "https://xyz.com/") + Message + https://xyz.com/reverse?lat=40.75728&lon=-73.98&format=jsonv2&addressdetails=1 not reachable. + diff --git a/tests/testthat/_snaps/reverse_geo_lite_sf.md b/tests/testthat/_snaps/reverse_geo_lite_sf.md new file mode 100644 index 00000000..bcc965cc --- /dev/null +++ b/tests/testthat/_snaps/reverse_geo_lite_sf.md @@ -0,0 +1,8 @@ +# Fail + + Code + several <- reverse_geo_lite_sf(40.75728, -73.98, full_results = TRUE, + nominatim_server = "https://xyz.com/") + Message + https://xyz.com/reverse?lat=40.75728&lon=-73.98&format=geojson&addressdetails=1 not reachable. + diff --git a/tests/testthat/test-deprecated.R b/tests/testthat/test-deprecated.R deleted file mode 100644 index 3ae58ed0..00000000 --- a/tests/testthat/test-deprecated.R +++ /dev/null @@ -1,11 +0,0 @@ -test_that("Deprecated geo_amenity_sf", { - skip_if_not_installed("lifecycle") - - expect_snapshot(geo_amenity_sf(), error = TRUE) -}) - -test_that("Deprecated geo_amenity", { - skip_if_not_installed("lifecycle") - - expect_snapshot(geo_amenity(), error = TRUE) -}) diff --git a/tests/testthat/test-geo_address_lookup.R b/tests/testthat/test-geo_address_lookup.R index f6b4184c..4f02d342 100644 --- a/tests/testthat/test-geo_address_lookup.R +++ b/tests/testthat/test-geo_address_lookup.R @@ -84,7 +84,7 @@ test_that("Checking query", { expect_equal( nrow(geo_address_lookup(34633854, "W", full_results = TRUE, - custom_query = list(extratags = 1) + custom_query = list(extratags = TRUE) )), 1 ) @@ -125,3 +125,20 @@ test_that("Handle several", { expect_identical(as.vector(several$query), paste0(vector_type, vector_ids)[2]) }) + +test_that("Fail", { + skip_on_cran() + skip_if_api_server() + skip_if_offline() + + # KO + vector_ids <- c(146656, 240109189) + vector_type <- c("R", "N") + expect_snapshot(several <- geo_address_lookup( + vector_ids, vector_type, + full_results = TRUE, + nominatim_server = "https://xyz.com/" + )) + + expect_true(all(is.na(several[, 2:3]))) +}) diff --git a/tests/testthat/test-geo_address_lookup_sf.R b/tests/testthat/test-geo_address_lookup_sf.R index 1b77f847..d952a341 100644 --- a/tests/testthat/test-geo_address_lookup_sf.R +++ b/tests/testthat/test-geo_address_lookup_sf.R @@ -39,7 +39,7 @@ test_that("Checking query", { expect_equal( nrow(geo_address_lookup_sf(34633854, "W", full_results = TRUE, - custom_query = list(extratags = 1) + custom_query = list(extratags = TRUE) )), 1 ) expect_equal( @@ -110,3 +110,20 @@ test_that("Verify names", { # Do I have dups by any chance? expect_false(any(grepl("\\.[0-9]$", names(several)))) }) + +test_that("Fail", { + skip_on_cran() + skip_if_api_server() + skip_if_offline() + + # KO + vector_ids <- c(146656, 240109189) + vector_type <- c("R", "N") + expect_snapshot(several <- geo_address_lookup_sf( + vector_ids, vector_type, + full_results = TRUE, + nominatim_server = "https://xyz.com/" + )) + + expect_true(all(sf::st_is_empty(several))) +}) diff --git a/tests/testthat/test-geo_amenity.R b/tests/testthat/test-geo_amenity.R new file mode 100644 index 00000000..d3d56de4 --- /dev/null +++ b/tests/testthat/test-geo_amenity.R @@ -0,0 +1,113 @@ +test_that("Progress bar", { + skip_on_cran() + skip_if_api_server() + skip_if_offline() + + bbox <- c(-73.9894467311, 40.75573629, -73.9830630737, 40.75789245) + + # No pbar + expect_silent(geo_amenity(bbox, "restaurant")) + expect_silent(geo_amenity(bbox, "restaurant", progressbar = TRUE)) + + # Get a pbar + expect_output(aa <- geo_amenity(bbox, c("pub", "restaurant"))) + + # Not + expect_silent(aa <- geo_amenity( + bbox, c("pub", "restaurant"), + progressbar = FALSE + )) +}) + +test_that("Checking query", { + skip_on_cran() + skip_if_api_server() + skip_if_offline() + + expect_message(obj <- geo_amenity( + bbox = c(-1.1446, 41.5022, -0.4854, 41.8795), + c("pub", "restaurant"), + limit = 51 + ), "50 results") + + + expect_identical(names(obj), c("query", "lat", "lon", "address")) + + obj <- geo_amenity( + bbox = c(-1.1446, 41.5022, -0.4854, 41.8795), + "pub", + long = "ong", lat = "at", + full_results = FALSE, + return_addresses = FALSE + ) + expect_identical(names(obj), c("query", "at", "ong")) + + obj <- geo_amenity( + bbox = c(-1.1446, 41.5022, -0.4854, 41.8795), + "pub", + long = "ong", lat = "at", + full_results = FALSE, + return_addresses = TRUE + ) + + expect_identical(names(obj), c("query", "at", "ong", "address")) + + obj <- geo_amenity( + bbox = c(-1.1446, 41.5022, -0.4854, 41.8795), + "pub", + long = "ong", lat = "at", + full_results = TRUE, + return_addresses = FALSE + ) + + expect_identical(names(obj)[1:4], c("query", "at", "ong", "address")) + expect_gt(ncol(obj), 4) + + + expect_gt(nrow(geo_amenity( + bbox = c(-1.1446, 41.5022, -0.4854, 41.8795), + "pub", + limit = 10, + custom_query = list(countrycode = "es") + )), 4) + expect_equal(nrow(geo_amenity( + bbox = c(-1.1446, 41.5022, -0.4854, 41.8795), + "pub", + custom_query = list(countrycode = "es") + )), 1) + expect_equal(nrow(geo_amenity( + bbox = c(-1.1446, 41.5022, -0.4854, 41.8795), + "pub", + custom_query = list(extratags = 1) + )), 1) + + expect_lt(nrow(geo_amenity( + bbox = c(-1.1446, 41.5022, -0.4854, 41.8795), + "pub", + limit = 1, + strict = TRUE + )), 2) + + bbox_sfc <- bbox_to_poly(c(-1.1446, 41.5022, -0.4854, 41.8795)) + expect_s3_class(bbox_sfc, "sfc") + + expect_silent(a <- geo_amenity( + bbox = bbox_sfc, + "pub", + limit = 1, + strict = TRUE + )) + + + bbox_sf <- sf::st_sf(x = 1, bbox_sfc) + expect_s3_class(bbox_sf, "sf") + + bbox_sf <- sf::st_transform(bbox_sf, 3857) + + expect_silent(a <- geo_amenity( + bbox = bbox_sf, + "pub", + limit = 1, + strict = TRUE + )) +}) diff --git a/tests/testthat/test-geo_amenity_sf.R b/tests/testthat/test-geo_amenity_sf.R new file mode 100644 index 00000000..99a0be87 --- /dev/null +++ b/tests/testthat/test-geo_amenity_sf.R @@ -0,0 +1,110 @@ +test_that("Progress bar", { + skip_on_cran() + skip_if_api_server() + skip_if_offline() + + bbox <- c(-73.9894467311, 40.75573629, -73.9830630737, 40.75789245) + + # No pbar + expect_silent(geo_amenity_sf(bbox, "restaurant")) + expect_silent(geo_amenity_sf(bbox, "restaurant", progressbar = TRUE)) + + # Get a pbar + expect_output(aa <- geo_amenity_sf(bbox, c("pub", "restaurant"))) + + # Not + expect_silent(aa <- geo_amenity_sf( + bbox, c("pub", "restaurant"), + progressbar = FALSE + )) +}) + +test_that("Checking query", { + skip_on_cran() + skip_if_api_server() + skip_if_offline() + + expect_message(obj <- geo_amenity_sf( + bbox = c(-1.1446, 41.5022, -0.4854, 41.8795), + c("pub", "restaurant"), + limit = 51 + ), "50 results") + + + expect_identical(names(obj), c("query", "address", "geometry")) + + obj <- geo_amenity_sf( + bbox = c(-1.1446, 41.5022, -0.4854, 41.8795), + "pub", + full_results = FALSE, + return_addresses = FALSE + ) + expect_identical(names(obj), c("query", "geometry")) + + obj <- geo_amenity_sf( + bbox = c(-1.1446, 41.5022, -0.4854, 41.8795), + "pub", + full_results = FALSE, + return_addresses = TRUE + ) + + expect_identical(names(obj), c("query", "address", "geometry")) + + obj <- geo_amenity_sf( + bbox = c(-1.1446, 41.5022, -0.4854, 41.8795), + "pub", + full_results = TRUE, + return_addresses = FALSE + ) + + expect_identical(names(obj)[1:2], c("query", "address")) + expect_gt(ncol(obj), 3) + + + expect_gt(nrow(geo_amenity_sf( + bbox = c(-1.1446, 41.5022, -0.4854, 41.8795), + "pub", + limit = 10, + custom_query = list(countrycode = "es") + )), 4) + expect_equal(nrow(geo_amenity_sf( + bbox = c(-1.1446, 41.5022, -0.4854, 41.8795), + "pub", + custom_query = list(countrycode = "es") + )), 1) + expect_equal(nrow(geo_amenity_sf( + bbox = c(-1.1446, 41.5022, -0.4854, 41.8795), + "pub", + custom_query = list(extratags = 1) + )), 1) + + expect_lt(nrow(geo_amenity_sf( + bbox = c(-1.1446, 41.5022, -0.4854, 41.8795), + "pub", + limit = 1, + strict = TRUE + )), 2) + + bbox_sfc <- bbox_to_poly(c(-1.1446, 41.5022, -0.4854, 41.8795)) + expect_s3_class(bbox_sfc, "sfc") + + expect_silent(a <- geo_amenity_sf( + bbox = bbox_sfc, + "pub", + limit = 1, + strict = TRUE + )) + + + bbox_sf <- sf::st_sf(x = 1, bbox_sfc) + expect_s3_class(bbox_sf, "sf") + + bbox_sf <- sf::st_transform(bbox_sf, 3857) + + expect_silent(a <- geo_amenity_sf( + bbox = bbox_sf, + "pub", + limit = 1, + strict = TRUE + )) +}) diff --git a/tests/testthat/test-geo_lite.R b/tests/testthat/test-geo_lite.R index 790650dc..95142637 100644 --- a/tests/testthat/test-geo_lite.R +++ b/tests/testthat/test-geo_lite.R @@ -102,7 +102,7 @@ test_that("Checking query", { expect_equal( nrow(geo_lite("Madrid", - custom_query = list(extratags = 1) + custom_query = list(extratags = TRUE) )), 1 ) }) @@ -146,3 +146,17 @@ test_that("Progress bar", { # Not expect_silent(aa <- geo_lite(c("Madrid", "Barcelona"), progressbar = FALSE)) }) +test_that("Fail", { + skip_on_cran() + skip_if_api_server() + skip_if_offline() + + # KO + expect_snapshot(several <- geo_lite( + "Madrid", + full_results = TRUE, + nominatim_server = "https://xyz.com/" + )) + + expect_true(all(is.na(several[, 2:3]))) +}) diff --git a/tests/testthat/test-geo_lite_sf.R b/tests/testthat/test-geo_lite_sf.R index 51461554..9c371f77 100644 --- a/tests/testthat/test-geo_lite_sf.R +++ b/tests/testthat/test-geo_lite_sf.R @@ -109,7 +109,7 @@ test_that("Checking query", { expect_equal( nrow(geo_lite_sf("Madrid", - custom_query = list(extratags = 1) + custom_query = list(extratags = TRUE) )), 1 ) }) @@ -172,3 +172,17 @@ test_that("Progress bar", { aa <- geo_lite_sf(c("Madrid", "Barcelona"), progressbar = FALSE) ) }) +test_that("Fail", { + skip_on_cran() + skip_if_api_server() + skip_if_offline() + + # KO + expect_snapshot(several <- geo_lite_sf( + "madrid", + full_results = TRUE, + nominatim_server = "https://xyz.com/" + )) + + expect_true(all(sf::st_is_empty(several))) +}) diff --git a/tests/testthat/test-geo_lite_struct.R b/tests/testthat/test-geo_lite_struct.R new file mode 100644 index 00000000..f03ea411 --- /dev/null +++ b/tests/testthat/test-geo_lite_struct.R @@ -0,0 +1,141 @@ +test_that("Returning empty query", { + skip_on_cran() + skip_if_api_server() + + expect_message( + obj <- geo_lite_struct(), + "Nothing to search for" + ) + + expect_message( + obj <- geo_lite_struct(amenity = "xbzbzbzoa aiaia"), + "No results for" + ) + + expect_true(nrow(obj) == 1) + expect_true(obj$q_amenity == "xbzbzbzoa aiaia") + expect_s3_class(obj, "tbl") + expect_identical(names(obj), c( + "q_amenity", "q_street", "q_city", "q_county", + "q_state", "q_country", "q_postalcode", "lat", + "lon" + )) + + expect_true(is.na(obj$lat)) + expect_true(is.na(obj$lon)) + + expect_message( + obj_renamed <- geo_lite_struct("xbzbzbzoa aiaia", + lat = "lata", + long = "longa" + ), + "No results for" + ) + + expect_identical( + names(obj_renamed), + c( + "q_amenity", "q_street", "q_city", "q_county", + "q_state", "q_country", "q_postalcode", "lata", + "longa" + ) + ) + + names(obj_renamed) <- names(obj) + + expect_identical(obj, obj_renamed) +}) + +test_that("Data format", { + skip_on_cran() + skip_if_api_server() + skip_if_offline() + + obj <- geo_lite_struct(city = "Madrid") + + expect_s3_class(obj, "tbl") + expect_false(inherits(geo_lite("Madrid"), "sf")) + # this is _not_ a _sf function +}) + + +test_that("Checking query", { + skip_on_cran() + skip_if_api_server() + skip_if_offline() + + + expect_message( + obj <- geo_lite_struct( + city = c("Madrid", "Barcelona"), + limit = 51 + ), "50 results" + ) + + + expect_identical( + rev(names(obj))[1:3], + rev(c("lat", "lon", "address")) + ) + + + obj <- geo_lite_struct( + city = "Madrid", + long = "ong", lat = "at", + full_results = FALSE, + return_addresses = FALSE + ) + expect_identical(rev(names(obj))[1:2], rev(c("at", "ong"))) + + obj <- geo_lite_struct( + city = "Madrid", + long = "ong", lat = "at", + full_results = FALSE, + return_addresses = TRUE + ) + + expect_identical(rev(names(obj))[1:3], rev(c("at", "ong", "address"))) + + obj <- geo_lite_struct( + city = "Madrid", + long = "ong", lat = "at", + full_results = TRUE, + return_addresses = FALSE + ) + + expect_gt(ncol(obj), 10) + + + expect_gt( + nrow(geo_lite_struct("Catedral", + country = "ES", + limit = 10 + )), 4 + ) + + expect_equal( + nrow(geo_lite_struct("Madrid", + custom_query = list(countrycode = "es") + )), 1 + ) + + expect_equal( + nrow(geo_lite_struct("Madrid", + custom_query = list(extratags = TRUE) + )), 1 + ) +}) +test_that("Fail", { + skip_on_cran() + skip_if_api_server() + skip_if_offline() + + # KO + expect_snapshot(several <- geo_lite_struct( + "Madrid", + full_results = TRUE, + nominatim_server = "https://xyz.com/" + )) + + expect_true(all(is.na(several[, c("lat", "lon")]))) +}) diff --git a/tests/testthat/test-geo_lite_struct_sf.R b/tests/testthat/test-geo_lite_struct_sf.R new file mode 100644 index 00000000..b0ac7fb6 --- /dev/null +++ b/tests/testthat/test-geo_lite_struct_sf.R @@ -0,0 +1,143 @@ +test_that("Returning empty query", { + skip_on_cran() + skip_if_api_server() + + expect_message( + obj <- geo_lite_struct_sf(), + "Nothing to search for" + ) + expect_s3_class(obj, "sf") + expect_true(sf::st_is_empty(obj)) + + + expect_message( + obj <- geo_lite_struct_sf("xbzbzbzoa aiaia"), + "No results for" + ) + expect_s3_class(obj, "sf") + expect_true(sf::st_is_empty(obj)) + + expect_true(nrow(obj) == 1) + expect_true(obj$q_amenity == "xbzbzbzoa aiaia") + expect_s3_class(obj, "sf") + expect_s3_class(obj, "tbl") + expect_true(sf::st_is_empty(obj)) + expect_identical(sf::st_crs(obj), sf::st_crs(4326)) +}) + + +test_that("Data format", { + skip_on_cran() + skip_if_api_server() + skip_if_offline() + + obj <- geo_lite_struct_sf(city = "Madrid") + + expect_s3_class(obj, "sf") + expect_s3_class(obj, "tbl") + expect_equal(nrow(obj), 1) + expect_identical(as.character(obj$q_city), "Madrid") + expect_true(all(grepl("POINT", sf::st_geometry_type(obj)))) + + + # Polygon + + expect_message( + test <- geo_lite_struct_sf( + city = "Madrid", + points_only = FALSE, limit = 100 + ), + "Nominatim provides 50 results as a maximum" + ) + + + expect_true(any(grepl("POLYGON", sf::st_geometry_type(test)))) + expect_s3_class(test, "sf") + expect_s3_class(test, "tbl") + expect_gt(nrow(test), 2) + expect_identical(unique(as.character(test$q_city)), "Madrid") +}) + +test_that("Checking query", { + skip_on_cran() + skip_if_api_server() + skip_if_offline() + + + expect_message( + obj <- geo_lite_struct_sf( + city = c("Madrid", "Barcelona"), + limit = 51 + ), "50 results" + ) + + expect_s3_class(obj, "sf") + expect_s3_class(obj, "tbl") + expect_identical(rev(names(obj))[1:2], rev(c("address", "geometry"))) + + obj_old <- obj + obj <- geo_lite_struct_sf("Madrid", + full_results = FALSE, + return_addresses = FALSE + ) + + expect_s3_class(obj, "sf") + expect_s3_class(obj, "tbl") + expect_false("address" %in% names(obj)) + + obj_old1 <- obj + obj <- geo_lite_struct_sf( + city = "Madrid", + full_results = FALSE, + return_addresses = TRUE + ) + + expect_s3_class(obj, "sf") + expect_s3_class(obj, "tbl") + expect_identical(rev(names(obj))[1:2], rev(c("address", "geometry"))) + + obj <- geo_lite_struct_sf( + city = "Madrid", + full_results = TRUE, + return_addresses = FALSE + ) + expect_s3_class(obj, "sf") + expect_s3_class(obj, "tbl") + expect_gt(ncol(obj), 4) + + + expect_gt( + nrow(geo_lite_struct_sf("restaurant", + limit = 50, + custom_query = list(countrycode = "es") + )), 4 + ) + + expect_equal( + nrow(geo_lite_struct_sf("school", + custom_query = list(countrycode = "es") + )), 1 + ) + + expect_equal( + nrow(geo_lite_struct_sf("hospital", + custom_query = list(extratags = TRUE) + )), 1 + ) +}) + + +test_that("Fail", { + skip_on_cran() + skip_if_api_server() + skip_if_offline() + + # KO + expect_snapshot(several <- geo_lite_struct_sf( + "madrid", + full_results = TRUE, + nominatim_server = "https://xyz.com/" + )) + + expect_true(all(sf::st_is_empty(several))) +}) diff --git a/tests/testthat/test-reverse_geo_lite.R b/tests/testthat/test-reverse_geo_lite.R index 7b785f11..2b164882 100644 --- a/tests/testthat/test-reverse_geo_lite.R +++ b/tests/testthat/test-reverse_geo_lite.R @@ -156,7 +156,7 @@ test_that("Check unnesting", { lat = c(40.75728, 55.95335), long = c(-73.98586, -3.188375), full_results = TRUE, - custom_query = list(extratags = 1) + custom_query = list(extratags = TRUE) ) expect_s3_class(sev, "tbl") @@ -228,3 +228,17 @@ test_that("Progress bar", { # Not expect_silent(aa <- reverse_geo_lite(lat, long, progressbar = FALSE)) }) +test_that("Fail", { + skip_on_cran() + skip_if_api_server() + skip_if_offline() + + # KO + expect_snapshot(several <- reverse_geo_lite( + 40.75728, -73.98, + full_results = TRUE, + nominatim_server = "https://xyz.com/" + )) + + expect_true(is.na(several$address)) +}) diff --git a/tests/testthat/test-reverse_geo_lite_sf.R b/tests/testthat/test-reverse_geo_lite_sf.R index 0af288db..d47cdc3f 100644 --- a/tests/testthat/test-reverse_geo_lite_sf.R +++ b/tests/testthat/test-reverse_geo_lite_sf.R @@ -186,7 +186,7 @@ test_that("Check unnesting", { lat = c(40.75728, 55.95335), long = c(-73.98586, -3.188375), full_results = TRUE, - custom_query = list(extratags = 1) + custom_query = list(extratags = TRUE) ) expect_s3_class(sev, "tbl") @@ -249,3 +249,17 @@ test_that("Progress bar", { # Not expect_silent(aa <- reverse_geo_lite_sf(lat, long, progressbar = FALSE)) }) +test_that("Fail", { + skip_on_cran() + skip_if_api_server() + skip_if_offline() + + # KO + expect_snapshot(several <- reverse_geo_lite_sf( + 40.75728, -73.98, + full_results = TRUE, + nominatim_server = "https://xyz.com/" + )) + + expect_true(all(sf::st_is_empty(several))) +}) diff --git a/vignettes/articles/ex_leaflet.Rmd b/vignettes/articles/ex_leaflet.Rmd new file mode 100644 index 00000000..e1b984aa --- /dev/null +++ b/vignettes/articles/ex_leaflet.Rmd @@ -0,0 +1,193 @@ +--- +title: "Example: nominatimlite and leaflet maps" +subtitle: "Combine nominatimlite and leaflet maps" +--- + +```{r, include = FALSE} +knitr::opts_chunk$set( + collapse = TRUE, + comment = "#>", + warning = FALSE, + message = FALSE, + dpi = 300, + tidy = "styler", + dev = "ragg_png", + out.width = "100%" +) +``` + +## Example + +The following example shows how it is possible to create a nice [leaflet +map](https://rstudio.github.io/leaflet/) with data retrieved with +**nominatimlite**. + +This widget is browsable and filterable thanks to **crosstalk** and +**reactable**: + +```{r example} +# Coffee Shops and Restaurants around the Eiffel Tower + + +library(nominatimlite) +library(sf) +library(leaflet) +library(dplyr) +library(tidyr) +library(reactable) +library(crosstalk) + + + +# Step 1: Eiffel Tower +eiffel_tower <- geo_lite_sf("Eiffel Tower, Paris, France", points_only = FALSE) + + + +# Step 2: Coffee Shops and Restaurants nearby + +# Create a buffer of 1km around the Eiffel Tower +buff <- eiffel_tower %>% + st_transform(3857) %>% + st_centroid() %>% + st_buffer(1000) + +cf_bk <- geo_amenity_sf(buff, + amenity = c("cafe", "restaurant"), limit = 50, + full_results = TRUE, + custom_query = list(extratags = TRUE) +) %>% + # Build address with street, house number, suburb and postcode + unite("addr", address.road, address.house_number, address.postcode, + address.suburb, + sep = ", ", na.rm = TRUE + ) + + +# Labels and icons +labs <- paste0("", cf_bk$name, "
", cf_bk$addr) + +# Assign icons +# Base url for icons +icon_url <- paste0( + "https://raw.githubusercontent.com/dieghernan/arcgeocoder/", + "main/vignettes/articles/" +) + +leaf_icons <- icons( + ifelse(cf_bk$type == "cafe", + paste0(icon_url, "coffee-cup.png"), + paste0(icon_url, "restaurant.png") + ), + iconWidth = 20, iconHeight = 20, + iconAnchorX = 10, iconAnchorY = 10 +) + + + +# Step 3: Crosstalk object +cf_bk_data <- cf_bk %>% + select( + Place = name, Type = type, Address = addr, + City = address.city, URL = extratags.website, + Phone = extratags.phone + ) %>% + SharedData$new(group = "Food") + + +# Step 4: Leaflet map with crosstalk +# Init leaflet map +lmend <- leaflet( + data = cf_bk_data, + elementId = "EiffelTower", width = "100%", height = "60vh", + options = leafletOptions(minZoom = 12) +) %>% + addProviderTiles( + provider = "CartoDB.Positron", + group = "CartoDB.Positron" + ) %>% + addTiles(group = "OSM") %>% + addPolygons(data = eiffel_tower) %>% + addMarkers(popup = labs, icon = leaf_icons) %>% + addLayersControl( + baseGroups = c("CartoDB.Positron", "OSM"), + position = "topleft", + options = layersControlOptions(collapsed = FALSE) + ) + + + +# Step 5: Reactable for filtering +tb <- reactable(cf_bk_data, + selection = "multiple", + onClick = "select", + rowStyle = list(cursor = "pointer"), + filterable = TRUE, + searchable = TRUE, + showPageSizeOptions = TRUE, + striped = TRUE, + defaultColDef = colDef(vAlign = "center", minWidth = 150), + paginationType = "jump", + elementId = "coffees", + columns = list( + Place = colDef( + sticky = "left", rowHeader = TRUE, name = "", + cell = function(value) { + htmltools::strong(value) + } + ), + URL = colDef(cell = function(value) { + # Render as a link + if (is.null(value) | is.na(value)) { + return("") + } + htmltools::a(href = value, target = "_blank", as.character(value)) + }), + Phone = colDef(cell = function(value) { + # Render as a link + if (is.null(value) | is.na(value)) { + return("") + } + clearphone <- gsub("-", "", value) + clearphone <- gsub(" ", "", clearphone) + htmltools::a( + href = paste0("tel:", clearphone), target = "_blank", + as.character(value) + ) + }) + ) +) +``` + +## Widget + +```{r widget} +# Last step: Display all +htmltools::browsable( + htmltools::tagList(lmend, tb) +) +``` + +## Attributions + +- [Eiffel tower icons created by Freepik - + Flaticon](https://www.flaticon.com/free-icons/eiffel-tower "eiffel tower icons") +- [Mug icons created by Freepik - + Flaticon](https://www.flaticon.com/free-icons/mug "mug icons") +- [Food icons created by Freepik - + Flaticon](https://www.flaticon.com/free-icons/food "restaurant icons") + +## Session info + +
+ +Details + +```{r session, echo=FALSE} +if (!require("sessioninfo")) { + install.packages("sessioninfo") +} +sessioninfo::session_info() +``` + +
diff --git a/vignettes/nominatimlite.Rmd b/vignettes/nominatimlite.Rmd index b7f50c9d..f4e64dbe 100644 --- a/vignettes/nominatimlite.Rmd +++ b/vignettes/nominatimlite.Rmd @@ -1,164 +1,164 @@ ---- -title: "Get started with nominatimlite" -output: rmarkdown::html_vignette -desc: > - Quick examples showing what **nominatimlite** can do for you. -vignette: > - %\VignetteIndexEntry{Get started with nominatimlite} - %\VignetteEngine{knitr::rmarkdown} - %\VignetteEncoding{UTF-8} -bibliography: references.bib -link-citations: true ---- - - - - - -The goal of **nominatimlite** is to provide a light interface for geocoding -addresses, based on the [Nominatim -API](https://nominatim.org/release-docs/latest/). It also allows to load spatial -objects using the **sf** package. - -Full site with examples and vignettes on - - -## What is Nominatim? - -**Nominatim** is a tool to search -[OpenStreetMap](https://www.openstreetmap.org/) data by name and address -([geocoding](https://wiki.openstreetmap.org/wiki/Geocoding "Geocoding")) and to -generate synthetic addresses of OSM points (reverse geocoding). - -## Why **nominatimlite**? - -The main goal of **nominatimlite** is to access the Nominatim API avoiding the -dependency on **curl**. In some situations, **curl** may not be available or -accessible, so **nominatimlite** uses base functions to overcome this -limitation. - -## Recommended packages - -There are other packages much more complete and mature than **nominatimlite**, -that presents similar features: - -- [**tidygeocoder**](https://jessecambon.github.io/tidygeocoder/) - [@R-tidygeocoder]: Allows to interface with Nominatim, Google, TomTom, - Mapbox, etc. for geocoding and reverse geocoding. -- [**osmdata**](https://docs.ropensci.org/osmdata/) [@R-osmdata]: Great for - downloading spatial data from OpenStreetMap, via the [Overpass - API](https://wiki.openstreetmap.org/wiki/Overpass_API). -- [**arcgeocoder**](https://dieghernan.github.io/arcgeocoder/) - [@R-arcgeocoder]: Lite interface for geocoding with the ArcGIS REST API - Service. - -## Usage - -### `sf` objects - -With **nominatimlite** you can extract spatial objects easily: - - -```r -library(nominatimlite) - -# Extract some points - Pizza Hut in California - -CA <- geo_lite_sf("California", points_only = FALSE) - -pizzahut <- geo_lite_sf("Pizza Hut, California", - limit = 50, - custom_query = list(countrycodes = "us") -) - -library(ggplot2) - -ggplot(CA) + - geom_sf() + - geom_sf(data = pizzahut, col = "red") -``` - -![Pizza Hut in California](../man/figures/README-pizzahut-1.png){width="100%"} - -You can also extract polygon and line objects (if available) using the option -`points_only = FALSE`: - - -```r -sol_poly <- geo_lite_sf("Statue of Liberty, NY, USA", points_only = FALSE) - -ggplot(sol_poly) + - geom_sf() -``` - -![The Statue of -Liberty](../man/figures/README-statue_liberty-1.png){width="100%"} - -### Geocoding and reverse geocoding - -*Note: examples adapted from **tidygeocoder** package* - -In this first example we will geocode a few addresses using the `geo_lite()` -function: - - -```r -library(tibble) - -# create a dataframe with addresses -some_addresses <- tribble( - ~name, ~addr, - "White House", "1600 Pennsylvania Ave NW, Washington, DC", - "Transamerica Pyramid", "600 Montgomery St, San Francisco, CA 94111", - "Willis Tower", "233 S Wacker Dr, Chicago, IL 60606" -) - -# geocode the addresses -lat_longs <- geo_lite(some_addresses$addr, lat = "latitude", long = "longitude") -#> | | | 0% | |================= | 33% | |================================= | 67% | |==================================================| 100% -``` - -Only latitude and longitude are returned from the geocoder service in this -example, but `full_results = TRUE` can be used to return all of the data from -the geocoder service. - - - -|query | latitude| longitude|address | -|:------------------------------------------|--------:|----------:|:-----------------------------------------------------------------------------------------------------------------| -|1600 Pennsylvania Ave NW, Washington, DC | 38.89770| -77.03655|White House, 1600, Pennsylvania Avenue Northwest, Ward 2, Washington, District of Columbia, 20500, United States | -|600 Montgomery St, San Francisco, CA 94111 | 37.79520| -122.40279|Transamerica Pyramid, 600, Montgomery Street, Financial District, San Francisco, California, 94111, United States | -|233 S Wacker Dr, Chicago, IL 60606 | 41.87874| -87.63596|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 -`geo_lite()` function, but now we specify the input data columns with the `lat` -and `long` arguments. The dataset used here is from the geocoder query above. -The single line address is returned in a column named by the `address`. - - -```r -reverse <- reverse_geo_lite( - lat = lat_longs$latitude, long = lat_longs$longitude, - address = "address_found" -) -#> | | | 0% | |================= | 33% | |================================= | 67% | |==================================================| 100% -``` - - - -|address_found | lat| lon| -|:-----------------------------------------------------------------------------------------------------------------|--------:|----------:| -|White House, 1600, Pennsylvania Avenue Northwest, Ward 2, Washington, District of Columbia, 20500, United States | 38.89770| -77.03655| -|Transamerica Pyramid, 600, Montgomery Street, Financial District, San Francisco, California, 94111, United States | 37.79520| -122.40279| -|Willis Tower, 233, South Wacker Drive, Printer's Row, Loop, Chicago, Cook County, Illinois, 60606, United States | 41.87874| -87.63596| - - - -For more advance users, see [Nominatim -docs](https://nominatim.org/release-docs/latest/api/Search/) to check the -parameters available. - -## References +--- +title: "Get started with nominatimlite" +output: rmarkdown::html_vignette +desc: > + Quick examples showing what **nominatimlite** can do for you. +vignette: > + %\VignetteIndexEntry{Get started with nominatimlite} + %\VignetteEngine{knitr::rmarkdown} + %\VignetteEncoding{UTF-8} +bibliography: references.bib +link-citations: true +--- + + + + + +The goal of **nominatimlite** is to provide a light interface for geocoding +addresses, based on the [Nominatim +API](https://nominatim.org/release-docs/latest/). It also allows to load spatial +objects using the **sf** package. + +Full site with examples and vignettes on + + +## What is Nominatim? + +**Nominatim** is a tool to search +[OpenStreetMap](https://www.openstreetmap.org/) data by name and address +([geocoding](https://wiki.openstreetmap.org/wiki/Geocoding "Geocoding")) and to +generate synthetic addresses of OSM points (reverse geocoding). + +## Why **nominatimlite**? + +The main goal of **nominatimlite** is to access the Nominatim API avoiding the +dependency on **curl**. In some situations, **curl** may not be available or +accessible, so **nominatimlite** uses base functions to overcome this +limitation. + +## Recommended packages + +There are other packages much more complete and mature than **nominatimlite**, +that presents similar features: + +- [**tidygeocoder**](https://jessecambon.github.io/tidygeocoder/) + [@R-tidygeocoder]: Allows to interface with Nominatim, Google, TomTom, + Mapbox, etc. for geocoding and reverse geocoding. +- [**osmdata**](https://docs.ropensci.org/osmdata/) [@R-osmdata]: Great for + downloading spatial data from OpenStreetMap, via the [Overpass + API](https://wiki.openstreetmap.org/wiki/Overpass_API). +- [**arcgeocoder**](https://dieghernan.github.io/arcgeocoder/) + [@R-arcgeocoder]: Lite interface for geocoding with the ArcGIS REST API + Service. + +## Usage + +### `sf` objects + +With **nominatimlite** you can extract spatial objects easily: + + +```r +library(nominatimlite) + +# Extract some points - Pizza Hut in California + +CA <- geo_lite_sf("California", points_only = FALSE) + +pizzahut <- geo_lite_sf("Pizza Hut, California", + limit = 50, + custom_query = list(countrycodes = "us") +) + +library(ggplot2) + +ggplot(CA) + + geom_sf() + + geom_sf(data = pizzahut, col = "red") +``` + +![Pizza Hut in California](../man/figures/README-pizzahut-1.png){width="100%"} + +You can also extract polygon and line objects (if available) using the option +`points_only = FALSE`: + + +```r +sol_poly <- geo_lite_sf("Statue of Liberty, NY, USA", points_only = FALSE) + +ggplot(sol_poly) + + geom_sf() +``` + +![The Statue of +Liberty](../man/figures/README-statue_liberty-1.png){width="100%"} + +### Geocoding and reverse geocoding + +*Note: examples adapted from **tidygeocoder** package* + +In this first example we will geocode a few addresses using the `geo_lite()` +function: + + +```r +library(tibble) + +# create a dataframe with addresses +some_addresses <- tribble( + ~name, ~addr, + "White House", "1600 Pennsylvania Ave NW, Washington, DC", + "Transamerica Pyramid", "600 Montgomery St, San Francisco, CA 94111", + "Willis Tower", "233 S Wacker Dr, Chicago, IL 60606" +) + +# geocode the addresses +lat_longs <- geo_lite(some_addresses$addr, lat = "latitude", long = "longitude") +#> | | | 0% | |================= | 33% | |================================= | 67% | |==================================================| 100% +``` + +Only latitude and longitude are returned from the geocoder service in this +example, but `full_results = TRUE` can be used to return all of the data from +the geocoder service. + + + +|query | latitude| longitude|address | +|:------------------------------------------|--------:|----------:|:-----------------------------------------------------------------------------------------------------------------| +|1600 Pennsylvania Ave NW, Washington, DC | 38.89770| -77.03655|White House, 1600, Pennsylvania Avenue Northwest, Ward 2, Washington, District of Columbia, 20500, United States | +|600 Montgomery St, San Francisco, CA 94111 | 37.79520| -122.40279|Transamerica Pyramid, 600, Montgomery Street, Financial District, San Francisco, California, 94111, United States | +|233 S Wacker Dr, Chicago, IL 60606 | 41.87874| -87.63596|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 +`geo_lite()` function, but now we specify the input data columns with the `lat` +and `long` arguments. The dataset used here is from the geocoder query above. +The single line address is returned in a column named by the `address`. + + +```r +reverse <- reverse_geo_lite( + lat = lat_longs$latitude, long = lat_longs$longitude, + address = "address_found" +) +#> | | | 0% | |================= | 33% | |================================= | 67% | |==================================================| 100% +``` + + + +|address_found | lat| lon| +|:-----------------------------------------------------------------------------------------------------------------|--------:|----------:| +|White House, 1600, Pennsylvania Avenue Northwest, Ward 2, Washington, District of Columbia, 20500, United States | 38.89770| -77.03655| +|Transamerica Pyramid, 600, Montgomery Street, Financial District, San Francisco, California, 94111, United States | 37.79520| -122.40279| +|Willis Tower, 233, South Wacker Drive, Printer's Row, Loop, Chicago, Cook County, Illinois, 60606, United States | 41.87874| -87.63596| + + + +For more advance users, see [Nominatim +docs](https://nominatim.org/release-docs/latest/api/Search/) to check the +parameters available. + +## References