├── vignettes ├── .gitignore └── ohsome.Rmd ├── tests ├── testthat │ ├── setup.R │ ├── data │ │ ├── elements-count-buildings-csv.rds │ │ ├── elements-centroid-shop-convenience-bcircles.rds │ │ ├── elements-count-shop-convenience-bcircles-csv.rds │ │ ├── elements-fullHistory-geometry-id-way-625011340.rds │ │ ├── elements-geometry-amenity-hospitals-Germany-content.rds │ │ └── elements-count-buildings-groupby-boundary-groupby-tag-csv.rds │ ├── test-set_boundary.R │ ├── mock_api │ │ ├── 200 │ │ │ └── api.ohsome.org │ │ │ │ └── v1 │ │ │ │ ├── elements │ │ │ │ └── count-754ee5-POST.csv │ │ │ │ └── metadata.json │ │ └── 404 │ │ │ └── api.ohsome.org │ │ │ └── v1 │ │ │ ├── metadata.R │ │ │ └── elements │ │ │ └── amount-754ee5-POST.R │ ├── test-ohsome_extract_elements.R │ ├── test-ohsome_users_count.R │ ├── test-ohsome_aggregate_elements.R │ ├── test-ohsome_extract_elementsFullHistory.R │ ├── test-ohsome_extract_contributions.R │ ├── test-ohsome_contributions_count.R │ ├── test-ohsome_get_metadata.R │ ├── test-ohsome_post.R │ ├── test-ohsome_query.R │ ├── test-set_endpoint.R │ ├── test-set_parameters.R │ ├── test-ohsome_boundary.R │ ├── test-ohsome_parse.R │ └── test-validate.R ├── testthat.R └── spelling.R ├── data ├── ohsome_api_url.rda └── ohsome_endpoints.rda ├── man ├── figures │ ├── README-density-1.png │ ├── README-buildings-1.png │ ├── README-building_levels-1.png │ └── README-contribution_extraction-1.png ├── ohsome_endpoints.Rd ├── ohsome_temporalExtent.Rd ├── ohsome_api_url.Rd ├── ohsome_metadata.Rd ├── ohsome_get_metadata.Rd ├── ohsome_post.Rd ├── ohsome_parse.Rd ├── set_boundary.Rd ├── ohsome_contributions_count.Rd ├── set_endpoint.Rd ├── ohsome_boundary.Rd ├── set_parameters.Rd ├── ohsome_query.Rd ├── ohsome_users_count.Rd ├── ohsome_extract_elements.Rd ├── ohsome_extract_elementsFullHistory.Rd ├── ohsome_extract_contributions.Rd └── ohsome_aggregate_elements.Rd ├── .gitignore ├── data-raw ├── ohsome_api_url.R └── ohsome_endpoints.R ├── .Rbuildignore ├── inst ├── WORDLIST └── CITATION ├── R ├── zzz.R ├── ohsome_users_count.R ├── set_boundary.R ├── data.R ├── ohsome_contributions_count.R ├── ohsome_get_metadata.R ├── ohsome_extract_elementsFullHistory.R ├── utils.R ├── ohsome_post.R ├── ohsome_extract_elements.R ├── ohsome_extract_contributions.R ├── set_endpoint.R ├── ohsome_query.R ├── ohsome_aggregate_elements.R ├── set_parameters.R ├── ohsome_parse.R ├── validate.R └── ohsome_boundary.R ├── cran-comments.md ├── NAMESPACE ├── DESCRIPTION ├── NEWS.md ├── LICENSE.md └── CITATION.cff /vignettes/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *.R 3 | -------------------------------------------------------------------------------- /tests/testthat/setup.R: -------------------------------------------------------------------------------- 1 | library(httptest) 2 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(ohsome) 3 | 4 | test_check("ohsome") 5 | -------------------------------------------------------------------------------- /data/ohsome_api_url.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GIScience/ohsome-r/HEAD/data/ohsome_api_url.rda -------------------------------------------------------------------------------- /data/ohsome_endpoints.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GIScience/ohsome-r/HEAD/data/ohsome_endpoints.rda -------------------------------------------------------------------------------- /man/figures/README-density-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GIScience/ohsome-r/HEAD/man/figures/README-density-1.png -------------------------------------------------------------------------------- /man/figures/README-buildings-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GIScience/ohsome-r/HEAD/man/figures/README-buildings-1.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .Rdata 4 | .httr-oauth 5 | .DS_Store 6 | *.Rproj 7 | inst/doc 8 | /doc/ 9 | /Meta/ 10 | -------------------------------------------------------------------------------- /man/figures/README-building_levels-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GIScience/ohsome-r/HEAD/man/figures/README-building_levels-1.png -------------------------------------------------------------------------------- /man/figures/README-contribution_extraction-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GIScience/ohsome-r/HEAD/man/figures/README-contribution_extraction-1.png -------------------------------------------------------------------------------- /tests/testthat/data/elements-count-buildings-csv.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GIScience/ohsome-r/HEAD/tests/testthat/data/elements-count-buildings-csv.rds -------------------------------------------------------------------------------- /tests/testthat/data/elements-centroid-shop-convenience-bcircles.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GIScience/ohsome-r/HEAD/tests/testthat/data/elements-centroid-shop-convenience-bcircles.rds -------------------------------------------------------------------------------- /tests/spelling.R: -------------------------------------------------------------------------------- 1 | if(requireNamespace('spelling', quietly = TRUE)) 2 | spelling::spell_check_test(vignettes = TRUE, error = FALSE, 3 | skip_on_cran = TRUE) 4 | -------------------------------------------------------------------------------- /tests/testthat/data/elements-count-shop-convenience-bcircles-csv.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GIScience/ohsome-r/HEAD/tests/testthat/data/elements-count-shop-convenience-bcircles-csv.rds -------------------------------------------------------------------------------- /tests/testthat/data/elements-fullHistory-geometry-id-way-625011340.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GIScience/ohsome-r/HEAD/tests/testthat/data/elements-fullHistory-geometry-id-way-625011340.rds -------------------------------------------------------------------------------- /data-raw/ohsome_api_url.R: -------------------------------------------------------------------------------- 1 | require(usethis) 2 | 3 | ohsome_api_url <- list( 4 | base = "https://api.ohsome.org", 5 | version = "v1" 6 | ) 7 | 8 | usethis::use_data(ohsome_api_url, overwrite = TRUE) 9 | -------------------------------------------------------------------------------- /tests/testthat/data/elements-geometry-amenity-hospitals-Germany-content.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GIScience/ohsome-r/HEAD/tests/testthat/data/elements-geometry-amenity-hospitals-Germany-content.rds -------------------------------------------------------------------------------- /tests/testthat/data/elements-count-buildings-groupby-boundary-groupby-tag-csv.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GIScience/ohsome-r/HEAD/tests/testthat/data/elements-count-buildings-groupby-boundary-groupby-tag-csv.rds -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^ohsome\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^README\.Rmd$ 4 | ^data-raw$ 5 | ^LICENSE\.md$ 6 | ^CITATION\.cff$ 7 | ^ohsome-r\.Rproj$ 8 | ^doc$ 9 | ^Meta$ 10 | ^cran-comments\.md$ 11 | ^CRAN-SUBMISSION$ 12 | -------------------------------------------------------------------------------- /tests/testthat/test-set_boundary.R: -------------------------------------------------------------------------------- 1 | test_that("removes bcircles parameter when setting new bboxes parameter", { 2 | 3 | q1 <- ohsome_elements_count("8,49,1000") 4 | q2 <- set_boundary(q1, "8,49,9,50") 5 | expect_null(q2$body$bcircles) 6 | }) 7 | -------------------------------------------------------------------------------- /tests/testthat/mock_api/200/api.ohsome.org/v1/elements/count-754ee5-POST.csv: -------------------------------------------------------------------------------- 1 | # Copyright URL: https://ohsome.org/copyrights 2 | # Copyright Text: © OpenStreetMap contributors 3 | # API Version: 1.6.0 4 | timestamp;value 5 | "2021-08-29T20:00:00Z";"24.0" 6 | -------------------------------------------------------------------------------- /man/ohsome_endpoints.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{ohsome_endpoints} 5 | \alias{ohsome_endpoints} 6 | \title{ohsome API endpoints} 7 | \format{ 8 | A list of ohsome API endpoints. 9 | } 10 | \description{ 11 | Available ohsome API endpoints with their parameters 12 | } 13 | -------------------------------------------------------------------------------- /man/ohsome_temporalExtent.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{ohsome_temporalExtent} 5 | \alias{ohsome_temporalExtent} 6 | \title{ohsome API temporal extent} 7 | \format{ 8 | A vector of POSIXct 9 | } 10 | \description{ 11 | Temporal extent of the OSM data in the underlying OSHDB 12 | } 13 | -------------------------------------------------------------------------------- /man/ohsome_api_url.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{ohsome_api_url} 5 | \alias{ohsome_api_url} 6 | \title{ohsome API URL} 7 | \format{ 8 | A list: 9 | \itemize{ 10 | \item \code{base}: character; base URL 11 | \item \code{version}: character; path to current major API version 12 | } 13 | } 14 | \description{ 15 | The base URL of the ohsome API with path to current major 16 | version. 17 | } 18 | -------------------------------------------------------------------------------- /tests/testthat/test-ohsome_extract_elements.R: -------------------------------------------------------------------------------- 1 | test_that("creates ohsome_query object with the correct endpoint", { 2 | q <- ohsome_elements_geometry() 3 | expect_equal(httr::parse_url(q$url)$path, "v1/elements/geometry") 4 | }) 5 | 6 | test_that("returns object of class ohsome_query", { 7 | expect_s3_class(ohsome_extract_elements(), "ohsome_query") 8 | }) 9 | 10 | test_that("correctly sets clipGeometry parameter", { 11 | q <- ohsome_elements_geometry(clipGeometry = FALSE) 12 | expect_equal(q$body$clipGeometry, "FALSE") 13 | }) 14 | -------------------------------------------------------------------------------- /inst/WORDLIST: -------------------------------------------------------------------------------- 1 | API's 2 | API’s 3 | CRS 4 | FeatureCollection 5 | FeatureCollections 6 | Franconia 7 | GeoJSON 8 | JSON 9 | MultiPolygons 10 | Neukölln 11 | Nominatim 12 | OSHDB 13 | OSM 14 | OpenStreetMap 15 | POSIXct 16 | URIs 17 | WGS 18 | bboxes 19 | bcircles 20 | bpolys 21 | centroids 22 | changeset 23 | choropleth 24 | coercible 25 | contributionTypes 26 | dplyr 27 | elementsFullHistory 28 | geodata 29 | mapview 30 | nominatimlite 31 | rgeoboundaries 32 | rnaturalearth 33 | sfc 34 | tidyr 35 | timeframe 36 | timespan 37 | timestring 38 | ’s 39 | ️ 40 | -------------------------------------------------------------------------------- /tests/testthat/test-ohsome_users_count.R: -------------------------------------------------------------------------------- 1 | test_that("creates ohsome_query object with the correct endpoint", { 2 | q <- ohsome_users_count() 3 | expect_equal(httr::parse_url(q$url)$path, "v1/users/count") 4 | }) 5 | 6 | test_that("returns object of class ohsome_query", { 7 | expect_s3_class(ohsome_users_count(), "ohsome_query") 8 | }) 9 | 10 | test_that("correctly sets density endpoint on return_value arg", { 11 | q <- ohsome_users_count(return_value = "density") 12 | expect_equal(httr::parse_url(q$url)$path, "v1/users/count/density") 13 | }) 14 | 15 | -------------------------------------------------------------------------------- /tests/testthat/test-ohsome_aggregate_elements.R: -------------------------------------------------------------------------------- 1 | q <- ohsome_elements_count("0,0,1000") 2 | 3 | test_that("creates ohsome_query object with the correct endpoint", { 4 | expect_equal(httr::parse_url(q$url)$path, "v1/elements/count") 5 | }) 6 | 7 | test_that("returns object of class ohsome_query", { 8 | expect_s3_class(q, "ohsome_query") 9 | }) 10 | 11 | test_that("correctly sets ratio endpoint on return_value arg", { 12 | q <- ohsome_elements_length(return_value = "ratio") 13 | expect_equal(httr::parse_url(q$url)$path, "v1/elements/length/ratio") 14 | }) 15 | -------------------------------------------------------------------------------- /tests/testthat/test-ohsome_extract_elementsFullHistory.R: -------------------------------------------------------------------------------- 1 | test_that("creates ohsome_query object with the correct endpoint", { 2 | q <- ohsome_elementsFullHistory_geometry(time = "2010,2020") 3 | expect_equal(httr::parse_url(q$url)$path, "v1/elementsFullHistory/geometry") 4 | }) 5 | 6 | test_that("returns object of class ohsome_query", { 7 | expect_s3_class(ohsome_extract_elementsFullHistory(time = "2010,2020"), "ohsome_query") 8 | }) 9 | 10 | test_that("correctly sets clipGeometry parameter", { 11 | q <- ohsome_elementsFullHistory_geometry(time = "2010,2020", clipGeometry = FALSE) 12 | expect_equal(q$body$clipGeometry, "FALSE") 13 | }) 14 | -------------------------------------------------------------------------------- /tests/testthat/mock_api/200/api.ohsome.org/v1/metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "attribution": { 3 | "url": "https://ohsome.org/copyrights", 4 | "text": "© OpenStreetMap contributors" 5 | }, 6 | "apiVersion": "1.8.0-RC1", 7 | "timeout": 600.0, 8 | "extractRegion": { 9 | "spatialExtent": { 10 | "type": "Polygon", 11 | "coordinates": [ 12 | [ 13 | [ 14 | -180.0, 15 | -90.0 16 | ], 17 | [ 18 | 180.0, 19 | -90.0 20 | ], 21 | [ 22 | 180.0, 23 | 90.0 24 | ], 25 | [ 26 | -180.0, 27 | 90.0 28 | ], 29 | [ 30 | -180.0, 31 | -90.0 32 | ] 33 | ] 34 | ] 35 | }, 36 | "temporalExtent": { 37 | "fromTimestamp": "2007-10-08T00:00:00Z", 38 | "toTimestamp": "2021-06-06T20:00Z" 39 | }, 40 | "replicationSequenceNumber": 76549 41 | } 42 | } -------------------------------------------------------------------------------- /tests/testthat/test-ohsome_extract_contributions.R: -------------------------------------------------------------------------------- 1 | test_that("creates ohsome_query object with the correct endpoint", { 2 | q <- ohsome_contributions_geometry() 3 | expect_equal(httr::parse_url(q$url)$path, "v1/contributions/geometry") 4 | }) 5 | 6 | test_that("correctly modifies endpoint when latest = TRUE", { 7 | q <- ohsome_contributions_geometry(latest = TRUE) 8 | expect_equal(httr::parse_url(q$url)$path, "v1/contributions/latest/geometry") 9 | }) 10 | 11 | test_that("returns object of class ohsome_query", { 12 | expect_s3_class(ohsome_extract_contributions(), "ohsome_query") 13 | }) 14 | 15 | test_that("correctly sets clipGeometry parameter", { 16 | q <- ohsome_contributions_geometry(clipGeometry = FALSE) 17 | expect_equal(q$body$clipGeometry, "FALSE") 18 | }) 19 | -------------------------------------------------------------------------------- /tests/testthat/test-ohsome_contributions_count.R: -------------------------------------------------------------------------------- 1 | test_that("creates ohsome_query object with the correct endpoint", { 2 | q <- ohsome_contributions_count() 3 | expect_equal(httr::parse_url(q$url)$path, "v1/contributions/count") 4 | }) 5 | 6 | test_that("returns object of class ohsome_query", { 7 | expect_s3_class(ohsome_contributions_count(), "ohsome_query") 8 | }) 9 | 10 | test_that("correctly sets density endpoint on return_value arg", { 11 | q <- ohsome_contributions_count(return_value = "density") 12 | expect_equal(httr::parse_url(q$url)$path, "v1/contributions/count/density") 13 | }) 14 | 15 | test_that("correctly modifies endpoint when latest = TRUE", { 16 | q <- ohsome_contributions_count(latest = TRUE) 17 | expect_equal(httr::parse_url(q$url)$path, "v1/contributions/latest/count") 18 | }) -------------------------------------------------------------------------------- /R/zzz.R: -------------------------------------------------------------------------------- 1 | # Upon attaching package, request ohsome API metadata and assign to ohsome_metadata 2 | .onLoad <- function(libname, pkgname) { 3 | tryCatch({ 4 | ohsome_metadata <- ohsome_get_metadata(quiet = TRUE) 5 | assign( 6 | "ohsome_metadata", 7 | ohsome_metadata, 8 | envir = parent.env(environment()) 9 | ) 10 | assign( 11 | "ohsome_temporalExtent", 12 | ohsome_metadata$extractRegion$temporalExtent, 13 | envir = parent.env(environment()) 14 | ) 15 | }, 16 | error = function(e) return(TRUE) 17 | ) 18 | } 19 | 20 | .onAttach <- function(libname, pkgname) { 21 | if(exists("ohsome_metadata", where = parent.env(environment()))) { 22 | packageStartupMessage(create_metadata_message(ohsome_metadata)) 23 | } else { 24 | packageStartupMessage( 25 | "Could not retrieve metadata from ohsome API.", 26 | "\nPlease check your internet connection and try to run ", 27 | "ohsome_get_metadata()" 28 | ) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /data-raw/ohsome_endpoints.R: -------------------------------------------------------------------------------- 1 | library(httr) 2 | library(rapiclient) 3 | library(data.table) 4 | 5 | extract_endpoint <- function(endpoint, paths) { 6 | 7 | params <- rbindlist(paths[[endpoint]]$post$parameters, fill = TRUE) 8 | deprecated <- grepl("deprecated", params$description) 9 | 10 | invisible(list( 11 | summary = paths[[endpoint]]$post$summary, 12 | produces = paths[[endpoint]]$post$produces, 13 | parameters = as.data.frame( 14 | params[!deprecated, c("name", "description", "required")] 15 | ) 16 | )) 17 | } 18 | 19 | extract_spec <- function(spec) { 20 | api <- "https://api.ohsome.org/stable/docs" |> 21 | modify_url(query = spec) |> 22 | get_api() 23 | 24 | paths <- api$paths 25 | endpoints <- names(paths) 26 | 27 | out <- lapply(endpoints, extract_endpoint, paths = paths) 28 | names(out) <- sub("^/", "", endpoints) 29 | invisible(out) 30 | } 31 | 32 | specs <- c("group=Data%20Aggregation", "group=Data%20Extraction") 33 | ohsome_endpoints <- unlist(lapply(specs, extract_spec), recursive = FALSE) 34 | 35 | usethis::use_data(ohsome_endpoints, overwrite = TRUE) 36 | -------------------------------------------------------------------------------- /tests/testthat/test-ohsome_get_metadata.R: -------------------------------------------------------------------------------- 1 | with_mock_api({ 2 | 3 | # mock response: tests/testthat/mock_api/200/api.ohsome.org/v1/metadata.json 4 | .mockPaths("./mock_api/200") 5 | 6 | test_that( 7 | "returns metadata", { 8 | meta <- ohsome_get_metadata(quiet = T) 9 | expect_equal(meta$apiVersion, "1.8.0-RC1") 10 | expect_type(meta, "list") 11 | expect_s3_class(meta, "ohsome_metadata") 12 | }) 13 | 14 | test_that( 15 | "converts spatial extent", { 16 | meta <- ohsome_get_metadata(quiet = T) 17 | expect_s3_class(meta$extractRegion$spatialExtent, "sfc_POLYGON") 18 | expect_equal( 19 | sf::st_bbox(meta$extractRegion$spatialExtent), 20 | sf::st_bbox( 21 | obj = c(xmin = -180, xmax = 180, ymax = 90, ymin = -90), 22 | crs = 4326 23 | ) 24 | ) 25 | }) 26 | 27 | test_that( 28 | "message if quiet = F", { 29 | expect_message(ohsome_get_metadata(quiet = F)) 30 | }) 31 | }) 32 | 33 | 34 | with_mock_api({ 35 | 36 | # mock response: tests/testthat/mock_api/404/api.ohsome.org/v1/metadata.R 37 | .mockPaths("./mock_api/404") 38 | 39 | test_that( 40 | "throws error on API request fail", { 41 | expect_error(ohsome_get_metadata(quiet = T)) 42 | }) 43 | }) 44 | 45 | -------------------------------------------------------------------------------- /man/ohsome_metadata.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{ohsome_metadata} 5 | \alias{ohsome_metadata} 6 | \title{ohsome API metadata} 7 | \format{ 8 | An \code{ohsome_metadata} object. This is a named list with the 9 | attributes \code{date}, \code{status_code} (of the GET request) and the following list 10 | elements: 11 | \itemize{ 12 | \item \code{attribution}: character; \code{url} and \code{text} of OSM data copyrights and 13 | attribution 14 | \item \code{apiVersion}: numeric_version; Version of the ohsome API 15 | \item \code{timeout}: numeric; limit of the processing time in seconds 16 | \item \code{extractRegion}: 17 | \itemize{ 18 | \item \code{spatialExtent}: sfc_POLYGON; spatial boundary of the OSM data in the 19 | underlying OSHDB 20 | \item \code{temporalExtent}: vector of POSIXct; timeframe of the OSM data in the 21 | underlying OSHDB data 22 | \item \code{replicationSequenceNumber}: numeric; precise state of the OSM data 23 | contained in the underlying OSHDB, expressed as the id of the last 24 | applied (hourly) diff file from \href{https://planet.openstreetmap.org}{Planet OSM} 25 | } 26 | } 27 | } 28 | \description{ 29 | Metadata of the ohsome API that is requested on loading the 30 | package 31 | } 32 | -------------------------------------------------------------------------------- /cran-comments.md: -------------------------------------------------------------------------------- 1 | ## Test environments 2 | - R-hub windows-x86_64-devel (r-devel) 3 | - R-hub ubuntu-gcc-release (r-release) 4 | - R-hub fedora-clang-devel (r-devel) 5 | 6 | ## R CMD check results 7 | ❯ On windows-x86_64-devel (r-devel) 8 | checking for non-standard things in the check directory ... NOTE 9 | Found the following files/directories: 10 | ''NULL'' 11 | 12 | This seems to be an Rhub issue and can likely be ignored. See [R-hub issue #560](https://github.com/r-hub/rhub/issues/560) 13 | 14 | ❯ On windows-x86_64-devel (r-devel) 15 | checking for detritus in the temp directory ... NOTE 16 | Found the following files/directories: 17 | 'lastMiKTeXException' 18 | 19 | This seems to be an Rhub issue and can likely be ignored. See [R-hub issue #503](https://github.com/r-hub/rhub/issues/503) 20 | 21 | 22 | ❯ On fedora-clang-devel (r-devel), ubuntu-gcc-release (r-release) 23 | checking HTML version of manual ... NOTE 24 | Skipping checking HTML validation: no command 'tidy' found 25 | 26 | This seems to indicate that HTML validity checks were skipped due to missing 27 | 'tidy' in R-hub's environment. However, the package manual has passed 28 | HTML validity checks in the Windows environment. Thus, the note can 29 | probably be ignored. 30 | 31 | 0 errors ✔ | 0 warnings ✔ | 3 notes ✖ 32 | -------------------------------------------------------------------------------- /R/ohsome_users_count.R: -------------------------------------------------------------------------------- 1 | #' Count OSM users 2 | #' 3 | #' Create an `ohsome_query` object for OSM users count 4 | #' 5 | #' `ohsome_users_count()` creates an `ohsome_query` object for OSM users 6 | #' aggregation. Boundary objects are passed via [set_boundary()] into 7 | #' [ohsome_boundary()]. 8 | #' 9 | #' @inherit ohsome_query params return 10 | #' @inheritParams ohsome_contributions_count 11 | #' @param return_value character; the value to be returned by the ohsome API: 12 | #' * `"absolute"` returns the absolute number of users. This is the 13 | #' default. 14 | #' * `"density"` returns the number of users per square kilometer. 15 | #' @seealso [ohsome API Endpoints -- Users Aggregation](https://docs.ohsome.org/ohsome-api/stable/endpoints.html#users-aggregation) 16 | #' @export 17 | #' @examples 18 | #' # Yearly count of users contributing to man-made objects around "Null Island" 19 | #' ohsome_users_count("0,0,10", filter = "man_made=*", time = "2012/2022/P1Y") 20 | #' 21 | ohsome_users_count <- function( 22 | boundary = NULL, 23 | return_value = c("absolute", "density"), 24 | grouping = NULL, 25 | time = NULL, 26 | ... 27 | ) { 28 | return_value <- match.arg(return_value) 29 | if(return_value == "absolute") return_value <- NULL 30 | 31 | q <- ohsome_query(c("users", "count", return_value), boundary, grouping,...) 32 | set_time(q, time) 33 | } 34 | -------------------------------------------------------------------------------- /tests/testthat/test-ohsome_post.R: -------------------------------------------------------------------------------- 1 | with_mock_api({ 2 | 3 | .mockPaths("./mock_api/200") 4 | 5 | # mock response: tests/testthat/mock_api/200/api.ohsome.org/v1/elements/count-754ee5-POST.csv 6 | test_that( 7 | "returns ohsome API response if parse = FALSE", { 8 | 9 | q <- ohsome_query( 10 | c("elements", "count"), 11 | filter = "shop=convenience", 12 | bcircles = "13.45,52.5,1000" 13 | ) 14 | 15 | expect_s3_class(ohsome_post(q, parse = FALSE, validate = FALSE), "response") 16 | }) 17 | 18 | test_that( 19 | "returns data.frame if parse = TRUE", { 20 | 21 | q <- ohsome_query( 22 | c("elements", "count"), 23 | filter = "shop=convenience", 24 | bcircles = "13.45,52.5,1000" 25 | ) 26 | 27 | expect_s3_class(ohsome_post(q, parse = TRUE, validate = FALSE), "data.frame") 28 | }) 29 | }) 30 | 31 | 32 | with_mock_api({ 33 | 34 | # mock response: tests/testthat/mock_api/404/api.ohsome.org/elements/amount-754ee5-POST.R 35 | .mockPaths("./mock_api/404") 36 | 37 | test_that( 38 | "throws error on API request fail", { 39 | 40 | q <- ohsome_query( 41 | c("elements", "amount"), 42 | filter = "shop=convenience", 43 | bcircles = "13.45,52.5,1000" 44 | ) 45 | 46 | expect_error(ohsome_post(q, validate = FALSE)) 47 | }) 48 | }) 49 | 50 | test_that("throws error on invalid queries by default (strict=TRUE)", { 51 | q <- ohsome_query("foo") 52 | expect_error(suppressWarnings(ohsome_post(q))) 53 | }) 54 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method(ohsome_boundary,bbox) 4 | S3method(ohsome_boundary,character) 5 | S3method(ohsome_boundary,list) 6 | S3method(ohsome_boundary,matrix) 7 | S3method(ohsome_boundary,ohsome_boundary) 8 | S3method(ohsome_boundary,sf) 9 | S3method(ohsome_boundary,sfc) 10 | S3method(ohsome_boundary,sfg) 11 | export(ohsome_aggregate_elements) 12 | export(ohsome_boundary) 13 | export(ohsome_contributions_bbox) 14 | export(ohsome_contributions_centroid) 15 | export(ohsome_contributions_count) 16 | export(ohsome_contributions_geometry) 17 | export(ohsome_df) 18 | export(ohsome_elementsFullHistory_bbox) 19 | export(ohsome_elementsFullHistory_centroid) 20 | export(ohsome_elementsFullHistory_geometry) 21 | export(ohsome_elements_area) 22 | export(ohsome_elements_bbox) 23 | export(ohsome_elements_centroid) 24 | export(ohsome_elements_count) 25 | export(ohsome_elements_geometry) 26 | export(ohsome_elements_length) 27 | export(ohsome_elements_perimeter) 28 | export(ohsome_extract_contributions) 29 | export(ohsome_extract_elements) 30 | export(ohsome_extract_elementsFullHistory) 31 | export(ohsome_get_metadata) 32 | export(ohsome_parse) 33 | export(ohsome_post) 34 | export(ohsome_query) 35 | export(ohsome_sf) 36 | export(ohsome_users_count) 37 | export(set_boundary) 38 | export(set_endpoint) 39 | export(set_filter) 40 | export(set_groupByKey) 41 | export(set_groupByKeys) 42 | export(set_groupByValues) 43 | export(set_grouping) 44 | export(set_parameters) 45 | export(set_properties) 46 | export(set_time) 47 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: ohsome 2 | Title: An 'ohsome API' Client 3 | Version: 0.2.2.9000 4 | Authors@R: c( 5 | person("Heidelberg Institute for Geoinformation Technology (HeiGIT) gGmbH", role = "cph"), 6 | person("Oliver", "Fritz", , "oliver.fritz@heigit.org", role = c("aut", "cre"), 7 | comment = c(ORCID = "0000-0001-6324-7295")) 8 | ) 9 | Description: A client that grants access to the power of the 'ohsome API' 10 | from R. It lets you analyze the rich data source of the 11 | 'OpenStreetMap (OSM)' history. You can retrieve the geometry of 'OSM' 12 | data at specific points in time, and you can get aggregated statistics 13 | on the evolution of 'OSM' elements and specify your own temporal, 14 | spatial and/or thematic filters. 15 | License: LGPL (>= 3) 16 | URL: 17 | https://github.com/GIScience/ohsome-r,https://docs.ohsome.org/ohsome-api/stable/ 18 | BugReports: https://github.com/GIScience/ohsome-r/issues 19 | Depends: 20 | R (>= 2.10) 21 | Imports: 22 | geojsonsf, 23 | httr, 24 | jsonlite, 25 | readr, 26 | sf, 27 | utils 28 | Suggests: 29 | dplyr, 30 | ggplot2, 31 | httptest, 32 | janitor, 33 | knitr, 34 | mapview, 35 | nominatimlite, 36 | osmdata, 37 | rmarkdown, 38 | spelling, 39 | testthat (>= 3.0.0), 40 | tmaptools, 41 | tidyr 42 | VignetteBuilder: 43 | knitr 44 | Config/testthat/edition: 3 45 | Encoding: UTF-8 46 | Language: en-US 47 | LazyData: true 48 | Roxygen: list(markdown = TRUE) 49 | RoxygenNote: 7.2.3 50 | -------------------------------------------------------------------------------- /inst/CITATION: -------------------------------------------------------------------------------- 1 | bibentry( 2 | bibtype = "Misc", 3 | title = "OSHDB - OpenStreetMap History Data Analysis (0.7.2)", 4 | author = c( 5 | person(given = "Martin", family = "Raifer"), 6 | person(given = "Rafael", family = "Troilo"), 7 | person(given = "Franz-Benjamin", family = "Mocnik"), 8 | person(given = "Moritz", family = "Schott") 9 | ), 10 | month = "Sep", 11 | year = "2021", 12 | publisher = "Zenodo", 13 | version = "0.7.2", 14 | doi = "10.5281/zenodo.5512545", 15 | header = "When using the OSHDB indirectly through ohsome for a publication, please cite it as:" 16 | ) 17 | 18 | bibentry( 19 | bibtype = "Article", 20 | title = "OSHDB: a framework for spatio-temporal analysis of OpenStreetMap history data", 21 | author = c( 22 | person(given = "Martin", family = "Raifer"), 23 | person(given = "Rafael", family = "Troilo"), 24 | person(given = "Fabian", family = "Kowatsch"), 25 | person(given = "Michael", family = "Auer"), 26 | person(given = "Lukas", family = "Loos"), 27 | person(given = "Sabrina", family = "Marx"), 28 | person(given = "Katharina", family = "Przybill"), 29 | person(given = "Sascha", family = "Fendrich"), 30 | person(given = "Franz-Benjamin", family = "Mocnik"), 31 | person(given = "Alexander", family = "Zipf") 32 | ), 33 | journal = "Open Geospatial Data, Software and Standards", 34 | year = "2019", 35 | month = "Apr", 36 | day = "08", 37 | volume = 4, 38 | number = 1, 39 | pages = 3, 40 | doi = "10.1186/s40965-019-0061-3", 41 | header = "For scientific papers or similar publications around the analysis of OSM history data, please consider to additionally cite the technical paper describing the OSHDB:" 42 | ) 43 | -------------------------------------------------------------------------------- /R/set_boundary.R: -------------------------------------------------------------------------------- 1 | #' Set boundary 2 | #' 3 | #' Set or modify the spatial filter of an existing `ohsome_query` object 4 | #' 5 | #' [set_boundary()] adds a spatial filter to an `ohsome_query` object or 6 | #' replaces an existing one. The spatial filter of a query to the ohsome API can 7 | #' be defined as one or more polygons, bounding boxes or bounding circles. 8 | #' 9 | #' @inheritParams ohsome_boundary 10 | #' @inheritParams ohsome_post 11 | #' @inherit ohsome_query return 12 | #' @seealso [ohsome API documentation](https://docs.ohsome.org/ohsome-api/v1/) 13 | #' @export 14 | #' @examples 15 | #' # Query without boundary definition 16 | #' q <- ohsome_query( 17 | #' "elements/count/groupBy/boundary", 18 | #' filter = "building=*", 19 | #' time = "2022-01-01" 20 | #' ) 21 | #' 22 | #' # Use franconia from the mapview package as bounding polygons 23 | #' \donttest{ 24 | #' set_boundary(q, mapview::franconia, digits = 4) 25 | #' } 26 | #' 27 | #' # Use the bounding box of franconia 28 | #' \donttest{ 29 | #' set_boundary(q, sf::st_bbox(mapview::franconia)) 30 | #' } 31 | #' 32 | #' \dontrun{ 33 | #' # Get bounding box of the city of Kigali from OSM 34 | #' set_boundary(q, osmdata::getbb("Kigali")) 35 | #' } 36 | #' 37 | #' # Definition of two named bounding circles 38 | #' set_boundary(q, c("Circle 1:8.6528,49.3683,1000", "Circle 2:8.7294,49.4376,1000")) 39 | #' 40 | set_boundary <- function(query, boundary = NULL, ...) { 41 | 42 | endpoint <- gsub("^.*?/", "", httr::parse_url(query$url)$path) 43 | body <- query$body 44 | 45 | btypes <- c("bpolys", "bboxes", "bcircles") 46 | 47 | boundary <- ohsome_boundary(boundary %||% Reduce(`%||%`, body[btypes]), ...) 48 | 49 | body[[boundary$type]] <- boundary$boundary 50 | body[btypes[btypes != boundary$type]] <- NULL 51 | 52 | return(do.call(ohsome_query, c(endpoint, body))) 53 | } 54 | -------------------------------------------------------------------------------- /man/ohsome_get_metadata.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ohsome_get_metadata.R 3 | \name{ohsome_get_metadata} 4 | \alias{ohsome_get_metadata} 5 | \title{GET metadata from ohsome API} 6 | \usage{ 7 | ohsome_get_metadata(quiet = FALSE) 8 | } 9 | \arguments{ 10 | \item{quiet}{logical; suppresses message on data attribution, API version and 11 | temporal extent.} 12 | } 13 | \value{ 14 | An \code{ohsome_metadata} object. This is a named list with the 15 | attributes \code{date}, \code{status_code} (of the GET request) and the following list 16 | elements: 17 | \itemize{ 18 | \item \code{attribution}: character; \code{url} and \code{text} of OSM data copyrights and 19 | attribution 20 | \item \code{apiVersion}: character; Version of the ohsome API 21 | \item \code{timeout}: numeric; limit of the processing time in seconds 22 | \item \code{extractRegion}: 23 | \itemize{ 24 | \item \code{spatialExtent}: sfc_POLYGON; spatial boundary of the OSM data in the 25 | underlying OSHDB 26 | \item \code{temporalExtent}: vector of ISO 8601 character; start and end of the temporal extent of OSM data in the 27 | underlying OSHDB 28 | \item \code{replicationSequenceNumber}: numeric; precise state of the OSM data 29 | contained in the underlying OSHDB, expressed as the id of the last 30 | applied (hourly) diff file from \href{https://planet.openstreetmap.org}{Planet OSM} 31 | } 32 | } 33 | } 34 | \description{ 35 | Returns parsed metadata from ohsome API 36 | } 37 | \details{ 38 | \code{ohsome_get_metadata()} sends a GET request to the metadata endpoint of 39 | ohsome API and parses the response. The parsed metadata is silently returned. 40 | } 41 | \examples{ 42 | \dontrun{ 43 | ohsome_get_metadata() 44 | } 45 | } 46 | \seealso{ 47 | \href{https://docs.ohsome.org/ohsome-api/v1/endpoints.html#metadata}{ohsome API Endpoints -- Metadata} 48 | } 49 | -------------------------------------------------------------------------------- /R/data.R: -------------------------------------------------------------------------------- 1 | #' @docType data 2 | #' @name ohsome_api_url 3 | #' @title ohsome API URL 4 | #' @description The base URL of the ohsome API with path to current major 5 | #' version. 6 | #' @format A list: 7 | #' * `base`: character; base URL 8 | #' * `version`: character; path to current major API version 9 | NULL 10 | 11 | #' @docType data 12 | #' @name ohsome_endpoints 13 | #' @title ohsome API endpoints 14 | #' @description Available ohsome API endpoints with their parameters 15 | #' @format A list of ohsome API endpoints. 16 | NULL 17 | 18 | #' @docType data 19 | #' @name ohsome_metadata 20 | #' @title ohsome API metadata 21 | #' @description Metadata of the ohsome API that is requested on loading the 22 | #' package 23 | #' @format An `ohsome_metadata` object. This is a named list with the 24 | #' attributes `date`, `status_code` (of the GET request) and the following list 25 | #' elements: 26 | #' * `attribution`: character; `url` and `text` of OSM data copyrights and 27 | #' attribution 28 | #' * `apiVersion`: numeric_version; Version of the ohsome API 29 | #' * `timeout`: numeric; limit of the processing time in seconds 30 | #' * `extractRegion`: 31 | #' * `spatialExtent`: sfc_POLYGON; spatial boundary of the OSM data in the 32 | #' underlying OSHDB 33 | #' * `temporalExtent`: vector of POSIXct; timeframe of the OSM data in the 34 | #' underlying OSHDB data 35 | #' * `replicationSequenceNumber`: numeric; precise state of the OSM data 36 | #' contained in the underlying OSHDB, expressed as the id of the last 37 | #' applied (hourly) diff file from [Planet OSM](https://planet.openstreetmap.org) 38 | NULL 39 | 40 | #' @docType data 41 | #' @name ohsome_temporalExtent 42 | #' @title ohsome API temporal extent 43 | #' @description Temporal extent of the OSM data in the underlying OSHDB 44 | #' @format A vector of POSIXct 45 | NULL 46 | -------------------------------------------------------------------------------- /tests/testthat/mock_api/404/api.ohsome.org/v1/metadata.R: -------------------------------------------------------------------------------- 1 | structure(list(url = "https://api.ohsome.org/404/metadata", status_code = 404L, 2 | headers = structure(list(date = "Wed, 16 Jun 2021 07:38:04 GMT", 3 | server = "Apache/2.4.29 (Ubuntu)", `content-length` = "277", 4 | `content-type` = "text/html; charset=iso-8859-1"), class = c("insensitive", 5 | "list")), all_headers = list(list(status = 301L, version = "HTTP/1.1", 6 | headers = structure(list(date = "Wed, 16 Jun 2021 07:38:04 GMT", 7 | server = "Apache/2.4.29 (Ubuntu)", location = "https://api.ohsome.org/404/metadata", 8 | `content-length` = "323", `content-type` = "text/html; charset=iso-8859-1"), class = c("insensitive", 9 | "list"))), list(status = 404L, version = "HTTP/1.1", 10 | headers = structure(list(date = "Wed, 16 Jun 2021 07:38:04 GMT", 11 | server = "Apache/2.4.29 (Ubuntu)", `content-length` = "277", 12 | `content-type` = "text/html; charset=iso-8859-1"), class = c("insensitive", 13 | "list")))), cookies = structure(list(domain = logical(0), 14 | flag = logical(0), path = logical(0), secure = logical(0), 15 | expiration = structure(numeric(0), class = c("POSIXct", 16 | "POSIXt")), name = logical(0), value = logical(0)), row.names = integer(0), class = "data.frame"), 17 | content = charToRaw("\n\n404 Not Found\n\n

