Catalog search¶
The UmbraCatalog is the entry point for discovery: it searches Umbra's static
STAC catalog by area, date range, product type, place name, or polygon, and can
read through a local index for fast, repeatable, offline queries.
A full scan of the open archive should not walk the bucket and should not
page the hosted /search. Download the weekly snapshot with
umbra index fetch (flags --db, --url, --insecure-url, --if-changed,
and --force; --force only with --if-changed) from
catalog.db,
then search it with umbra search --local. The local index
page says what each flag does.
catalog.sha256
is the GNU sha256sum manifest beside that file. umbra index fetch checks
catalog.db against it when the manifest is present, and warns and continues
when the manifest is missing.
UmbraCatalog
¶
UmbraCatalog(bucket=S3_BUCKET, region=S3_REGION, session=None, *, token=None, archive_url=CANOPY_ARCHIVE_URL, collections=None)
Client for searching Umbra SAR data.
By default this searches Umbra's open data by crawling the public S3
bucket (a static STAC catalog with no search API). Pass a Canopy token
and the same :meth:search interface instead queries Umbra's
authenticated commercial archive over its real STAC API
(:data:~umbra_py.constants.CANOPY_ARCHIVE_URL)::
# open data (default) -- no account needed
UmbraCatalog().search(area="Centerfield", limit=5)
# commercial archive -- same call, one extra argument
UmbraCatalog(token="...").search(bbox=bbox, start="2024", limit=5)
Both paths yield :class:~umbra_py.UmbraItem objects, so every downstream
verb (download, quicklook, change, chips, ...) works unchanged against
either archive. That is the funnel made literal: a user onboarded on the
free bucket is already holding the tool they'd use as a paying customer.
Get a token from https://docs.canopy.umbra.space/.
search
¶
search(*, bbox=None, intersects=None, start=None, end=None, product_types=None, area=None, fuzzy=False, polarizations=None, min_incidence=None, max_incidence=None, max_resolution=None, canonical=None, limit=None, max_per_task=None)
Yield items matching the filters.
Parameters¶
bbox:
(min_lon, min_lat, max_lon, max_lat) footprint filter.
intersects:
A polygon geometry (the exterior-ring form from
:func:umbra_py._geometry.parse_geometry); keep only items whose
footprint intersects it. A tighter spatial filter than the
rectangular bbox -- the standard STAC intersects. Combines
with bbox (both must match) when both are given.
start, end:
Inclusive acquisition-date bounds. Accepts date /
datetime objects or ISO YYYY-MM-DD strings. The walker
still has to list each task to discover what's published in
range. Named tasks/ is tens of directories; task-data/
is thousands of UUID directories, so an unconstrained live
walk is slow -- provide limit to stop as soon as you have
enough, and prefer a local index for repeats.
product_types:
Keep only items exposing at least one of these assets
(e.g. ["GEC"]).
area:
Case-insensitive substring matched against each
sar-data/tasks/<task>/ or sar-data/task-data/<id>/
directory name. Named campaigns (e.g. "Centerfield, Utah")
live under tasks/; UUID collects live under task-data/.
area="centerfield" returns just that named site's
acquisitions. Non-matching task directories are skipped
before they're listed, so this also makes the search much
faster -- the ergonomic way to gather the co-located passes a
change composite needs. Place / bbox search is what finds a
UUID collect whose directory name is not a place.
fuzzy:
Widen area from a literal substring to a deterministic
token-wise fuzzy match (:func:umbra_py.fuzzy.task_matches):
word-order- and punctuation-independent, tolerant of a small
typo, and a strict superset of the substring match (it never
drops a result). So area="utah centerfield" or
area="centrfield" still reaches "Centerfield, Utah".
No model call -- the C1 deterministic first step.
polarizations:
Keep only items exposing at least one of these polarizations
(case-insensitive, e.g. ["VV"]) -- the SAR-native filter that
keeps a change comparison like-with-like (HH and VV image different
physics). An item with no polarization metadata is excluded.
min_incidence, max_incidence:
Inclusive bounds (degrees) on the view incidence angle
(:attr:UmbraItem.incidence_angle). An item with no incidence
metadata is excluded when either bound is set.
max_resolution:
Keep only items at least this fine -- both range and azimuth
resolution <= max_resolution metres. An item missing either
resolution value is excluded. See :meth:UmbraItem.matches_filters
for the exact acquisition-property semantics (a set filter excludes
items lacking that property, matching the STAC Query extension).
canonical:
True keeps items that are the canonical copy of a republished
collect; False keeps only the other copies. None (the
default) keeps every item. A live sidecar has not been linked, so
it counts as canonical until an index build stamps the field.
See :mod:umbra_py.duplicates.
limit:
Stop after yielding this many items.
max_per_task:
Cap the number of items yielded from any one
sar-data/tasks/<task>/ or sar-data/task-data/<id>/
directory. Each task is a tasking campaign over the same
area, so max_per_task=1 swaps the usual "every revisit of
a few sites" output for "one acquisition per distinct site"
-- much better diversity on a map.
Notes¶
When this catalog was created with a Canopy token, the search runs
against Umbra's commercial STAC API instead of the open bucket. The
filters mean the same thing; bbox and the date bounds are sent to
the API, while product_types and area/fuzzy are applied to
the returned items (exactly as they are on the open-bucket path), so the
interface is identical across both archives.
has_matching_task
¶
Whether area matches a task directory (or a Canopy item).
Directory names only: a hit does not download a sidecar. The search
that follows lists the prefixes again. A Canopy catalog has no task
directories, so it asks :meth:search for one item.
get_item
¶
Fetch a single acquisition from the Canopy commercial archive by id.
The keyed-retrieval complement to :meth:search's listing: given a STAC
item id, return that one :class:~umbra_py.UmbraItem, or None when the
archive has no such item. It is implemented with the STAC API ids
search extension over the same /archive/search endpoint
:meth:search already POSTs to -- POST {"ids": [item_id], "limit": 1}
-- so it introduces no new endpoint to guess and stays offline-testable
against a mocked API, exactly like the search path. Bearer auth, the
helpful 401/403 "token rejected" message and the 500 wrap are all
inherited from :meth:_archive_page.
Requires a Canopy token. The open bucket is a static catalog with no
id-to-item index, so a keyed lookup isn't meaningful there -- resolve an
open-data item from its sidecar URL instead
(:meth:UmbraItem.from_dict / umbra info <url>) or from a built index
(:meth:umbra_py.CatalogIndex.get).