mirror of
https://github.com/sensebox/opensensmapr
synced 2025-02-22 14:53:57 +01:00
improved runtime to be affordable on CRAN submission
This commit is contained in:
parent
e2e9e3dbb3
commit
b1001b174e
6 changed files with 52 additions and 44 deletions
|
@ -1,6 +1,6 @@
|
|||
Package: opensensmapr
|
||||
Type: Package
|
||||
Title: Client for the Data API of openSenseMap.org
|
||||
Title: Client for the Data API of 'openSenseMap.org'
|
||||
Version: 0.6.0
|
||||
URL: https://github.com/sensebox/opensensmapR
|
||||
BugReports: https://github.com/sensebox/opensensmapR/issues
|
||||
|
|
|
@ -2,21 +2,9 @@ source('testhelpers.R')
|
|||
context('box')
|
||||
|
||||
try({
|
||||
boxes = osem_boxes()
|
||||
box = osem_box('57000b8745fd40c8196ad04c')
|
||||
})
|
||||
|
||||
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]])
|
||||
expect_silent(osem_box(boxes$X_id[[1]]))
|
||||
})
|
||||
|
||||
test_that('required box attributes are correctly parsed', {
|
||||
check_api()
|
||||
|
@ -61,11 +49,6 @@ test_that('unknown box throws', {
|
|||
expect_error(osem_box('57000b8745fd40c800000000'), 'not found')
|
||||
})
|
||||
|
||||
test_that('[.sensebox maintains attributes', {
|
||||
check_api()
|
||||
|
||||
expect_true(all(attributes(boxes[1:nrow(boxes), ]) %in% attributes(boxes)))
|
||||
})
|
||||
|
||||
test_that("print.sensebox filters important attributes for a single box", {
|
||||
check_api()
|
||||
|
|
|
@ -172,3 +172,44 @@ test_that('requests can be cached', {
|
|||
osem_clear_cache()
|
||||
expect_length(list.files(tempdir(), pattern = 'osemcache\\..*\\.rds'), 0)
|
||||
})
|
||||
|
||||
context('single box from 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]])
|
||||
expect_silent(osem_box(boxes$X_id[[1]]))
|
||||
})
|
||||
|
||||
test_that('[.sensebox maintains attributes', {
|
||||
check_api()
|
||||
|
||||
expect_true(all(attributes(boxes[1:nrow(boxes), ]) %in% attributes(boxes)))
|
||||
})
|
||||
|
||||
context('measurements boxes')
|
||||
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('phenomenon is required when requesting measurements, error otherwise', {
|
||||
check_api()
|
||||
|
||||
expect_error(osem_measurements(boxes), 'Parameter "phenomenon" is required')
|
||||
})
|
|
@ -1,9 +1,6 @@
|
|||
source('testhelpers.R')
|
||||
context('measurements')
|
||||
|
||||
try({
|
||||
boxes = osem_boxes()
|
||||
})
|
||||
|
||||
test_that('measurements can be retrieved for a phenomenon', {
|
||||
check_api()
|
||||
|
@ -47,20 +44,6 @@ test_that('measurements can be retrieved for a phenomenon and exposure', {
|
|||
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()
|
||||
|
@ -105,7 +88,6 @@ test_that('phenomenon is required when requesting measurements, error otherwise'
|
|||
check_api()
|
||||
|
||||
expect_error(osem_measurements())
|
||||
expect_error(osem_measurements(boxes), 'Parameter "phenomenon" is required')
|
||||
|
||||
sfc = sf::st_sfc(sf::st_linestring(x = matrix(data = c(7, 8, 50, 51), ncol = 2)), crs = 4326)
|
||||
bbox = sf::st_bbox(sfc)
|
||||
|
|
|
@ -115,7 +115,9 @@ berlin = st_point(c(13.4034, 52.5120)) %>%
|
|||
st_transform(4326) %>% # the opensensemap expects WGS 84
|
||||
st_bbox()
|
||||
```
|
||||
```{r results = F}
|
||||
|
||||
Since the API takes quite long to response measurements, especially filtered on space and time, we do not run the following chunks for publication of the package on CRAN.
|
||||
```{r results = F, eval=FALSE}
|
||||
pm25 = osem_measurements(
|
||||
berlin,
|
||||
phenomenon = 'PM2.5',
|
||||
|
@ -129,7 +131,7 @@ plot(pm25)
|
|||
Now we can get started with actual spatiotemporal data analysis.
|
||||
First, lets mask the seemingly uncalibrated sensors:
|
||||
|
||||
```{r}
|
||||
```{r, eval=FALSE}
|
||||
outliers = filter(pm25, value > 100)$sensorId
|
||||
bad_sensors = outliers[, drop = T] %>% levels()
|
||||
|
||||
|
@ -138,13 +140,13 @@ pm25 = mutate(pm25, invalid = sensorId %in% bad_sensors)
|
|||
|
||||
Then plot the measuring locations, flagging the outliers:
|
||||
|
||||
```{r}
|
||||
```{r, eval=FALSE}
|
||||
st_as_sf(pm25) %>% st_geometry() %>% plot(col = factor(pm25$invalid), axes = T)
|
||||
```
|
||||
|
||||
Removing these sensors yields a nicer time series plot:
|
||||
|
||||
```{r}
|
||||
```{r, eval=FALSE}
|
||||
pm25 %>% filter(invalid == FALSE) %>% plot()
|
||||
```
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@ osem_clear_cache(getwd()) # clears a custom cache
|
|||
If you want to roll your own serialization method to support custom data formats,
|
||||
here's how:
|
||||
|
||||
```{r data, results='hide'}
|
||||
```{r data, results='hide', eval=FALSE}
|
||||
# first get our example data:
|
||||
measurements = osem_measurements('Windgeschwindigkeit')
|
||||
```
|
||||
|
@ -79,7 +79,7 @@ measurements = osem_measurements('Windgeschwindigkeit')
|
|||
If you are paranoid and worry about `.rds` files not being decodable anymore
|
||||
in the (distant) future, you could serialize to a plain text format such as JSON.
|
||||
This of course comes at the cost of storage space and performance.
|
||||
```{r serialize_json}
|
||||
```{r serialize_json, eval=FALSE}
|
||||
# serializing senseBoxes to JSON, and loading from file again:
|
||||
write(jsonlite::serializeJSON(measurements), 'measurements.json')
|
||||
measurements_from_file = jsonlite::unserializeJSON(readr::read_file('measurements.json'))
|
||||
|
@ -90,7 +90,7 @@ This method also persists the R object metadata (classes, attributes).
|
|||
If you were to use a serialization method that can't persist object metadata, you
|
||||
could re-apply it with the following functions:
|
||||
|
||||
```{r serialize_attrs}
|
||||
```{r serialize_attrs, eval=FALSE}
|
||||
# note the toJSON call instead of serializeJSON
|
||||
write(jsonlite::toJSON(measurements), 'measurements_bad.json')
|
||||
measurements_without_attrs = jsonlite::fromJSON('measurements_bad.json')
|
||||
|
@ -101,6 +101,6 @@ class(measurements_with_attrs)
|
|||
```
|
||||
The same goes for boxes via `osem_as_sensebox()`.
|
||||
|
||||
```{r cleanup, include=FALSE}
|
||||
```{r cleanup, include=FALSE, eval=FALSE}
|
||||
file.remove('measurements.json', 'measurements_bad.json')
|
||||
```
|
||||
|
|
Loading…
Add table
Reference in a new issue