mirror of
https://github.com/sensebox/opensensmapr
synced 2025-02-22 23:23: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
|
Package: opensensmapr
|
||||||
Type: Package
|
Type: Package
|
||||||
Title: Client for the Data API of openSenseMap.org
|
Title: Client for the Data API of 'openSenseMap.org'
|
||||||
Version: 0.6.0
|
Version: 0.6.0
|
||||||
URL: https://github.com/sensebox/opensensmapR
|
URL: https://github.com/sensebox/opensensmapR
|
||||||
BugReports: https://github.com/sensebox/opensensmapR/issues
|
BugReports: https://github.com/sensebox/opensensmapR/issues
|
||||||
|
|
|
@ -2,21 +2,9 @@ source('testhelpers.R')
|
||||||
context('box')
|
context('box')
|
||||||
|
|
||||||
try({
|
try({
|
||||||
boxes = osem_boxes()
|
|
||||||
box = osem_box('57000b8745fd40c8196ad04c')
|
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', {
|
test_that('required box attributes are correctly parsed', {
|
||||||
check_api()
|
check_api()
|
||||||
|
@ -61,11 +49,6 @@ test_that('unknown box throws', {
|
||||||
expect_error(osem_box('57000b8745fd40c800000000'), 'not found')
|
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", {
|
test_that("print.sensebox filters important attributes for a single box", {
|
||||||
check_api()
|
check_api()
|
||||||
|
|
|
@ -172,3 +172,44 @@ test_that('requests can be cached', {
|
||||||
osem_clear_cache()
|
osem_clear_cache()
|
||||||
expect_length(list.files(tempdir(), pattern = 'osemcache\\..*\\.rds'), 0)
|
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')
|
source('testhelpers.R')
|
||||||
context('measurements')
|
context('measurements')
|
||||||
|
|
||||||
try({
|
|
||||||
boxes = osem_boxes()
|
|
||||||
})
|
|
||||||
|
|
||||||
test_that('measurements can be retrieved for a phenomenon', {
|
test_that('measurements can be retrieved for a phenomenon', {
|
||||||
check_api()
|
check_api()
|
||||||
|
@ -47,20 +44,6 @@ test_that('measurements can be retrieved for a phenomenon and exposure', {
|
||||||
expect_equal(nrow(measurements), 0)
|
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', {
|
test_that('measurements can be retrieved for a bounding box', {
|
||||||
check_api()
|
check_api()
|
||||||
|
@ -105,7 +88,6 @@ test_that('phenomenon is required when requesting measurements, error otherwise'
|
||||||
check_api()
|
check_api()
|
||||||
|
|
||||||
expect_error(osem_measurements())
|
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)
|
sfc = sf::st_sfc(sf::st_linestring(x = matrix(data = c(7, 8, 50, 51), ncol = 2)), crs = 4326)
|
||||||
bbox = sf::st_bbox(sfc)
|
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_transform(4326) %>% # the opensensemap expects WGS 84
|
||||||
st_bbox()
|
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(
|
pm25 = osem_measurements(
|
||||||
berlin,
|
berlin,
|
||||||
phenomenon = 'PM2.5',
|
phenomenon = 'PM2.5',
|
||||||
|
@ -129,7 +131,7 @@ plot(pm25)
|
||||||
Now we can get started with actual spatiotemporal data analysis.
|
Now we can get started with actual spatiotemporal data analysis.
|
||||||
First, lets mask the seemingly uncalibrated sensors:
|
First, lets mask the seemingly uncalibrated sensors:
|
||||||
|
|
||||||
```{r}
|
```{r, eval=FALSE}
|
||||||
outliers = filter(pm25, value > 100)$sensorId
|
outliers = filter(pm25, value > 100)$sensorId
|
||||||
bad_sensors = outliers[, drop = T] %>% levels()
|
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:
|
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)
|
st_as_sf(pm25) %>% st_geometry() %>% plot(col = factor(pm25$invalid), axes = T)
|
||||||
```
|
```
|
||||||
|
|
||||||
Removing these sensors yields a nicer time series plot:
|
Removing these sensors yields a nicer time series plot:
|
||||||
|
|
||||||
```{r}
|
```{r, eval=FALSE}
|
||||||
pm25 %>% filter(invalid == FALSE) %>% plot()
|
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,
|
If you want to roll your own serialization method to support custom data formats,
|
||||||
here's how:
|
here's how:
|
||||||
|
|
||||||
```{r data, results='hide'}
|
```{r data, results='hide', eval=FALSE}
|
||||||
# first get our example data:
|
# first get our example data:
|
||||||
measurements = osem_measurements('Windgeschwindigkeit')
|
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
|
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.
|
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.
|
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:
|
# serializing senseBoxes to JSON, and loading from file again:
|
||||||
write(jsonlite::serializeJSON(measurements), 'measurements.json')
|
write(jsonlite::serializeJSON(measurements), 'measurements.json')
|
||||||
measurements_from_file = jsonlite::unserializeJSON(readr::read_file('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
|
If you were to use a serialization method that can't persist object metadata, you
|
||||||
could re-apply it with the following functions:
|
could re-apply it with the following functions:
|
||||||
|
|
||||||
```{r serialize_attrs}
|
```{r serialize_attrs, eval=FALSE}
|
||||||
# note the toJSON call instead of serializeJSON
|
# note the toJSON call instead of serializeJSON
|
||||||
write(jsonlite::toJSON(measurements), 'measurements_bad.json')
|
write(jsonlite::toJSON(measurements), 'measurements_bad.json')
|
||||||
measurements_without_attrs = jsonlite::fromJSON('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()`.
|
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')
|
file.remove('measurements.json', 'measurements_bad.json')
|
||||||
```
|
```
|
||||||
|
|
Loading…
Add table
Reference in a new issue