Skip to content

Commit 8bb9f8d

Browse files
Florian MayerFlorian Mayer
authored andcommitted
ruODK v0.9.1: patch geotrace/geoshape coords
* Closes #88 * Include test data snapshot with "bad" coordinates * Test that bad coords are fixed, good coords kept * Update news, citation, codemeta * Add vignette "Spatial"
1 parent 41b0527 commit 8bb9f8d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+579
-79
lines changed

DESCRIPTION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Type: Package
22
Package: ruODK
33
Title: An R Client for the ODK Central API
4-
Version: 0.9.0.9000
4+
Version: 0.9.1
55
Authors@R:
66
c(person(given = c("Florian", "W."),
77
family = "Mayer",

NAMESPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export(attachment_get)
66
export(attachment_link)
77
export(attachment_list)
88
export(audit_get)
9+
export(drop_null_coords)
910
export(enexpr)
1011
export(enquo)
1112
export(ensym)

NEWS.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
# `ruODK` (development version)
22
* Development continues in the default branch `main`.
3+
4+
# `ruODK` 0.9.1
5+
## Major fixes
6+
ODK Central versions 0.7 to 0.9 export geotraces and geoshapes via OData with
7+
a trailing empty coordinate. `ruODK` removes any trailing empty coordinates from
8+
both GeoJSON and WKT formats. (#88, HT Timon Weitkamp for the bug report)
9+
10+
## Documentation
11+
A new vignette "Spatial" demonstrates how to parse spatial data into native
12+
formats, such as `sf`, and gives pointers on what to do next with them.
13+
314

415
# `ruODK` 0.9.0
516
This is the release on passing

R/data.R

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,23 @@
391391
#' @encoding UTF-8
392392
"geo_gj"
393393

394+
#' The parsed submissions of a form containing geofields in GeoJSON
395+
#' with trailing empty coordinates present.
396+
#'
397+
#' \lifecycle{stable}
398+
#'
399+
#' This issue was fixed in #88.
400+
#' ODK Central versions 0.7 - 0.9 export geotraces and geoshapes with trailing
401+
#' empty coordinates. ruODK has a patch to drop trailing empty coordinates.
402+
#' This dataset is used to test the patch in ruODK.
403+
#'
404+
#' @source \code{\link{odata_submission_get}(wkt=FALSE, parse=TRUE)}
405+
#' run on the test form
406+
#' `system.file("extdata", "Locations.xml", package = "ruODK")`.
407+
#' @family included
408+
#' @encoding UTF-8
409+
"geo_gj88"
410+
394411
#' The unparsed submissions of a form containing geofields in WKT.
395412
#'
396413
#' \lifecycle{stable}
@@ -413,6 +430,23 @@
413430
#' @encoding UTF-8
414431
"geo_wkt"
415432

433+
#' The parsed submissions of a form containing geofields in WKT
434+
#' with trailing empty coordinates present.
435+
#'
436+
#' \lifecycle{stable}
437+
#'
438+
#' This issue was fixed in #88.
439+
#' ODK Central versions 0.7 - 0.9 export geotraces and geoshapes with trailing
440+
#' empty coordinates. ruODK has a patch to drop trailing empty coordinates.
441+
#' This dataset is used to test the patch in ruODK.
442+
#'
443+
#' @source \code{\link{odata_submission_get}(wkt=TRUE, parse=TRUE)}
444+
#' run on the test form
445+
#' `system.file("extdata", "Locations.xml", package = "ruODK")`.
446+
#' @family included
447+
#' @encoding UTF-8
448+
"geo_wkt88"
449+
416450
#' The unparsed XML form_schema of a form from ODK Central v0.6 as nested list.
417451
#'
418452
#' \lifecycle{stable}

R/drop_null_coords.R

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#' Drop any NULL coordinates from a GeoJSON geometry.
2+
#'
3+
#' This helper patches a bug/feature in ODK Central (versions 0.7-0.9), where
4+
#' geotrace / geoshape GeoJSON contains a last coordinate pair with NULL
5+
#' lat/lon (no alt/acc), and WKT ends in `, undefined NaN`.
6+
#'
7+
#' While \code{\link{split_geotrace}} and \code{\link{split_geoshape}} modify
8+
#' the WKT inline, it is more maintainable to separate the GeoJSON cleaner
9+
#' into this function.
10+
#'
11+
#' This helper drops the last element of a GeoJSON coordinate list if it is
12+
#' `list(NULL, NULL)`.
13+
#'
14+
#' @param x A GeoJSON geometry parsed as nested list.
15+
#' E.g. `geo_gj$path_location_path_gps`.
16+
#' @return The nested list minus the last element (if NULL).
17+
#' @family utilities
18+
#' @export
19+
#' @examples
20+
#' # A snapshot of geo data with trailing empty coordinates.
21+
#' data("geo_gj88")
22+
#'
23+
#' len_coords <- length(geo_gj88$path_location_path_gps[[1]]$coordinates)
24+
#'
25+
#' length(geo_gj88$path_location_path_gps[[1]]$coordinates[[len_coords]]) %>%
26+
#' testthat::expect_equal(2)
27+
#'
28+
#' geo_gj88$path_location_path_gps[[1]]$coordinates[[len_coords]][[1]] %>%
29+
#' testthat::expect_null()
30+
#'
31+
#' geo_gj88$path_location_path_gps[[1]]$coordinates[[len_coords]][[2]] %>%
32+
#' testthat::expect_null()
33+
#'
34+
#' # The last coordinate pair is a list(NULL, NULL).
35+
#' # Invalid coordinates like these are a choking hazard for geospatial
36+
#' # packages. We should remove them before we can convert ODK data into native
37+
#' # spatial formats, such as sf.
38+
#' str(geo_gj88$path_location_path_gps[[1]]$coordinates[[len_coords]])
39+
#'
40+
#' geo_gj_repaired <- geo_gj88 %>%
41+
#' dplyr::mutate(
42+
#' path_location_path_gps = path_location_path_gps %>%
43+
#' purrr::map(drop_null_coords)
44+
#' )
45+
#'
46+
#' len_coords_repaired <- length(
47+
#' geo_gj_repaired$path_location_path_gps[[1]]$coordinates
48+
#' )
49+
#' testthat::expect_equal(len_coords_repaired + 1, len_coords)
50+
drop_null_coords <- function(x) {
51+
# Extract and simplify last coords. list(NULL, NULL) becomes list().
52+
xx <- purrr::compact(x$coordinates[[length(x$coordinates)]])
53+
# Remove empty coords by setting the entire list(NULL, NULL) to NULL.
54+
if (is.list(xx) && length(xx) == 0) {
55+
x$coordinates[[length(x$coordinates)]] <- NULL
56+
}
57+
x
58+
}
59+
60+
# usethis::use_test("drop_null_coords") # nolint

R/split_geoshape.R

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -69,17 +69,16 @@
6969
#' )
7070
#' }
7171
split_geoshape <- function(
72-
data,
73-
colname,
74-
wkt = FALSE,
75-
odkc_version = odkc_version
76-
) {
72+
data,
73+
colname,
74+
wkt = FALSE,
75+
odkc_version = odkc_version) {
7776
if (nrow(data) == 0) {
7877
# Option 1: Early exit - nothing to do
7978
return(data)
8079
} else if (odkc_version < 0.8) {
8180
# Option 2: ODK linestring
82-
# ODK Central <=0.7 ignores the WKT argument for geotrace and geoshape
81+
# ODK Central <=0.7 ignores the WKT argument for geotrace and geoshape.
8382
# nolint start
8483
# ruODK::odata_submission_get(wkt = TRUE, parse = TRUE)
8584
# ruODK::odata_submission_get(wkt = FALSE, parse = FALSE) %>%
@@ -116,8 +115,13 @@ split_geoshape <- function(
116115
dplyr::rename_at(
117116
dplyr::vars(dplyr::starts_with("XXX")),
118117
list(~ stringr::str_replace(., "XXX", colname))
118+
) %>%
119+
# Drop last empty coordinate from colname, a list(NULL, NULL).
120+
# Affects ODK Central Version 0.7-0.9.
121+
dplyr::mutate_at(
122+
dplyr::vars(colname),
123+
list(~ purrr::map(., drop_null_coords))
119124
)
120-
# TODO #88 drop last empty coord from colname
121125
} else {
122126
# WKT
123127
data %>%
@@ -132,6 +136,8 @@ split_geoshape <- function(
132136
remove = FALSE,
133137
convert = TRUE
134138
) %>%
139+
# Drop last empty coordinate from colname, a trailing ",undefined NaN".
140+
# Affects ODK Central Version 0.7-0.9.
135141
dplyr::mutate_at(
136142
dplyr::vars(colname),
137143
list(~ stringr::str_replace_all(., ",undefined NaN", ""))

R/split_geotrace.R

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,13 @@ split_geotrace <- function(data,
130130
dplyr::rename_at(
131131
dplyr::vars(dplyr::starts_with("XXX")),
132132
list(~ stringr::str_replace(., "XXX", colname))
133+
) %>%
134+
# Drop last empty coordinate from colname, a list(NULL, NULL).
135+
# Affects ODK Central Version 0.7-0.9.
136+
dplyr::mutate_at(
137+
dplyr::vars(colname),
138+
list(~ purrr::map(., drop_null_coords))
133139
)
134-
# TODO #88 drop last empty coord from colname
135140
} else {
136141
# Option 4: ODKC v0.8 WKT
137142
data %>%

codemeta.json

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,10 @@
1414
],
1515
"issueTracker": "https://github.com/ropensci/ruODK/issues",
1616
"license": "https://spdx.org/licenses/GPL-3.0",
17-
"version": "0.9.0",
17+
"version": "0.9.1",
1818
"programmingLanguage": {
1919
"@type": "ComputerLanguage",
2020
"name": "R",
21-
"version": "4.0.0",
2221
"url": "https://r-project.org"
2322
},
2423
"runtimePlatform": "R version 4.0.0 (2020-04-24)",
@@ -136,6 +135,19 @@
136135
},
137136
"sameAs": "https://CRAN.R-project.org/package=listviewer"
138137
},
138+
{
139+
"@type": "SoftwareApplication",
140+
"identifier": "mapview",
141+
"name": "mapview",
142+
"version": ">= 2.7.8",
143+
"provider": {
144+
"@id": "https://cran.r-project.org",
145+
"@type": "Organization",
146+
"name": "Comprehensive R Archive Network (CRAN)",
147+
"url": "https://cran.r-project.org"
148+
},
149+
"sameAs": "https://CRAN.R-project.org/package=mapview"
150+
},
139151
{
140152
"@type": "SoftwareApplication",
141153
"identifier": "rmarkdown",
@@ -162,6 +174,19 @@
162174
},
163175
"sameAs": "https://CRAN.R-project.org/package=roxygen2"
164176
},
177+
{
178+
"@type": "SoftwareApplication",
179+
"identifier": "sf",
180+
"name": "sf",
181+
"version": ">= 0.9-5",
182+
"provider": {
183+
"@id": "https://cran.r-project.org",
184+
"@type": "Organization",
185+
"name": "Comprehensive R Archive Network (CRAN)",
186+
"url": "https://cran.r-project.org"
187+
},
188+
"sameAs": "https://CRAN.R-project.org/package=sf"
189+
},
165190
{
166191
"@type": "SoftwareApplication",
167192
"identifier": "testthat",
@@ -472,28 +497,17 @@
472497
],
473498
"name": "ruODK: Client for the ODK Central API",
474499
"url": "https://github.com/ropensci/ruODK",
475-
"description": "R package version 0.9.0"
500+
"description": "R package version 0.9.1"
476501
}
477502
],
478503
"releaseNotes": "https://github.com/ropensci/ruODK/blob/master/NEWS.md",
479504
"readme": "https://github.com/ropensci/ruODK/blob/main/README.md",
480-
"fileSize": "1010.359KB",
481-
"contIntegration": [
482-
"https://travis-ci.org/ropensci/ruODK",
483-
"https://ci.appveyor.com/project/florianm/ruodk/branch/master",
484-
"https://codecov.io/github/ropensci/ruODK?branch=master",
485-
"https://travis-ci.org/ropensci/ruODK",
486-
"https://codecov.io/github/ropensci/ruODK?branch=master",
487-
"https://ci.appveyor.com/project/florianm/ruodk/branch/master",
488-
"https://codecov.io/gh/validmeasures/odkr",
489-
"https://travis-ci.org/unhcr/koboloadeR",
490-
"https://ci.appveyor.com/project/unhcr/koboloadeR",
491-
"https://codecov.io/gh/unhcr/koboloadeR",
492-
"https://travis-ci.org/ropensci/ruODK",
493-
"https://ci.appveyor.com/project/florianm/ruodk/branch/master",
494-
"https://codecov.io/github/ropensci/ruODK?branch=master",
495-
"https://travis-ci.org/edwindj/cbsodataR",
496-
"https://ci.appveyor.com/project/edwindj/cbsodatar"
497-
],
498-
"developmentStatus": "https://www.repostatus.org/#active"
505+
"fileSize": "3126.119KB",
506+
"contIntegration": ["https://travis-ci.org/ropensci/ruODK", "https://ci.appveyor.com/project/florianm/ruodk/branch/main", "https://codecov.io/gh/ropensci/ruODK", "https://travis-ci.org/ropensci/ruODK", "https://codecov.io/gh/ropensci/ruODK", "https://ci.appveyor.com/project/florianm/ruodk/branch/main", "https://codecov.io/gh/validmeasures/odkr", "https://travis-ci.org/unhcr/koboloadeR", "https://ci.appveyor.com/project/unhcr/koboloadeR", "https://codecov.io/gh/unhcr/koboloadeR", "https://travis-ci.org/ropensci/ruODK", "https://ci.appveyor.com/project/florianm/ruodk/branch/main", "https://codecov.io/gh/ropensci/ruODK", "https://travis-ci.org/edwindj/cbsodataR", "https://ci.appveyor.com/project/edwindj/cbsodatar"],
507+
"developmentStatus": "https://www.repostatus.org/#active",
508+
"review": {
509+
"@type": "Review",
510+
"url": "https://github.com/ropensci/software-review/issues/335",
511+
"provider": "https://ropensci.org"
512+
}
499513
}

data-raw/make_release.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ source(here::here("data-raw/make_data.R"))
1919
devtools::test()
2020
#
2121
# Docs
22-
lintr:::addin_lint_package()
2322
styler::style_pkg()
23+
lintr:::addin_lint_package()
2424
devtools::document(roclets = c("rd", "collate", "namespace"))
2525
spelling::spell_check_package()
2626
spelling::spell_check_files("README.Rmd", lang = "en_AU") # TODO update wordlist

data/geo_gj.rda

-34 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)