mirror of
https://github.com/sensebox/opensensmapr
synced 2025-02-21 13:23:57 +01:00
Merge pull request #17 from nuest/master
Add tests and CI integrations, add CoC
This commit is contained in:
commit
12f37006bd
27 changed files with 535 additions and 51 deletions
|
@ -2,3 +2,7 @@
|
|||
^\.Rproj\.user$
|
||||
^CHANGES\.md$
|
||||
^tools*$
|
||||
^\.travis\.yml$
|
||||
^appveyor\.yml$
|
||||
^CONDUCT\.md$
|
||||
^codecov\.yml$
|
||||
|
|
24
.travis.yml
Normal file
24
.travis.yml
Normal file
|
@ -0,0 +1,24 @@
|
|||
# R for travis: see documentation at https://docs.travis-ci.com/user/languages/r
|
||||
|
||||
language: R
|
||||
sudo: false
|
||||
cache: packages
|
||||
|
||||
r:
|
||||
- release
|
||||
- devel
|
||||
|
||||
r_github_packages:
|
||||
- r-lib/covr
|
||||
|
||||
before_install:
|
||||
- sudo add-apt-repository ppa:ubuntugis/ubuntugis-unstable --yes
|
||||
- sudo apt-get --yes --force-yes update -qq
|
||||
# units/udunits2 dependency:
|
||||
- sudo apt-get install --yes libudunits2-dev
|
||||
# sf dependencies:
|
||||
- sudo apt-get install --yes libproj-dev libgeos-dev libgdal-dev
|
||||
|
||||
after_success:
|
||||
- Rscript -e 'covr::codecov()'
|
||||
#- Rscript -e 'lintr::lint_package()'
|
25
CONDUCT.md
Normal file
25
CONDUCT.md
Normal file
|
@ -0,0 +1,25 @@
|
|||
# Contributor Code of Conduct
|
||||
|
||||
As contributors and maintainers of this project, we pledge to respect all people who
|
||||
contribute through reporting issues, posting feature requests, updating documentation,
|
||||
submitting pull requests or patches, and other activities.
|
||||
|
||||
We are committed to making participation in this project a harassment-free experience for
|
||||
everyone, regardless of level of experience, gender, gender identity and expression,
|
||||
sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
|
||||
|
||||
Examples of unacceptable behavior by participants include the use of sexual language or
|
||||
imagery, derogatory comments or personal attacks, trolling, public or private harassment,
|
||||
insults, or other unprofessional conduct.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments,
|
||||
commits, code, wiki edits, issues, and other contributions that are not aligned to this
|
||||
Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed
|
||||
from the project team.
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by
|
||||
opening an issue or contacting one or more of the project maintainers.
|
||||
|
||||
This Code of Conduct is adapted from the Contributor Covenant
|
||||
(http:contributor-covenant.org), version 1.0.0, available at
|
||||
http://contributor-covenant.org/version/1/0/0/
|
10
DESCRIPTION
10
DESCRIPTION
|
@ -1,7 +1,7 @@
|
|||
Package: opensensmapr
|
||||
Type: Package
|
||||
Title: Work with Sensor Data from opensensemap.org in R
|
||||
Version: 0.3.2
|
||||
Version: 0.3.3
|
||||
URL: http://github.com/noerw/opensensmapR
|
||||
BugReports: http://github.com/noerw/opensensmapR/issues
|
||||
Imports:
|
||||
|
@ -17,9 +17,11 @@ Suggests:
|
|||
knitr,
|
||||
rmarkdown,
|
||||
lubridate,
|
||||
units
|
||||
Author: Norwin Roosen
|
||||
Maintainer: Norwin Roosen <bugs@nroo.de>
|
||||
units,
|
||||
testthat,
|
||||
covr
|
||||
Authors@R: c(person("Norwin", "Roosen", role = c("aut", "cre"), email = "bugs@nroo.de"),
|
||||
person("Daniel", "Nuest", role = c("ctb"), email = "daniel.nuest@uni-muenster.de", comment = c(ORCID = "0000-0003-2392-6140")))
|
||||
Description: Download data (environmental measurements, sensorstations) from the
|
||||
API of open data sensor web platform 'opensensemap.org' for analysis in R.
|
||||
This platform provides realtime data of more than 1000 low-cost sensor
|
||||
|
|
|
@ -20,6 +20,7 @@ export(osem_as_sensebox)
|
|||
export(osem_box)
|
||||
export(osem_boxes)
|
||||
export(osem_counts)
|
||||
export(osem_endpoint)
|
||||
export(osem_measurements)
|
||||
export(osem_phenomena)
|
||||
export(st_as_sf.osem_measurements)
|
||||
|
|
|
@ -29,7 +29,7 @@ dplyr_class_wrapper = function(callback) {
|
|||
#' the callstack. See https://stackoverflow.com/a/33108841
|
||||
#'
|
||||
#' @noRd
|
||||
isNonInteractive = function () {
|
||||
is_non_interactive = function () {
|
||||
ff <- sapply(sys.calls(), function(f) as.character(f[1]))
|
||||
any(ff %in% c("knit2html", "render")) || !interactive()
|
||||
}
|
||||
|
|
16
R/api.R
16
R/api.R
|
@ -4,12 +4,19 @@
|
|||
# for CSV responses (get_measurements) the readr package is a hidden dependency
|
||||
# ==============================================================================
|
||||
|
||||
#' Get the default openSenseMap API endpoint
|
||||
#' @export
|
||||
#' @return A character string with the HTTP URL of the openSenseMap API
|
||||
osem_endpoint = function() {
|
||||
'https://api.opensensemap.org'
|
||||
}
|
||||
|
||||
get_boxes_ = function (..., endpoint) {
|
||||
response = osem_request_(endpoint, path = c('boxes'), ...)
|
||||
|
||||
if (length(response) == 0) {
|
||||
warning('no boxes found for this query')
|
||||
return(response)
|
||||
warning('no senseBoxes found for this query')
|
||||
return(osem_as_sensebox(as.data.frame(response)))
|
||||
}
|
||||
|
||||
# parse each list element as sensebox & combine them to a single data.frame
|
||||
|
@ -17,7 +24,8 @@ get_boxes_ = function (..., endpoint) {
|
|||
df = dplyr::bind_rows(boxesList)
|
||||
df$exposure = df$exposure %>% as.factor()
|
||||
df$model = df$model %>% as.factor()
|
||||
df$grouptag = df$grouptag %>% as.factor()
|
||||
if(!is.null(df$grouptag))
|
||||
df$grouptag = df$grouptag %>% as.factor()
|
||||
df
|
||||
}
|
||||
|
||||
|
@ -52,7 +60,7 @@ get_stats_ = function (endpoint) {
|
|||
}
|
||||
|
||||
osem_request_ = function (host, path, ..., type = 'parsed', progress) {
|
||||
progress = if (progress && !isNonInteractive()) httr::progress() else NULL
|
||||
progress = if (progress && !is_non_interactive()) httr::progress() else NULL
|
||||
res = httr::GET(host, progress, path = path, query = list(...))
|
||||
|
||||
if (httr::http_error(res)) {
|
||||
|
|
8
R/box.R
8
R/box.R
|
@ -19,7 +19,7 @@
|
|||
#' @param phenomenon Only return boxes that measured the given phenomenon in the
|
||||
#' time interval as specified through \code{date} or \code{from / to}
|
||||
#' @param endpoint The URL of the openSenseMap API instance
|
||||
#' @param progress Whether to print download progress information
|
||||
#' @param progress Whether to print download progress information defaults to \code{TRUE}
|
||||
#' @return A \code{sensebox data.frame} containing a box in each row
|
||||
#'
|
||||
#' @seealso \href{https://docs.opensensemap.org/#api-Measurements-findAllBoxes}{openSenseMap API documentation (web)}
|
||||
|
@ -38,8 +38,8 @@
|
|||
#'
|
||||
osem_boxes = function (exposure = NA, model = NA, grouptag = NA,
|
||||
date = NA, from = NA, to = NA, phenomenon = NA,
|
||||
endpoint = 'https://api.opensensemap.org',
|
||||
progress = T) {
|
||||
endpoint = osem_endpoint(),
|
||||
progress = TRUE) {
|
||||
|
||||
# error, if phenomenon, but no time given
|
||||
if (!is.na(phenomenon) && is.na(date) && is.na(to) && is.na(from))
|
||||
|
@ -87,7 +87,7 @@ osem_boxes = function (exposure = NA, model = NA, grouptag = NA,
|
|||
#' # get a specific box by ID
|
||||
#' b = osem_box('57000b8745fd40c8196ad04c')
|
||||
#'
|
||||
osem_box = function (boxId, endpoint = 'https://api.opensensemap.org') {
|
||||
osem_box = function (boxId, endpoint = osem_endpoint()) {
|
||||
get_box_(boxId, endpoint = endpoint)
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,8 @@ plot.sensebox = function (x, ..., mar = c(2,2,1,1)) {
|
|||
print.sensebox = function(x, ...) {
|
||||
important_columns = c('name', 'exposure', 'lastMeasurement', 'phenomena')
|
||||
data = as.data.frame(x)
|
||||
print(data[important_columns], ...)
|
||||
available_columns = important_columns %in% names(data)
|
||||
print(data[available_columns], ...)
|
||||
|
||||
invisible(x)
|
||||
}
|
||||
|
@ -106,7 +107,7 @@ mutate.sensebox = dplyr_class_wrapper(osem_as_sensebox)
|
|||
#' @export
|
||||
`[.sensebox` = function(x, i, ...) {
|
||||
s = NextMethod('[')
|
||||
mostattributes(s) = attributes(x)
|
||||
mostattributes(s) = attributes(s)
|
||||
s
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,6 @@
|
|||
#'
|
||||
#' @export
|
||||
#' @seealso \href{https://docs.opensensemap.org/#api-Misc-getStatistics}{openSenseMap API documentation (web)}
|
||||
osem_counts = function (endpoint = 'https://api.opensensemap.org') {
|
||||
osem_counts = function(endpoint = osem_endpoint()) {
|
||||
get_stats_(endpoint)
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#' @param exposure Filter sensors by their exposure ('indoor', 'outdoor', 'mobile')
|
||||
#' @param from A \code{POSIXt} like object to select a time interval
|
||||
#' @param to A \code{POSIXt} like object to select a time interval
|
||||
#' @param columns Select specific column in the output (see oSeM documentation)
|
||||
#' @param columns Select specific column in the output (see openSenseMap API documentation)
|
||||
#' @param endpoint The URL of the openSenseMap API
|
||||
#' @param progress Whether to print download progress information
|
||||
#'
|
||||
|
@ -36,7 +36,7 @@ osem_measurements = function (x, ...) UseMethod('osem_measurements')
|
|||
#' @export
|
||||
#' @examples
|
||||
#' # get measurements from all boxes
|
||||
#' osem_measurements('Windrichtung')
|
||||
#' m1 = osem_measurements('Windrichtung')
|
||||
#'
|
||||
osem_measurements.default = function (x, ...) {
|
||||
bbox = structure(c(-180, -90, 180, 90), class = 'bbox')
|
||||
|
@ -50,16 +50,16 @@ osem_measurements.default = function (x, ...) {
|
|||
#' @examples
|
||||
#' # get measurements from sensors within a bounding box
|
||||
#' bbox = structure(c(7, 51, 8, 52), class = 'bbox')
|
||||
#' osem_measurements(bbox, 'Temperatur')
|
||||
#' m2 = osem_measurements(bbox, 'Temperatur')
|
||||
#'
|
||||
#' points = sf::st_multipoint(matrix(c(7.5, 7.8, 51.7, 52), 2, 2))
|
||||
#' bbox2 = sf::st_bbox(points)
|
||||
#' osem_measurements(bbox2, 'Temperatur', exposure = 'outdoor')
|
||||
#' m3 = osem_measurements(bbox2, 'Temperatur', exposure = 'outdoor')
|
||||
#'
|
||||
osem_measurements.bbox = function (x, phenomenon, exposure = NA,
|
||||
from = NA, to = NA, columns = NA,
|
||||
...,
|
||||
endpoint = 'https://api.opensensemap.org',
|
||||
endpoint = osem_endpoint(),
|
||||
progress = T) {
|
||||
bbox = x
|
||||
environment() %>%
|
||||
|
@ -79,12 +79,12 @@ osem_measurements.bbox = function (x, phenomenon, exposure = NA,
|
|||
#'
|
||||
#' # ...or a single box
|
||||
#' b = osem_box('57000b8745fd40c8196ad04c')
|
||||
#' osem_measurements(b, phenomenon = 'Temperatur')
|
||||
#' m4 = osem_measurements(b, phenomenon = 'Temperatur')
|
||||
#'
|
||||
osem_measurements.sensebox = function (x, phenomenon, exposure = NA,
|
||||
from = NA, to = NA, columns = NA,
|
||||
...,
|
||||
endpoint = 'https://api.opensensemap.org',
|
||||
endpoint = osem_endpoint(),
|
||||
progress = T) {
|
||||
boxes = x
|
||||
environment() %>%
|
||||
|
@ -106,8 +106,10 @@ parse_get_measurements_params = function (params) {
|
|||
if (is.null(params$phenomenon) | is.na(params$phenomenon))
|
||||
stop('Parameter "phenomenon" is required')
|
||||
|
||||
if (!is.na(params$from) && is.na(params$to))
|
||||
stop('specify "from" only together with "to"')
|
||||
if (
|
||||
(!is.na(params$from) && is.na(params$to)) ||
|
||||
(!is.na(params$to) && is.na(params$from))
|
||||
) stop('specify "from" only together with "to"')
|
||||
|
||||
if (
|
||||
(!is.null(params$bbox) && !is.null(params$boxes)) ||
|
||||
|
@ -130,7 +132,7 @@ parse_get_measurements_params = function (params) {
|
|||
}
|
||||
|
||||
if (!is.na(params$exposure)) query$exposure = params$exposure
|
||||
if (!is.na(params$columns))
|
||||
if (!any(is.na(params$columns)))
|
||||
query$columns = paste(params$columns, collapse = ',')
|
||||
else
|
||||
query$columns = 'value,createdAt,lon,lat,sensorId,unit'
|
||||
|
@ -160,7 +162,7 @@ paged_measurements_req = function (query) {
|
|||
query$`to-date` = date_as_isostring(page$to)
|
||||
res = do.call(get_measurements_, query)
|
||||
|
||||
if (query$progress && !isNonInteractive())
|
||||
if (query$progress && !is_non_interactive())
|
||||
cat(paste(query$`from-date`, query$`to-date`, sep = ' - '), '\n')
|
||||
|
||||
res
|
||||
|
|
|
@ -43,7 +43,7 @@ mutate.osem_measurements = dplyr_class_wrapper(osem_as_measurements)
|
|||
#' @export
|
||||
`[.osem_measurements` = function(x, i, ...) {
|
||||
s = NextMethod('[')
|
||||
mostattributes(s) = attributes(x)
|
||||
mostattributes(s) = attributes(s)
|
||||
s
|
||||
}
|
||||
|
||||
|
|
28
README.md
28
README.md
|
@ -1,9 +1,12 @@
|
|||
# opensensmapr
|
||||
This R package ingests data (environmental measurements, sensor stations) from
|
||||
the API of opensensemap.org for analysis in R.
|
||||
|
||||
[](https://cran.r-project.org/package=opensensmapr) [](https://travis-ci.org/noerw/opensensmapR) [](https://ci.appveyor.com/project/noerw/opensensmapR) [](https://codecov.io/github/noerw/opensensmapR?branch=master)
|
||||
|
||||
This R package ingests data (environmental measurements, sensor stations) from the API of opensensemap.org for analysis in R.
|
||||
The package aims to be compatible with sf and the tidyverse.
|
||||
|
||||
## Installation
|
||||
|
||||
Right now, the package is not on CRAN. To install it from GitHub, run:
|
||||
|
||||
```r
|
||||
|
@ -12,9 +15,10 @@ devtools::install_github('noerw/opensensmapr')
|
|||
```
|
||||
|
||||
## Usage
|
||||
|
||||
A verbose usage example is shown in the vignette [`osem-intro`](https://noerw.github.com/opensensmapR/inst/doc/osem-intro.html).
|
||||
Each functions documentation can be viewed with `?<function-name>`. An overview
|
||||
is given in `?opensensmapr`.
|
||||
Each functions documentation can be viewed with `?<function-name>`.
|
||||
An overview is given in `?opensensmapr`.
|
||||
In short, the following pseudocode shows the main functions for data retrieval:
|
||||
|
||||
```r
|
||||
|
@ -39,17 +43,23 @@ osem_counts()
|
|||
Additionally there are some helpers: `summary.sensebox(), plot.sensebox(), st_as_sf.sensebox(), osem_as_sensebox(), [.sensebox(), filter.sensebox(), mutate.sensebox(), ...`.
|
||||
|
||||
## Changelog
|
||||
This project adheres to semantic versioning, for changes in recent versions
|
||||
please consult [CHANGES.md](CHANGES.md).
|
||||
|
||||
This project adheres to semantic versioning, for changes in recent versions please consult [CHANGES.md](CHANGES.md).
|
||||
|
||||
## FAQ
|
||||
- *Whats up with that package name?* idk, the R people seem to [enjoy][1]
|
||||
[dropping][2] [vovels][3] so.. Unfortunately I couldn't fit the naming
|
||||
convention to drop an `y` in there.
|
||||
|
||||
- *Whats up with that package name?* idk, the R people seem to [enjoy][1] [dropping][2] [vovels][3] so..
|
||||
Unfortunately I couldn't fit the naming convention to drop an `y` in there.
|
||||
|
||||
[1]: https://github.com/tidyverse/readr
|
||||
[2]: https://github.com/tidyverse/dplyr
|
||||
[3]: https://github.com/tidyverse/tidyr
|
||||
|
||||
## Development
|
||||
|
||||
Please note that this project is released with a [Contributor Code of Conduct](CONDUCT.md).
|
||||
By participating in this project you agree to abide by its terms.
|
||||
|
||||
## License
|
||||
|
||||
GPL-2.0 - Norwin Roosen
|
||||
|
|
45
appveyor.yml
Normal file
45
appveyor.yml
Normal file
|
@ -0,0 +1,45 @@
|
|||
# DO NOT CHANGE the "init" and "install" sections below
|
||||
|
||||
# Download script file from GitHub
|
||||
init:
|
||||
ps: |
|
||||
$ErrorActionPreference = "Stop"
|
||||
Invoke-WebRequest http://raw.github.com/krlmlr/r-appveyor/master/scripts/appveyor-tool.ps1 -OutFile "..\appveyor-tool.ps1"
|
||||
Import-Module '..\appveyor-tool.ps1'
|
||||
|
||||
install:
|
||||
ps: Bootstrap
|
||||
|
||||
cache:
|
||||
- C:\RLibrary
|
||||
|
||||
# Adapt as necessary starting from here
|
||||
|
||||
build_script:
|
||||
- travis-tool.sh install_deps
|
||||
|
||||
test_script:
|
||||
- travis-tool.sh run_tests
|
||||
|
||||
on_failure:
|
||||
- 7z a failure.zip *.Rcheck\*
|
||||
- appveyor PushArtifact failure.zip
|
||||
|
||||
artifacts:
|
||||
- path: '*.Rcheck\**\*.log'
|
||||
name: Logs
|
||||
|
||||
- path: '*.Rcheck\**\*.out'
|
||||
name: Logs
|
||||
|
||||
- path: '*.Rcheck\**\*.fail'
|
||||
name: Logs
|
||||
|
||||
- path: '*.Rcheck\**\*.Rout'
|
||||
name: Logs
|
||||
|
||||
- path: '\*_*.tar.gz'
|
||||
name: Bits
|
||||
|
||||
- path: '\*_*.zip'
|
||||
name: Bits
|
12
codecov.yml
Normal file
12
codecov.yml
Normal file
|
@ -0,0 +1,12 @@
|
|||
comment: false
|
||||
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
target: auto
|
||||
threshold: 1%
|
||||
patch:
|
||||
default:
|
||||
target: auto
|
||||
threshold: 1%
|
|
@ -91,3 +91,12 @@ openSenseMap API: \url{https://api.opensensemap.org/}
|
|||
|
||||
official openSenseMap API documentation: \url{https://docs.opensensemap.org/}
|
||||
}
|
||||
\author{
|
||||
\strong{Maintainer}: Norwin Roosen \email{bugs@nroo.de}
|
||||
|
||||
Other contributors:
|
||||
\itemize{
|
||||
\item Daniel Nuest \email{daniel.nuest@uni-muenster.de} (0000-0003-2392-6140) [contributor]
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
\alias{osem_box}
|
||||
\title{Get a single senseBox by its ID}
|
||||
\usage{
|
||||
osem_box(boxId, endpoint = "https://api.opensensemap.org")
|
||||
osem_box(boxId, endpoint = osem_endpoint())
|
||||
}
|
||||
\arguments{
|
||||
\item{boxId}{A string containing a senseBox ID}
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
\title{Get a set of senseBoxes from the openSenseMap}
|
||||
\usage{
|
||||
osem_boxes(exposure = NA, model = NA, grouptag = NA, date = NA,
|
||||
from = NA, to = NA, phenomenon = NA,
|
||||
endpoint = "https://api.opensensemap.org", progress = T)
|
||||
from = NA, to = NA, phenomenon = NA, endpoint = osem_endpoint(),
|
||||
progress = TRUE)
|
||||
}
|
||||
\arguments{
|
||||
\item{exposure}{Only return boxes with the given exposure ('indoor', 'outdoor', 'mobile')}
|
||||
|
@ -26,7 +26,7 @@ time interval as specified through \code{date} or \code{from / to}}
|
|||
|
||||
\item{endpoint}{The URL of the openSenseMap API instance}
|
||||
|
||||
\item{progress}{Whether to print download progress information}
|
||||
\item{progress}{Whether to print download progress information defaults to \code{TRUE}}
|
||||
}
|
||||
\value{
|
||||
A \code{sensebox data.frame} containing a box in each row
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
\alias{osem_counts}
|
||||
\title{Get count statistics of the openSenseMap Instance}
|
||||
\usage{
|
||||
osem_counts(endpoint = "https://api.opensensemap.org")
|
||||
osem_counts(endpoint = osem_endpoint())
|
||||
}
|
||||
\arguments{
|
||||
\item{endpoint}{The URL of the openSenseMap API}
|
||||
|
|
14
man/osem_endpoint.Rd
Normal file
14
man/osem_endpoint.Rd
Normal file
|
@ -0,0 +1,14 @@
|
|||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/api.R
|
||||
\name{osem_endpoint}
|
||||
\alias{osem_endpoint}
|
||||
\title{Get the default openSenseMap API endpoint}
|
||||
\usage{
|
||||
osem_endpoint()
|
||||
}
|
||||
\value{
|
||||
A character string with the HTTP URL of the openSenseMap API
|
||||
}
|
||||
\description{
|
||||
Get the default openSenseMap API endpoint
|
||||
}
|
|
@ -12,12 +12,11 @@ osem_measurements(x, ...)
|
|||
\method{osem_measurements}{default}(x, ...)
|
||||
|
||||
\method{osem_measurements}{bbox}(x, phenomenon, exposure = NA, from = NA,
|
||||
to = NA, columns = NA, ..., endpoint = "https://api.opensensemap.org",
|
||||
progress = T)
|
||||
to = NA, columns = NA, ..., endpoint = osem_endpoint(), progress = T)
|
||||
|
||||
\method{osem_measurements}{sensebox}(x, phenomenon, exposure = NA,
|
||||
from = NA, to = NA, columns = NA, ...,
|
||||
endpoint = "https://api.opensensemap.org", progress = T)
|
||||
from = NA, to = NA, columns = NA, ..., endpoint = osem_endpoint(),
|
||||
progress = T)
|
||||
}
|
||||
\arguments{
|
||||
\item{x}{Depending on the method, either
|
||||
|
@ -38,7 +37,7 @@ osem_measurements(x, ...)
|
|||
|
||||
\item{to}{A \code{POSIXt} like object to select a time interval}
|
||||
|
||||
\item{columns}{Select specific column in the output (see oSeM documentation)}
|
||||
\item{columns}{Select specific column in the output (see openSenseMap API documentation)}
|
||||
|
||||
\item{endpoint}{The URL of the openSenseMap API}
|
||||
|
||||
|
@ -64,15 +63,15 @@ a bounding box spanning the whole world.
|
|||
|
||||
\examples{
|
||||
# get measurements from all boxes
|
||||
osem_measurements('Windrichtung')
|
||||
m1 = osem_measurements('Windrichtung')
|
||||
|
||||
# get measurements from sensors within a bounding box
|
||||
bbox = structure(c(7, 51, 8, 52), class = 'bbox')
|
||||
osem_measurements(bbox, 'Temperatur')
|
||||
m2 = osem_measurements(bbox, 'Temperatur')
|
||||
|
||||
points = sf::st_multipoint(matrix(c(7.5, 7.8, 51.7, 52), 2, 2))
|
||||
bbox2 = sf::st_bbox(points)
|
||||
osem_measurements(bbox2, 'Temperatur', exposure = 'outdoor')
|
||||
m3 = osem_measurements(bbox2, 'Temperatur', exposure = 'outdoor')
|
||||
|
||||
# get measurements from a set of boxes
|
||||
b = osem_boxes(grouptag = 'ifgi')
|
||||
|
@ -80,7 +79,7 @@ osem_measurements(b, phenomenon = 'Temperatur')
|
|||
|
||||
# ...or a single box
|
||||
b = osem_box('57000b8745fd40c8196ad04c')
|
||||
osem_measurements(b, phenomenon = 'Temperatur')
|
||||
m4 = osem_measurements(b, phenomenon = 'Temperatur')
|
||||
|
||||
}
|
||||
\seealso{
|
||||
|
|
5
tests/testthat.R
Normal file
5
tests/testthat.R
Normal file
|
@ -0,0 +1,5 @@
|
|||
library("testthat")
|
||||
library("opensensmapr")
|
||||
library("sf")
|
||||
|
||||
test_check("opensensmapr")
|
31
tests/testthat/test_box.R
Normal file
31
tests/testthat/test_box.R
Normal file
|
@ -0,0 +1,31 @@
|
|||
context("box")
|
||||
|
||||
check_api <- function() {
|
||||
skip_on_cran()
|
||||
|
||||
code <- NA
|
||||
try(code <- httr::status_code(httr::GET(osem_endpoint())))
|
||||
if (is.na(code)) skip("API not available")
|
||||
}
|
||||
|
||||
try({
|
||||
boxes <- osem_boxes()
|
||||
})
|
||||
|
||||
test_that("a single box can be retrieved by ID", {
|
||||
check_api()
|
||||
|
||||
box <- osem_box(boxes$X_id[[1]])
|
||||
|
||||
expect_true("sensebox" %in% class(box))
|
||||
expect_true("data.frame" %in% class(box))
|
||||
expect_true(nrow(box) == 1)
|
||||
expect_true(box$X_id == boxes$X_id[[1]])
|
||||
})
|
||||
|
||||
|
||||
test_that("[.sensebox maintains attributes", {
|
||||
check_api()
|
||||
|
||||
expect_true(all(attributes(boxes[1:nrow(boxes), ]) %in% attributes(boxes)))
|
||||
})
|
112
tests/testthat/test_boxes.R
Normal file
112
tests/testthat/test_boxes.R
Normal file
|
@ -0,0 +1,112 @@
|
|||
context("boxes")
|
||||
|
||||
check_api <- function() {
|
||||
skip_on_cran()
|
||||
|
||||
code <- NA
|
||||
try(code <- httr::status_code(httr::GET(osem_endpoint())))
|
||||
if (is.na(code)) skip("API not available")
|
||||
}
|
||||
|
||||
test_that("a list of all boxes can be retrieved and returns a sensebox data.frame", {
|
||||
check_api()
|
||||
|
||||
boxes <- osem_boxes()
|
||||
expect_true(is.data.frame(boxes))
|
||||
expect_true(is.factor(boxes$model))
|
||||
expect_true(is.character(boxes$name))
|
||||
expect_length(names(boxes), 14)
|
||||
expect_true(any("sensebox" %in% class(boxes)))
|
||||
})
|
||||
|
||||
test_that("both from and to are required when requesting boxes, error otherwise", {
|
||||
expect_error(osem_boxes(from = as.POSIXct("2017-01-01")), "must be used together")
|
||||
expect_error(osem_boxes(to = as.POSIXct("2017-01-01")), "must be used together")
|
||||
})
|
||||
|
||||
test_that("a list of boxes with exposure filter returns only the requested exposure", {
|
||||
check_api()
|
||||
|
||||
boxes <- osem_boxes(exposure = "mobile")
|
||||
expect_true(all(boxes$exposure == "mobile"))
|
||||
})
|
||||
|
||||
test_that("a list of boxes with model filter returns only the requested model", {
|
||||
check_api()
|
||||
|
||||
boxes <- osem_boxes(model = "homeWifi")
|
||||
expect_true(all(boxes$model == "homeWifi"))
|
||||
})
|
||||
|
||||
test_that("box query can combine exposure and model filter", {
|
||||
check_api()
|
||||
|
||||
boxes <- osem_boxes(exposure = "mobile", model = "homeWifi")
|
||||
expect_true(all(boxes$model == "homeWifi"))
|
||||
expect_true(all(boxes$exposure == "mobile"))
|
||||
})
|
||||
|
||||
test_that("a list of boxes with grouptype returns only boxes of that group", {
|
||||
check_api()
|
||||
|
||||
boxes <- osem_boxes(grouptag = "codeformuenster")
|
||||
expect_true(all(boxes$grouptag == "codeformuenster"))
|
||||
})
|
||||
|
||||
test_that("endpoint can be (mis)configured", {
|
||||
check_api()
|
||||
|
||||
expect_error(osem_boxes(endpoint = "http://not.the.opensensemap.org"), "resolve host")
|
||||
})
|
||||
|
||||
test_that("a response with no matches returns empty sensebox data.frame and a warning", {
|
||||
check_api()
|
||||
|
||||
suppressWarnings(boxes <- osem_boxes(grouptag = "does_not_exist"))
|
||||
expect_true(is.data.frame(boxes))
|
||||
expect_true(any("sensebox" %in% class(boxes)))
|
||||
})
|
||||
|
||||
test_that("a response with no matches gives a warning", {
|
||||
check_api()
|
||||
|
||||
expect_warning(osem_boxes(grouptag = "does_not_exist"), "no senseBoxes found")
|
||||
})
|
||||
|
||||
test_that("data.frame can be converted to sensebox data.frame", {
|
||||
df <- osem_as_sensebox(data.frame(c(1,2), c("a", "b")))
|
||||
expect_equal(class(df), c("sensebox", "data.frame"))
|
||||
})
|
||||
|
||||
test_that("boxes can be converted to sf object", {
|
||||
check_api()
|
||||
|
||||
boxes <- osem_boxes()
|
||||
boxes_sf <- sf::st_as_sf(boxes)
|
||||
|
||||
expect_true(all(sf::st_is_simple(boxes_sf)))
|
||||
expect_true("sf" %in% class(boxes_sf))
|
||||
})
|
||||
|
||||
test_that("boxes converted to sf object keep all attributes", {
|
||||
check_api()
|
||||
|
||||
boxes <- osem_boxes()
|
||||
boxes_sf <- sf::st_as_sf(boxes)
|
||||
|
||||
# coord columns get removed!
|
||||
cols <- names(boxes)[!names(boxes) %in% c('lon', 'lat')]
|
||||
expect_true(all(cols %in% names(boxes_sf)))
|
||||
|
||||
expect_true("sensebox" %in% class(boxes_sf))
|
||||
})
|
||||
|
||||
test_that("box retrieval does not give progress information in non-interactive mode", {
|
||||
check_api()
|
||||
|
||||
if (!opensensmapr:::is_non_interactive()) skip("interactive session")
|
||||
|
||||
out <- capture.output(b <- osem_boxes())
|
||||
expect_length(out, 0)
|
||||
})
|
||||
|
19
tests/testthat/test_counts.R
Normal file
19
tests/testthat/test_counts.R
Normal file
|
@ -0,0 +1,19 @@
|
|||
context("counts")
|
||||
|
||||
check_api <- function() {
|
||||
skip_on_cran()
|
||||
|
||||
code <- NA
|
||||
try(code <- httr::status_code(httr::GET(osem_endpoint())))
|
||||
if (is.na(code)) skip("API not available")
|
||||
}
|
||||
|
||||
test_that("counts can be retrieved as a list of numbers", {
|
||||
check_api()
|
||||
|
||||
counts <- osem_counts()
|
||||
|
||||
expect_true(is.list(counts))
|
||||
expect_true(is.numeric(unlist(counts)))
|
||||
expect_length(counts, 3)
|
||||
})
|
121
tests/testthat/test_measurements.R
Normal file
121
tests/testthat/test_measurements.R
Normal file
|
@ -0,0 +1,121 @@
|
|||
context("measurements")
|
||||
|
||||
check_api <- function() {
|
||||
skip_on_cran()
|
||||
|
||||
code <- NA
|
||||
try(code <- httr::status_code(httr::GET(osem_endpoint())))
|
||||
if (is.na(code)) skip("API not available")
|
||||
}
|
||||
|
||||
try({
|
||||
boxes <- osem_boxes()
|
||||
})
|
||||
|
||||
test_that("measurements can be retrieved for a phenomenon", {
|
||||
check_api()
|
||||
|
||||
measurements <- osem_measurements(x = "Windgeschwindigkeit")
|
||||
expect_true(is.data.frame(measurements))
|
||||
expect_true("osem_measurements" %in% class(measurements))
|
||||
})
|
||||
|
||||
test_that("measurement retrieval does not give progress information in non-interactive mode", {
|
||||
check_api()
|
||||
|
||||
if (!opensensmapr:::is_non_interactive()) skip("interactive session")
|
||||
|
||||
out <- capture.output(measurements <- osem_measurements(x = "Licht"))
|
||||
expect_length(out, 0)
|
||||
})
|
||||
|
||||
test_that("a response with no matching senseBoxes gives an error", {
|
||||
check_api()
|
||||
|
||||
expect_error(osem_measurements(x = "Windgeschwindigkeit", exposure = "indoor"), "No senseBoxes found")
|
||||
})
|
||||
|
||||
test_that("data.frame can be converted to measurements data.frame", {
|
||||
df <- osem_as_measurements(data.frame(c(1,2), c("a", "b")))
|
||||
expect_equal(class(df), c("osem_measurements", "data.frame"))
|
||||
})
|
||||
|
||||
test_that("columns can be specified for phenomena", {
|
||||
check_api()
|
||||
|
||||
cols <- c("value", "boxId", "boxName")
|
||||
measurements <- osem_measurements(x = "Windgeschwindigkeit", columns = cols)
|
||||
expect_equal(names(measurements), cols)
|
||||
})
|
||||
|
||||
test_that("measurements can be retrieved for a phenomenon and exposure", {
|
||||
check_api()
|
||||
|
||||
measurements <- osem_measurements(x = "Temperatur", exposure = "unknown",
|
||||
columns = c("value", "boxId", "boxName"))
|
||||
expect_equal(nrow(measurements), 0)
|
||||
})
|
||||
|
||||
test_that("measurements of specific boxes can be retrieved for one phenomenon and returns a measurements data.frame", {
|
||||
check_api()
|
||||
|
||||
# fix for subsetting
|
||||
class(boxes) <- c("data.frame")
|
||||
three_boxes <- boxes[1:3,]
|
||||
class(boxes) <- c("sensebox", "data.frame")
|
||||
three_boxes <- osem_as_sensebox(three_boxes)
|
||||
phens <- names(osem_phenomena(three_boxes))
|
||||
|
||||
measurements <- osem_measurements(x = three_boxes, phenomenon = phens[[1]])
|
||||
expect_true(is.data.frame(measurements))
|
||||
expect_true("osem_measurements" %in% class(measurements))
|
||||
})
|
||||
|
||||
test_that("measurements can be retrieved for a bounding box", {
|
||||
check_api()
|
||||
|
||||
sfc <- sf::st_sfc(sf::st_linestring(x = matrix(data = c(7, 8, 50, 51), ncol = 2)), crs = 4326)
|
||||
bbox <- sf::st_bbox(sfc)
|
||||
measurements <- osem_measurements(x = bbox, phenomenon = "Windrichtung")
|
||||
expect_true(all(unique(measurements$lat) > 50))
|
||||
expect_true(all(unique(measurements$lat) < 51))
|
||||
expect_true(all(unique(measurements$lon) < 8))
|
||||
expect_true(all(unique(measurements$lon) > 7))
|
||||
})
|
||||
|
||||
test_that("measurements can be retrieved for a time period", {
|
||||
check_api()
|
||||
|
||||
from_date <- as.POSIXct("2018-01-01 12:00:00")
|
||||
to_date <- as.POSIXct("2018-01-01 13:00:00")
|
||||
measurements <- osem_measurements(x = "Temperature", from = from_date, to = to_date)
|
||||
|
||||
expect_true(all(measurements$createdAt < to_date))
|
||||
expect_true(all(measurements$createdAt > from_date))
|
||||
})
|
||||
|
||||
test_that("measurements can be retrieved for a time period > 31 days", {
|
||||
check_api()
|
||||
|
||||
from_date <- as.POSIXct("2017-11-01 12:00:00")
|
||||
to_date <- as.POSIXct("2018-01-01 13:00:00")
|
||||
measurements <- osem_measurements(x = "Windrichtung", from = from_date, to = to_date)
|
||||
|
||||
expect_true(all(measurements$createdAt < to_date))
|
||||
expect_true(all(measurements$createdAt > from_date))
|
||||
})
|
||||
|
||||
test_that("both from and to are required when requesting measurements, error otherwise", {
|
||||
expect_error(osem_measurements(x = "Temperature", from = as.POSIXct("2017-01-01")), "only together with")
|
||||
expect_error(osem_measurements(x = "Temperature", to = as.POSIXct("2017-01-01")), "only together with")
|
||||
})
|
||||
|
||||
test_that("[.osem_measurements maintains attributes", {
|
||||
check_api()
|
||||
|
||||
sfc <- sf::st_sfc(sf::st_linestring(x = matrix(data = c(7, 8, 50, 51), ncol = 2)), crs = 4326)
|
||||
bbox <- sf::st_bbox(sfc)
|
||||
m <- osem_measurements(x = bbox, phenomenon = "Windrichtung")
|
||||
|
||||
expect_true(all(attributes(m[1:nrow(m), ]) %in% attributes(m)))
|
||||
})
|
40
tests/testthat/test_phenomena.R
Normal file
40
tests/testthat/test_phenomena.R
Normal file
|
@ -0,0 +1,40 @@
|
|||
context("phenomena")
|
||||
|
||||
check_api <- function() {
|
||||
skip_on_cran()
|
||||
|
||||
code <- NA
|
||||
try(code <- httr::status_code(httr::GET(osem_endpoint())))
|
||||
if (is.na(code)) skip("API not available")
|
||||
}
|
||||
|
||||
try({
|
||||
boxes <- osem_boxes()
|
||||
all_phen <- unique(unlist(boxes$phenomena))
|
||||
})
|
||||
|
||||
test_that("phenomena from boxes is a list of counts", {
|
||||
check_api()
|
||||
|
||||
phenomena <- osem_phenomena(boxes)
|
||||
|
||||
expect_true(is.numeric(unlist(phenomena)))
|
||||
expect_true(is.list(phenomena))
|
||||
})
|
||||
|
||||
test_that("phenomena from boxes has all phenomena", {
|
||||
check_api()
|
||||
|
||||
phenomena <- osem_phenomena(boxes)
|
||||
|
||||
expect_true(all(all_phen %in% names(phenomena)))
|
||||
expect_true(all(names(phenomena) %in% all_phen))
|
||||
})
|
||||
|
||||
test_that("phenomena from a not sensebox data.frame returns error", {
|
||||
expect_error(osem_phenomena(list()), "no applicable method")
|
||||
expect_error(osem_phenomena(data.frame()), "no applicable method")
|
||||
boxes_df <- boxes
|
||||
class(boxes_df) <- c("data.frame")
|
||||
expect_error(osem_phenomena(boxes_df), "no applicable method")
|
||||
})
|
Loading…
Add table
Reference in a new issue