Skip to content

Commit 96b5b60

Browse files
committed
Merge origin dev
2 parents 993337a + ec49751 commit 96b5b60

19 files changed

Lines changed: 403 additions & 63 deletions

DESCRIPTION

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Package: rstac
22
Title: Client Library for SpatioTemporal Asset Catalog
3-
Version: 1.0.0
3+
Version: 1.0.1
44
Authors@R:
55
c(person("Rolf", "Simoes",
66
email = "rolfsimoes@gmail.com",
@@ -38,7 +38,14 @@ Imports:
3838
Suggests:
3939
lifecycle,
4040
testthat,
41-
knitr
41+
knitr,
42+
tmap,
43+
leaflet,
44+
stars,
45+
slider,
46+
ggplot2,
47+
purrr,
48+
dplyr
4249
Collate:
4350
'cql2-expr-funs.R'
4451
'cql2-types.R'
@@ -54,6 +61,7 @@ Collate:
5461
'assets-funs.R'
5562
'check-utils.R'
5663
'conformance-query.R'
64+
'collections-funs.R'
5765
'collections-query.R'
5866
'deprec-funs.R'
5967
'doc-funs.R'

NAMESPACE

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,10 @@ export(assets_rename)
174174
export(assets_select)
175175
export(assets_url)
176176
export(collections)
177+
export(collections_fetch)
178+
export(collections_length)
179+
export(collections_matched)
180+
export(collections_next)
177181
export(conformance)
178182
export(cql2_bbox_as_geojson)
179183
export(cql2_date)

NEWS.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
# rstac (development version)
2+
3+
# rstac 1.0.1 (Released 2024-07-18)
4+
5+
* Add support to `jpg` in `preview_plot()` function (#161)
6+
* Fix variable in filter expressions (non-standard evaluation) (#160)
7+
* Fix `limit` parameter as integer type in `stac_search()` and `items()`
8+
* Improve `items_reap()` documentation (#152)
9+
110
# rstac 1.0.0 (Released 2024-02-14)
211

312
* Add support to static catalogs;

R/assets-funs.R

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@
4343
#' @param progress a `logical` indicating if a progress bar must be
4444
#' shown or not. Defaults to `TRUE`.
4545
#'
46+
#' @param use_gdal a `logical` indicating if the file should be downloaded
47+
#' by GDAL instead httr package.
48+
#'
4649
#' @param download_fn a `function` to handle download of assets for
4750
#' each item to be downloaded. Using this function, you can change the
4851
#' hrefs for each asset, as well as the way download is done.
@@ -88,7 +91,10 @@
8891
#' Multiple expressions are combine with `AND` operator. Expressions can
8992
#' use `asset` helper functions (i.e. `asset_key()`, `asset_eo_bands()`,
9093
#' and `asset_raster_bands()`). Multiple expressions are combined with
91-
#' `AND` operator.
94+
#' `AND` operator. `assets_select()` uses non-standard evaluation to evaluate
95+
#' its expressions. That means users must escape any variable or call to
96+
#' be able to use them in the expressions. The escape is done by using
97+
#' `double-curly-braces`, i.e., `{{variable}}`.
9298
#'
9399
#' **WARNING:** Errors in the evaluation of expressions are
94100
#' considered as `FALSE`.
@@ -186,6 +192,7 @@ assets_download <- function(items,
186192
asset_names = NULL,
187193
output_dir = getwd(),
188194
overwrite = FALSE, ...,
195+
use_gdal = FALSE,
189196
download_fn = NULL) {
190197
# check output dir
191198
if (!dir.exists(output_dir))
@@ -201,6 +208,7 @@ assets_download.doc_item <- function(items,
201208
asset_names = NULL,
202209
output_dir = getwd(),
203210
overwrite = FALSE, ...,
211+
use_gdal = FALSE,
204212
create_json = FALSE,
205213
download_fn = NULL) {
206214
if (!is.null(asset_names)) {
@@ -213,7 +221,7 @@ assets_download.doc_item <- function(items,
213221
}
214222
items$assets <- lapply(
215223
items$assets, asset_download, output_dir = output_dir,
216-
overwrite = overwrite, ..., download_fn = download_fn
224+
overwrite = overwrite, use_gdal = use_gdal, download_fn = download_fn, ...
217225
)
218226
if (create_json) {
219227
file <- "item.json"
@@ -232,6 +240,7 @@ assets_download.doc_items <- function(items,
232240
asset_names = NULL,
233241
output_dir = getwd(),
234242
overwrite = FALSE, ...,
243+
use_gdal = FALSE,
235244
download_fn = NULL,
236245
create_json = TRUE,
237246
items_max = Inf,
@@ -253,7 +262,7 @@ assets_download.doc_items <- function(items,
253262
items$features[[i]] <- assets_download(
254263
items = items$features[[i]], asset_names = asset_names,
255264
output_dir = output_dir, overwrite = overwrite,
256-
create_json = FALSE, download_fn = download_fn, ...
265+
use_gdal = use_gdal, create_json = FALSE, download_fn = download_fn, ...
257266
)
258267
}
259268
if (create_json)
@@ -340,10 +349,7 @@ assets_select <- function(items, ..., asset_names = NULL, select_fn = NULL) {
340349
assets_select.doc_item <- function(items, ...,
341350
asset_names = NULL,
342351
select_fn = NULL) {
343-
exprs <- unquote(
344-
expr = as.list(substitute(list(...), env = environment())[-1]),
345-
env = parent.frame()
346-
)
352+
exprs <- as.list(substitute(list(...), env = environment()))[-1]
347353
init_length <- length(items$assets)
348354
if (!is.null(asset_names)) {
349355
asset_names <- intersect(names(items$assets), asset_names)
@@ -352,9 +358,10 @@ assets_select.doc_item <- function(items, ...,
352358
if (length(exprs) > 0) {
353359
if (!is.null(names(exprs)))
354360
.error("Select expressions cannot be named.")
355-
for (i in seq_along(exprs)) {
361+
for (expr in exprs) {
362+
expr <- unquote(expr = expr, env = parent.frame())
356363
sel <- map_lgl(names(items$assets), function(key) {
357-
select_eval(key = key, asset = items$assets[[key]], expr = exprs[[i]])
364+
select_eval(key = key, asset = items$assets[[key]], expr = expr)
358365
})
359366
items$assets <- items$assets[sel]
360367
}

R/assets-utils.R

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -56,19 +56,36 @@ select_exec <- function(key, asset, select_fn) {
5656
asset_download <- function(asset,
5757
output_dir,
5858
overwrite, ...,
59+
use_gdal = FALSE,
5960
download_fn = NULL) {
6061
if (!is.null(download_fn))
6162
return(download_fn(asset))
6263
# create a full path name
63-
path <- url_get_path(asset$href)
64-
out_file <- path_normalize(output_dir, path)
65-
dir_create(out_file)
66-
make_get_request(
67-
url = asset$href,
68-
httr::write_disk(path = out_file, overwrite = overwrite),
69-
...,
70-
error_msg = "Error while downloading"
71-
)
72-
asset$href <- path
64+
out_file <- path_normalize(output_dir, url_get_path(asset$href))
65+
out_dir <- dirname(out_file)
66+
if (!dir.exists(out_dir))
67+
dir.create(out_dir, recursive = TRUE)
68+
stopifnot(dir.exists(out_dir))
69+
if (use_gdal) {
70+
if (file.exists(out_file) && !overwrite)
71+
.error("File already exists. Use `overwrite=TRUE`.")
72+
if (file.exists(out_file))
73+
unlink(out_file)
74+
sf::gdal_utils(
75+
util = "translate",
76+
source = gdalvsi_append(asset$href),
77+
destination = out_file, ...
78+
)
79+
if (!file.exists(out_file)) {
80+
.error("Download failed. File: '%s'.", asset$href)
81+
}
82+
} else {
83+
make_get_request(
84+
url = asset$href,
85+
httr::write_disk(path = out_file, overwrite = overwrite),
86+
error_msg = "Error while downloading", ...
87+
)
88+
}
89+
asset$href <- out_file
7390
asset
7491
}

R/check-utils.R

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,14 @@ check_collection <- function(collection) {
7474
collection
7575
}
7676

77+
check_collections <- function(collections) {
78+
if (!is.list(collections) || is.null(names(collections)))
79+
.error("Invalid doc_collections object.")
80+
if (!"links" %in% names(collections))
81+
.error("Invalid doc_collections object. Expecting `links` key.")
82+
collections
83+
}
84+
7785
check_character <- function(x, msg, ...) {
7886
if (!is.character(x))
7987
.error(msg, ...)

R/collections-funs.R

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
#' @title Collections functions
2+
#'
3+
#' @description
4+
#' These functions provide support to work with
5+
#' `doc_collections`objects.
6+
#'
7+
#' \itemize{
8+
#' \item `collections_length()`: `r lifecycle::badge('experimental')`
9+
#' shows how many items there are in the `doc_items` object.
10+
#'
11+
#' \item `collections_matched()`: `r lifecycle::badge('experimental')`
12+
#' shows how many items matched the search criteria.
13+
#'
14+
#' \item `collections_fetch()`: `r lifecycle::badge('experimental')`
15+
#' request all STAC Items through pagination.
16+
#'
17+
#' \item `collections_next()`: `r lifecycle::badge('experimental')`
18+
#' fetches a new page from STAC service.
19+
#'
20+
#' }
21+
#'
22+
#' @param collections a `doc_collections` object.
23+
#'
24+
#' @param matched_field a `character` vector with the path
25+
#' where is the number of collections returned.
26+
#'
27+
#' @param progress a `logical` indicating if a progress bar must be
28+
#' shown or not. Defaults to `TRUE`.
29+
#'
30+
#' @param ... additional arguments. See details.
31+
#'
32+
#' @details
33+
#' Ellipsis argument (`...`) appears in different items functions and
34+
#' has distinct purposes:
35+
#'
36+
#' \itemize{
37+
#' \item `collections_fetch()` and `collections_next()`: ellipsis is used to
38+
#' pass additional `httr` options to [GET][httr::GET] method, such as
39+
#' [add_headers][httr::add_headers] or [set_cookies][httr::set_cookies].
40+
#'
41+
#' }
42+
#'
43+
#' @return
44+
#'
45+
#' \itemize{
46+
#' \item `collections_length()`: an `integer` value.
47+
#'
48+
#' \item `collections_matched()`: returns an `integer` value if the STAC web
49+
#' server does support this extension. Otherwise returns `NULL`.
50+
#'
51+
#' \item `collections_fetch()`: a `doc_items` with all matched items.
52+
#'
53+
#' \item `collections_next()`: fetches a new page from STAC service.
54+
#'
55+
#' }
56+
#'
57+
#' @examples
58+
#' \dontrun{
59+
#' # doc_items object
60+
#' stac("https://cmr.earthdata.nasa.gov/stac/LPCLOUD") |>
61+
#' collections() |>
62+
#' get_request() |>
63+
#' collections_fetch()
64+
#' }
65+
#'
66+
#' @name collections_functions
67+
NULL
68+
69+
70+
71+
#' @rdname collections_functions
72+
#'
73+
#' @export
74+
collections_next <- function(collections, ...) {
75+
check_collection(collections)
76+
# get url of the next page
77+
rel <- NULL
78+
next_link <- links(collections, rel == "next")
79+
if (length(next_link) == 0)
80+
.error("Cannot get next link URL.", class = "next_error")
81+
next_link <- next_link[[1]]
82+
res <- make_get_request(
83+
url = next_link$href,
84+
headers = next_link$headers,
85+
...,
86+
error_msg = "Error while requesting next page"
87+
)
88+
content <- content_response_json(res)
89+
# return items
90+
doc_collections(content)
91+
}
92+
93+
#' @rdname collections_functions
94+
#'
95+
#' @export
96+
collections_matched <- function(collections, matched_field) {
97+
check_collections(collections)
98+
matched <- NULL
99+
if (is.character(matched_field) && matched_field %in% names(collections))
100+
matched <- as.numeric(collections[[matched_field]])
101+
matched
102+
}
103+
104+
#' @rdname collections_functions
105+
#'
106+
#' @export
107+
collections_length <- function(collections) {
108+
check_collections(collections)
109+
return(length(collections$collections))
110+
}
111+
112+
#' @rdname collections_functions
113+
#'
114+
#' @export
115+
collections_fetch <- function(collections, ...,
116+
progress = TRUE,
117+
matched_field = NULL) {
118+
check_collections(collections)
119+
matched <- collections_matched(collections, matched_field)
120+
# verify if progress bar can be shown
121+
progress <- progress &
122+
(!is.null(matched) && (collections_fetch(collections) < matched))
123+
if (progress) {
124+
pb <- utils::txtProgressBar(
125+
min = collections_length(collections),
126+
max = matched,
127+
style = 3
128+
)
129+
# close progress bar when exit
130+
on.exit({
131+
if (progress) {
132+
utils::setTxtProgressBar(pb, matched)
133+
close(pb)
134+
}
135+
})
136+
}
137+
# Initialize the items
138+
next_collections <- collections
139+
while (TRUE) {
140+
# check if features is complete
141+
if (!is.null(matched) && (collections_length(collections) == matched))
142+
break
143+
# protect against infinite loop
144+
if (!is.null(matched) && (collections_length(collections) > matched))
145+
.error(paste(
146+
"Length of returned collections (%s) is different",
147+
"from matched collections (%s)."),
148+
collections_length(collections), matched)
149+
next_collections <- tryCatch({
150+
collections_next(next_collections, ...)
151+
}, next_error = function(e) NULL)
152+
if (is.null(next_collections))
153+
break
154+
collections$collections <- c(collections$collections,
155+
next_collections$collections)
156+
# update progress bar
157+
if (progress)
158+
utils::setTxtProgressBar(pb, length(next_collections))
159+
}
160+
collections
161+
}

0 commit comments

Comments
 (0)