├── .github ├── .gitignore ├── workflows │ ├── lint.yaml │ ├── document.yaml │ ├── update-citation-cff.yaml │ ├── R-CMD-check.yaml │ ├── test-coverage.yaml │ ├── style.yaml │ └── rhub.yaml ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── pull_request_template.md └── contributing.md ├── revdep ├── failures.md ├── problems.md ├── .gitignore ├── cran.md └── README.md ├── tests ├── testthat.R └── testthat │ ├── _snaps │ ├── deprecated.md │ └── oc_check_query.md │ ├── test-oc_build_url.R │ ├── test-oc_api_ok.R │ ├── test-oc_clear_cache.R │ ├── test-oc_key.R │ ├── test-oc_points.R │ ├── test-oc_get.R │ ├── test-oc_check_status.R │ ├── helper-opencage.R │ ├── test-oc_bbox.R │ ├── test-oc_config.R │ ├── test-deprecated.R │ ├── test-oc_check_query.R │ ├── test-oc_reverse.R │ └── test-oc_forward.R ├── data └── countrycodes.rda ├── R ├── opencage-package.R ├── countrycodes.R ├── oc_api_ok.R ├── memoise.R ├── oc_progress.R ├── zzz.R ├── oc_key.R ├── oc_points.R ├── oc_bbox.R ├── oc_config.R ├── oc_check_query.R ├── deprecated.R └── oc_process.R ├── data-raw ├── countrycodes.R └── countrycodes.csv ├── .lintr ├── man ├── countrycodes.Rd ├── oc_key_present.Rd ├── oc_api_ok.Rd ├── opencage-deprecated.Rd ├── oc_clear_cache.Rd ├── opencage_key.Rd ├── figures │ ├── lifecycle-stable.svg │ ├── lifecycle-defunct.svg │ ├── lifecycle-archived.svg │ ├── lifecycle-maturing.svg │ ├── lifecycle-deprecated.svg │ ├── lifecycle-superseded.svg │ ├── lifecycle-experimental.svg │ ├── lifecycle-questioning.svg │ └── lifecycle-soft-deprecated.svg ├── opencage-package.Rd ├── oc_points.Rd ├── oc_bbox.Rd ├── opencage_reverse.Rd ├── oc_reverse.Rd ├── opencage_forward.Rd ├── oc_config.Rd ├── oc_reverse_df.Rd ├── oc_forward.Rd └── oc_forward_df.Rd ├── .Rbuildignore ├── vignettes ├── precompile.R └── output_options.Rmd.src ├── opencage.Rproj ├── NAMESPACE ├── .gitignore ├── cran-comments.md ├── CITATION.cff ├── DESCRIPTION ├── README.Rmd ├── README.md └── NEWS.md /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /revdep/failures.md: -------------------------------------------------------------------------------- 1 | *Wow, no problems at all. :)* -------------------------------------------------------------------------------- /revdep/problems.md: -------------------------------------------------------------------------------- 1 | *Wow, no problems at all. :)* -------------------------------------------------------------------------------- /revdep/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | !*.md 4 | !*.R 5 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | 3 | test_check("opencage") 4 | -------------------------------------------------------------------------------- /data/countrycodes.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ropensci/opencage/HEAD/data/countrycodes.rda -------------------------------------------------------------------------------- /R/opencage-package.R: -------------------------------------------------------------------------------- 1 | #' @keywords internal 2 | #' @importFrom rlang .data 3 | "_PACKAGE" 4 | 5 | utils::globalVariables("countrycodes") 6 | -------------------------------------------------------------------------------- /data-raw/countrycodes.R: -------------------------------------------------------------------------------- 1 | countrycodes <- read.csv("data-raw/countrycodes.csv", na = "") 2 | usethis::use_data(countrycodes, overwrite = TRUE, version = 2) 3 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/deprecated.md: -------------------------------------------------------------------------------- 1 | # `opencage_key(quiet = FALSE)` messages 2 | 3 | Code 4 | opencage_key(quiet = FALSE) 5 | Message 6 | Using OpenCage API Key from envvar OPENCAGE_KEY 7 | 8 | -------------------------------------------------------------------------------- /R/countrycodes.R: -------------------------------------------------------------------------------- 1 | #' Country codes 2 | #' @format All possible ISO 3166-1 Alpha 2 standard country codes. 3 | #' @name countrycodes 4 | #' @docType data 5 | #' @keywords data 6 | #' @examples 7 | #' data("countrycodes") 8 | NULL 9 | -------------------------------------------------------------------------------- /revdep/cran.md: -------------------------------------------------------------------------------- 1 | ## revdepcheck results 2 | 3 | We checked 1 reverse dependencies, comparing R CMD check results across CRAN and dev versions of this package. 4 | 5 | * We saw 0 new problems 6 | * We failed to check 0 packages 7 | 8 | -------------------------------------------------------------------------------- /.lintr: -------------------------------------------------------------------------------- 1 | linters: linters_with_tags( 2 | c("package_development", "default"), 3 | cyclocomp_linter = NULL, # .oc_check_query() 4 | indentation_linter = NULL) # https://github.com/r-lib/lintr/issues/2034, https://github.com/r-lib/lintr/issues/2535 5 | -------------------------------------------------------------------------------- /tests/testthat/test-oc_build_url.R: -------------------------------------------------------------------------------- 1 | test_that("oc_build_url returns a string", { 2 | expect_type( 3 | oc_build_url( 4 | query_par = list(placename = "Haarlem"), 5 | endpoint = "json" 6 | ), 7 | "character" 8 | ) 9 | }) 10 | -------------------------------------------------------------------------------- /tests/testthat/test-oc_api_ok.R: -------------------------------------------------------------------------------- 1 | test_that("oc_api_ok", { 2 | skip_on_cran() 3 | skip_if_offline("httpbin.org") 4 | 5 | # API ok 6 | expect_true(oc_api_ok()) 7 | 8 | # not ok 9 | expect_false(oc_api_ok("https://httpbin.org/status/500")) 10 | }) 11 | -------------------------------------------------------------------------------- /man/countrycodes.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/countrycodes.R 3 | \docType{data} 4 | \name{countrycodes} 5 | \alias{countrycodes} 6 | \title{Country codes} 7 | \format{ 8 | All possible ISO 3166-1 Alpha 2 standard country codes. 9 | } 10 | \description{ 11 | Country codes 12 | } 13 | \examples{ 14 | data("countrycodes") 15 | } 16 | \keyword{data} 17 | -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^\.travis\.yml$ 4 | ^README\.md$ 5 | ^README\.Rmd$ 6 | ^\.lintr$ 7 | ^\.gitignore$ 8 | ^\.Rhistory$ 9 | ^CONDUCT\.md$ 10 | datamanagement.R 11 | ^appveyor\.yml$ 12 | ^cran-comments\.md$ 13 | ^doc$ 14 | ^Meta$ 15 | ^vignettes\/precompile.R$ 16 | ^vignettes\/.+\.Rmd\.src$ 17 | ^\.github$ 18 | ^revdep$ 19 | ^CRAN-RELEASE$ 20 | ^data-raw$ 21 | ^CITATION\.cff$ 22 | -------------------------------------------------------------------------------- /vignettes/precompile.R: -------------------------------------------------------------------------------- 1 | # Precompiled vignettes that depend on API key 2 | 3 | library(knitr) # also load the current version of the package 4 | stopifnot("no OPENCAGE_KEY envvar present" = nzchar(Sys.getenv("OPENCAGE_KEY"))) 5 | knit("vignettes/opencage.Rmd.src", "vignettes/opencage.Rmd") 6 | knit("vignettes/customise_query.Rmd.src", "vignettes/customise_query.Rmd") 7 | knit("vignettes/output_options.Rmd.src", "vignettes/output_options.Rmd") 8 | -------------------------------------------------------------------------------- /R/oc_api_ok.R: -------------------------------------------------------------------------------- 1 | #' Is the OpenCage API available? 2 | #' 3 | #' Checks whether the OpenCage API can be reached. 4 | #' 5 | #' @param url The URL of the OpenCage API, . 6 | #' 7 | #' @return A single logical value, `TRUE` or `FALSE`. 8 | #' 9 | #' @export 10 | #' @keywords internal 11 | 12 | oc_api_ok <- function(url = "https://api.opencagedata.com") { 13 | crul::ok(url, useragent = "https://github.com/ropensci/opencage") 14 | } 15 | -------------------------------------------------------------------------------- /R/memoise.R: -------------------------------------------------------------------------------- 1 | #' Clear the opencage cache 2 | #' 3 | #' Forget past results and reset the \pkg{opencage} cache. 4 | #' 5 | #' @examplesIf oc_key_present() && oc_api_ok() 6 | #' 7 | #' system.time(oc_reverse(latitude = 10, longitude = 10)) 8 | #' system.time(oc_reverse(latitude = 10, longitude = 10)) 9 | #' oc_clear_cache() 10 | #' system.time(oc_reverse(latitude = 10, longitude = 10)) 11 | #' 12 | #' @export 13 | oc_clear_cache <- function() { 14 | memoise::forget(oc_get_memoise) 15 | } 16 | -------------------------------------------------------------------------------- /man/oc_key_present.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/oc_key.R 3 | \name{oc_key_present} 4 | \alias{oc_key_present} 5 | \title{Is an OpenCage API key present?} 6 | \usage{ 7 | oc_key_present() 8 | } 9 | \value{ 10 | A single logical value, \code{TRUE} or \code{FALSE}. 11 | } 12 | \description{ 13 | Checks whether a potential OpenCage API key, i.e. a 32 character long, 14 | alphanumeric string, is stored in the environment variable \code{OPENCAGE_KEY}. 15 | } 16 | \keyword{internal} 17 | -------------------------------------------------------------------------------- /man/oc_api_ok.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/oc_api_ok.R 3 | \name{oc_api_ok} 4 | \alias{oc_api_ok} 5 | \title{Is the OpenCage API available?} 6 | \usage{ 7 | oc_api_ok(url = "https://api.opencagedata.com") 8 | } 9 | \arguments{ 10 | \item{url}{The URL of the OpenCage API, \url{https://api.opencagedata.com}.} 11 | } 12 | \value{ 13 | A single logical value, \code{TRUE} or \code{FALSE}. 14 | } 15 | \description{ 16 | Checks whether the OpenCage API can be reached. 17 | } 18 | \keyword{internal} 19 | -------------------------------------------------------------------------------- /man/opencage-deprecated.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/deprecated.R 3 | \name{opencage-deprecated} 4 | \alias{opencage-deprecated} 5 | \title{Deprecated functions in opencage} 6 | \description{ 7 | These functions still work but will be removed (defunct) in the next version. 8 | } 9 | \details{ 10 | \itemize{ 11 | \item \code{\link[=opencage_forward]{opencage_forward()}} 12 | \item \code{\link[=opencage_reverse]{opencage_reverse()}} 13 | \item \code{\link[=opencage_key]{opencage_key()}} 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /opencage.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | ProjectId: 3d5685ec-c81f-43a6-a376-4b6992e50ed3 3 | 4 | RestoreWorkspace: Default 5 | SaveWorkspace: Default 6 | AlwaysSaveHistory: Default 7 | 8 | EnableCodeIndexing: Yes 9 | UseSpacesForTab: Yes 10 | NumSpacesForTab: 2 11 | Encoding: UTF-8 12 | 13 | RnwWeave: Sweave 14 | LaTeX: pdfLaTeX 15 | 16 | AutoAppendNewline: Yes 17 | StripTrailingWhitespace: Yes 18 | 19 | BuildType: Package 20 | PackageUseDevtools: Yes 21 | PackageInstallArgs: --no-multiarch --with-keep.source 22 | PackageRoxygenize: rd,collate,namespace 23 | -------------------------------------------------------------------------------- /tests/testthat/test-oc_clear_cache.R: -------------------------------------------------------------------------------- 1 | test_that("oc_clear_cache clears cache", { 2 | skip_on_cran() 3 | skip_if_offline("httpbin.org") 4 | 5 | # until a memoise >v.1.1 is released, we need to run oc_get_memoise() twice to 6 | # have it really cache results 7 | # https://github.com/ropensci/opencage/pull/87#issuecomment-573573183 8 | replicate(2, oc_get_memoise("https://httpbin.org/get")) 9 | expect_true(memoise::has_cache(oc_get_memoise)("https://httpbin.org/get")) 10 | oc_clear_cache() 11 | expect_false(memoise::has_cache(oc_get_memoise)("https://httpbin.org/get")) 12 | }) 13 | -------------------------------------------------------------------------------- /man/oc_clear_cache.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/memoise.R 3 | \name{oc_clear_cache} 4 | \alias{oc_clear_cache} 5 | \title{Clear the opencage cache} 6 | \usage{ 7 | oc_clear_cache() 8 | } 9 | \description{ 10 | Forget past results and reset the \pkg{opencage} cache. 11 | } 12 | \examples{ 13 | \dontshow{if (oc_key_present() && oc_api_ok()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 14 | 15 | system.time(oc_reverse(latitude = 10, longitude = 10)) 16 | system.time(oc_reverse(latitude = 10, longitude = 10)) 17 | oc_clear_cache() 18 | system.time(oc_reverse(latitude = 10, longitude = 10)) 19 | \dontshow{\}) # examplesIf} 20 | } 21 | -------------------------------------------------------------------------------- /man/opencage_key.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/deprecated.R 3 | \name{opencage_key} 4 | \alias{opencage_key} 5 | \title{Retrieve Opencage API key} 6 | \usage{ 7 | opencage_key(quiet = TRUE) 8 | } 9 | \arguments{ 10 | \item{quiet}{Logical vector of length one indicating whether the key is 11 | returned quietly or whether a message is printed.} 12 | } 13 | \description{ 14 | \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} 15 | 16 | Deprecated and will be removed from the package together with 17 | \code{opencage_forward()} and \code{opencage_reverse()}. 18 | 19 | Retrieves the OpenCage API Key from the environment variable \code{OPENCAGE_KEY}. 20 | } 21 | \keyword{internal} 22 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method(oc_bbox,bbox) 4 | S3method(oc_bbox,data.frame) 5 | S3method(oc_bbox,default) 6 | S3method(oc_bbox,numeric) 7 | S3method(oc_forward_df,character) 8 | S3method(oc_forward_df,data.frame) 9 | S3method(oc_forward_df,default) 10 | S3method(oc_points,data.frame) 11 | S3method(oc_points,default) 12 | S3method(oc_points,numeric) 13 | S3method(oc_reverse_df,data.frame) 14 | S3method(oc_reverse_df,default) 15 | S3method(oc_reverse_df,numeric) 16 | export(oc_api_ok) 17 | export(oc_bbox) 18 | export(oc_clear_cache) 19 | export(oc_config) 20 | export(oc_forward) 21 | export(oc_forward_df) 22 | export(oc_key_present) 23 | export(oc_points) 24 | export(oc_reverse) 25 | export(oc_reverse_df) 26 | export(opencage_forward) 27 | export(opencage_key) 28 | export(opencage_reverse) 29 | importFrom(rlang,.data) 30 | -------------------------------------------------------------------------------- /R/oc_progress.R: -------------------------------------------------------------------------------- 1 | #' Initialise progress bar 2 | #' 3 | #' @param vec character vector for which to initialise a progress bar 4 | #' 5 | #' @noRd 6 | 7 | oc_init_progress <- function(vec) { 8 | progress::progress_bar$new( 9 | format = 10 | "Retrieving results from OpenCage [:spin] :percent ETA: :eta", 11 | total = length(vec), 12 | clear = FALSE, 13 | width = 60 14 | ) 15 | } 16 | 17 | #' Are the conditions met to show a progress bar 18 | #' 19 | #' A progress bar should only be shown 20 | #' - in an interactive session 21 | #' - when not knitting a document (i.e. knitr is in progress) 22 | #' - when not running in an R Notebook (html_notebook) chunk 23 | #' 24 | #' @noRd 25 | 26 | oc_show_progress <- function() { 27 | interactive() && 28 | !isTRUE(getOption("knitr.in.progress")) && 29 | !isTRUE(getOption("rstudio.notebook.executing")) 30 | } 31 | -------------------------------------------------------------------------------- /.github/workflows/lint.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: main 6 | pull_request: 7 | 8 | name: lint.yaml 9 | 10 | permissions: read-all 11 | 12 | jobs: 13 | lint: 14 | runs-on: ubuntu-latest 15 | env: 16 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 17 | steps: 18 | - uses: actions/checkout@v4 19 | 20 | - uses: r-lib/actions/setup-r@v2 21 | with: 22 | use-public-rspm: true 23 | 24 | - uses: r-lib/actions/setup-r-dependencies@v2 25 | with: 26 | extra-packages: any::lintr, local::. 27 | needs: lint 28 | 29 | - name: Lint 30 | run: lintr::lint_package() 31 | shell: Rscript {0} 32 | env: 33 | LINTR_ERROR_ON_LINT: true 34 | -------------------------------------------------------------------------------- /R/zzz.R: -------------------------------------------------------------------------------- 1 | # We use `<<-` below to modify the package's namespace. 2 | # It doesn't modify the global environment. 3 | # We do this to prevent build time dependencies on {memoise} and {ratelimitr}, 4 | # as recommended in . 5 | # Cf. for further details. 6 | 7 | # First make sure that the functions are defined at build time 8 | oc_get_limited <- oc_get 9 | oc_get_memoise <- oc_get_limited 10 | 11 | # Then modify them at load-time 12 | # nocov start 13 | .onLoad <- function(libname, pkgname) { 14 | # limit requests per second 15 | oc_get_limited <<- 16 | ratelimitr::limit_rate( 17 | oc_get, 18 | # rate can be changed via oc_config()/ratelimitr::UPDATE_RATE() 19 | ratelimitr::rate( 20 | n = 1L, 21 | period = 1L 22 | ) 23 | ) 24 | 25 | oc_get_memoise <<- memoise::memoise(oc_get_limited) 26 | } 27 | # nocov end 28 | -------------------------------------------------------------------------------- /man/figures/lifecycle-stable.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecyclestablestable -------------------------------------------------------------------------------- /man/figures/lifecycle-defunct.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecycledefunctdefunct -------------------------------------------------------------------------------- /man/figures/lifecycle-archived.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecyclearchivedarchived -------------------------------------------------------------------------------- /man/figures/lifecycle-maturing.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecyclematuringmaturing -------------------------------------------------------------------------------- /man/figures/lifecycle-deprecated.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecycledeprecateddeprecated -------------------------------------------------------------------------------- /man/figures/lifecycle-superseded.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecyclesupersededsuperseded -------------------------------------------------------------------------------- /man/figures/lifecycle-experimental.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecycleexperimentalexperimental -------------------------------------------------------------------------------- /man/figures/lifecycle-questioning.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecyclequestioningquestioning -------------------------------------------------------------------------------- /man/figures/lifecycle-soft-deprecated.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecyclesoft-deprecatedsoft-deprecated -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/r 2 | # Edit at https://www.gitignore.io/?templates=r 3 | 4 | ### R ### 5 | # History files 6 | .Rhistory 7 | .Rapp.history 8 | 9 | # Session Data files 10 | .RData 11 | .RDataTmp 12 | 13 | # User-specific files 14 | .Ruserdata 15 | 16 | # Example code in package build process 17 | *-Ex.R 18 | 19 | # Output files from R CMD build 20 | /*.tar.gz 21 | 22 | # Output files from R CMD check 23 | /*.Rcheck/ 24 | 25 | # RStudio files 26 | .Rproj.user/ 27 | 28 | # produced vignettes 29 | vignettes/*.html 30 | vignettes/*.pdf 31 | 32 | # OAuth2 token, see https://github.com/hadley/httr/releases/tag/v0.3 33 | .httr-oauth 34 | 35 | # knitr and R markdown default cache directories 36 | *_cache/ 37 | /cache/ 38 | 39 | # Temporary files created by R markdown 40 | *.utf8.md 41 | *.knit.md 42 | 43 | ### R.Bookdown Stack ### 44 | # R package: bookdown caching files 45 | /*_files/ 46 | 47 | # End of https://www.gitignore.io/api/r 48 | 49 | inst/doc 50 | doc 51 | Meta 52 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/oc_check_query.md: -------------------------------------------------------------------------------- 1 | # oc_check_between works 2 | 3 | Code 4 | oc_check_between(symbol, 0, 10) 5 | Condition 6 | Error: 7 | ! Every `symbol` must be between 0 and 10. 8 | 9 | --- 10 | 11 | Code 12 | oc_check_between(symbol, 11.0001, 11.0002) 13 | Condition 14 | Error: 15 | ! Every `symbol` must be between 11.0001 and 11.0002. 16 | 17 | --- 18 | 19 | Code 20 | oc_check_between(symbol, 0L, 10L) 21 | Condition 22 | Error: 23 | ! Every `symbol` must be between 0 and 10. 24 | 25 | --- 26 | 27 | Code 28 | oc_check_between(symbol, 11.0001, 11.0002) 29 | Condition 30 | Error: 31 | ! Every `symbol` must be between 11.0001 and 11.0002. 32 | 33 | --- 34 | 35 | Code 36 | oc_check_between(symbol, 0, 11) 37 | Condition 38 | Error: 39 | ! Every `symbol` must be numeric. 40 | 41 | --- 42 | 43 | Code 44 | oc_check_between(symbol, 0, 11) 45 | Condition 46 | Error: 47 | ! Every `symbol` must be numeric. 48 | 49 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for {opencage} 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 22 | 23 | ## Description 24 | 25 | 36 | -------------------------------------------------------------------------------- /cran-comments.md: -------------------------------------------------------------------------------- 1 | ## Release summary 2 | 3 | ### v0.2.2 4 | This patch version fixes a test that caused an error on CRAN's Solaris (, ). 5 | 6 | ## Test environments 7 | * local x86_64-w64-mingw32/x64, R 4.0.4 8 | * GitHub Actions : 9 | * Ubuntu 20.04, R devel, release and oldrel 10 | * windows-latest, R release 11 | * macOS-latest, R release 12 | * R-hub: 13 | * Fedora Linux, R-devel, clang, gfortran 14 | * Ubuntu Linux 20.04.1 LTS, R-release, GCC 15 | * Windows Server 2008 R2 SP1, R-devel, 32/64 bit 16 | * win-builder (devel) 17 | 18 | ## R CMD check results (local) 19 | 20 | Duration: 2m 10.8s 21 | 22 | > checking CRAN incoming feasibility ... NOTE 23 | Maintainer: 'Daniel Possenriede ' 24 | 25 | Days since last update: 4 26 | 27 | 0 errors √ | 0 warnings √ | 1 note x 28 | 29 | ## revdepcheck results 30 | 31 | We checked 1 reverse dependency, comparing R CMD check results across CRAN and dev versions of this package. 32 | 33 | * We saw 0 new problems 34 | * We failed to check 0 packages 35 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 14 | 15 | ## Description 16 | 17 | 20 | 21 | - [ ] example included 22 | 26 | - [ ] related issue mentioned 27 | 31 | - [ ] documentation included 32 | 37 | - [ ] tests included 38 | 41 | -------------------------------------------------------------------------------- /man/opencage-package.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/opencage-package.R 3 | \docType{package} 4 | \name{opencage-package} 5 | \alias{opencage} 6 | \alias{opencage-package} 7 | \title{opencage: Geocode with the OpenCage API} 8 | \description{ 9 | Geocode with the OpenCage API, either from place name to longitude and latitude (forward geocoding) or from longitude and latitude to the name and address of a location (reverse geocoding), see \url{https://opencagedata.com}. 10 | } 11 | \seealso{ 12 | Useful links: 13 | \itemize{ 14 | \item \url{https://docs.ropensci.org/opencage/} 15 | \item \url{https://github.com/ropensci/opencage} 16 | \item Report bugs at \url{https://github.com/ropensci/opencage/issues} 17 | } 18 | 19 | } 20 | \author{ 21 | \strong{Maintainer}: Daniel Possenriede \email{possenriede+r@gmail.com} (\href{https://orcid.org/0000-0002-6738-9845}{ORCID}) 22 | 23 | Authors: 24 | \itemize{ 25 | \item Jesse Sadler (\href{https://orcid.org/0000-0001-6081-9681}{ORCID}) 26 | \item Maëlle Salmon (\href{https://orcid.org/0000-0002-2815-0399}{ORCID}) 27 | } 28 | 29 | Other contributors: 30 | \itemize{ 31 | \item Noam Ross [contributor] 32 | \item Jake Russ [contributor] 33 | \item Julia Silge (Julia Silge reviewed \{opencage\} (v. 0.1.0) for rOpenSci, see .) [reviewer] 34 | } 35 | 36 | } 37 | \keyword{internal} 38 | -------------------------------------------------------------------------------- /.github/workflows/document.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | paths: ["R/**"] 6 | 7 | name: document.yaml 8 | 9 | permissions: read-all 10 | 11 | jobs: 12 | document: 13 | runs-on: ubuntu-latest 14 | env: 15 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 16 | permissions: 17 | contents: write 18 | steps: 19 | - name: Checkout repo 20 | uses: actions/checkout@v4 21 | with: 22 | fetch-depth: 0 23 | 24 | - name: Setup R 25 | uses: r-lib/actions/setup-r@v2 26 | with: 27 | use-public-rspm: true 28 | 29 | - name: Install dependencies 30 | uses: r-lib/actions/setup-r-dependencies@v2 31 | with: 32 | extra-packages: any::roxygen2 33 | needs: roxygen2 34 | 35 | - name: Document 36 | run: roxygen2::roxygenise() 37 | shell: Rscript {0} 38 | 39 | - name: Commit and push changes 40 | run: | 41 | git config --local user.name "$GITHUB_ACTOR" 42 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 43 | git add man/\* NAMESPACE DESCRIPTION 44 | git commit -m "Update documentation" || echo "No changes to commit" 45 | git pull --ff-only 46 | git push origin 47 | -------------------------------------------------------------------------------- /man/oc_points.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/oc_points.R 3 | \name{oc_points} 4 | \alias{oc_points} 5 | \alias{oc_points.numeric} 6 | \alias{oc_points.data.frame} 7 | \title{List of points for OpenCage queries} 8 | \usage{ 9 | oc_points(...) 10 | 11 | \method{oc_points}{numeric}(latitude, longitude, ...) 12 | 13 | \method{oc_points}{data.frame}(data, latitude, longitude, ...) 14 | } 15 | \arguments{ 16 | \item{...}{Ignored.} 17 | 18 | \item{latitude, longitude}{Numeric vectors of latitude and longitude values.} 19 | 20 | \item{data}{A \code{data.frame} containing at least 2 columns with \code{latitude} and 21 | \code{longitude} values.} 22 | } 23 | \value{ 24 | A list of points. Each point is a named vector of length 2 containing 25 | a latitude/longitude coordinate pair. 26 | } 27 | \description{ 28 | Create a list of points (latitude/longitude coordinate pairs) for OpenCage 29 | queries. 30 | } 31 | \examples{ 32 | oc_points(-21.01404, 55.26077) 33 | 34 | xdf <- 35 | data.frame( 36 | place = c("Hamburg", "Los Angeles"), 37 | lat = c(53.5503, 34.0536), 38 | lon = c(10.0006, -118.2427) 39 | ) 40 | oc_points( 41 | data = xdf, 42 | latitude = lat, 43 | longitude = lon 44 | ) 45 | 46 | # create a list column with points with dplyr 47 | library(dplyr) 48 | xdf \%>\% 49 | mutate( 50 | points = 51 | oc_points( 52 | lat, 53 | lon 54 | ) 55 | ) 56 | 57 | } 58 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | --- 8 | 9 | 23 | 24 | ## Description & steps to reproduce 25 | 32 | 33 | ## Session information 34 | 37 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | # -------------------------------------------- 2 | # CITATION file created with {cffr} R package 3 | # See also: https://docs.ropensci.org/cffr/ 4 | # -------------------------------------------- 5 | 6 | cff-version: 1.2.0 7 | message: 'To cite package "opencage" in publications use:' 8 | type: software 9 | license: GPL-2.0-or-later 10 | title: 'opencage: Geocode with the OpenCage API' 11 | version: 0.2.2.9000 12 | doi: 10.32614/CRAN.package.opencage 13 | abstract: Geocode with the OpenCage API, either from place name to longitude and latitude 14 | (forward geocoding) or from longitude and latitude to the name and address of a 15 | location (reverse geocoding), see . 16 | authors: 17 | - family-names: Possenriede 18 | given-names: Daniel 19 | email: possenriede+r@gmail.com 20 | orcid: https://orcid.org/0000-0002-6738-9845 21 | - family-names: Sadler 22 | given-names: Jesse 23 | orcid: https://orcid.org/0000-0001-6081-9681 24 | - family-names: Salmon 25 | given-names: Maëlle 26 | orcid: https://orcid.org/0000-0002-2815-0399 27 | repository: https://CRAN.R-project.org/package=opencage 28 | repository-code: https://github.com/ropensci/opencage 29 | url: https://docs.ropensci.org/opencage/ 30 | contact: 31 | - family-names: Possenriede 32 | given-names: Daniel 33 | email: possenriede+r@gmail.com 34 | orcid: https://orcid.org/0000-0002-6738-9845 35 | keywords: 36 | - geocode 37 | - geocoder 38 | - opencage 39 | - opencage-api 40 | - opencage-geocoder 41 | - peer-reviewed 42 | - placenames 43 | - r 44 | - r-package 45 | - rspatial 46 | - rstats 47 | 48 | -------------------------------------------------------------------------------- /tests/testthat/test-oc_key.R: -------------------------------------------------------------------------------- 1 | # Test oc_check_key() ----------------------------------------------------- 2 | 3 | test_that("oc_check_key checks key", { 4 | expect_error( 5 | oc_check_key( 6 | key = 45 7 | ), 8 | "`key` must be a character vector." 9 | ) 10 | expect_error( 11 | oc_check_key( 12 | key = c(key_200, key_402) 13 | ), 14 | "`key` must be a vector of length one." 15 | ) 16 | expect_error( 17 | oc_check_key( 18 | key = NULL 19 | ), 20 | "`key` must be provided." 21 | ) 22 | expect_error( 23 | oc_check_key(substr(key_200, 1L, 30L)), 24 | "32 character long, alphanumeric string" 25 | ) 26 | }) 27 | 28 | ## Test oc_mask_key ## 29 | 30 | test_that("oc_mask_key masks key", { 31 | withr::local_envvar(c("OPENCAGE_KEY" = key_200)) 32 | expect_match(oc_mask_key(key_200), "OPENCAGE_KEY", fixed = TRUE) 33 | }) 34 | 35 | test_that("oc_mask_key does nothing if no key present", { 36 | withr::local_envvar(c("OPENCAGE_KEY" = "")) 37 | expect_match( 38 | oc_mask_key("no_key_available"), 39 | "no_key_available", 40 | fixed = TRUE 41 | ) 42 | }) 43 | 44 | # Test oc_key_present() --------------------------------------------------- 45 | 46 | test_that("oc_key_present detects if key is present", { 47 | withr::local_envvar(c("OPENCAGE_KEY" = key_200)) 48 | expect_true(oc_key_present()) 49 | }) 50 | 51 | test_that("oc_key_present detects if key is not present", { 52 | withr::local_envvar(c("OPENCAGE_KEY" = "")) 53 | expect_false(oc_key_present()) 54 | 55 | withr::local_envvar(c("OPENCAGE_KEY" = "string_but_no_key!!!11")) 56 | expect_false(oc_key_present()) 57 | }) 58 | -------------------------------------------------------------------------------- /.github/workflows/update-citation-cff.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/master/examples 2 | # The action runs when: 3 | # - A new release is published 4 | # - The DESCRIPTION or inst/CITATION are modified 5 | # - Can be run manually 6 | # For customizing the triggers, visit https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows 7 | on: 8 | release: 9 | types: [published] 10 | push: 11 | paths: 12 | - DESCRIPTION 13 | - inst/CITATION 14 | workflow_dispatch: 15 | 16 | name: update-citation-cff.yaml 17 | 18 | permissions: read-all 19 | 20 | jobs: 21 | update-citation-cff: 22 | runs-on: ubuntu-latest 23 | env: 24 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 25 | steps: 26 | - uses: actions/checkout@v3 27 | - uses: r-lib/actions/setup-r@v2 28 | with: 29 | use-public-rspm: true 30 | - uses: r-lib/actions/setup-r-dependencies@v2 31 | with: 32 | extra-packages: | 33 | any::cffr 34 | any::V8 35 | 36 | - name: Update CITATION.cff 37 | run: | 38 | 39 | library(cffr) 40 | 41 | # Create your CITATION.cff file 42 | cff_write(dependencies = FALSE) 43 | 44 | shell: Rscript {0} 45 | 46 | - name: Commit results 47 | run: | 48 | git config --local user.name "$GITHUB_ACTOR" 49 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 50 | git add CITATION.cff 51 | git commit -m 'Update CITATION.cff' || echo "No changes to commit" 52 | git push origin || echo "No changes to commit" 53 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | branches: 8 | - main 9 | 10 | name: R-CMD-check.yaml 11 | 12 | permissions: read-all 13 | 14 | jobs: 15 | R-CMD-check: 16 | runs-on: ${{ matrix.config.os }} 17 | 18 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 19 | 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | config: 24 | - {os: macos-latest, r: 'release', oc_auth: OPENCAGE_KEY} 25 | - {os: windows-latest, r: 'release', oc_auth: OPENCAGE_NOKEY} 26 | - {os: ubuntu-latest, r: 'devel', oc_auth: OPENCAGE_NOKEY, http-user-agent: 'release'} 27 | - {os: ubuntu-latest, r: 'release', oc_auth: OPENCAGE_NOKEY} 28 | - {os: ubuntu-latest, r: 'oldrel-1', oc_auth: OPENCAGE_NOKEY} 29 | 30 | env: 31 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 32 | R_KEEP_PKG_SOURCE: yes 33 | 34 | steps: 35 | - uses: actions/checkout@v4 36 | 37 | - uses: r-lib/actions/setup-pandoc@v2 38 | 39 | - uses: r-lib/actions/setup-r@v2 40 | with: 41 | r-version: ${{ matrix.config.r }} 42 | http-user-agent: ${{ matrix.config.http-user-agent }} 43 | use-public-rspm: true 44 | 45 | - uses: r-lib/actions/setup-r-dependencies@v2 46 | with: 47 | extra-packages: any::rcmdcheck 48 | needs: check 49 | 50 | - uses: r-lib/actions/check-r-package@v2 51 | env: 52 | OPENCAGE_KEY: ${{ secrets[matrix.config.oc_auth] }} 53 | with: 54 | upload-snapshots: true 55 | build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' 56 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Type: Package 2 | Package: opencage 3 | Title: Geocode with the OpenCage API 4 | Version: 0.2.2.9000 5 | Authors@R: c( 6 | person("Daniel", "Possenriede", , "possenriede+r@gmail.com", role = c("aut", "cre"), 7 | comment = c(ORCID = "0000-0002-6738-9845")), 8 | person("Jesse", "Sadler", role = "aut", 9 | comment = c(ORCID = "0000-0001-6081-9681")), 10 | person("Maëlle", "Salmon", role = "aut", 11 | comment = c(ORCID = "0000-0002-2815-0399")), 12 | person("Noam", "Ross", role = "ctb"), 13 | person("Jake", "Russ", role = "ctb"), 14 | person("Julia", "Silge", role = "rev", 15 | comment = "Julia Silge reviewed \\{opencage\\} (v. 0.1.0) for rOpenSci, see .") 16 | ) 17 | Description: Geocode with the OpenCage API, either from place name to 18 | longitude and latitude (forward geocoding) or from longitude and 19 | latitude to the name and address of a location (reverse geocoding), 20 | see . 21 | License: GPL (>= 2) 22 | URL: https://docs.ropensci.org/opencage/, 23 | https://github.com/ropensci/opencage 24 | BugReports: https://github.com/ropensci/opencage/issues 25 | Depends: 26 | R (>= 3.4.0) 27 | Imports: 28 | crul (>= 0.5.2), 29 | dplyr (>= 0.7.4), 30 | jsonlite (>= 1.5), 31 | lifecycle, 32 | memoise (>= 1.1.0), 33 | progress (>= 1.1.2), 34 | purrr (>= 0.2.4), 35 | ratelimitr (>= 0.4.0), 36 | rlang, 37 | tibble (>= 1.4.2), 38 | tidyr (>= 0.8.0), 39 | withr (>= 2.0.0) 40 | Suggests: 41 | knitr (>= 1.19), 42 | mockery, 43 | rmarkdown (>= 1.8), 44 | sf (>= 0.9), 45 | testthat (>= 3.1.0) 46 | VignetteBuilder: 47 | knitr 48 | RdMacros: 49 | lifecycle 50 | Config/testthat/edition: 3 51 | Encoding: UTF-8 52 | Language: en-GB 53 | LazyData: TRUE 54 | Roxygen: list(markdown = TRUE) 55 | RoxygenNote: 7.3.2 56 | -------------------------------------------------------------------------------- /tests/testthat/test-oc_points.R: -------------------------------------------------------------------------------- 1 | # Test oc_points ---------------------------------------------------------- 2 | 3 | test_that("oc_points works with numeric", { 4 | pnts1 <- oc_points(-5.6, 51.2) 5 | expect_type(pnts1, "list") 6 | expect_type(pnts1[[1]], "double") 7 | expect_length(pnts1[[1]], 2) 8 | expect_identical(unlist(pnts1), c(latitude = -5.6, longitude = 51.2)) 9 | expect_output( 10 | object = print(pnts1), 11 | regexp = "latitude\\s+longitude\\s+\\n\\s+-5.6\\s+51.2" 12 | ) 13 | }) 14 | 15 | test_that("oc_points works with data.frame", { 16 | xdf <- 17 | data.frame( 18 | y = c(54.0, 42.73), 19 | x = c(10.3, -78.81) 20 | ) 21 | 22 | pnts2 <- oc_points(data = xdf, latitude = y, longitude = x) 23 | expect_type(pnts2, "list") 24 | expect_type(pnts2[[1]], "double") 25 | expect_type(pnts2[[2]], "double") 26 | expect_length(pnts2[[1]], 2) 27 | expect_length(pnts2[[2]], 2) 28 | expect_identical(unlist(pnts2[1]), c(latitude = 54.0, longitude = 10.3)) 29 | expect_identical(unlist(pnts2[2]), c(latitude = 42.73, longitude = -78.81)) 30 | }) 31 | 32 | test_that("oc_points.default gives informative error message", { 33 | expect_error( 34 | object = oc_points("one", "two"), 35 | regexp = "Can't create a list of points", 36 | fixed = TRUE 37 | ) 38 | }) 39 | 40 | # Test checks for oc_points ------------------------------------------------- 41 | 42 | test_that("oc_point checks point", { 43 | expect_error( 44 | oc_points(NA_real_, 51.280430), 45 | "Every `point` element must be non-missing." 46 | ) 47 | expect_error( 48 | oc_points(-0.563160, "51.280430"), 49 | "Every `longitude` must be numeric." 50 | ) 51 | expect_error( 52 | oc_points(-0.563160, 51280430), 53 | "Every `longitude` must be between -180 and 180." 54 | ) 55 | expect_error( 56 | oc_points(-563160, 51.280430), 57 | "Every `latitude` must be between -90 and 90." 58 | ) 59 | }) 60 | -------------------------------------------------------------------------------- /R/oc_key.R: -------------------------------------------------------------------------------- 1 | #' Check OpenCage API key 2 | #' 3 | #' Function that checks the OpenCage API key 4 | #' 5 | #' @param key OpenCage API key 6 | #' 7 | #' @noRd 8 | 9 | oc_check_key <- function(key) { 10 | if (is.null(key) || identical(key, "")) { 11 | stop( 12 | "An OpenCage API `key` must be provided.\n", 13 | "See help(oc_config)", 14 | call. = FALSE 15 | ) 16 | } else if (!is.character(key)) { 17 | stop("The OpenCage API `key` must be a character vector.", call. = FALSE) 18 | } else if (length(key) > 1) { 19 | stop( 20 | "The OpenCage API `key` must be a vector of length one.", 21 | call. = FALSE 22 | ) 23 | } else if (!identical(nchar(key), 32L)) { 24 | stop( 25 | "The OpenCage API key must be a 32 character long, alphanumeric string.\n", # nolint: line_length_linter. 26 | "See ", 27 | call. = FALSE 28 | ) 29 | } 30 | } 31 | 32 | #' Mask OpenCage API key 33 | #' 34 | #' Function that masks the OpenCage API key. It looks up the environment 35 | #' variable `OPENCAGE_KEY` and replaces the key in a string with by replacing it 36 | #' with "OPENCAGE_KEY". 37 | #' 38 | #' @param string Character string, which may contain an OpenCage API key 39 | #' 40 | #' @noRd 41 | 42 | oc_mask_key <- function(string) { 43 | if (oc_key_present()) { 44 | gsub( 45 | x = string, 46 | pattern = Sys.getenv("OPENCAGE_KEY"), 47 | replacement = "OPENCAGE_KEY" 48 | ) 49 | } else { 50 | return(string) 51 | } 52 | } 53 | 54 | #' Is an OpenCage API key present? 55 | #' 56 | #' Checks whether a potential OpenCage API key, i.e. a 32 character long, 57 | #' alphanumeric string, is stored in the environment variable `OPENCAGE_KEY`. 58 | #' 59 | #' @return A single logical value, `TRUE` or `FALSE`. 60 | #' 61 | #' @export 62 | #' @keywords internal 63 | 64 | oc_key_present <- function() { 65 | identical(nchar(Sys.getenv("OPENCAGE_KEY")), 32L) 66 | } 67 | -------------------------------------------------------------------------------- /tests/testthat/test-oc_get.R: -------------------------------------------------------------------------------- 1 | # Test oc_get() ----------------------------------------------------------- 2 | 3 | test_that("oc_get returns a response object", { 4 | skip_if_oc_offline() 5 | withr::local_envvar(c("OPENCAGE_KEY" = key_200)) 6 | 7 | expect_s3_class( 8 | oc_get( 9 | oc_build_url( 10 | query_par = list( 11 | placename = "irrelevant", 12 | key = Sys.getenv("OPENCAGE_KEY") 13 | ), 14 | endpoint = "json" 15 | ) 16 | ), 17 | "HttpResponse" 18 | ) 19 | }) 20 | 21 | test_that("oc_get returns a response object for Namibia NA countrycode", { 22 | skip_if_no_key() 23 | skip_if_oc_offline() 24 | 25 | expect_s3_class( 26 | oc_get( 27 | oc_build_url( 28 | query_par = list( 29 | placename = "Windhoek", 30 | key = Sys.getenv("OPENCAGE_KEY"), 31 | countrycode = "NA" 32 | ), 33 | endpoint = "json" 34 | ) 35 | ), 36 | "HttpResponse" 37 | ) 38 | }) 39 | 40 | test_that("oc_get returns a response object for vector countrycode", { 41 | skip_if_no_key() 42 | skip_if_oc_offline() 43 | 44 | expect_s3_class( 45 | oc_get( 46 | oc_build_url( 47 | query_par = list( 48 | placename = "Paris", 49 | key = Sys.getenv("OPENCAGE_KEY"), 50 | countrycode = c("FR", "US") 51 | ), 52 | endpoint = "json" 53 | ) 54 | ), 55 | "HttpResponse" 56 | ) 57 | }) 58 | 59 | test_that("oc_get_limited is rate limited", { 60 | skip_on_cran() 61 | skip_if_offline("httpbin.org") 62 | 63 | tm <- system.time({ 64 | replicate(2, oc_get_limited("https://httpbin.org/get")) 65 | }) 66 | rate <- ratelimitr::get_rates(oc_get_limited) 67 | expect_gte(tm[["elapsed"]], rate[[1]][["period"]] / rate[[1]][["n"]]) 68 | }) 69 | 70 | test_that("oc_get_memoise memoises", { 71 | skip_on_cran() 72 | skip_if_offline("httpbin.org") 73 | 74 | oc_get_memoise("https://httpbin.org/get") 75 | tm <- system.time({ 76 | oc_get_memoise("https://httpbin.org/get") 77 | }) 78 | expect_lt(tm["elapsed"], 0.5) 79 | }) 80 | -------------------------------------------------------------------------------- /revdep/README.md: -------------------------------------------------------------------------------- 1 | # Platform 2 | 3 | |field |value | 4 | |:--------|:----------------------------| 5 | |version |R version 4.0.4 (2021-02-15) | 6 | |os |Windows 10 x64 | 7 | |system |x86_64, mingw32 | 8 | |ui |RStudio | 9 | |language |en | 10 | |collate |German_Germany.1252 | 11 | |ctype |German_Germany.1252 | 12 | |tz |Europe/Berlin | 13 | |date |2021-02-18 | 14 | 15 | # Dependencies 16 | 17 | |package |old |new | | 18 | |:-----------|:------|:------|:--| 19 | |opencage |0.2.1 |0.2.2 |* | 20 | |assertthat |0.2.1 |0.2.1 | | 21 | |cachem |1.0.4 |1.0.4 | | 22 | |cli |2.3.0 |2.3.0 | | 23 | |cpp11 |0.2.6 |0.2.6 | | 24 | |crayon |1.4.1 |1.4.1 | | 25 | |crul |1.1.0 |1.1.0 | | 26 | |curl |4.3 |4.3 | | 27 | |digest |0.6.27 |0.6.27 | | 28 | |dplyr |1.0.4 |1.0.4 | | 29 | |ellipsis |0.3.1 |0.3.1 | | 30 | |fansi |0.4.2 |0.4.2 | | 31 | |fastmap |1.1.0 |1.1.0 | | 32 | |generics |0.1.0 |0.1.0 | | 33 | |glue |1.4.2 |1.4.2 | | 34 | |hms |1.0.0 |1.0.0 | | 35 | |httpcode |0.3.0 |0.3.0 | | 36 | |jsonlite |1.7.2 |1.7.2 | | 37 | |lifecycle |1.0.0 |1.0.0 | | 38 | |magrittr |2.0.1 |2.0.1 | | 39 | |memoise |2.0.0 |2.0.0 | | 40 | |mime |0.10 |0.10 | | 41 | |pillar |1.4.7 |1.4.7 | | 42 | |pkgconfig |2.0.3 |2.0.3 | | 43 | |prettyunits |1.1.1 |1.1.1 | | 44 | |progress |1.2.2 |1.2.2 | | 45 | |purrr |0.3.4 |0.3.4 | | 46 | |R6 |2.5.0 |2.5.0 | | 47 | |ratelimitr |0.4.1 |0.4.1 | | 48 | |Rcpp |1.0.6 |1.0.6 | | 49 | |rlang |0.4.10 |0.4.10 | | 50 | |tibble |3.0.6 |3.0.6 | | 51 | |tidyr |1.1.2 |1.1.2 | | 52 | |tidyselect |1.1.0 |1.1.0 | | 53 | |triebeard |0.3.0 |0.3.0 | | 54 | |urltools |1.7.3 |1.7.3 | | 55 | |utf8 |1.1.4 |1.1.4 | | 56 | |vctrs |0.3.6 |0.3.6 | | 57 | |withr |2.4.1 |2.4.1 | | 58 | 59 | # Revdeps 60 | 61 | -------------------------------------------------------------------------------- /data-raw/countrycodes.csv: -------------------------------------------------------------------------------- 1 | code 2 | AF 3 | AX 4 | AL 5 | DZ 6 | AS 7 | AD 8 | AO 9 | AI 10 | AQ 11 | AG 12 | AR 13 | AM 14 | AW 15 | AU 16 | AT 17 | AZ 18 | BS 19 | BH 20 | BD 21 | BB 22 | BY 23 | BE 24 | BZ 25 | BJ 26 | BM 27 | BT 28 | BO 29 | BQ 30 | BA 31 | BW 32 | BV 33 | BR 34 | IO 35 | BN 36 | BG 37 | BF 38 | BI 39 | KH 40 | CM 41 | CA 42 | CV 43 | KY 44 | CF 45 | TD 46 | CL 47 | CN 48 | CX 49 | CC 50 | CO 51 | KM 52 | CG 53 | CD 54 | CK 55 | CR 56 | CI 57 | HR 58 | CU 59 | CW 60 | CY 61 | CZ 62 | DK 63 | DJ 64 | DM 65 | DO 66 | EC 67 | EG 68 | SV 69 | GQ 70 | ER 71 | EE 72 | ET 73 | FK 74 | FO 75 | FJ 76 | FI 77 | FR 78 | GF 79 | PF 80 | TF 81 | GA 82 | GM 83 | GE 84 | DE 85 | GH 86 | GI 87 | GR 88 | GL 89 | GD 90 | GP 91 | GU 92 | GT 93 | GG 94 | GN 95 | GW 96 | GY 97 | HT 98 | HM 99 | VA 100 | HN 101 | HK 102 | HU 103 | IS 104 | IN 105 | ID 106 | IR 107 | IQ 108 | IE 109 | IM 110 | IL 111 | IT 112 | JM 113 | JP 114 | JE 115 | JO 116 | KZ 117 | KE 118 | KI 119 | KP 120 | KR 121 | KW 122 | KG 123 | LA 124 | LV 125 | LB 126 | LS 127 | LR 128 | LY 129 | LI 130 | LT 131 | LU 132 | MO 133 | MK 134 | MG 135 | MW 136 | MY 137 | MV 138 | ML 139 | MT 140 | MH 141 | MQ 142 | MR 143 | MU 144 | YT 145 | MX 146 | FM 147 | MD 148 | MC 149 | MN 150 | ME 151 | MS 152 | MA 153 | MZ 154 | MM 155 | NA 156 | NR 157 | NP 158 | NL 159 | NC 160 | NZ 161 | NI 162 | NE 163 | NG 164 | NU 165 | NF 166 | MP 167 | NO 168 | OM 169 | PK 170 | PW 171 | PS 172 | PA 173 | PG 174 | PY 175 | PE 176 | PH 177 | PN 178 | PL 179 | PT 180 | PR 181 | QA 182 | RE 183 | RO 184 | RU 185 | RW 186 | BL 187 | SH 188 | KN 189 | LC 190 | MF 191 | PM 192 | VC 193 | WS 194 | SM 195 | ST 196 | SA 197 | SN 198 | RS 199 | SC 200 | SL 201 | SG 202 | SX 203 | SK 204 | SI 205 | SB 206 | SO 207 | ZA 208 | GS 209 | SS 210 | ES 211 | LK 212 | SD 213 | SR 214 | SJ 215 | SZ 216 | SE 217 | CH 218 | SY 219 | TW 220 | TJ 221 | TZ 222 | TH 223 | TL 224 | TG 225 | TK 226 | TO 227 | TT 228 | TN 229 | TR 230 | TM 231 | TC 232 | TV 233 | UG 234 | UA 235 | AE 236 | GB 237 | US 238 | UM 239 | UY 240 | UZ 241 | VU 242 | VE 243 | VN 244 | VG 245 | VI 246 | WF 247 | EH 248 | YE 249 | ZM 250 | ZW 251 | -------------------------------------------------------------------------------- /tests/testthat/test-oc_check_status.R: -------------------------------------------------------------------------------- 1 | # Test oc_check_status() -------------------------------------------------- 2 | 3 | test_that("oc_check_status returns no error if HTTP status 200", { 4 | skip_on_cran() 5 | skip_if_oc_offline() 6 | 7 | withr::local_envvar(c("OPENCAGE_KEY" = key_200)) 8 | expect_type( 9 | oc_reverse(latitude = 0, longitude = 0), 10 | "list" 11 | ) 12 | }) 13 | 14 | test_that("oc_check_status returns 400 error if request is invalid", { 15 | skip_if_no_key() 16 | skip_if_oc_offline() 17 | 18 | # This shouldn't happen since we oc_check_query 19 | expect_error( 20 | oc_process( 21 | latitude = 280, 22 | longitude = 0, 23 | return = "json_list" 24 | ), 25 | "HTTP failure: 400" 26 | ) 27 | 28 | # We don't send queries with nchar(query) <= 1 to the API, see .oc_process() 29 | expect_error( 30 | oc_process( 31 | placename = " ", 32 | return = "json_list" 33 | ), 34 | "HTTP failure: 400" 35 | ) 36 | }) 37 | 38 | test_that("oc_check_status returns 401 error if key is invalid", { 39 | skip_on_cran() 40 | skip_if_oc_offline() 41 | 42 | withr::local_envvar(c("OPENCAGE_KEY" = "32charactersandnumbers1234567890")) 43 | expect_error( 44 | oc_reverse(latitude = 0, longitude = 0), 45 | "HTTP failure: 401" 46 | ) 47 | }) 48 | 49 | test_that("oc_check_status returns 402 error if quota exceeded", { 50 | skip_on_cran() 51 | skip_if_oc_offline() 52 | 53 | withr::local_envvar(c("OPENCAGE_KEY" = key_402)) 54 | expect_error( 55 | oc_reverse(latitude = 0, longitude = 0), 56 | "HTTP failure: 402" 57 | ) 58 | }) 59 | 60 | test_that("oc_check_status returns 403 error if key is blocked", { 61 | skip_on_cran() 62 | skip_if_oc_offline() 63 | 64 | withr::local_envvar(c("OPENCAGE_KEY" = key_403)) 65 | expect_error( 66 | oc_reverse(latitude = 0, longitude = 0), 67 | "HTTP failure: 403" 68 | ) 69 | }) 70 | 71 | test_that("oc_check_status returns 429 error if rate limit is exceeded", { 72 | skip_on_cran() 73 | skip_if_oc_offline() 74 | 75 | withr::local_envvar(c("OPENCAGE_KEY" = key_429)) 76 | expect_error( 77 | oc_reverse(latitude = 0, longitude = 0), 78 | "HTTP failure: 429" 79 | ) 80 | }) 81 | -------------------------------------------------------------------------------- /.github/workflows/test-coverage.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | name: test-coverage.yaml 10 | 11 | permissions: read-all 12 | 13 | jobs: 14 | test-coverage: 15 | runs-on: ubuntu-latest 16 | # skip for forks as secrets are only available in main repo 17 | # keep an eye on https://github.com/orgs/community/discussions/9098 18 | if: ${{ github.repository == 'ropensci/opencage' }} 19 | env: 20 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 21 | 22 | steps: 23 | - uses: actions/checkout@v4 24 | 25 | - uses: r-lib/actions/setup-r@v2 26 | with: 27 | use-public-rspm: true 28 | 29 | - uses: r-lib/actions/setup-r-dependencies@v2 30 | with: 31 | extra-packages: any::covr, any::xml2 32 | needs: coverage 33 | 34 | - name: Test coverage 35 | env: 36 | OPENCAGE_KEY: ${{ secrets.OPENCAGE_KEY }} 37 | run: | 38 | cov <- covr::package_coverage( 39 | quiet = FALSE, 40 | clean = FALSE, 41 | install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package") 42 | ) 43 | covr::to_cobertura(cov) 44 | shell: Rscript {0} 45 | 46 | - uses: codecov/codecov-action@v4 47 | with: 48 | # Fail if error if not on PR, or if on PR and token is given 49 | fail_ci_if_error: ${{ github.event_name != 'pull_request' || secrets.CODECOV_TOKEN }} 50 | file: ./cobertura.xml 51 | plugin: noop 52 | disable_search: true 53 | token: ${{ secrets.CODECOV_TOKEN }} 54 | 55 | - name: Show testthat output 56 | if: always() 57 | run: | 58 | ## -------------------------------------------------------------------- 59 | find '${{ runner.temp }}/package' -name 'testthat.Rout*' -exec cat '{}' \; || true 60 | shell: bash 61 | 62 | - name: Upload test results 63 | if: failure() 64 | uses: actions/upload-artifact@v4 65 | with: 66 | name: coverage-test-failures 67 | path: ${{ runner.temp }}/package 68 | -------------------------------------------------------------------------------- /man/oc_bbox.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/oc_bbox.R 3 | \name{oc_bbox} 4 | \alias{oc_bbox} 5 | \alias{oc_bbox.numeric} 6 | \alias{oc_bbox.data.frame} 7 | \alias{oc_bbox.bbox} 8 | \title{List of bounding boxes for OpenCage queries} 9 | \usage{ 10 | oc_bbox(...) 11 | 12 | \method{oc_bbox}{numeric}(xmin, ymin, xmax, ymax, ...) 13 | 14 | \method{oc_bbox}{data.frame}(data, xmin, ymin, xmax, ymax, ...) 15 | 16 | \method{oc_bbox}{bbox}(bbox, ...) 17 | } 18 | \arguments{ 19 | \item{...}{Ignored.} 20 | 21 | \item{xmin}{Minimum longitude (also known as \code{min_lon}, \code{southwest_lng}, 22 | \code{west}, or \code{left}).} 23 | 24 | \item{ymin}{Minimum latitude (also known as \code{min_lat}, \code{southwest_lat}, 25 | \code{south}, or \code{bottom}).} 26 | 27 | \item{xmax}{Maximum longitude (also known as \code{max_lon}, \code{northeast_lng}, 28 | \code{east}, or \code{right}).} 29 | 30 | \item{ymax}{Maximum latitude (also known as \code{max_lat}, \code{northeast_lat}, 31 | \code{north}, or \code{top}).} 32 | 33 | \item{data}{A \code{data.frame} containing at least 4 columns with \code{xmin}, \code{ymin}, 34 | \code{xmax}, and \code{ymax} values, respectively.} 35 | 36 | \item{bbox}{A \code{bbox} object, see \code{sf::st_bbox}.} 37 | } 38 | \value{ 39 | A list of bounding boxes, each of class \code{bbox}. 40 | } 41 | \description{ 42 | Create a list of bounding boxes for OpenCage queries. 43 | } 44 | \examples{ 45 | oc_bbox(-5.63160, 51.280430, 0.278970, 51.683979) 46 | 47 | xdf <- 48 | data.frame( 49 | place = c("Hamburg", "Hamburg"), 50 | northeast_lat = c(54.0276817, 42.7397729), 51 | northeast_lng = c(10.3252805, -78.812825), 52 | southwest_lat = c(53.3951118, 42.7091669), 53 | southwest_lng = c(8.1053284, -78.860521) 54 | ) 55 | oc_bbox( 56 | xdf, 57 | southwest_lng, 58 | southwest_lat, 59 | northeast_lng, 60 | northeast_lat 61 | ) 62 | 63 | # create bbox list column with dplyr 64 | library(dplyr) 65 | xdf \%>\% 66 | mutate( 67 | bbox = 68 | oc_bbox( 69 | southwest_lng, 70 | southwest_lat, 71 | northeast_lng, 72 | northeast_lat 73 | ) 74 | ) 75 | 76 | # create bbox list from a simple features bbox 77 | if (requireNamespace("sf", quietly = TRUE)) { 78 | library(sf) 79 | bbox <- st_bbox(c(xmin = 16.1, xmax = 16.6, ymax = 48.6, ymin = 47.9), 80 | crs = 4326 81 | ) 82 | oc_bbox(bbox) 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /.github/workflows/style.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | paths: ["**.[rR]", "**.[qrR]md", "**.[rR]markdown", "**.[rR]nw", "**.[rR]profile"] 6 | 7 | name: style.yaml 8 | 9 | permissions: read-all 10 | 11 | jobs: 12 | style: 13 | runs-on: ubuntu-latest 14 | permissions: 15 | contents: write 16 | env: 17 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 18 | steps: 19 | - name: Checkout repo 20 | uses: actions/checkout@v4 21 | with: 22 | fetch-depth: 0 23 | 24 | - name: Setup R 25 | uses: r-lib/actions/setup-r@v2 26 | with: 27 | use-public-rspm: true 28 | 29 | - name: Install dependencies 30 | uses: r-lib/actions/setup-r-dependencies@v2 31 | with: 32 | extra-packages: any::styler, any::roxygen2 33 | needs: styler 34 | 35 | - name: Enable styler cache 36 | run: styler::cache_activate() 37 | shell: Rscript {0} 38 | 39 | - name: Determine cache location 40 | id: styler-location 41 | run: | 42 | cat( 43 | "location=", 44 | styler::cache_info(format = "tabular")$location, 45 | "\n", 46 | file = Sys.getenv("GITHUB_OUTPUT"), 47 | append = TRUE, 48 | sep = "" 49 | ) 50 | shell: Rscript {0} 51 | 52 | - name: Cache styler 53 | uses: actions/cache@v4 54 | with: 55 | path: ${{ steps.styler-location.outputs.location }} 56 | key: ${{ runner.os }}-styler-${{ github.sha }} 57 | restore-keys: | 58 | ${{ runner.os }}-styler- 59 | ${{ runner.os }}- 60 | 61 | - name: Style 62 | run: styler::style_pkg() 63 | shell: Rscript {0} 64 | 65 | - name: Commit and push changes 66 | run: | 67 | if FILES_TO_COMMIT=($(git diff-index --name-only ${{ github.sha }} \ 68 | | egrep --ignore-case '\.(R|[qR]md|Rmarkdown|Rnw|Rprofile)$')) 69 | then 70 | git config --local user.name "$GITHUB_ACTOR" 71 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 72 | git commit ${FILES_TO_COMMIT[*]} -m "Style code (GHA)" 73 | git pull --ff-only 74 | git push origin 75 | else 76 | echo "No changes to commit." 77 | fi 78 | -------------------------------------------------------------------------------- /R/oc_points.R: -------------------------------------------------------------------------------- 1 | #' List of points for OpenCage queries 2 | #' 3 | #' Create a list of points (latitude/longitude coordinate pairs) for OpenCage 4 | #' queries. 5 | #' 6 | #' @param latitude,longitude Numeric vectors of latitude and longitude values. 7 | #' @param data A `data.frame` containing at least 2 columns with `latitude` and 8 | #' `longitude` values. 9 | #' @param ... Ignored. 10 | #' 11 | #' @return A list of points. Each point is a named vector of length 2 containing 12 | #' a latitude/longitude coordinate pair. 13 | #' @export 14 | #' 15 | #' @examples 16 | #' oc_points(-21.01404, 55.26077) 17 | #' 18 | #' xdf <- 19 | #' data.frame( 20 | #' place = c("Hamburg", "Los Angeles"), 21 | #' lat = c(53.5503, 34.0536), 22 | #' lon = c(10.0006, -118.2427) 23 | #' ) 24 | #' oc_points( 25 | #' data = xdf, 26 | #' latitude = lat, 27 | #' longitude = lon 28 | #' ) 29 | #' 30 | #' # create a list column with points with dplyr 31 | #' library(dplyr) 32 | #' xdf %>% 33 | #' mutate( 34 | #' points = 35 | #' oc_points( 36 | #' lat, 37 | #' lon 38 | #' ) 39 | #' ) 40 | #' 41 | oc_points <- function(...) UseMethod("oc_points") 42 | 43 | # No @name so it does not show up in the docs. 44 | #' @export 45 | oc_points.default <- function(x, ...) { 46 | stop( 47 | "Can't create a list of points from an object of class `", 48 | class(x)[[1]], "`.", 49 | call. = FALSE 50 | ) 51 | } 52 | 53 | #' @name oc_points 54 | #' @export 55 | oc_points.numeric <- function(latitude, longitude, ...) { 56 | pnts <- function(latitude, longitude) { 57 | oc_check_point(latitude = latitude, longitude = longitude) 58 | c(latitude = latitude, longitude = longitude) 59 | } 60 | purrr::pmap(list(latitude, longitude), pnts) 61 | } 62 | 63 | #' @name oc_points 64 | #' @export 65 | oc_points.data.frame <- function(data, latitude, longitude, ...) { 66 | latitude <- rlang::enquo(latitude) 67 | longitude <- rlang::enquo(longitude) 68 | 69 | oc_points( 70 | latitude = rlang::eval_tidy(latitude, data = data), 71 | longitude = rlang::eval_tidy(longitude, data = data) 72 | ) 73 | } 74 | 75 | #' Check whether a point has valid latitude and longitude values 76 | #' 77 | #' @param latitude numeric latitude value to check 78 | #' @param longitude numeric longitude value to check 79 | #' 80 | #' @noRd 81 | 82 | oc_check_point <- function(latitude, longitude) { 83 | if (anyNA(c(latitude, longitude))) { 84 | stop("Every `point` element must be non-missing.", call. = FALSE) 85 | } 86 | oc_check_between(latitude, -90, 90) 87 | oc_check_between(longitude, -180, 180) 88 | } 89 | -------------------------------------------------------------------------------- /tests/testthat/helper-opencage.R: -------------------------------------------------------------------------------- 1 | # test keys --------------------------------------------------------------- 2 | # https://opencagedata.com/api#testingkeys 3 | 4 | key_200 <- "6d0e711d72d74daeb2b0bfd2a5cdfdba" # always returns a 200 response 5 | key_402 <- "4372eff77b8343cebfc843eb4da4ddc4" # always returns a 402 responce 6 | key_403 <- "2e10e5e828262eb243ec0b54681d699a" # always returns a 403 responce 7 | key_429 <- "d6d0f0065f4348a4bdfe4587ba02714b" # always returns a 429 responce 8 | 9 | 10 | # skip if API offline ----------------------------------------------------- 11 | 12 | skip_if_oc_offline <- function(host = "api.opencagedata.com") { 13 | testthat::skip_if_offline(host = host) 14 | } 15 | 16 | 17 | # skip if API key is missing ---------------------------------------------- 18 | 19 | skip_if_no_key <- function() { 20 | testthat::skip_if_not( 21 | condition = oc_key_present(), 22 | message = "OpenCage API key is missing" 23 | ) 24 | } 25 | 26 | # test data --------------------------------------------------------------- 27 | 28 | ## forward ----------------------------------------------------------------- 29 | 30 | oc_locs <- function() c("Brest", "Flensburg", "Los Angeles") 31 | 32 | oc_fw1 <- function() tibble::tibble(id = 1:3, loc = oc_locs()) 33 | 34 | oc_fw2 <- function() { 35 | tibble::add_column( 36 | oc_fw1(), 37 | bounds = oc_bbox( 38 | xmin = c(17, -98, -73), 39 | ymin = c(49, 43, -38), 40 | xmax = c(18, -90, -71), 41 | ymax = c(50, 49, -36) 42 | ), 43 | proximity = oc_points( 44 | latitude = c(49.3, 46, -37), 45 | longitude = c(17.4, -95, -72) 46 | ), 47 | countrycode = c("cz", "us", "cl"), 48 | language = c("de", "fr", "ja"), 49 | limit = 1:3, 50 | annotation = c(FALSE, TRUE, TRUE), 51 | abbrv = c(FALSE, FALSE, TRUE), 52 | address_only = c(TRUE, FALSE, FALSE) 53 | ) 54 | } 55 | 56 | oc_fw3 <- function() { 57 | tibble::tibble( 58 | id = 1:3, 59 | loc = c("Brest", "Elbphilharmonie Hamburg", "Los Angeles City Hall"), 60 | roadinfo = c(FALSE, TRUE, TRUE) 61 | ) 62 | } 63 | 64 | ## reverse ----------------------------------------------------------------- 65 | 66 | oc_lat1 <- function() c(52.087106, 53.55034, 34.05369) 67 | oc_lng1 <- function() c(4.297312, 10.000654, -118.242767) 68 | 69 | oc_rev1 <- function() tibble::tibble(id = 1:3, lat = oc_lat1(), lng = oc_lng1()) 70 | 71 | oc_rev2 <- function() { 72 | tibble::add_column( 73 | oc_rev1(), 74 | language = c("en", "fr", "ja"), 75 | annotation = c(FALSE, TRUE, TRUE), 76 | roadinfo = c(FALSE, TRUE, TRUE), 77 | abbrv = c(FALSE, FALSE, TRUE), 78 | address_only = c(TRUE, TRUE, FALSE) 79 | ) 80 | } 81 | 82 | oc_rev3 <- function() { 83 | tibble::add_row(oc_rev2(), id = 4, lat = 25, lng = 36) 84 | } 85 | -------------------------------------------------------------------------------- /.github/workflows/rhub.yaml: -------------------------------------------------------------------------------- 1 | # R-hub's generic GitHub Actions workflow file. It's canonical location is at 2 | # https://github.com/r-hub/actions/blob/v1/workflows/rhub.yaml 3 | # You can update this file to a newer version using the rhub2 package: 4 | # 5 | # rhub::rhub_setup() 6 | # 7 | # It is unlikely that you need to modify this file manually. 8 | 9 | name: rhub.yaml 10 | run-name: "${{ github.event.inputs.id }}: ${{ github.event.inputs.name || format('Manually run by {0}', github.triggering_actor) }}" 11 | 12 | on: 13 | workflow_dispatch: 14 | inputs: 15 | config: 16 | description: 'A comma separated list of R-hub platforms to use.' 17 | type: string 18 | default: 'linux,windows,macos' 19 | name: 20 | description: 'Run name. You can leave this empty now.' 21 | type: string 22 | id: 23 | description: 'Unique ID. You can leave this empty now.' 24 | type: string 25 | 26 | jobs: 27 | 28 | setup: 29 | runs-on: ubuntu-latest 30 | outputs: 31 | containers: ${{ steps.rhub-setup.outputs.containers }} 32 | platforms: ${{ steps.rhub-setup.outputs.platforms }} 33 | 34 | steps: 35 | # NO NEED TO CHECKOUT HERE 36 | - uses: r-hub/actions/setup@v1 37 | with: 38 | config: ${{ github.event.inputs.config }} 39 | id: rhub-setup 40 | 41 | linux-containers: 42 | needs: setup 43 | if: ${{ needs.setup.outputs.containers != '[]' }} 44 | runs-on: ubuntu-latest 45 | name: ${{ matrix.config.label }} 46 | strategy: 47 | fail-fast: false 48 | matrix: 49 | config: ${{ fromJson(needs.setup.outputs.containers) }} 50 | container: 51 | image: ${{ matrix.config.container }} 52 | 53 | steps: 54 | - uses: r-hub/actions/checkout@v1 55 | - uses: r-hub/actions/platform-info@v1 56 | with: 57 | token: ${{ secrets.RHUB_TOKEN }} 58 | job-config: ${{ matrix.config.job-config }} 59 | - uses: r-hub/actions/setup-deps@v1 60 | with: 61 | token: ${{ secrets.RHUB_TOKEN }} 62 | job-config: ${{ matrix.config.job-config }} 63 | - uses: r-hub/actions/run-check@v1 64 | with: 65 | token: ${{ secrets.RHUB_TOKEN }} 66 | job-config: ${{ matrix.config.job-config }} 67 | 68 | other-platforms: 69 | needs: setup 70 | if: ${{ needs.setup.outputs.platforms != '[]' }} 71 | runs-on: ${{ matrix.config.os }} 72 | name: ${{ matrix.config.label }} 73 | strategy: 74 | fail-fast: false 75 | matrix: 76 | config: ${{ fromJson(needs.setup.outputs.platforms) }} 77 | 78 | steps: 79 | - uses: r-hub/actions/checkout@v1 80 | - uses: r-hub/actions/setup-r@v1 81 | with: 82 | job-config: ${{ matrix.config.job-config }} 83 | token: ${{ secrets.RHUB_TOKEN }} 84 | - uses: r-hub/actions/platform-info@v1 85 | with: 86 | token: ${{ secrets.RHUB_TOKEN }} 87 | job-config: ${{ matrix.config.job-config }} 88 | - uses: r-hub/actions/setup-deps@v1 89 | with: 90 | job-config: ${{ matrix.config.job-config }} 91 | token: ${{ secrets.RHUB_TOKEN }} 92 | - uses: r-hub/actions/run-check@v1 93 | with: 94 | job-config: ${{ matrix.config.job-config }} 95 | token: ${{ secrets.RHUB_TOKEN }} 96 | -------------------------------------------------------------------------------- /tests/testthat/test-oc_bbox.R: -------------------------------------------------------------------------------- 1 | # Test oc_bbox ------------------------------------------------------------ 2 | 3 | test_that("oc_bbox works with numeric", { 4 | bbox1 <- oc_bbox(-5.6, 51.2, 0.2, 51.6) 5 | expect_type(bbox1, "list") 6 | expect_s3_class(bbox1[[1]], "bbox") 7 | expect_identical( 8 | unlist(bbox1), 9 | c(xmin = -5.6, ymin = 51.2, xmax = 0.2, ymax = 51.6) 10 | ) 11 | expect_output( 12 | object = print(bbox1), 13 | regexp = "xmin\\s+ymin\\s+xmax\\s+ymax\\s+\\n-5.6\\s+51.2\\s+0.2\\s+51.6" 14 | ) 15 | }) 16 | 17 | test_that("oc_bbox works with data.frame", { 18 | xdf <- 19 | data.frame( 20 | northeast_lat = c(54.0, 42.73), 21 | northeast_lng = c(10.3, -78.81), 22 | southwest_lat = c(53.3, 42.70), 23 | southwest_lng = c(8.1, -78.86) 24 | ) 25 | 26 | bbox2 <- 27 | oc_bbox( 28 | xdf, 29 | southwest_lng, 30 | southwest_lat, 31 | northeast_lng, 32 | northeast_lat 33 | ) 34 | expect_type(bbox2, "list") 35 | expect_s3_class(bbox2[[1]], "bbox") 36 | expect_identical( 37 | unlist(bbox2[1]), 38 | c(xmin = 8.1, ymin = 53.3, xmax = 10.3, ymax = 54.0) 39 | ) 40 | expect_identical( 41 | unlist(bbox2[2]), 42 | c(xmin = -78.86, ymin = 42.70, xmax = -78.81, ymax = 42.73) 43 | ) 44 | }) 45 | 46 | test_that("oc_bbox works with simple features bbox", { 47 | skip_if_not_installed("sf") 48 | 49 | sfbbox <- 50 | sf::st_bbox( 51 | c(xmin = 16.1, xmax = 16.6, ymax = 48.6, ymin = 47.9), 52 | crs = 4326 53 | ) 54 | ocbbox <- oc_bbox(sfbbox) 55 | 56 | expect_type(ocbbox, "list") 57 | expect_s3_class(ocbbox[[1]], "bbox") 58 | expect_identical( 59 | unlist(ocbbox), 60 | c(xmin = 16.1, ymin = 47.9, xmax = 16.6, ymax = 48.6) 61 | ) 62 | 63 | sfbbox_3857 <- 64 | sf::st_bbox( 65 | c(xmin = 1792244, ymin = 6090234, xmax = 1847904, ymax = 6207260), 66 | crs = 3857 67 | ) 68 | expect_error( 69 | object = oc_bbox(sfbbox_3857), 70 | regexp = "The coordinate reference system of `bbox` must be EPSG 4326." 71 | ) 72 | }) 73 | 74 | test_that("oc_bbox.default gives informative error message", { 75 | expect_error( 76 | object = oc_bbox(TRUE), 77 | regexp = "Can't create a list of bounding boxes", 78 | fixed = TRUE 79 | ) 80 | }) 81 | 82 | # Test checks for oc_bbox ------------------------------------------------- 83 | 84 | test_that("oc_bbox checks bbox", { 85 | expect_error( 86 | oc_bbox(NA_real_, 51.280430, 0.278970, 51.683979), 87 | "Every `bbox` element must be non-missing." 88 | ) 89 | expect_error( 90 | oc_bbox(-0.563160, "51.280430", 0.278970, 51.683979), 91 | "Every `ymin` must be numeric." 92 | ) 93 | expect_error( 94 | oc_bbox(-563160, 51.280430, 0.278970, 51.683979), 95 | "`xmin` must be between -180 and 180." 96 | ) 97 | expect_error( 98 | oc_bbox(-0.563160, 51280430, 0.278970, 51.683979), 99 | "`ymin` must be between -90 and 90." 100 | ) 101 | expect_error( 102 | oc_bbox(-0.563160, 51.280430, 278970, 51.683979), 103 | "`xmax` must be between -180 and 180." 104 | ) 105 | expect_error( 106 | oc_bbox(-0.563160, 51.280430, 0.278970, 51683979), 107 | "`ymax` must be between -90 and 90." 108 | ) 109 | expect_error( 110 | oc_bbox(0.563160, 51.280430, 0.278970, 51.683979), 111 | "`xmin` must always be smaller than `xmax`" 112 | ) 113 | expect_error( 114 | oc_bbox(-0.563160, 53.280430, 0.278970, 51.683979), 115 | "`ymin` must always be smaller than `ymax`" 116 | ) 117 | }) 118 | -------------------------------------------------------------------------------- /tests/testthat/test-oc_config.R: -------------------------------------------------------------------------------- 1 | # Test oc_config() -------------------------------------------------------- 2 | 3 | test_that("oc_config sets OPENCAGE_KEY environment variable", { 4 | # default envvar 5 | withr::local_envvar(c("OPENCAGE_KEY" = key_200)) 6 | oc_config() 7 | expect_identical(Sys.getenv("OPENCAGE_KEY"), key_200) 8 | 9 | # set key directly (or via keyring etc.) 10 | withr::local_envvar(c("OPENCAGE_KEY" = "")) 11 | oc_config(key = key_200) 12 | expect_identical(Sys.getenv("OPENCAGE_KEY"), key_200) 13 | 14 | # override previously set key 15 | withr::local_envvar(c("OPENCAGE_KEY" = key_402)) 16 | oc_config(key = key_200) 17 | expect_identical(Sys.getenv("OPENCAGE_KEY"), key_200) 18 | }) 19 | 20 | test_that("oc_config requests key from terminal", { 21 | skip_if_not_installed("mockery") 22 | rlang::local_interactive(TRUE) 23 | withr::local_envvar(c("OPENCAGE_KEY" = "")) 24 | mockery::stub( 25 | oc_config, 26 | "readline", 27 | key_200 28 | ) 29 | expect_message( 30 | oc_config(key = ""), 31 | "Please enter your OpenCage API key and press enter:" 32 | ) 33 | expect_identical(Sys.getenv("OPENCAGE_KEY"), key_200) 34 | }) 35 | 36 | test_that("oc_config throws error with faulty OpenCage key", { 37 | # unset key 38 | withr::local_envvar(c("OPENCAGE_KEY" = "")) 39 | expect_identical(Sys.getenv("OPENCAGE_KEY"), "") 40 | 41 | # error without key in non-interactive mode 42 | expect_error(oc_config(key = ""), "set the environment variable OPENCAGE_KEY") 43 | 44 | # error with key that is not 32 characters long 45 | expect_error( 46 | oc_config(key = "incomplete_key"), 47 | "(OpenCage API key must be a )*.( string.)" 48 | ) 49 | }) 50 | 51 | # test rate_sec argument -------------------------------------------------- 52 | 53 | test_that("oc_config updates rate limit of oc_get_limit", { 54 | # make sure there is a key present 55 | withr::local_envvar(c("OPENCAGE_KEY" = key_200)) 56 | 57 | rps <- 5L 58 | oc_config(rate_sec = rps) 59 | expect_identical(ratelimitr::get_rates(oc_get_limited)[[1]][["n"]], rps) 60 | 61 | rps <- 3L 62 | oc_config(rate_sec = rps) 63 | expect_identical(ratelimitr::get_rates(oc_get_limited)[[1]][["n"]], rps) 64 | 65 | # set rate_sec back to default: 1L/sec 66 | withr::local_options(oc_rate_sec = NULL) 67 | oc_config() 68 | expect_identical(ratelimitr::get_rates(oc_get_limited)[[1]][["n"]], 1L) 69 | }) 70 | 71 | # test no_record argument ------------------------------------------------- 72 | 73 | test_that("oc_config sets no_record option", { 74 | # make sure there is a key present 75 | withr::local_envvar(c("OPENCAGE_KEY" = key_200)) 76 | 77 | # Default without envvar 78 | withr::local_options(list(oc_no_record = NULL)) 79 | res <- oc_process("Hamburg", return = "url_only") 80 | expect_match(res[[1]], "&no_record=1", fixed = TRUE) 81 | 82 | # Default with oc_config(no_record = TRUE) 83 | oc_config() 84 | expect_true(getOption("oc_no_record")) 85 | res <- oc_process("Hamburg", return = "url_only") 86 | expect_match(res[[1]], "&no_record=1", fixed = TRUE) 87 | 88 | # Set oc_config(no_record = FALSE) 89 | oc_config(no_record = FALSE) 90 | expect_false(getOption("oc_no_record")) 91 | res <- oc_process("Hamburg", return = "url_only") 92 | expect_match(res[[1]], "&no_record=0", fixed = TRUE) 93 | 94 | # Set oc_config(no_record = TRUE) 95 | oc_config(no_record = TRUE) 96 | expect_true(getOption("oc_no_record")) 97 | res <- oc_process("Hamburg", return = "url_only") 98 | expect_match(res[[1]], "&no_record=1", fixed = TRUE) 99 | }) 100 | 101 | # test show_key argument -------------------------------------------------- 102 | 103 | test_that("oc_config sets show_key option", { 104 | # make sure there is a key present 105 | withr::local_envvar(c("OPENCAGE_KEY" = key_200)) 106 | 107 | # Default with oc_config(show_key = FALSE) 108 | oc_config() 109 | expect_false(getOption("oc_show_key")) 110 | 111 | # Set oc_config(show_key = TRUE) 112 | withr::local_envvar(c("OPENCAGE_KEY" = key_200)) 113 | oc_config(show_key = TRUE) 114 | expect_true(getOption("oc_show_key")) 115 | }) 116 | -------------------------------------------------------------------------------- /man/opencage_reverse.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/deprecated.R 3 | \name{opencage_reverse} 4 | \alias{opencage_reverse} 5 | \title{Reverse geocoding} 6 | \usage{ 7 | opencage_reverse( 8 | latitude, 9 | longitude, 10 | key = opencage_key(), 11 | bounds = NULL, 12 | countrycode = NULL, 13 | language = NULL, 14 | limit = 10, 15 | min_confidence = NULL, 16 | no_annotations = FALSE, 17 | no_dedupe = FALSE, 18 | no_record = FALSE, 19 | abbrv = FALSE, 20 | add_request = TRUE 21 | ) 22 | } 23 | \arguments{ 24 | \item{latitude, longitude}{Numeric vectors of latitude and longitude values.} 25 | 26 | \item{key}{Your OpenCage API key as a character vector of length one. By 27 | default, \code{\link[=opencage_key]{opencage_key()}} will attempt to retrieve the key from the 28 | environment variable \code{OPENCAGE_KEY}.} 29 | 30 | \item{bounds}{Bounding box, ignored for reverse geocoding.} 31 | 32 | \item{countrycode}{Country code, ignored for reverse geocoding.} 33 | 34 | \item{language}{An \href{https://en.wikipedia.org/wiki/IETF_language_tag}{IETF BCP 47 language tag} (such as "es" for 35 | Spanish or "pt-BR" for Brazilian Portuguese). OpenCage will attempt to 36 | return results in that language. Alternatively you can specify the "native" 37 | tag, in which case OpenCage will attempt to return the response in the 38 | "official" language(s). In case the \code{language} parameter is set to \code{NULL} 39 | (which is the default), the tag is not recognized, or OpenCage does not 40 | have a record in that language, the results will be returned in English.} 41 | 42 | \item{limit}{How many results should be returned (1-100), ignored for reverse 43 | geocoding.} 44 | 45 | \item{min_confidence}{Numeric vector of integer values between 0 and 10 46 | indicating the precision of the returned result as defined by its 47 | geographical extent, (i.e. by the extent of the result's bounding box). See 48 | the \href{https://opencagedata.com/api#confidence}{API documentation} for 49 | details. Only results with at least the requested confidence will be 50 | returned. Default is \code{NULL}.} 51 | 52 | \item{no_annotations}{Logical vector indicating whether additional 53 | information about the result location should be returned. \code{TRUE} by 54 | default, which means that the results will not contain annotations.} 55 | 56 | \item{no_dedupe}{Logical vector (default \code{FALSE}), when \code{TRUE} the results 57 | will not be deduplicated.} 58 | 59 | \item{no_record}{Logical vector of length one (default \code{FALSE}), when \code{TRUE} 60 | no log entry of the query is created, and the geocoding request is not 61 | cached by OpenCage.} 62 | 63 | \item{abbrv}{Logical vector (default \code{FALSE}), when \code{TRUE} addresses in the 64 | \code{formatted} field of the results are abbreviated (e.g. "Main St." instead 65 | of "Main Street").} 66 | 67 | \item{add_request}{Logical vector (default \code{FALSE}) indicating whether the 68 | request is returned again with the results. If the \code{return} value is a 69 | \code{df_list}, the query text is added as a column to the results. \code{json_list} 70 | results will contain all request parameters, including the API key used! 71 | This is currently ignored by OpenCage if return value is \code{geojson_list}.} 72 | } 73 | \value{ 74 | A list with 75 | \itemize{ 76 | \item results as a tibble with one line per result, 77 | \item the number of results as an integer, 78 | \item the timestamp as a POSIXct object, 79 | \item rate_info tibble/data.frame with the maximal number of API calls per 80 | day for the used key, the number of remaining calls for the day and the time 81 | at which the number of remaining calls will be reset. 82 | } 83 | } 84 | \description{ 85 | \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} 86 | 87 | Deprecated: use \code{oc_reverse} or \code{oc_reverse_df} for reverse geocoding. 88 | } 89 | \examples{ 90 | \dontshow{if (oc_key_present() && oc_api_ok()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 91 | 92 | opencage_reverse( 93 | latitude = 0, longitude = 0, 94 | limit = 2 95 | ) 96 | \dontshow{\}) # examplesIf} 97 | } 98 | -------------------------------------------------------------------------------- /tests/testthat/test-deprecated.R: -------------------------------------------------------------------------------- 1 | # Test deprecated opencage_forward ---------------------------------------- 2 | 3 | test_that("opencage_forward/opencage_reverse return what they should.", { 4 | lifecycle::expect_deprecated( 5 | results <- opencage_forward(placename = "Sarzeau", key = key_200) 6 | ) 7 | expect_type(results, "list") 8 | expect_s3_class(results[["results"]], "tbl_df") 9 | expect_type(results[["total_results"]], "integer") 10 | expect_s3_class(results[["time_stamp"]], "POSIXct") 11 | expect_length(results, 4L) 12 | 13 | lifecycle::expect_deprecated( 14 | results <- opencage_reverse(longitude = 0, latitude = 0, key = key_200) 15 | ) 16 | expect_type(results, "list") 17 | expect_s3_class(results[["results"]], "tbl_df") 18 | expect_type(results[["total_results"]], "integer") 19 | expect_s3_class(results[["time_stamp"]], "POSIXct") 20 | expect_length(results, 4L) 21 | }) 22 | 23 | test_that("opencage_forward/opencage_reverse return what they should 24 | with several parameters.", { 25 | withr::local_envvar(c("OPENCAGE_KEY" = key_200)) 26 | withr::local_options(lifecycle_verbosity = "quiet") 27 | 28 | results <- opencage_forward( 29 | placename = "Paris", 30 | limit = 2, 31 | language = "fr", 32 | no_annotations = TRUE 33 | ) 34 | 35 | expect_type(results, "list") 36 | expect_s3_class(results[["results"]], "tbl_df") 37 | expect_type(results[["total_results"]], "integer") 38 | expect_s3_class(results[["time_stamp"]], "POSIXct") 39 | expect_length(results, 4L) 40 | expect_no_match(colnames(results[["results"]]), "annotation", fixed = TRUE) 41 | expect_true(dplyr::between(nrow(results[["results"]]), 1, 2)) 42 | 43 | results <- opencage_reverse( 44 | latitude = 44, 45 | longitude = 44, 46 | language = "pt-BR", 47 | no_annotations = TRUE 48 | ) 49 | 50 | expect_type(results, "list") 51 | expect_s3_class(results[["results"]], "tbl_df") 52 | expect_type(results[["total_results"]], "integer") 53 | expect_s3_class(results[["time_stamp"]], "POSIXct") 54 | expect_length(results, 4L) 55 | expect_no_match(colnames(results[["results"]]), "annotation", fixed = TRUE) 56 | }) 57 | 58 | test_that("opencage_forward deals well with results being NULL", { 59 | skip_if_no_key() 60 | skip_if_oc_offline() 61 | withr::local_options(lifecycle_verbosity = "quiet") 62 | 63 | # the query NOWHERE-INTERESTING will return a valid response with 0 results 64 | # https://opencagedata.com/api#no-results 65 | results <- opencage_forward( 66 | placename = "NOWHERE-INTERESTING", 67 | key = Sys.getenv("OPENCAGE_KEY"), 68 | limit = 2, 69 | language = "pt-BR", 70 | no_annotations = TRUE 71 | ) 72 | 73 | expect_type(results, "list") 74 | expect_null(results[["results"]]) 75 | expect_type(results[["total_results"]], "integer") 76 | expect_s3_class(results[["time_stamp"]], "POSIXct") 77 | }) 78 | 79 | test_that("the bounds argument is well taken into account", { 80 | skip_if_no_key() 81 | skip_if_oc_offline() 82 | withr::local_options(lifecycle_verbosity = "quiet") 83 | 84 | results1 <- opencage_forward( 85 | placename = "Berlin", 86 | key = Sys.getenv("OPENCAGE_KEY") 87 | ) 88 | expect_true("Germany" %in% results1$results$components.country) 89 | 90 | results2 <- opencage_forward( 91 | placename = "Berlin", 92 | bounds = c(-90, 38, 0, 45), 93 | key = Sys.getenv("OPENCAGE_KEY") 94 | ) 95 | expect_false("Germany" %in% results2$results$components.country) 96 | }) 97 | 98 | test_that("Errors with multiple inputs", { 99 | withr::local_envvar(c("OPENCAGE_KEY" = key_200)) 100 | 101 | expect_error( 102 | opencage_forward(c("Hamburg", "Los Angeles")), 103 | "`opencage_forward` is not vectorised; use `oc_forward` instead." 104 | ) 105 | expect_error( 106 | opencage_reverse(c(5, 20), c(6, 21)), 107 | "`opencage_reverse` is not vectorised, use `oc_reverse` instead." 108 | ) 109 | }) 110 | 111 | 112 | # Test opencage_key ------------------------------------------------------- 113 | 114 | test_that("`opencage_key()` returns NULL if OPENCAGE_KEY envvar not found", { 115 | withr::local_envvar(c("OPENCAGE_KEY" = "")) 116 | withr::local_options(lifecycle_verbosity = "quiet") 117 | 118 | expect_null(opencage_key()) 119 | }) 120 | 121 | test_that("`opencage_key(quiet = FALSE)` messages", { 122 | withr::local_envvar(c("OPENCAGE_KEY" = "fakekey")) 123 | withr::local_options(lifecycle_verbosity = "quiet") 124 | 125 | expect_snapshot(x = opencage_key(quiet = FALSE)) 126 | }) 127 | -------------------------------------------------------------------------------- /R/oc_bbox.R: -------------------------------------------------------------------------------- 1 | #' List of bounding boxes for OpenCage queries 2 | #' 3 | #' Create a list of bounding boxes for OpenCage queries. 4 | #' 5 | #' @param xmin Minimum longitude (also known as `min_lon`, `southwest_lng`, 6 | #' `west`, or `left`). 7 | #' @param ymin Minimum latitude (also known as `min_lat`, `southwest_lat`, 8 | #' `south`, or `bottom`). 9 | #' @param xmax Maximum longitude (also known as `max_lon`, `northeast_lng`, 10 | #' `east`, or `right`). 11 | #' @param ymax Maximum latitude (also known as `max_lat`, `northeast_lat`, 12 | #' `north`, or `top`). 13 | #' @param data A `data.frame` containing at least 4 columns with `xmin`, `ymin`, 14 | #' `xmax`, and `ymax` values, respectively. 15 | #' @param bbox A `bbox` object, see `sf::st_bbox`. 16 | #' @param ... Ignored. 17 | #' 18 | #' @return A list of bounding boxes, each of class `bbox`. 19 | #' @export 20 | #' 21 | #' @examples 22 | #' oc_bbox(-5.63160, 51.280430, 0.278970, 51.683979) 23 | #' 24 | #' xdf <- 25 | #' data.frame( 26 | #' place = c("Hamburg", "Hamburg"), 27 | #' northeast_lat = c(54.0276817, 42.7397729), 28 | #' northeast_lng = c(10.3252805, -78.812825), 29 | #' southwest_lat = c(53.3951118, 42.7091669), 30 | #' southwest_lng = c(8.1053284, -78.860521) 31 | #' ) 32 | #' oc_bbox( 33 | #' xdf, 34 | #' southwest_lng, 35 | #' southwest_lat, 36 | #' northeast_lng, 37 | #' northeast_lat 38 | #' ) 39 | #' 40 | #' # create bbox list column with dplyr 41 | #' library(dplyr) 42 | #' xdf %>% 43 | #' mutate( 44 | #' bbox = 45 | #' oc_bbox( 46 | #' southwest_lng, 47 | #' southwest_lat, 48 | #' northeast_lng, 49 | #' northeast_lat 50 | #' ) 51 | #' ) 52 | #' 53 | #' # create bbox list from a simple features bbox 54 | #' if (requireNamespace("sf", quietly = TRUE)) { 55 | #' library(sf) 56 | #' bbox <- st_bbox(c(xmin = 16.1, xmax = 16.6, ymax = 48.6, ymin = 47.9), 57 | #' crs = 4326 58 | #' ) 59 | #' oc_bbox(bbox) 60 | #' } 61 | #' 62 | oc_bbox <- function(...) UseMethod("oc_bbox") 63 | 64 | # No @name so it does not show up in the docs. 65 | #' @export 66 | oc_bbox.default <- function(x, ...) { 67 | stop( 68 | "Can't create a list of bounding boxes from an object of class `", 69 | class(x)[[1]], 70 | "`.", 71 | call. = FALSE 72 | ) 73 | } 74 | 75 | #' @name oc_bbox 76 | #' @export 77 | oc_bbox.numeric <- function(xmin, ymin, xmax, ymax, ...) { 78 | bbox <- function(xmin, ymin, xmax, ymax) { 79 | oc_check_bbox(xmin = xmin, ymin = ymin, xmax = xmax, ymax = ymax) 80 | structure( 81 | c(xmin = xmin, ymin = ymin, xmax = xmax, ymax = ymax), 82 | crs = 83 | structure( 84 | list( 85 | epsg = 4326L, 86 | proj4string = "+proj=longlat +datum=WGS84 +no_defs" 87 | ), 88 | class = "crs" 89 | ), 90 | class = "bbox" 91 | ) 92 | } 93 | purrr::pmap(list(xmin, ymin, xmax, ymax), bbox) 94 | } 95 | 96 | #' @name oc_bbox 97 | #' @export 98 | oc_bbox.data.frame <- function(data, xmin, ymin, xmax, ymax, ...) { 99 | xmin <- rlang::enquo(xmin) 100 | ymin <- rlang::enquo(ymin) 101 | xmax <- rlang::enquo(xmax) 102 | ymax <- rlang::enquo(ymax) 103 | 104 | oc_bbox( 105 | xmin = rlang::eval_tidy(xmin, data = data), 106 | ymin = rlang::eval_tidy(ymin, data = data), 107 | xmax = rlang::eval_tidy(xmax, data = data), 108 | ymax = rlang::eval_tidy(ymax, data = data) 109 | ) 110 | } 111 | 112 | #' @name oc_bbox 113 | #' @export 114 | oc_bbox.bbox <- function(bbox, ...) { 115 | # check coordinate reference system (and be lenient if NA_crs_) 116 | crs <- attr(bbox, "crs")[["input"]] 117 | if (!is.na(crs) && !identical(crs, "EPSG:4326")) { 118 | stop( 119 | call. = FALSE, 120 | "The coordinate reference system of `bbox` must be EPSG 4326." 121 | ) 122 | } 123 | oc_check_bbox(bbox[[1]], bbox[[2]], bbox[[3]], bbox[[4]]) 124 | list(bbox) 125 | } 126 | 127 | #' Check whether a bounding box has valid longitude and latitude values 128 | #' 129 | #' @param xmin numeric minimum longitude 130 | #' @param ymin numeric minimum latitude 131 | #' @param xmax numeric maximum longitude 132 | #' @param ymax numeric maximum latitude 133 | #' 134 | #' @noRd 135 | 136 | oc_check_bbox <- function(xmin, ymin, xmax, ymax) { 137 | if (anyNA(c(xmin, ymin, xmax, ymax))) { 138 | stop("Every `bbox` element must be non-missing.", call. = FALSE) 139 | } 140 | 141 | oc_check_between(xmin, -180, 180) 142 | oc_check_between(xmax, -180, 180) 143 | oc_check_between(ymin, -90, 90) 144 | oc_check_between(ymax, -90, 90) 145 | 146 | if (xmin > xmax) { 147 | stop("`xmin` must always be smaller than `xmax`", call. = FALSE) 148 | } 149 | if (ymin > ymax) { 150 | stop("`ymin` must always be smaller than `ymax`", call. = FALSE) 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /vignettes/output_options.Rmd.src: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Output options" 3 | subtitle: "Get different kinds of output from OpenCage" 4 | author: "Daniel Possenriede, Jesse Sadler, Maëlle Salmon" 5 | date: "`r Sys.Date()`" 6 | description: > 7 | "`oc_forward()`/`oc_reverse()` return lists of various type, namely data frames, JSON, GeoJSON or URLs, depending on the `return` value you specify. The possible `return` values are `df_list`, `json_list`, `geojson_list` and `url_only`." 8 | output: 9 | rmarkdown::html_vignette: 10 | df_print: kable 11 | vignette: > 12 | %\VignetteIndexEntry{Output options} 13 | %\VignetteEngine{knitr::rmarkdown} 14 | %\VignetteEncoding{UTF-8} 15 | --- 16 | 17 | ```{r setup, echo=FALSE} 18 | knitr::opts_chunk$set( 19 | collapse = TRUE, 20 | comment = "#>" 21 | ) 22 | 23 | library("opencage") 24 | 25 | ``` 26 | 27 | {opencage} contains two main types of main functions distinguished by the manner in which the results are returned: 28 | 29 | 1. The `oc_forward_df()`/`oc_reverse_df()` functions always return a single tibble. 30 | 2. The `oc_forward()`/`oc_reverse()` functions return a list of various types, depending on the `return` value you specify. 31 | The possible `return` values are `df_list`, `json_list`, `geojson_list` and `url_only`. 32 | 33 | Use of the `oc_forward_df()`/`oc_reverse_df()` functions is demonstrated in the "Introduction to opencage" and the "Customise your query" vignettes (`vignette("opencage")` and `vignette("customise_query")`, respectively) . 34 | The function arguments mentioned in these other vignettes are also generally available with `oc_forward()`/`oc_reverse()`. 35 | Here we will show the different return values available with `oc_forward()`/`oc_reverse()`. 36 | 37 | ## `df_list` 38 | 39 | The default return value is `df_list`. 40 | It returns a list of tibbles. 41 | 42 | ```{r df_list} 43 | stations <- c("Casey Station", "McMurdo Station") 44 | oc_forward(stations, return = "df_list") 45 | ``` 46 | 47 | The `df_list` type drives the `oc_forward_df()`/`oc_reverse_df()` functions. 48 | You can use the `df_list` output in a `dplyr::mutate()` chain to replicate the functionality of `oc_forward_df()`: 49 | 50 | ```{r mutate} 51 | library(dplyr, warn.conflicts = FALSE) 52 | 53 | oc_data <- 54 | tibble(place = stations) %>% 55 | mutate(oc_result = oc_forward(place)) 56 | 57 | oc_data 58 | ``` 59 | 60 | This creates a list column `oc_result`, which can be easily unnested with `tidyr::unnest()`: 61 | 62 | ```{r unnest} 63 | library(tidyr, warn.conflicts = FALSE) 64 | 65 | oc_data %>% unnest(oc_result) 66 | ``` 67 | 68 | ## `json_list` 69 | 70 | OpenCage's main output format is JSON. When you specify `json_list` as the return type, you get the JSON as an R `list()`. 71 | 72 | ```{r json_list} 73 | oc_forward("Casey Station", return = "json_list") 74 | ``` 75 | 76 | ## `geojson_list` 77 | 78 | When you choose `geojson_list` as the return type, the geocoder response will be returned as GeoJSON specified as an R `list()`. 79 | 80 | ```{r geojson_list} 81 | gjsn_lst <- oc_forward("Casey Station", return = "geojson_list") 82 | gjsn_lst 83 | ``` 84 | 85 | In fact, {opencage} returns a list of results in `geo_list` format, which should be compatible with the {[geojsonio](https://docs.ropensci.org/geojsonio/)} package. 86 | 87 | ```{r geo_list} 88 | class(gjsn_lst[[1]]) 89 | ``` 90 | 91 | ## `url_only` 92 | 93 | `url_only` returns the OpenCage URL for debugging purposes. 94 | 95 | ```{r url_only} 96 | oc_forward("Casey Station", return = "url_only") 97 | ``` 98 | 99 | Your OpenCage API key is masked with the `OPENCAGE_KEY` string, by default. 100 | If you really want {opencage} to display your API key with the URL, set the `show_key` argument in `oc_config()` to `TRUE`. 101 | 102 | ```{r show_key, include=TRUE, eval=FALSE} 103 | oc_config(show_key = TRUE) 104 | ``` 105 | 106 | Note that the API key will only be returned with the URL in `base::interactive()` mode. 107 | 108 | ## `xml` 109 | 110 | {opencage} does not support the XML response type at the moment. 111 | Please file an [issue](https://github.com/ropensci/opencage/issues) or a [pull-request](https://github.com/ropensci/opencage/pulls) if you have a use-case that requires this. 112 | 113 | ## Return query text 114 | 115 | `oc_forward()` and `oc_reverse()` have an `add_request` argument, indicating whether the request is returned again with the results. 116 | If the `return` value is a `df_list`, the `placename` or `latitude,longitude` is added as a column to the results without a roundtrip to the API. 117 | `json_list` results will contain all request parameters as returned by the API. 118 | This would normally include your OpenCage API key, but {opencage} masks the key and replaces it with the `OPENCAGE_KEY` string in the output. 119 | `add_request` is currently ignored by OpenCage for GeoJSON results. 120 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: 3 | github_document: 4 | df_print: kable 5 | --- 6 | 7 | 8 | 9 | # opencage 10 | 11 | 12 | 13 | [![CRAN Version](https://www.r-pkg.org/badges/version/opencage)](https://cran.r-project.org/package=opencage) 14 | [![CRAN Checks Status](https://badges.cranchecks.info/worst/opencage.svg)](https://cran.r-project.org/web/checks/check_results_opencage.html) 15 | [![CRAN Downloads per Month](https://cranlogs.r-pkg.org/badges/opencage)](https://cran.r-project.org/package=opencage) 16 | [![R-universe status](https://ropensci.r-universe.dev/badges/opencage)](https://ropensci.r-universe.dev/ui#package:opencage) 17 | [![Project Status: Active – The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active) 18 | [![R-CMD-check Status on GitHub Actions](https://github.com/ropensci/opencage/workflows/R-CMD-check/badge.svg)](https://github.com/ropensci/opencage/actions?query=workflow%3AR-CMD-check) 19 | [![codecov.io Status](https://codecov.io/github/ropensci/opencage/coverage.svg?branch=main)](https://codecov.io/github/ropensci/opencage?branch=main) 20 | [![rOpenSci Peer-Review](https://badges.ropensci.org/36_status.svg)](https://github.com/ropensci/software-review/issues/36) 21 | [![License](https://img.shields.io/cran/l/opencage)](https://opensource.org/licenses/gpl-license) 22 | 23 | 24 | 25 | Geocode with the [OpenCage](https://opencagedata.com) API, either from place name to longitude and latitude (forward geocoding) or from longitude and latitude to the name and address of the location (reverse geocoding). 26 | 27 | ## Installation 28 | 29 | You can install {opencage} from [CRAN](https://cran.r-project.org/package=opencage), [R-universe](https://ropensci.r-universe.dev/ui#package:opencage) or [GitHub](https://github.com/ropensci/opencage) like this: 30 | 31 | - Release version from CRAN 32 | 33 | ```r 34 | install.packages("opencage") 35 | ``` 36 | 37 | - Development version from R-universe 38 | 39 | ```r 40 | install.packages( 41 | "opencage", 42 | repos = c("https://ropensci.r-universe.dev", getOption("repos")) 43 | ) 44 | ``` 45 | 46 | - Development version from GitHub with {[pak](https://github.com/r-lib/pak/)} 47 | 48 | ```r 49 | pak::pak("ropensci/opencage") 50 | ``` 51 | 52 | or with {[remotes](https://github.com/r-lib/remotes/)} 53 | 54 | ```r 55 | remotes::install_github("ropensci/opencage") 56 | ``` 57 | 58 | ## Quickstart 59 | 60 | For the best experience, we recommend that you read through the "[Introduction to opencage](https://docs.ropensci.org/opencage/articles/opencage.html)" vignette (`vignette("opencage")`), but if you are in a hurry: 61 | 62 | 1. Register at [opencagedata.com/users/sign_up](https://opencagedata.com/users/sign_up). 63 | 2. Generate an API key at the [OpenCage dashboard](https://opencagedata.com/dashboard#api-keys). 64 | 3. Save your API key as an [environment variable](https://rstats.wtf/r-startup.html#renviron) like `OPENCAGE_KEY=yourkey` in `.Renviron`. 65 | See `help(oc_config)` for alternative ways to set your OpenCage API key. 66 | 67 | Now you are ready to turn place names into latitude and longitude coordinates: 68 | 69 | ```{r forward} 70 | library(opencage) 71 | oc_forward_df(placename = "Sarzeau") 72 | ``` 73 | 74 | Or turn a set of coordinates into the name and address of the location: 75 | 76 | ```{r reverse} 77 | oc_reverse_df(latitude = 51.5034070, longitude = -0.1275920) 78 | ``` 79 | 80 | But remember, the vignettes are really great! We have: 81 | 82 | - "[Introduction to opencage](https://docs.ropensci.org/opencage/articles/opencage.html)" `vignette("opencage")` 83 | - "[Customise your query](https://docs.ropensci.org/opencage/articles/customise_query.html)" `vignette("customise_query")` 84 | - "[Output options](https://docs.ropensci.org/opencage/articles/output_options.html)" `vignette("output_options")` 85 | 86 | ## About OpenCage 87 | 88 | The [OpenCage](https://opencagedata.com) API supports forward and reverse geocoding. 89 | Sources of OpenCage are open geospatial data including 90 | [OpenStreetMap](https://www.openstreetmap.org), 91 | [DataScienceToolkit](https://github.com/petewarden/dstk), 92 | [GeoPlanet](https://en.wikipedia.org/wiki/GeoPlanet), 93 | [Natural Earth Data](https://www.naturalearthdata.com), 94 | [libpostal](https://github.com/openvenues/libpostal), 95 | [GeoNames](https://www.geonames.org), and 96 | [Flickr's shapefiles](https://code.flickr.net/2009/05/21/flickr-shapefiles-public-dataset-10/) 97 | plus a whole lot more besides. 98 | Refer to the current full [list of credits](https://opencagedata.com/credits). 99 | 100 | ## Code of Conduct 101 | 102 | Please note that this package is released with a [Contributor Code of Conduct](https://ropensci.org/code-of-conduct/). 103 | By contributing to this project, you agree to abide by its terms. 104 | -------------------------------------------------------------------------------- /man/oc_reverse.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/oc_reverse.R 3 | \name{oc_reverse} 4 | \alias{oc_reverse} 5 | \title{Reverse geocoding} 6 | \usage{ 7 | oc_reverse( 8 | latitude, 9 | longitude, 10 | return = c("df_list", "json_list", "geojson_list", "url_only"), 11 | language = NULL, 12 | min_confidence = NULL, 13 | no_annotations = TRUE, 14 | roadinfo = FALSE, 15 | no_dedupe = FALSE, 16 | abbrv = FALSE, 17 | address_only = FALSE, 18 | add_request = FALSE, 19 | ... 20 | ) 21 | } 22 | \arguments{ 23 | \item{latitude, longitude}{Numeric vectors of latitude and longitude values.} 24 | 25 | \item{return}{A character vector of length one indicating the return value of 26 | the function, either a list of tibbles (\code{df_list}, the default), a JSON 27 | list (\code{json_list}), a GeoJSON list (\code{geojson_list}), or the URL with which 28 | the API would be called (\code{url_only}).} 29 | 30 | \item{language}{An \href{https://en.wikipedia.org/wiki/IETF_language_tag}{IETF BCP 47 language tag} (such as "es" for 31 | Spanish or "pt-BR" for Brazilian Portuguese). OpenCage will attempt to 32 | return results in that language. Alternatively you can specify the "native" 33 | tag, in which case OpenCage will attempt to return the response in the 34 | "official" language(s). In case the \code{language} parameter is set to \code{NULL} 35 | (which is the default), the tag is not recognized, or OpenCage does not 36 | have a record in that language, the results will be returned in English.} 37 | 38 | \item{min_confidence}{Numeric vector of integer values between 0 and 10 39 | indicating the precision of the returned result as defined by its 40 | geographical extent, (i.e. by the extent of the result's bounding box). See 41 | the \href{https://opencagedata.com/api#confidence}{API documentation} for 42 | details. Only results with at least the requested confidence will be 43 | returned. Default is \code{NULL}.} 44 | 45 | \item{no_annotations}{Logical vector indicating whether additional 46 | information about the result location should be returned. \code{TRUE} by 47 | default, which means that the results will not contain annotations.} 48 | 49 | \item{roadinfo}{Logical vector indicating whether the geocoder should attempt 50 | to match the nearest road (rather than an address) and provide additional 51 | road and driving information. Default is \code{FALSE}.} 52 | 53 | \item{no_dedupe}{Logical vector (default \code{FALSE}), when \code{TRUE} the results 54 | will not be deduplicated.} 55 | 56 | \item{abbrv}{Logical vector (default \code{FALSE}), when \code{TRUE} addresses in the 57 | \code{formatted} field of the results are abbreviated (e.g. "Main St." instead 58 | of "Main Street").} 59 | 60 | \item{address_only}{Logical vector (default \code{FALSE}), when \code{TRUE} only the 61 | address details are returned in the \code{formatted} field of the results, not 62 | the name of a point-of-interest should there be one at this address.} 63 | 64 | \item{add_request}{Logical vector (default \code{FALSE}) indicating whether the 65 | request is returned again with the results. If the \code{return} value is a 66 | \code{df_list}, the query text is added as a column to the results. \code{json_list} 67 | results will contain all request parameters, including the API key used! 68 | This is currently ignored by OpenCage if return value is \code{geojson_list}.} 69 | 70 | \item{...}{Ignored.} 71 | } 72 | \value{ 73 | Depending on the \code{return} argument, \code{oc_reverse} returns a list with 74 | either 75 | \itemize{ 76 | \item the results as tibbles (\code{"df_list"}, the default), 77 | \item the results as JSON specified as a list (\code{"json_list"}), 78 | \item the results as GeoJSON specified as a list (\code{"geojson_list"}), 79 | or 80 | \item the URL of the OpenCage API call for debugging purposes 81 | (\code{"url_only"}). 82 | } 83 | 84 | When the results are returned as (a list of) tibbles, the column names 85 | coming from the OpenCage API are prefixed with \code{"oc_"}. 86 | } 87 | \description{ 88 | Reverse geocoding from numeric vectors of latitude and longitude pairs to 89 | the names and addresses of a location. 90 | } 91 | \examples{ 92 | \dontshow{if (oc_key_present() && oc_api_ok()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 93 | 94 | # Reverse geocode a single location 95 | oc_reverse(latitude = -36.85007, longitude = 174.7706) 96 | 97 | # Reverse geocode multiple locations 98 | lat <- c(47.21864, 53.55034, 34.05369) 99 | lng <- c(-1.554136, 10.000654, -118.242767) 100 | 101 | oc_reverse(latitude = lat, longitude = lng) 102 | 103 | # Return results in a preferred language if possible 104 | oc_reverse( 105 | latitude = lat, longitude = lng, 106 | language = "fr" 107 | ) 108 | 109 | # Return results as a json list 110 | oc_reverse( 111 | latitude = lat, longitude = lng, 112 | return = "json_list" 113 | ) 114 | \dontshow{\}) # examplesIf} 115 | } 116 | \seealso{ 117 | \code{\link[=oc_reverse_df]{oc_reverse_df()}} for inputs as a data frame, or \code{\link[=oc_forward]{oc_forward()}} and 118 | \code{\link[=oc_forward]{oc_forward()}} for forward geocoding. For more information about the API 119 | and the various parameters, see the \href{https://opencagedata.com/api}{OpenCage API documentation}. 120 | } 121 | -------------------------------------------------------------------------------- /man/opencage_forward.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/deprecated.R 3 | \name{opencage_forward} 4 | \alias{opencage_forward} 5 | \title{Forward geocoding} 6 | \usage{ 7 | opencage_forward( 8 | placename, 9 | key = opencage_key(), 10 | bounds = NULL, 11 | countrycode = NULL, 12 | language = NULL, 13 | limit = 10L, 14 | min_confidence = NULL, 15 | no_annotations = FALSE, 16 | no_dedupe = FALSE, 17 | no_record = FALSE, 18 | abbrv = FALSE, 19 | add_request = TRUE 20 | ) 21 | } 22 | \arguments{ 23 | \item{placename}{A character vector with the location names or addresses to 24 | be geocoded. 25 | 26 | If the locations are addresses, see \href{https://github.com/OpenCageData/opencagedata-misc-docs/blob/master/query-formatting.md}{OpenCage's instructions} 27 | on how to format addresses for best forward geocoding results.} 28 | 29 | \item{key}{Your OpenCage API key as a character vector of length one. By 30 | default, \code{\link[=opencage_key]{opencage_key()}} will attempt to retrieve the key from the 31 | environment variable \code{OPENCAGE_KEY}.} 32 | 33 | \item{bounds}{A list of bounding boxes of length one or \code{length(placename)}. 34 | Bounding boxes are named numeric vectors, each with four coordinates 35 | forming the south-west and north-east corners of the bounding box: 36 | \code{list(c(xmin, ymin, xmax, ymax))}. \code{bounds} restricts the possible results 37 | to the supplied region. It can be specified with the \code{\link[=oc_bbox]{oc_bbox()}} helper. 38 | For example: \code{bounds = oc_bbox(-0.563160, 51.280430, 0.278970, 51.683979)}. 39 | Default is \code{NULL}.} 40 | 41 | \item{countrycode}{A two letter code as defined by the \href{https://www.iso.org/obp/ui/#search/code}{ISO 3166-1 Alpha 2} standard that restricts the 42 | results to the given country or countries. E.g. "AR" for Argentina, "FR" 43 | for France, "NZ" for the New Zealand. Multiple countrycodes per \code{placename} 44 | must be wrapped in a list. Default is \code{NULL}.} 45 | 46 | \item{language}{An \href{https://en.wikipedia.org/wiki/IETF_language_tag}{IETF BCP 47 language tag} (such as "es" for 47 | Spanish or "pt-BR" for Brazilian Portuguese). OpenCage will attempt to 48 | return results in that language. Alternatively you can specify the "native" 49 | tag, in which case OpenCage will attempt to return the response in the 50 | "official" language(s). In case the \code{language} parameter is set to \code{NULL} 51 | (which is the default), the tag is not recognized, or OpenCage does not 52 | have a record in that language, the results will be returned in English.} 53 | 54 | \item{limit}{Numeric vector of integer values to determine the maximum number 55 | of results returned for each \code{placename}. Integer values between 1 and 100 56 | are allowed. Default is 10.} 57 | 58 | \item{min_confidence}{Numeric vector of integer values between 0 and 10 59 | indicating the precision of the returned result as defined by its 60 | geographical extent, (i.e. by the extent of the result's bounding box). See 61 | the \href{https://opencagedata.com/api#confidence}{API documentation} for 62 | details. Only results with at least the requested confidence will be 63 | returned. Default is \code{NULL}.} 64 | 65 | \item{no_annotations}{Logical vector indicating whether additional 66 | information about the result location should be returned. \code{TRUE} by 67 | default, which means that the results will not contain annotations.} 68 | 69 | \item{no_dedupe}{Logical vector (default \code{FALSE}), when \code{TRUE} the results 70 | will not be deduplicated.} 71 | 72 | \item{no_record}{Logical vector of length one (default \code{FALSE}), when \code{TRUE} 73 | no log entry of the query is created, and the geocoding request is not 74 | cached by OpenCage.} 75 | 76 | \item{abbrv}{Logical vector (default \code{FALSE}), when \code{TRUE} addresses in the 77 | \code{formatted} field of the results are abbreviated (e.g. "Main St." instead 78 | of "Main Street").} 79 | 80 | \item{add_request}{Logical vector (default \code{FALSE}) indicating whether the 81 | request is returned again with the results. If the \code{return} value is a 82 | \code{df_list}, the query text is added as a column to the results. \code{json_list} 83 | results will contain all request parameters, including the API key used! 84 | This is currently ignored by OpenCage if return value is \code{geojson_list}.} 85 | } 86 | \value{ 87 | A list with 88 | \itemize{ 89 | \item results as a tibble with one line per result, 90 | \item the number of results as an integer, 91 | \item the timestamp as a POSIXct object, 92 | \item rate_info tibble/data.frame with the maximal number of API calls per 93 | day for the used key, the number of remaining calls for the day and the time 94 | at which the number of remaining calls will be reset. 95 | } 96 | } 97 | \description{ 98 | \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} 99 | 100 | Deprecated: use \code{oc_forward} or \code{oc_forward_df} for forward geocoding. 101 | } 102 | \examples{ 103 | \dontshow{if (oc_key_present() && oc_api_ok()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 104 | opencage_forward(placename = "Sarzeau") 105 | opencage_forward(placename = "Islington, London") 106 | opencage_forward(placename = "Triererstr 15, 107 | Weimar 99423, 108 | Deutschland") 109 | \dontshow{\}) # examplesIf} 110 | } 111 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # opencage 5 | 6 | 7 | 8 | [![CRAN 9 | Version](https://www.r-pkg.org/badges/version/opencage)](https://cran.r-project.org/package=opencage) 10 | [![CRAN Checks 11 | Status](https://badges.cranchecks.info/worst/opencage.svg)](https://cran.r-project.org/web/checks/check_results_opencage.html) 12 | [![CRAN Downloads per 13 | Month](https://cranlogs.r-pkg.org/badges/opencage)](https://cran.r-project.org/package=opencage) 14 | [![R-universe 15 | status](https://ropensci.r-universe.dev/badges/opencage)](https://ropensci.r-universe.dev/ui#package:opencage) 16 | [![Project Status: Active – The project has reached a stable, usable 17 | state and is being actively 18 | developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active) 19 | [![R-CMD-check Status on GitHub 20 | Actions](https://github.com/ropensci/opencage/workflows/R-CMD-check/badge.svg)](https://github.com/ropensci/opencage/actions?query=workflow%3AR-CMD-check) 21 | [![codecov.io 22 | Status](https://codecov.io/github/ropensci/opencage/coverage.svg?branch=main)](https://codecov.io/github/ropensci/opencage?branch=main) 23 | [![rOpenSci 24 | Peer-Review](https://badges.ropensci.org/36_status.svg)](https://github.com/ropensci/software-review/issues/36) 25 | [![License](https://img.shields.io/cran/l/opencage)](https://opensource.org/licenses/gpl-license) 26 | 27 | 28 | 29 | Geocode with the [OpenCage](https://opencagedata.com) API, either from 30 | place name to longitude and latitude (forward geocoding) or from 31 | longitude and latitude to the name and address of the location (reverse 32 | geocoding). 33 | 34 | ## Installation 35 | 36 | You can install {opencage} from 37 | [CRAN](https://cran.r-project.org/package=opencage), 38 | [R-universe](https://ropensci.r-universe.dev/ui#package:opencage) or 39 | [GitHub](https://github.com/ropensci/opencage) like this: 40 | 41 | - Release version from CRAN 42 | 43 | ``` r 44 | install.packages("opencage") 45 | ``` 46 | 47 | - Development version from R-universe 48 | 49 | ``` r 50 | install.packages( 51 | "opencage", 52 | repos = c("https://ropensci.r-universe.dev", getOption("repos")) 53 | ) 54 | ``` 55 | 56 | - Development version from GitHub with 57 | {[pak](https://github.com/r-lib/pak/)} 58 | 59 | ``` r 60 | pak::pak("ropensci/opencage") 61 | ``` 62 | 63 | or with {[remotes](https://github.com/r-lib/remotes/)} 64 | 65 | ``` r 66 | remotes::install_github("ropensci/opencage") 67 | ``` 68 | 69 | ## Quickstart 70 | 71 | For the best experience, we recommend that you read through the 72 | “[Introduction to 73 | opencage](https://docs.ropensci.org/opencage/articles/opencage.html)” 74 | vignette (`vignette("opencage")`), but if you are in a hurry: 75 | 76 | 1. Register at 77 | [opencagedata.com/users/sign_up](https://opencagedata.com/users/sign_up). 78 | 2. Generate an API key at the [OpenCage 79 | dashboard](https://opencagedata.com/dashboard#api-keys). 80 | 3. Save your API key as an [environment 81 | variable](https://rstats.wtf/r-startup.html#renviron) like 82 | `OPENCAGE_KEY=yourkey` in `.Renviron`. See `help(oc_config)` for 83 | alternative ways to set your OpenCage API key. 84 | 85 | Now you are ready to turn place names into latitude and longitude 86 | coordinates: 87 | 88 | ``` r 89 | library(opencage) 90 | oc_forward_df(placename = "Sarzeau") 91 | ``` 92 | 93 |
94 | 95 | | placename | oc_lat | oc_lng | oc_formatted | 96 | |:----------|---------:|--------:|:----------------------| 97 | | Sarzeau | 47.52877 | -2.7642 | 56370 Sarzeau, France | 98 | 99 |
100 | 101 | Or turn a set of coordinates into the name and address of the location: 102 | 103 | ``` r 104 | oc_reverse_df(latitude = 51.5034070, longitude = -0.1275920) 105 | ``` 106 | 107 |
108 | 109 | | latitude | longitude | oc_formatted | 110 | |---:|---:|:---| 111 | | 51.50341 | -0.127592 | 10 Downing Street, Westminster, London, SW1A 2AA, United Kingdom | 112 | 113 |
114 | 115 | But remember, the vignettes are really great! We have: 116 | 117 | - “[Introduction to 118 | opencage](https://docs.ropensci.org/opencage/articles/opencage.html)” 119 | `vignette("opencage")` 120 | - “[Customise your 121 | query](https://docs.ropensci.org/opencage/articles/customise_query.html)” 122 | `vignette("customise_query")` 123 | - “[Output 124 | options](https://docs.ropensci.org/opencage/articles/output_options.html)” 125 | `vignette("output_options")` 126 | 127 | ## About OpenCage 128 | 129 | The [OpenCage](https://opencagedata.com) API supports forward and 130 | reverse geocoding. Sources of OpenCage are open geospatial data 131 | including [OpenStreetMap](https://www.openstreetmap.org), 132 | [DataScienceToolkit](https://github.com/petewarden/dstk), 133 | [GeoPlanet](https://en.wikipedia.org/wiki/GeoPlanet), [Natural Earth 134 | Data](https://www.naturalearthdata.com), 135 | [libpostal](https://github.com/openvenues/libpostal), 136 | [GeoNames](https://www.geonames.org), and [Flickr’s 137 | shapefiles](https://code.flickr.net/2009/05/21/flickr-shapefiles-public-dataset-10/) 138 | plus a whole lot more besides. Refer to the current full [list of 139 | credits](https://opencagedata.com/credits). 140 | 141 | ## Code of Conduct 142 | 143 | Please note that this package is released with a [Contributor Code of 144 | Conduct](https://ropensci.org/code-of-conduct/). By contributing to this 145 | project, you agree to abide by its terms. 146 | -------------------------------------------------------------------------------- /man/oc_config.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/oc_config.R 3 | \name{oc_config} 4 | \alias{oc_config} 5 | \title{Configure settings} 6 | \usage{ 7 | oc_config( 8 | key = Sys.getenv("OPENCAGE_KEY"), 9 | rate_sec = getOption("oc_rate_sec", default = 1L), 10 | no_record = getOption("oc_no_record", default = TRUE), 11 | show_key = getOption("oc_show_key", default = FALSE), 12 | ... 13 | ) 14 | } 15 | \arguments{ 16 | \item{key}{Your OpenCage API key as a character vector of length one. Do not 17 | pass the key directly as a parameter, though. See details.} 18 | 19 | \item{rate_sec}{Numeric vector of length one. Sets the maximum number of 20 | requests sent to the OpenCage API per second. Defaults to the value set in 21 | the \code{oc_rate_sec} option, or, in case that does not exist, to 1L.} 22 | 23 | \item{no_record}{Logical vector of length one. When \code{TRUE}, OpenCage will not 24 | create log entries of the queries and will not cache the geocoding 25 | requests. Defaults to the value set in the \code{oc_no_record} option, or, in 26 | case that does not exist, to \code{TRUE}.} 27 | 28 | \item{show_key}{Logical vector of length one. This is only relevant when 29 | debugging \code{oc_forward()} or \code{oc_reverse()} calls with the \code{return = "url_only"} argument. When \code{TRUE}, the result will show your OpenCage API 30 | key in the URL as stored in the \code{OPENCAGE_KEY} environment variable. When 31 | not \code{TRUE}, the API key will be replaced with the string \code{OPENCAGE_KEY}. 32 | \code{show_key} defaults to the value set in the \code{oc_show_key} option, or, in 33 | case that does not exist, to \code{FALSE}.} 34 | 35 | \item{...}{Ignored.} 36 | } 37 | \description{ 38 | Configure session settings for \pkg{opencage}. 39 | } 40 | \section{Set your OpenCage API key}{ 41 | 42 | 43 | \pkg{opencage} will conveniently retrieve your API key if it is saved in the 44 | environment variable \code{"OPENCAGE_KEY"}. \code{\link[=oc_config]{oc_config()}} will help to set that 45 | environment variable. Do not pass the key directly as a parameter to the 46 | function, though, as you risk exposing it via your script or your history. 47 | There are three safer ways to set your API key instead: 48 | \enumerate{ 49 | \item Save your API key as an environment variable in 50 | \code{\link[base:Startup]{.Renviron}} as described in \href{https://rstats.wtf/r-startup.html#renviron}{What They Forgot to Teach You About R} or \href{https://csgillespie.github.io/efficientR/set-up.html#renviron}{Efficient R Programming}. 51 | From there it will be fetched by all functions that call the OpenCage API. 52 | You do not even have to call \code{oc_config()} to set your key; you can start 53 | geocoding right away. If you have the \pkg{usethis} package installed, you 54 | can edit your \code{\link[base:Startup]{.Renviron}} with \code{usethis::edit_r_environ()}. 55 | We strongly recommend storing your API key in the user-level .Renviron, as 56 | opposed to the project-level. This makes it less likely you will share 57 | sensitive information by mistake. 58 | \item If you use a package like \pkg{keyring} to store your credentials, you can 59 | safely pass your key in a script with a function call like this 60 | \code{oc_config(key = keyring::key_get("opencage"))}. 61 | \item If you call \code{oc_config()} in an \code{\link[base:interactive]{base::interactive()}} session and the 62 | \code{OPENCAGE_KEY} environment variable is not set, it will prompt you to enter 63 | the key in the console. 64 | } 65 | } 66 | 67 | \section{Set your OpenCage API rate limit}{ 68 | 69 | 70 | The rate limit allowed by the API depends on the OpenCage plan you purchased 71 | and ranges from 1 request/sec for the "Free Trial" plan to 15 requests/sec 72 | for the "Medium" or "Large" plans, see \url{https://opencagedata.com/pricing} for 73 | details and up-to-date information. You can set the rate limit persistently 74 | across sessions by setting an \code{oc_rate_sec} \link[base:options]{option} in your 75 | \code{\link[base:Startup]{.Rprofile}}. If you have the \pkg{usethis} package 76 | installed, you can edit your \code{\link[base:Startup]{.Rprofile}} with 77 | \code{usethis::edit_r_profile()}. 78 | } 79 | 80 | \section{Prevent query logging and caching}{ 81 | 82 | 83 | By default, OpenCage will store your queries in its server logs and will 84 | cache the forward geocoding requests on their side. They do this in order to 85 | speed up response times and to be able to debug errors and improve their 86 | service. Logs are automatically deleted after six months according to 87 | OpenCage's \href{https://opencagedata.com/gdpr}{page on data protection and GDPR}. 88 | 89 | If you set \code{no_record} to \code{TRUE}, the query contents are not logged nor 90 | cached. OpenCage still records that you made a request, but not the specific 91 | query you made. \code{oc_config(no_record = TRUE)} sets the \code{oc_no_record} 92 | \link[base:options]{option} for the active R session, so it will be used for all 93 | subsequent OpenCage queries. You can set the \code{oc_no_record} 94 | \link[base:options]{option} persistently across sessions in your 95 | \code{\link[base:Startup]{.Rprofile}}. 96 | 97 | For increased privacy \pkg{opencage} sets \code{no_record} to \code{TRUE}, by default. 98 | Please note, however, that \pkg{opencage} always caches the data it receives 99 | from the OpenCage API locally, but only for as long as your R session is 100 | alive. 101 | 102 | For more information on OpenCage's policies on privacy and data protection 103 | see \href{https://opencagedata.com/faq#legal}{their FAQs}, their \href{https://opencagedata.com/gdpr}{GDPR page}, and, for the \code{no_record} parameter, see 104 | the relevant \href{https://blog.opencagedata.com/post/145602604628/more-privacy-with-norecord-parameter}{blog post}. 105 | } 106 | 107 | -------------------------------------------------------------------------------- /man/oc_reverse_df.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/oc_reverse.R 3 | \name{oc_reverse_df} 4 | \alias{oc_reverse_df} 5 | \alias{oc_reverse_df.data.frame} 6 | \alias{oc_reverse_df.numeric} 7 | \title{Reverse geocoding with data frames} 8 | \usage{ 9 | oc_reverse_df(...) 10 | 11 | \method{oc_reverse_df}{data.frame}( 12 | data, 13 | latitude, 14 | longitude, 15 | bind_cols = TRUE, 16 | output = c("short", "all"), 17 | language = NULL, 18 | min_confidence = NULL, 19 | roadinfo = FALSE, 20 | no_annotations = TRUE, 21 | no_dedupe = FALSE, 22 | abbrv = FALSE, 23 | address_only = FALSE, 24 | ... 25 | ) 26 | 27 | \method{oc_reverse_df}{numeric}( 28 | latitude, 29 | longitude, 30 | output = c("short", "all"), 31 | language = NULL, 32 | min_confidence = NULL, 33 | no_annotations = TRUE, 34 | no_dedupe = FALSE, 35 | abbrv = FALSE, 36 | address_only = FALSE, 37 | ... 38 | ) 39 | } 40 | \arguments{ 41 | \item{...}{Ignored.} 42 | 43 | \item{data}{A data frame.} 44 | 45 | \item{latitude, longitude}{Unquoted variable names of numeric columns or 46 | vectors of latitude and longitude values.} 47 | 48 | \item{bind_cols}{When \code{bind_col = TRUE}, the default, the results are column 49 | bound to \code{data}. When \code{FALSE}, the results are returned as a new tibble.} 50 | 51 | \item{output}{A character vector of length one indicating whether only the 52 | formatted address (\code{"short"}, the default) or all variables (\code{"all"}) 53 | variables should be returned.} 54 | 55 | \item{language}{Character vector, or an unquoted variable name of such a 56 | vector, of \href{https://en.wikipedia.org/wiki/IETF_language_tag}{IETF BCP 47 language tags} (such as "es" for 57 | Spanish or "pt-BR" for Brazilian Portuguese). OpenCage will attempt to 58 | return results in that language. Alternatively you can specify the "native" 59 | tag, in which case OpenCage will attempt to return the response in the 60 | "official" language(s). In case the \code{language} parameter is set to \code{NULL} 61 | (which is the default), the tag is not recognized, or OpenCage does not 62 | have a record in that language, the results will be returned in English.} 63 | 64 | \item{min_confidence}{Numeric vector of integer values, or an unquoted 65 | variable name of such a vector, between 0 and 10 indicating the precision 66 | of the returned result as defined by its geographical extent, (i.e. by the 67 | extent of the result's bounding box). See the \href{https://opencagedata.com/api#confidence}{API documentation} for details. Only 68 | results with at least the requested confidence will be returned. Default is 69 | \code{NULL}).} 70 | 71 | \item{roadinfo}{Logical vector, or an unquoted variable name of such a 72 | vector, indicating whether the geocoder should attempt to match the nearest 73 | road (rather than an address) and provide additional road and driving 74 | information. Default is \code{FALSE}.} 75 | 76 | \item{no_annotations}{Logical vector, or an unquoted variable name of such a 77 | vector, indicating whether additional information about the result location 78 | should be returned. \code{TRUE} by default, which means that the results will 79 | not contain annotations.} 80 | 81 | \item{no_dedupe}{Logical vector, or an unquoted variable name of such a 82 | vector. Default is \code{FALSE}. When \code{TRUE} the results will not be 83 | deduplicated.} 84 | 85 | \item{abbrv}{Logical vector, or an unquoted variable name of such a vector. 86 | Default is \code{FALSE}. When \code{TRUE} addresses in the \code{oc_formatted} variable of 87 | the results are abbreviated (e.g. "Main St." instead of "Main Street").} 88 | 89 | \item{address_only}{Logical vector, or an unquoted variable name of such a 90 | vector. Default is \code{FALSE}. When \code{TRUE} only the address details are 91 | returned in the \code{oc_formatted} variable of the results, not the name of a 92 | point-of-interest should there be one at this address.} 93 | } 94 | \value{ 95 | A tibble. Column names coming from the OpenCage API are prefixed with 96 | \code{"oc_"}. 97 | } 98 | \description{ 99 | Reverse geocoding from latitude and longitude pairs to the names and 100 | addresses of a location. 101 | } 102 | \examples{ 103 | \dontshow{if (oc_key_present() && oc_api_ok()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 104 | 105 | library(tibble) 106 | df <- tibble( 107 | id = 1:4, 108 | lat = c(-36.85007, 47.21864, 53.55034, 34.05369), 109 | lng = c(174.7706, -1.554136, 10.000654, -118.242767) 110 | ) 111 | 112 | # Return formatted address of lat/lng values 113 | oc_reverse_df(df, latitude = lat, longitude = lng) 114 | 115 | # Return more detailed information about the locations 116 | oc_reverse_df(df, 117 | latitude = lat, longitude = lng, 118 | output = "all" 119 | ) 120 | 121 | # Return results in a preferred language if possible 122 | oc_reverse_df(df, 123 | latitude = lat, longitude = lng, 124 | language = "fr" 125 | ) 126 | 127 | # oc_reverse_df accepts unquoted column names for all 128 | # arguments except bind_cols and output. 129 | # This makes it possible to build up more detailed queries 130 | # through the data frame passed to the data argument. 131 | 132 | df2 <- add_column(df, 133 | language = c("en", "fr", "de", "en"), 134 | confidence = c(8, 10, 10, 10) 135 | ) 136 | 137 | # Use language column to specify preferred language of results 138 | # and confidence column to allow different confidence levels 139 | oc_reverse_df(df2, 140 | latitude = lat, longitude = lng, 141 | language = language, 142 | min_confidence = confidence 143 | ) 144 | \dontshow{\}) # examplesIf} 145 | } 146 | \seealso{ 147 | \code{\link[=oc_reverse]{oc_reverse()}} for inputs as vectors, or \code{\link[=oc_forward]{oc_forward()}} and 148 | \code{\link[=oc_forward]{oc_forward()}} for forward geocoding. For more information about the API 149 | and the various parameters, see the \href{https://opencagedata.com/api}{OpenCage API documentation}. 150 | } 151 | -------------------------------------------------------------------------------- /R/oc_config.R: -------------------------------------------------------------------------------- 1 | #' Configure settings 2 | #' 3 | #' Configure session settings for \pkg{opencage}. 4 | #' 5 | #' @param key Your OpenCage API key as a character vector of length one. Do not 6 | #' pass the key directly as a parameter, though. See details. 7 | #' @param rate_sec Numeric vector of length one. Sets the maximum number of 8 | #' requests sent to the OpenCage API per second. Defaults to the value set in 9 | #' the `oc_rate_sec` option, or, in case that does not exist, to 1L. 10 | #' @param no_record Logical vector of length one. When `TRUE`, OpenCage will not 11 | #' create log entries of the queries and will not cache the geocoding 12 | #' requests. Defaults to the value set in the `oc_no_record` option, or, in 13 | #' case that does not exist, to `TRUE`. 14 | #' @param show_key Logical vector of length one. This is only relevant when 15 | #' debugging `oc_forward()` or `oc_reverse()` calls with the `return = 16 | #' "url_only"` argument. When `TRUE`, the result will show your OpenCage API 17 | #' key in the URL as stored in the `OPENCAGE_KEY` environment variable. When 18 | #' not `TRUE`, the API key will be replaced with the string `OPENCAGE_KEY`. 19 | #' `show_key` defaults to the value set in the `oc_show_key` option, or, in 20 | #' case that does not exist, to `FALSE`. 21 | #' @param ... Ignored. 22 | #' 23 | #' @section Set your OpenCage API key: 24 | #' 25 | #' \pkg{opencage} will conveniently retrieve your API key if it is saved in the 26 | #' environment variable `"OPENCAGE_KEY"`. [oc_config()] will help to set that 27 | #' environment variable. Do not pass the key directly as a parameter to the 28 | #' function, though, as you risk exposing it via your script or your history. 29 | #' There are three safer ways to set your API key instead: 30 | #' 31 | #' 1. Save your API key as an environment variable in 32 | #' [`.Renviron`][base::Startup] as described in [What They Forgot to Teach You 33 | #' About R](https://rstats.wtf/r-startup.html#renviron) or [Efficient R 34 | #' Programming](https://csgillespie.github.io/efficientR/set-up.html#renviron). 35 | #' From there it will be fetched by all functions that call the OpenCage API. 36 | #' You do not even have to call `oc_config()` to set your key; you can start 37 | #' geocoding right away. If you have the \pkg{usethis} package installed, you 38 | #' can edit your [`.Renviron`][base::Startup] with `usethis::edit_r_environ()`. 39 | #' We strongly recommend storing your API key in the user-level .Renviron, as 40 | #' opposed to the project-level. This makes it less likely you will share 41 | #' sensitive information by mistake. 42 | #' 43 | #' 2. If you use a package like \pkg{keyring} to store your credentials, you can 44 | #' safely pass your key in a script with a function call like this 45 | #' `oc_config(key = keyring::key_get("opencage"))`. 46 | #' 47 | #' 3. If you call `oc_config()` in an [base::interactive()] session and the 48 | #' `OPENCAGE_KEY` environment variable is not set, it will prompt you to enter 49 | #' the key in the console. 50 | #' 51 | #' @section Set your OpenCage API rate limit: 52 | #' 53 | #' The rate limit allowed by the API depends on the OpenCage plan you purchased 54 | #' and ranges from 1 request/sec for the "Free Trial" plan to 15 requests/sec 55 | #' for the "Medium" or "Large" plans, see for 56 | #' details and up-to-date information. You can set the rate limit persistently 57 | #' across sessions by setting an `oc_rate_sec` [option][base::options] in your 58 | #' [`.Rprofile`][base::Startup]. If you have the \pkg{usethis} package 59 | #' installed, you can edit your [`.Rprofile`][base::Startup] with 60 | #' `usethis::edit_r_profile()`. 61 | #' 62 | #' @section Prevent query logging and caching: 63 | #' 64 | #' By default, OpenCage will store your queries in its server logs and will 65 | #' cache the forward geocoding requests on their side. They do this in order to 66 | #' speed up response times and to be able to debug errors and improve their 67 | #' service. Logs are automatically deleted after six months according to 68 | #' OpenCage's [page on data protection and GDPR](https://opencagedata.com/gdpr). 69 | #' 70 | #' If you set `no_record` to `TRUE`, the query contents are not logged nor 71 | #' cached. OpenCage still records that you made a request, but not the specific 72 | #' query you made. `oc_config(no_record = TRUE)` sets the `oc_no_record` 73 | #' [option][base::options] for the active R session, so it will be used for all 74 | #' subsequent OpenCage queries. You can set the `oc_no_record` 75 | #' [option][base::options] persistently across sessions in your 76 | #' [`.Rprofile`][base::Startup]. 77 | #' 78 | #' For increased privacy \pkg{opencage} sets `no_record` to `TRUE`, by default. 79 | #' Please note, however, that \pkg{opencage} always caches the data it receives 80 | #' from the OpenCage API locally, but only for as long as your R session is 81 | #' alive. 82 | #' 83 | # nolint start: line_length_linter. 84 | #' For more information on OpenCage's policies on privacy and data protection 85 | #' see [their FAQs](https://opencagedata.com/faq#legal), their [GDPR 86 | #' page](https://opencagedata.com/gdpr), and, for the `no_record` parameter, see 87 | #' the relevant [blog 88 | #' post](https://blog.opencagedata.com/post/145602604628/more-privacy-with-norecord-parameter). 89 | # nolint end 90 | #' 91 | #' @export 92 | oc_config <- 93 | function(key = Sys.getenv("OPENCAGE_KEY"), 94 | rate_sec = getOption("oc_rate_sec", default = 1L), 95 | no_record = getOption("oc_no_record", default = TRUE), 96 | show_key = getOption("oc_show_key", default = FALSE), 97 | ...) { 98 | key_needed <- 99 | c( 100 | "Using the OpenCage Geocoder requires a valid API key.\n", 101 | "See and help(oc_config)\n", 102 | "\n" 103 | ) 104 | 105 | if (!identical(key, "")) { 106 | pat <- key 107 | } else if (!rlang::is_interactive()) { 108 | stop( 109 | key_needed, 110 | "Please set the environment variable OPENCAGE_KEY to your OpenCage API key.", # nolint: line_length_linter. 111 | call. = FALSE 112 | ) 113 | } else { 114 | message(key_needed, "Please enter your OpenCage API key and press enter:") 115 | pat <- readline(": ") 116 | } 117 | 118 | oc_check_key(pat) 119 | 120 | Sys.setenv(OPENCAGE_KEY = pat) 121 | 122 | # set rate limit 123 | ratelimitr::UPDATE_RATE( 124 | oc_get_limited, 125 | ratelimitr::rate(n = rate_sec, period = 1L) 126 | ) 127 | 128 | # set no_record 129 | oc_check_logical(no_record, check_length_one = TRUE) 130 | options("oc_no_record" = no_record) 131 | 132 | # set show_key 133 | options("oc_show_key" = show_key) 134 | } 135 | -------------------------------------------------------------------------------- /.github/contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing to opencage 2 | 3 | First of all, thanks for considering contributing to {opencage}! 👍 4 | We welcome bug reports and pull requests that expand and improve the functionality of {opencage} from all contributors. 5 | This document outlines how to propose a change to {opencage}. 6 | 7 | ## Code of Conduct 8 | 9 | Please note that this project is released with a [Contributor Code of Conduct](https://ropensci.org/code-of-conduct/). 10 | By contributing to this project you agree to abide by its terms. 11 | 12 | ## How you can contribute 13 | 14 | There are several ways you can contribute to this project. 15 | 16 | ### Share the love ❤️ 17 | 18 | Think {opencage} is useful? 19 | Let others discover it, by telling them in person, via your preferred social medium, or a blog post. 20 | Please also share your use case in our discussion forum at [discuss.ropensci.org](https://discuss.ropensci.org). 21 | 22 | Using {opencage} for a paper you are writing? 23 | Please consider [citing it](https://docs.ropensci.org/opencage/authors.html). 24 | Get citation information for {opencage} in R with `citation(package = 'opencage')`. 25 | 26 | ### Ask a question ❓ 27 | 28 | Using {opencage} and got stuck? 29 | Browse the [documentation](https://docs.ropensci.org/opencage/) to see if you can find a solution. 30 | Still stuck? 31 | Post your question on our [discussion forum](https://discuss.ropensci.org) and tag it with the package name. 32 | While we cannot offer user support, we'll try to do our best to address it, as questions often lead to better documentation or the discovery of bugs. 33 | 34 | Want to ask a question in private? 35 | Email the person listed as maintainer in the `DESCRIPTION` file of this repo. 36 | Keep in mind that private discussions over email don't help others - but of course email is totally warranted if it's a sensitive problem of any kind. 37 | 38 | ### Improve the documentation ✍ 39 | 40 | Noticed a typo on the website? 41 | Think a function could use a better example? 42 | Good documentation makes all the difference, so your help to improve it is very welcome! 43 | 44 | Small typos or grammatical errors in documentation can be edited directly using the GitHub web interface, as long as the changes are made in the _source_ file. 45 | 46 | This means you should 47 | 48 | * edit a roxygen comment in a `.R` file below `R/`, not the `.Rd` files below `man/`. 49 | * edit the `README.Rmd` file, not the `README.md` file in the package root directory. 50 | 51 | Since we use a non-standard workflow to render the vignettes in this package, you should 52 | 53 | * edit the `*.Rmd.src` files in the `vignettes/` directory, not the `*.Rmd` files there. 54 | 55 | ### Reporting an issue 🐛 56 | 57 | Using our_package and discovered a bug? 58 | That's annoying! 59 | Don't let others have the same experience and open an [issue report on GitHub](https://github.com/ropensci/opencage/issues/new) so we can fix it. 60 | Please illustrate the bug with a minimal working example, also known as a [reprex](https://www.tidyverse.org/help/#reprex), i.e. please provide detailed steps to reproduce the bug and any information that might be helpful in troubleshooting. The {[reprex](https://reprex.tidyverse.org/)} 📦 can help you with this. 61 | 62 | ### Contribute code 🛠 63 | 64 | Care to fix bugs or implement new functionality for {opencage}? 65 | Awesome! 👏 66 | Before you make a substantial change to the package, it is often preferable to first discuss need and scope for the change with the author(s) of the package in an issue report. 67 | 68 | You should then follow the following process: 69 | 70 | * Fork the package and clone onto your computer. 71 | If you haven't done this before, we recommend using `usethis::create_from_github("ropensci/opencage")`. 72 | See the [Pull Request Helper](https://usethis.r-lib.org/articles/articles/pr-functions.html) vignette for more details on how {[usethis](https://usethis.r-lib.org/)} can assist you with contributing code via pull requests (PR), . 73 | * Install all development dependencies with `devtools::install_dev_deps()`, and then make sure the package passes R CMD check by running `devtools::check()`. 74 | If R CMD check doesn't pass cleanly, it's a good idea to ask for help before continuing. 75 | * Create a Git branch for each issue you want to address. 76 | We recommend using `usethis::pr_init("brief-description-of-change")`. 77 | * Make your changes, commit to git, and then create a PR by running `usethis::pr_push()`, and following the prompts in your browser. 78 | The title of your PR should briefly describe the change; the body of your PR should contain "Fixes [#issue-number]". 79 | * Add a bullet point to the top of `NEWS.md` describing the changes made followed by your GitHub username, and links to relevant issue(s)/PR(s). 80 | 81 | You should also consider the following: 82 | 83 | * Keep the changes in your PR as small and succinct as possible. 84 | Most importantly only address one issue per PR. 85 | This makes it easier for us to review and merge your PR. 86 | * We mostly follow the tidyverse [style guide](http://style.tidyverse.org). 87 | You can use the {[styler](https://styler.r-lib.org/)} package to apply these styles, but please do not restyle code that has nothing to do with your PR. 88 | * We use {[roxygen2](https://roxygen2.r-lib.org/)}, with [Markdown syntax](https://roxygen2.r-lib.org/articles/rd-formatting.html), for documentation. 89 | * We would prefer it if your PR also included unit tests. 90 | Contributions with test cases included are easier to accept and unit tests ensure that the functionality you just added will not break in the future. 91 | We use {[testthat](https://testthat.r-lib.org/)} for unit tests and we track test coverage with [covr](https://covr.r-lib.org/) and [Codecov](https://codecov.io/). 92 | * We use {[lintr](https://github.com/jimhester/lintr)} for [static code analysis](https://github.com/jimhester/lintr). 93 | * We use [GitHub Actions](https://docs.github.com/en/actions) for continuous integration. 94 | Workflows are adapted from [r-lib/actions](https://github.com/r-lib/actions). 95 | Unfortunately tests requiring an API key will not run on a PR, because neither our nor your API key is available there to prevent it from leaking. 96 | 97 | ## rOpenSci discussion forum 👄 98 | 99 | Check out our [discussion forum](https://discuss.ropensci.org) if 100 | 101 | * you have a question, a use case, or otherwise not a bug or feature request for the software itself. 102 | * you think your issue requires a longer discussion. 103 | 104 | ## License 📜 105 | 106 | {opencage} is licensed under the [GPL-2 or later](https://opensource.org/licenses/gpl-license). 107 | 108 | ## Thanks for contributing! 🙏 109 | 110 | For more detailed info about contributing to rOpenSci, please see the [rOpenSci Community Contributing Guide](https://contributing.ropensci.org/). 111 | -------------------------------------------------------------------------------- /R/oc_check_query.R: -------------------------------------------------------------------------------- 1 | #' Check OpenCage query arguments 2 | #' 3 | #' Function that checks the query arguments passed to OpenCage 4 | #' 5 | #' @param limit The maximum number of results that should be returned. Integer 6 | #' values between 1 and 100 are allowed. 7 | #' @inheritParams oc_forward 8 | #' 9 | #' @noRd 10 | 11 | oc_check_query <- 12 | function(placename = NULL, 13 | latitude = NULL, 14 | longitude = NULL, 15 | bounds = NULL, 16 | proximity = NULL, 17 | countrycode = NULL, 18 | language = NULL, 19 | limit = NULL, 20 | min_confidence = NULL, 21 | no_annotations = NULL, 22 | roadinfo = NULL, 23 | no_dedupe = NULL, 24 | abbrv = NULL, 25 | address_only = NULL, 26 | add_request = NULL) { 27 | arglist <- 28 | purrr::compact( 29 | list( 30 | placename = placename, 31 | latitude = latitude, 32 | longitude = longitude, 33 | bounds = bounds, 34 | proximity = proximity, 35 | countrycode = countrycode, 36 | language = language, 37 | limit = limit, 38 | min_confidence = min_confidence, 39 | no_annotations = no_annotations, 40 | roadinfo = roadinfo, 41 | no_dedupe = no_dedupe, 42 | abbrv = abbrv, 43 | address_only = address_only, 44 | add_request = add_request 45 | ) 46 | ) 47 | 48 | # ensure arguments are length one or match length of placename/latitude 49 | arglngths <- lengths(arglist) 50 | if (!all(arglngths == arglngths[1] | arglngths == 1, na.rm = TRUE)) { 51 | stop( 52 | call. = FALSE, "All arguments must be of length one \n", 53 | "or of the same length as `placename` or `latitude`." 54 | ) 55 | } 56 | 57 | purrr::pwalk( 58 | .l = arglist, 59 | .f = .oc_check_query 60 | ) 61 | } 62 | 63 | .oc_check_query <- 64 | function(placename = NULL, 65 | latitude = NULL, 66 | longitude = NULL, 67 | bounds = NULL, 68 | proximity = NULL, 69 | countrycode = NULL, 70 | language = NULL, 71 | limit = NULL, 72 | min_confidence = NULL, 73 | no_annotations = NULL, 74 | roadinfo = NULL, 75 | no_dedupe = NULL, 76 | abbrv = NULL, 77 | address_only = NULL, 78 | add_request = NULL) { 79 | # check placename 80 | if (!is.null(placename) && !is.character(placename)) { 81 | stop("`placename` must be a character vector.", call. = FALSE) 82 | } 83 | 84 | # check latitude 85 | if (!is.null(latitude)) oc_check_between(latitude, -90, 90) 86 | 87 | # check longitude 88 | if (!is.null(longitude)) oc_check_between(longitude, -180, 180) 89 | 90 | # check bounds 91 | if (!is.null(bounds)) { 92 | if (length(bounds) != 4) { 93 | stop( 94 | call. = FALSE, 95 | "Every `bbox` must be a numeric vector of length 4.\n", 96 | "Did you forget to wrap the vector(s) in a list?" 97 | ) 98 | } 99 | oc_check_bbox(bounds[[1]], bounds[[2]], bounds[[3]], bounds[[4]]) 100 | } 101 | 102 | # check proximity 103 | if (!is.null(proximity)) { 104 | if (length(proximity) != 2) { 105 | stop( 106 | call. = FALSE, 107 | "Every `proximity` point must be a numeric vector of length 2.\n", 108 | "Did you forget to wrap the vector(s) in a list?" 109 | ) 110 | } 111 | if (!utils::hasName(proximity, "latitude") || 112 | !utils::hasName(proximity, "longitude")) { 113 | stop( 114 | call. = FALSE, 115 | "The coordinates of every `proximity` point must be named ", 116 | "'latitude' and 'longitude'." 117 | ) 118 | } 119 | oc_check_point( 120 | latitude = proximity[["latitude"]], 121 | longitude = proximity[["longitude"]] 122 | ) 123 | } 124 | 125 | # check countrycode 126 | if (!is.null(countrycode)) { 127 | if (!(all(toupper(unlist(countrycode)) %in% countrycodes$code))) { 128 | stop("Every `countrycode` must be valid. ", 129 | "See `data('countrycodes')` for valid values.", 130 | call. = FALSE 131 | ) 132 | } 133 | } 134 | 135 | # check language 136 | if (!is.null(language)) { 137 | if (!is.character(language)) { 138 | stop(call. = FALSE, "`language` must be a character vector.") 139 | } 140 | } 141 | 142 | # check limit 143 | if (!is.null(limit)) { 144 | if (!(limit %in% 1:100)) { 145 | stop( 146 | call. = FALSE, 147 | "Every `limit` must be an integer between 1 and 100." 148 | ) 149 | } 150 | } 151 | 152 | # check min_confidence 153 | if (!is.null(min_confidence)) { 154 | if (!(min_confidence %in% 1:10)) { 155 | stop( 156 | call. = FALSE, 157 | "Every `min_confidence` must be an integer between 1 and 10." 158 | ) 159 | } 160 | } 161 | 162 | oc_check_logical(no_annotations) 163 | 164 | oc_check_logical(roadinfo) 165 | 166 | oc_check_logical(no_dedupe) 167 | 168 | oc_check_logical(abbrv) 169 | 170 | oc_check_logical(address_only) 171 | 172 | oc_check_logical(add_request) 173 | } 174 | 175 | 176 | #' Check whether an argument is a boolean and (optionally) of length one 177 | #' 178 | #' @param variable argument to check 179 | #' @param check_length_one boolean whether to check if the argument is of length 180 | #' one 181 | #' 182 | #' @noRd 183 | 184 | oc_check_logical <- function(variable, check_length_one = FALSE) { 185 | if (!is.null(variable)) { 186 | if (!is.logical(variable)) { 187 | var_name <- deparse(substitute(variable)) # deparse only if check fails 188 | stop("`", var_name, "` must be a logical vector.", call. = FALSE) 189 | } else if (check_length_one && !identical(length(variable), 1L)) { 190 | var_name <- deparse(substitute(variable)) # deparse only if check fails 191 | stop("`", var_name, "` must be a vector of length one.", call. = FALSE) 192 | } 193 | } 194 | } 195 | 196 | 197 | #' Check whether a value is between two values 198 | #' 199 | #' @param x numeric value to check 200 | #' @param left numeric lower bound 201 | #' @param right numeric upper bound 202 | #' 203 | #' @noRd 204 | 205 | oc_check_between <- function(x, left, right) { 206 | if (!is.numeric(x)) { 207 | stop("Every `", deparse(substitute(x)), "` must be numeric.", call. = FALSE) 208 | } 209 | if (isTRUE(x < left || x > right)) { 210 | stop( 211 | "Every `", 212 | deparse(substitute(x)), 213 | "` must be between ", 214 | left, 215 | " and ", 216 | right, 217 | ".", 218 | call. = FALSE 219 | ) 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /tests/testthat/test-oc_check_query.R: -------------------------------------------------------------------------------- 1 | # Test oc_check_query() -------------------------------------------------- 2 | 3 | test_that("oc_check_query checks placename", { 4 | expect_error( 5 | oc_check_query( 6 | placename = 222 7 | ), 8 | "`placename` must be a character vector." 9 | ) 10 | }) 11 | 12 | test_that("oc_check_query checks latitude", { 13 | expect_error( 14 | oc_check_query( 15 | latitude = "fortytwo", 16 | longitude = 51.11892 17 | ), 18 | "Every `latitude` must be numeric." 19 | ) 20 | expect_error( 21 | oc_check_query( 22 | latitude = 433, 23 | longitude = 51.11892 24 | ), 25 | "Every `latitude` must be between -90 and 90." 26 | ) 27 | }) 28 | 29 | test_that("oc_check_query checks longitude", { 30 | expect_error( 31 | oc_check_query( 32 | latitude = 43, 33 | longitude = TRUE 34 | ), 35 | "Every `longitude` must be numeric." 36 | ) 37 | expect_error( 38 | oc_check_query( 39 | latitude = 43, 40 | longitude = 5111892 41 | ), 42 | "Every `longitude` must be between -180 and 180." 43 | ) 44 | }) 45 | 46 | test_that("oc_check_query checks bounds", { 47 | expect_error( 48 | oc_check_query( 49 | placename = "Sarzeau", 50 | bounds = list(c(-5, 51, 0)) 51 | ), 52 | "Every `bbox` must be a numeric vector of length 4." 53 | ) 54 | }) 55 | 56 | test_that("oc_check_query checks proximity", { 57 | expect_error( 58 | oc_check_query( 59 | placename = "Sarzeau", 60 | proximity = list(c(47.5, longitude = -2.7)) # latitude not named 61 | ), 62 | "must be named 'latitude' and 'longitude'" 63 | ) 64 | expect_error( 65 | oc_check_query( 66 | placename = "Sarzeau", 67 | proximity = list(c(longitude = 47.5, -2.7)) # longitude not named 68 | ), 69 | "must be named 'latitude' and 'longitude'" 70 | ) 71 | expect_error( 72 | oc_check_query( 73 | placename = "Sarzeau", 74 | proximity = list(c(1, 2, 3)) # too many coordinates 75 | ), 76 | "Every `proximity` point must be a numeric vector of length 2." 77 | ) 78 | expect_error( 79 | oc_check_query( 80 | placename = "Sarzeau", 81 | proximity = list(c(1)) # too few coordinates 82 | ), 83 | "Every `proximity` point must be a numeric vector of length 2." 84 | ) 85 | expect_error( 86 | oc_check_query( 87 | placename = c("Sarzeau", "Biarritz"), 88 | proximity = c(1, 2) # not wrapped in a list 89 | ), 90 | "Every `proximity` point must be a numeric vector of length 2." 91 | ) 92 | expect_error( 93 | oc_check_query( 94 | placename = "Sarzeau", 95 | proximity = list(c(latitude = -91, longitude = 2)) 96 | ), 97 | "Every `latitude` must be between -90 and 90.", 98 | fixed = TRUE 99 | ) 100 | expect_error( 101 | oc_check_query( 102 | placename = "Sarzeau", 103 | proximity = list(c(latitude = 0, longitude = 181)) 104 | ), 105 | "Every `longitude` must be between -180 and 180.", 106 | fixed = TRUE 107 | ) 108 | }) 109 | 110 | test_that("oc_check_query checks countrycode", { 111 | expect_error( 112 | oc_check_query( 113 | placename = "Sarzeau", 114 | countrycode = "notacountrycode" 115 | ), 116 | "Every `countrycode` must be valid.*" 117 | ) 118 | }) 119 | 120 | test_that("oc_check_query ok with lower case countrycode", { 121 | expect_silent( 122 | oc_check_query( 123 | placename = "Sarzeau", 124 | countrycode = "fr" 125 | ) 126 | ) 127 | expect_silent( 128 | oc_check_query( 129 | placename = "Sarzeau", 130 | countrycode = "FR" 131 | ) 132 | ) 133 | }) 134 | 135 | test_that("oc_check_query checks language", { 136 | expect_error( 137 | oc_check_query( 138 | placename = "Sarzeau", 139 | language = TRUE 140 | ), 141 | "`language` must be a character vector." 142 | ) 143 | }) 144 | 145 | test_that("oc_check_query checks limit", { 146 | expect_error( 147 | oc_check_query( 148 | placename = "Sarzeau", 149 | limit = 200 150 | ), 151 | "`limit` must be an integer between 1 and 100." 152 | ) 153 | }) 154 | 155 | test_that("oc_check_query checks no_annotations", { 156 | expect_error( 157 | oc_check_query( 158 | placename = "Sarzeau", 159 | no_annotations = "yes" 160 | ), 161 | "`no_annotations` must be a logical vector." 162 | ) 163 | }) 164 | 165 | test_that("oc_check_query checks roadinfo", { 166 | expect_error( 167 | oc_check_query( 168 | placename = "Afsluitdijk", 169 | roadinfo = "yes" 170 | ), 171 | "`roadinfo` must be a logical vector." 172 | ) 173 | expect_silent( 174 | oc_check_query( 175 | placename = "Afsluitdijk", 176 | roadinfo = TRUE 177 | ) 178 | ) 179 | }) 180 | 181 | test_that("oc_check_query checks no_dedupe", { 182 | expect_error( 183 | oc_check_query( 184 | placename = "Sarzeau", 185 | no_dedupe = "yes" 186 | ), 187 | "`no_dedupe` must be a logical vector." 188 | ) 189 | }) 190 | 191 | test_that("oc_check_query checks abbrv", { 192 | expect_error( 193 | oc_check_query( 194 | placename = "Sarzeau", 195 | abbrv = "yes" 196 | ), 197 | "`abbrv` must be a logical vector." 198 | ) 199 | }) 200 | 201 | test_that("oc_check_query checks address_only", { 202 | expect_error( 203 | oc_check_query( 204 | placename = "Sarzeau", 205 | address_only = "yes" 206 | ), 207 | "`address_only` must be a logical vector." 208 | ) 209 | }) 210 | 211 | test_that("oc_check_query checks add_request", { 212 | expect_error( 213 | oc_check_query( 214 | placename = "Sarzeau", 215 | add_request = "yes" 216 | ), 217 | "`add_request` must be a logical vector." 218 | ) 219 | }) 220 | 221 | test_that("oc_check_query checks argument lengths", { 222 | expect_error( 223 | oc_check_query( 224 | placename = "Sarzeau", 225 | abbrv = c(TRUE, FALSE) 226 | ), 227 | "same length as `placename` or `latitude`" 228 | ) 229 | }) 230 | 231 | 232 | # Test oc_check_logical() ------------------------------------------------- 233 | test_that("oc_check_logical checks no_record", { 234 | no_record <- "yes" 235 | expect_error( 236 | oc_check_logical( 237 | variable = no_record, 238 | check_length_one = TRUE 239 | ), 240 | "`no_record` must be a logical vector." 241 | ) 242 | 243 | no_record <- c(TRUE, FALSE) 244 | expect_error( 245 | oc_check_logical( 246 | variable = no_record, 247 | check_length_one = TRUE 248 | ), 249 | "`no_record` must be a vector of length one." 250 | ) 251 | 252 | no_record <- TRUE 253 | expect_silent( 254 | oc_check_logical( 255 | variable = no_record, 256 | check_length_one = TRUE 257 | ) 258 | ) 259 | }) 260 | 261 | # Test oc_check_between() ------------------------------------------------- 262 | test_that("oc_check_between works", { 263 | symbol <- 11 264 | expect_snapshot(oc_check_between(symbol, 0, 10), error = TRUE) 265 | expect_snapshot(oc_check_between(symbol, 11.0001, 11.0002), error = TRUE) 266 | expect_silent(oc_check_between(symbol, 0, 11)) 267 | 268 | # integer 269 | symbol <- 11L 270 | expect_snapshot(oc_check_between(symbol, 0L, 10L), error = TRUE) 271 | expect_snapshot(oc_check_between(symbol, 11.0001, 11.0002), error = TRUE) 272 | expect_silent(oc_check_between(symbol, 0L, 11L)) 273 | 274 | # non-numeric 275 | symbol <- "11" 276 | expect_snapshot(oc_check_between(symbol, 0, 11), error = TRUE) 277 | 278 | symbol <- "TRUE" 279 | expect_snapshot(oc_check_between(symbol, 0, 11), error = TRUE) 280 | }) 281 | -------------------------------------------------------------------------------- /R/deprecated.R: -------------------------------------------------------------------------------- 1 | #' Deprecated functions in opencage 2 | #' 3 | #' These functions still work but will be removed (defunct) in the next version. 4 | #' 5 | #' \itemize{ 6 | #' \item [opencage_forward()] 7 | #' \item [opencage_reverse()] 8 | #' \item [opencage_key()] 9 | #' } 10 | #' 11 | #' @name opencage-deprecated 12 | NULL 13 | 14 | #' Forward geocoding 15 | #' 16 | #' @description 17 | #' 18 | #' `r lifecycle::badge("deprecated")` 19 | #' 20 | #' Deprecated: use `oc_forward` or `oc_forward_df` for forward geocoding. 21 | #' 22 | #' @param key Your OpenCage API key as a character vector of length one. By 23 | #' default, [opencage_key()] will attempt to retrieve the key from the 24 | #' environment variable `OPENCAGE_KEY`. 25 | #' @param no_record Logical vector of length one (default `FALSE`), when `TRUE` 26 | #' no log entry of the query is created, and the geocoding request is not 27 | #' cached by OpenCage. 28 | #' @inheritParams oc_forward 29 | #' 30 | #' @return A list with 31 | #' \itemize{ 32 | #' \item results as a tibble with one line per result, 33 | #' \item the number of results as an integer, 34 | #' \item the timestamp as a POSIXct object, 35 | #' \item rate_info tibble/data.frame with the maximal number of API calls per 36 | #' day for the used key, the number of remaining calls for the day and the time 37 | #' at which the number of remaining calls will be reset. 38 | #' } 39 | #' 40 | #' @export 41 | #' 42 | #' @examplesIf oc_key_present() && oc_api_ok() 43 | #' opencage_forward(placename = "Sarzeau") 44 | #' opencage_forward(placename = "Islington, London") 45 | #' opencage_forward(placename = "Triererstr 15, 46 | #' Weimar 99423, 47 | #' Deutschland") 48 | #' 49 | opencage_forward <- 50 | function(placename, 51 | key = opencage_key(), 52 | bounds = NULL, 53 | countrycode = NULL, 54 | language = NULL, 55 | limit = 10L, 56 | min_confidence = NULL, 57 | no_annotations = FALSE, 58 | no_dedupe = FALSE, 59 | no_record = FALSE, 60 | abbrv = FALSE, 61 | add_request = TRUE) { 62 | if (length(placename) > 1) { 63 | stop( 64 | call. = FALSE, 65 | "`opencage_forward` is not vectorised; use `oc_forward` instead." 66 | ) 67 | } 68 | 69 | lifecycle::deprecate_warn("0.2.0", "opencage_forward()", "oc_forward()") 70 | 71 | # set key and no_record option locally, 72 | # i.e. go back to default after function is finished 73 | withr::local_envvar(list("OPENCAGE_KEY" = key)) 74 | withr::local_options(list(oc_no_record = no_record)) 75 | 76 | lst <- oc_forward( 77 | placename = placename, 78 | return = "json_list", 79 | bounds = list(bounds), 80 | countrycode = countrycode, 81 | language = language, 82 | limit = limit, 83 | min_confidence = min_confidence, 84 | no_annotations = no_annotations, 85 | no_dedupe = no_dedupe, 86 | abbrv = abbrv, 87 | add_request = add_request 88 | ) 89 | lst <- lst[[1]] 90 | opencage_format(lst) 91 | } 92 | 93 | 94 | #' Reverse geocoding 95 | #' 96 | #' @description 97 | #' 98 | #' `r lifecycle::badge("deprecated")` 99 | #' 100 | #' Deprecated: use `oc_reverse` or `oc_reverse_df` for reverse geocoding. 101 | #' 102 | #' @param bounds Bounding box, ignored for reverse geocoding. 103 | #' @param countrycode Country code, ignored for reverse geocoding. 104 | #' @param limit How many results should be returned (1-100), ignored for reverse 105 | #' geocoding. 106 | #' @inheritParams oc_reverse 107 | #' @inheritParams opencage_forward 108 | #' @inherit opencage_forward return 109 | #' 110 | #' @export 111 | #' 112 | #' @examplesIf oc_key_present() && oc_api_ok() 113 | #' 114 | #' opencage_reverse( 115 | #' latitude = 0, longitude = 0, 116 | #' limit = 2 117 | #' ) 118 | #' 119 | opencage_reverse <- 120 | function(latitude, 121 | longitude, 122 | key = opencage_key(), 123 | bounds = NULL, 124 | countrycode = NULL, 125 | language = NULL, 126 | limit = 10, 127 | min_confidence = NULL, 128 | no_annotations = FALSE, 129 | no_dedupe = FALSE, 130 | no_record = FALSE, 131 | abbrv = FALSE, 132 | add_request = TRUE) { 133 | if (length(latitude) > 1) { 134 | stop( 135 | call. = FALSE, 136 | "`opencage_reverse` is not vectorised, use `oc_reverse` instead." 137 | ) 138 | } 139 | 140 | lifecycle::deprecate_warn("0.2.0", "opencage_reverse()", "oc_reverse()") 141 | 142 | # set key and no_record option locally, 143 | # i.e. go back to default after function is finished 144 | withr::local_envvar(list("OPENCAGE_KEY" = key)) 145 | withr::local_options(list(oc_no_record = no_record)) 146 | 147 | lst <- oc_reverse( 148 | latitude = latitude, 149 | longitude = longitude, 150 | return = "json_list", 151 | language = language, 152 | limit = limit, 153 | min_confidence = min_confidence, 154 | no_annotations = no_annotations, 155 | no_dedupe = no_dedupe, 156 | abbrv = abbrv, 157 | add_request = add_request 158 | ) 159 | lst <- lst[[1]] 160 | opencage_format(lst) 161 | } 162 | 163 | # format to "old" style (version <= 0.1.4) 164 | # for opencage_forward, opencage_reverse 165 | opencage_format <- function(lst) { 166 | no_results <- lst[["total_results"]] 167 | if (no_results > 0) { 168 | results <- lapply(lst[["results"]], unlist) 169 | results <- lapply(results, as.data.frame) 170 | results <- lapply(results, t) 171 | results <- lapply(results, as.data.frame, stringsAsFactors = FALSE) 172 | results <- suppressWarnings(dplyr::bind_rows(results)) 173 | results$"geometry.lat" <- as.numeric(results$"geometry.lat") 174 | results$"geometry.lng" <- as.numeric(results$"geometry.lng") 175 | 176 | # if requests exists in the api response add the query to results 177 | if ("request" %in% names(lst)) { 178 | results$query <- as.character(lst$request$query) 179 | } 180 | } else { 181 | results <- NULL 182 | } 183 | 184 | if (!is.null(lst$rate)) { 185 | rate_info <- tibble::as_tibble(data.frame( 186 | limit = lst$rate$limit, 187 | remaining = lst$rate$remaining, 188 | reset = as.POSIXct(lst$rate$reset, origin = "1970-01-01") 189 | )) 190 | } else { 191 | rate_info <- NULL 192 | } 193 | 194 | if (!is.null(results)) { 195 | results <- tibble::as_tibble(results) 196 | } 197 | 198 | list( 199 | results = results, 200 | total_results = no_results, 201 | time_stamp = as.POSIXct( 202 | lst$timestamp$created_unix, 203 | origin = "1970-01-01" 204 | ), 205 | rate_info = rate_info 206 | ) 207 | } 208 | 209 | #' Retrieve Opencage API key 210 | #' 211 | #' @description 212 | #' 213 | #' `r lifecycle::badge("deprecated")` 214 | #' 215 | #' Deprecated and will be removed from the package together with 216 | #' `opencage_forward()` and `opencage_reverse()`. 217 | #' 218 | #' Retrieves the OpenCage API Key from the environment variable `OPENCAGE_KEY`. 219 | #' 220 | #' @param quiet Logical vector of length one indicating whether the key is 221 | #' returned quietly or whether a message is printed. 222 | #' @keywords internal 223 | #' @export 224 | opencage_key <- function(quiet = TRUE) { 225 | lifecycle::deprecate_warn("0.2.0", "opencage_key()") 226 | 227 | pat <- Sys.getenv("OPENCAGE_KEY") 228 | 229 | if (identical(pat, "")) { 230 | return(NULL) 231 | } 232 | 233 | if (!quiet) { 234 | message("Using OpenCage API Key from envvar OPENCAGE_KEY") 235 | } 236 | 237 | invisible(pat) 238 | } 239 | -------------------------------------------------------------------------------- /man/oc_forward.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/oc_forward.R 3 | \name{oc_forward} 4 | \alias{oc_forward} 5 | \title{Forward geocoding} 6 | \usage{ 7 | oc_forward( 8 | placename, 9 | return = c("df_list", "json_list", "geojson_list", "url_only"), 10 | bounds = NULL, 11 | proximity = NULL, 12 | countrycode = NULL, 13 | language = NULL, 14 | limit = 10L, 15 | min_confidence = NULL, 16 | no_annotations = TRUE, 17 | roadinfo = FALSE, 18 | no_dedupe = FALSE, 19 | abbrv = FALSE, 20 | address_only = FALSE, 21 | add_request = FALSE, 22 | ... 23 | ) 24 | } 25 | \arguments{ 26 | \item{placename}{A character vector with the location names or addresses to 27 | be geocoded. 28 | 29 | If the locations are addresses, see \href{https://github.com/OpenCageData/opencagedata-misc-docs/blob/master/query-formatting.md}{OpenCage's instructions} 30 | on how to format addresses for best forward geocoding results.} 31 | 32 | \item{return}{A character vector of length one indicating the return value of 33 | the function, either a list of tibbles (\code{df_list}, the default), a JSON 34 | list (\code{json_list}), a GeoJSON list (\code{geojson_list}), or the URL with which 35 | the API would be called (\code{url_only}).} 36 | 37 | \item{bounds}{A list of bounding boxes of length one or \code{length(placename)}. 38 | Bounding boxes are named numeric vectors, each with four coordinates 39 | forming the south-west and north-east corners of the bounding box: 40 | \code{list(c(xmin, ymin, xmax, ymax))}. \code{bounds} restricts the possible results 41 | to the supplied region. It can be specified with the \code{\link[=oc_bbox]{oc_bbox()}} helper. 42 | For example: \code{bounds = oc_bbox(-0.563160, 51.280430, 0.278970, 51.683979)}. 43 | Default is \code{NULL}.} 44 | 45 | \item{proximity}{A list of points of length one or \code{length(placename)}. A 46 | point is a named numeric vector of a latitude, longitude coordinate pair in 47 | decimal format. \code{proximity} provides OpenCage with a hint to bias results 48 | in favour of those closer to the specified location. It can be specified 49 | with the \code{\link[=oc_points]{oc_points()}} helper. For example: \code{proximity = oc_points(51.9526, 7.6324)}. Default is \code{NULL}.} 50 | 51 | \item{countrycode}{A two letter code as defined by the \href{https://www.iso.org/obp/ui/#search/code}{ISO 3166-1 Alpha 2} standard that restricts the 52 | results to the given country or countries. E.g. "AR" for Argentina, "FR" 53 | for France, "NZ" for the New Zealand. Multiple countrycodes per \code{placename} 54 | must be wrapped in a list. Default is \code{NULL}.} 55 | 56 | \item{language}{An \href{https://en.wikipedia.org/wiki/IETF_language_tag}{IETF BCP 47 language tag} (such as "es" for 57 | Spanish or "pt-BR" for Brazilian Portuguese). OpenCage will attempt to 58 | return results in that language. Alternatively you can specify the "native" 59 | tag, in which case OpenCage will attempt to return the response in the 60 | "official" language(s). In case the \code{language} parameter is set to \code{NULL} 61 | (which is the default), the tag is not recognized, or OpenCage does not 62 | have a record in that language, the results will be returned in English.} 63 | 64 | \item{limit}{Numeric vector of integer values to determine the maximum number 65 | of results returned for each \code{placename}. Integer values between 1 and 100 66 | are allowed. Default is 10.} 67 | 68 | \item{min_confidence}{Numeric vector of integer values between 0 and 10 69 | indicating the precision of the returned result as defined by its 70 | geographical extent, (i.e. by the extent of the result's bounding box). See 71 | the \href{https://opencagedata.com/api#confidence}{API documentation} for 72 | details. Only results with at least the requested confidence will be 73 | returned. Default is \code{NULL}.} 74 | 75 | \item{no_annotations}{Logical vector indicating whether additional 76 | information about the result location should be returned. \code{TRUE} by 77 | default, which means that the results will not contain annotations.} 78 | 79 | \item{roadinfo}{Logical vector indicating whether the geocoder should attempt 80 | to match the nearest road (rather than an address) and provide additional 81 | road and driving information. Default is \code{FALSE}.} 82 | 83 | \item{no_dedupe}{Logical vector (default \code{FALSE}), when \code{TRUE} the results 84 | will not be deduplicated.} 85 | 86 | \item{abbrv}{Logical vector (default \code{FALSE}), when \code{TRUE} addresses in the 87 | \code{formatted} field of the results are abbreviated (e.g. "Main St." instead 88 | of "Main Street").} 89 | 90 | \item{address_only}{Logical vector (default \code{FALSE}), when \code{TRUE} only the 91 | address details are returned in the \code{formatted} field of the results, not 92 | the name of a point-of-interest should there be one at this address.} 93 | 94 | \item{add_request}{Logical vector (default \code{FALSE}) indicating whether the 95 | request is returned again with the results. If the \code{return} value is a 96 | \code{df_list}, the query text is added as a column to the results. \code{json_list} 97 | results will contain all request parameters, including the API key used! 98 | This is currently ignored by OpenCage if return value is \code{geojson_list}.} 99 | 100 | \item{...}{Ignored.} 101 | } 102 | \value{ 103 | Depending on the \code{return} argument, \code{oc_forward} returns a list with 104 | either 105 | \itemize{ 106 | \item the results as tibbles (\code{"df_list"}, the default), 107 | \item the results as JSON specified as a list (\code{"json_list"}), 108 | \item the results as GeoJSON specified as a list (\code{"geojson_list"}), 109 | or 110 | \item the URL of the OpenCage API call for debugging purposes 111 | (\code{"url_only"}). 112 | } 113 | 114 | When the results are returned as (a list of) tibbles, the column names 115 | coming from the OpenCage API are prefixed with \code{"oc_"}. 116 | } 117 | \description{ 118 | Forward geocoding from a character vector of location names to latitude and 119 | longitude tuples. 120 | } 121 | \examples{ 122 | \dontshow{if (oc_key_present() && oc_api_ok()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 123 | 124 | # Geocode a single location, an address in this case 125 | oc_forward(placename = "Triererstr 15, 99432, Weimar, Deutschland") 126 | 127 | # Geocode multiple locations 128 | locations <- c("Nantes", "Hamburg", "Los Angeles") 129 | oc_forward(placename = locations) 130 | 131 | # Use bounding box to help return accurate results 132 | # for each placename 133 | bounds <- oc_bbox( 134 | xmin = c(-2, 9, -119), 135 | ymin = c(47, 53, 34), 136 | xmax = c(0, 10, -117), 137 | ymax = c(48, 54, 35) 138 | ) 139 | oc_forward(placename = locations, bounds = bounds) 140 | 141 | # Another way to help specify the desired results 142 | # is with country codes. 143 | oc_forward( 144 | placename = locations, 145 | countrycode = c("ca", "us", "co") 146 | ) 147 | 148 | # With multiple countrycodes per placename 149 | oc_forward( 150 | placename = locations, 151 | countrycode = list(c("fr", "ca"), c("de", "us"), c("us", "co")) 152 | ) 153 | 154 | # Return results in a preferred language if possible 155 | oc_forward( 156 | placename = c("Brugge", "Mechelen", "Antwerp"), 157 | language = "fr" 158 | ) 159 | 160 | # Limit the number of results per placename and return json_list 161 | oc_forward( 162 | placename = locations, 163 | bounds = bounds, 164 | limit = 1, 165 | return = "json_list" 166 | ) 167 | \dontshow{\}) # examplesIf} 168 | } 169 | \seealso{ 170 | \code{\link[=oc_forward_df]{oc_forward_df()}} for inputs as a data frame, or \code{\link[=oc_reverse]{oc_reverse()}} and 171 | \code{\link[=oc_reverse_df]{oc_reverse_df()}} for reverse geocoding. For more information about the API 172 | and the various parameters, see the \href{https://opencagedata.com/api}{OpenCage API documentation}. 173 | } 174 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # opencage (development version) 2 | 3 | * opencage now supports an `address_only` parameter, see "[New optional API parameter 'address_only'](https://blog.opencagedata.com/post/new-optional-parameter-addressonly)", ([#151](https://github.com/ropensci/opencage/pull/151)). 4 | * The geocoding functions will not send a query to the API anymore if no API key is present ([#133](https://github.com/ropensci/opencage/issues/133)). 5 | * `NA`s are allowed again for the `placename` or `latitude`/`longitude` arguments (also empty strings for `placename`). 6 | These queries are not sent to the API. 7 | An empty/`NA` response will be returned depending on the specific query ([#143](https://github.com/ropensci/opencage/issues/143)). 8 | * `opencage_forward()`, `opencage_reverse()`, and `opencage_key()` are now deprecated (leveling up from soft-deprecated, [#147](https://github.com/ropensci/opencage/pull/147)). 9 | 10 | ## Internals 11 | 12 | * {opencage} now uses [{testthat} 3e](https://testthat.r-lib.org/articles/third-edition.html) for unit tests ([#141](https://github.com/ropensci/opencage/issues/141)). 13 | * Use [{lintr} version 3.0](https://www.tidyverse.org/blog/2022/07/lintr-3-0-0/) and add "package development" linters ([#144](https://github.com/ropensci/opencage/pull/144)). 14 | * `countrycodes` source and script were moved to `data-raw` ([#146](https://github.com/ropensci/opencage/pull/146)). 15 | * Add CITATION.cff and a corresponding GitHub action ([#148](https://github.com/ropensci/opencage/pull/148)). 16 | * Select expressions inside `oc_forward_df()` and `oc_reverse_df()` now use `"column"` instead of `.data$column`, because the latter is [deprecated as of tidyselect v1.2.0](https://tidyselect.r-lib.org/news/index.html#tidyselect-120) ([#150](https://github.com/ropensci/opencage/pull/150)). 17 | * The opencage code now uses a consistent style (`styler::tidyverse_style()`) and all internal functions are documented ([#153](https://github.com/ropensci/opencage/pull/153)). 18 | * GitHub action workflows have been updated ([#142](https://github.com/ropensci/opencage/issues/142), [#149](https://github.com/ropensci/opencage/pull/149), [#152](https://github.com/ropensci/opencage/pull/152)), [#164](https://github.com/ropensci/opencage/pull/164)). 19 | * Styler, document, and rhub GitHub action workflows have been added ([#153](https://github.com/ropensci/opencage/pull/153), [#164](https://github.com/ropensci/opencage/pull/164)). 20 | * Tests are updated to reflect changes in API responses ([#163](https://github.com/ropensci/opencage/pull/163), [#165](https://github.com/ropensci/opencage/pull/165)) 21 | 22 | # opencage 0.2.2 23 | 24 | * Fixed a test that caused an error on CRAN's Solaris ([#131](https://github.com/ropensci/opencage/issues/131)). 25 | 26 | # opencage 0.2.1 27 | 28 | This is a major rewrite of the {opencage} package. `opencage_forward()` and `opencage_reverse()` have been deprecated and are superseded by `oc_forward()` and `oc_reverse()`, respectively. In addition there are two new functions `oc_forward_df()` and `oc_reverse_df()`, which geocode place names or addresses into geographic coordinates (latitude and longitude) or vice versa, and return a data frame. 29 | 30 | The new features include: 31 | 32 | * `oc_forward()` and `oc_reverse()` return either lists of data frames, JSON strings, GeoJSON strings, or URLs to be sent to the API (the latter for debugging purposes). 33 | * `oc_forward_df()` and `oc_reverse_df()` take a data frame or vectors as input and return a data frame with the geocoding results, optionally with the source data frame bound to the results data frame. 34 | * Almost all arguments of the geocoding functions are vectorised (the exceptions being `output`), so it is possible to serially (reverse) geocode lists of locations or coordinates. The geocoding functions show a progress indicator when more than one `placename` or `latitude`/`longitude` pair is provided. 35 | * The forward geocoding functions now support multiple `countrycode`s in accordance with the OpenCage API ([#44](https://github.com/ropensci/opencage/issues/44)). The `countrycode`s can now be provided in upper or lower case ([#47](https://github.com/ropensci/opencage/issues/47)). 36 | * A helper function `oc_bbox()` now makes it easier to create a list of bounding boxes from numeric vectors, bbox objects or data frames. 37 | * `oc_forward()` and `oc_forward_df()` now support [OpenCage's `proximity` parameter](https://blog.opencagedata.com/post/new-optional-parameter-proximity). The results of the geocoding request will be biased towards that location ([#60](https://github.com/ropensci/opencage/issues/60)). 38 | * A helper function `oc_points()` now makes it easier to create a list of point coordinates from numeric vectors or data frames to pass to the `proximity` argument for example. 39 | * All geocoding functions now support [OpenCage's `roadinfo` parameter](https://blog.opencagedata.com/post/new-optional-parameter-roadinfo) ([#65](https://github.com/ropensci/opencage/issues/65)). If set to `TRUE`, OpenCage attempts to match the nearest road (rather than an address) and provides additional road and driving information. 40 | * Language tags passed to the `language` argument are not validated anymore, since the language tags used by OpenStreetMap and hence OpenCage do not always conform with the IETF BCP 47 standard ([#90](https://github.com/ropensci/opencage/issues/90)). The `languagecodes`, which were stored in {opencage} as external data, have therefore been omitted from the package. In addition, it is now possible to specify `language = "native"`, so OpenCage will attempt to return the [results in the "official" language](https://blog.opencagedata.com/post/support-for-local-language) of the country. 41 | * http requests are now handled by {[crul](https://docs.ropensci.org/crul/)}, not {[httr](https://httr.r-lib.org)} ([#37](https://github.com/ropensci/opencage/issues/37)). 42 | * API calls are now rate limited ([#32](https://github.com/ropensci/opencage/issues/32)). The default limit is set to 1 call per second as per the API limit of the [Free Trial plan](https://opencagedata.com/pricing). 43 | * {opencage} settings like the OpenCage API key or the API rate limit can be configured with `oc_config()`. If you want OpenCage to have no record of the contents of your queries, you can also set the `no_record` parameter for the active R session with `oc_config()` (as opposed to providing the parameter with each function call). All `oc_config()` settings can be set more permanently via `options()` or environment variables, see `help(oc_config)`. 44 | 45 | ## Breaking changes 46 | 47 | * `opencage_forward()`, `opencage_reverse()`, and `opencage_key()` are [soft-deprecated](https://lifecycle.r-lib.org/reference/deprecate_soft.html). 48 | * `opencage_forward()` and `opencage_reverse()` will always output strings as characters, i.e. they won't coerce to factor depending on the `stringsAsFactor` option. 49 | * `opencage_key()` returns the OpenCage API key invisibly. 50 | * `NA` values are not allowed anymore for the `placename` or `latitude`/`longitude` arguments, because OpenCage throws a HTTP 400 ‘bad query’ error when the query is empty ([#98](https://github.com/ropensci/opencage/issues/98)). 51 | 52 | ## Minor changes 53 | 54 | * The column name for `countrycodes` is now `code`, not `Code`. 55 | * HTTP error messages are now returned directly from the API and are therefore always up-to-date. The previously used responses in `code_message`, which were stored in {opencage} as external data, have been deleted. For more information on OpenCage's HTTP status codes see https://opencagedata.com/api#codes. 56 | * Fixed two URLs, one of which was rejected on the v0.2.0 submission. 57 | 58 | # opencage 0.1.4 59 | 60 | * Bug fix: now the `countrycode` argument can be used for Namibia ([#24](https://github.com/ropensci/opencage/issues/24), [#25](https://github.com/ropensci/opencage/issues/25)). 61 | 62 | # opencage 0.1.3 63 | 64 | * Added a `add_request` parameter (for appending original query to results). 65 | 66 | # opencage 0.1.2 67 | 68 | * Added a `abbrv` parameter, see https://blog.opencagedata.com/post/160294347883/shrtr-pls. 69 | 70 | # opencage 0.1.1 71 | 72 | * Added a `no_record` parameter, see https://blog.opencagedata.com/post/145602604628/more-privacy-with-norecord-parameter 73 | 74 | # opencage 0.1.0 75 | 76 | * Added a `NEWS.md` file to track changes to the package. 77 | -------------------------------------------------------------------------------- /tests/testthat/test-oc_reverse.R: -------------------------------------------------------------------------------- 1 | # Test oc_reverse functions ----------------------------------------------- 2 | 3 | # oc_reverse -------------------------------------------------------------- 4 | 5 | test_that("oc_reverse works", { 6 | skip_if_no_key() 7 | skip_if_oc_offline() 8 | 9 | res1 <- oc_reverse(oc_lat1(), oc_lng1()) 10 | expect_type(res1, "list") 11 | expect_length(res1, 3L) 12 | expect_s3_class(res1[[1]], c("tbl_df", "tbl", "data.frame")) 13 | }) 14 | 15 | test_that("oc_reverse returns correct type", { 16 | skip_if_no_key() 17 | skip_if_oc_offline() 18 | 19 | # json_list 20 | res2 <- oc_reverse(oc_lat1(), oc_lng1(), return = "json_list") 21 | expect_type(res2, "list") 22 | expect_length(res2, 3L) 23 | expect_type(res2[[1]], "list") 24 | 25 | # geojson_list 26 | res3 <- oc_reverse(oc_lat1(), oc_lng1(), return = "geojson_list") 27 | expect_type(res3, "list") 28 | expect_length(res3, 3L) 29 | expect_s3_class(res3[[1]], "geo_list") 30 | }) 31 | 32 | test_that("oc_reverse adds request with add_request", { 33 | skip_if_no_key() 34 | skip_if_oc_offline() 35 | 36 | expected <- paste(oc_lat1()[1], oc_lng1()[1], sep = ",") 37 | 38 | # df_list 39 | res <- 40 | oc_reverse( 41 | oc_lat1()[1], 42 | oc_lng1()[1], 43 | return = "df_list", 44 | add_request = TRUE 45 | ) 46 | expect_identical(res[[1]][["oc_query"]], expected) 47 | 48 | # json_list 49 | res <- 50 | oc_reverse( 51 | oc_lat1()[1], 52 | oc_lng1()[1], 53 | return = "json_list", 54 | add_request = TRUE 55 | ) 56 | expect_identical(res[[1]][["request"]][["query"]], expected) 57 | }) 58 | 59 | test_that("oc_reverse masks key when add_request = TRUE", { 60 | skip_if_oc_offline() 61 | withr::local_envvar(c("OPENCAGE_KEY" = key_200)) 62 | 63 | res <- 64 | oc_reverse( 65 | oc_lat1()[1], 66 | oc_lng1()[1], 67 | return = "json_list", 68 | add_request = TRUE 69 | ) 70 | expect_identical(res[[1]][["request"]][["key"]], "OPENCAGE_KEY") 71 | }) 72 | 73 | test_that("oc_reverse handles NAs", { 74 | skip_if_no_key() 75 | skip_if_oc_offline() 76 | 77 | # df_list 78 | res1 <- oc_reverse(latitude = 0, longitude = NA_real_) 79 | expect_identical(res1[[1]][[1, "oc_lat"]], NA_real_) 80 | 81 | res2 <- oc_reverse(latitude = NA_real_, longitude = 0) 82 | expect_identical(res2[[1]][[1, "oc_lng"]], NA_real_) 83 | 84 | # json_list 85 | res3 <- oc_reverse(latitude = NA_real_, longitude = 0, return = "json_list") 86 | expect_identical(res3[[1]][["results"]], list()) 87 | 88 | # geojson_list 89 | res4 <- 90 | oc_reverse(latitude = NA_real_, longitude = 0, return = "geojson_list") 91 | expect_identical(res4[[1]][["features"]], list()) 92 | }) 93 | 94 | # oc_reverse_df ----------------------------------------------------------- 95 | 96 | test_that("oc_reverse_df works", { 97 | skip_if_no_key() 98 | skip_if_oc_offline() 99 | 100 | tbl1 <- oc_reverse_df(oc_rev1(), lat, lng) 101 | expect_s3_class(tbl1, c("tbl_df", "tbl", "data.frame")) 102 | expect_identical(nrow(tbl1), 3L) 103 | 104 | tbl2 <- oc_reverse_df(oc_rev1()[1, ], lat, lng) 105 | expect_s3_class(tbl2, c("tbl_df", "tbl", "data.frame")) 106 | expect_identical(nrow(tbl2), 1L) 107 | 108 | tbl3 <- oc_reverse_df(oc_lat1(), oc_lng1()) 109 | expect_s3_class(tbl3, c("tbl_df", "tbl", "data.frame")) 110 | expect_identical(nrow(tbl3), 3L) 111 | }) 112 | 113 | test_that("oc_reverse_df works with NA", { 114 | skip_if_no_key() 115 | skip_if_oc_offline() 116 | 117 | lt <- c(0, NA_real_) 118 | ln <- c(NA_real_, 0) 119 | 120 | tbl1 <- oc_reverse_df(lt, ln) 121 | 122 | expect_identical(nrow(tbl1), 2L) 123 | expect_identical(tbl1$latitude, lt) 124 | expect_identical(tbl1$longitude, ln) 125 | expect_true(all(is.na(tbl1$oc_formatted))) 126 | 127 | tbl2 <- oc_reverse_df(data.frame(lt_col = lt, ln_col = ln), lt_col, ln_col) 128 | 129 | expect_identical(nrow(tbl2), 2L) 130 | expect_identical(tbl2$lt_col, lt) 131 | expect_identical(tbl2$ln_col, ln) 132 | expect_true(all(is.na(tbl2$oc_formatted))) 133 | 134 | tbl3 <- 135 | oc_reverse_df( 136 | data.frame(lt_col = lt, ln_col = ln), 137 | lt_col, 138 | ln_col, 139 | bind_cols = FALSE 140 | ) 141 | 142 | expect_identical(nrow(tbl3), 2L) 143 | expect_true( 144 | all( 145 | is.na(tbl3$oc_query), 146 | is.na(tbl3$oc_formatted) 147 | ) 148 | ) 149 | }) 150 | 151 | test_that("oc_reverse_df doesn't work for default class", { 152 | expect_error( 153 | oc_reverse_df("Hamburg"), 154 | "Can't geocode an object of class `character`." 155 | ) 156 | }) 157 | 158 | test_that("output arguments work", { 159 | skip_if_no_key() 160 | skip_if_oc_offline() 161 | 162 | expect_named( 163 | oc_reverse_df(oc_rev1(), lat, lng, bind_cols = TRUE), 164 | c("id", "lat", "lng", "oc_formatted") 165 | ) 166 | expect_named( 167 | oc_reverse_df(oc_rev1(), lat, lng, bind_cols = FALSE), 168 | c("oc_query", "oc_formatted") 169 | ) 170 | expect_gt(ncol(oc_reverse_df(oc_rev1(), lat, lng, output = "all")), 5L) 171 | expect_gt( 172 | ncol(oc_reverse_df(oc_rev1(), lat, lng, bind_cols = FALSE, output = "all")), 173 | 5L 174 | ) 175 | }) 176 | 177 | test_that("tidyeval works for arguments", { 178 | skip_if_no_key() 179 | skip_if_oc_offline() 180 | 181 | noarg <- oc_reverse_df(oc_rev2(), lat, lng) 182 | 183 | # language 184 | lang <- 185 | oc_reverse_df(oc_rev2(), lat, lng, language = language, output = "all") 186 | expect_identical(lang$oc_country, c("Netherlands", "Allemagne", "アメリカ合衆国")) 187 | 188 | # no_annotations 189 | ann <- 190 | oc_reverse_df( 191 | oc_rev2(), 192 | lat, 193 | lng, 194 | bind_cols = FALSE, 195 | no_annotations = annotation 196 | ) 197 | expect_gt(ncol(ann), 40) 198 | expect_identical(ann$oc_currency_name, c("Euro", NA, NA)) 199 | 200 | # roadinfo 201 | ri <- oc_reverse_df( 202 | oc_rev2(), 203 | lat, 204 | lng, 205 | bind_cols = FALSE, 206 | roadinfo = roadinfo 207 | ) 208 | expect_identical(ri$oc_roadinfo_speed_in, c(NA_character_, "km/h", "mph")) 209 | 210 | # abbrv 211 | abbrv <- oc_reverse_df(oc_rev2(), lat, lng, abbrv = abbrv) 212 | expect_identical(abbrv[[1, "oc_formatted"]], noarg[[1, "oc_formatted"]]) 213 | expect_identical(abbrv[[2, "oc_formatted"]], noarg[[2, "oc_formatted"]]) 214 | expect_false(identical( 215 | abbrv[[3, "oc_formatted"]], 216 | noarg[[3, "oc_formatted"]] 217 | )) 218 | 219 | # address_only 220 | address_only <- 221 | oc_reverse_df(oc_rev2(), lat, lng, address_only = address_only) 222 | expect_false(identical( 223 | address_only["oc_formatted"], 224 | noarg["oc_formatted"] 225 | )) 226 | expect_false(identical( 227 | noarg[1, "oc_formatted"], 228 | address_only[1, "oc_formatted"] 229 | )) 230 | expect_false(identical( 231 | noarg[2, "oc_formatted"], 232 | address_only[2, "oc_formatted"] 233 | )) 234 | expect_match( 235 | noarg[[1, "oc_formatted"]], 236 | address_only[[1, "oc_formatted"]], 237 | fixed = TRUE 238 | ) 239 | expect_match( 240 | noarg[[2, "oc_formatted"]], 241 | address_only[[2, "oc_formatted"]], 242 | fixed = TRUE 243 | ) 244 | expect_identical(address_only[3, "oc_formatted"], noarg[3, "oc_formatted"]) 245 | }) 246 | 247 | # Checks ------------------------------------------------------------------ 248 | 249 | test_that("Check that latitude & longitude are present", { 250 | # oc_reverse 251 | expect_error( 252 | oc_reverse(latitude = oc_lat1()), 253 | "`latitude` and `longitude` must be provided." 254 | ) 255 | expect_error( 256 | oc_reverse(longitude = oc_lng1()), 257 | "`latitude` and `longitude` must be provided." 258 | ) 259 | expect_error( 260 | oc_reverse(latitude = NULL, longitude = oc_lng1()), 261 | "`latitude` and `longitude` must be provided." 262 | ) 263 | expect_error( 264 | oc_reverse(latitude = oc_lat1(), longitude = NULL), 265 | "`latitude` and `longitude` must be provided." 266 | ) 267 | 268 | # oc_reverse_df 269 | expect_error( 270 | oc_reverse_df(oc_rev1(), latitude = lat), 271 | "`latitude` and `longitude` must be provided." 272 | ) 273 | expect_error( 274 | oc_reverse_df(oc_rev1(), longitude = lng), 275 | "`latitude` and `longitude` must be provided." 276 | ) 277 | }) 278 | -------------------------------------------------------------------------------- /man/oc_forward_df.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/oc_forward.R 3 | \name{oc_forward_df} 4 | \alias{oc_forward_df} 5 | \alias{oc_forward_df.data.frame} 6 | \alias{oc_forward_df.character} 7 | \title{Forward geocoding with data frames} 8 | \usage{ 9 | oc_forward_df(...) 10 | 11 | \method{oc_forward_df}{data.frame}( 12 | data, 13 | placename, 14 | bind_cols = TRUE, 15 | output = c("short", "all"), 16 | bounds = NULL, 17 | proximity = NULL, 18 | countrycode = NULL, 19 | language = NULL, 20 | limit = 1L, 21 | min_confidence = NULL, 22 | no_annotations = TRUE, 23 | roadinfo = FALSE, 24 | no_dedupe = FALSE, 25 | abbrv = FALSE, 26 | address_only = FALSE, 27 | ... 28 | ) 29 | 30 | \method{oc_forward_df}{character}( 31 | placename, 32 | output = c("short", "all"), 33 | bounds = NULL, 34 | proximity = NULL, 35 | countrycode = NULL, 36 | language = NULL, 37 | limit = 1L, 38 | min_confidence = NULL, 39 | no_annotations = TRUE, 40 | roadinfo = FALSE, 41 | no_dedupe = FALSE, 42 | abbrv = FALSE, 43 | address_only = FALSE, 44 | ... 45 | ) 46 | } 47 | \arguments{ 48 | \item{...}{Ignored.} 49 | 50 | \item{data}{A data frame.} 51 | 52 | \item{placename}{An unquoted variable name of a character column or vector 53 | with the location names or addresses to be geocoded. 54 | 55 | If the locations are addresses, see \href{https://github.com/OpenCageData/opencagedata-misc-docs/blob/master/query-formatting.md}{OpenCage's instructions} 56 | on how to format addresses for best forward geocoding results.} 57 | 58 | \item{bind_cols}{When \code{bind_col = TRUE}, the default, the results are column 59 | bound to \code{data}. When \code{FALSE}, the results are returned as a new tibble.} 60 | 61 | \item{output}{A character vector of length one indicating whether only 62 | latitude, longitude, and formatted address variables (\code{"short"}, the 63 | default), or all variables (\code{"all"}) variables should be returned.} 64 | 65 | \item{bounds}{A list of length one, or an unquoted variable name of a list 66 | column of bounding boxes. Bounding boxes are named numeric vectors, each 67 | with 4 coordinates forming the south-west and north-east corners of the 68 | bounding box: \code{list(c(xmin, ymin, xmax, ymax))}. \code{bounds} restricts the 69 | possible results to the supplied region. It can be specified with the 70 | \code{\link[=oc_bbox]{oc_bbox()}} helper. For example: \code{bounds = oc_bbox(-0.563160, 51.280430, 0.278970, 51.683979)}. Default is \code{NULL}.} 71 | 72 | \item{proximity}{A list of length one, or an unquoted variable name of a list 73 | column of points. Points are named numeric vectors with latitude, longitude 74 | coordinate pairs in decimal format. \code{proximity} provides OpenCage with a 75 | hint to bias results in favour of those closer to the specified location. 76 | It can be specified with the \code{\link[=oc_points]{oc_points()}} helper. For example: \code{proximity = oc_points(41.40139, 2.12870)}. Default is \code{NULL}.} 77 | 78 | \item{countrycode}{Character vector, or an unquoted variable name of such a 79 | vector, of two-letter codes as defined by the \href{https://www.iso.org/obp/ui/#search/code}{ISO 3166-1 Alpha 2} standard that restricts the 80 | results to the given country or countries. E.g. "AR" for Argentina, "FR" 81 | for France, "NZ" for the New Zealand. Multiple countrycodes per \code{placename} 82 | must be wrapped in a list. Default is \code{NULL}.} 83 | 84 | \item{language}{Character vector, or an unquoted variable name of such a 85 | vector, of \href{https://en.wikipedia.org/wiki/IETF_language_tag}{IETF BCP 47 language tags} (such as "es" for 86 | Spanish or "pt-BR" for Brazilian Portuguese). OpenCage will attempt to 87 | return results in that language. Alternatively you can specify the "native" 88 | tag, in which case OpenCage will attempt to return the response in the 89 | "official" language(s). In case the \code{language} parameter is set to \code{NULL} 90 | (which is the default), the tag is not recognized, or OpenCage does not 91 | have a record in that language, the results will be returned in English.} 92 | 93 | \item{limit}{Numeric vector of integer values, or an unquoted variable name 94 | of such a vector, to determine the maximum number of results returned for 95 | each \code{placename}. Integer values between 1 and 100 are allowed. Default is 96 | 1.} 97 | 98 | \item{min_confidence}{Numeric vector of integer values, or an unquoted 99 | variable name of such a vector, between 0 and 10 indicating the precision 100 | of the returned result as defined by its geographical extent, (i.e. by the 101 | extent of the result's bounding box). See the \href{https://opencagedata.com/api#confidence}{API documentation} for details. Only 102 | results with at least the requested confidence will be returned. Default is 103 | \code{NULL}).} 104 | 105 | \item{no_annotations}{Logical vector, or an unquoted variable name of such a 106 | vector, indicating whether additional information about the result location 107 | should be returned. \code{TRUE} by default, which means that the results will 108 | not contain annotations.} 109 | 110 | \item{roadinfo}{Logical vector, or an unquoted variable name of such a 111 | vector, indicating whether the geocoder should attempt to match the nearest 112 | road (rather than an address) and provide additional road and driving 113 | information. Default is \code{FALSE}.} 114 | 115 | \item{no_dedupe}{Logical vector, or an unquoted variable name of such a 116 | vector. Default is \code{FALSE}. When \code{TRUE} the results will not be 117 | deduplicated.} 118 | 119 | \item{abbrv}{Logical vector, or an unquoted variable name of such a vector. 120 | Default is \code{FALSE}. When \code{TRUE} addresses in the \code{oc_formatted} variable of 121 | the results are abbreviated (e.g. "Main St." instead of "Main Street").} 122 | 123 | \item{address_only}{Logical vector, or an unquoted variable name of such a 124 | vector. Default is \code{FALSE}. When \code{TRUE} only the address details are 125 | returned in the \code{oc_formatted} variable of the results, not the name of a 126 | point-of-interest should there be one at this address.} 127 | } 128 | \value{ 129 | A tibble. Column names coming from the OpenCage API are prefixed with 130 | \code{"oc_"}. 131 | } 132 | \description{ 133 | Forward geocoding from a column or vector of location names to latitude and 134 | longitude tuples. 135 | } 136 | \examples{ 137 | \dontshow{if (oc_key_present() && oc_api_ok()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 138 | 139 | library(tibble) 140 | df <- tibble( 141 | id = 1:3, 142 | locations = c("Nantes", "Hamburg", "Los Angeles") 143 | ) 144 | 145 | # Return lat, lng, and formatted address 146 | oc_forward_df(df, placename = locations) 147 | 148 | # Return more detailed information about the locations 149 | oc_forward_df(df, placename = locations, output = "all") 150 | 151 | # Do not column bind results to input data frame 152 | oc_forward_df(df, placename = locations, bind_cols = FALSE) 153 | 154 | # Add more results by changing the limit from the default of 1. 155 | oc_forward_df(df, placename = locations, limit = 5) 156 | 157 | # Restrict results to a given bounding box 158 | oc_forward_df(df, 159 | placename = locations, 160 | bounds = oc_bbox(-5, 45, 15, 55) 161 | ) 162 | 163 | # oc_forward_df accepts unquoted column names for all 164 | # arguments except bind_cols and output. 165 | # This makes it possible to build up more detailed queries 166 | # through the data frame passed to the data argument. 167 | 168 | df2 <- add_column(df, 169 | bounds = oc_bbox( 170 | xmin = c(-2, 9, -119), 171 | ymin = c(47, 53, 34), 172 | xmax = c(0, 10, -117), 173 | ymax = c(48, 54, 35) 174 | ), 175 | limit = 1:3, 176 | countrycode = c("ca", "us", "co"), 177 | language = c("fr", "de", "en") 178 | ) 179 | 180 | # Use the bounds column to help return accurate results and 181 | # language column to specify preferred language of results 182 | oc_forward_df(df2, 183 | placename = locations, 184 | bounds = bounds, 185 | language = language 186 | ) 187 | 188 | # Different limit of results for each placename 189 | oc_forward_df(df2, 190 | placename = locations, 191 | limit = limit 192 | ) 193 | 194 | # Specify the desired results by the countrycode column 195 | oc_forward_df(df2, 196 | placename = locations, 197 | countrycode = countrycode 198 | ) 199 | \dontshow{\}) # examplesIf} 200 | } 201 | \seealso{ 202 | \code{\link[=oc_forward]{oc_forward()}} for inputs as vectors, or \code{\link[=oc_reverse]{oc_reverse()}} and 203 | \code{\link[=oc_reverse_df]{oc_reverse_df()}} for reverse geocoding. For more information about the API 204 | and the various parameters, see the \href{https://opencagedata.com/api}{OpenCage API documentation}. 205 | } 206 | -------------------------------------------------------------------------------- /tests/testthat/test-oc_forward.R: -------------------------------------------------------------------------------- 1 | # Test oc_forward functions ----------------------------------------------- 2 | 3 | # oc_forward -------------------------------------------------------------- 4 | 5 | test_that("oc_forward works", { 6 | skip_if_no_key() 7 | skip_if_oc_offline() 8 | 9 | res1 <- oc_forward(oc_locs()) 10 | expect_type(res1, "list") 11 | expect_length(res1, 3L) 12 | expect_s3_class(res1[[1]], c("tbl_df", "tbl", "data.frame")) 13 | }) 14 | 15 | test_that("oc_forward returns correct type", { 16 | skip_if_no_key() 17 | skip_if_oc_offline() 18 | 19 | # json_list 20 | res2 <- oc_forward(oc_locs(), return = "json_list") 21 | expect_type(res2, "list") 22 | expect_length(res2, 3L) 23 | expect_type(res2[[1]], "list") 24 | 25 | # geojson_list 26 | res3 <- oc_forward(oc_locs(), return = "geojson_list") 27 | expect_type(res3, "list") 28 | expect_length(res3, 3L) 29 | expect_s3_class(res3[[1]], "geo_list") 30 | }) 31 | 32 | test_that("oc_forward adds request with add_request", { 33 | skip_if_no_key() 34 | skip_if_oc_offline() 35 | 36 | # df_list 37 | res <- oc_forward("Hmbg", return = "df_list", add_request = TRUE) 38 | expect_identical(res[[1]][["oc_query"]], "Hmbg") 39 | 40 | # json_list 41 | res <- oc_forward("Hmbg", return = "json_list", add_request = TRUE) 42 | expect_identical(res[[1]][["request"]][["query"]], "Hmbg") 43 | }) 44 | 45 | test_that("oc_forward masks key when add_request = TRUE", { 46 | skip_if_oc_offline() 47 | withr::local_envvar(c("OPENCAGE_KEY" = key_200)) 48 | 49 | # json_list 50 | res <- oc_forward("irrelevant", return = "json_list", add_request = TRUE) 51 | expect_identical(res[[1]][["request"]][["key"]], "OPENCAGE_KEY") 52 | }) 53 | 54 | test_that("oc_forward handles response with no results", { 55 | skip_if_no_key() 56 | skip_if_oc_offline() 57 | 58 | # https://opencagedata.com/api#no-results 59 | nores <- oc_forward("NOWHERE-INTERESTING") 60 | expect_type(nores, "list") 61 | expect_length(nores, 1L) 62 | expect_s3_class(nores[[1]], c("tbl_df", "tbl", "data.frame")) 63 | expect_identical(nores[[1]][[1, "oc_lat"]], NA_real_) 64 | }) 65 | 66 | test_that("oc_forward handles NAs", { 67 | skip_if_no_key() 68 | skip_if_oc_offline() 69 | 70 | # df_list 71 | res <- oc_forward(NA_character_) 72 | expect_identical(res[[1]][[1, "oc_lat"]], NA_real_) 73 | 74 | # json_list 75 | res2 <- oc_forward(NA_character_, return = "json_list") 76 | expect_identical(res2[[1]][["results"]], list()) 77 | 78 | # geojson_list 79 | res3 <- oc_forward(NA_character_, return = "geojson_list") 80 | expect_identical(res3[[1]][["features"]], list()) 81 | }) 82 | 83 | test_that("oc_forward handles empty strings", { 84 | skip_if_no_key() 85 | skip_if_oc_offline() 86 | 87 | res <- oc_forward("") 88 | expect_type(res, "list") 89 | expect_length(res, 1L) 90 | expect_s3_class(res[[1]], c("tbl_df", "tbl", "data.frame")) 91 | expect_identical(res[[1]][[1, "oc_lat"]], NA_real_) 92 | }) 93 | 94 | # oc_forward_df ----------------------------------------------------------- 95 | 96 | test_that("oc_forward_df works", { 97 | skip_if_no_key() 98 | skip_if_oc_offline() 99 | 100 | tbl1 <- oc_forward_df(oc_fw1(), loc) 101 | expect_s3_class(tbl1, c("tbl_df", "tbl", "data.frame")) 102 | expect_identical(nrow(tbl1), 3L) 103 | 104 | tbl2 <- oc_forward_df(data.frame(loc = "Nantes"), loc) 105 | expect_s3_class(tbl2, c("tbl_df", "tbl", "data.frame")) 106 | expect_identical(nrow(tbl2), 1L) 107 | 108 | tbl3 <- oc_forward_df(oc_locs()) 109 | expect_s3_class(tbl3, c("tbl_df", "tbl", "data.frame")) 110 | expect_identical(nrow(tbl3), 3L) 111 | }) 112 | 113 | test_that("oc_forward_df works with NA and empty strings", { 114 | skip_if_no_key() 115 | skip_if_oc_offline() 116 | 117 | q <- c(NA_character_, "") 118 | 119 | tbl1 <- oc_forward_df(q) 120 | 121 | expect_identical(nrow(tbl1), 2L) 122 | expect_identical(tbl1$placename, q) 123 | expect_true( 124 | all( 125 | is.na(tbl1$oc_formatted), 126 | is.na(tbl1$oc_lat), 127 | is.na(tbl1$oc_lng) 128 | ) 129 | ) 130 | 131 | tbl2 <- oc_forward_df(data.frame(q_col = q), q_col) 132 | 133 | expect_identical(nrow(tbl2), 2L) 134 | expect_identical(tbl2$q_col, q) 135 | expect_true( 136 | all( 137 | is.na(tbl2$oc_formatted), 138 | is.na(tbl2$oc_lat), 139 | is.na(tbl2$oc_lng) 140 | ) 141 | ) 142 | 143 | tbl3 <- oc_forward_df(data.frame(q_col = q), q_col, bind_cols = FALSE) 144 | 145 | expect_identical(nrow(tbl3), 2L) 146 | expect_identical(tbl3$oc_query, q) 147 | expect_true( 148 | all( 149 | is.na(tbl3$oc_formatted), 150 | is.na(tbl3$oc_lat), 151 | is.na(tbl3$oc_lng) 152 | ) 153 | ) 154 | }) 155 | 156 | test_that("oc_forward_df doesn't work for default class", { 157 | expect_error( 158 | oc_forward_df(53.6), 159 | "Can't geocode an object of class `numeric`." 160 | ) 161 | }) 162 | 163 | test_that("output arguments work", { 164 | skip_if_no_key() 165 | skip_if_oc_offline() 166 | 167 | expect_named( 168 | oc_forward_df(oc_fw1(), loc, bind_cols = TRUE), 169 | c("id", "loc", "oc_lat", "oc_lng", "oc_formatted") 170 | ) 171 | expect_named( 172 | oc_forward_df(oc_fw1(), loc, bind_cols = FALSE), 173 | c("oc_query", "oc_lat", "oc_lng", "oc_formatted") 174 | ) 175 | expect_gt(ncol(oc_forward_df(oc_fw1(), loc, output = "all")), 5) 176 | expect_gt( 177 | ncol(oc_forward_df(oc_fw1(), loc, bind_cols = FALSE, output = "all")), 178 | 5 179 | ) 180 | }) 181 | 182 | test_that("tidyeval works for arguments", { 183 | skip_if_no_key() 184 | skip_if_oc_offline() 185 | 186 | noarg <- oc_forward_df(oc_fw2(), loc, bind_cols = FALSE) 187 | 188 | ## bounds, proximity and countrycode 189 | bounds <- oc_forward_df(oc_fw2(), loc, bounds = bounds, bind_cols = FALSE) 190 | prx <- oc_forward_df(oc_fw2(), loc, proximity = proximity, bind_cols = FALSE) 191 | cc <- 192 | oc_forward_df(oc_fw2(), loc, countrycode = countrycode, bind_cols = FALSE) 193 | expect_false(identical(bounds, noarg)) 194 | expect_false(identical(prx, noarg)) 195 | expect_false(identical(cc, noarg)) 196 | expect_identical(bounds, prx) 197 | expect_identical(bounds, cc) 198 | 199 | # language 200 | lang <- oc_forward_df(oc_fw2(), loc, language = language, output = "all") 201 | expect_identical( 202 | lang$oc_country, 203 | c("Frankreich", "Allemagne", "アメリカ合衆国") 204 | ) 205 | 206 | # limit 207 | limit <- oc_forward_df(oc_fw2(), loc, limit = limit) 208 | expect_identical(nrow(limit), 6L) 209 | expect_identical(limit$id, c(1L, 2L, 2L, 3L, 3L, 3L)) 210 | 211 | # no_annotations 212 | ann <- 213 | oc_forward_df( 214 | oc_fw2(), 215 | loc, 216 | bind_cols = FALSE, 217 | no_annotations = annotation 218 | ) 219 | expect_gt(ncol(ann), 30) 220 | expect_identical(ann$oc_currency_name, c("Euro", NA, NA)) 221 | 222 | # roadinfo 223 | ri <- oc_forward_df( 224 | oc_fw3(), 225 | loc, 226 | roadinfo = roadinfo 227 | ) 228 | expect_identical(ri$oc_roadinfo_speed_in, c(NA_character_, "km/h", "mph")) 229 | 230 | # abbrv 231 | abbrv <- oc_forward_df( 232 | oc_fw2(), 233 | loc, 234 | abbrv = abbrv, 235 | bind_cols = FALSE 236 | ) 237 | expect_identical(abbrv[[1, "oc_formatted"]], noarg[[1, "oc_formatted"]]) 238 | expect_identical(abbrv[[2, "oc_formatted"]], noarg[[2, "oc_formatted"]]) 239 | expect_false(identical( 240 | abbrv[[3, "oc_formatted"]], 241 | noarg[[3, "oc_formatted"]] 242 | )) 243 | 244 | # address_only 245 | city_halls <- c("Hôtel de ville de Nantes", "Los Angeles City Hall") 246 | address_full <- oc_forward_df(city_halls, address_only = FALSE) 247 | address_only <- oc_forward_df(city_halls, address_only = TRUE) 248 | expect_false(identical( 249 | address_full[[1, "oc_formatted"]], 250 | address_only[[1, "oc_formatted"]] 251 | )) 252 | expect_false(identical( 253 | address_full[[1, "oc_formatted"]], 254 | address_only[[1, "oc_formatted"]] 255 | )) 256 | expect_match( 257 | address_full[[1, "oc_formatted"]], 258 | address_only[[1, "oc_formatted"]], 259 | fixed = TRUE 260 | ) 261 | expect_match( 262 | address_full[[1, "oc_formatted"]], 263 | address_only[[1, "oc_formatted"]], 264 | fixed = TRUE 265 | ) 266 | }) 267 | 268 | test_that("list columns are not dropped (by tidyr)", { 269 | skip_if_no_key() 270 | skip_if_oc_offline() 271 | 272 | bnds <- oc_forward_df(oc_fw2(), loc, bounds = bounds, bind_cols = TRUE) 273 | expect_false(is.null(bnds[["bounds"]])) 274 | }) 275 | 276 | test_that("oc_forward_df handles response with no results", { 277 | skip_if_no_key() 278 | skip_if_oc_offline() 279 | 280 | # https://opencagedata.com/api#no-results 281 | nores_df <- oc_forward_df("NOWHERE-INTERESTING") 282 | expect_s3_class(nores_df, c("tbl_df", "tbl", "data.frame")) 283 | expect_identical(nores_df[[1, "oc_lat"]], NA_real_) 284 | }) 285 | 286 | # Checks ------------------------------------------------------------------ 287 | 288 | test_that("Check that placename is present", { 289 | # oc_forward 290 | expect_error(oc_forward(), "`placename` must be provided.") 291 | expect_error(oc_forward(placename = NULL), "`placename` must be provided.") 292 | 293 | # oc_forward_df 294 | expect_error(oc_forward_df(oc_fw1()), "`placename` must be provided.") 295 | expect_error(oc_forward_df(oc_fw1(), NULL), "`placename` must be provided.") 296 | }) 297 | -------------------------------------------------------------------------------- /R/oc_process.R: -------------------------------------------------------------------------------- 1 | #' Process OpenCage geocoding request 2 | #' 3 | #' This function processes all geocoding requests issued by [oc_forward()] and 4 | #' [oc_reverse()] by calling the respective functions (after the query arguments 5 | #' have been checked by [oc_check_query()]). It builds the URL, fetches the 6 | #' results, checks the status of the returned results and finally parses them. 7 | #' 8 | #' @param limit The maximum number of results that should be returned. Integer 9 | #' values between 1 and 100 are allowed, the default is 1. 10 | #' @inheritParams oc_forward 11 | #' 12 | #' @return `oc_forward` returns, depending on the `return` parameter, a list 13 | #' with either 14 | #' \itemize{ 15 | #' \item the results as tibbles (`"df_list"`, the default), 16 | #' \item the results as JSON lists (`"json_list"`), 17 | #' \item the results as GeoJSON lists (`"geojson_list"`), or 18 | #' \item the URL of the OpenCage API call for debugging purposes 19 | #' (`"url_only"`). 20 | #' } 21 | #' 22 | #' @noRd 23 | 24 | oc_process <- 25 | function(placename = NULL, 26 | latitude = NULL, 27 | longitude = NULL, 28 | return = "url_only", 29 | bounds = NULL, 30 | proximity = NULL, 31 | countrycode = NULL, 32 | language = NULL, 33 | limit = 1L, 34 | min_confidence = NULL, 35 | no_annotations = TRUE, 36 | roadinfo = FALSE, 37 | no_dedupe = FALSE, 38 | abbrv = FALSE, 39 | address_only = FALSE, 40 | add_request = FALSE) { 41 | # get key 42 | key <- Sys.getenv("OPENCAGE_KEY") 43 | oc_check_key(key) 44 | 45 | # get & check no_record 46 | no_record <- getOption("oc_no_record", default = TRUE) 47 | oc_check_logical(no_record, check_length_one = TRUE) 48 | 49 | # show progress? 50 | if (length(placename) > 1 && oc_show_progress()) { 51 | pb <- oc_init_progress(placename) 52 | } else if (length(latitude) > 1 && oc_show_progress()) { 53 | pb <- oc_init_progress(latitude) 54 | } else { 55 | pb <- NULL 56 | } 57 | 58 | arglist <- 59 | purrr::compact( 60 | list( 61 | placename = placename, 62 | latitude = latitude, 63 | longitude = longitude, 64 | bounds = bounds, 65 | proximity = proximity, 66 | countrycode = countrycode, 67 | language = language, 68 | limit = limit, 69 | min_confidence = min_confidence, 70 | no_annotations = no_annotations, 71 | roadinfo = roadinfo, 72 | no_dedupe = no_dedupe, 73 | abbrv = abbrv, 74 | address_only = address_only, 75 | add_request = add_request 76 | ) 77 | ) 78 | 79 | purrr::pmap( 80 | .l = arglist, 81 | .f = .oc_process, 82 | return = return, 83 | key = key, 84 | no_record = no_record, 85 | pb = pb 86 | ) 87 | } 88 | 89 | .oc_process <- 90 | function(placename = NULL, 91 | latitude = NULL, 92 | longitude = NULL, 93 | key = NULL, 94 | return = NULL, 95 | bounds = NULL, 96 | proximity = NULL, 97 | countrycode = NULL, 98 | language = NULL, 99 | limit = NULL, 100 | min_confidence = NULL, 101 | no_annotations = NULL, 102 | roadinfo = NULL, 103 | no_dedupe = NULL, 104 | no_record = NULL, 105 | abbrv = NULL, 106 | address_only = NULL, 107 | add_request = NULL, 108 | pb = NULL) { 109 | if (!is.null(pb)) pb$tick() 110 | 111 | # define endpoint 112 | if (return == "geojson_list") { 113 | endpoint <- "geojson" 114 | } else { 115 | endpoint <- "json" 116 | } 117 | 118 | # define query 119 | if (!is.null(placename)) { 120 | query <- placename 121 | } 122 | if (!is.null(latitude)) { 123 | if (!is.na(latitude) && !is.na(longitude)) { 124 | query <- paste(latitude, longitude, sep = ",") 125 | } else { 126 | # set query to NA (and do not send it to the API) 127 | # if either latitude or longitude is NA 128 | query <- NA_character_ 129 | } 130 | } 131 | 132 | # build url 133 | oc_url <- oc_build_url( 134 | query_par = list( 135 | q = query, 136 | bounds = bounds, 137 | proximity = proximity, 138 | countrycode = countrycode, 139 | language = language, 140 | limit = limit, 141 | min_confidence = min_confidence, 142 | no_annotations = as.integer(no_annotations), 143 | roadinfo = as.integer(roadinfo), 144 | no_dedupe = as.integer(no_dedupe), 145 | no_record = as.integer(no_record), 146 | abbrv = as.integer(abbrv), 147 | address_only = as.integer(address_only), 148 | add_request = as.integer(add_request), 149 | key = key 150 | ), 151 | endpoint = endpoint 152 | ) 153 | 154 | # return url only 155 | if (return == "url_only") { 156 | if ( 157 | is.null(key) || 158 | (rlang::is_interactive() && isTRUE(getOption("oc_show_key", FALSE))) 159 | ) { 160 | return(oc_url) 161 | } else { 162 | return(oc_mask_key(oc_url)) 163 | } 164 | } 165 | 166 | # send query to API if not NA, else return (fake) empty res_text 167 | if (!is.na(query) && nchar(query) >= 2) { 168 | # get response 169 | res_env <- oc_get_memoise(oc_url) 170 | 171 | # parse response 172 | res_text <- oc_parse_text(res_env) 173 | 174 | # check status message 175 | oc_check_status(res_env, res_text) 176 | } else { 177 | # Fake 0 results response 178 | 179 | if (identical(return, "geojson_list")) { 180 | res_text <- 181 | "{\"features\":[],\"total_results\":0,\"type\":\"FeatureCollection\"}" 182 | } else { 183 | request_json <- 184 | if (add_request) { 185 | "\"request\":{\"add_request\":1,\"query\":[]}, " 186 | } else { 187 | "" 188 | } 189 | 190 | res_text <- 191 | paste0("{", request_json, "\"results\":[],\"total_results\":0}") 192 | } 193 | } 194 | 195 | # format output 196 | oc_format(res_text = res_text, return = return, query = query) 197 | } 198 | 199 | 200 | #' Build query URL 201 | #' 202 | #' @param query_par A list of query parameters 203 | #' @param endpoint The endpoint to query ("json" or "geojson") 204 | #' 205 | #' @return A character string URL 206 | #' @noRd 207 | 208 | oc_build_url <- function(query_par, endpoint) { 209 | query_par <- purrr::compact(query_par) 210 | query_par <- purrr::discard(query_par, .p = anyNA) 211 | 212 | if ("countrycode" %in% names(query_par)) { 213 | query_par$countrycode <- 214 | tolower(paste(query_par$countrycode, collapse = ",")) 215 | } 216 | 217 | if (!is.null(query_par$bounds)) { 218 | bounds <- query_par$bounds 219 | query_par$bounds <- paste( 220 | bounds[1], 221 | bounds[2], 222 | bounds[3], 223 | bounds[4], 224 | sep = "," 225 | ) 226 | } 227 | 228 | if (!is.null(query_par$proximity)) { 229 | proximity <- query_par$proximity 230 | query_par$proximity <- paste( 231 | proximity["latitude"], 232 | proximity["longitude"], 233 | sep = "," 234 | ) 235 | } 236 | 237 | oc_path <- paste0("geocode/v1/", endpoint) 238 | 239 | crul::url_build( 240 | url = "https://api.opencagedata.com", 241 | path = oc_path, 242 | query = query_par 243 | ) 244 | } 245 | 246 | 247 | #' GET request from OpenCage 248 | #' 249 | #' @param oc_url character string URL with query parameters, built with 250 | #' `oc_build_url()` 251 | #' 252 | #' @return crul::HttpResponse object 253 | #' @noRd 254 | 255 | oc_get <- function(oc_url) { 256 | client <- crul::HttpClient$new( 257 | url = oc_url, 258 | headers = list(`User-Agent` = oc_ua_string) 259 | ) 260 | client$get() 261 | } 262 | 263 | # user-agent string: this is set at build-time, but that should be okay, 264 | # since the version number is too. 265 | oc_ua_string <- 266 | paste0( 267 | ", version ", 268 | utils::packageVersion("opencage") 269 | ) 270 | 271 | 272 | #' Parse HttpResponse object to character string 273 | #' 274 | #' @param res crul::HttpResponse object 275 | #' 276 | #' @return character string (depending on queried endpoint, json or geojson) 277 | #' @noRd 278 | 279 | oc_parse_text <- function(res) { 280 | text <- res$parse(encoding = "UTF-8") 281 | if (identical(text, "")) { 282 | stop("OpenCage returned an empty response.", call. = FALSE) 283 | } 284 | text 285 | } 286 | 287 | 288 | #' Check the status of the HttpResponse 289 | #' 290 | #' @param res_env crul::HttpResponse object 291 | #' @param res_text parsed HttpResponse 292 | #' 293 | #' @return NULL if status code less than or equal to 201, else `stop()` 294 | #' @noRd 295 | 296 | oc_check_status <- function(res_env, res_text) { 297 | if (res_env$success()) { 298 | return(invisible()) 299 | } 300 | message <- 301 | jsonlite::fromJSON( 302 | res_text, 303 | simplifyVector = TRUE, 304 | flatten = TRUE 305 | )$status$message 306 | stop("HTTP failure: ", res_env$status_code, "\n", message, call. = FALSE) 307 | } 308 | 309 | 310 | #' Format the result string 311 | #' 312 | #' @param res_text parsed HttpResponse 313 | #' @param return character, which type of object to return (`df_list`, 314 | #' `json_list`, `geojson_list`) 315 | #' @param query query string ("placename" or "latitude,longitude") 316 | #' 317 | #' @return A list of tibbles, json lists or geojson_lists 318 | #' @noRd 319 | 320 | oc_format <- function(res_text, return, query) { 321 | if (return == "df_list") { 322 | jsn <- jsonlite::fromJSON(res_text, simplifyVector = TRUE, flatten = TRUE) 323 | if (identical(jsn$total_results, 0L)) { 324 | # in oc_forward_df we rely on oc_lat, oc_lng, oc_formatted to be present 325 | results <- 326 | tibble::tibble( 327 | oc_lat = NA_real_, 328 | oc_lng = NA_real_, 329 | oc_formatted = NA_character_ 330 | ) 331 | } else { 332 | results <- tibble::as_tibble(jsn$results) 333 | # Make column names nicer 334 | colnames(results) <- 335 | sub( 336 | "^annotations\\.|^bounds\\.|^components\\.|^geometry\\.", 337 | "", 338 | colnames(results) 339 | ) 340 | colnames(results) <- sub("^_", "", colnames(results)) # components:_type 341 | colnames(results) <- gsub("\\.|-", "_", colnames(results)) 342 | results <- 343 | rlang::set_names(results, ~ tolower(paste0("oc_", .))) 344 | } 345 | if ("request" %in% names(jsn)) { 346 | # add request directly, not from OpenCage roundtrip 347 | tibble::add_column(results, oc_query = query, .before = 1) 348 | } else { 349 | results 350 | } 351 | } else if (return == "json_list" || return == "geojson_list") { 352 | res_text_masked <- oc_mask_key(res_text) 353 | jsn <- jsonlite::fromJSON(res_text_masked, simplifyVector = FALSE) 354 | if (return == "geojson_list") { 355 | structure(jsn, class = "geo_list") 356 | } else { 357 | jsn 358 | } 359 | } 360 | } 361 | --------------------------------------------------------------------------------