Not Found

\n

The requested URL was not found on this server.

\n
\n
Apache/2.4.29 (Ubuntu) Server at api.ohsome.org Port 443
\n\n"), 18 | date = structure(1623829084, class = c("POSIXct", "POSIXt" 19 | ), tzone = "GMT"), times = c(redirect = 0.105752, namelookup = 0.008729, 20 | connect = 0.128672, pretransfer = 0.158497, starttransfer = 0.220432, 21 | total = 0.220477)), class = "response") 22 | -------------------------------------------------------------------------------- /R/ohsome_contributions_count.R: -------------------------------------------------------------------------------- 1 | #' Count OSM contributions 2 | #' 3 | #' Creates an `ohsome_query` object for OSM contributions count 4 | #' 5 | #' `ohsome_contributions_count()` creates an `ohsome_query` object for 6 | #' OSM element aggregation. Boundary objects are passed via [set_boundary()] 7 | #' into [ohsome_boundary()]. 8 | #' 9 | #' @inherit ohsome_query params return 10 | #' @param latest logical; if `TRUE`, request only the latest contributions 11 | #' provided to each OSM element. 12 | #' @param return_value character; the value to be returned by the ohsome API: 13 | #' * `"absolute"` returns the absolute number of contributions. This is the 14 | #' default. 15 | #' * `"density"` returns the number of contributions per square kilometer. 16 | #' @param time character; `time` parameter of the query (see 17 | #' [Supported time formats](https://docs.ohsome.org/ohsome-api/v1/time.html)). 18 | #' @inherit ohsome_query return 19 | #' @seealso [ohsome API Endpoints - Contributions Aggregation](https://docs.ohsome.org/ohsome-api/v1/endpoints.html#contributions-aggregation) 20 | #' @export 21 | #' @examples 22 | #' # Monthly counts of contributions to man-made objects around "Null Island" 23 | #' ohsome_contributions_count("0,0,10", filter = "man_made=*", time = "2010/2020/P1Y") 24 | #' 25 | #' # Monthly counts of latest contributions to man-made objects around "Null Island" 26 | #' ohsome_contributions_count( 27 | #' "0,0,10", 28 | #' latest = TRUE, 29 | #' filter = "man_made=*", 30 | #' time = "2010/2020/P1Y" 31 | #' ) 32 | #' 33 | ohsome_contributions_count <- function( 34 | boundary = NULL, 35 | latest = FALSE, 36 | return_value = c("absolute", "density"), 37 | time = NULL, 38 | ... 39 | ) { 40 | return_value <- match.arg(return_value) 41 | if(latest) latest <- "latest" else latest <- NULL 42 | if(return_value == "absolute") return_value <- NULL 43 | 44 | q <- ohsome_query(c("contributions", latest, "count", return_value), boundary,...) 45 | set_time(q, time) 46 | } -------------------------------------------------------------------------------- /tests/testthat/test-ohsome_query.R: -------------------------------------------------------------------------------- 1 | test_that("requests geojson content with groupBy/boundary queries", { 2 | 3 | q1 <- ohsome_query(c("elements", "count", "groupBy", "boundary")) 4 | q2 <- ohsome_query("elements/length/density/groupBy/boundary") 5 | q3 <- ohsome_query("elements/count", grouping = "boundary") 6 | expect_equal(q1$body$format, "geojson") 7 | expect_equal(q2$body$format, "geojson") 8 | expect_equal(q3$body$format, "geojson") 9 | }) 10 | 11 | test_that("requests csv content with aggregation not grouped by boundary", { 12 | q1 <- ohsome_query(c("elements", "area", "ratio")) 13 | q2 <- ohsome_query("elements/count/groupBy/tag") 14 | expect_equal(q1$body$format, "csv") 15 | expect_equal(q2$body$format, "csv") 16 | }) 17 | 18 | test_that("accepts custom format parameter", { 19 | q <- ohsome_query(c("elements", "count"), format = "foo") 20 | expect_equal(q$body$format, "foo") 21 | }) 22 | 23 | test_that("does not set format parameter with elements extraction queries", { 24 | q1 <- ohsome_query(c("elements", "bbox")) 25 | q2 <- ohsome_query("elements/centroid") 26 | expect_null(q1$body$format) 27 | expect_null(q2$body$format) 28 | }) 29 | 30 | test_that("does not overwrite explicit format with groupBy/boundary", { 31 | q <- ohsome_query("elements/count", grouping = "boundary", format = "csv") 32 | expect_equal(q$body$format, "csv") 33 | }) 34 | 35 | test_that("returns object of class ohsome_query", { 36 | expect_s3_class(ohsome_query("elements/count"), "ohsome_query") 37 | }) 38 | 39 | test_that("issues warning if boundary and other bounding geom params are set", { 40 | expect_warning( 41 | ohsome_query("elements/count", boundary = "0,0,1,1", bboxes = "1,1,2,2") 42 | ) 43 | expect_warning( 44 | ohsome_query("elements/count", boundary = "0,0,1,1", bcircles = "0,0,1000") 45 | ) 46 | expect_warning( 47 | ohsome_query("elements/count", boundary = "0,0,1,1", bpolys = "foo") 48 | ) 49 | }) 50 | 51 | test_that("correctly sets bboxes param based on boundary argument", { 52 | bbox <- "0,0,1,1" 53 | q <- ohsome_query("elements/count", boundary = bbox) 54 | expect_equal(q$body$bboxes, bbox) 55 | }) 56 | 57 | -------------------------------------------------------------------------------- /tests/testthat/test-set_endpoint.R: -------------------------------------------------------------------------------- 1 | test_that("replaces endpoint in ohsome_query by default (append = FALSE)", { 2 | 3 | q1 <- ohsome_query("elements/count") 4 | q2 <- set_endpoint(q1, "elements/length") 5 | expect_equal(httr::parse_url(q2$url)$path, "v1/elements/length") 6 | }) 7 | 8 | test_that("appends endpoint if append = TRUE", { 9 | 10 | q1 <- ohsome_query("elements/count") 11 | q2 <- set_endpoint(q1, c("groupBy", "boundary"), append = TRUE) 12 | expect_equal(httr::parse_url(q2$url)$path, "v1/elements/count/groupBy/boundary") 13 | }) 14 | 15 | test_that("resets response format correctly according to reset_format argument",{ 16 | q1 <- ohsome_query("elements/count") 17 | q2 <- set_endpoint(q1, c("groupBy", "boundary"), append = TRUE, reset_format = FALSE) 18 | q3 <- set_endpoint(q1, c("groupBy", "boundary"), append = TRUE) 19 | q4 <- set_endpoint(q1, c("elements/geometry")) 20 | expect_equal(q2$body$format, "csv") 21 | expect_equal(q3$body$format, "geojson") 22 | expect_null(q4$body$format) 23 | }) 24 | 25 | test_that("returns unmodified endpoint if grouping arg is missing", { 26 | q1 <- ohsome_query("elements/count/groupBy/boundary/groupBy/tag") 27 | q2 <- set_grouping(q1) 28 | expect_equal(httr::parse_url(q1$url)$path, httr::parse_url(q2$url)$path) 29 | 30 | q1 <- ohsome_query("elements/count") 31 | q2 <- set_grouping(q1) 32 | expect_equal(httr::parse_url(q1$url)$path, httr::parse_url(q2$url)$path) 33 | }) 34 | 35 | test_that("correctly modifies grouping", { 36 | q1 <- ohsome_query("elements/count/groupBy/boundary") 37 | q2 <- set_grouping(q1, "tag") 38 | expect_equal(httr::parse_url(q2$url)$path, "v1/elements/count/groupBy/tag") 39 | 40 | q1 <- ohsome_query("elements/count") 41 | q2 <- set_grouping(q1, "tag") 42 | expect_equal(httr::parse_url(q2$url)$path, "v1/elements/count/groupBy/tag") 43 | 44 | q1 <- ohsome_query("elements/count") 45 | q2 <- set_grouping(q1, c("Boundary", "Tag")) 46 | expect_equal(httr::parse_url(q2$url)$path, "v1/elements/count/groupBy/boundary/groupBy/tag") 47 | }) 48 | 49 | test_that("removes grouping if grouping = NULL", { 50 | q1 <- ohsome_query("elements/count/groupBy/boundary") 51 | q2 <- set_grouping(q1, grouping = NULL) 52 | expect_equal(httr::parse_url(q2$url)$path, "v1/elements/count") 53 | }) -------------------------------------------------------------------------------- /tests/testthat/test-set_parameters.R: -------------------------------------------------------------------------------- 1 | test_that("sets and modifies parameters of ohsome_query", { 2 | 3 | q <- ohsome_query("elements/count") %>% 4 | set_parameters(foo = "bar", foo2 = "bar") %>% 5 | set_parameters(foo = "baz") 6 | 7 | expect_equal(q$body$foo, "baz") 8 | expect_equal(q$body$foo2, "bar") 9 | }) 10 | 11 | test_that("returns unmodified query if no args defined", { 12 | q1 <- ohsome_query("elements/count", filter = "foo") 13 | q2 <- set_parameters(q1) 14 | expect_equal(q1, q2) 15 | }) 16 | 17 | test_that("returns unmodified query if filter arg is missing", { 18 | q1 <- ohsome_query("elements/count", filter = "foo") 19 | q2 <- set_filter(q1) 20 | expect_equal(q1$body$filter, q2$body$filter) 21 | }) 22 | 23 | test_that("removes filter with arg filter = NULL", { 24 | q1 <- ohsome_query("elements/count", filter = "foo") 25 | q2 <- set_filter(q1, filter = NULL) 26 | expect_null(q2$body$filter) 27 | }) 28 | 29 | # set properties 30 | test_that("removes properties from query body by default", { 31 | q <- ohsome_query("elements/centroid", properties = "tags") 32 | expect_null(set_properties(q)$body$properties) 33 | }) 34 | 35 | test_that("correctly sets properties", { 36 | q <- ohsome_query("elements/centroid") 37 | 38 | expect_equal( 39 | set_properties(q, "tags")$body$properties, 40 | "tags" 41 | ) 42 | expect_equal( 43 | set_properties(q, "metadata, tags")$body$properties, 44 | "metadata,tags" 45 | ) 46 | expect_equal( 47 | set_properties(q, c("tags", "metadata"))$body$properties, 48 | "tags,metadata" 49 | ) 50 | }) 51 | 52 | test_that("throws error on properties argument 'foo'", { 53 | 54 | q <- ohsome_query("elements/centroid") 55 | 56 | expect_error(set_properties(q, "foo")) 57 | }) 58 | 59 | test_that("removes 'contributionTypes' from element extraction query", { 60 | 61 | q <- ohsome_query("elements/elements/bbox") 62 | 63 | expect_equal( 64 | set_properties(q, c("tags", "contributionTypes"))$body$properties, 65 | "tags" 66 | ) 67 | }) 68 | 69 | test_that("accepts 'contributionTypes' in contribution extraction", { 70 | 71 | q <- ohsome_query("elements/contributions/bbox") 72 | 73 | expect_equal( 74 | set_properties(q, c("tags", "contributionTypes"))$body$properties, 75 | "tags,contributionTypes" 76 | ) 77 | }) 78 | -------------------------------------------------------------------------------- /man/ohsome_post.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ohsome_post.R 3 | \name{ohsome_post} 4 | \alias{ohsome_post} 5 | \title{Send POST request to ohsome API} 6 | \usage{ 7 | ohsome_post( 8 | query, 9 | parse = TRUE, 10 | validate = TRUE, 11 | strict = validate, 12 | additional_identifiers = NULL, 13 | ... 14 | ) 15 | } 16 | \arguments{ 17 | \item{query}{An \code{ohsome_query} object constructed with \code{\link[=ohsome_query]{ohsome_query()}} or 18 | any of its wrapper functions} 19 | 20 | \item{parse}{logical; if \code{TRUE}, parse the ohsome API response with 21 | \code{\link[=ohsome_parse]{ohsome_parse()}}} 22 | 23 | \item{validate}{logical; if \code{TRUE}, issues warning for invalid endpoint or 24 | invalid/missing query parameters.} 25 | 26 | \item{strict}{logical; If \code{TRUE}, throws error on invalid query. Overrules 27 | validate argument \code{TRUE}.} 28 | 29 | \item{additional_identifiers}{vector coercible to character; optional user 30 | agent identifiers in addition to \code{"ohsome-r/{version}"}.} 31 | 32 | \item{...}{Additional arguments passed to \code{\link[=ohsome_parse]{ohsome_parse()}}: 33 | \itemize{ 34 | \item \code{returnclass}: class of the returned object 35 | \item \code{omit_empty}: logical; omit features with empty geometries (only if 36 | \code{returnclass = "sf"}) 37 | }} 38 | } 39 | \value{ 40 | An \code{ohsome_response} object if \code{parse = FALSE}, else an \code{sf} object, 41 | a \code{data.frame}, a \code{list} or a \code{character} 42 | } 43 | \description{ 44 | Sends an \code{ohsome_query} object as a POST request to the ohsome API and 45 | returns the response. 46 | } 47 | \examples{ 48 | \dontrun{ 49 | # Get bounding box of the city of Berlin 50 | bbberlin <- osmdata::getbb("Berlin") 51 | 52 | # Query for cinema geometries within bounding box 53 | q <- ohsome_elements_geometry(bbberlin, filter = "amenity=cinema") 54 | 55 | # Send query to ohsome API and return sf by default 56 | ohsome_post(q) 57 | 58 | # Send query to ohsome API and return data.frame 59 | ohsome_post(q, returnclass = "data.frame") 60 | 61 | # Send query and return unparsed response 62 | ohsome_post(q, parse = FALSE) 63 | } 64 | 65 | } 66 | \seealso{ 67 | \href{https://docs.ohsome.org/ohsome-api/v1/}{ohsome API documentation} 68 | } 69 | -------------------------------------------------------------------------------- /R/ohsome_get_metadata.R: -------------------------------------------------------------------------------- 1 | #' GET metadata from ohsome API 2 | #' 3 | #' Returns parsed metadata from ohsome API 4 | #' 5 | #' `ohsome_get_metadata()` sends a GET request to the metadata endpoint of 6 | #' ohsome API and parses the response. The parsed metadata is silently returned. 7 | #' 8 | #' @param quiet logical; suppresses message on data attribution, API version and 9 | #' temporal extent. 10 | #' @return An `ohsome_metadata` object. This is a named list with the 11 | #' attributes `date`, `status_code` (of the GET request) and the following list 12 | #' elements: 13 | #' * `attribution`: character; `url` and `text` of OSM data copyrights and 14 | #' attribution 15 | #' * `apiVersion`: character; Version of the ohsome API 16 | #' * `timeout`: numeric; limit of the processing time in seconds 17 | #' * `extractRegion`: 18 | #' * `spatialExtent`: sfc_POLYGON; spatial boundary of the OSM data in the 19 | #' underlying OSHDB 20 | #' * `temporalExtent`: vector of ISO 8601 character; start and end of the temporal extent of OSM data in the 21 | #' underlying OSHDB 22 | #' * `replicationSequenceNumber`: numeric; precise state of the OSM data 23 | #' contained in the underlying OSHDB, expressed as the id of the last 24 | #' applied (hourly) diff file from [Planet OSM](https://planet.openstreetmap.org) 25 | #' @seealso [ohsome API Endpoints -- Metadata](https://docs.ohsome.org/ohsome-api/v1/endpoints.html#metadata) 26 | #' @export 27 | #' @examples 28 | #' \dontrun{ 29 | #' ohsome_get_metadata() 30 | #' } 31 | 32 | ohsome_get_metadata <- function(quiet = FALSE) { 33 | 34 | response <- httr::GET( 35 | url = build_endpoint_url("metadata"), 36 | httr::user_agent(paste("ohsome-r", utils::packageVersion("ohsome"), sep = "/")) 37 | ) 38 | 39 | httr::stop_for_status(response) 40 | 41 | parsed <- ohsome_parse(response, returnclass = "list") 42 | spatialExtent <- parsed$extractRegion$spatialExtent 43 | parsed$extractRegion$spatialExtent <- convert_spatialExtent(spatialExtent) 44 | 45 | ohsome_metadata <- structure( 46 | .Data = parsed, 47 | status_code = httr::status_code(response), 48 | date = httr::headers(response)$date, 49 | class = "ohsome_metadata" 50 | ) 51 | 52 | if(!quiet) message(create_metadata_message(ohsome_metadata)) 53 | invisible(ohsome_metadata) 54 | } 55 | -------------------------------------------------------------------------------- /man/ohsome_parse.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ohsome_parse.R 3 | \name{ohsome_parse} 4 | \alias{ohsome_parse} 5 | \alias{ohsome_sf} 6 | \alias{ohsome_df} 7 | \title{Parse content from an ohsome API response} 8 | \usage{ 9 | ohsome_parse( 10 | response, 11 | returnclass = c("default", "sf", "data.frame", "list", "character"), 12 | omit_empty = TRUE 13 | ) 14 | 15 | ohsome_sf(response, omit_empty = TRUE) 16 | 17 | ohsome_df(response, omit_empty = TRUE) 18 | } 19 | \arguments{ 20 | \item{response}{An \code{ohsome_response} object} 21 | 22 | \item{returnclass}{character; one of the following: 23 | \itemize{ 24 | \item \code{"default"} returns \code{sf} if the \code{ohsome_response} contains GeoJSON, or 25 | else a \code{data.frame}. 26 | \item \code{"sf"} returns \code{sf} if the \code{ohsome_response} contains GeoJSON, else 27 | issues a warning and returns a \code{data.frame}. 28 | \item \code{"data.frame"} returns a \code{data.frame}. 29 | \item \code{"list"} returns a \code{list}. 30 | \item \code{"character"} returns the ohsome API response body as text (JSON or 31 | semicolon-separated values) 32 | }} 33 | 34 | \item{omit_empty}{logical; omit features with empty geometries (only if 35 | \code{returnclass = "sf"})} 36 | } 37 | \value{ 38 | An \code{sf} object, a \code{data.frame}, a \code{list} or a \code{character} 39 | } 40 | \description{ 41 | Extracts and parses the content from an ohsome API response 42 | } 43 | \details{ 44 | \code{ohsome_parse()} parses an \code{ohsome_response} object into an object of the 45 | specified class. By default, this is an \code{sf} object if the ohsome API 46 | response contains GeoJSON data or a \code{data.frame} if it does not. 47 | \code{ohsome_sf()} and \code{ohsome_df()} wrapper functions for specific return 48 | classes. 49 | } 50 | \examples{ 51 | \dontrun{ 52 | # Create and send a query to ohsome API 53 | r <- ohsome_query("elements/centroid", filter = "amenity=*") |> 54 | set_boundary(osmdata::getbb("Heidelberg")) |> 55 | set_time("2021") |> 56 | set_properties("metadata") |> 57 | ohsome_post(parse = FALSE) 58 | 59 | # Parse response to object of default class (here: sf) 60 | ohsome_parse(r) 61 | 62 | # Parse response to data.frame 63 | ohsome_df(r) 64 | 65 | # Parse response to sf 66 | ohsome_sf(r) 67 | } 68 | 69 | } 70 | \concept{Extract and parse the content from an ohsome API response} 71 | -------------------------------------------------------------------------------- /R/ohsome_extract_elementsFullHistory.R: -------------------------------------------------------------------------------- 1 | #' Extract OSM elements' full history 2 | #' 3 | #' Creates an `ohsome_query` object for the extraction of OSM elements' full 4 | #' history 5 | #' 6 | #' `ohsome_extract_elementsFullHistory()` creates an `ohsome_query` object for OSM 7 | #' element full history extraction. `ohsome_elementsFullHistory_bbox()`, 8 | #' `ohsome_elementsFullHistory_centroid()` and 9 | #' `ohsome_elementsFullHistory_geometry()` are wrapper functions for specific 10 | #' elementsFullHistory extraction endpoints. Boundary objects are passed via 11 | #' [set_boundary()] into [ohsome_boundary()]. 12 | #' 13 | #' @inherit ohsome_query params return 14 | #' @inheritParams ohsome_contributions_count 15 | #' @inheritParams ohsome_extract_elements 16 | #' @family Extract elements full History 17 | #' @seealso [ohsome API Endpoints -- Elements Full History Extraction](https://docs.ohsome.org/ohsome-api/v1/endpoints.html#elements-full-history-extraction) 18 | #' @export 19 | #' @examples 20 | #' 21 | #' # Extract full history of building geometries around Heidelberg main station: 22 | #' ohsome_elementsFullHistory_geometry( 23 | #' boundary = "8.67542,49.40347,1000", 24 | #' time = "2012,2022", 25 | #' filter = "building=* and geometry:polygon", 26 | #' clipGeometry = FALSE 27 | #' ) 28 | 29 | ohsome_extract_elementsFullHistory <- function( 30 | boundary = NULL, 31 | geometryType = c("centroid", "bbox", "geometry"), 32 | time = NULL, 33 | properties = NULL, 34 | clipGeometry = TRUE, 35 | ... 36 | ) { 37 | geometryType <- match.arg(geometryType) 38 | q <- ohsome_query(c("elementsFullHistory", geometryType), boundary, ...) 39 | q <- set_properties(q, properties) 40 | q <- set_parameters(q, clipGeometry = as.character(clipGeometry)) 41 | q <- set_time(q, time) 42 | return(q) 43 | } 44 | 45 | #' @export 46 | #' @rdname ohsome_extract_elementsFullHistory 47 | ohsome_elementsFullHistory_bbox <- function(boundary = NULL, ...) { 48 | ohsome_extract_elementsFullHistory(boundary, geometryType = "bbox", ...) 49 | } 50 | 51 | #' @export 52 | #' @rdname ohsome_extract_elementsFullHistory 53 | ohsome_elementsFullHistory_centroid <- function(boundary = NULL, ...) { 54 | ohsome_extract_elementsFullHistory(boundary, geometryType = "centroid", ...) 55 | } 56 | 57 | #' @export 58 | #' @rdname ohsome_extract_elementsFullHistory 59 | ohsome_elementsFullHistory_geometry <- function(boundary = NULL, ...) { 60 | ohsome_extract_elementsFullHistory(boundary, geometryType = "geometry", ...) 61 | } -------------------------------------------------------------------------------- /R/utils.R: -------------------------------------------------------------------------------- 1 | #' Build URL to an ohsome API endpoint 2 | #' 3 | #' @param endpoint character (atomic or vector) 4 | #' @return character 5 | #' @keywords Internal 6 | #' @noRd 7 | build_endpoint_url <- function(endpoint) { 8 | trimws( 9 | httr::modify_url( 10 | ohsome::ohsome_api_url$base, 11 | path = c(ohsome::ohsome_api_url$version, endpoint) 12 | ), 13 | whitespace = "/" 14 | ) 15 | } 16 | 17 | 18 | #' Convert spatialExtent of ohsome metadata 19 | #' 20 | #' @param spatialExtent The `$extractRegion$spatialExtent` element of the 21 | #' parsed content of a response from the metadata endpoint of ohsome API 22 | #' @return An `sfc_POLYGON` object 23 | #' @keywords Internal 24 | #' @noRd 25 | convert_spatialExtent <- function(spatialExtent) { 26 | 27 | if(spatialExtent$type != "Polygon") return(spatialExtent) 28 | 29 | coords <- list(matrix(spatialExtent$coordinates, ncol = 2)) 30 | sf::st_sfc(sf::st_polygon(coords), crs = 4326) 31 | } 32 | 33 | 34 | #' Create metadata message 35 | #' 36 | #' Creates a message text from an `ohsome_metadata` object. 37 | #' 38 | #' @param meta An [ohsome_metadata] object 39 | #' @return character 40 | #' @keywords Internal 41 | #' @noRd 42 | create_metadata_message <- function(meta) { 43 | sprintf( 44 | "Data: %s %s\nohsome API version: %s\nTemporal extent: %s to %s", 45 | meta$attribution$text, 46 | meta$attribution$url, 47 | meta$apiVersion, 48 | meta$extractRegion$temporalExtent[1], 49 | meta$extractRegion$temporalExtent[2] 50 | ) 51 | } 52 | 53 | 54 | #' Extract endpoint 55 | #' 56 | #' Extracts the API endpoint path from the URL in an `ohsome_query` object 57 | #' @inheritParams ohsome_post 58 | #' @return character 59 | #' @keywords Internal 60 | #' @noRd 61 | extract_endpoint <- function(query) { 62 | gsub("^.*?/", "", httr::parse_url(query$url)$path) 63 | } 64 | 65 | 66 | #' Type convert without message 67 | #' 68 | #' Converts types of data.frame columns with [readr::type_convert()] while 69 | #' suppressing messages 70 | #' 71 | #' @param df data.frame 72 | #' @return data.frame 73 | #' @keywords Internal 74 | #' @noRd 75 | convert_quietly <- function(df) suppressMessages(readr::type_convert(df)) 76 | 77 | 78 | #' Null coalesce operator 79 | #' 80 | #' Operator that returns the left-hand side operand if it is not `NULL`, else 81 | #' returns the right-hand side operand 82 | #' 83 | #' @name null-coalesce 84 | #' @param a left-side argument 85 | #' @param b right-side argument 86 | #' @keywords Internal 87 | #' @noRd 88 | `%||%` <- function(a, b) if (is.null(a)) b else a 89 | -------------------------------------------------------------------------------- /tests/testthat/mock_api/404/api.ohsome.org/v1/elements/amount-754ee5-POST.R: -------------------------------------------------------------------------------- 1 | structure(list(url = "https://api.ohsome.org/v1/elements/amount", 2 | status_code = 404L, headers = structure(list(date = "Tue, 22 Jun 2021 15:13:36 GMT", 3 | server = "Apache/2.4.29 (Ubuntu)", `access-control-allow-origin` = "*", 4 | `access-control-allow-methods` = "POST, GET", `access-control-max-age` = "3600", 5 | `access-control-allow-credentials` = "true", `access-control-allow-headers` = "Origin,Accept,X-Requested-With,Content-Type,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization", 6 | `content-disposition` = "attachment;filename=ohsome.csv", 7 | `content-type` = "application/json;charset=UTF-8", `content-encoding` = "gzip", 8 | vary = "Accept-Encoding", `cache-control` = "max-age=0, no-store", 9 | `transfer-encoding` = "chunked"), class = c("insensitive", 10 | "list")), all_headers = list(list(status = 404L, version = "HTTP/1.1", 11 | headers = structure(list(date = "Tue, 22 Jun 2021 15:13:36 GMT", 12 | server = "Apache/2.4.29 (Ubuntu)", `access-control-allow-origin` = "*", 13 | `access-control-allow-methods` = "POST, GET", `access-control-max-age` = "3600", 14 | `access-control-allow-credentials` = "true", `access-control-allow-headers` = "Origin,Accept,X-Requested-With,Content-Type,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization", 15 | `content-disposition` = "attachment;filename=ohsome.csv", 16 | `content-type` = "application/json;charset=UTF-8", 17 | `content-encoding` = "gzip", vary = "Accept-Encoding", 18 | `cache-control` = "max-age=0, no-store", `transfer-encoding` = "chunked"), class = c("insensitive", 19 | "list")))), cookies = structure(list(domain = logical(0), 20 | flag = logical(0), path = logical(0), secure = logical(0), 21 | expiration = structure(numeric(0), class = c("POSIXct", 22 | "POSIXt")), name = logical(0), value = logical(0)), row.names = integer(0), class = "data.frame"), 23 | content = charToRaw("{\n \"timestamp\" : \"2021-06-22T15:13:36.122+0000\",\n \"status\" : 404,\n \"error\" : \"Not Found\",\n \"message\" : \"No message available\",\n \"path\" : \"/elements/amount\"\n}"), 24 | date = structure(1624374816, class = c("POSIXct", "POSIXt" 25 | ), tzone = "GMT"), times = c(redirect = 0, namelookup = 0.015879, 26 | connect = 0.113829, pretransfer = 0.155543, starttransfer = 0, 27 | total = 0.248149)), class = "response") 28 | -------------------------------------------------------------------------------- /R/ohsome_post.R: -------------------------------------------------------------------------------- 1 | #' Send POST request to ohsome API 2 | #' 3 | #' Sends an `ohsome_query` object as a POST request to the ohsome API and 4 | #' returns the response. 5 | #' 6 | #' @param query An `ohsome_query` object constructed with [ohsome_query()] or 7 | #' any of its wrapper functions 8 | #' @param parse logical; if `TRUE`, parse the ohsome API response with 9 | #' [ohsome_parse()] 10 | #' @param validate logical; if `TRUE`, issues warning for invalid endpoint or 11 | #' invalid/missing query parameters. 12 | #' @param strict logical; If `TRUE`, throws error on invalid query. Overrules 13 | #' validate argument `TRUE`. 14 | #' @param additional_identifiers vector coercible to character; optional user 15 | #' agent identifiers in addition to `"ohsome-r/{version}"`. 16 | #' @param ... Additional arguments passed to [ohsome_parse()]: 17 | #' * `returnclass`: class of the returned object 18 | #' * `omit_empty`: logical; omit features with empty geometries (only if 19 | #' `returnclass = "sf"`) 20 | #' @return An `ohsome_response` object if `parse = FALSE`, else an `sf` object, 21 | #' a `data.frame`, a `list` or a `character` 22 | #' @seealso [ohsome API documentation](https://docs.ohsome.org/ohsome-api/v1/) 23 | #' @export 24 | #' @examples 25 | #' \dontrun{ 26 | #' # Get bounding box of the city of Berlin 27 | #' bbberlin <- osmdata::getbb("Berlin") 28 | #' 29 | #' # Query for cinema geometries within bounding box 30 | #' q <- ohsome_elements_geometry(bbberlin, filter = "amenity=cinema") 31 | #' 32 | #' # Send query to ohsome API and return sf by default 33 | #' ohsome_post(q) 34 | #' 35 | #' # Send query to ohsome API and return data.frame 36 | #' ohsome_post(q, returnclass = "data.frame") 37 | #' 38 | #' # Send query and return unparsed response 39 | #' ohsome_post(q, parse = FALSE) 40 | #' } 41 | #' 42 | ohsome_post <- function( 43 | query, 44 | parse = TRUE, 45 | validate = TRUE, 46 | strict = validate, 47 | additional_identifiers = NULL, 48 | ... 49 | ) { 50 | if(validate || strict) valid <- validate_query(query) 51 | stopifnot("Query invalid. See warnings for details." = !strict || valid) 52 | 53 | if(is.null(additional_identifiers)) additional_identifiers <- "" 54 | user_agent <- trimws(sprintf( 55 | "%s %s/%s", 56 | paste(as.character(additional_identifiers), collapse = " "), 57 | "ohsome-r", utils::packageVersion("ohsome") 58 | )) 59 | 60 | response <- httr::POST( 61 | url = query$url, 62 | body = query$body, 63 | encode = query$encode, 64 | httr::user_agent(user_agent) 65 | ) 66 | 67 | attr(response, "request_body") <- query$body 68 | attr(response, "class") <- c("ohsome_response", "response") 69 | 70 | httr::stop_for_status(response) 71 | 72 | if(parse) { return(ohsome_parse(response, ...)) } else { return(response) } 73 | } 74 | -------------------------------------------------------------------------------- /R/ohsome_extract_elements.R: -------------------------------------------------------------------------------- 1 | #' Extract OSM elements 2 | #' 3 | #' Create an `ohsome_query` object for OSM element extraction 4 | #' 5 | #' `ohsome_extract_elements()` creates an `ohsome_query` object for OSM element 6 | #' extraction. `ohsome_elements_bbox()`, `ohsome_elements_centroid()` and 7 | #' `ohsome_elements_geometry()` are wrapper functions for specific elements 8 | #' extraction endpoints. Boundary objects are passed via [set_boundary()] into 9 | #' [ohsome_boundary()]. 10 | #' 11 | #' @inherit ohsome_query params return 12 | #' @inheritParams ohsome_aggregate_elements 13 | #' @param geometryType character; type of geometry to be extracted: 14 | #' * `"centroid"`, 15 | #' * `"bboxes"` (bounding boxes), or 16 | #' * `"geometry"` 17 | #' 18 | #' Caveat: Node elements are omitted from results in queries for bounding 19 | #' boxes. 20 | #' @param properties character; properties to be extracted with the features: 21 | #' * `"tags"`, and/or 22 | #' * `"metadata"` (i.e. `@changesetId`, `@lastEdit`, `@osmType`, and 23 | #' `@version`) 24 | #' 25 | #' Multiple values can be provided as comma-separated character or as 26 | #' character vector. This defaults to `NULL` (provides `@osmId`). 27 | #' @param clipGeometry logical; specifies whether the returned geometries should 28 | #' be clipped to the query’s spatial boundary 29 | #' @family Extract elements 30 | #' @seealso [ohsome API Endpoints -- Elements Extraction](https://docs.ohsome.org/ohsome-api/stable/endpoints.html#elements-extraction) 31 | #' @export 32 | #' @examples 33 | #' # Extract geometries, metadata and tags of man-made objects around "Null Island": 34 | #' ohsome_elements_geometry( 35 | #' "0,0,10", 36 | #' filter = "man_made=*", 37 | #' time = "2022-01-01", 38 | #' properties = c("metadata", "tags") 39 | #' ) 40 | #' 41 | ohsome_extract_elements <- function( 42 | boundary = NULL, 43 | geometryType = c("centroid", "bbox", "geometry"), 44 | time = NULL, 45 | properties = NULL, 46 | clipGeometry = TRUE, 47 | ... 48 | ) { 49 | geometryType <- match.arg(geometryType) 50 | q <- ohsome_query(c("elements", geometryType), boundary, ...) 51 | q <- set_properties(q, properties) 52 | q <- set_parameters(q, clipGeometry = as.character(clipGeometry)) 53 | q <- set_time(q, time) 54 | return(q) 55 | } 56 | 57 | #' @export 58 | #' @rdname ohsome_extract_elements 59 | ohsome_elements_bbox <- function(boundary = NULL, ...) { 60 | ohsome_extract_elements(boundary, geometryType = "bbox", ...) 61 | } 62 | 63 | #' @export 64 | #' @rdname ohsome_extract_elements 65 | ohsome_elements_centroid <- function(boundary = NULL, ...) { 66 | ohsome_extract_elements(boundary, geometryType = "centroid", ...) 67 | } 68 | 69 | #' @export 70 | #' @rdname ohsome_extract_elements 71 | ohsome_elements_geometry <- function(boundary = NULL, ...) { 72 | ohsome_extract_elements(boundary, geometryType = "geometry", ...) 73 | } 74 | -------------------------------------------------------------------------------- /R/ohsome_extract_contributions.R: -------------------------------------------------------------------------------- 1 | #' Extract OSM contributions 2 | #' 3 | #' Creates an `ohsome_query` object for OSM contribution extraction 4 | #' 5 | #' `ohsome_extract_contributions()` creates an `ohsome_query` object for OSM 6 | #' contribution extraction. `ohsome_contributions_bbox()`, 7 | #' `ohsome_contributions_centroid()` and `ohsome_contributions_geometry()` 8 | #' are wrapper functions for specific contributions extraction endpoints. 9 | #' Boundary objects are passed via [set_boundary()] into [ohsome_boundary()]. 10 | #' 11 | #' @inherit ohsome_query params return 12 | #' @inheritParams ohsome_contributions_count 13 | #' @inheritParams ohsome_extract_elements 14 | #' @param properties character; properties to be extracted with the 15 | #' contributions: 16 | #' * `"tags"`, and/or 17 | #' * `"metadata"` (i.e. `@changesetId`, `@lastEdit`, `@osmType`, 18 | #' `@version`), and/or 19 | #' * `"contributionTypes"` (i.e. `@creation`, `@tagChange`, `@deletion`, and 20 | #' `@geometryChange`) 21 | #' 22 | #' Multiple values can be provided as comma-separated character or as 23 | #' character vector. This defaults to `NULL` (provides 24 | #' `@contributionChangesetId`, `@osmId` and `@timestamp`). 25 | #' @family Extract contributions 26 | #' @seealso [ohsome API Endpoints -- Contributions Extraction](https://docs.ohsome.org/ohsome-api/stable/endpoints.html#contributions-extraction) 27 | #' @export 28 | #' @examples 29 | #' # Extract contributions to man-made objects around "Null Island" with metadata: 30 | #' ohsome_contributions_geometry( 31 | #' "0,0,10", 32 | #' filter = "man_made=*", 33 | #' time = c("2021-01-01", "2022-01-01"), 34 | #' properties = "metadata" 35 | #' ) 36 | #' 37 | ohsome_extract_contributions <- function( 38 | boundary = NULL, 39 | geometryType = c("centroid", "bbox", "geometry"), 40 | latest = FALSE, 41 | time = NULL, 42 | properties = NULL, 43 | clipGeometry = TRUE, 44 | ... 45 | ) { 46 | geometryType <- match.arg(geometryType) 47 | if(latest) latest <- "latest" else latest <- NULL 48 | q <- ohsome_query(c("contributions", latest, geometryType), boundary, ...) 49 | q <- set_properties(q, properties) 50 | q <- set_parameters(q, clipGeometry = as.character(clipGeometry)) 51 | q <- set_time(q, time) 52 | return(q) 53 | } 54 | 55 | #' @export 56 | #' @rdname ohsome_extract_contributions 57 | ohsome_contributions_bbox <- function(boundary = NULL, ...) { 58 | ohsome_extract_contributions(boundary, geometryType = "bbox", ...) 59 | } 60 | 61 | #' @export 62 | #' @rdname ohsome_extract_contributions 63 | ohsome_contributions_centroid <- function(boundary = NULL, ...) { 64 | ohsome_extract_contributions(boundary, geometryType = "centroid", ...) 65 | } 66 | 67 | #' @export 68 | #' @rdname ohsome_extract_contributions 69 | ohsome_contributions_geometry <- function(boundary = NULL, ...) { 70 | ohsome_extract_contributions(boundary, geometryType = "geometry", ...) 71 | } 72 | -------------------------------------------------------------------------------- /R/set_endpoint.R: -------------------------------------------------------------------------------- 1 | #' Set endpoint 2 | #' 3 | #' Modifies the endpoint of an existing `ohsome_query` object 4 | #' 5 | #' `set_endpoint()` takes an `ohsome_query` object and modifies the ohsome API 6 | #' endpoint. `set_grouping()` takes an `ohsome_query` object and modifies the 7 | #' endpoint path for grouped aggregations. 8 | #' 9 | #' @inherit ohsome_query params return 10 | #' @inheritParams ohsome_post 11 | #' @param append logical; If `TRUE`, the provided endpoint string is appended to 12 | #' the existing endpoint definition instead of replacing it. This is 13 | #' particularly useful if you wish to add `density`/`ratio` and/or a grouping 14 | #' to an existing aggregation query. 15 | #' @param reset_format logical; if `TRUE`, the format parameter of the query is 16 | #' updated depending on the new endpoint. 17 | #' @param ... Additional arguments passed to `set_endpoint()` 18 | #' @family Set endpoint 19 | #' @seealso [ohsome API Endpoints](https://docs.ohsome.org/ohsome-api/v1/endpoints.html) 20 | #' @export 21 | #' @examples 22 | #' # Query for count of elements 23 | #' q <- ohsome_elements_count( 24 | #' boundary = "HD:8.5992,49.3567,8.7499,49.4371|HN:9.1638,49.113,9.2672,49.1766", 25 | #' time = "2022-01-01", 26 | #' filter = "highway=*" 27 | #' ) 28 | #' 29 | #' # Modify query to aggregate length of elements instead of count 30 | #' set_endpoint(q, "elements/length") 31 | #' 32 | #' # Modify query to extract geometries instead of aggregating elements 33 | #' set_endpoint(q, "elements/geometry") 34 | #' 35 | #' # Append the endpoint path in order to group aggregation by boundary 36 | #' set_endpoint(q, "groupBy/boundary", append = TRUE) 37 | #' 38 | #' # Modify query to group aggregation by boundary 39 | #' set_grouping(q, grouping = "boundary") 40 | #' 41 | #' # Modify query to group by boundary, but keep format csv instead of geojson 42 | #' set_grouping(q, grouping = "boundary", reset_format = FALSE) 43 | #' 44 | #' # Append the endpoint path to query for element densities per boundary 45 | #' set_endpoint(q, c("density", "groupBy", "boundary"), append = TRUE) 46 | #' 47 | #' # Modify query to group aggregation by OSM element type 48 | #' set_grouping(q, grouping = "type") 49 | #' 50 | set_endpoint <- function(query, endpoint, append = FALSE, reset_format = TRUE) { 51 | 52 | if(append) { 53 | old <- gsub("^.*?/", "", httr::parse_url(query$url)$path) 54 | endpoint <- paste(old, paste(endpoint, collapse = "/"), sep = "/") 55 | } 56 | 57 | body <- query$body 58 | if(reset_format) body$format <- NULL 59 | 60 | return(do.call(ohsome_query, c(endpoint, body))) 61 | } 62 | 63 | #' @export 64 | #' @rdname set_endpoint 65 | set_grouping <- function(query, grouping, ...) { 66 | 67 | if(missing(grouping)) return(query) 68 | 69 | old <- gsub("^.*?/", "", httr::parse_url(query$url)$path) 70 | split <- unlist(strsplit(old, "/groupBy/")) 71 | if(!is.null(grouping)) { 72 | grouping <- paste("groupBy", tolower(grouping), sep = "/", collapse = "/") 73 | } 74 | endpoint <- paste(split[1], grouping, sep = "/") 75 | 76 | set_endpoint(query, endpoint, ...) 77 | } 78 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # ohsome (development version) 2 | 3 | # ohsome 0.2.2 4 | 5 | * On attach, the package now issues an informative startup message instead of a 6 | warning if it could not retrieve metadata information from the ohsome API. 7 | * Vignette code chunks do not evaluate on CRAN to avoid errors when internet 8 | resources are temporally unavailable 9 | * Fixed tests to use suggested packages conditionally 10 | 11 | # ohsome 0.2.1 12 | 13 | * Package CITATION file calls `bibentry()` instead of old-style `citEntry()`. 14 | * Fixed incomplete file URIs in docs. 15 | * Fixed missing `strict = FALSE` in query example in `README`. 16 | 17 | 18 | # ohsome 0.2.0 19 | 20 | * Added a `NEWS.md` file to track changes to the package. 21 | * Added `strict`argument to `ohsome_post`. When set to TRUE (default), an error 22 | is thrown on invalid queries and the request is **not** sent to the API. Queries 23 | with undefined filter or time parameters are considered as invalid in strict 24 | mode. 25 | * Changed behavior of `set_properties()`: Removes properties parameter from 26 | query body by default, accepts *tags* and/or *metadata* and/or 27 | *contributionTypes* as properties argument (multiple values provided as 28 | comma-separated character or character vector). 29 | * Changed behavior of `set_time()`, `set_filter()`, `set_groupByKeys()`, 30 | `set_groupByKey()` and `set_groupByValues`: Return unmodified query object if 31 | parameter argument is missing (e.g. `set_filter(query)`), but remove parameter 32 | from body if explicitly set to NULL (e.g. `set_filter(query, filter = NULL)`) 33 | * Added `grouping` argument to `ohsome_query()` and `set_grouping()`function. 34 | Based on `grouping`, the endpoint URL is appended so that aggregations are 35 | grouped accordingly. 36 | * Added `return_value` argument to `ohsome_aggregate_elements()`. Based on 37 | `return_value`, the endpoint URL is appended so that either absolute aggregate 38 | values, densities or ratios are requested from the ohsome API. 39 | * Added `ohsome_query()` wrapper functions `ohsome_extract_elements()`, 40 | `ohsome_elements_bbox`, `ohsome_elements_centroid` and `ohsome_elements_geometry` 41 | for elements extraction endpoints of ohsome API 42 | * Added `ohsome_query()` wrapper functions `ohsome_extract_elementsFullHistory()`, 43 | `ohsome_elementsFullHistory_bbox()`, `ohsome_elementsFullHistory_centroid()` and 44 | `ohsome_elementsFullHistory_geometry()` for elementsFullHistory extraction 45 | endpoints of ohsome API 46 | * Added `ohsome_query()` wrapper functions `ohsome_extract_contributions()`, 47 | `ohsome_contributions_bbox()`, `ohsome_contributios_centroid()` and 48 | `ohsome_contributions_geometry()` for contributions extraction endpoints of ohsome 49 | API 50 | * Added `ohsome_query()` wrapper functions `ohsome_contributions_count()` for 51 | contributions aggregation endpoints of ohsome API 52 | * Added `ohsome_query()` wrapper function `ohsome_users_count()` for user 53 | aggregation endpoints of ohsome API 54 | * `ohsome_medata` and `ohsome_temporalExtent` are assigned on loading (not on 55 | attaching) the package 56 | * `ohsome_metadata$extractRegion$temporalExtent` and `ohsome_temporalExtent` are 57 | no longer converted to POSIXct, but provided as ISO 8601 strings 58 | * Added endpoint-specific check for missing required parameters to query 59 | validation 60 | * Added validation of JSON response content 61 | * Updated `README` to reflect new features, added hints on boundary polygon 62 | acquisition through third-party packages 63 | * Added `CITATION` file with reference to OSHDB/ohsome API and technical paper 64 | -------------------------------------------------------------------------------- /tests/testthat/test-ohsome_boundary.R: -------------------------------------------------------------------------------- 1 | franconia <- suppressMessages(sf::st_set_crs(mapview::franconia, 4326)) 2 | breweries <- suppressMessages(sf::st_set_crs(mapview::breweries, 4326)) 3 | 4 | test_that("correctly detects boundary type for character objects", { 5 | 6 | bboxes1 <- paste( 7 | "8.5992,49.3567,8.7499,49.4371", 8 | "9.1638,49.113,9.2672,49.1766", 9 | sep = "|" 10 | ) 11 | bboxes2 <- paste( 12 | "Heidelberg:8.5992,49.3567,8.7499,49.4371", 13 | "Heilbronn:9.1638,49.113,9.2672,49.1766", 14 | sep = "|" 15 | ) 16 | bcircles1 <- paste( 17 | "8.6528,49.3683,1000", 18 | "8.7294,49.4376,1000", 19 | sep = "|" 20 | ) 21 | bcircles2 <- paste( 22 | "Circle 1:8.6528,49.3683,1000", 23 | "Circle 2:8.7294,49.4376,1000", 24 | sep = "|" 25 | ) 26 | bpolys1 <- paste( 27 | "8.65821,49.41129,8.65821,49.41825,8.70053,49.41825,8.70053,49.41129,8.65821,49.41129", 28 | "8.67817,49.42147,8.67817,49.4342,8.70053,49.4342,8.70053,49.42147,8.67817,49.42147", 29 | sep = "|" 30 | ) 31 | bpolys2 = paste( 32 | "Region 1:8.65821,49.41129,8.65821,49.41825,8.70053,49.41825,8.70053,49.41129,8.65821,49.41129", 33 | "Region 2:8.67817,49.42147,8.67817,49.4342,8.70053,49.4342,8.70053,49.42147,8.67817,49.42147", 34 | sep = "|" 35 | ) 36 | bpolys3 = '{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"id":"Region 1"},"geometry":{"type":"Polygon","coordinates":[[[8.65821,49.41129],[8.65821,49.41825],[8.70053,49.41825],[8.70053,49.41129],[8.65821,49.41129]]]}},{"type":"Feature","properties":{"id":"Region 2"},"geometry":{"type":"Polygon","coordinates":[[[8.67817,49.42147],[8.67817,49.4342],[8.70053,49.4342],[8.70053,49.42147],[8.67817,49.42147]]]}}]}' 37 | 38 | expect_equal(ohsome_boundary(bboxes1)$type, "bboxes") 39 | expect_equal(ohsome_boundary(bboxes2)$type, "bboxes") 40 | expect_equal(ohsome_boundary(bcircles1)$type, "bcircles") 41 | expect_equal(ohsome_boundary(bcircles2)$type, "bcircles") 42 | expect_equal(ohsome_boundary(bpolys1)$type, "bpolys") 43 | expect_equal(ohsome_boundary(bpolys2)$type, "bpolys") 44 | expect_equal(ohsome_boundary(bpolys3)$type, "bpolys") 45 | }) 46 | 47 | test_that("returns ohsome_boundary object", { 48 | 49 | bpolys <- paste( 50 | "8.65821,49.41129,8.65821,49.41825,8.70053,49.41825,8.70053,49.41129,8.65821,49.41129", 51 | "8.67817,49.42147,8.67817,49.4342,8.70053,49.4342,8.70053,49.42147,8.67817,49.42147", 52 | sep = "|" 53 | ) 54 | expect_s3_class(ohsome_boundary(bpolys), "ohsome_boundary") 55 | }) 56 | 57 | test_that("returns ohsome_boundary object with type = bpolys for sf boundaries", { 58 | expect_equal(ohsome_boundary(franconia)$type, "bpolys") 59 | }) 60 | 61 | test_that("returns ohsome_boundary object with type = bboxes for bbox boundaries", { 62 | expect_equal(ohsome_boundary(sf::st_bbox(franconia))$type, "bboxes") 63 | }) 64 | 65 | test_that("throws error on sf with point geometries only", { 66 | expect_error(ohsome_boundary(breweries)) 67 | }) 68 | 69 | test_that("issues warning for sf boundaries that contain polygon and other geoms", { 70 | 71 | skip_if_not_installed("dplyr") 72 | 73 | mixed <- dplyr::bind_rows(franconia, breweries) 74 | expect_warning(ohsome_boundary(mixed)) 75 | }) 76 | 77 | test_that("creates ohsome_boundary object from list of bboxes of various classes", { 78 | 79 | skip_if_not_installed("osmdata") 80 | 81 | bboxes1 <- c( 82 | "8.5992,49.3567,8.7499,49.4371", 83 | "9.1638,49.113,9.2672,49.1766" 84 | ) 85 | # output of dput(osmdata::getbb("Berlin")) 86 | bboxes2 <- structure( 87 | c(13.088345, 52.3382448, 13.7611609, 52.6755087), 88 | dim = c(2L, 2L), 89 | dimnames = list(c("x", "y"), c("min", "max")) 90 | ) 91 | bboxes3 <- sf::st_bbox(breweries) 92 | 93 | b <- ohsome_boundary(list(bboxes1, bboxes2, bboxes3)) 94 | expect_s3_class(b, "ohsome_boundary") 95 | }) 96 | -------------------------------------------------------------------------------- /man/set_boundary.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/set_boundary.R 3 | \name{set_boundary} 4 | \alias{set_boundary} 5 | \title{Set boundary} 6 | \usage{ 7 | set_boundary(query, boundary = NULL, ...) 8 | } 9 | \arguments{ 10 | \item{query}{An \code{ohsome_query} object constructed with \code{\link[=ohsome_query]{ohsome_query()}} or 11 | any of its wrapper functions} 12 | 13 | \item{boundary}{Bounding geometries specified by WGS84 coordinates in the 14 | order \verb{lon,lat}. The geometries of \code{sf} are transformed to WGS84 if the CRS 15 | of the object is known. The following classes are supported: 16 | \itemize{ 17 | \item \code{sf} with (MULTI)POLYGON geometries 18 | \item \code{sfc} with (MULTI)POLYGON geometries 19 | \item \code{sfg} with (MULTI)POLYGON geometries and WGS 84 coordinates 20 | \item \code{bbox} created with \code{\link[sf:st_bbox]{sf::st_bbox()}} or \code{\link[tmaptools:bb]{tmaptools::bb()}} 21 | \item \code{matrix} created with \code{\link[sp:bbox]{sp::bbox()}} or \code{\link[osmdata:getbb]{osmdata::getbb()}} 22 | \item \code{character} providing textual definitions of bounding polygons, boxes or 23 | circles as allowed by the ohsome API (see 24 | \href{https://docs.ohsome.org/ohsome-api/stable/boundaries.html}{ohsome API - Boundaries} 25 | ): 26 | \itemize{ 27 | \item bboxes: WGS84 coordinates in the following format: 28 | \code{"id1:lon1,lat1,lon2,lat2|id2:lon1,lat1,lon2,lat2|..."} OR 29 | \code{"lon1,lat1,lon2,lat2|lon1,lat1,lon2,lat2|..."} 30 | \item bcircles: WGS84 coordinates + radius in meter in the following 31 | format: \code{"id1:lon,lat,r|id2:lon,lat,r|..."} OR 32 | \code{"lon,lat,r|lon,lat,r|..."} 33 | \item bpolys: WGS84 coordinates given as a list of coordinate pairs (as for 34 | bboxes) or GeoJSON FeatureCollection. The first point has to be the same 35 | as the last point and MultiPolygons are only supported in GeoJSON. 36 | } 37 | \item \code{list} of \code{bbox}, \code{matrix} or \code{character}. Bounding geometry types of all 38 | list elements must be the same. Does not work with GeoJSON 39 | FeatureCollections. 40 | }} 41 | 42 | \item{...}{Additional arguments other than \code{digits} are ignored.} 43 | } 44 | \value{ 45 | An \code{ohsome_query} object. The object can be sent to the ohsome API 46 | with \code{\link[=ohsome_post]{ohsome_post()}}. It consists of the following elements: 47 | \itemize{ 48 | \item \code{url}: The URL of the endpoint. 49 | \item \code{encode}: The way the information is encoded and then posted to the 50 | ohsome API. Set as \code{"form"}. 51 | \item \code{body}: The parameters of the query such as \code{format}, \code{filter} or 52 | \code{bpolys}. 53 | } 54 | } 55 | \description{ 56 | Set or modify the spatial filter of an existing \code{ohsome_query} object 57 | } 58 | \details{ 59 | \code{\link[=set_boundary]{set_boundary()}} adds a spatial filter to an \code{ohsome_query} object or 60 | replaces an existing one. The spatial filter of a query to the ohsome API can 61 | be defined as one or more polygons, bounding boxes or bounding circles. 62 | } 63 | \examples{ 64 | # Query without boundary definition 65 | q <- ohsome_query( 66 | "elements/count/groupBy/boundary", 67 | filter = "building=*", 68 | time = "2022-01-01" 69 | ) 70 | 71 | # Use franconia from the mapview package as bounding polygons 72 | \donttest{ 73 | set_boundary(q, mapview::franconia, digits = 4) 74 | } 75 | 76 | # Use the bounding box of franconia 77 | \donttest{ 78 | set_boundary(q, sf::st_bbox(mapview::franconia)) 79 | } 80 | 81 | \dontrun{ 82 | # Get bounding box of the city of Kigali from OSM 83 | set_boundary(q, osmdata::getbb("Kigali")) 84 | } 85 | 86 | # Definition of two named bounding circles 87 | set_boundary(q, c("Circle 1:8.6528,49.3683,1000", "Circle 2:8.7294,49.4376,1000")) 88 | 89 | } 90 | \seealso{ 91 | \href{https://docs.ohsome.org/ohsome-api/v1/}{ohsome API documentation} 92 | } 93 | -------------------------------------------------------------------------------- /R/ohsome_query.R: -------------------------------------------------------------------------------- 1 | #' Create an `ohsome_query` object 2 | #' 3 | #' Creates an `ohsome_query` object specifying the ohsome API endpoint and 4 | #' the request parameters. 5 | #' 6 | #' @inheritParams ohsome_boundary 7 | #' @inheritParams ohsome_post 8 | #' @param endpoint The path to the 9 | #' [ohsome API endpoint](https://docs.ohsome.org/ohsome-api/v1/endpoints.html). 10 | #' Either a single string (e.g. `"elements/count"`) or a vector of character 11 | #' in the right order (e.g. `c("elements", "count")`). 12 | #' @param grouping character; group type(s) for grouped aggregations (only 13 | #' available for queries to aggregation endpoints). The following group types 14 | #' are available: 15 | #' * `"boundary"` groups the result by the given boundaries that are defined 16 | #' through any of the `boundary` query parameters. 17 | #' * `"key"` groups the result by the given keys that are defined through the 18 | #' `groupByKeys` query parameter. 19 | #' * `"tag"` groups the result by the given tags that are defined through the 20 | #' `groupByKey` and `groupByValues` query parameters. 21 | #' * `"type"` groups the result by OSM element type. 22 | #' * `c("boundary", "tag")` groups the result by the given boundaries and 23 | #' tags. 24 | #' 25 | #' Not all of these group types are accepted by all of the aggregation 26 | #' endpoints. Check 27 | #' [Grouping](https://docs.ohsome.org/ohsome-api/v1/group-by.html) 28 | #' for available group types. 29 | #' @param ... Parameters of the request to the ohsome API endpoint. 30 | #' @return An `ohsome_query` object. The object can be sent to the ohsome API 31 | #' with [ohsome_post()]. It consists of the following elements: 32 | #' * `url`: The URL of the endpoint. 33 | #' * `encode`: The way the information is encoded and then posted to the 34 | #' ohsome API. Set as `"form"`. 35 | #' * `body`: The parameters of the query such as `format`, `filter` or 36 | #' `bpolys`. 37 | #' @seealso [ohsome API documentation](https://docs.ohsome.org/ohsome-api/v1/) 38 | #' @export 39 | #' @examples 40 | #' # Extract building geometries with manually set bboxes parameter 41 | #' ohsome_query( 42 | #' "elements/geometry", 43 | #' bboxes = "8.6,49.36,8.75,49.44", 44 | #' time = "2022-01-01", 45 | #' filter = "building=*" 46 | #' ) 47 | #' 48 | #' # Extract building geometries using a boundary object: 49 | #' ohsome_query( 50 | #' "elements/geometry", 51 | #' boundary = "8.6,49.36,8.75,49.44", 52 | #' time = "2022-01-01", 53 | #' filter = "building=*" 54 | #' ) 55 | #' 56 | ohsome_query <- function( 57 | endpoint, 58 | boundary = NULL, 59 | grouping = NULL, 60 | ..., 61 | validate = FALSE) { 62 | 63 | body <- lapply(list(...), paste, collapse=",") 64 | explicit_format <- !is.null(body[["format"]]) 65 | 66 | if( 67 | !explicit_format && 68 | !any(grepl("(centroid|bbox|geometry)", endpoint)) 69 | ) { 70 | if(any(grepl("boundary", endpoint))) { 71 | body[["format"]] = "geojson" 72 | } else { 73 | body[["format"]] = "csv" 74 | } 75 | } 76 | 77 | query <- structure( 78 | list( 79 | url = build_endpoint_url(endpoint), 80 | encode = "form", 81 | body = body 82 | ), 83 | class = "ohsome_query" 84 | ) 85 | 86 | if(!is.null(grouping)) { 87 | query <- set_grouping(query, grouping, reset_format = !explicit_format) 88 | } 89 | 90 | if(!is.null(boundary)) { 91 | btypes <- c("bpolys", "bboxes", "bcircles") 92 | check <- btypes %in% names(query$body) 93 | if(any(check)) warning( 94 | paste( 95 | "Boundary overwrites", 96 | paste(btypes[check], collapse = ", "), 97 | "parameter(s)."), 98 | call. = FALSE 99 | ) 100 | 101 | query <- set_boundary(query, boundary) 102 | 103 | } 104 | 105 | if(validate) validate_query(query) 106 | 107 | return(query) 108 | } 109 | -------------------------------------------------------------------------------- /man/ohsome_contributions_count.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ohsome_contributions_count.R 3 | \name{ohsome_contributions_count} 4 | \alias{ohsome_contributions_count} 5 | \title{Count OSM contributions} 6 | \usage{ 7 | ohsome_contributions_count( 8 | boundary = NULL, 9 | latest = FALSE, 10 | return_value = c("absolute", "density"), 11 | time = NULL, 12 | ... 13 | ) 14 | } 15 | \arguments{ 16 | \item{boundary}{Bounding geometries specified by WGS84 coordinates in the 17 | order \verb{lon,lat}. The geometries of \code{sf} are transformed to WGS84 if the CRS 18 | of the object is known. The following classes are supported: 19 | \itemize{ 20 | \item \code{sf} with (MULTI)POLYGON geometries 21 | \item \code{sfc} with (MULTI)POLYGON geometries 22 | \item \code{sfg} with (MULTI)POLYGON geometries and WGS 84 coordinates 23 | \item \code{bbox} created with \code{\link[sf:st_bbox]{sf::st_bbox()}} or \code{\link[tmaptools:bb]{tmaptools::bb()}} 24 | \item \code{matrix} created with \code{\link[sp:bbox]{sp::bbox()}} or \code{\link[osmdata:getbb]{osmdata::getbb()}} 25 | \item \code{character} providing textual definitions of bounding polygons, boxes or 26 | circles as allowed by the ohsome API (see 27 | \href{https://docs.ohsome.org/ohsome-api/stable/boundaries.html}{ohsome API - Boundaries} 28 | ): 29 | \itemize{ 30 | \item bboxes: WGS84 coordinates in the following format: 31 | \code{"id1:lon1,lat1,lon2,lat2|id2:lon1,lat1,lon2,lat2|..."} OR 32 | \code{"lon1,lat1,lon2,lat2|lon1,lat1,lon2,lat2|..."} 33 | \item bcircles: WGS84 coordinates + radius in meter in the following 34 | format: \code{"id1:lon,lat,r|id2:lon,lat,r|..."} OR 35 | \code{"lon,lat,r|lon,lat,r|..."} 36 | \item bpolys: WGS84 coordinates given as a list of coordinate pairs (as for 37 | bboxes) or GeoJSON FeatureCollection. The first point has to be the same 38 | as the last point and MultiPolygons are only supported in GeoJSON. 39 | } 40 | \item \code{list} of \code{bbox}, \code{matrix} or \code{character}. Bounding geometry types of all 41 | list elements must be the same. Does not work with GeoJSON 42 | FeatureCollections. 43 | }} 44 | 45 | \item{latest}{logical; if \code{TRUE}, request only the latest contributions 46 | provided to each OSM element.} 47 | 48 | \item{return_value}{character; the value to be returned by the ohsome API: 49 | \itemize{ 50 | \item \code{"absolute"} returns the absolute number of contributions. This is the 51 | default. 52 | \item \code{"density"} returns the number of contributions per square kilometer. 53 | }} 54 | 55 | \item{time}{character; \code{time} parameter of the query (see 56 | \href{https://docs.ohsome.org/ohsome-api/v1/time.html}{Supported time formats}).} 57 | 58 | \item{...}{Parameters of the request to the ohsome API endpoint.} 59 | } 60 | \value{ 61 | An \code{ohsome_query} object. The object can be sent to the ohsome API 62 | with \code{\link[=ohsome_post]{ohsome_post()}}. It consists of the following elements: 63 | \itemize{ 64 | \item \code{url}: The URL of the endpoint. 65 | \item \code{encode}: The way the information is encoded and then posted to the 66 | ohsome API. Set as \code{"form"}. 67 | \item \code{body}: The parameters of the query such as \code{format}, \code{filter} or 68 | \code{bpolys}. 69 | } 70 | } 71 | \description{ 72 | Creates an \code{ohsome_query} object for OSM contributions count 73 | } 74 | \details{ 75 | \code{ohsome_contributions_count()} creates an \code{ohsome_query} object for 76 | OSM element aggregation. Boundary objects are passed via \code{\link[=set_boundary]{set_boundary()}} 77 | into \code{\link[=ohsome_boundary]{ohsome_boundary()}}. 78 | } 79 | \examples{ 80 | # Monthly counts of contributions to man-made objects around "Null Island" 81 | ohsome_contributions_count("0,0,10", filter = "man_made=*", time = "2010/2020/P1Y") 82 | 83 | # Monthly counts of latest contributions to man-made objects around "Null Island" 84 | ohsome_contributions_count( 85 | "0,0,10", 86 | latest = TRUE, 87 | filter = "man_made=*", 88 | time = "2010/2020/P1Y" 89 | ) 90 | 91 | } 92 | \seealso{ 93 | \href{https://docs.ohsome.org/ohsome-api/v1/endpoints.html#contributions-aggregation}{ohsome API Endpoints - Contributions Aggregation} 94 | } 95 | -------------------------------------------------------------------------------- /tests/testthat/test-ohsome_parse.R: -------------------------------------------------------------------------------- 1 | # original query: 2 | # q <- ohsome_query( 3 | # c("elements", "count"), 4 | # filter = "shop=convenience", 5 | # bcircles = "13.45,52.5,1000", 6 | # format = "csv" 7 | # ) 8 | # r <- ohsome_post(q, parse = FALSE, validate = FALSE) 9 | 10 | r <- readRDS("data/elements-count-shop-convenience-bcircles-csv.rds") 11 | 12 | test_that( 13 | 'returns data.frame by default when content CSV', { 14 | expect_s3_class(ohsome_parse(r), "data.frame") 15 | }) 16 | 17 | test_that( 18 | 'converts timestamp to POSIXct in data.frame', { 19 | p <- ohsome_parse(r) 20 | expect_s3_class(p$timestamp, "POSIXct") 21 | }) 22 | 23 | test_that( 24 | 'issues warning and returns data.frame if returnclass = "sf" and content not GeoJSON', { 25 | expect_warning(ohsome_parse(r, returnclass = "sf")) 26 | expect_s3_class(suppressWarnings(ohsome_parse(r, returnclass = "sf")), "data.frame") 27 | }) 28 | 29 | test_that( 30 | 'returns list if returnclass = "list"', { 31 | expect_type(ohsome_parse(r, returnclass = "list"), "list") 32 | }) 33 | 34 | test_that( 35 | 'returns character if returnclass = "character"', { 36 | expect_type(ohsome_parse(r, returnclass = "character"), "character") 37 | }) 38 | 39 | 40 | 41 | # original query: 42 | # q <- ohsome_query( 43 | # c("elements", "centroid"), 44 | # filter = "shop=convenience", 45 | # bcircles = "13.45,52.5,1000" 46 | # ) 47 | # r <- ohsome_post(q, parse = FALSE, validate = FALSE) 48 | 49 | r <- readRDS("data/elements-centroid-shop-convenience-bcircles.rds") 50 | 51 | test_that( 52 | 'returns sf by default when content GeoJSON', { 53 | expect_s3_class(ohsome_parse(r, returnclass = "sf"), "sf") 54 | }) 55 | 56 | test_that( 57 | 'converts @snapshotTimestamp to POSIXct in sf objects', { 58 | p <- ohsome_parse(r) 59 | expect_s3_class(p$`@snapshotTimestamp`, "POSIXct") 60 | }) 61 | 62 | test_that( 63 | 'returns data.frame when returnclass = "data.frame" and content GeoOJSON', { 64 | expect_s3_class(ohsome_parse(r, returnclass = "data.frame"), "data.frame") 65 | }) 66 | 67 | test_that( 68 | 'returns list if returnclass = "list" and content GeoJSON', { 69 | expect_type(ohsome_parse(r, returnclass = "list"), "list") 70 | }) 71 | 72 | test_that( 73 | 'does not issue warning if no geometries are omitted when omit_empty = TRUE', { 74 | expect_silent(ohsome_parse(r, returnclass = "sf", omit_empty = TRUE)) 75 | }) 76 | 77 | # original query: 78 | # q <- franconia |> 79 | # mutate(id = NUTS_ID) |> 80 | # ohsome_elements_count(filter = "building=*", time = "2015/2020", format = "csv") |> 81 | # set_endpoint("groupBy/boundary/groupBy/tag", reset_format = FALSE, append = TRUE) |> 82 | # set_groupByKey("building:levels") 83 | # r <- ohsome_post(q, parse = FALSE, validate = FALSE) 84 | 85 | r <- readRDS("data/elements-count-buildings-groupby-boundary-groupby-tag-csv.rds") 86 | 87 | test_that( 88 | 'succesfully parses csv response of groupBy/boundary/groupBy/tag query', { 89 | expect_s3_class(ohsome_parse(r), "data.frame") 90 | }) 91 | 92 | # original query: 93 | # q <- ohsome_query( 94 | # "elementsFullHistory/geometry", 95 | # bboxes = "-180,-90,180,90", 96 | # filter = "id:way/625011340", 97 | # time = "2008-01-01,2020-01-01" 98 | # ) 99 | # r <- ohsome_post(q, parse = FALSE, validate = FALSE) 100 | 101 | r <- readRDS("data/elements-fullHistory-geometry-id-way-625011340.rds") 102 | 103 | test_that( 104 | "parses GeoJSON FeatureCollection with faulty feature (way without nodes)", { 105 | expect_s3_class( 106 | suppressWarnings(ohsome_parse(r, returnclass = "sf")), 107 | "sf" 108 | ) 109 | }) 110 | 111 | test_that( 112 | "issues warning when omitting empty geometry features", { 113 | expect_warning(ohsome_parse(r, returnclass = "sf")) 114 | }) 115 | 116 | # original query: 117 | # boundary <- "6.75,51.4,500000" 118 | # q <- ohsome_elements_count(boundary, filter = 'building=*', time = '2022-01') 119 | # r <- ohsome_post(q, parse = F) 120 | 121 | r <- readRDS("data/elements-count-buildings-csv.rds") 122 | 123 | test_that( 124 | "correctly converts scientific notation value", { 125 | p <- ohsome_parse(r) 126 | expect_equal(p$value[1], 67003817L) 127 | } 128 | ) 129 | -------------------------------------------------------------------------------- /man/set_endpoint.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/set_endpoint.R 3 | \name{set_endpoint} 4 | \alias{set_endpoint} 5 | \alias{set_grouping} 6 | \title{Set endpoint} 7 | \usage{ 8 | set_endpoint(query, endpoint, append = FALSE, reset_format = TRUE) 9 | 10 | set_grouping(query, grouping, ...) 11 | } 12 | \arguments{ 13 | \item{query}{An \code{ohsome_query} object constructed with \code{\link[=ohsome_query]{ohsome_query()}} or 14 | any of its wrapper functions} 15 | 16 | \item{endpoint}{The path to the 17 | \href{https://docs.ohsome.org/ohsome-api/v1/endpoints.html}{ohsome API endpoint}. 18 | Either a single string (e.g. \code{"elements/count"}) or a vector of character 19 | in the right order (e.g. \code{c("elements", "count")}).} 20 | 21 | \item{append}{logical; If \code{TRUE}, the provided endpoint string is appended to 22 | the existing endpoint definition instead of replacing it. This is 23 | particularly useful if you wish to add \code{density}/\code{ratio} and/or a grouping 24 | to an existing aggregation query.} 25 | 26 | \item{reset_format}{logical; if \code{TRUE}, the format parameter of the query is 27 | updated depending on the new endpoint.} 28 | 29 | \item{grouping}{character; group type(s) for grouped aggregations (only 30 | available for queries to aggregation endpoints). The following group types 31 | are available: 32 | \itemize{ 33 | \item \code{"boundary"} groups the result by the given boundaries that are defined 34 | through any of the \code{boundary} query parameters. 35 | \item \code{"key"} groups the result by the given keys that are defined through the 36 | \code{groupByKeys} query parameter. 37 | \item \code{"tag"} groups the result by the given tags that are defined through the 38 | \code{groupByKey} and \code{groupByValues} query parameters. 39 | \item \code{"type"} groups the result by OSM element type. 40 | \item \code{c("boundary", "tag")} groups the result by the given boundaries and 41 | tags. 42 | } 43 | 44 | Not all of these group types are accepted by all of the aggregation 45 | endpoints. Check 46 | \href{https://docs.ohsome.org/ohsome-api/v1/group-by.html}{Grouping} 47 | for available group types.} 48 | 49 | \item{...}{Additional arguments passed to \code{set_endpoint()}} 50 | } 51 | \value{ 52 | An \code{ohsome_query} object. The object can be sent to the ohsome API 53 | with \code{\link[=ohsome_post]{ohsome_post()}}. It consists of the following elements: 54 | \itemize{ 55 | \item \code{url}: The URL of the endpoint. 56 | \item \code{encode}: The way the information is encoded and then posted to the 57 | ohsome API. Set as \code{"form"}. 58 | \item \code{body}: The parameters of the query such as \code{format}, \code{filter} or 59 | \code{bpolys}. 60 | } 61 | } 62 | \description{ 63 | Modifies the endpoint of an existing \code{ohsome_query} object 64 | } 65 | \details{ 66 | \code{set_endpoint()} takes an \code{ohsome_query} object and modifies the ohsome API 67 | endpoint. \code{set_grouping()} takes an \code{ohsome_query} object and modifies the 68 | endpoint path for grouped aggregations. 69 | } 70 | \examples{ 71 | # Query for count of elements 72 | q <- ohsome_elements_count( 73 | boundary = "HD:8.5992,49.3567,8.7499,49.4371|HN:9.1638,49.113,9.2672,49.1766", 74 | time = "2022-01-01", 75 | filter = "highway=*" 76 | ) 77 | 78 | # Modify query to aggregate length of elements instead of count 79 | set_endpoint(q, "elements/length") 80 | 81 | # Modify query to extract geometries instead of aggregating elements 82 | set_endpoint(q, "elements/geometry") 83 | 84 | # Append the endpoint path in order to group aggregation by boundary 85 | set_endpoint(q, "groupBy/boundary", append = TRUE) 86 | 87 | # Modify query to group aggregation by boundary 88 | set_grouping(q, grouping = "boundary") 89 | 90 | # Modify query to group by boundary, but keep format csv instead of geojson 91 | set_grouping(q, grouping = "boundary", reset_format = FALSE) 92 | 93 | # Append the endpoint path to query for element densities per boundary 94 | set_endpoint(q, c("density", "groupBy", "boundary"), append = TRUE) 95 | 96 | # Modify query to group aggregation by OSM element type 97 | set_grouping(q, grouping = "type") 98 | 99 | } 100 | \seealso{ 101 | \href{https://docs.ohsome.org/ohsome-api/v1/endpoints.html}{ohsome API Endpoints} 102 | } 103 | \concept{Set endpoint} 104 | -------------------------------------------------------------------------------- /man/ohsome_boundary.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ohsome_boundary.R 3 | \name{ohsome_boundary} 4 | \alias{ohsome_boundary} 5 | \alias{ohsome_boundary.ohsome_boundary} 6 | \alias{ohsome_boundary.character} 7 | \alias{ohsome_boundary.sf} 8 | \alias{ohsome_boundary.sfc} 9 | \alias{ohsome_boundary.sfg} 10 | \alias{ohsome_boundary.bbox} 11 | \alias{ohsome_boundary.matrix} 12 | \alias{ohsome_boundary.list} 13 | \title{Create an \code{ohsome_boundary} object} 14 | \usage{ 15 | ohsome_boundary(boundary, ...) 16 | 17 | \method{ohsome_boundary}{ohsome_boundary}(boundary, ...) 18 | 19 | \method{ohsome_boundary}{character}(boundary, ...) 20 | 21 | \method{ohsome_boundary}{sf}(boundary, digits = 6, ...) 22 | 23 | \method{ohsome_boundary}{sfc}(boundary, ...) 24 | 25 | \method{ohsome_boundary}{sfg}(boundary, ...) 26 | 27 | \method{ohsome_boundary}{bbox}(boundary, ...) 28 | 29 | \method{ohsome_boundary}{matrix}(boundary, ...) 30 | 31 | \method{ohsome_boundary}{list}(boundary, ...) 32 | } 33 | \arguments{ 34 | \item{boundary}{Bounding geometries specified by WGS84 coordinates in the 35 | order \verb{lon,lat}. The geometries of \code{sf} are transformed to WGS84 if the CRS 36 | of the object is known. The following classes are supported: 37 | \itemize{ 38 | \item \code{sf} with (MULTI)POLYGON geometries 39 | \item \code{sfc} with (MULTI)POLYGON geometries 40 | \item \code{sfg} with (MULTI)POLYGON geometries and WGS 84 coordinates 41 | \item \code{bbox} created with \code{\link[sf:st_bbox]{sf::st_bbox()}} or \code{\link[tmaptools:bb]{tmaptools::bb()}} 42 | \item \code{matrix} created with \code{\link[sp:bbox]{sp::bbox()}} or \code{\link[osmdata:getbb]{osmdata::getbb()}} 43 | \item \code{character} providing textual definitions of bounding polygons, boxes or 44 | circles as allowed by the ohsome API (see 45 | \href{https://docs.ohsome.org/ohsome-api/stable/boundaries.html}{ohsome API - Boundaries} 46 | ): 47 | \itemize{ 48 | \item bboxes: WGS84 coordinates in the following format: 49 | \code{"id1:lon1,lat1,lon2,lat2|id2:lon1,lat1,lon2,lat2|..."} OR 50 | \code{"lon1,lat1,lon2,lat2|lon1,lat1,lon2,lat2|..."} 51 | \item bcircles: WGS84 coordinates + radius in meter in the following 52 | format: \code{"id1:lon,lat,r|id2:lon,lat,r|..."} OR 53 | \code{"lon,lat,r|lon,lat,r|..."} 54 | \item bpolys: WGS84 coordinates given as a list of coordinate pairs (as for 55 | bboxes) or GeoJSON FeatureCollection. The first point has to be the same 56 | as the last point and MultiPolygons are only supported in GeoJSON. 57 | } 58 | \item \code{list} of \code{bbox}, \code{matrix} or \code{character}. Bounding geometry types of all 59 | list elements must be the same. Does not work with GeoJSON 60 | FeatureCollections. 61 | }} 62 | 63 | \item{...}{Additional arguments other than \code{digits} are ignored.} 64 | 65 | \item{digits}{integer; number of decimal places of coordinates in the 66 | resulting GeoJSON when converting \code{sf} to GeoJSON (defaults to 6).} 67 | } 68 | \value{ 69 | An \code{ohsome_boundary} object which contains the following elements: 70 | \itemize{ 71 | \item \code{boundary}: the boundary in textual format 72 | \item \code{type} of the boundary (\code{bpolys}, \code{bcircles}, or \code{bboxes}). 73 | } 74 | } 75 | \description{ 76 | Creates an \code{ohsome_boundary} object from various classes of input geometries. 77 | The \code{ohsome_boundary} object is used to set the \code{bpolys}, \code{bboxes} or 78 | \code{bcircles} parameter of an \code{ohsome_query} object. 79 | } 80 | \examples{ 81 | # Defintion of a bounding circle (lon,lat,radius in meters) 82 | ohsome_boundary("8.6528,49.3683,1000") 83 | 84 | # Definition of two named bounding circles 85 | ohsome_boundary("Circle 1:8.6528,49.3683,1000|Circle 2:8.7294,49.4376,1000") 86 | 87 | # Definition of two named bounding circles with a character vector 88 | ohsome_boundary(c("Circle 1:8.6528,49.3683,1000", "Circle 2:8.7294,49.4376,1000")) 89 | 90 | # Use franconia from the mapview package as bounding polygons 91 | \donttest{ 92 | ohsome_boundary(mapview::franconia, digits = 4) 93 | } 94 | 95 | # Use the bounding box of franconia 96 | \donttest{ 97 | ohsome_boundary(sf::st_bbox(mapview::franconia)) 98 | } 99 | 100 | # Get bounding box of the city of Berlin from OSM 101 | \dontrun{ 102 | ohsome_boundary(osmdata::getbb("Berlin")) 103 | } 104 | 105 | # Use a list of two bounding boxes 106 | \dontrun{ 107 | ohsome_boundary(list(osmdata::getbb("Berlin"), sf::st_bbox(mapview::franconia))) 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /man/set_parameters.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/set_parameters.R 3 | \name{set_parameters} 4 | \alias{set_parameters} 5 | \alias{set_time} 6 | \alias{set_filter} 7 | \alias{set_groupByKeys} 8 | \alias{set_groupByKey} 9 | \alias{set_groupByValues} 10 | \alias{set_properties} 11 | \title{Set parameters} 12 | \usage{ 13 | set_parameters(query, ...) 14 | 15 | set_time(query, time = query$body$time) 16 | 17 | set_filter(query, filter = query$body$filter, filter2 = query$body$filter2) 18 | 19 | set_groupByKeys(query, groupByKeys = query$body$groupByKeys) 20 | 21 | set_groupByKey(query, groupByKey = query$body$groupByKey) 22 | 23 | set_groupByValues(query, groupByValues = query$body$groupByValues) 24 | 25 | set_properties(query, properties = NULL) 26 | } 27 | \arguments{ 28 | \item{query}{An \code{ohsome_query} object constructed with \code{\link[=ohsome_query]{ohsome_query()}} or 29 | any of its wrapper functions} 30 | 31 | \item{...}{Parameters of the request to the ohsome API endpoint.} 32 | 33 | \item{time}{character; \code{time} parameter of the query (see 34 | \href{https://docs.ohsome.org/ohsome-api/v1/time.html}{Supported time formats}).} 35 | 36 | \item{filter}{character; \code{filter} parameter of the query (see 37 | \href{https://docs.ohsome.org/ohsome-api/v1/filter.html}{Filter})} 38 | 39 | \item{filter2}{character; \code{filter2} parameter of a ratio query} 40 | 41 | \item{groupByKeys}{character; \code{groupByKeys} parameter of a \code{groupBy/key} query} 42 | 43 | \item{groupByKey}{character; \code{groupByKey} parameter of a \code{groupBy/tag} query} 44 | 45 | \item{groupByValues}{character; \code{groupByValues} parameter of a \code{groupBy/tag} 46 | query} 47 | 48 | \item{properties}{character; properties to be extracted with extraction 49 | queries: 50 | \itemize{ 51 | \item \code{"tags"}, and/or 52 | \item \code{"metadata"} (i.e. \verb{@changesetId}, \verb{@lastEdit}, \verb{@osmType}, 53 | \verb{@version}), and/or 54 | \item \code{"contributionTypes"} (i.e. \verb{@creation}, \verb{@tagChange}, \verb{@deletion}, and 55 | \verb{@geometryChange}; only for contributions extraction) 56 | } 57 | 58 | Multiple values can be provided as comma-separated character or as 59 | character vector. This defaults to \code{NULL} (removes \code{properties} parameter 60 | from the query body).} 61 | } 62 | \value{ 63 | An \code{ohsome_query} object. The object can be sent to the ohsome API 64 | with \code{\link[=ohsome_post]{ohsome_post()}}. It consists of the following elements: 65 | \itemize{ 66 | \item \code{url}: The URL of the endpoint. 67 | \item \code{encode}: The way the information is encoded and then posted to the 68 | ohsome API. Set as \code{"form"}. 69 | \item \code{body}: The parameters of the query such as \code{format}, \code{filter} or 70 | \code{bpolys}. 71 | } 72 | } 73 | \description{ 74 | Sets or modifies parameters of an existing \code{ohsome_query} object 75 | } 76 | \details{ 77 | \code{set_parameters()} takes an \code{ohsome_query} object and an arbitrary number of 78 | named parameters as an input. It sets or modifies these parameters in the 79 | \code{ohsome_query} and returns the modified object. \code{set_time()}, \code{set_filter()}, 80 | \code{set_groupByKeys()}, \code{set_groupByKey()}, \code{set_groupByValues()} and 81 | \code{set_properties()} are wrapper functions to set specific parameters. By 82 | default, an unmodified \code{ohsome_query} object is returned. In order to remove 83 | a parameter from the query object, you can set the respective argument 84 | explicitly to \code{NULL} (e.g. \code{set_filter(query, filter = NULL)}). 85 | } 86 | \examples{ 87 | # Query ratio grouped by boundary 88 | q1 <- ohsome_query( 89 | endpoint = "elements/count/ratio/groupBy/boundary", 90 | boundary = "HD:8.5992,49.3567,8.7499,49.4371|HN:9.1638,49.113,9.2672,49.1766" 91 | ) 92 | 93 | # Add time, filter and format parameters 94 | q1 |> 95 | set_time("2021/2022/P3M") |> 96 | set_filter("building=*", filter2 = "building=* and building:levels=3") |> 97 | set_parameters(format = "csv") 98 | 99 | # Query elements area grouped by tag 100 | q2 <- ohsome_query( 101 | endpoint = "elements/area/groupBy/tag", 102 | boundary = "HD:8.5992,49.3567,8.7499,49.4371" 103 | ) 104 | 105 | # Add time, filter and groupByKey parameters 106 | q2 |> 107 | set_time("2021/2022/P3M") |> 108 | set_filter("building=*") |> 109 | set_groupByKey("building:levels") 110 | 111 | } 112 | \seealso{ 113 | \url{https://docs.ohsome.org/ohsome-api/v1/} 114 | } 115 | \concept{Set parameters} 116 | -------------------------------------------------------------------------------- /man/ohsome_query.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ohsome_query.R 3 | \name{ohsome_query} 4 | \alias{ohsome_query} 5 | \title{Create an \code{ohsome_query} object} 6 | \usage{ 7 | ohsome_query(endpoint, boundary = NULL, grouping = NULL, ..., validate = FALSE) 8 | } 9 | \arguments{ 10 | \item{endpoint}{The path to the 11 | \href{https://docs.ohsome.org/ohsome-api/v1/endpoints.html}{ohsome API endpoint}. 12 | Either a single string (e.g. \code{"elements/count"}) or a vector of character 13 | in the right order (e.g. \code{c("elements", "count")}).} 14 | 15 | \item{boundary}{Bounding geometries specified by WGS84 coordinates in the 16 | order \verb{lon,lat}. The geometries of \code{sf} are transformed to WGS84 if the CRS 17 | of the object is known. The following classes are supported: 18 | \itemize{ 19 | \item \code{sf} with (MULTI)POLYGON geometries 20 | \item \code{sfc} with (MULTI)POLYGON geometries 21 | \item \code{sfg} with (MULTI)POLYGON geometries and WGS 84 coordinates 22 | \item \code{bbox} created with \code{\link[sf:st_bbox]{sf::st_bbox()}} or \code{\link[tmaptools:bb]{tmaptools::bb()}} 23 | \item \code{matrix} created with \code{\link[sp:bbox]{sp::bbox()}} or \code{\link[osmdata:getbb]{osmdata::getbb()}} 24 | \item \code{character} providing textual definitions of bounding polygons, boxes or 25 | circles as allowed by the ohsome API (see 26 | \href{https://docs.ohsome.org/ohsome-api/stable/boundaries.html}{ohsome API - Boundaries} 27 | ): 28 | \itemize{ 29 | \item bboxes: WGS84 coordinates in the following format: 30 | \code{"id1:lon1,lat1,lon2,lat2|id2:lon1,lat1,lon2,lat2|..."} OR 31 | \code{"lon1,lat1,lon2,lat2|lon1,lat1,lon2,lat2|..."} 32 | \item bcircles: WGS84 coordinates + radius in meter in the following 33 | format: \code{"id1:lon,lat,r|id2:lon,lat,r|..."} OR 34 | \code{"lon,lat,r|lon,lat,r|..."} 35 | \item bpolys: WGS84 coordinates given as a list of coordinate pairs (as for 36 | bboxes) or GeoJSON FeatureCollection. The first point has to be the same 37 | as the last point and MultiPolygons are only supported in GeoJSON. 38 | } 39 | \item \code{list} of \code{bbox}, \code{matrix} or \code{character}. Bounding geometry types of all 40 | list elements must be the same. Does not work with GeoJSON 41 | FeatureCollections. 42 | }} 43 | 44 | \item{grouping}{character; group type(s) for grouped aggregations (only 45 | available for queries to aggregation endpoints). The following group types 46 | are available: 47 | \itemize{ 48 | \item \code{"boundary"} groups the result by the given boundaries that are defined 49 | through any of the \code{boundary} query parameters. 50 | \item \code{"key"} groups the result by the given keys that are defined through the 51 | \code{groupByKeys} query parameter. 52 | \item \code{"tag"} groups the result by the given tags that are defined through the 53 | \code{groupByKey} and \code{groupByValues} query parameters. 54 | \item \code{"type"} groups the result by OSM element type. 55 | \item \code{c("boundary", "tag")} groups the result by the given boundaries and 56 | tags. 57 | } 58 | 59 | Not all of these group types are accepted by all of the aggregation 60 | endpoints. Check 61 | \href{https://docs.ohsome.org/ohsome-api/v1/group-by.html}{Grouping} 62 | for available group types.} 63 | 64 | \item{...}{Parameters of the request to the ohsome API endpoint.} 65 | 66 | \item{validate}{logical; if \code{TRUE}, issues warning for invalid endpoint or 67 | invalid/missing query parameters.} 68 | } 69 | \value{ 70 | An \code{ohsome_query} object. The object can be sent to the ohsome API 71 | with \code{\link[=ohsome_post]{ohsome_post()}}. It consists of the following elements: 72 | \itemize{ 73 | \item \code{url}: The URL of the endpoint. 74 | \item \code{encode}: The way the information is encoded and then posted to the 75 | ohsome API. Set as \code{"form"}. 76 | \item \code{body}: The parameters of the query such as \code{format}, \code{filter} or 77 | \code{bpolys}. 78 | } 79 | } 80 | \description{ 81 | Creates an \code{ohsome_query} object specifying the ohsome API endpoint and 82 | the request parameters. 83 | } 84 | \examples{ 85 | # Extract building geometries with manually set bboxes parameter 86 | ohsome_query( 87 | "elements/geometry", 88 | bboxes = "8.6,49.36,8.75,49.44", 89 | time = "2022-01-01", 90 | filter = "building=*" 91 | ) 92 | 93 | # Extract building geometries using a boundary object: 94 | ohsome_query( 95 | "elements/geometry", 96 | boundary = "8.6,49.36,8.75,49.44", 97 | time = "2022-01-01", 98 | filter = "building=*" 99 | ) 100 | 101 | } 102 | \seealso{ 103 | \href{https://docs.ohsome.org/ohsome-api/v1/}{ohsome API documentation} 104 | } 105 | -------------------------------------------------------------------------------- /man/ohsome_users_count.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ohsome_users_count.R 3 | \name{ohsome_users_count} 4 | \alias{ohsome_users_count} 5 | \title{Count OSM users} 6 | \usage{ 7 | ohsome_users_count( 8 | boundary = NULL, 9 | return_value = c("absolute", "density"), 10 | grouping = NULL, 11 | time = NULL, 12 | ... 13 | ) 14 | } 15 | \arguments{ 16 | \item{boundary}{Bounding geometries specified by WGS84 coordinates in the 17 | order \verb{lon,lat}. The geometries of \code{sf} are transformed to WGS84 if the CRS 18 | of the object is known. The following classes are supported: 19 | \itemize{ 20 | \item \code{sf} with (MULTI)POLYGON geometries 21 | \item \code{sfc} with (MULTI)POLYGON geometries 22 | \item \code{sfg} with (MULTI)POLYGON geometries and WGS 84 coordinates 23 | \item \code{bbox} created with \code{\link[sf:st_bbox]{sf::st_bbox()}} or \code{\link[tmaptools:bb]{tmaptools::bb()}} 24 | \item \code{matrix} created with \code{\link[sp:bbox]{sp::bbox()}} or \code{\link[osmdata:getbb]{osmdata::getbb()}} 25 | \item \code{character} providing textual definitions of bounding polygons, boxes or 26 | circles as allowed by the ohsome API (see 27 | \href{https://docs.ohsome.org/ohsome-api/stable/boundaries.html}{ohsome API - Boundaries} 28 | ): 29 | \itemize{ 30 | \item bboxes: WGS84 coordinates in the following format: 31 | \code{"id1:lon1,lat1,lon2,lat2|id2:lon1,lat1,lon2,lat2|..."} OR 32 | \code{"lon1,lat1,lon2,lat2|lon1,lat1,lon2,lat2|..."} 33 | \item bcircles: WGS84 coordinates + radius in meter in the following 34 | format: \code{"id1:lon,lat,r|id2:lon,lat,r|..."} OR 35 | \code{"lon,lat,r|lon,lat,r|..."} 36 | \item bpolys: WGS84 coordinates given as a list of coordinate pairs (as for 37 | bboxes) or GeoJSON FeatureCollection. The first point has to be the same 38 | as the last point and MultiPolygons are only supported in GeoJSON. 39 | } 40 | \item \code{list} of \code{bbox}, \code{matrix} or \code{character}. Bounding geometry types of all 41 | list elements must be the same. Does not work with GeoJSON 42 | FeatureCollections. 43 | }} 44 | 45 | \item{return_value}{character; the value to be returned by the ohsome API: 46 | \itemize{ 47 | \item \code{"absolute"} returns the absolute number of users. This is the 48 | default. 49 | \item \code{"density"} returns the number of users per square kilometer. 50 | }} 51 | 52 | \item{grouping}{character; group type(s) for grouped aggregations (only 53 | available for queries to aggregation endpoints). The following group types 54 | are available: 55 | \itemize{ 56 | \item \code{"boundary"} groups the result by the given boundaries that are defined 57 | through any of the \code{boundary} query parameters. 58 | \item \code{"key"} groups the result by the given keys that are defined through the 59 | \code{groupByKeys} query parameter. 60 | \item \code{"tag"} groups the result by the given tags that are defined through the 61 | \code{groupByKey} and \code{groupByValues} query parameters. 62 | \item \code{"type"} groups the result by OSM element type. 63 | \item \code{c("boundary", "tag")} groups the result by the given boundaries and 64 | tags. 65 | } 66 | 67 | Not all of these group types are accepted by all of the aggregation 68 | endpoints. Check 69 | \href{https://docs.ohsome.org/ohsome-api/v1/group-by.html}{Grouping} 70 | for available group types.} 71 | 72 | \item{time}{character; \code{time} parameter of the query (see 73 | \href{https://docs.ohsome.org/ohsome-api/v1/time.html}{Supported time formats}).} 74 | 75 | \item{...}{Parameters of the request to the ohsome API endpoint.} 76 | } 77 | \value{ 78 | An \code{ohsome_query} object. The object can be sent to the ohsome API 79 | with \code{\link[=ohsome_post]{ohsome_post()}}. It consists of the following elements: 80 | \itemize{ 81 | \item \code{url}: The URL of the endpoint. 82 | \item \code{encode}: The way the information is encoded and then posted to the 83 | ohsome API. Set as \code{"form"}. 84 | \item \code{body}: The parameters of the query such as \code{format}, \code{filter} or 85 | \code{bpolys}. 86 | } 87 | } 88 | \description{ 89 | Create an \code{ohsome_query} object for OSM users count 90 | } 91 | \details{ 92 | \code{ohsome_users_count()} creates an \code{ohsome_query} object for OSM users 93 | aggregation. Boundary objects are passed via \code{\link[=set_boundary]{set_boundary()}} into 94 | \code{\link[=ohsome_boundary]{ohsome_boundary()}}. 95 | } 96 | \examples{ 97 | # Yearly count of users contributing to man-made objects around "Null Island" 98 | ohsome_users_count("0,0,10", filter = "man_made=*", time = "2012/2022/P1Y") 99 | 100 | } 101 | \seealso{ 102 | \href{https://docs.ohsome.org/ohsome-api/stable/endpoints.html#users-aggregation}{ohsome API Endpoints -- Users Aggregation} 103 | } 104 | -------------------------------------------------------------------------------- /R/ohsome_aggregate_elements.R: -------------------------------------------------------------------------------- 1 | #' Aggregate OSM elements 2 | #' 3 | #' Creates an `ohsome_query` object for OSM element aggregation 4 | #' 5 | #' `ohsome_aggregate_elements()` creates an `ohsome_query` object for 6 | #' OSM element aggregation. `ohsome_elements_count()`, 7 | #' `ohsome_elements_length()`, `ohsome_elements_perimeter()` and 8 | #' `ohsome_elements_area()` are wrapper functions for specific aggregation 9 | #' endpoints. Boundary objects are passed via [set_boundary()] into 10 | #' [ohsome_boundary()]. 11 | #' 12 | #' @inherit ohsome_query params return 13 | #' @param aggregation character; aggregation type: 14 | #' * `"count"` returns the total number of elements. This is the default. 15 | #' * `"length"` returns the total length of elements in meters. 16 | #' * `"perimeter"` returns the total perimeter of elements in meters. 17 | #' * `"area"` returns the total area of elements in square meters. 18 | #' @param return_value character; the value to be returned by the ohsome API: 19 | #' * `"absolute"` returns the absolute number, length, perimeter or area of 20 | #' elements. This is the default. 21 | #' * `"density"` returns the number, length, perimeter or area (in meters!) of 22 | #' elements per square kilometer. 23 | #' * `"ratio"` returns an absolute `value` for elements satisfying the 24 | #' `filter` argument, an absolute `value2` for elements satisfying the 25 | #' `filter2` argument, and the `ratio` of `value2` to `value`. 26 | #' @param time character; `time` parameter of the query (see 27 | #' [Supported time formats](https://docs.ohsome.org/ohsome-api/v1/time.html)). 28 | #' @seealso [ohsome API Endpoints - Elements Aggregation](https://docs.ohsome.org/ohsome-api/stable/endpoints.html#elements-aggregation) 29 | #' @export 30 | #' @family Aggregate elements 31 | #' @examples 32 | #' \donttest{ 33 | #' # Count of breweries in Franconia 34 | #' ohsome_aggregate_elements( 35 | #' mapview::franconia, 36 | #' aggregation = "count", 37 | #' filter = "craft=brewery", 38 | #' time = "2022-01-01" 39 | #' ) 40 | #' 41 | #' ohsome_elements_count( 42 | #' mapview::franconia, 43 | #' filter = "craft=brewery", 44 | #' time = "2022-01-01" 45 | #' ) 46 | #' 47 | #' # Monthly counts of breweries in Franconia from 2012 to 2022 48 | #' ohsome_elements_count( 49 | #' mapview::franconia, 50 | #' filter = "craft=brewery", 51 | #' time = "2012/2022/P1M" 52 | #' ) 53 | #' 54 | #' # Count of breweries per district of Franconia 55 | #' ohsome_elements_count( 56 | #' mapview::franconia, 57 | #' filter = "craft=brewery", 58 | #' grouping = "boundary", 59 | #' time = "2022-01-01" 60 | #' ) 61 | #' 62 | #' # Number of breweries per square kilometer 63 | #' ohsome_elements_count( 64 | #' mapview::franconia, 65 | #' filter = "craft=brewery", 66 | #' return_value = "density", 67 | #' time = "2022-01-01" 68 | #' ) 69 | #' 70 | #' # Proportion of breweries that are microbreweries 71 | #' ohsome_elements_count( 72 | #' mapview::franconia, 73 | #' filter = "craft=brewery", 74 | #' filter2 = "craft=brewery and microbrewery=yes", 75 | #' return_value = "ratio", 76 | #' time = "2022-01-01" 77 | #' ) 78 | #' 79 | #' # Total length of highway elements in Franconia 80 | #' ohsome_elements_length( 81 | #' mapview::franconia, 82 | #' filter = "highway=* and geometry:line", 83 | #' time = "2022-01-01" 84 | #' ) 85 | #' } 86 | #' 87 | ohsome_aggregate_elements <- function( 88 | boundary = NULL, 89 | aggregation = c("count", "length", "perimeter", "area"), 90 | return_value = c("absolute", "density", "ratio"), 91 | grouping = NULL, 92 | time = NULL, 93 | ... 94 | ) { 95 | aggregation <- match.arg(aggregation) 96 | return_value <- match.arg(return_value) 97 | if(return_value == "absolute") return_value <- NULL 98 | q <- ohsome_query(c("elements", aggregation, return_value), boundary, grouping, ...) 99 | set_time(q, time) 100 | } 101 | 102 | #' @export 103 | #' @rdname ohsome_aggregate_elements 104 | ohsome_elements_count <- function(boundary = NULL, ...) { 105 | ohsome_aggregate_elements(boundary, aggregation = "count", ...) 106 | } 107 | 108 | #' @export 109 | #' @rdname ohsome_aggregate_elements 110 | ohsome_elements_length <- function(boundary = NULL, ...) { 111 | ohsome_aggregate_elements(boundary, aggregation = "length", ...) 112 | } 113 | 114 | #' @export 115 | #' @rdname ohsome_aggregate_elements 116 | ohsome_elements_perimeter <- function(boundary = NULL, ...) { 117 | ohsome_aggregate_elements(boundary, aggregation = "perimeter", ...) 118 | } 119 | 120 | #' @export 121 | #' @rdname ohsome_aggregate_elements 122 | ohsome_elements_area <- function(boundary = NULL, ...) { 123 | ohsome_aggregate_elements(boundary, aggregation = "area", ...) 124 | } 125 | -------------------------------------------------------------------------------- /tests/testthat/test-validate.R: -------------------------------------------------------------------------------- 1 | # simple valid query: 2 | q <- ohsome_query( 3 | "elements/count", 4 | boundary = "0,0,1000", 5 | filter = "highway=*", 6 | time = "2020-01-01" 7 | ) 8 | 9 | test_that("silent on valid query and returns true", { 10 | expect_silent(validate_query(q)) 11 | expect_true(validate_query(q)) 12 | }) 13 | 14 | test_that("issues warning on unknown endpoint and returns false", { 15 | q <- set_endpoint(q, "foo") 16 | expect_warning( 17 | validate_query(q), 18 | regexp = "ohsome does not know endpoint foo" 19 | ) 20 | expect_false(suppressWarnings(validate_query(q))) 21 | }) 22 | 23 | test_that("issues warning on missing bounding geometry and returns fals", { 24 | q <- set_parameters(q, bcircles = NULL) 25 | expect_warning( 26 | validate_query(q), 27 | regexp = "bpolys, bboxes, or bcircles" 28 | ) 29 | expect_false(suppressWarnings(validate_query(q))) 30 | }) 31 | 32 | test_that("issues warning on unknown param", { 33 | q <- set_parameters(q, foo = "bar") 34 | expect_warning( 35 | validate_query(q), 36 | regexp = "foo is not a known parameter" 37 | ) 38 | expect_false(suppressWarnings(validate_query(q))) 39 | }) 40 | 41 | test_that("issues warning on missing required param", { 42 | q <- set_endpoint(q, "groupBy/key", append = TRUE) 43 | expect_warning( 44 | validate_query(q), 45 | regexp = "groupByKeys is a required parameter" 46 | ) 47 | expect_false(suppressWarnings(validate_query(q))) 48 | }) 49 | 50 | test_that("issues warning on defaulting time param", { 51 | q <- set_parameters(q, time = NULL) 52 | expect_warning( 53 | validate_query(q), 54 | regexp = "time parameter is not defined and defaults" 55 | ) 56 | expect_false(suppressWarnings(validate_query(q))) 57 | }) 58 | 59 | test_that("does not issue warn on missing time param when time has default", { 60 | q <- set_parameters(q, time = NULL) 61 | suppressWarnings(expect_failure( 62 | expect_warning( 63 | validate_query(q), 64 | regexp = "time is a required parameter" 65 | ) 66 | )) 67 | expect_false(suppressWarnings(validate_query(q))) 68 | }) 69 | 70 | test_that("does not warn on defaulting time param when time is required", { 71 | q <- set_endpoint(q, "elementsFullHistory/centroid") 72 | q <- set_parameters(q, time = NULL) 73 | suppressWarnings(expect_failure( 74 | expect_warning( 75 | validate_query(q), 76 | regexp = "time parameter is not defined and defaults" 77 | ) 78 | )) 79 | expect_false(suppressWarnings(validate_query(q))) 80 | }) 81 | 82 | test_that("issues warning on missing time param when time is required", { 83 | q <- set_endpoint(q, "elementsFullHistory/centroid") 84 | q <- set_parameters(q, time = NULL) 85 | expect_warning( 86 | validate_query(q), 87 | regexp = "time is a required parameter" 88 | ) 89 | expect_false(suppressWarnings(validate_query(q))) 90 | }) 91 | 92 | test_that("issues warning on missing filter param", { 93 | q <- set_parameters(q, filter = NULL) 94 | expect_warning( 95 | validate_query(q), 96 | regexp = "filter parameter is not defined" 97 | ) 98 | expect_false(suppressWarnings(validate_query(q))) 99 | }) 100 | 101 | test_that("issues warning on ratio query without filter2 param", { 102 | q <- set_endpoint(q, "ratio", append = TRUE) 103 | expect_warning( 104 | validate_query(q), 105 | regexp = "filter2 parameter needs to be defined in ratio queries." 106 | ) 107 | expect_false(suppressWarnings(validate_query(q))) 108 | }) 109 | 110 | test_that("issues specific warning on endpoint with unavailable grouping", { 111 | q <- set_endpoint(q, "groupBy/foo", append = TRUE) 112 | expect_warning( 113 | validate_query(q), 114 | regexp = "Only the following groupings are allowed with" 115 | ) 116 | expect_false(suppressWarnings(validate_query(q))) 117 | }) 118 | 119 | test_that("issues specific warning on endpoint that allows no grouping", { 120 | q <- ohsome_query("elements/geometry", grouping = "tag") 121 | expect_warning( 122 | validate_query(q), 123 | regexp = "Grouping is not allowed" 124 | ) 125 | expect_false(suppressWarnings(validate_query(q))) 126 | }) 127 | 128 | # original_query 129 | # q <- ohsome_elements_geometry( 130 | # rgeoboundaries::gb_adm0("Germany"), 131 | # filter ="amenity=hospital", 132 | # time="2022-01-01", 133 | # timeout = 200 134 | # ) 135 | # r <- ohsome_post(q, parse = FALSE, validate = FALSE) 136 | # content <- httr::content(r, as = "text", encoding = "utf-8") 137 | 138 | content <- readRDS("data/elements-geometry-amenity-hospitals-Germany-content.rds") 139 | 140 | test_that( 141 | "issues specific warning on invalid JSON with 413 timeout message", { 142 | expect_warning( 143 | validate_json(content), 144 | "response content:\nThe given query is too large" 145 | ) 146 | expect_false(suppressWarnings(validate_json(content))) 147 | } 148 | ) 149 | -------------------------------------------------------------------------------- /R/set_parameters.R: -------------------------------------------------------------------------------- 1 | #' Set parameters 2 | #' 3 | #' Sets or modifies parameters of an existing `ohsome_query` object 4 | #' 5 | #' `set_parameters()` takes an `ohsome_query` object and an arbitrary number of 6 | #' named parameters as an input. It sets or modifies these parameters in the 7 | #' `ohsome_query` and returns the modified object. `set_time()`, `set_filter()`, 8 | #' `set_groupByKeys()`, `set_groupByKey()`, `set_groupByValues()` and 9 | #' `set_properties()` are wrapper functions to set specific parameters. By 10 | #' default, an unmodified `ohsome_query` object is returned. In order to remove 11 | #' a parameter from the query object, you can set the respective argument 12 | #' explicitly to `NULL` (e.g. `set_filter(query, filter = NULL)`). 13 | #' 14 | #' @inherit ohsome_query params return 15 | #' @inheritParams ohsome_post 16 | #' @param time character; `time` parameter of the query (see 17 | #' [Supported time formats](https://docs.ohsome.org/ohsome-api/v1/time.html)). 18 | #' @param filter character; `filter` parameter of the query (see 19 | #' [Filter](https://docs.ohsome.org/ohsome-api/v1/filter.html)) 20 | #' @param filter2 character; `filter2` parameter of a ratio query 21 | #' @param groupByKeys character; `groupByKeys` parameter of a `groupBy/key` query 22 | #' @param groupByKey character; `groupByKey` parameter of a `groupBy/tag` query 23 | #' @param groupByValues character; `groupByValues` parameter of a `groupBy/tag` 24 | #' query 25 | #' @param properties character; properties to be extracted with extraction 26 | #' queries: 27 | #' * `"tags"`, and/or 28 | #' * `"metadata"` (i.e. `@changesetId`, `@lastEdit`, `@osmType`, 29 | #' `@version`), and/or 30 | #' * `"contributionTypes"` (i.e. `@creation`, `@tagChange`, `@deletion`, and 31 | #' `@geometryChange`; only for contributions extraction) 32 | #' 33 | #' Multiple values can be provided as comma-separated character or as 34 | #' character vector. This defaults to `NULL` (removes `properties` parameter 35 | #' from the query body). 36 | #' @family Set parameters 37 | #' @seealso \url{https://docs.ohsome.org/ohsome-api/v1/} 38 | #' @export 39 | #' @examples 40 | #' # Query ratio grouped by boundary 41 | #' q1 <- ohsome_query( 42 | #' endpoint = "elements/count/ratio/groupBy/boundary", 43 | #' boundary = "HD:8.5992,49.3567,8.7499,49.4371|HN:9.1638,49.113,9.2672,49.1766" 44 | #' ) 45 | #' 46 | #' # Add time, filter and format parameters 47 | #' q1 |> 48 | #' set_time("2021/2022/P3M") |> 49 | #' set_filter("building=*", filter2 = "building=* and building:levels=3") |> 50 | #' set_parameters(format = "csv") 51 | #' 52 | #' # Query elements area grouped by tag 53 | #' q2 <- ohsome_query( 54 | #' endpoint = "elements/area/groupBy/tag", 55 | #' boundary = "HD:8.5992,49.3567,8.7499,49.4371" 56 | #' ) 57 | #' 58 | #' # Add time, filter and groupByKey parameters 59 | #' q2 |> 60 | #' set_time("2021/2022/P3M") |> 61 | #' set_filter("building=*") |> 62 | #' set_groupByKey("building:levels") 63 | #' 64 | set_parameters <- function(query, ...) { 65 | 66 | endpoint <- extract_endpoint(query) 67 | body <- query$body 68 | 69 | params <- list(...) 70 | 71 | if(length(params) > 0) { 72 | for(i in 1:length(params)) body[[names(params[i])]] <- params[[i]] 73 | } 74 | 75 | return(do.call(ohsome_query, c(endpoint, body))) 76 | } 77 | 78 | #' @export 79 | #' @rdname set_parameters 80 | set_time <- function(query, time = query$body$time) { 81 | set_parameters(query, time = time) 82 | } 83 | 84 | #' @export 85 | #' @rdname set_parameters 86 | set_filter <- function(query, filter = query$body$filter, filter2 = query$body$filter2) { 87 | set_parameters(query, filter = filter, filter2 = filter2) 88 | } 89 | 90 | #' @export 91 | #' @rdname set_parameters 92 | set_groupByKeys <- function(query, groupByKeys = query$body$groupByKeys) { 93 | set_parameters(query, groupByKeys = groupByKeys) 94 | } 95 | 96 | #' @export 97 | #' @rdname set_parameters 98 | set_groupByKey <- function(query, groupByKey = query$body$groupByKey) { 99 | set_parameters(query, groupByKey = groupByKey) 100 | } 101 | 102 | #' @export 103 | #' @rdname set_parameters 104 | set_groupByValues <- function(query, groupByValues = query$body$groupByValues) { 105 | set_parameters(query, groupByValues = groupByValues) 106 | } 107 | 108 | #' @export 109 | #' @rdname set_parameters 110 | set_properties <- function(query, properties = NULL) { 111 | 112 | if(is.null(properties)) return(set_parameters(query, properties = NULL)) 113 | 114 | endpoint <- extract_endpoint(query) 115 | choices <- c("tags", "metadata") 116 | if(grepl("contributions", endpoint)) choices <- c(choices, "contributionTypes") 117 | 118 | properties <- gsub(" ", "", unlist(strsplit(properties, ","))) 119 | properties <- match.arg(properties, choices = choices, several.ok = TRUE) 120 | properties <- paste(properties, collapse = ",") 121 | 122 | set_parameters(query, properties = properties) 123 | } 124 | -------------------------------------------------------------------------------- /man/ohsome_extract_elements.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ohsome_extract_elements.R 3 | \name{ohsome_extract_elements} 4 | \alias{ohsome_extract_elements} 5 | \alias{ohsome_elements_bbox} 6 | \alias{ohsome_elements_centroid} 7 | \alias{ohsome_elements_geometry} 8 | \title{Extract OSM elements} 9 | \usage{ 10 | ohsome_extract_elements( 11 | boundary = NULL, 12 | geometryType = c("centroid", "bbox", "geometry"), 13 | time = NULL, 14 | properties = NULL, 15 | clipGeometry = TRUE, 16 | ... 17 | ) 18 | 19 | ohsome_elements_bbox(boundary = NULL, ...) 20 | 21 | ohsome_elements_centroid(boundary = NULL, ...) 22 | 23 | ohsome_elements_geometry(boundary = NULL, ...) 24 | } 25 | \arguments{ 26 | \item{boundary}{Bounding geometries specified by WGS84 coordinates in the 27 | order \verb{lon,lat}. The geometries of \code{sf} are transformed to WGS84 if the CRS 28 | of the object is known. The following classes are supported: 29 | \itemize{ 30 | \item \code{sf} with (MULTI)POLYGON geometries 31 | \item \code{sfc} with (MULTI)POLYGON geometries 32 | \item \code{sfg} with (MULTI)POLYGON geometries and WGS 84 coordinates 33 | \item \code{bbox} created with \code{\link[sf:st_bbox]{sf::st_bbox()}} or \code{\link[tmaptools:bb]{tmaptools::bb()}} 34 | \item \code{matrix} created with \code{\link[sp:bbox]{sp::bbox()}} or \code{\link[osmdata:getbb]{osmdata::getbb()}} 35 | \item \code{character} providing textual definitions of bounding polygons, boxes or 36 | circles as allowed by the ohsome API (see 37 | \href{https://docs.ohsome.org/ohsome-api/stable/boundaries.html}{ohsome API - Boundaries} 38 | ): 39 | \itemize{ 40 | \item bboxes: WGS84 coordinates in the following format: 41 | \code{"id1:lon1,lat1,lon2,lat2|id2:lon1,lat1,lon2,lat2|..."} OR 42 | \code{"lon1,lat1,lon2,lat2|lon1,lat1,lon2,lat2|..."} 43 | \item bcircles: WGS84 coordinates + radius in meter in the following 44 | format: \code{"id1:lon,lat,r|id2:lon,lat,r|..."} OR 45 | \code{"lon,lat,r|lon,lat,r|..."} 46 | \item bpolys: WGS84 coordinates given as a list of coordinate pairs (as for 47 | bboxes) or GeoJSON FeatureCollection. The first point has to be the same 48 | as the last point and MultiPolygons are only supported in GeoJSON. 49 | } 50 | \item \code{list} of \code{bbox}, \code{matrix} or \code{character}. Bounding geometry types of all 51 | list elements must be the same. Does not work with GeoJSON 52 | FeatureCollections. 53 | }} 54 | 55 | \item{geometryType}{character; type of geometry to be extracted: 56 | \itemize{ 57 | \item \code{"centroid"}, 58 | \item \code{"bboxes"} (bounding boxes), or 59 | \item \code{"geometry"} 60 | } 61 | 62 | Caveat: Node elements are omitted from results in queries for bounding 63 | boxes.} 64 | 65 | \item{time}{character; \code{time} parameter of the query (see 66 | \href{https://docs.ohsome.org/ohsome-api/v1/time.html}{Supported time formats}).} 67 | 68 | \item{properties}{character; properties to be extracted with the features: 69 | \itemize{ 70 | \item \code{"tags"}, and/or 71 | \item \code{"metadata"} (i.e. \verb{@changesetId}, \verb{@lastEdit}, \verb{@osmType}, and 72 | \verb{@version}) 73 | } 74 | 75 | Multiple values can be provided as comma-separated character or as 76 | character vector. This defaults to \code{NULL} (provides \verb{@osmId}).} 77 | 78 | \item{clipGeometry}{logical; specifies whether the returned geometries should 79 | be clipped to the query’s spatial boundary} 80 | 81 | \item{...}{Parameters of the request to the ohsome API endpoint.} 82 | } 83 | \value{ 84 | An \code{ohsome_query} object. The object can be sent to the ohsome API 85 | with \code{\link[=ohsome_post]{ohsome_post()}}. It consists of the following elements: 86 | \itemize{ 87 | \item \code{url}: The URL of the endpoint. 88 | \item \code{encode}: The way the information is encoded and then posted to the 89 | ohsome API. Set as \code{"form"}. 90 | \item \code{body}: The parameters of the query such as \code{format}, \code{filter} or 91 | \code{bpolys}. 92 | } 93 | } 94 | \description{ 95 | Create an \code{ohsome_query} object for OSM element extraction 96 | } 97 | \details{ 98 | \code{ohsome_extract_elements()} creates an \code{ohsome_query} object for OSM element 99 | extraction. \code{ohsome_elements_bbox()}, \code{ohsome_elements_centroid()} and 100 | \code{ohsome_elements_geometry()} are wrapper functions for specific elements 101 | extraction endpoints. Boundary objects are passed via \code{\link[=set_boundary]{set_boundary()}} into 102 | \code{\link[=ohsome_boundary]{ohsome_boundary()}}. 103 | } 104 | \examples{ 105 | # Extract geometries, metadata and tags of man-made objects around "Null Island": 106 | ohsome_elements_geometry( 107 | "0,0,10", 108 | filter = "man_made=*", 109 | time = "2022-01-01", 110 | properties = c("metadata", "tags") 111 | ) 112 | 113 | } 114 | \seealso{ 115 | \href{https://docs.ohsome.org/ohsome-api/stable/endpoints.html#elements-extraction}{ohsome API Endpoints -- Elements Extraction} 116 | } 117 | \concept{Extract elements} 118 | -------------------------------------------------------------------------------- /man/ohsome_extract_elementsFullHistory.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ohsome_extract_elementsFullHistory.R 3 | \name{ohsome_extract_elementsFullHistory} 4 | \alias{ohsome_extract_elementsFullHistory} 5 | \alias{ohsome_elementsFullHistory_bbox} 6 | \alias{ohsome_elementsFullHistory_centroid} 7 | \alias{ohsome_elementsFullHistory_geometry} 8 | \title{Extract OSM elements' full history} 9 | \usage{ 10 | ohsome_extract_elementsFullHistory( 11 | boundary = NULL, 12 | geometryType = c("centroid", "bbox", "geometry"), 13 | time = NULL, 14 | properties = NULL, 15 | clipGeometry = TRUE, 16 | ... 17 | ) 18 | 19 | ohsome_elementsFullHistory_bbox(boundary = NULL, ...) 20 | 21 | ohsome_elementsFullHistory_centroid(boundary = NULL, ...) 22 | 23 | ohsome_elementsFullHistory_geometry(boundary = NULL, ...) 24 | } 25 | \arguments{ 26 | \item{boundary}{Bounding geometries specified by WGS84 coordinates in the 27 | order \verb{lon,lat}. The geometries of \code{sf} are transformed to WGS84 if the CRS 28 | of the object is known. The following classes are supported: 29 | \itemize{ 30 | \item \code{sf} with (MULTI)POLYGON geometries 31 | \item \code{sfc} with (MULTI)POLYGON geometries 32 | \item \code{sfg} with (MULTI)POLYGON geometries and WGS 84 coordinates 33 | \item \code{bbox} created with \code{\link[sf:st_bbox]{sf::st_bbox()}} or \code{\link[tmaptools:bb]{tmaptools::bb()}} 34 | \item \code{matrix} created with \code{\link[sp:bbox]{sp::bbox()}} or \code{\link[osmdata:getbb]{osmdata::getbb()}} 35 | \item \code{character} providing textual definitions of bounding polygons, boxes or 36 | circles as allowed by the ohsome API (see 37 | \href{https://docs.ohsome.org/ohsome-api/stable/boundaries.html}{ohsome API - Boundaries} 38 | ): 39 | \itemize{ 40 | \item bboxes: WGS84 coordinates in the following format: 41 | \code{"id1:lon1,lat1,lon2,lat2|id2:lon1,lat1,lon2,lat2|..."} OR 42 | \code{"lon1,lat1,lon2,lat2|lon1,lat1,lon2,lat2|..."} 43 | \item bcircles: WGS84 coordinates + radius in meter in the following 44 | format: \code{"id1:lon,lat,r|id2:lon,lat,r|..."} OR 45 | \code{"lon,lat,r|lon,lat,r|..."} 46 | \item bpolys: WGS84 coordinates given as a list of coordinate pairs (as for 47 | bboxes) or GeoJSON FeatureCollection. The first point has to be the same 48 | as the last point and MultiPolygons are only supported in GeoJSON. 49 | } 50 | \item \code{list} of \code{bbox}, \code{matrix} or \code{character}. Bounding geometry types of all 51 | list elements must be the same. Does not work with GeoJSON 52 | FeatureCollections. 53 | }} 54 | 55 | \item{geometryType}{character; type of geometry to be extracted: 56 | \itemize{ 57 | \item \code{"centroid"}, 58 | \item \code{"bboxes"} (bounding boxes), or 59 | \item \code{"geometry"} 60 | } 61 | 62 | Caveat: Node elements are omitted from results in queries for bounding 63 | boxes.} 64 | 65 | \item{time}{character; \code{time} parameter of the query (see 66 | \href{https://docs.ohsome.org/ohsome-api/v1/time.html}{Supported time formats}).} 67 | 68 | \item{properties}{character; properties to be extracted with the features: 69 | \itemize{ 70 | \item \code{"tags"}, and/or 71 | \item \code{"metadata"} (i.e. \verb{@changesetId}, \verb{@lastEdit}, \verb{@osmType}, and 72 | \verb{@version}) 73 | } 74 | 75 | Multiple values can be provided as comma-separated character or as 76 | character vector. This defaults to \code{NULL} (provides \verb{@osmId}).} 77 | 78 | \item{clipGeometry}{logical; specifies whether the returned geometries should 79 | be clipped to the query’s spatial boundary} 80 | 81 | \item{...}{Parameters of the request to the ohsome API endpoint.} 82 | } 83 | \value{ 84 | An \code{ohsome_query} object. The object can be sent to the ohsome API 85 | with \code{\link[=ohsome_post]{ohsome_post()}}. It consists of the following elements: 86 | \itemize{ 87 | \item \code{url}: The URL of the endpoint. 88 | \item \code{encode}: The way the information is encoded and then posted to the 89 | ohsome API. Set as \code{"form"}. 90 | \item \code{body}: The parameters of the query such as \code{format}, \code{filter} or 91 | \code{bpolys}. 92 | } 93 | } 94 | \description{ 95 | Creates an \code{ohsome_query} object for the extraction of OSM elements' full 96 | history 97 | } 98 | \details{ 99 | \code{ohsome_extract_elementsFullHistory()} creates an \code{ohsome_query} object for OSM 100 | element full history extraction. \code{ohsome_elementsFullHistory_bbox()}, 101 | \code{ohsome_elementsFullHistory_centroid()} and 102 | \code{ohsome_elementsFullHistory_geometry()} are wrapper functions for specific 103 | elementsFullHistory extraction endpoints. Boundary objects are passed via 104 | \code{\link[=set_boundary]{set_boundary()}} into \code{\link[=ohsome_boundary]{ohsome_boundary()}}. 105 | } 106 | \examples{ 107 | 108 | # Extract full history of building geometries around Heidelberg main station: 109 | ohsome_elementsFullHistory_geometry( 110 | boundary = "8.67542,49.40347,1000", 111 | time = "2012,2022", 112 | filter = "building=* and geometry:polygon", 113 | clipGeometry = FALSE 114 | ) 115 | } 116 | \seealso{ 117 | \href{https://docs.ohsome.org/ohsome-api/v1/endpoints.html#elements-full-history-extraction}{ohsome API Endpoints -- Elements Full History Extraction} 118 | } 119 | \concept{Extract elements full History} 120 | -------------------------------------------------------------------------------- /man/ohsome_extract_contributions.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ohsome_extract_contributions.R 3 | \name{ohsome_extract_contributions} 4 | \alias{ohsome_extract_contributions} 5 | \alias{ohsome_contributions_bbox} 6 | \alias{ohsome_contributions_centroid} 7 | \alias{ohsome_contributions_geometry} 8 | \title{Extract OSM contributions} 9 | \usage{ 10 | ohsome_extract_contributions( 11 | boundary = NULL, 12 | geometryType = c("centroid", "bbox", "geometry"), 13 | latest = FALSE, 14 | time = NULL, 15 | properties = NULL, 16 | clipGeometry = TRUE, 17 | ... 18 | ) 19 | 20 | ohsome_contributions_bbox(boundary = NULL, ...) 21 | 22 | ohsome_contributions_centroid(boundary = NULL, ...) 23 | 24 | ohsome_contributions_geometry(boundary = NULL, ...) 25 | } 26 | \arguments{ 27 | \item{boundary}{Bounding geometries specified by WGS84 coordinates in the 28 | order \verb{lon,lat}. The geometries of \code{sf} are transformed to WGS84 if the CRS 29 | of the object is known. The following classes are supported: 30 | \itemize{ 31 | \item \code{sf} with (MULTI)POLYGON geometries 32 | \item \code{sfc} with (MULTI)POLYGON geometries 33 | \item \code{sfg} with (MULTI)POLYGON geometries and WGS 84 coordinates 34 | \item \code{bbox} created with \code{\link[sf:st_bbox]{sf::st_bbox()}} or \code{\link[tmaptools:bb]{tmaptools::bb()}} 35 | \item \code{matrix} created with \code{\link[sp:bbox]{sp::bbox()}} or \code{\link[osmdata:getbb]{osmdata::getbb()}} 36 | \item \code{character} providing textual definitions of bounding polygons, boxes or 37 | circles as allowed by the ohsome API (see 38 | \href{https://docs.ohsome.org/ohsome-api/stable/boundaries.html}{ohsome API - Boundaries} 39 | ): 40 | \itemize{ 41 | \item bboxes: WGS84 coordinates in the following format: 42 | \code{"id1:lon1,lat1,lon2,lat2|id2:lon1,lat1,lon2,lat2|..."} OR 43 | \code{"lon1,lat1,lon2,lat2|lon1,lat1,lon2,lat2|..."} 44 | \item bcircles: WGS84 coordinates + radius in meter in the following 45 | format: \code{"id1:lon,lat,r|id2:lon,lat,r|..."} OR 46 | \code{"lon,lat,r|lon,lat,r|..."} 47 | \item bpolys: WGS84 coordinates given as a list of coordinate pairs (as for 48 | bboxes) or GeoJSON FeatureCollection. The first point has to be the same 49 | as the last point and MultiPolygons are only supported in GeoJSON. 50 | } 51 | \item \code{list} of \code{bbox}, \code{matrix} or \code{character}. Bounding geometry types of all 52 | list elements must be the same. Does not work with GeoJSON 53 | FeatureCollections. 54 | }} 55 | 56 | \item{geometryType}{character; type of geometry to be extracted: 57 | \itemize{ 58 | \item \code{"centroid"}, 59 | \item \code{"bboxes"} (bounding boxes), or 60 | \item \code{"geometry"} 61 | } 62 | 63 | Caveat: Node elements are omitted from results in queries for bounding 64 | boxes.} 65 | 66 | \item{latest}{logical; if \code{TRUE}, request only the latest contributions 67 | provided to each OSM element.} 68 | 69 | \item{time}{character; \code{time} parameter of the query (see 70 | \href{https://docs.ohsome.org/ohsome-api/v1/time.html}{Supported time formats}).} 71 | 72 | \item{properties}{character; properties to be extracted with the 73 | contributions: 74 | \itemize{ 75 | \item \code{"tags"}, and/or 76 | \item \code{"metadata"} (i.e. \verb{@changesetId}, \verb{@lastEdit}, \verb{@osmType}, 77 | \verb{@version}), and/or 78 | \item \code{"contributionTypes"} (i.e. \verb{@creation}, \verb{@tagChange}, \verb{@deletion}, and 79 | \verb{@geometryChange}) 80 | } 81 | 82 | Multiple values can be provided as comma-separated character or as 83 | character vector. This defaults to \code{NULL} (provides 84 | \verb{@contributionChangesetId}, \verb{@osmId} and \verb{@timestamp}).} 85 | 86 | \item{clipGeometry}{logical; specifies whether the returned geometries should 87 | be clipped to the query’s spatial boundary} 88 | 89 | \item{...}{Parameters of the request to the ohsome API endpoint.} 90 | } 91 | \value{ 92 | An \code{ohsome_query} object. The object can be sent to the ohsome API 93 | with \code{\link[=ohsome_post]{ohsome_post()}}. It consists of the following elements: 94 | \itemize{ 95 | \item \code{url}: The URL of the endpoint. 96 | \item \code{encode}: The way the information is encoded and then posted to the 97 | ohsome API. Set as \code{"form"}. 98 | \item \code{body}: The parameters of the query such as \code{format}, \code{filter} or 99 | \code{bpolys}. 100 | } 101 | } 102 | \description{ 103 | Creates an \code{ohsome_query} object for OSM contribution extraction 104 | } 105 | \details{ 106 | \code{ohsome_extract_contributions()} creates an \code{ohsome_query} object for OSM 107 | contribution extraction. \code{ohsome_contributions_bbox()}, 108 | \code{ohsome_contributions_centroid()} and \code{ohsome_contributions_geometry()} 109 | are wrapper functions for specific contributions extraction endpoints. 110 | Boundary objects are passed via \code{\link[=set_boundary]{set_boundary()}} into \code{\link[=ohsome_boundary]{ohsome_boundary()}}. 111 | } 112 | \examples{ 113 | # Extract contributions to man-made objects around "Null Island" with metadata: 114 | ohsome_contributions_geometry( 115 | "0,0,10", 116 | filter = "man_made=*", 117 | time = c("2021-01-01", "2022-01-01"), 118 | properties = "metadata" 119 | ) 120 | 121 | } 122 | \seealso{ 123 | \href{https://docs.ohsome.org/ohsome-api/stable/endpoints.html#contributions-extraction}{ohsome API Endpoints -- Contributions Extraction} 124 | } 125 | \concept{Extract contributions} 126 | -------------------------------------------------------------------------------- /R/ohsome_parse.R: -------------------------------------------------------------------------------- 1 | #' Parse content from an ohsome API response 2 | #' 3 | #' Extracts and parses the content from an ohsome API response 4 | #' 5 | #' `ohsome_parse()` parses an `ohsome_response` object into an object of the 6 | #' specified class. By default, this is an `sf` object if the ohsome API 7 | #' response contains GeoJSON data or a `data.frame` if it does not. 8 | #' `ohsome_sf()` and `ohsome_df()` wrapper functions for specific return 9 | #' classes. 10 | #' 11 | #' @param response An `ohsome_response` object 12 | #' @param returnclass character; one of the following: 13 | #' * `"default"` returns `sf` if the `ohsome_response` contains GeoJSON, or 14 | #' else a `data.frame`. 15 | #' * `"sf"` returns `sf` if the `ohsome_response` contains GeoJSON, else 16 | #' issues a warning and returns a `data.frame`. 17 | #' * `"data.frame"` returns a `data.frame`. 18 | #' * `"list"` returns a `list`. 19 | #' * `"character"` returns the ohsome API response body as text (JSON or 20 | #' semicolon-separated values) 21 | #' @param omit_empty logical; omit features with empty geometries (only if 22 | #' `returnclass = "sf"`) 23 | #' @family Extract and parse the content from an ohsome API response 24 | #' @return An `sf` object, a `data.frame`, a `list` or a `character` 25 | #' @export 26 | #' @examples 27 | #' \dontrun{ 28 | #' # Create and send a query to ohsome API 29 | #' r <- ohsome_query("elements/centroid", filter = "amenity=*") |> 30 | #' set_boundary(osmdata::getbb("Heidelberg")) |> 31 | #' set_time("2021") |> 32 | #' set_properties("metadata") |> 33 | #' ohsome_post(parse = FALSE) 34 | #' 35 | #' # Parse response to object of default class (here: sf) 36 | #' ohsome_parse(r) 37 | #' 38 | #' # Parse response to data.frame 39 | #' ohsome_df(r) 40 | #' 41 | #' # Parse response to sf 42 | #' ohsome_sf(r) 43 | #' } 44 | #' 45 | ohsome_parse <- function( 46 | response, 47 | returnclass = c("default", "sf", "data.frame", "list", "character"), 48 | omit_empty = TRUE 49 | ) { 50 | 51 | returnclass <- match.arg(returnclass) 52 | 53 | type <- httr::http_type(response) 54 | content <- httr::content(response, as = "text", encoding = "utf-8") 55 | 56 | if(returnclass == "character") { 57 | 58 | return(content) 59 | 60 | } else if( 61 | type == "application/json" && 62 | grepl('\"type\" : \"FeatureCollection\"', content) || 63 | type == "application/geo+json" 64 | 65 | ) { 66 | if(!validate_json(content)) { 67 | warning("Invalid JSON in ohsome API response. Returning character.") 68 | return(content) 69 | } 70 | 71 | content <- jsonlite::minify(content) 72 | pattern <- '\"(Multi)?(Point|LineString|Polygon)\",\"coordinates\":\\[+\\]+' 73 | loc <- gregexpr(pattern = pattern, text = content) 74 | empty_coords <- sapply(loc, function(x) { 75 | match.length <- attr(x, "match.length") 76 | length(match.length[match.length > 0]) 77 | }) 78 | 79 | 80 | if(empty_coords > 0) { 81 | content <- gsub(pattern, '"Point","coordinates":[360, 360]', content) 82 | } 83 | 84 | p <- geojsonsf::geojson_sf(content) 85 | 86 | if(empty_coords > 0) { 87 | i <- suppressWarnings(suppressMessages( 88 | sf::st_intersects( 89 | p, 90 | sf::st_point(rep(360, 2)), 91 | sparse = FALSE 92 | ))) 93 | p[i, "geometry"] <- NULL 94 | } 95 | 96 | if(returnclass == "data.frame") { 97 | return(convert_quietly(sf::st_drop_geometry(p))) 98 | } else if(returnclass == "list") { 99 | return(as.list(sf::st_sf(convert_quietly(as.data.frame(p))))) 100 | } else { 101 | 102 | empty <- sf::st_is_empty(p) 103 | if(omit_empty & sum(empty) > 0) { 104 | p <- subset(p, !sf::st_is_empty(p)) 105 | warning( 106 | paste(sum(empty), "element(s) with empty geometries omitted."), 107 | call. = FALSE 108 | ) 109 | } 110 | 111 | return(sf::st_sf(convert_quietly(as.data.frame(p)))) 112 | } 113 | 114 | } else if(type == "application/json") { 115 | 116 | if(!validate_json(content)) { 117 | warning("Invalid JSON in ohsome API response. Returning character.") 118 | return(content) 119 | } 120 | 121 | p <- jsonlite::fromJSON(content, simplifyVector = TRUE) 122 | 123 | if(returnclass == "list") { 124 | return(p) 125 | } else { 126 | if(returnclass == "sf") { 127 | warning( 128 | "No geodata in ohsome API response. Returning data.frame ", 129 | "instead of sf." 130 | ) 131 | } 132 | 133 | return(convert_quietly(as.data.frame(p))) 134 | } 135 | 136 | } else if(type == "text/csv") { 137 | 138 | p <- utils::read.csv2( 139 | textConnection(content), 140 | comment.char = "#", 141 | header = TRUE, 142 | stringsAsFactors = FALSE 143 | ) 144 | 145 | if(returnclass == "list") { 146 | return(as.list(convert_quietly(p))) 147 | } else { 148 | if(returnclass == "sf") { 149 | warning( 150 | "No geodata in ohsome API response. Returning data.frame ", 151 | "instead of sf." 152 | ) 153 | } 154 | 155 | return(convert_quietly(p)) 156 | } 157 | } else { 158 | stop("ohsome API response content is neither of type (Geo)JSON nor CSV.") 159 | } 160 | } 161 | 162 | #' @export 163 | #' @rdname ohsome_parse 164 | ohsome_sf <- function(response, omit_empty = TRUE) { 165 | ohsome_parse(response, returnclass = "sf", omit_empty = omit_empty) 166 | } 167 | 168 | #' @export 169 | #' @rdname ohsome_parse 170 | ohsome_df <- function(response, omit_empty = TRUE) { 171 | ohsome_parse(response, returnclass = "data.frame", omit_empty = omit_empty) 172 | } 173 | -------------------------------------------------------------------------------- /R/validate.R: -------------------------------------------------------------------------------- 1 | #' Validate parameters 2 | #' 3 | #' Checks if the specified parameters in a request body are valid and issues a 4 | #' warning if not. Returns a logical that indicates the validity of the 5 | #' parameters. 6 | #' 7 | #' @inherit validate_query return 8 | #' @param endpoint The path to the ohsome API endpoint as a single string 9 | #' (e.g. `"elements/count"`) 10 | #' @param body A list of named parameters to the ohsome API request 11 | #' @keywords Internal 12 | #' @noRd 13 | validate_parameters <- function(endpoint, body) { 14 | 15 | valid <- TRUE 16 | 17 | endpoint <- gsub("/$", "", endpoint) 18 | params <- ohsome::ohsome_endpoints[[endpoint]]$parameters$name 19 | required <- params[ohsome::ohsome_endpoints[[endpoint]]$parameters$required] 20 | unknown_params <- setdiff(names(body), params) 21 | missing_params <- setdiff(required, names(body)) 22 | 23 | if(length(intersect(names(body), c("bpolys", "bboxes", "bcircles"))) != 1) { 24 | warning( 25 | "One (and only one) of the following parameters should be set: ", 26 | "bpolys, bboxes, or bcircles.", 27 | "\nYou can use set_boundary() to set a bounding geometry parameter.", 28 | call. = FALSE, 29 | immediate. = TRUE 30 | ) 31 | valid <- FALSE 32 | } 33 | 34 | if(!("time" %in% names(body)) && !("time" %in% missing_params)) { 35 | warning( 36 | "The time parameter is not defined and defaults to the latest ", 37 | "available timestamp within the underlying OSHDB.", 38 | "\nYou can use set_time() to set the time parameter.", 39 | call. = FALSE, 40 | immediate. = TRUE 41 | ) 42 | valid <- FALSE 43 | } 44 | 45 | if(!("filter" %in% names(body))) { 46 | warning( 47 | "The filter parameter is not defined.", 48 | "\nYou can use set_filter() to set the filter parameter.", 49 | call. = FALSE, 50 | immediate. = TRUE 51 | ) 52 | valid <- FALSE 53 | } 54 | 55 | if(grepl("ratio", endpoint) & !("filter2" %in% names(body))) { 56 | warning( 57 | "The filter2 parameter needs to be defined in ratio queries.", 58 | "\nYou can use set_filter() to set the filter2 parameter.", 59 | call. = FALSE, 60 | immediate. = TRUE 61 | ) 62 | valid <- FALSE 63 | } 64 | 65 | for(param in unknown_params) { 66 | warning( 67 | param, " is not a known parameter of ", 68 | "endpoint ", endpoint, ".", 69 | "\nYou can use set_parameters() with the argument ", 70 | param, " = NULL to remove it from the query.", 71 | call. = FALSE, 72 | immediate. = TRUE 73 | ) 74 | valid <- FALSE 75 | } 76 | 77 | for(param in missing_params) { 78 | warning( 79 | param, " is a required parameter in queries to the endpoint ", 80 | endpoint, ".", 81 | "\nYou can use set_parameters() to set the ", 82 | param, " parameter.", 83 | call. = FALSE, 84 | immediate. = TRUE 85 | ) 86 | valid <- FALSE 87 | } 88 | 89 | return(valid) 90 | } 91 | 92 | #' Validate endpoint 93 | #' 94 | #' Checks if the specified endpoint is in the list of known ohsome API endpoints 95 | #' and issues a warning if not. Specifically checks for invalid groupings in 96 | #' the endpoint path. Returns a logical that indicates the validity of 97 | #' the endpoint. 98 | #' 99 | #' @inheritParams validate_parameters 100 | #' @inherit validate_query return 101 | #' @keywords Internal 102 | #' @noRd 103 | validate_endpoint <- function(endpoint) { 104 | 105 | endpoint <- gsub("/$", "", endpoint) 106 | endpoints <- names(ohsome::ohsome_endpoints) 107 | split <- unlist(strsplit(endpoint, "/groupBy")) 108 | grouping_message <- NULL 109 | 110 | if(endpoint %in% endpoints) { 111 | return(TRUE) 112 | } else if(split[1] %in% endpoints) { 113 | 114 | if(length(split) > 1) { 115 | allowed <- endpoints[grepl(paste0("^", split[[1]], "/groupBy"), endpoints)] 116 | grouping_message <- ifelse( 117 | length(allowed) > 0, 118 | paste0( 119 | "\nOnly the following groupings are allowed with ", 120 | split[1], ":\n", 121 | paste("\t", gsub(split[[1]], "", allowed), collapse = "\n") 122 | ), 123 | paste0("\nGrouping is not allowed with ", split[1], "." 124 | ) 125 | ) 126 | } 127 | } 128 | 129 | warning( 130 | "ohsome does not know endpoint ", endpoint, 131 | grouping_message, 132 | "\nSee ", 133 | "https://docs.ohsome.org/ohsome-api/v1/endpoint-visualisation.html", 134 | " for available endpoints.", 135 | call. = FALSE, 136 | immediate. = TRUE 137 | ) 138 | return(FALSE) 139 | 140 | } 141 | 142 | #' Validate ohsome_query 143 | #' 144 | #' Validates an ohsome_query object by checking against [ohsome_endpoints]. 145 | #' Returns a logical that indicates the validity of the query. 146 | #' 147 | #' @inheritParams ohsome_post 148 | #' @return logical 149 | #' @keywords Internal 150 | #' @noRd 151 | validate_query <- function(query) { 152 | 153 | endpoint <- gsub("^.*?/", "", httr::parse_url(query$url)$path) 154 | valid_endpoint <- validate_endpoint(endpoint) 155 | if(valid_endpoint) { 156 | valid_params <- validate_parameters(endpoint, query$body) 157 | } 158 | return(valid_endpoint && valid_params) 159 | } 160 | 161 | #' Validate JSON response 162 | #' 163 | #' Validates ohsome API response content of type (Geo)JSON. Specifically checks 164 | #' for HTTP status messages in the content and uses any message to issue a 165 | #' warning. Returns a logical that indicates the validity of the JSON. 166 | #' 167 | #' @param json A JSON text string 168 | #' @return logical 169 | #' @keywords Internal 170 | #' @noRd 171 | validate_json <- function(json) { 172 | 173 | valid_json <- jsonlite::validate(json) 174 | status_message_pattern <- '.*status\" : [0-9]{3},\n \"message\" : \"' 175 | 176 | if(!valid_json && grepl(status_message_pattern, json)) { 177 | warning( 178 | paste( 179 | "HTTP status message in ohsome API response content:", 180 | sub('\".*$', "", sub(status_message_pattern, "", json)), 181 | sep = "\n" 182 | ), 183 | call. = FALSE 184 | ) 185 | } 186 | return(valid_json) 187 | } -------------------------------------------------------------------------------- /R/ohsome_boundary.R: -------------------------------------------------------------------------------- 1 | #' Create an `ohsome_boundary` object 2 | #' 3 | #' Creates an `ohsome_boundary` object from various classes of input geometries. 4 | #' The `ohsome_boundary` object is used to set the `bpolys`, `bboxes` or 5 | #' `bcircles` parameter of an `ohsome_query` object. 6 | #' 7 | #' @param boundary Bounding geometries specified by WGS84 coordinates in the 8 | #' order `lon,lat`. The geometries of `sf` are transformed to WGS84 if the CRS 9 | #' of the object is known. The following classes are supported: 10 | #' * `sf` with (MULTI)POLYGON geometries 11 | #' * `sfc` with (MULTI)POLYGON geometries 12 | #' * `sfg` with (MULTI)POLYGON geometries and WGS 84 coordinates 13 | #' * `bbox` created with [sf::st_bbox()] or [tmaptools::bb()] 14 | #' * `matrix` created with [sp::bbox()] or [osmdata::getbb()] 15 | #' * `character` providing textual definitions of bounding polygons, boxes or 16 | #' circles as allowed by the ohsome API (see 17 | #' [ohsome API - Boundaries](https://docs.ohsome.org/ohsome-api/stable/boundaries.html) 18 | #' ): 19 | #' * bboxes: WGS84 coordinates in the following format: 20 | #' `"id1:lon1,lat1,lon2,lat2|id2:lon1,lat1,lon2,lat2|..."` OR 21 | #' `"lon1,lat1,lon2,lat2|lon1,lat1,lon2,lat2|..."` 22 | #' * bcircles: WGS84 coordinates + radius in meter in the following 23 | #' format: `"id1:lon,lat,r|id2:lon,lat,r|..."` OR 24 | #' `"lon,lat,r|lon,lat,r|..."` 25 | #' * bpolys: WGS84 coordinates given as a list of coordinate pairs (as for 26 | #' bboxes) or GeoJSON FeatureCollection. The first point has to be the same 27 | #' as the last point and MultiPolygons are only supported in GeoJSON. 28 | #' * `list` of `bbox`, `matrix` or `character`. Bounding geometry types of all 29 | #' list elements must be the same. Does not work with GeoJSON 30 | #' FeatureCollections. 31 | #' @param ... Additional arguments other than `digits` are ignored. 32 | #' @param digits integer; number of decimal places of coordinates in the 33 | #' resulting GeoJSON when converting `sf` to GeoJSON (defaults to 6). 34 | #' @return An `ohsome_boundary` object which contains the following elements: 35 | #' * `boundary`: the boundary in textual format 36 | #' * `type` of the boundary (`bpolys`, `bcircles`, or `bboxes`). 37 | #' @export 38 | #' @examples 39 | #' # Defintion of a bounding circle (lon,lat,radius in meters) 40 | #' ohsome_boundary("8.6528,49.3683,1000") 41 | #' 42 | #' # Definition of two named bounding circles 43 | #' ohsome_boundary("Circle 1:8.6528,49.3683,1000|Circle 2:8.7294,49.4376,1000") 44 | #' 45 | #' # Definition of two named bounding circles with a character vector 46 | #' ohsome_boundary(c("Circle 1:8.6528,49.3683,1000", "Circle 2:8.7294,49.4376,1000")) 47 | #' 48 | #' # Use franconia from the mapview package as bounding polygons 49 | #' \donttest{ 50 | #' ohsome_boundary(mapview::franconia, digits = 4) 51 | #' } 52 | #' 53 | #' # Use the bounding box of franconia 54 | #' \donttest{ 55 | #' ohsome_boundary(sf::st_bbox(mapview::franconia)) 56 | #' } 57 | #' 58 | #' # Get bounding box of the city of Berlin from OSM 59 | #' \dontrun{ 60 | #' ohsome_boundary(osmdata::getbb("Berlin")) 61 | #' } 62 | #' 63 | #' # Use a list of two bounding boxes 64 | #' \dontrun{ 65 | #' ohsome_boundary(list(osmdata::getbb("Berlin"), sf::st_bbox(mapview::franconia))) 66 | #' } 67 | #' 68 | ohsome_boundary <- function(boundary, ...) UseMethod("ohsome_boundary") 69 | 70 | #' @name ohsome_boundary 71 | #' @export 72 | ohsome_boundary.ohsome_boundary <- function(boundary, ...) return(boundary) 73 | 74 | #' @name ohsome_boundary 75 | #' @export 76 | ohsome_boundary.character <- function(boundary, ...) { 77 | 78 | if(jsonlite::validate(boundary) && length(boundary) == 1) { 79 | type <- "bpolys" 80 | } else { 81 | splt <- unlist(strsplit(boundary, split = "|", fixed = TRUE)) 82 | len <- unique(sapply(strsplit(splt, split = ","), length)) 83 | 84 | if(length(len) == 1 && len == 3) { 85 | type <- "bcircles" 86 | } else if(length(len) == 1 && len == 4) { 87 | type <- "bboxes" 88 | } else type <- "bpolys" 89 | } 90 | 91 | structure(list( 92 | boundary = paste(boundary, collapse = "|"), type = type), 93 | class = "ohsome_boundary" 94 | ) 95 | } 96 | 97 | #' @name ohsome_boundary 98 | #' @export 99 | ohsome_boundary.sf <- function(boundary, digits = 6, ...) { 100 | 101 | if( (sf::st_crs(boundary)$input != "EPSG:4326") & !is.na(sf::st_crs(boundary)) ) 102 | boundary <- sf::st_transform(boundary, 4326) 103 | 104 | types <- sf::st_geometry_type(boundary) 105 | 106 | if(!any(types %in% c("POLYGON", "MULTIPOLYGON"))) { 107 | stop( 108 | "None of the geometries provided are of type (MULTI)POLYGON.", 109 | call. = FALSE 110 | ) 111 | } 112 | 113 | if(!all(types %in% c("POLYGON", "MULTIPOLYGON"))) { 114 | warning( 115 | "At least one of the bounding geometries is not of type ", 116 | "(MULTI)POLYGON and was omitted.", 117 | call. = FALSE 118 | ) 119 | boundary <- boundary[types %in% c("POLYGON", "MULTIPOLYGON"),] 120 | } 121 | 122 | geojson <- geojsonsf::sf_geojson(boundary, digits = digits, simplify = FALSE) 123 | type <- "bpolys" 124 | 125 | structure(list(boundary = geojson, type = type), class = "ohsome_boundary") 126 | } 127 | 128 | #' @name ohsome_boundary 129 | #' @export 130 | ohsome_boundary.sfc <- function(boundary, ...) ohsome_boundary(sf::st_as_sf(boundary), ...) 131 | 132 | #' @name ohsome_boundary 133 | #' @export 134 | ohsome_boundary.sfg <- function(boundary, ...) ohsome_boundary(sf::st_sfc(boundary, crs = 4326), ...) 135 | 136 | #' @name ohsome_boundary 137 | #' @export 138 | ohsome_boundary.bbox <- function(boundary, ...) { 139 | 140 | if( (attributes(boundary)$crs$input != "EPSG:4326") & (!is.na((attributes(boundary)$crs$input)) ) ) { 141 | boundary <- sf::st_bbox(sf::st_transform(sf::st_as_sfc(boundary), 4326)) 142 | } 143 | 144 | ohsome_boundary(paste(boundary, collapse = ",")) 145 | } 146 | 147 | #' @name ohsome_boundary 148 | #' @export 149 | ohsome_boundary.matrix <- function(boundary, ...) ohsome_boundary(paste(boundary, collapse = ",")) 150 | 151 | #' @name ohsome_boundary 152 | #' @export 153 | ohsome_boundary.list <- function(boundary, ...) { 154 | ohsome_boundary(paste(sapply(boundary, ohsome_boundary)[1,], collapse = "|")) 155 | } 156 | -------------------------------------------------------------------------------- /man/ohsome_aggregate_elements.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ohsome_aggregate_elements.R 3 | \name{ohsome_aggregate_elements} 4 | \alias{ohsome_aggregate_elements} 5 | \alias{ohsome_elements_count} 6 | \alias{ohsome_elements_length} 7 | \alias{ohsome_elements_perimeter} 8 | \alias{ohsome_elements_area} 9 | \title{Aggregate OSM elements} 10 | \usage{ 11 | ohsome_aggregate_elements( 12 | boundary = NULL, 13 | aggregation = c("count", "length", "perimeter", "area"), 14 | return_value = c("absolute", "density", "ratio"), 15 | grouping = NULL, 16 | time = NULL, 17 | ... 18 | ) 19 | 20 | ohsome_elements_count(boundary = NULL, ...) 21 | 22 | ohsome_elements_length(boundary = NULL, ...) 23 | 24 | ohsome_elements_perimeter(boundary = NULL, ...) 25 | 26 | ohsome_elements_area(boundary = NULL, ...) 27 | } 28 | \arguments{ 29 | \item{boundary}{Bounding geometries specified by WGS84 coordinates in the 30 | order \verb{lon,lat}. The geometries of \code{sf} are transformed to WGS84 if the CRS 31 | of the object is known. The following classes are supported: 32 | \itemize{ 33 | \item \code{sf} with (MULTI)POLYGON geometries 34 | \item \code{sfc} with (MULTI)POLYGON geometries 35 | \item \code{sfg} with (MULTI)POLYGON geometries and WGS 84 coordinates 36 | \item \code{bbox} created with \code{\link[sf:st_bbox]{sf::st_bbox()}} or \code{\link[tmaptools:bb]{tmaptools::bb()}} 37 | \item \code{matrix} created with \code{\link[sp:bbox]{sp::bbox()}} or \code{\link[osmdata:getbb]{osmdata::getbb()}} 38 | \item \code{character} providing textual definitions of bounding polygons, boxes or 39 | circles as allowed by the ohsome API (see 40 | \href{https://docs.ohsome.org/ohsome-api/stable/boundaries.html}{ohsome API - Boundaries} 41 | ): 42 | \itemize{ 43 | \item bboxes: WGS84 coordinates in the following format: 44 | \code{"id1:lon1,lat1,lon2,lat2|id2:lon1,lat1,lon2,lat2|..."} OR 45 | \code{"lon1,lat1,lon2,lat2|lon1,lat1,lon2,lat2|..."} 46 | \item bcircles: WGS84 coordinates + radius in meter in the following 47 | format: \code{"id1:lon,lat,r|id2:lon,lat,r|..."} OR 48 | \code{"lon,lat,r|lon,lat,r|..."} 49 | \item bpolys: WGS84 coordinates given as a list of coordinate pairs (as for 50 | bboxes) or GeoJSON FeatureCollection. The first point has to be the same 51 | as the last point and MultiPolygons are only supported in GeoJSON. 52 | } 53 | \item \code{list} of \code{bbox}, \code{matrix} or \code{character}. Bounding geometry types of all 54 | list elements must be the same. Does not work with GeoJSON 55 | FeatureCollections. 56 | }} 57 | 58 | \item{aggregation}{character; aggregation type: 59 | \itemize{ 60 | \item \code{"count"} returns the total number of elements. This is the default. 61 | \item \code{"length"} returns the total length of elements in meters. 62 | \item \code{"perimeter"} returns the total perimeter of elements in meters. 63 | \item \code{"area"} returns the total area of elements in square meters. 64 | }} 65 | 66 | \item{return_value}{character; the value to be returned by the ohsome API: 67 | \itemize{ 68 | \item \code{"absolute"} returns the absolute number, length, perimeter or area of 69 | elements. This is the default. 70 | \item \code{"density"} returns the number, length, perimeter or area (in meters!) of 71 | elements per square kilometer. 72 | \item \code{"ratio"} returns an absolute \code{value} for elements satisfying the 73 | \code{filter} argument, an absolute \code{value2} for elements satisfying the 74 | \code{filter2} argument, and the \code{ratio} of \code{value2} to \code{value}. 75 | }} 76 | 77 | \item{grouping}{character; group type(s) for grouped aggregations (only 78 | available for queries to aggregation endpoints). The following group types 79 | are available: 80 | \itemize{ 81 | \item \code{"boundary"} groups the result by the given boundaries that are defined 82 | through any of the \code{boundary} query parameters. 83 | \item \code{"key"} groups the result by the given keys that are defined through the 84 | \code{groupByKeys} query parameter. 85 | \item \code{"tag"} groups the result by the given tags that are defined through the 86 | \code{groupByKey} and \code{groupByValues} query parameters. 87 | \item \code{"type"} groups the result by OSM element type. 88 | \item \code{c("boundary", "tag")} groups the result by the given boundaries and 89 | tags. 90 | } 91 | 92 | Not all of these group types are accepted by all of the aggregation 93 | endpoints. Check 94 | \href{https://docs.ohsome.org/ohsome-api/v1/group-by.html}{Grouping} 95 | for available group types.} 96 | 97 | \item{time}{character; \code{time} parameter of the query (see 98 | \href{https://docs.ohsome.org/ohsome-api/v1/time.html}{Supported time formats}).} 99 | 100 | \item{...}{Parameters of the request to the ohsome API endpoint.} 101 | } 102 | \value{ 103 | An \code{ohsome_query} object. The object can be sent to the ohsome API 104 | with \code{\link[=ohsome_post]{ohsome_post()}}. It consists of the following elements: 105 | \itemize{ 106 | \item \code{url}: The URL of the endpoint. 107 | \item \code{encode}: The way the information is encoded and then posted to the 108 | ohsome API. Set as \code{"form"}. 109 | \item \code{body}: The parameters of the query such as \code{format}, \code{filter} or 110 | \code{bpolys}. 111 | } 112 | } 113 | \description{ 114 | Creates an \code{ohsome_query} object for OSM element aggregation 115 | } 116 | \details{ 117 | \code{ohsome_aggregate_elements()} creates an \code{ohsome_query} object for 118 | OSM element aggregation. \code{ohsome_elements_count()}, 119 | \code{ohsome_elements_length()}, \code{ohsome_elements_perimeter()} and 120 | \code{ohsome_elements_area()} are wrapper functions for specific aggregation 121 | endpoints. Boundary objects are passed via \code{\link[=set_boundary]{set_boundary()}} into 122 | \code{\link[=ohsome_boundary]{ohsome_boundary()}}. 123 | } 124 | \examples{ 125 | \donttest{ 126 | # Count of breweries in Franconia 127 | ohsome_aggregate_elements( 128 | mapview::franconia, 129 | aggregation = "count", 130 | filter = "craft=brewery", 131 | time = "2022-01-01" 132 | ) 133 | 134 | ohsome_elements_count( 135 | mapview::franconia, 136 | filter = "craft=brewery", 137 | time = "2022-01-01" 138 | ) 139 | 140 | # Monthly counts of breweries in Franconia from 2012 to 2022 141 | ohsome_elements_count( 142 | mapview::franconia, 143 | filter = "craft=brewery", 144 | time = "2012/2022/P1M" 145 | ) 146 | 147 | # Count of breweries per district of Franconia 148 | ohsome_elements_count( 149 | mapview::franconia, 150 | filter = "craft=brewery", 151 | grouping = "boundary", 152 | time = "2022-01-01" 153 | ) 154 | 155 | # Number of breweries per square kilometer 156 | ohsome_elements_count( 157 | mapview::franconia, 158 | filter = "craft=brewery", 159 | return_value = "density", 160 | time = "2022-01-01" 161 | ) 162 | 163 | # Proportion of breweries that are microbreweries 164 | ohsome_elements_count( 165 | mapview::franconia, 166 | filter = "craft=brewery", 167 | filter2 = "craft=brewery and microbrewery=yes", 168 | return_value = "ratio", 169 | time = "2022-01-01" 170 | ) 171 | 172 | # Total length of highway elements in Franconia 173 | ohsome_elements_length( 174 | mapview::franconia, 175 | filter = "highway=* and geometry:line", 176 | time = "2022-01-01" 177 | ) 178 | } 179 | 180 | } 181 | \seealso{ 182 | \href{https://docs.ohsome.org/ohsome-api/stable/endpoints.html#elements-aggregation}{ohsome API Endpoints - Elements Aggregation} 183 | } 184 | \concept{Aggregate elements} 185 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | GNU Lesser General Public License 2 | ================================= 3 | 4 | _Version 3, 29 June 2007_ 5 | _Copyright © 2007 Free Software Foundation, Inc. <>_ 6 | 7 | Everyone is permitted to copy and distribute verbatim copies 8 | of this license document, but changing it is not allowed. 9 | 10 | 11 | This version of the GNU Lesser General Public License incorporates 12 | the terms and conditions of version 3 of the GNU General Public 13 | License, supplemented by the additional permissions listed below. 14 | 15 | ### 0. Additional Definitions 16 | 17 | As used herein, “this License” refers to version 3 of the GNU Lesser 18 | General Public License, and the “GNU GPL” refers to version 3 of the GNU 19 | General Public License. 20 | 21 | “The Library” refers to a covered work governed by this License, 22 | other than an Application or a Combined Work as defined below. 23 | 24 | An “Application” is any work that makes use of an interface provided 25 | by the Library, but which is not otherwise based on the Library. 26 | Defining a subclass of a class defined by the Library is deemed a mode 27 | of using an interface provided by the Library. 28 | 29 | A “Combined Work” is a work produced by combining or linking an 30 | Application with the Library. The particular version of the Library 31 | with which the Combined Work was made is also called the “Linked 32 | Version”. 33 | 34 | The “Minimal Corresponding Source” for a Combined Work means the 35 | Corresponding Source for the Combined Work, excluding any source code 36 | for portions of the Combined Work that, considered in isolation, are 37 | based on the Application, and not on the Linked Version. 38 | 39 | The “Corresponding Application Code” for a Combined Work means the 40 | object code and/or source code for the Application, including any data 41 | and utility programs needed for reproducing the Combined Work from the 42 | Application, but excluding the System Libraries of the Combined Work. 43 | 44 | ### 1. Exception to Section 3 of the GNU GPL 45 | 46 | You may convey a covered work under sections 3 and 4 of this License 47 | without being bound by section 3 of the GNU GPL. 48 | 49 | ### 2. Conveying Modified Versions 50 | 51 | If you modify a copy of the Library, and, in your modifications, a 52 | facility refers to a function or data to be supplied by an Application 53 | that uses the facility (other than as an argument passed when the 54 | facility is invoked), then you may convey a copy of the modified 55 | version: 56 | 57 | * **a)** under this License, provided that you make a good faith effort to 58 | ensure that, in the event an Application does not supply the 59 | function or data, the facility still operates, and performs 60 | whatever part of its purpose remains meaningful, or 61 | 62 | * **b)** under the GNU GPL, with none of the additional permissions of 63 | this License applicable to that copy. 64 | 65 | ### 3. Object Code Incorporating Material from Library Header Files 66 | 67 | The object code form of an Application may incorporate material from 68 | a header file that is part of the Library. You may convey such object 69 | code under terms of your choice, provided that, if the incorporated 70 | material is not limited to numerical parameters, data structure 71 | layouts and accessors, or small macros, inline functions and templates 72 | (ten or fewer lines in length), you do both of the following: 73 | 74 | * **a)** Give prominent notice with each copy of the object code that the 75 | Library is used in it and that the Library and its use are 76 | covered by this License. 77 | * **b)** Accompany the object code with a copy of the GNU GPL and this license 78 | document. 79 | 80 | ### 4. Combined Works 81 | 82 | You may convey a Combined Work under terms of your choice that, 83 | taken together, effectively do not restrict modification of the 84 | portions of the Library contained in the Combined Work and reverse 85 | engineering for debugging such modifications, if you also do each of 86 | the following: 87 | 88 | * **a)** Give prominent notice with each copy of the Combined Work that 89 | the Library is used in it and that the Library and its use are 90 | covered by this License. 91 | 92 | * **b)** Accompany the Combined Work with a copy of the GNU GPL and this license 93 | document. 94 | 95 | * **c)** For a Combined Work that displays copyright notices during 96 | execution, include the copyright notice for the Library among 97 | these notices, as well as a reference directing the user to the 98 | copies of the GNU GPL and this license document. 99 | 100 | * **d)** Do one of the following: 101 | - **0)** Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | - **1)** Use a suitable shared library mechanism for linking with the 109 | Library. A suitable mechanism is one that **(a)** uses at run time 110 | a copy of the Library already present on the user's computer 111 | system, and **(b)** will operate properly with a modified version 112 | of the Library that is interface-compatible with the Linked 113 | Version. 114 | 115 | * **e)** Provide Installation Information, but only if you would otherwise 116 | be required to provide such information under section 6 of the 117 | GNU GPL, and only to the extent that such information is 118 | necessary to install and execute a modified version of the 119 | Combined Work produced by recombining or relinking the 120 | Application with a modified version of the Linked Version. (If 121 | you use option **4d0**, the Installation Information must accompany 122 | the Minimal Corresponding Source and Corresponding Application 123 | Code. If you use option **4d1**, you must provide the Installation 124 | Information in the manner specified by section 6 of the GNU GPL 125 | for conveying Corresponding Source.) 126 | 127 | ### 5. Combined Libraries 128 | 129 | You may place library facilities that are a work based on the 130 | Library side by side in a single library together with other library 131 | facilities that are not Applications and are not covered by this 132 | License, and convey such a combined library under terms of your 133 | choice, if you do both of the following: 134 | 135 | * **a)** Accompany the combined library with a copy of the same work based 136 | on the Library, uncombined with any other library facilities, 137 | conveyed under the terms of this License. 138 | * **b)** Give prominent notice with the combined library that part of it 139 | is a work based on the Library, and explaining where to find the 140 | accompanying uncombined form of the same work. 141 | 142 | ### 6. Revised Versions of the GNU Lesser General Public License 143 | 144 | The Free Software Foundation may publish revised and/or new versions 145 | of the GNU Lesser General Public License from time to time. Such new 146 | versions will be similar in spirit to the present version, but may 147 | differ in detail to address new problems or concerns. 148 | 149 | Each version is given a distinguishing version number. If the 150 | Library as you received it specifies that a certain numbered version 151 | of the GNU Lesser General Public License “or any later version” 152 | applies to it, you have the option of following the terms and 153 | conditions either of that published version or of any later version 154 | published by the Free Software Foundation. If the Library as you 155 | received it does not specify a version number of the GNU Lesser 156 | General Public License, you may choose any version of the GNU Lesser 157 | General Public License ever published by the Free Software Foundation. 158 | 159 | If the Library as you received it specifies that a proxy can decide 160 | whether future versions of the GNU Lesser General Public License shall 161 | apply, that proxy's public statement of acceptance of any version is 162 | permanent authorization for you to choose that version for the 163 | Library. 164 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------- 2 | # CITATION file created with {cffr} R package, v0.4.0 3 | # See also: https://docs.ropensci.org/cffr/ 4 | # ----------------------------------------------------------- 5 | 6 | cff-version: 1.2.0 7 | message: 'To cite package "ohsome" in publications use:' 8 | type: software 9 | license: LGPL-3.0-or-later 10 | title: 'ohsome: An ''ohsome API'' Client' 11 | version: 0.2.0 12 | doi: 10.5281/zenodo.5512545 13 | abstract: A client that grants access to the power of the 'ohsome API' from R. It 14 | lets you analyze the rich data source of the 'OpenStreetMap (OSM)' history. You 15 | can retrieve the geometry of 'OSM' data at specific points in time, and you can 16 | get aggregated statistics on the evolution of 'OSM' elements and specify your own 17 | temporal, spatial and/or thematic filters. 18 | authors: 19 | - family-names: Fritz 20 | given-names: Oliver 21 | email: oliver.fritz@heigit.org 22 | orcid: https://orcid.org/0000-0001-6324-7295 23 | preferred-citation: 24 | type: generic 25 | title: OSHDB - OpenStreetMap History Data Analysis (0.7.2) 26 | authors: 27 | - family-names: Raifer 28 | given-names: Martin 29 | - family-names: Troilo 30 | given-names: Rafael 31 | - family-names: Mocnik 32 | given-names: Franz-Benjamin 33 | - family-names: Schott 34 | given-names: Moritz 35 | month: '9' 36 | year: '2021' 37 | publisher: 38 | name: Zenodo 39 | version: 0.7.2 40 | doi: 10.5281/zenodo.5512545 41 | url: https://doi.org/10.5281/zenodo.5512545 42 | repository-code: https://github.com/GIScience/ohsome-r 43 | url: https://docs.ohsome.org/ohsome-api/stable/ 44 | contact: 45 | - family-names: Fritz 46 | given-names: Oliver 47 | email: oliver.fritz@heigit.org 48 | orcid: https://orcid.org/0000-0001-6324-7295 49 | keywords: 50 | - heigit 51 | - ohsome 52 | - openstreetmap 53 | - openstreetmap-data 54 | - openstreetmap-history 55 | - osm 56 | - osm-data 57 | references: 58 | - type: article 59 | title: 'OSHDB: a framework for spatio-temporal analysis of OpenStreetMap history 60 | data' 61 | authors: 62 | - family-names: Raifer 63 | given-names: Martin 64 | - family-names: Troilo 65 | given-names: Rafael 66 | - family-names: Kowatsch 67 | given-names: Fabian 68 | - family-names: Auer 69 | given-names: Michael 70 | - family-names: Loos 71 | given-names: Lukas 72 | - family-names: Marx 73 | given-names: Sabrina 74 | - family-names: Przybill 75 | given-names: Katharina 76 | - family-names: Fendrich 77 | given-names: Sascha 78 | - family-names: Mocnik 79 | given-names: Franz-Benjamin 80 | - family-names: Zipf 81 | given-names: Alexander 82 | journal: Open Geospatial Data, Software and Standards 83 | year: '2019' 84 | month: '4' 85 | volume: '4' 86 | issue: '1' 87 | doi: 10.1186/s40965-019-0061-3 88 | url: https://doi.org/10.1186/s40965-019-0061-3 89 | start: '3' 90 | - type: software 91 | title: 'R: A Language and Environment for Statistical Computing' 92 | notes: Depends 93 | url: https://www.R-project.org/ 94 | authors: 95 | - name: R Core Team 96 | location: 97 | name: Vienna, Austria 98 | year: '2023' 99 | institution: 100 | name: R Foundation for Statistical Computing 101 | version: '>= 2.10' 102 | - type: software 103 | title: geojsonsf 104 | abstract: 'geojsonsf: GeoJSON to Simple Feature Converter' 105 | notes: Imports 106 | url: https://github.com/SymbolixAU/geojsonsf 107 | repository: https://CRAN.R-project.org/package=geojsonsf 108 | authors: 109 | - family-names: Cooley 110 | given-names: David 111 | email: dcooley@symbolix.com.au 112 | year: '2023' 113 | - type: software 114 | title: httr 115 | abstract: 'httr: Tools for Working with URLs and HTTP' 116 | notes: Imports 117 | url: https://httr.r-lib.org/ 118 | repository: https://CRAN.R-project.org/package=httr 119 | authors: 120 | - family-names: Wickham 121 | given-names: Hadley 122 | email: hadley@rstudio.com 123 | year: '2023' 124 | - type: software 125 | title: jsonlite 126 | abstract: 'jsonlite: A Simple and Robust JSON Parser and Generator for R' 127 | notes: Imports 128 | url: https://arxiv.org/abs/1403.2805 129 | repository: https://CRAN.R-project.org/package=jsonlite 130 | authors: 131 | - family-names: Ooms 132 | given-names: Jeroen 133 | email: jeroen@berkeley.edu 134 | orcid: https://orcid.org/0000-0002-4035-0289 135 | year: '2023' 136 | - type: software 137 | title: readr 138 | abstract: 'readr: Read Rectangular Text Data' 139 | notes: Imports 140 | url: https://readr.tidyverse.org 141 | repository: https://CRAN.R-project.org/package=readr 142 | authors: 143 | - family-names: Wickham 144 | given-names: Hadley 145 | email: hadley@rstudio.com 146 | - family-names: Hester 147 | given-names: Jim 148 | - family-names: Bryan 149 | given-names: Jennifer 150 | email: jenny@rstudio.com 151 | orcid: https://orcid.org/0000-0002-6983-2759 152 | year: '2023' 153 | - type: software 154 | title: sf 155 | abstract: 'sf: Simple Features for R' 156 | notes: Imports 157 | url: https://r-spatial.github.io/sf/ 158 | repository: https://CRAN.R-project.org/package=sf 159 | authors: 160 | - family-names: Pebesma 161 | given-names: Edzer 162 | email: edzer.pebesma@uni-muenster.de 163 | orcid: https://orcid.org/0000-0001-8049-7069 164 | year: '2023' 165 | - type: software 166 | title: utils 167 | abstract: 'R: A Language and Environment for Statistical Computing' 168 | notes: Imports 169 | authors: 170 | - name: R Core Team 171 | location: 172 | name: Vienna, Austria 173 | year: '2023' 174 | institution: 175 | name: R Foundation for Statistical Computing 176 | - type: software 177 | title: dplyr 178 | abstract: 'dplyr: A Grammar of Data Manipulation' 179 | notes: Suggests 180 | url: https://dplyr.tidyverse.org 181 | repository: https://CRAN.R-project.org/package=dplyr 182 | authors: 183 | - family-names: Wickham 184 | given-names: Hadley 185 | email: hadley@rstudio.com 186 | orcid: https://orcid.org/0000-0003-4757-117X 187 | - family-names: François 188 | given-names: Romain 189 | orcid: https://orcid.org/0000-0002-2444-4226 190 | - family-names: Henry 191 | given-names: Lionel 192 | - family-names: Müller 193 | given-names: Kirill 194 | orcid: https://orcid.org/0000-0002-1416-3412 195 | year: '2023' 196 | - type: software 197 | title: ggplot2 198 | abstract: 'ggplot2: Create Elegant Data Visualisations Using the Grammar of Graphics' 199 | notes: Suggests 200 | url: https://ggplot2.tidyverse.org 201 | repository: https://CRAN.R-project.org/package=ggplot2 202 | authors: 203 | - family-names: Wickham 204 | given-names: Hadley 205 | email: hadley@rstudio.com 206 | orcid: https://orcid.org/0000-0003-4757-117X 207 | - family-names: Chang 208 | given-names: Winston 209 | orcid: https://orcid.org/0000-0002-1576-2126 210 | - family-names: Henry 211 | given-names: Lionel 212 | - family-names: Pedersen 213 | given-names: Thomas Lin 214 | email: thomas.pedersen@rstudio.com 215 | orcid: https://orcid.org/0000-0002-5147-4711 216 | - family-names: Takahashi 217 | given-names: Kohske 218 | - family-names: Wilke 219 | given-names: Claus 220 | orcid: https://orcid.org/0000-0002-7470-9261 221 | - family-names: Woo 222 | given-names: Kara 223 | orcid: https://orcid.org/0000-0002-5125-4188 224 | - family-names: Yutani 225 | given-names: Hiroaki 226 | orcid: https://orcid.org/0000-0002-3385-7233 227 | - family-names: Dunnington 228 | given-names: Dewey 229 | orcid: https://orcid.org/0000-0002-9415-4582 230 | year: '2023' 231 | - type: software 232 | title: httptest 233 | abstract: 'httptest: A Test Environment for HTTP Requests' 234 | notes: Suggests 235 | url: https://enpiar.com/r/httptest/ 236 | repository: https://CRAN.R-project.org/package=httptest 237 | authors: 238 | - family-names: Richardson 239 | given-names: Neal 240 | email: neal.p.richardson@gmail.com 241 | year: '2023' 242 | - type: software 243 | title: janitor 244 | abstract: 'janitor: Simple Tools for Examining and Cleaning Dirty Data' 245 | notes: Suggests 246 | url: https://github.com/sfirke/janitor 247 | repository: https://CRAN.R-project.org/package=janitor 248 | authors: 249 | - family-names: Firke 250 | given-names: Sam 251 | email: samuel.firke@gmail.com 252 | year: '2023' 253 | - type: software 254 | title: knitr 255 | abstract: 'knitr: A General-Purpose Package for Dynamic Report Generation in R' 256 | notes: Suggests 257 | url: https://yihui.org/knitr/ 258 | repository: https://CRAN.R-project.org/package=knitr 259 | authors: 260 | - family-names: Xie 261 | given-names: Yihui 262 | email: xie@yihui.name 263 | orcid: https://orcid.org/0000-0003-0645-5666 264 | year: '2023' 265 | - type: software 266 | title: mapview 267 | abstract: 'mapview: Interactive Viewing of Spatial Data in R' 268 | notes: Suggests 269 | url: https://github.com/r-spatial/mapview 270 | repository: https://CRAN.R-project.org/package=mapview 271 | authors: 272 | - family-names: Appelhans 273 | given-names: Tim 274 | email: tim.appelhans@gmail.com 275 | - family-names: Detsch 276 | given-names: Florian 277 | email: fdetsch@web.de 278 | - family-names: Reudenbach 279 | given-names: Christoph 280 | email: reudenbach@geo.uni-marburg.de 281 | - family-names: Woellauer 282 | given-names: Stefan 283 | email: stephan.woellauer@geo.uni-marburg.de 284 | year: '2023' 285 | - type: software 286 | title: nominatimlite 287 | abstract: 'nominatimlite: Interface with ''Nominatim'' API Service' 288 | notes: Suggests 289 | url: https://dieghernan.github.io/nominatimlite/ 290 | repository: https://CRAN.R-project.org/package=nominatimlite 291 | authors: 292 | - family-names: Hernangómez 293 | given-names: Diego 294 | email: diego.hernangomezherrero@gmail.com 295 | orcid: https://orcid.org/0000-0001-8457-4658 296 | year: '2023' 297 | - type: software 298 | title: osmdata 299 | abstract: 'osmdata: Import ''OpenStreetMap'' Data as Simple Features or Spatial 300 | Objects' 301 | notes: Suggests 302 | url: https://docs.ropensci.org/osmdata/ 303 | repository: https://CRAN.R-project.org/package=osmdata 304 | authors: 305 | - family-names: Padgham 306 | given-names: Mark 307 | email: mark.padgham@email.com 308 | - family-names: Rudis 309 | given-names: Bob 310 | - family-names: Lovelace 311 | given-names: Robin 312 | - family-names: Salmon 313 | given-names: Maëlle 314 | year: '2023' 315 | - type: software 316 | title: rmarkdown 317 | abstract: 'rmarkdown: Dynamic Documents for R' 318 | notes: Suggests 319 | url: https://pkgs.rstudio.com/rmarkdown/ 320 | repository: https://CRAN.R-project.org/package=rmarkdown 321 | authors: 322 | - family-names: Allaire 323 | given-names: JJ 324 | email: jj@rstudio.com 325 | - family-names: Xie 326 | given-names: Yihui 327 | email: xie@yihui.name 328 | orcid: https://orcid.org/0000-0003-0645-5666 329 | - family-names: McPherson 330 | given-names: Jonathan 331 | email: jonathan@rstudio.com 332 | - family-names: Luraschi 333 | given-names: Javier 334 | email: javier@rstudio.com 335 | - family-names: Ushey 336 | given-names: Kevin 337 | email: kevin@rstudio.com 338 | - family-names: Atkins 339 | given-names: Aron 340 | email: aron@rstudio.com 341 | - family-names: Wickham 342 | given-names: Hadley 343 | email: hadley@rstudio.com 344 | - family-names: Cheng 345 | given-names: Joe 346 | email: joe@rstudio.com 347 | - family-names: Chang 348 | given-names: Winston 349 | email: winston@rstudio.com 350 | - family-names: Iannone 351 | given-names: Richard 352 | email: rich@rstudio.com 353 | orcid: https://orcid.org/0000-0003-3925-190X 354 | year: '2023' 355 | - type: software 356 | title: spelling 357 | abstract: 'spelling: Tools for Spell Checking in R' 358 | notes: Suggests 359 | url: https://docs.ropensci.org/spelling/ 360 | repository: https://CRAN.R-project.org/package=spelling 361 | authors: 362 | - family-names: Ooms 363 | given-names: Jeroen 364 | email: jeroen@berkeley.edu 365 | orcid: https://orcid.org/0000-0002-4035-0289 366 | - family-names: Hester 367 | given-names: Jim 368 | email: james.hester@rstudio.com 369 | year: '2023' 370 | - type: software 371 | title: testthat 372 | abstract: 'testthat: Unit Testing for R' 373 | notes: Suggests 374 | url: https://testthat.r-lib.org 375 | repository: https://CRAN.R-project.org/package=testthat 376 | authors: 377 | - family-names: Wickham 378 | given-names: Hadley 379 | email: hadley@rstudio.com 380 | year: '2023' 381 | version: '>= 3.0.0' 382 | - type: software 383 | title: tmaptools 384 | abstract: 'tmaptools: Thematic Map Tools' 385 | notes: Suggests 386 | url: https://github.com/mtennekes/tmaptools 387 | repository: https://CRAN.R-project.org/package=tmaptools 388 | authors: 389 | - family-names: Tennekes 390 | given-names: Martijn 391 | email: mtennekes@gmail.com 392 | year: '2023' 393 | - type: software 394 | title: tidyr 395 | abstract: 'tidyr: Tidy Messy Data' 396 | notes: Suggests 397 | url: https://tidyr.tidyverse.org 398 | repository: https://CRAN.R-project.org/package=tidyr 399 | authors: 400 | - family-names: Wickham 401 | given-names: Hadley 402 | email: hadley@rstudio.com 403 | - family-names: Girlich 404 | given-names: Maximilian 405 | year: '2023' 406 | -------------------------------------------------------------------------------- /vignettes/ohsome.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "ohsome" 3 | output: rmarkdown::html_vignette 4 | vignette: > 5 | %\VignetteIndexEntry{ohsome} 6 | %\VignetteEngine{knitr::rmarkdown} 7 | %\VignetteEncoding{UTF-8} 8 | --- 9 | 10 | ```{r, include = FALSE} 11 | NOT_CRAN <- identical(tolower(Sys.getenv("NOT_CRAN")), "true") 12 | httr::set_config(httr::config(http_version = 1)) 13 | knitr::opts_chunk$set( 14 | collapse = TRUE, 15 | comment = "#>", 16 | fig.path = "figures/", 17 | out.width = "100%", 18 | purl = NOT_CRAN, 19 | eval = NOT_CRAN 20 | ) 21 | ``` 22 | 23 | This ohsome R package grants access to the power of the [ohsome API](https://api.ohsome.org){target=blank} 24 | from R. ohsome lets you analyze the rich data source of the 25 | [OpenStreetMap](https://www.openstreetmap.org/){target=blank} 26 | (OSM) history. It aims to leverage the tools of the 27 | [OpenStreetMap History Database](https://github.com/GIScience/oshdb){target=blank} 28 | (OSHDB). 29 | 30 | With ohsome, you can ... 31 | 32 | - Get **aggregated statistics** on the evolution of OpenStreetMap elements and 33 | specify your own temporal, spatial and/or thematic filters. The data aggregation 34 | endpoint allows you to access functions, e.g., to calculate the area of 35 | buildings or the length of streets at any given timestamp. 36 | 37 | - Retrieve the **geometry** of the historical OpenStreetMap data, e.g., to 38 | visualize the evolution of certain OpenStreetMap elements over time. You can get 39 | the geometries for specific points in time or all changes within a timespan 40 | (full-history). 41 | 42 | ## Getting started 43 | 44 | Upon attaching the ohsome package, a metadata request is sent to the ohsome 45 | API. The package message provides some essential metadata information, such as 46 | the current temporal extent of the underlying OSHDB: 47 | 48 | ```{r library} 49 | library(ohsome) 50 | ``` 51 | 52 | The metadata is stored in `.ohsome_metadata`. You can print it to the console 53 | to get more details. 54 | 55 | You can create any ohsome API query using the generic `ohsome_query()` function. 56 | It takes the endpoint path and any query parameters as inputs. For information 57 | on all available endpoints with their parameters, consult the 58 | [ohsome API documentation](https://docs.ohsome.org/ohsome-api/stable/endpoints.html){target=blank} 59 | or print `ohsome_endpoints` to the console. 60 | 61 | However, this ohsome R package provides specific wrapper functions for queries 62 | to all available endpoints. 63 | 64 | ### OSM elements 65 | 66 | #### Aggregating OSM elements 67 | 68 | The 69 | [elements aggregation endpoints](https://docs.ohsome.org/ohsome-api/stable/endpoints.html#elements-aggregation){target=blank} 70 | of the ohsome API allow querying for the aggregated amount, length, area or 71 | perimeter of OpenStreetMap elements with given properties, within given 72 | boundaries and at given points in time. 73 | 74 | Let us create a query for the total amount of breweries on OSM in the region of 75 | Franconia. The first argument to `ohsome_elements_count()` is the `sf` object 76 | `franconia` that is included in the 77 | [mapview](https://r-spatial.github.io/mapview/){target=blank} 78 | package and contains boundary 79 | polygons of the `r nrow(mapview::franconia)` districts of the region: 80 | 81 | ```{r fix, include = FALSE} 82 | # avoid messages when handling franconia with old-style crs object 83 | franconia <- sf::st_set_crs(mapview::franconia, 4326) 84 | ``` 85 | 86 | ```{r elements_count} 87 | library(mapview) 88 | 89 | q <- ohsome_elements_count(franconia, filter = "craft=brewery") 90 | ``` 91 | 92 | The resulting `ohsome_query` object can be sent to the ohsome API with 93 | `ohsome_post()`. By default, `ohsome_post()` returns the parsed API 94 | response. In this case, this is a simple `data.frame` of only one row. 95 | 96 | ```{r post} 97 | ohsome_post(q, strict = FALSE) 98 | ``` 99 | 100 | As you can see, `ohsome_post()` issues a warning that the time parameter of the 101 | query is not defined. The `ohsome` API returns the number of elements at the 102 | latest available timestamp by default. ^[When the `strict` 103 | argument is set to TRUE (default), `ohsome_post` throws an error on a missing 104 | `time` parameter and does not send the request to the API at all.] 105 | 106 | Defining the `time` parameter unlocks the full power of ohsome API by giving 107 | access to the OSM history. The `time` parameter requires one or more 108 | [ISO-8601 conform timestring(s)](https://docs.ohsome.org/ohsome-api/stable/time.html){target=blank}. 109 | Here is how to create a query for the number of breweries at the first of each 110 | month between 2010 and 2020: 111 | 112 | ```{r time, eval = FALSE} 113 | ohsome_elements_count(franconia, filter = "craft=brewery", time = "2010/2020/P1M") 114 | ``` 115 | 116 | Alternatively, we can update the existing `ohsome_query` object `q` with the 117 | `set_time()` function, 118 | pipe ^[Instead of the new R native pipe `|>` you may choose to use `magrittr`'s `%>%`.] 119 | the modified query directly into `ohsome_post()` 120 | and make a quick visualization with `ggplot2`: 121 | 122 | ```{r pipe, dev = "svg"} 123 | library(ggplot2) 124 | 125 | q |> 126 | set_time("2010/2020/P1M") |> 127 | ohsome_post() |> 128 | ggplot(aes(x = timestamp, y = value)) + 129 | geom_line() 130 | ``` 131 | 132 | This is how to query the total number of breweries in all of Franconia. But 133 | what if we want to aggregate the amount per district? The ohsome API provides 134 | specific endpoints for different grouped calculations, such as aggregation 135 | grouped by bounding geometry. 136 | 137 | There are several ways to define a query for an aggregation grouped by boundary: 138 | 139 | The `set_endpoint`function is used to change or append the endpoint path of an 140 | API request. In this case, we could append `groupBy/boundary` to the existing 141 | query to the `elements/count` endpoint. The endpoint path can either be given 142 | as a single string (`/groupBy/boundary`) or as a character vector: 143 | `set_endpoint(q, c("groupBy", "boundary"), append = TRUE)` 144 | ^[The order of the elements in the character vector is critical!]. 145 | 146 | More comfortable, however, is the use of either the grouping argument with an 147 | elements aggregation function (e.g. 148 | `ohsome_elements_count(grouping = "boundary)`) or of the `set_grouping()` 149 | function to modify an existing query object: 150 | 151 | ```{r groupBy_boundary, message = FALSE} 152 | library(dplyr) 153 | 154 | franconia |> 155 | mutate(id = NAME_ASCI) |> 156 | ohsome_elements_count(filter = "craft=brewery", time = "2021-06-01") |> 157 | set_grouping("boundary") |> 158 | ohsome_post() 159 | ``` 160 | 161 | If you want your own identifiers for the geometries returned by ohsome, your 162 | input `sf` object needs a column explicitly named `id`. You can use `mutate()` 163 | or `rename()` from the 164 | [dplyr](https://dplyr.tidyverse.org){target=blank} 165 | package to create such a column as in the example 166 | below. 167 | 168 | By default, `ohsome_post()` returns an `sf` object whenever the ohsome API 169 | is capable of delivering GeoJSON data. This is the case for elements 170 | extraction queries as well as for aggregations grouped by boundaries. 171 | 172 | Thus, you can easily create a choropleth map from the query results. 173 | In addition, you can set the argument `return_value` to `density`. This will 174 | modify the endpoint path of the query so that ohsome returns the number of 175 | breweries per area instead of the absolute value: 176 | 177 | ```{r density} 178 | franconia |> 179 | mutate(id = NAME_ASCI) |> 180 | ohsome_elements_count(filter = "craft=brewery", return_value = "density") |> 181 | set_time("2021-06-01") |> 182 | set_grouping("boundary") |> 183 | ohsome_post() |> 184 | mapview(zcol = "value", layer.name = "Breweries per sqkm") 185 | ``` 186 | 187 | #### Extracting OSM elements 188 | 189 | The 190 | [elements extraction endpoints](https://docs.ohsome.org/ohsome-api/stable/endpoints.html#elements-extraction){target=blank} 191 | of the ohsome API allow obtaining geometries, bounding boxes or centroids of OSM 192 | elements with given properties, within given boundaries and at given points in 193 | time. Together with the elements, you can choose to query for their tags and/or 194 | their metadata such as the changeset ID, the time of the last edit or the 195 | version number. 196 | 197 | The following query extracts the geometries of buildings within 500 m of 198 | Heidelberg main station with their tags. The response is used to visualize 199 | the buildings and the values of their `building:levels` tag (if available): 200 | 201 | ```{r building_levels, warning = FALSE} 202 | hd_station_500m <- ohsome_boundary("8.67542,49.40347,500") 203 | 204 | ohsome_elements_geometry( 205 | boundary = hd_station_500m, 206 | filter = "building=* and type:way", 207 | time = "2021-12-01", 208 | properties = "tags", 209 | clipGeometry = FALSE 210 | ) |> 211 | ohsome_post() |> 212 | transmute(level = factor(`building:levels`)) |> 213 | mapview(zcol = "level", lwd = 0, layer.name = "Building level") 214 | ``` 215 | 216 | Similarly, you can use `ohsome_elements_centroid()` to extract centroids of OSM 217 | elements and `ohsome_elements_bbox()` for their bounding boxes. Note that OSM 218 | node elements (with point geometries) are omitted from the results if querying 219 | for bounding boxes. 220 | 221 | 222 | #### Extracting the full history of OSM elements 223 | 224 | While the elements extraction endpoints provide geometries and properties of OSM 225 | elements at specific timestamps, results of queries to the 226 | [full history endpoints](https://docs.ohsome.org/ohsome-api/v1/endpoints.html#elements-full-history-extraction){target=blank} 227 | will include all changes to matching OSM features with corresponding 228 | `validFrom` and `validTo` timestamps. 229 | 230 | Here, we request the full history of OSM buildings within 500 m of Heidelberg 231 | main station, filter for features that still exist and visualize all building 232 | features with their year of creation: 233 | 234 | ```{r buildings} 235 | hd_station_1km <- ohsome_boundary("8.67542,49.40347,1000") 236 | 237 | ohsome_elements_geometry( 238 | boundary = hd_station_1km, 239 | filter = "building=* and type:way", 240 | time = "2021-12-01", 241 | properties = "tags", 242 | clipGeometry = FALSE 243 | ) |> 244 | ohsome_post() |> 245 | transmute(level = factor(`building:levels`)) |> 246 | mapview(zcol = "level", lwd = 0, layer.name = "Building level") 247 | ``` 248 | 249 | You may find using `clean_names()` from the 250 | [janitor](https://github.com/sfirke/janitor){target=blank} 251 | package helpful in order to remove special characters from column names in the 252 | parsed ohsome API response -- just as in the example above. 253 | 254 | ### OSM contributions 255 | 256 | #### Aggregating OSM contributions 257 | 258 | With queries to the ohsome API's 259 | [contributions aggregation endpoints](https://docs.ohsome.org/ohsome-api/v1/endpoints.html#contributions-aggregation){target=blank}, 260 | you can get counts of the contributions provided by users to OSM. The following 261 | code requests the number of deletions of man-made objects at the location 262 | of the hypothetical 263 | [Null Island](https://en.wikipedia.org/wiki/Null_Island){target=blank} 264 | per year between 2010 and 2020: 265 | 266 | ```{r contribution_count} 267 | ohsome_contributions_count( 268 | boundary = "0,0,10", 269 | filter = "man_made=*", 270 | time = "2010/2020/P1Y", 271 | contributionType = "deletion" 272 | ) |> 273 | ohsome_post() 274 | ``` 275 | 276 | The `contributionType` parameter is used to filter for specific types of 277 | contributions (in this case: deletions). If it is not set, any contribution is 278 | counted. Note that the resulting values apply to time intervals defined by a 279 | `fromTimestamp` and a `toTimestamp`. 280 | 281 | #### Extracting OSM contributions 282 | 283 | The 284 | [contributions extraction](https://docs.ohsome.org/ohsome-api/v1/endpoints.html#contributions-extraction){target=blank} 285 | endpoints of the ohsome API can be used to extract feature geometries of 286 | contributions. 287 | 288 | In the following example, we extract the centroids of all amenities in the Berlin 289 | city district of Neukölln that have had contributions in March 2020. 290 | Consequently, we filter for features that have had tags changed and visualize 291 | their locations: 292 | 293 | ```{r contribution_extraction} 294 | nominatimlite::geo_lite_sf("Berlin Neukoelln", points_only = FALSE) |> 295 | ohsome_contributions_centroid() |> 296 | set_filter("amenity=*") |> 297 | set_time("2020-03,2020-04") |> 298 | set_properties("contributionTypes") |> 299 | ohsome_post() |> 300 | filter(`@tagChange`) |> 301 | mapview(layer.name = "Amenities with Tag Changes") 302 | ``` 303 | 304 | ### OSM users 305 | 306 | You can get statistics on the number of users editing specific features through 307 | the 308 | [users aggregation](https://docs.ohsome.org/ohsome-api/v1/endpoints.html#users-aggregation){target=blank} 309 | endpoints of the ohsome API. 310 | 311 | Here, we show the number of users editing buildings before, during and after 312 | the Nepal earthquake 2015: 313 | 314 | ```{r nepal} 315 | ohsome_users_count( 316 | boundary = "82.3055,6.7576,87.4663,28.7025", 317 | filter = "building=* and geometry:polygon", 318 | time = "2015-03-01/2015-08-01/P1M" 319 | ) |> 320 | ohsome_post() 321 | ``` 322 | 323 | ### Bounding geometries 324 | 325 | The ohsome API requires bounding geometries either as bounding polygons 326 | (`bpolys`), bounding boxes (`bboxes`) or bounding circles (`bcircles`) 327 | parameters to the query in a textual form (see 328 | [ohsome API documentation](https://docs.ohsome.org/ohsome-api/stable/boundaries.html){target=blank}). 329 | The ohsome R package uses the generic function `ohsome_boundary()` under the 330 | hood to make your life easier. It accepts a wider range of input geometry 331 | formats, while guessing the right type of bounding geometry. 332 | 333 | As seen above, `sf` objects can be passed into the `boundary` argument of 334 | `ohsome_query()` and any of its wrapper functions. You can also update queries 335 | with `set_boundary()`. The `sf` object will be converted into GeoJSON and passed 336 | into the `bpolys` parameter of the query. 337 | 338 | If you wish to aggregate or extract OSM elements on administrative boundaries in 339 | the `sf` format, you might want to check out packages such as 340 | [rnaturalearth](https://github.com/ropensci/rnaturalearth){target=blank}, 341 | [geodata](https://github.com/rspatial/geodata){target=blank}, 342 | [raster](https://github.com/rspatial/raster){target=blank} 343 | (in particular its `getData()` function), 344 | [rgeoboundaries](https://gitlab.com/dickoa/rgeoboundaries){target=blank} or 345 | [nominatimlite](https://github.com/dieghernan/nominatimlite){target=blank} 346 | for the acquisition of boundary data that can be used with 347 | `ohsome_boundary()`. 348 | 349 | There are also the following methods of `ohsome_boundary()` for other classes 350 | of input geometry objects: 351 | 352 | 1. `bbox` objects created with `st_bbox` are converted into a textual `bboxes` 353 | parameter to the query: 354 | 355 | ```{r bbox} 356 | q <- ohsome_query("users/count") |> 357 | set_boundary(sf::st_bbox(franconia)) 358 | 359 | q$body$bboxes 360 | ``` 361 | 362 | 2. `matrix` objects created with `sp::bbox()`, `raster::bbox()` or 363 | `terra::bbox()` are also converted into a textual `bboxes` parameter. This even 364 | applies for matrices created with `osmdata::getbb()` and `tmaptools::bb()`, so 365 | that you can comfortably acquire bounding boxes from the Nominatim API: 366 | 367 | ```{r getbb} 368 | osmdata::getbb("Kigali") |> 369 | ohsome_elements_length(time = "2018/2018-12/P1M", filter = "route=bus") |> 370 | ohsome_post() 371 | ``` 372 | 373 | 3. You can pass any `character` object with text in the 374 | [format allowed by the ohsome API](https://docs.ohsome.org/ohsome-api/stable/boundaries.html){target=blank} 375 | to `ohsome_boundary()` -- even GeoJSON FeatureCollections. It will automatically 376 | detect whether you have passed the definition of `bpolys`, `bboxes` or 377 | `bcircles`. It is possible to use `character` vectors where each element 378 | represents one geometry: 379 | 380 | ```{r circles} 381 | c("Circle 1:8.6528,49.3683,1000", "Circle 2:8.7294,49.4376,1000") |> 382 | ohsome_elements_count(filter = "amenity=*", grouping = "boundary", time = 2021) |> 383 | ohsome_post() 384 | ``` 385 | 386 | While `sf` and `bbox` objects will be automatically 387 | transformed to WGS 84 if in a different coordinate reference system, coordinates 388 | in `character` and `matrix` objects always need to be provided as WGS 84. 389 | 390 | ### Modifying queries 391 | 392 | As seen above, existing `ohsome_query` objects can be modified by 393 | `set_endpoint()`, `set_grouping()`, `set_boundary()` or `set_time()`. The latter 394 | and other functions such as `set_filter()` are just wrappers around the more 395 | generic `set_parameters()`. This can be used to modify the parameters of a query 396 | in any possible way: 397 | 398 | ```{r set_parameters} 399 | q <- ohsome_elements_count("8.5992,49.3567,8.7499,49.4371") 400 | 401 | q |> 402 | set_endpoint("ratio", append = TRUE) |> 403 | set_parameters( 404 | filter = "building=*", 405 | filter2 = "building=* and building:levels=*", 406 | time = "2010/2020/P2Y" 407 | ) |> 408 | ohsome_post() 409 | 410 | ``` 411 | 412 | ### Grouping 413 | 414 | [Grouping endpoints](https://docs.ohsome.org/ohsome-api/v1/group-by.html){target=blank} 415 | are available for aggregation resources and can be used to compute the 416 | aggregated results grouped by: 417 | 418 | - boundary, 419 | - key, 420 | - tag, and 421 | - type. 422 | 423 | In many cases, a grouping by `boundary` can be combined with a grouping by `tag`. 424 | Some of the grouping endpoints require additional query parameters, e.g. `tag` 425 | groupings require a `groupByKey` parameter. Not all grouping endpoints are 426 | available for all aggregation resources -- please consult the 427 | [ohsome API documentation](https://docs.ohsome.org/ohsome-api/v1/group-by.html){target=blank} 428 | for details. 429 | 430 | You can set the `grouping` argument to any aggregation endpoint wrapper function 431 | (e.g. `ohsome_elements_count(grouping = c("boundary", "tag"))`) or use 432 | `set_grouping()` to modify existing query objects. 433 | 434 | ### Density and ratio requests 435 | 436 | Many 437 | [aggregation resources](https://docs.ohsome.org/ohsome-api/v1/endpoints.html){target=blank} 438 | have endpoints for requesting density (i.e. count, length, perimeter or area of 439 | features **per area**) or ratios (of OSM elements satisfying a `filter2` to elements 440 | satisfying a `filter`) instead of or in addition to absolute values. 441 | 442 | You can request density or ratio values by setting the `return_value` argument 443 | to aggregation endpoint wrapper functions (e.g. 444 | `ohsome_elements_count(return_value = "density")`). Mind that ratio endpoints 445 | require an additional `filter2` query parameter. Please consult the 446 | [ohsome API documentation](https://docs.ohsome.org/ohsome-api/v1/endpoints.html){target=blank} 447 | or print `names(ohsome_endpoints)` to the console in order to check for the 448 | availability of specific density and ratio endpoints. 449 | 450 | ### Dealing with complex API responses 451 | 452 | The ohsome API allows grouping aggregate values for various timestamps by 453 | boundary and tag at the same time. The parsed content of the response can be 454 | rather complex. In the following case, building feature counts for the districts 455 | of Franconia at two different timestamps are requested -- additionally grouped 456 | by the building:levels tag. To avoid lots of redundant geometries, 457 | comma-separated values (instead of GeoJSON) are explicitly requested as the 458 | response format: 459 | 460 | ```{r groupby_boundary_groupby_tag, message = FALSE} 461 | building_levels <- franconia |> 462 | mutate(id = NUTS_ID) |> 463 | ohsome_elements_count(grouping = c("boundary", "tag"), format = "csv") |> 464 | set_filter("building=* and geometry:polygon") |> 465 | set_time("2015/2020") |> 466 | set_groupByKey("building:levels") |> 467 | ohsome_post() 468 | 469 | dim(building_levels) 470 | ``` 471 | 472 | The query results in a confusing data.frame. This happens because there is a 473 | building count column for each combination of boundary polygon and number of 474 | levels, while the two requested timestamps are in the rows. Fortunately, there 475 | is the 476 | [tidyr](https://tidyr.tidyverse.org){target=blank} 477 | package to do its magic and pivot this table into a long format 478 | with one value per row: 479 | 480 | ```{r tidy} 481 | library(tidyr) 482 | 483 | building_levels |> 484 | pivot_longer(-timestamp, names_to = c("id", "levels"), names_sep = "_") 485 | ``` 486 | 487 | ## How to cite this package 488 | 489 | In order to cite this package in publications, please use the citation 490 | information provided through `citation("ohsome")`. 491 | 492 | ----- 493 | 494 | 495 | --------------------------------------------------------------------------------