Skip to content

Commit 7587460

Browse files
feat(downloadSSURGO): add LAPPLY.FUN argument for parallel unzipping (#472)
* feat(downloadSSURGO): add `LAPPLY.FUN` argument * fix(downloadSSURGO): explicitly fetch internal function our namespace to support parallel unzip workers * feat: support extra arguments to LAPPLY.FUN * docs: update * fix: exdir
1 parent 4f27893 commit 7587460

4 files changed

Lines changed: 86 additions & 71 deletions

File tree

NEWS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# soilDB 2.9.2 (development)
22
- EDIT base URL (for `get_EDIT_ecoclass_by_geoUnit()` and `make_EDIT_service_URL()`) updated to new USDA-managed server: <https://edit.sc.egov.usda.gov/>
33
- SoilWeb-based Web Coverage Services (`soilColor.wcs()`, `ISSR800.wcs()`, `mukey.wcs()`) have been updated with FY26 maps, now including most OCONUS soil surveys (AK, HI, PR, PW, GU, AS, MP)
4-
- `downloadSSURGO()` gains arguments `include_spatial` and `include_tabular` that are analogous to arguments of the same name from `createSSURGO()`. Thanks to feature request from @dylanbeaudette (#470).
4+
- `downloadSSURGO()` gains arguments `include_spatial` and `include_tabular` that are analogous to arguments of the same name from `createSSURGO()`. Also, added `LAPPLY.FUN` to support arbitrary parallel or progress reporting backends. Thanks to feature request from @dylanbeaudette (#470).
55

66
# soilDB 2.9.1 (2026-04-01)
77
- `ROSETTA()` updates thanks to Todd Skaggs (USDA-ARS):

R/createSSURGO.R

Lines changed: 53 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
#' Get SSURGO ZIP files from Web Soil Survey 'Download Soils Data'
22
#'
3-
#' Download ZIP files containing spatial (ESRI shapefile) and tabular (TXT) files with standard
4-
#' SSURGO format; optionally including the corresponding SSURGO Template Database with
5-
#' `include_template=TRUE`.
6-
#'
7-
#' To specify the Soil Survey Areas you would like to obtain data you use a `WHERE` clause for query
8-
#' of `sacatalog` table such as `areasymbol = 'CA067'`, `"areasymbol IN ('CA628', 'CA067')"` or
9-
#' `areasymbol LIKE 'CT%'`.
3+
#' Download ZIP files containing spatial (ESRI shapefile) and tabular (TXT) files in standard SSURGO
4+
#' format. To specify the Soil Survey Areas you would like to download, use a `WHERE` clause for
5+
#' query of `sacatalog` table, for example: `areasymbol = 'CA067'`, `"areasymbol IN ('CA628',
6+
#' 'CA067')"`, or `areasymbol LIKE 'CT%'`.
107
#'
118
#' @param WHERE _character_. A SQL `WHERE` clause expression used to filter records in `sacatalog`
129
#' table. Alternately `WHERE` can be any spatial object supported by `SDA_spatialQuery()` for
@@ -18,21 +15,25 @@
1815
#' not yet exist. Each ZIP file will extract to a folder labeled with `areasymbol` in this
1916
#' directory. Default: `destdir`
2017
#' @param include_template _logical_. Include the (possibly state-specific) MS Access template
21-
#' database? Default: `FALSE`
18+
#' database? Default: `FALSE`
2219
#' @param include_spatial _logical_ or _character_. Extract spatial data layers from ZIP file?
2320
#' Default: `TRUE` inserts all spatial tables. If `include_spatial` is a _character_ vector
2421
#' containing table names, only that set is extracted from the downloaded ZIP files. e.g.
2522
#' `include_spatial=c("mupolygon", "featpoint")` extracts only the shapefiles (with side car
2623
#' files) for mapunit polygons and special feature points.
27-
#' @param include_tabular _logical_ or _character_. Include tabular data layers in database?
28-
#' Default: `TRUE` inserts all tabular tables. If `include_tabular` is a _character_ vector
29-
#' containing table names, only that set is extracted from the downloaded ZIP files. e.g.
24+
#' @param include_tabular _logical_ or _character_. Extract tabular data from ZIP file? Default:
25+
#' `TRUE` inserts all tabular tables. If `include_tabular` is a _character_ vector containing
26+
#' table names, only that set is extracted from the downloaded ZIP files. e.g.
3027
#' `include_tabular=c("mapunit", "muaggatt")` writes only the `mapunit` and `muaggatt` tables.
3128
#' Note that special feature descriptions are stored in table `"featdesc"` and metadata for each
3229
#' soil survey area are stored in `"soil_metadata"` tables.
3330
#' @param db _character_. Either `"SSURGO"` (default; detailed soil map) or `"STATSGO"` (general
3431
#' soil map).
3532
#' @param extract _logical_. Extract ZIP files to `exdir`? Default: `TRUE`
33+
#' @param LAPPLY.FUN _function_. `lapply()`-like function to use for iteration during `extract`
34+
#' phase. Only used if `extract=TRUE`. This allows for the `utils::unzip()` operations to be run
35+
#' in parallel instead of sequential, custom progress reporting, or similar.
36+
#' @param LAPPLY.FUN.ARGS _list_. Optional list of additional arguments to pass to `LAPPLY.FUN`.
3637
#' @param remove_zip _logical_. Remove ZIP files after extracting? Default: `FALSE`
3738
#' @param overwrite _logical_. Overwrite by re-extracting if directory already exists? Default:
3839
#' `FALSE`
@@ -51,17 +52,17 @@
5152
#' have prefix `soilmu_` (mapunit), `soilsa_` (survey area), `soilsf_` (special features). There
5253
#' will also be a TXT file with prefix `soilsf_` describing any special features. Shapefile names
5354
#' then have an `a_` (polygon), `l_` (line), `p_` (point) followed by the soil survey area symbol.
54-
#' When `db="STATSGO"` the `WHERE` argument is not supported. Allowed `areasymbols` include
55-
#' `"US"` and two-letter state codes e.g. `"WY"` for the Wyoming general soils map.
55+
#' When `db="STATSGO"` the `WHERE` argument is not supported. Allowed `areasymbols` include `"US"`
56+
#' and two-letter state codes e.g. `"WY"` for the Wyoming general soils map.
5657
#'
57-
#' As in `createSSURGO()`, the `include_spatial` and `include_tabular` arguments either take a
58-
#' logical value (default `TRUE`) or a character vector of the specific table names to include. Note
59-
#' that when used in `downloadSSURGO()` the required metadata files are _always_ extracted to
60-
#' facilitate mapping to user-facing table names. These arguments allow for customizing the files
61-
#' that get extracted from ZIP files, not just filtering on file names (as is implemented with
62-
#' pre-existing `pattern` argument). This can dramatically improve efficiency of extraction and the
63-
#' overall size of the data in `exdir`. These arguments can be used in conjunction with the
64-
#' `pattern` argument to fine-tune the files included in the generated snapshot database.
58+
#' As in `createSSURGO()`, the `include_spatial` and `include_tabular` arguments either take a
59+
#' logical value (default `TRUE`) or a character vector of the specific table names to include.
60+
#' Note that when used in `downloadSSURGO()` the required metadata files are _always_ extracted to
61+
#' facilitate mapping to user-facing table names. These arguments allow for customizing the files
62+
#' that get extracted from ZIP files, not just filtering on file names (as is implemented with
63+
#' pre-existing `pattern` argument). This can dramatically improve efficiency of extraction and
64+
#' the overall size of the data in `exdir`. These arguments can be used in conjunction with the
65+
#' `pattern` argument to fine-tune the files included in the generated snapshot database.
6566
#'
6667
#' @return _character_. Paths to downloaded ZIP files (invisibly). May not exist if `remove_zip =
6768
#' TRUE`.
@@ -75,6 +76,8 @@ downloadSSURGO <- function(WHERE = NULL,
7576
include_tabular = TRUE,
7677
db = c('SSURGO', 'STATSGO'),
7778
extract = TRUE,
79+
LAPPLY.FUN = lapply,
80+
LAPPLY.FUN.ARGS = NULL,
7881
remove_zip = FALSE,
7982
overwrite = FALSE,
8083
quiet = FALSE) {
@@ -139,8 +142,7 @@ downloadSSURGO <- function(WHERE = NULL,
139142
dir.create(exdir, recursive = TRUE)
140143
}
141144

142-
res <- lapply(seq_along(paths2), function(i) {
143-
ssa <- gsub(".*wss_SSA_(.*)_.*", "\\1", paths2[i])
145+
UNZIP.FUN <- function(i) {
144146
if (isTRUE(include_spatial) && isTRUE(include_tabular)) {
145147
lz <- NULL
146148
} else {
@@ -150,18 +152,24 @@ downloadSSURGO <- function(WHERE = NULL,
150152
"^(mstab|mdstattabs|MetadataTable|mstabcol|mdstattabcol|MetadataColumnLookup|msidxdet|mdstatidxdet|MetadataIndexDetail)$",
151153
tools::file_path_sans_ext(basename(lz))
152154
)], exdir = exdir)
153-
inv <- .inventory_ssurgo_files(lz, exdir = exdir, include_spatial = include_spatial, include_tabular = include_tabular)
155+
156+
# explicitly fetch internal function our namespace to support parallel workers
157+
INV.FUN <- get(".inventory_ssurgo_files", envir = asNamespace("soilDB"))
158+
inv <- INV.FUN(lz, exdir = exdir, include_spatial = include_spatial, include_tabular = include_tabular)
159+
154160
lz <- unlist(c(inv$f.shp.sc, inv$f.txt.grp))
155161
}
156162
uz <- utils::unzip(paths2[i], files = lz, exdir = exdir)
157-
if ((!dir.exists(file.path(exdir, ssa)) || overwrite) && length(uz) == 0) {
163+
if (length(uz) == 0) {
158164
message(paste('Invalid zipfile:', paths2[i]))
159165
} else {
160166
if (!quiet) {
161167
message("Extracted: ", paths2[i])
162168
}
163169
}
164-
})
170+
}
171+
172+
res <- do.call(LAPPLY.FUN, c(list(seq_along(paths2), UNZIP.FUN), LAPPLY.FUN.ARGS))
165173

166174
if (remove_zip) {
167175
file.remove(paths2)
@@ -182,15 +190,15 @@ downloadSSURGO <- function(WHERE = NULL,
182190
#' you encounter issues using specific DBI connection types, please report in the soilDB issue
183191
#' tracker.
184192
#'
185-
#' @param filename _character_. Output file name (e.g. `'db.sqlite'` or `'db.gpkg'`). Only used when `con` is not
186-
#' specified by the user.
187-
#' @param exdir _character_. Path containing containing input SSURGO spatial (.shp) and tabular (.txt) files,
188-
#' downloaded and extracted by `downloadSSURGO()` or similar.
193+
#' @param filename _character_. Output file name (e.g. `'db.sqlite'` or `'db.gpkg'`). Only used when
194+
#' `con` is not specified by the user.
195+
#' @param exdir _character_. Path containing containing input SSURGO spatial (.shp) and tabular
196+
#' (.txt) files, downloaded and extracted by `downloadSSURGO()` or similar.
189197
#' @param conn A _DBIConnection_ object. Default is a `SQLiteConnection` used for writing .sqlite or
190198
#' .gpkg files. Alternate options are any DBI connection types. When `include_spatial=TRUE`, the
191199
#' sf package is used to write spatial data to the database.
192-
#' @param pattern _character_. Optional regular expression to use to filter subdirectories of `exdir`.
193-
#' Default: `NULL` will search all subdirectories for SSURGO export files.
200+
#' @param pattern _character_. Optional regular expression to use to filter subdirectories of
201+
#' `exdir`. Default: `NULL` will search all subdirectories for SSURGO export files.
194202
#' @param include_spatial _logical_ or _character_. Include spatial data layers in database?
195203
#' Default: `TRUE` inserts all spatial tables. If `include_spatial` is a _character_ vector
196204
#' containing table names, only that set are written to file. e.g. `include_spatial=c("mupolygon",
@@ -202,20 +210,19 @@ downloadSSURGO <- function(WHERE = NULL,
202210
#' descriptions are stored in table `"featdesc"` and metadata for each soil survey area are stored
203211
#' in `"soil_metadata"` tables.
204212
#' @param dissolve_field _character_. Dissolve geometries to create MULTIPOLYGON features? Column
205-
#' name
206-
#' specified is the grouping variable. Default: `NULL` does no aggregation, giving 1 `POLYGON`
207-
#' feature per delineation. `"mukey"` aggregates all related delineations within a soil survey
208-
#' area.
209-
#' @param maxruledepth _integer_. Maximum rule depth for `"cointerp"` table. Default `0` includes only
210-
#' shallowest ratings for smaller database size.
213+
#' name specified is the grouping variable. Default: `NULL` does no aggregation, giving 1
214+
#' `POLYGON` feature per delineation. `"mukey"` aggregates all related delineations within a soil
215+
#' survey area.
216+
#' @param maxruledepth _integer_. Maximum rule depth for `"cointerp"` table. Default `0` includes
217+
#' only shallowest ratings for smaller database size.
211218
#' @param overwrite _logical_. Overwrite existing layers? Default: `FALSE`
212219
#' @param append _logical_. Append to existing layers? Default: `FALSE`
213220
#' @param header _logical_. Passed to `read.delim()` for reading pipe-delimited (`|`) text files
214221
#' containing tabular data.
215222
#' @param quiet _logical_. Suppress messages and other output from database read/write operations?
216223
#' @param ... Additional arguments passed to `write_sf()` for writing spatial layers.
217224
#'
218-
#' @return Character. Vector of layer/table names in `filename`.
225+
#' @return _character_. Vector of layer/table names in `filename`.
219226
#' @seealso [downloadSSURGO()]
220227
#' @export
221228
#' @examples
@@ -294,7 +301,8 @@ createSSURGO <- function(filename = NULL,
294301
if ((missing(conn) || is.null(conn)) && !IS_GPKG) {
295302

296303
if (!requireNamespace("RSQLite")) {
297-
stop("package 'RSQLite' is required (when `conn` is not specified)", call. = FALSE)
304+
stop("package 'RSQLite' is required (when `conn` is not specified)",
305+
call. = FALSE)
298306
}
299307

300308
conn <- DBI::dbConnect(DBI::dbDriver("SQLite"),
@@ -306,7 +314,11 @@ createSSURGO <- function(filename = NULL,
306314
}
307315

308316
if (nrow(inv$shp.grp) >= 1 && ncol(inv$shp.grp) == 3 && include_spatial) {
309-
f.shp.grp <- split(inv$f.shp, list(feature = inv$shp.grp[, 1], geom = inv$shp.grp[, 2]), drop = TRUE)
317+
318+
f.shp.grp <- split(inv$f.shp,
319+
list(feature = inv$shp.grp[, 1],
320+
geom = inv$shp.grp[, 2]),
321+
drop = TRUE)
310322

311323
if (IS_DUCKDB) {
312324
DBI::dbExecute(conn, "INSTALL spatial; LOAD spatial;")

man/createSSURGO.Rd

Lines changed: 12 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)