Changelog
Source:NEWS.md
usgsm2m (development version)
Breaking changes
-
The package is now called
usgsm2m, renamed fromUSGSm2m. The old name read as though the package were issued by the USGS, which it is not. Updatelibrary()calls, and reinstall from https://github.com/stevenpawley/usgsm2m.Scene list ids generated by the package are now prefixed
usgsm2m_rather thanUSGSm2m_. Lists created under the old prefix are unaffected and can still be retrieved by passing their id toM2MSession$scene_list().
New
-
M2MSession$remove_items()andM2MDownloadQueue$remove_items()remove individual downloads from the queue, whereM2MDownloadQueue$cancel()only drops a whole order. Ids come from thedownloadIdcolumn of$downloads(),$ready()or$pending().The API removes one item per request and answers the same way for an id that does not exist, so a successful call is not evidence that anything was removed - check
$downloads()afterwards. -
M2MSceneSearch$combine()merges searches, so one$products()call and one download order can cover scenes found by several queries — two date ranges, say, or two areas:summer <- july$combine(sept) summer$products()Like
$filter()it returns a new search and leaves the originals alone, takes several at once, and chains. Scenes found by more than one search are kept once. Previously this meant passing the samelist_idto$scene_list()from each search in turn and relying on the API appending, which was easy to get wrong and gave no hint that it was accumulating. $select_products()now matches exactly, againstproductNameorproductCode, rather than as a pattern. It previously used a regular expression, so a name copied straight out of$scene_products()could match nothing:"Full-Resolution Browse (Natural Color) GeoTIFF"is present in the tibble, but its parentheses were read as regex groups. Chain$filter()for looser selection, e.g.$select_products()$filter(grepl("Bundle", productName)).$select_bands()matches literal substrings rather than regular expressions, so"B4"still picks out..._SR_B4but punctuation in a value is no longer read as pattern syntax.Both selectors now warn when nothing matches, listing what was available. Product names are not standardised across the catalogue — the bundle is “Landsat Collection 2 Level-2 Product Bundle” for
landsat_ot_c2_l2but “Standard Format” forcorona2— so a value carried over from another dataset used to select an empty set silently, surfacing later as an unexplained “No products selected”.Authorisation failures now raise a
m2m_no_accesscondition, a subclass ofm2m_api_error, so they can be caught separately from other API errors.
Bug fixes
M2MSceneSearch$products()no longer fails on large orders withCan't combine \secondaryDownloads[[1]]value`and `secondaryDownloads[[841]]value` . The API reports a file's optionalchecksumobject unevenly - present for most files, with a null value or absent entirely for others - and the column was typed from whichever form appeared first, so the per-scene tables could not be combined. Nested objects in API records are now hoisted into scalar columns named after their path, sochecksumappears aschecksum_algorithmandchecksum_valuein$bands(), withNA` where the API gave no value.Downloaded files now keep the extension the server gives them, taken from the
Content-Dispositionheader or the URL served. Scene-level products such as a Level-2 Product Bundle were written out named after the scene’sentityId, which has no extension at all, leaving a.taron disk that nothing would open.A download that fails no longer leaves a file behind holding the error body.
Asking for a dataset the account cannot access reported it as missing:
"Dataset not found: Dataset status is unavailable to this user". The dataset exists and is spelled correctly, so this sent people hunting for a typo. Such failures now say that access is the problem and point at$find_datasets(), which lists exactly the datasets the account can use.Proxied downloads could not be retrieved at all. USGS serves some products from another host (
landsatlook.usgs.gov, for instance) on a signed URL, and lists those under$requestedeven though they carry a working URL. The queue treated everything in$requestedas still being prepared, so$is_ready()never becameTRUEand$retrieve()refused with “No downloads are available yet”. Readiness is now a matter of having a URL, via the new$ready()and$pending()methods.The M2M session token was sent to whichever host a download URL pointed at. Proxied URLs are self-authenticating and ignore it, so this handed the token to hosts that neither needed nor asked for it. It is now sent only to the M2M API’s own host.
Proxied downloads are now reported back through
download-complete-proxiedonce fetched. The API does not serve them itself and so cannot observe the transfer; without this they stay in the download queue indefinitely.An expired signed URL is reported as
status = "expired"rather than a bare failure, since the fix is to$refresh()and retry.
usgsm2m 0.2.0
The package is now organised around R6 objects rather than a flat set of functions. This is a breaking change: every exported ers_* function has been removed. See “Migrating from 0.1.0” below.
The pipeline
Getting data out of the M2M API takes several steps that have to happen in a particular order. That order is now expressed by the objects themselves, each stage returning an object whose methods are the valid next steps:
M2MSession → M2MDataset → M2MSceneSearch → M2MDownloadOptions → M2MDownloadQueue
Tab-completing $ shows only what you can legitimately do from where you are, and every object prints a Next: hint. A whole download now reads as one chain:
sess <- m2m_session()
queue <- sess$
dataset("landsat_ot_c2_l2")$
search(
spatial = filter_spatial(ll_lon = -120, ll_lat = 40, ur_lon = -119.5, ur_lat = 40.5),
temporal = filter_temporal("2020-07-01", "2020-07-31"),
cloud = filter_cloud(0, 50)
)$
products()$
select_bands(c("B4", "B5"))$
filter(bulkAvailable)$
request(label = "ndvi_july_2020")
queue$retrieve("data/")Scene lists are now created for you. The M2M API requires one server-side before download options can be listed; $products() handles that, where previously you had to wire ers_scene_list_add() and ers_scene_products() together by hand and pass a list_id between them.
Migrating from 0.1.0
| 0.1.0 | 0.2.0 |
|---|---|
ers_session() |
m2m_session() |
ers_logout(s) |
s$logout() |
ers_dataset(s, dataset_name = x) |
s$dataset(x) |
ers_dataset_search(s, x) |
s$find_datasets(x) |
ers_dataset_filters(s, x) |
s$dataset(x)$filters() |
ers_scene_search(s, x, ...) |
s$dataset(x)$search(...) |
ers_scene_list_add(s, x, scenes, id) |
search$scene_list() |
ers_scene_list_get(s, id) |
list$scenes() |
ers_scene_list_summary(s, id) |
list$summary() |
ers_scene_list_remove(s, id) |
list$remove() |
ers_scene_products(s, x, id, ...) |
search$products() |
ers_download_request(s, p, label = l) |
products$request(label = l) |
ers_download_queue(s, label = l) |
s$download_queue(l) |
ers_download_search(s) |
s$downloads() |
ers_download_retrieve(s, r, l, dir) |
queue$retrieve(dir) |
ers_download_eula(s, code) |
s$eula(code) |
ers_download_remove_order(s, l) |
queue$cancel() |
ers_download_remove_items(s, ids) |
(no direct equivalent; use queue$cancel()) |
filter_spatial(), filter_temporal() and filter_cloud() are unchanged.
The filter() S3 method for scene_products objects has been removed; M2MDownloadOptions$filter() replaces it.
New
-
filter_metadata_value(),filter_metadata_between(),filter_metadata_and()andfilter_metadata_or()build metadata filters from the field ids thatM2MDataset$filters()reports. Those ids could be listed in 0.1.0 but there was nothing to turn them into a filter with. -
M2MDownloadOptions$scene_products()and$select_products()expose whole products — a Level-1 Product Bundle (one.tarper scene) or a full-resolution browse image — alongside the individual band files that$bands()lists. Browse products have no constituent files and so were previously unreachable entirely. -
M2MDataset$related_scenes()wrapsscene-search-secondary. Note that few datasets define a secondary relationship; the rest raiseDATASET_ERROR. -
M2MSession$download_labels()lists your orders, so an order can be found again without remembering its label. -
M2MDownloadQueue$summary()breaks an order down by dataset, and$prepare()moves a staged order into the queue for processing. -
M2MSceneList$dataset()reports which dataset a scene list holds, looking it up from the list’s summary when reattaching to a list by id and caching the answer.M2MSession$scene_list()also takes an optionaldataset_nameto skip that lookup, or to choose one when a list spans several datasets. - A usage vignette,
vignette("usgsm2m").
Errors
API failures now raise conditions of class m2m_error, with m2m_http_error, m2m_api_error and m2m_not_found subclasses, instead of printing a message and returning NULL. A failed step stops the chain where it failed rather than surfacing later as an unrelated error:
Bug fixes
Seven data-coercion bugs were found and fixed by testing against the live API across 20+ datasets. Several produced plausible but wrong data rather than failing, so results obtained with 0.1.0 are worth re-checking:
- Scene metadata was reduced to a single row of deparsed R code (
"c(\"5e83d150...\", ...)") whenever a search spanned scenes with differing metadata field counts — the common case in any multi-scene search. Uniform searches took a different code path and worked, which hid this. - Every file was queued four times. The API repeats a scene’s
secondaryDownloadsunder each of its product entries, and flattening them did not de-duplicate. -
download-optionsfailed outright on any dataset mixing products that have bands with products that do not, affectinglandsat_tm_c2_l1,landsat_mss_c2_l1andeo1_ali_pubamong others. - Dataset searches repeated a dataset once per catalog it belongs to. The same unnesting also mishandled null fields, which could drop a dataset’s row or fail outright when it had nothing else to expand against.
-
dataset-filtersrepeated a Select field once per permitted value, giving 22 rows for 12 fields. -
scene-list-addreturned HTTP 500 for any single-scene list: JSON serialisation turned a length-1entityIdsvector into a bare string instead of an array. The same fault is fixed foreulaCodes. -
scene-list-summaryreturned a column set that varied with the data —listTimeoutvanished when null,invalidSceneCountwas never returned, andinvalidSceneschanged type depending on whether any existed. An unknown or expired list also raised a confusing “Columncoordinatesdoesn’t exist” from dplyr, rather than summarizing as empty.