STAC Subpackage#
The pyramids.stac subpackage connects pyramids to the
SpatioTemporal Asset Catalog ecosystem — searching STAC
APIs, reading assets as pyramids Dataset / NetCDF objects, building cubes,
signing cloud credentials, and writing STAC Items back out. Everything is
GDAL-native (no rasterio, no labeled-array stack) and duck-typed over the STAC
Item/Asset contract, so pystac is never imported by pyramids itself — raw STAC
JSON dicts work as well as pystac.Item objects.
Module layout#
classDiagram
class Signer {
<<protocol>>
+name
+sign_request(request)
+sign_item(item)
+sign_href(href) str
+gdal_env() dict
}
class AnonymousSigner
class AWSRequesterPaysSigner
class BearerTokenSigner
Signer <|.. AnonymousSigner
Signer <|.. AWSRequesterPaysSigner
Signer <|.. BearerTokenSigner
Public API at a glance#
| Concern | Symbol | Page |
|---|---|---|
| Open a STAC API client | open_client |
Client & search |
| Typed item search | search (bbox/intersects/datetime/query/CQL2) |
Client & search |
Read one asset → Dataset/NetCDF |
load_asset · which_engine · resolved_href |
Assets |
Read proj/raster/eo metadata (no file open) |
read_extension_metadata |
Assets |
| Mosaic one asset across items (lazy VRT) | build_vrt_from_stac |
Assets |
| Item search, un-executed (hit count, paging) | item_search |
Client & search |
| Discover collections | list_collections · search_collections · get_queryables |
Search |
Prefer an alternate-assets href |
preferred_asset_href · asset_alternate_href |
Assets |
| Prefer an href and learn its media type | preferred_asset_source |
Assets |
| Verify an asset is reachable & well-typed | verify_asset |
Assets |
| Windowed read from an Item | read_item_part · read_item_preview · read_item_point |
Assets |
Download assets ([stac] extra) |
download_item · download_item_collection |
Assets |
Serialize items ↔ GeoParquet ([parquet] extra) |
to_geoparquet · from_geoparquet |
Assets |
| Interoperable stac-geoparquet 1.1 layout | to_geoparquet_spec · from_geoparquet_spec |
Assets |
| Cloud credentials (generic signers) | Signer protocol + concrete signers |
Signers |
Two STAC entry points live on the raster classes (documented there):
DatasetCollection.from_stac(items, asset, *, patch_url, bbox, max_items, signer, groupby, alignment, tolerance, fusion, read, provenance)— build a time-stacked cube from STAC items (single asset → time stack, or a list of assets → band axis).groupbytakes"solar_day","id","time", any item property key, or a callable, and each group is mosaicked withGroupFusion(method=...)(first/last/min/max/sum/count/mean) or a customGroupFusion(fuse_func=...).Alignment(grid=...)matches a target grid,AssetRead(rescale=True)returns physical units,AssetRead(cfg=...)supplies missing metadata and band aliases, andReadTolerance(errors_as_nodata=True)fills a nodata plane for an unreadable asset instead of losing the cube. See DatasetCollection.DatasetCollection.from_point(lat, lon, *, collection, bands, start_date, end_date, edge_size, resolution)— a cubo-style point + edge-size convenience cube. See DatasetCollection.Dataset.to_stac_item(item_id, *, asset_href, …, bands, footprint, extensions)— describe a raster as a STAC Item dict, withBandMetadatachoosing how much per-band metadata is published,Footprinthow the geometry and bbox are derived, andStacExtensionswhich extension blocks (proj,raster) are stamped. See I/O.
Install#
open_client / search need the [stac] extra (which also bundles the optional
asset downloader stac-asset); read_extension_metadata, load_asset,
build_vrt_from_stac, to_stac_item, and the signers need only core pyramids:
pip install 'pyramids-core[stac]' # client/search + download_item
pip install 'pyramids-core[stac,parquet]' # + GeoParquet round-trip
See the STAC tutorial for an end-to-end walkthrough, the offline STAC notebook, and the live-endpoint notebooks for Earth Search (anonymous) and the Planetary Computer (signed).
Option objects#
The frozen, all-defaulted groups the two entry points take. Each is re-exported
from pyramids.stac, so from pyramids.stac import Alignment, Footprint is the
import to use.
pyramids.dataset.stac_options
#
Grouped option objects for the two public STAC entry points.
:meth:pyramids.dataset.Dataset.to_stac_item and
:meth:pyramids.dataset.DatasetCollection.from_stac each grew past twenty
keyword arguments, most of which are only meaningful next to two or three of
their neighbours. The options that travel together are gathered here, one
frozen dataclass per group, so each entry point takes one object per concern
instead of a flat list of flags.
Every dataclass defaults every field, so each is optional at the call site and
None means "all defaults for that group": to_stac_item(ds, "id",
asset_href=...) and from_stac(items, "red") are unchanged.
The module is a leaf: it imports nothing from pyramids at run time (only
numpy and the standard library), which is what lets
:mod:pyramids.dataset._stac import it at module load without reviving the
dataset ↔ stac import cycle. It lives under :mod:pyramids.dataset for
the same reason :mod:pyramids.dataset._stac does — importing
pyramids.stac.anything initialises the :mod:pyramids.stac package, whose
__init__ imports :mod:pyramids.dataset — and every name here is
re-exported from :mod:pyramids.stac, which is where callers should import it
from.
Group behaviour that has to touch GDAL (measuring a band, resolving a
footprint) stays in :mod:pyramids.dataset._stac beside the helpers it calls;
what lives here is each group's data and the validation that data can answer on
its own.
BandMetadata
dataclass
#
How much per-band metadata an Item's asset pays to publish.
with_stats, with_histogram, histogram_bins and stats_approx_ok are
born together, are forwarded as a block and are read at a single point per
band; with_eo joins them because it is the third opt-in band block. They
share one property no other
:meth:~pyramids.dataset.Dataset.to_stac_item option has — each asks GDAL
to look at the raster's bands, and each degrades by omitting its field
with a warning rather than failing the Item — so they are one concept: how
much band measuring this Item pays for.
Attributes:
| Name | Type | Description |
|---|---|---|
with_stats |
bool
|
Add a per-band |
with_histogram |
bool
|
Add a per-band |
histogram_bins |
int
|
Number of histogram buckets when |
with_eo |
bool
|
Add |
stats_approx_ok |
bool
|
Let GDAL answer |
Examples:
- The default group measures nothing, so an Item costs no band reads:
- Opt into statistics and a 32-bucket histogram, and ask for exact figures:
- Hand the group to
to_stac_itemand read the blocks it published:>>> import numpy as np >>> from pyramids.dataset import Dataset, GeoReference >>> from pyramids.stac import BandMetadata >>> ds = Dataset.from_array( ... np.array([[1.0, 2.0], [3.0, 4.0]], dtype="float32"), ... geo_ref=GeoReference(top_left_corner=(0.0, 2.0), cell_size=1.0, epsg=4326), ... ) >>> item = ds.to_stac_item( ... "scene-1", ... asset_href="scene.tif", ... datetime="2023-06-01T00:00:00Z", ... bands=BandMetadata(with_stats=True, with_eo=True), ... ) >>> stats = item["assets"]["data"]["raster:bands"][0]["statistics"] >>> stats["minimum"], stats["maximum"], stats["mean"] (1.0, 4.0, 2.5) >>> item["assets"]["data"]["eo:bands"] [{'name': 'Band_1'}]
See Also
- StacExtensions: which extension blocks the Item declares at all.
- pyramids.dataset.Dataset.to_stac_item: the entry point that consumes this group.
Source code in src/pyramids/dataset/stac_options.py
Footprint
dataclass
#
How an Item's geometry and bbox are derived from a dataset's extent.
The six footprint* / geometry keywords of
:meth:~pyramids.dataset.Dataset.to_stac_item are born together, are never
read apart, and are consumed at a single point — the
one step that turns a dataset into (geometry, bbox). The two readings of
the mode, the no-EPSG fallback and the antimeridian split all read nothing
but this clump plus the dataset's own extent. precision belongs here
because the reprojected footprint's coordinates are the only thing it ever
rounds.
Attributes:
| Name | Type | Description |
|---|---|---|
mode |
str
|
|
band |
int
|
Zero-based band to footprint in |
max_samples |
int | None
|
Approximate pixel budget for the valid-pixel mask —
accuracy traded for speed — or |
simplify_tolerance |
float | None
|
Douglas-Peucker tolerance in degrees, applied to
the data footprint after reprojection, or |
densify |
float | None
|
Maximum segment length in native CRS units, applied to the
data footprint before reprojection so long edges follow the
projection's curvature; |
precision |
int
|
Decimal places for the reprojected footprint coordinates. |
Examples:
- The default group is the bounding rectangle at six decimals:
- Ask for the valid-pixel footprint, rounded to two decimals, and read the
geometry it produced — only the one valid pixel is covered, not the full grid:
>>> import numpy as np >>> from pyramids.dataset import Dataset, GeoReference >>> from pyramids.stac import Footprint >>> ds = Dataset.from_array( ... np.array([[1.0, np.nan], [np.nan, np.nan]], dtype="float32"), ... no_data_value=np.nan, ... geo_ref=GeoReference(top_left_corner=(0.0, 2.0), cell_size=1.0, epsg=4326), ... ) >>> item = ds.to_stac_item( ... "scene-1", ... asset_href="scene.tif", ... datetime="2023-06-01T00:00:00Z", ... footprint=Footprint(mode="data", precision=2), ... ) >>> item["bbox"] [0.0, 1.0, 1.0, 2.0] >>> item["geometry"]["coordinates"] [[[0.0, 2.0], [0.0, 1.0], [1.0, 1.0], [1.0, 2.0], [0.0, 2.0]]] - Any other mode is rejected on construction, before a dataset is touched:
See Also
- pyramids.dataset.Dataset.to_stac_item: the entry point that consumes this group.
Source code in src/pyramids/dataset/stac_options.py
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 | |
__post_init__()
#
Reject a mode that is neither "bbox" nor "data".
Raises:
| Type | Description |
|---|---|
ValueError
|
|
Source code in src/pyramids/dataset/stac_options.py
StacExtensions
dataclass
#
Which STAC extension blocks :meth:~pyramids.dataset.Dataset.to_stac_item stamps onto the Item.
Both flags answer the same question — which extension's fields are
populated and whose schema is appended to stac_extensions — and neither
reads anything the other does not, so they are one switchboard rather than
two unrelated booleans.
Attributes:
| Name | Type | Description |
|---|---|---|
with_proj |
bool
|
Populate the |
with_raster |
bool
|
Populate |
Examples:
- Both extensions are on by default:
- Dropping
rasterleaves the asset without araster:bandsblock while theprojfields stay:>>> import numpy as np >>> from pyramids.dataset import Dataset, GeoReference >>> from pyramids.stac import StacExtensions >>> ds = Dataset.from_array( ... np.array([[1.0, 2.0], [3.0, 4.0]], dtype="float32"), ... geo_ref=GeoReference(top_left_corner=(0.0, 2.0), cell_size=1.0, epsg=4326), ... ) >>> item = ds.to_stac_item( ... "scene-1", ... asset_href="scene.tif", ... datetime="2023-06-01T00:00:00Z", ... extensions=StacExtensions(with_raster=False), ... ) >>> item["stac_extensions"] ['https://stac-extensions.github.io/projection/v1.1.0/schema.json'] >>> "raster:bands" in item["assets"]["data"] False >>> item["properties"]["proj:epsg"] 4326 - With both off the Item declares no extension schemas at all:
>>> import numpy as np >>> from pyramids.dataset import Dataset, GeoReference >>> from pyramids.stac import StacExtensions >>> ds = Dataset.from_array( ... np.array([[1.0, 2.0], [3.0, 4.0]], dtype="float32"), ... geo_ref=GeoReference(top_left_corner=(0.0, 2.0), cell_size=1.0, epsg=4326), ... ) >>> item = ds.to_stac_item( ... "scene-1", ... asset_href="scene.tif", ... datetime="2023-06-01T00:00:00Z", ... extensions=StacExtensions(with_proj=False, with_raster=False), ... ) >>> item["stac_extensions"] [] >>> sorted(item["properties"]) ['datetime']
See Also
- BandMetadata: the opt-in band blocks that ride inside
raster:bands. - pyramids.dataset.Dataset.to_stac_item: the entry point that consumes this group.
Source code in src/pyramids/dataset/stac_options.py
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 | |
Alignment
dataclass
#
How :meth:~pyramids.dataset.DatasetCollection.from_stac puts an Item's assets on a common grid.
All three describe one decision — which grid the pixels land on and how
they get there. grid names the cube's output grid, align says whether a
mixed-resolution item may be resampled onto its first asset's grid at all,
and resampling picks the algorithm that resample uses; resampling
without align is meaningless, which is why they are one object.
Attributes:
| Name | Type | Description |
|---|---|---|
grid |
Grid | None
|
Optional :class: |
align |
bool
|
Multi-asset only. When |
resampling |
str | dict[str, str] | None
|
Multi-asset only. Which algorithm aligns mixed-resolution
assets onto the first requested asset's grid. |
Examples:
- The default group names no output grid and allows nearest-neighbour alignment of a multi-asset item:
- Pin the cube to an explicit output grid and read the grid back:
>>> from pyramids.dataset import Grid >>> from pyramids.stac import Alignment >>> alignment = Alignment(grid=Grid(crs=4326, resolution=1.0, bounds=(0.0, 0.0, 2.0, 2.0))) >>> alignment.grid.crs, alignment.grid.resolution (4326, 1.0) >>> alignment.grid.bounds (0.0, 0.0, 2.0, 2.0) >>> alignment.grid.is_empty False - Resample per asset, and refuse alignment entirely:
See Also
- ReadTolerance: whose substituted no-data plane borrows
gridwhen one is given. - pyramids.dataset.DatasetCollection.from_stac: the entry point that consumes this group.
Source code in src/pyramids/dataset/stac_options.py
ReadTolerance
dataclass
#
What :meth:~pyramids.dataset.DatasetCollection.from_stac does with an Item whose asset is not usable.
The two failures a build can forgive, and the pair the documentation contrasts: an asset the Item never declared, and one it declares but that will not open. Both answer "drop it or stop", both are independent of the other's answer, and both are read at the same points, so they belong together — while staying two fields, because the metadata gap and the IO failure are genuinely different events.
Attributes:
| Name | Type | Description |
|---|---|---|
skip_missing |
bool
|
When |
errors_as_nodata |
bool
|
Tolerate an asset that is present on the item but
cannot be opened (404, expired signed URL, unreachable host, a
file truncated badly enough that its header will not parse) by
substituting a no-data-filled ( Only the open is probed. A raster whose header parses but whose tiles are truncated opens cleanly and then fails later, on the lazy pixel read, by which point the cube is built and no plane can be substituted for it. So partial corruption is not covered, however the failure is spelled; a header that does not parse is. The plane needs a grid. The reference grid is
:attr: In single-asset mode this forces an eager open probe of every href (the hrefs themselves stay the collection's backing files, so reads remain lazy); a lazy build cannot discover an unreadable URL until it is too late to substitute anything. |
Examples:
- The default group forgives nothing, so either failure stops the build:
- Forgive both failures at once — the two fields stay independent:
- With
skip_missing, an item that never declares the asset key is dropped instead of raising, so the cube is one timestep shorter:>>> import os, tempfile >>> import numpy as np >>> from pyramids.dataset import Dataset, DatasetCollection, GeoReference >>> from pyramids.stac import ReadTolerance >>> folder = tempfile.mkdtemp() >>> items = [] >>> for index, key in enumerate(("data", "other")): ... path = os.path.join(folder, f"t{index}.tif") ... Dataset.from_array( ... np.full((2, 2), float(index + 1), dtype="float32"), ... geo_ref=GeoReference(top_left_corner=(0.0, 2.0), cell_size=1.0, epsg=4326), ... ).to_file(path) ... items.append( ... { ... "id": f"t{index}", ... "bbox": [0.0, 0.0, 2.0, 2.0], ... "properties": {"datetime": f"2023-06-0{index + 1}T00:00:00Z"}, ... "assets": {key: {"href": path}}, ... } ... ) >>> cube = DatasetCollection.from_stac( ... items, "data", tolerance=ReadTolerance(skip_missing=True) ... ) >>> cube.time_length 1 >>> cube.values[:, 0, 0].tolist() [1.0]
See Also
- Alignment: supplies the reference grid a substituted no-data plane lands on.
- pyramids.dataset.DatasetCollection.from_stac: the entry point that consumes this group.
Source code in src/pyramids/dataset/stac_options.py
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 | |
GroupFusion
dataclass
#
How a group's overlapping pixels resolve into one timestep.
method and fuse_func are two spellings of a single decision — the
overlap-resolution rule for a mosaicked group — which is why they are
mutually exclusive and why neither means anything without a groupby. One
object makes the exclusivity visible at the call site.
Attributes:
| Name | Type | Description |
|---|---|---|
method |
str
|
The overlap-resolution rule handed to
:func: |
fuse_func |
Callable[[ndarray, ndarray], None] | None
|
An odc-style in-place fuser |
Examples:
- The default group is the first-valid-pixel mosaic, with no custom fuser:
- A custom in-place fuser is kept as given and mutates its accumulator:
>>> import numpy as np >>> from pyramids.stac import GroupFusion >>> def keep_max(dst, src): ... np.fmax(dst, src, out=dst) >>> fusion = GroupFusion(fuse_func=keep_max) >>> fusion.method 'first' >>> accumulator = np.array([1.0, 5.0]) >>> fusion.fuse_func(accumulator, np.array([3.0, 2.0])) >>> accumulator.tolist() [3.0, 5.0] - Something that is not callable is rejected on construction:
See Also
- pyramids.dataset.merge.merge_rasters: the mosaic
methodis handed to. - pyramids.dataset.DatasetCollection.from_stac: the entry point that consumes this group.
Source code in src/pyramids/dataset/stac_options.py
505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 | |
__post_init__()
#
Reject a fuse_func that cannot be called.
Raises:
| Type | Description |
|---|---|
TypeError
|
|
Source code in src/pyramids/dataset/stac_options.py
AssetRead
dataclass
#
How each asset's values and metadata are normalised as they are read.
rescale and cfg are the only two reasons
:meth:~pyramids.dataset.DatasetCollection.from_stac has to rewrite a
source rather than read it as it stands, they decide together
whether a timestep is materialised instead of staying a lazy URL, and they
are threaded to exactly the same places. They are one concept: what the
reader fixes up on the way in.
Attributes:
| Name | Type | Description |
|---|---|---|
rescale |
bool
|
Return physical units rather than stored counts, applying
It changes how the cube is backed. Single-asset mode normally
backs each timestep with the asset URL itself and stays lazy over
Two consequences worth planning for. Units can be
inhomogeneous: an item that declares no |
cfg |
Any
|
Optional |
Examples:
- The default group rewrites nothing on the way in:
- A
cfgmapping is carried as given, keyed by collection id: - With
rescale, a packed int16 asset is read in physical units and its stored no-data becomesNaN, where the default read returns the stored counts:>>> import os, tempfile >>> import numpy as np >>> from pyramids.dataset import Dataset, DatasetCollection, GeoReference >>> from pyramids.stac import AssetRead >>> path = os.path.join(tempfile.mkdtemp(), "packed.tif") >>> Dataset.from_array( ... np.array([[0, 100], [200, 300]], dtype="int16"), ... no_data_value=0, ... geo_ref=GeoReference(top_left_corner=(0.0, 2.0), cell_size=1.0, epsg=4326), ... ).to_file(path) >>> item = { ... "id": "packed", ... "bbox": [0.0, 0.0, 2.0, 2.0], ... "properties": {"datetime": "2023-06-01T00:00:00Z"}, ... "assets": { ... "data": { ... "href": path, ... "raster:bands": [{"scale": 0.01, "offset": 0.0, "nodata": 0}], ... } ... }, ... } >>> DatasetCollection.from_stac([item], "data").first().tolist() [[0, 100], [200, 300]] >>> DatasetCollection.from_stac( ... [item], "data", read=AssetRead(rescale=True) ... ).first().tolist() [[nan, 1.0], [2.0, 3.0]]
See Also
- pyramids.stac._config: the
stac_cfgmapping shapecfgaccepts. - pyramids.dataset.DatasetCollection.from_stac: the entry point that consumes this group.
Source code in src/pyramids/dataset/stac_options.py
585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 | |
CubeProvenance
dataclass
#
What STAC metadata rides along on the cube :meth:~pyramids.dataset.DatasetCollection.from_stac builds.
Both fields carry Item metadata onto the output rather than changing a
single pixel: properties as the per-timestep attribute table, and
eo_band_names as the band axis' labels. Grouping them keeps the
"describe the cube" options apart from the ones that decide what it
contains.
Attributes:
| Name | Type | Description |
|---|---|---|
properties |
bool | str | Sequence[str]
|
Which STAC Item |
eo_band_names |
bool
|
Multi-asset only. When |
Examples:
- The default group carries nothing onto the cube:
- Naming the keys to carry, or asking for every property:
- The selected properties come back as one dict per timestep through
:attr:
~pyramids.dataset.DatasetCollection.time_attrs:>>> import os, tempfile >>> import numpy as np >>> from pyramids.dataset import Dataset, DatasetCollection, GeoReference >>> from pyramids.stac import CubeProvenance >>> folder = tempfile.mkdtemp() >>> items = [] >>> for index in range(2): ... path = os.path.join(folder, f"t{index}.tif") ... Dataset.from_array( ... np.full((2, 2), float(index + 1), dtype="float32"), ... geo_ref=GeoReference(top_left_corner=(0.0, 2.0), cell_size=1.0, epsg=4326), ... ).to_file(path) ... items.append( ... { ... "id": f"t{index}", ... "bbox": [0.0, 0.0, 2.0, 2.0], ... "properties": { ... "datetime": f"2023-06-0{index + 1}T00:00:00Z", ... "eo:cloud_cover": 10 * index, ... }, ... "assets": {"data": {"href": path}}, ... } ... ) >>> cube = DatasetCollection.from_stac( ... items, "data", provenance=CubeProvenance(properties=["eo:cloud_cover"]) ... ) >>> cube.time_attrs [{'eo:cloud_cover': 0}, {'eo:cloud_cover': 10}]
See Also
- GroupFusion: whose
methoddecides whether a group's first item really describes the emitted pixels. - pyramids.dataset.DatasetCollection.from_stac: the entry point that consumes this group.
Source code in src/pyramids/dataset/stac_options.py
701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 | |
Client & search#
pyramids.stac.client
#
Open a pystac-client Client with a pyramids signer wired in.
This thin wrapper attaches a :class:~pyramids.stac.signers.Signer to both
pystac-client hooks at once — modifier (post-response Item rewrite) and
request_modifier (pre-send HTTP request signing) — so the caller never has
to remember which boundary a given signer cares about.
pystac-client is an optional dependency. Install with one of:
- PyPI:
pip install 'pyramids-core[stac]' - conda-forge:
conda install -c conda-forge pyramids-stac
open_client(url, *, signer=None, headers=None, timeout=30)
#
Open a STAC API client with a pyramids signer wired into both hooks.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url
|
str
|
STAC API root URL (e.g. an |
required |
signer
|
Signer | None
|
A :class: |
None
|
headers
|
dict[str, str] | None
|
Optional static HTTP headers added to every request. |
None
|
timeout
|
float | None
|
Per-request timeout in seconds. |
30
|
Returns:
| Type | Description |
|---|---|
Any
|
A |
Raises:
| Type | Description |
|---|---|
OptionalPackageDoesNotExist
|
When |
Examples:
- Open a public catalog anonymously (requires the
[stac]extra):>>> from pyramids.stac import open_client # doctest: +SKIP >>> client = open_client("https://earth-search.aws.element84.com/v1") # doctest: +SKIP >>> search = client.search(collections=["sentinel-2-l2a"], max_items=1) # doctest: +SKIP >>> next(search.items()).collection_id # doctest: +SKIP 'sentinel-2-l2a' - Wire a bearer-token signer into both hooks:
Source code in src/pyramids/stac/client.py
pyramids.stac.search
#
Typed STAC item-search helper (PB-3).
A thin, typed wrapper over pystac_client.Client.search that makes the common
AOI / time / cloud query one call and returns an ItemCollection ready for
:meth:pyramids.dataset.DatasetCollection.from_stac. It:
- opens a client from a URL (or accepts an already-open
Client); - gates a CQL2
filteron the endpoint advertising theFILTERconformance class, raising a clear error instead of pystac-client's opaque one; - accepts a shapely geometry or a GeoJSON dict for
intersects; - bounds the query at the API —
bbox/datetime/max_items/limitare forwarded toclient.searchso the server (and paging) does the work (M3). This contrasts with :func:pyramids.dataset._stac.from_stac, whose ownbbox/max_itemsare client-side post-filters over an already-materialised item list.
Two entry points share one query builder:
- :func:
search— eager: returns the matchedItemCollection. - :func:
item_search— lazy: returns theItemSearchitself, so the caller can ask for the total hit count (.matched()), stream items (.items()) or walk result pages (.pages()) without materialising everything first.
That split decides who owns the HTTP session when a URL is passed rather than an
open Client. :func:search materialises everything, so the client it opened
is closed before it returns (see :mod:pyramids.stac._clients). The lazy
ItemSearch keeps issuing requests through its client, so :func:item_search
leaves the one it opened open — and releases it only when the query is rejected
and nothing is returned. Pass an open Client when that lifetime matters.
pystac-client is an optional dependency. Install with one of:
- PyPI:
pip install 'pyramids-core[stac]' - conda-forge:
conda install -c conda-forge pyramids-stac
item_search(client_or_url, collections, *, ids=None, bbox=None, intersects=None, datetime=None, query=None, filter=None, sortby=None, fields=None, max_items=None, limit=None, signer=None)
#
Build a STAC item search and return the lazy ItemSearch object.
Same query as :func:search, but nothing is fetched: the returned
pystac_client.ItemSearch is the handle to the unexecuted search, so the
caller can ask it for the total hit count or page through results instead of
materialising every item at once:
.matched()— total number of hits, orNonewhen the server does not advertise a count (it is an optional STAC API field; never assume it)..items()— stream items one by one, paging transparently..pages()— iterate whole result pages (one request per page)..item_collection()— materialise everything, exactly what :func:searchreturns.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
client_or_url
|
Any
|
An open |
required |
collections
|
str | Sequence[str]
|
Collection id, or a sequence of collection ids, to search. |
required |
ids
|
str | Sequence[str] | None
|
Optional item id, or sequence of item ids, to fetch directly. |
None
|
bbox
|
Sequence[float] | None
|
Optional |
None
|
intersects
|
Any
|
Optional AOI geometry — a shapely geometry (anything with a
|
None
|
datetime
|
Any
|
Optional RFC 3339 datetime or interval string
(e.g. |
None
|
query
|
Any
|
Optional |
None
|
filter
|
Any
|
Optional CQL2 filter (cql2-json dict or cql2-text string). When
given, the endpoint must advertise the |
None
|
sortby
|
Any
|
Optional sort specification forwarded to the API. |
None
|
fields
|
Any
|
Optional |
None
|
max_items
|
int | None
|
Optional cap on the total number of items returned (bounds paging at the API). |
None
|
limit
|
int | None
|
Optional page size forwarded to the API. |
None
|
signer
|
Any
|
Optional signer used only when |
None
|
Returns:
| Type | Description |
|---|---|
Any
|
The |
Raises:
| Type | Description |
|---|---|
OptionalPackageDoesNotExist
|
When |
ValueError
|
When both |
Examples:
- Count the hits before deciding to download anything (requires the
[stac]extra and network access):>>> from pyramids.stac import item_search # doctest: +SKIP >>> hits = item_search( # doctest: +SKIP ... "https://earth-search.aws.element84.com/v1", ... "sentinel-2-l2a", ... bbox=(11.0, 46.0, 11.2, 46.2), ... datetime="2023-06/2023-08", ... ) >>> hits.matched() # None when the server reports no count # doctest: +SKIP 128 >>> first_page = next(hits.pages()) # doctest: +SKIP
Source code in src/pyramids/stac/search.py
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | |
search(client_or_url, collections, *, ids=None, bbox=None, intersects=None, datetime=None, query=None, filter=None, sortby=None, fields=None, max_items=None, limit=None, signer=None)
#
Run a STAC item search and return the matched ItemCollection.
The eager counterpart of :func:item_search: it builds the very same query
and immediately materialises it. Reach for :func:item_search instead when
the hit count (.matched()) or page-by-page iteration is wanted — this
function always returns a materialised ItemCollection.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
client_or_url
|
Any
|
An open |
required |
collections
|
str | Sequence[str]
|
Collection id, or a sequence of collection ids, to search. |
required |
ids
|
str | Sequence[str] | None
|
Optional item id, or sequence of item ids, to fetch directly. |
None
|
bbox
|
Sequence[float] | None
|
Optional |
None
|
intersects
|
Any
|
Optional AOI geometry — a shapely geometry (anything with a
|
None
|
datetime
|
Any
|
Optional RFC 3339 datetime or interval string
(e.g. |
None
|
query
|
Any
|
Optional |
None
|
filter
|
Any
|
Optional CQL2 filter (cql2-json dict or cql2-text string). When
given, the endpoint must advertise the |
None
|
sortby
|
Any
|
Optional sort specification forwarded to the API. |
None
|
fields
|
Any
|
Optional |
None
|
max_items
|
int | None
|
Optional cap on the total number of items returned (bounds paging at the API). |
None
|
limit
|
int | None
|
Optional page size forwarded to the API. |
None
|
signer
|
Any
|
Optional signer used only when |
None
|
Returns:
| Type | Description |
|---|---|
Any
|
The |
Any
|
|
Raises:
| Type | Description |
|---|---|
OptionalPackageDoesNotExist
|
When |
ValueError
|
When both |
Examples:
- Search a collection over an AOI and time window, then build a cube
(requires the
[stac]extra and network access):>>> from pyramids.stac import search # doctest: +SKIP >>> from pyramids.dataset import DatasetCollection # doctest: +SKIP >>> items = search( # doctest: +SKIP ... "https://earth-search.aws.element84.com/v1", ... "sentinel-2-l2a", ... bbox=(11.0, 46.0, 11.2, 46.2), ... datetime="2023-06/2023-08", ... query={"eo:cloud_cover": {"lt": 20}}, ... max_items=10, ... ) >>> cube = DatasetCollection.from_stac(items, asset=["red", "green", "blue"]) # doctest: +SKIP - Fetch two known items and keep only a couple of fields:
Source code in src/pyramids/stac/search.py
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 | |