Skip to content

Commit 8044531

Browse files
committed
load_tree_data(): auto-detect CSV data types and force PLT_CN type
1 parent 3ed5fa8 commit 8044531

File tree

2 files changed

+84
-12
lines changed

2 files changed

+84
-12
lines changed

R/load_tree_data.R

Lines changed: 67 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@
7171
#' @param sql Optional character string containing a SQL SELECT statement to
7272
#' execute on `src` (instead of selecting all records, potentially from a subset
7373
#' of columns, i.e., mutually exclusive with `table` and/or `columns`).
74+
#' @param quoted_cols_as_char A logical value indicating whether to auto-detect
75+
#' columns that contain quoted values as `"character"` type, `TRUE` by default.
76+
#' Only used when `src` is a CSV file.
7477
#' @return
7578
#' A data frame containing tree records fetched from `src`.
7679
#'
@@ -84,6 +87,12 @@
8487
#'
8588
#' For more details: \url{https://gdal.org/en/stable/drivers/vector/index.html}
8689
#'
90+
#' `load_tree_data()` requires GDAL built with support for the PostgreSQL client
91+
#' library for access to PostgreSQL databases.
92+
#'
93+
#' Column names are generally case-sensitive in \pkg{FIAstemmap} functions and
94+
#' assumed to follow FIADB all upper case naming.
95+
#'
8796
#' @seealso
8897
#' [DEFAULT_TREE_COLUMNS]
8998
#'
@@ -95,7 +104,7 @@
95104
#' head(tree)
96105
#' @export
97106
load_tree_data <- function(src, table = NULL, columns = DEFAULT_TREE_COLUMNS,
98-
sql = NULL) {
107+
sql = NULL, quoted_cols_as_char = TRUE) {
99108

100109
if (missing(src) || is.null(src))
101110
stop("'src' is required")
@@ -108,6 +117,12 @@ load_tree_data <- function(src, table = NULL, columns = DEFAULT_TREE_COLUMNS,
108117
stop("could not connect to 'src'", call. = FALSE)
109118
}
110119

120+
src_fmt <- gdalraster::ogr_ds_format(src)
121+
if (is.null(src_fmt)) {
122+
cli::cli_alert_danger("unsupported format: {.path {src}}")
123+
stop("'src' is not recognized as a supported format", call. = FALSE)
124+
}
125+
111126
if (!is.null(table) && !is.null(sql))
112127
stop("'table' and 'sql' are mutually exclusive", call. = FALSE)
113128

@@ -120,18 +135,26 @@ load_tree_data <- function(src, table = NULL, columns = DEFAULT_TREE_COLUMNS,
120135
if (!is.null(sql) && !(is.character(sql) && length(sql == 1)))
121136
stop("'sql' must be a single character string")
122137

138+
if (missing(quoted_cols_as_char) || is.null(quoted_cols_as_char)) {
139+
quoted_cols_as_char <- TRUE
140+
} else if (!(is.logical(quoted_cols_as_char) ||
141+
length(quoted_cols_as_char) != 1)) {
142+
stop("'quoted_cols_as_char' must be a single logical value",
143+
call. = FALSE)
144+
}
145+
123146
if (is.null(sql) && !is.null(columns)) {
124147
if (any(c("DIST", "AZIMUTH") %in% columns)) {
125-
tbl <- ""
148+
tbl_tmp <- ""
126149
if (!is.null(table))
127-
tbl <- table
150+
tbl_tmp <- table
128151

129-
if (gdalraster::ogr_field_index(src, tbl, "DIST") < 0 ||
130-
gdalraster::ogr_field_index(src, tbl, "AZIMUTH") < 0) {
152+
if (gdalraster::ogr_field_index(src, tbl_tmp, "DIST") < 0 ||
153+
gdalraster::ogr_field_index(src, tbl_tmp, "AZIMUTH") < 0) {
131154

132-
cli::cli_alert_warning(c(
133-
"The data source does not have ",
134-
"{.field DIST} and/or {.field AZIMUTH}"))
155+
cli::cli_alert_warning(
156+
c("The data source does not have ",
157+
"{.field DIST} and/or {.field AZIMUTH}"))
135158

136159
columns <- columns[!columns %in% c("DIST", "AZIMUTH")]
137160
if (length(columns) == 0)
@@ -141,14 +164,47 @@ load_tree_data <- function(src, table = NULL, columns = DEFAULT_TREE_COLUMNS,
141164
}
142165

143166
ds <- NULL
167+
open_options <- character(0)
168+
169+
if (src_fmt == "CSV") {
170+
# auto-detect column data types
171+
open_options <- c(open_options, "AUTODETECT_TYPE=YES")
172+
if (quoted_cols_as_char) {
173+
open_options <- c(open_options, "QUOTED_FIELDS_AS_STRING=YES")
174+
}
175+
176+
# enforce PLT_CN as string data type by schema override (GDAL >= 3.11)
177+
if (gdalraster::gdal_version_num() >=
178+
gdalraster::gdal_compute_version(3, 11, 0)) {
179+
180+
tbl_tmp <- table
181+
if (is.null(tbl_tmp)) {
182+
tbl_tmp <- gdalraster:::.cpl_get_basename(src)
183+
}
184+
185+
schema <- 'OGR_SCHEMA={"layers": [{"name": "%s", "fields":[{
186+
"name": "PLT_CN", "type": "String" }]}]}'
187+
188+
override_schema <- sprintf(schema, tbl_tmp)
189+
open_options <- c(open_options, override_schema)
190+
}
191+
}
192+
193+
gdalraster::push_error_handler("quiet")
144194
if (is.null(table) && is.null(sql)) {
145-
ds <- try(methods::new(gdalraster::GDALVector, src), silent = TRUE)
195+
ds <- try(methods::new(gdalraster::GDALVector, src, "", TRUE,
196+
open_options),
197+
silent = TRUE)
146198
} else if (!is.null(table)) {
147-
ds <- try(methods::new(gdalraster::GDALVector, src, table),
199+
ds <- try(methods::new(gdalraster::GDALVector, src, table, TRUE,
200+
open_options),
148201
silent = TRUE)
149202
} else if (!is.null(sql)) {
150-
ds <- try(methods::new(gdalraster::GDALVector, src, sql), silent = TRUE)
203+
ds <- try(methods::new(gdalraster::GDALVector, src, sql, TRUE,
204+
open_options),
205+
silent = TRUE)
151206
}
207+
gdalraster::pop_error_handler()
152208

153209
if (!methods::is(ds, "Rcpp_GDALVector")) {
154210
cli::cli_alert_danger("Failed to access tree data in {.path {src}}")

man/load_tree_data.Rd

Lines changed: 17 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)