Data layer¶
Body-agnostic building blocks shared by every instrument dataset: STAC and PDS ODE search, windowed raster reads, the target grid, and the disposable cache. Three sources back instrument datasets — see Instrument datasets for which each instrument uses:
- STAC (
astrofetch.data.stac): the USGS Astrogeology Analysis Ready Data catalog, searched viapystac-client. - PDS ODE (
astrofetch.data.ode): the NASA PDS Orbital Data Explorer REST API, for instruments (LROC, LOLA, M3, ...) the STAC catalog does not carry. - Fixed mosaics: a handful of instruments are published as one global
(or near-global) file rather than many searchable items; these are read
directly from a well-known URL in
astrofetch.data.endpoints, no search.
All three reproject through the same windowed-read path and share the same sample-dict contract.
Target grid¶
astrofetch.data.grid ¶
Target grid definition and unit helpers (body-agnostic).
A :class:TargetGrid is the common raster every layer is reprojected onto so
that channels coregister exactly. Phase 1 uses a plate-carree (equirectangular)
grid in the IAU 2015 Moon geographic CRS; data/raster.py reprojects each
source COG — whatever its native projection, equirectangular or polar
stereographic — onto it. Keeping the grid geographic means the bbox a caller
passes and the crs a sample advertises are the same coordinate system.
BBox
module-attribute
¶
BBox = tuple[float, float, float, float]
(west, south, east, north) in degrees.
GEOGRAPHIC_CRS
module-attribute
¶
GEOGRAPHIC_CRS = 'IAU_2015:30100'
Ocentric IAU 2015 Moon geographic CRS (degrees); the target grid CRS.
MOON_RADIUS_M
module-attribute
¶
MOON_RADIUS_M = 1737400.0
IAU 2015 Moon sphere radius in metres (IAU code 30100; see any ARD COG WKT).
TargetGrid
dataclass
¶
A fixed output raster: a bbox rasterized to width x height pixels.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bbox
|
BBox
|
(west, south, east, north) in degrees, west < east, south < north. |
required |
width
|
int
|
output width in pixels. |
required |
height
|
int
|
output height in pixels. |
required |
crs
|
str
|
grid CRS; defaults to the IAU 2015 Moon geographic CRS. |
GEOGRAPHIC_CRS
|
meters_to_degrees ¶
meters_to_degrees(
meters: float, latitude: float
) -> tuple[float, float]
Approximate the (dlon, dlat) degree span of a ground distance in metres.
Spherical arc length on the IAU 2015 Moon sphere, used only to size a
sampling window from patch_size * resolution; the exact pixel geometry
is fixed later by :class:TargetGrid, so this need only be close. Longitude
degrees shrink with cos(latitude).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
meters
|
float
|
ground distance in metres. |
required |
latitude
|
float
|
latitude in degrees where the span is measured. |
required |
Returns:
| Type | Description |
|---|---|
tuple[float, float]
|
(dlon, dlat) span in degrees. |
Raster reads¶
astrofetch.data.raster ¶
Windowed COG reads reprojected onto a :class:TargetGrid (rasterio).
Reads only the region of a Cloud Optimized GeoTIFF that covers the grid — via
HTTP range requests against the right overview level — reprojects it onto the
grid's CRS and resolution, applies the band's scale/offset to recover physical
values, and returns the data with a boolean validity mask (False where the
source had nodata or simply does not cover the grid).
All map-projection and resampling math is delegated to rasterio/GDAL (AGENTS rule 1): this module wires it up, it does not reimplement it.
read_window ¶
read_window(
href: str,
grid: TargetGrid,
band: int = 1,
resampling: Resampling = Resampling.bilinear,
nodata_override: float | None = None,
) -> tuple[np.ndarray, np.ndarray]
Read one COG band, reprojected onto grid, in physical units.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
href
|
str
|
COG URL or local path. |
required |
grid
|
TargetGrid
|
output grid; defines CRS, size, and extent. |
required |
band
|
int
|
1-based band index to read. |
1
|
resampling
|
Resampling
|
resampling used when reprojecting to the grid. |
bilinear
|
nodata_override
|
float | None
|
nodata value to use in place of the source's own (which may be unset). Needed for a handful of PDS products that omit nodata from their label even though the raster does not cover its full extent (unwarped pixels would otherwise silently read back as valid zeros); prefer the source's own declared nodata whenever a product provides one. |
None
|
Returns:
| Type | Description |
|---|---|
ndarray
|
|
ndarray
|
float32 array of physical values (scale/offset applied) with invalid |
tuple[ndarray, ndarray]
|
pixels set to 0, and |
tuple[ndarray, ndarray]
|
where the pixel is valid. |
Raises:
| Type | Description |
|---|---|
EndpointError
|
the COG could not be opened or read. |
read_full ¶
read_full(
href: str,
window: Window | None = None,
bands: list[int] | None = None,
) -> tuple[np.ndarray, np.ndarray]
Read a raster's own pixels with no reprojection, in physical units.
Unlike :func:read_window, this does not warp onto a :class:TargetGrid
-- it reads the source in its own native geometry, for rasters that have
no map projection to warp to (e.g. raw camera-frame swaths). Used by
:mod:astrofetch.moon.granules.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
href
|
str
|
raster URL or local path. |
required |
window
|
Window | None
|
pixel window |
None
|
bands
|
list[int] | None
|
1-based band indices to read; |
None
|
Returns:
| Type | Description |
|---|---|
ndarray
|
|
ndarray
|
float32 array of physical values (per-band scale/offset applied) |
tuple[ndarray, ndarray]
|
with invalid pixels set to 0, and |
tuple[ndarray, ndarray]
|
array, |
Raises:
| Type | Description |
|---|---|
EndpointError
|
the raster could not be opened or read. |
STAC search¶
astrofetch.data.stac ¶
STAC search against the USGS ARD catalog (pystac-client), politely.
Wraps pystac-client with a retrying, backed-off HTTP session — AGENTS rule 5:
never hammer archive servers — and normalizes failures into :class:EndpointError
so callers never have to know pystac-client's exception surface. A single client
is reused across searches (one polite connection pool, not one per request).
catalog
cached
¶
catalog(root: str = STAC_API_ROOT) -> Client
Open (and memoize) the STAC API client for root.
Raises:
| Type | Description |
|---|---|
EndpointError
|
the catalog root could not be opened. |
find_asset_hrefs ¶
find_asset_hrefs(
collection: str,
asset_key: str,
bbox: BBox,
max_items: int = 20,
root: str = STAC_API_ROOT,
) -> list[str]
Return hrefs of one asset across items of collection intersecting bbox.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
collection
|
str
|
STAC collection id to search. |
required |
asset_key
|
str
|
asset key to pull from each matching item, e.g. |
required |
bbox
|
BBox
|
(west, south, east, north) in degrees. |
required |
max_items
|
int
|
cap on items returned; bounds the request volume per read. |
20
|
root
|
str
|
STAC API root; defaults to the configured USGS ARD catalog. |
STAC_API_ROOT
|
Returns:
| Type | Description |
|---|---|
list[str]
|
Asset hrefs, one per intersecting item (possibly empty if none overlap). |
Raises:
| Type | Description |
|---|---|
EndpointError
|
the search failed or a matched item lacks |
PDS ODE search¶
astrofetch.data.ode ¶
Product search against the NASA PDS Orbital Data Explorer (ODE), politely.
Mirrors :mod:astrofetch.data.stac: a single retrying, backed-off HTTP session
(AGENTS rule 5) and failures normalized into :class:EndpointError, so callers
never have to know ODE's JSON quirks — a lone result comes back as a dict
instead of a list, an empty result is the string "No Products Found"
instead of an empty list, and errors are HTTP 200 responses with an error
message in the body.
ODEFile ¶
Bases: NamedTuple
One file attached to an ODE product.
ODEProduct ¶
Bases: NamedTuple
One ODE product: its id, files, footprint, and raw metadata.
bbox
instance-attribute
¶
bbox: BBox | None
(west, south, east, north) in degrees, -180 to 180; None when the
footprint is not representable as a simple bbox (crosses the antimeridian,
or spans exactly 360 degrees of longitude).
query_products ¶
query_products(
ihid: str,
iid: str,
pt: str,
bbox: BBox,
max_products: int = 20,
product_id: str | None = None,
root: str = ODE_API_ROOT,
) -> list[ODEProduct]
Search ODE for products of one instrument and product type in bbox.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ihid
|
str
|
ODE instrument host id, e.g. |
required |
iid
|
str
|
ODE instrument id, e.g. |
required |
pt
|
str
|
ODE product type, e.g. |
required |
bbox
|
BBox
|
(west, south, east, north) in degrees, -180 to 180. |
required |
max_products
|
int
|
cap on products returned; bounds request volume and paging (fetched in pages of up to 100). |
20
|
product_id
|
str | None
|
ODE |
None
|
root
|
str
|
ODE API root; defaults to the configured endpoint. |
ODE_API_ROOT
|
Returns:
| Type | Description |
|---|---|
list[ODEProduct]
|
Matching products, possibly empty if none overlap |
Raises:
| Type | Description |
|---|---|
EndpointError
|
the query failed or ODE reported an error. |
Example
from astrofetch.data.ode import query_products query_products("LRO", "LROC", "SDNDTM", bbox=(3.0, 25.5, 4.5, 26.5)) # doctest: +SKIP [ODEProduct(pdsid='sdp.nac_dtm.apollo15...', ...), ...]
match_files ¶
match_files(
files: tuple[ODEFile, ...],
pattern: str,
file_type: str | None = "Product",
) -> list[str]
Return URLs of files whose name matches pattern and file_type.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
files
|
tuple[ODEFile, ...]
|
files to filter, typically |
required |
pattern
|
str
|
regex, matched against the filename with |
required |
file_type
|
str | None
|
required ODE file role, e.g. |
'Product'
|
Returns:
| Type | Description |
|---|---|
list[str]
|
Matching URLs, sorted by filename for deterministic ordering. |
find_file_urls ¶
find_file_urls(
ihid: str,
iid: str,
pt: str,
pattern: str,
bbox: BBox,
max_products: int = 20,
file_type: str | None = "Product",
product_id: str | None = None,
root: str = ODE_API_ROOT,
) -> list[str]
Return file URLs matching pattern across products in bbox.
The ODE analogue of :func:astrofetch.data.stac.find_asset_hrefs: a
product bundle can contain many files (data, browse, derived), so this
searches products then filters their files by name and role. A product
with no matching file is not an error — masks report coverage truthfully
instead.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ihid
|
str
|
ODE instrument host id, e.g. |
required |
iid
|
str
|
ODE instrument id, e.g. |
required |
pt
|
str
|
ODE product type, e.g. |
required |
pattern
|
str
|
regex matched against filenames, case-insensitive. |
required |
bbox
|
BBox
|
(west, south, east, north) in degrees, -180 to 180. |
required |
max_products
|
int
|
cap on products searched. |
20
|
file_type
|
str | None
|
required ODE file role; |
'Product'
|
product_id
|
str | None
|
ODE |
None
|
root
|
str
|
ODE API root; defaults to the configured endpoint. |
ODE_API_ROOT
|
Returns:
| Type | Description |
|---|---|
list[str]
|
Matching URLs, product order then filename order. |
Raises:
| Type | Description |
|---|---|
EndpointError
|
the search failed or ODE reported an error. |
Cache¶
astrofetch.data.cache ¶
Disposable local cache for reprojected layer windows.
Never load-bearing (AGENTS rule 4): every entry is re-fetchable from the archive and safe to delete at any time. A key is a hash of the layer id and the exact target grid, so an identical request skips the network on a warm cache while any change to bbox, size, or CRS misses cleanly.
WindowCache ¶
Filesystem cache of (image, mask) arrays keyed by layer and grid.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
root
|
str | Path | None
|
cache directory; defaults to :func: |
None
|
default_cache_dir ¶
default_cache_dir() -> Path
Cache root: $ASTROFETCH_CACHE if set, else an XDG cache subdir.
Endpoints¶
astrofetch.data.endpoints ¶
External archive endpoints — the ONLY place URLs live (AGENTS rule 2).
Every COG asset href is discovered through the STAC API at request time, so the API root below is the single external URL AstroFetch hard-codes. When a service moves (as QuickMap's domain once did), this is the only file to change.
STAC_API_ROOT
module-attribute
¶
STAC_API_ROOT = 'https://stac.astrogeology.usgs.gov/api/'
USGS Astrogeology Analysis Ready Data STAC API root (pystac-client entry).
ODE_API_ROOT
module-attribute
¶
ODE_API_ROOT = 'https://oderest.rsl.wustl.edu/live2/'
NASA PDS Orbital Data Explorer (ODE) REST API root, Washington Univ. St. Louis. Used to search PDS3/PDS4 products (LROC, LOLA, M3, ...) by instrument and bounding box; the USGS ARD STAC catalog does not carry these instruments. Last verified 2026-07-20.
LROC_WAC_MOSAIC_100M_URL
module-attribute
¶
LROC_WAC_MOSAIC_100M_URL = "https://asc-pds-services.s3.us-west-2.amazonaws.com/mosaic/Lunar_LRO_LROC-WAC_Mosaic_global_100m_June2013.tif"
LRO LROC WAC global morphology mosaic, 100 m/px, equirectangular.
Not a Cloud Optimized GeoTIFF (striped, no overviews): windowed reads at native resolution (100 m) are efficient; heavily downsampled reads are not. Last verified 2026-07-20.
LOLA_DEM_128_URL
module-attribute
¶
LOLA_DEM_128_URL = "https://pds-geosciences.wustl.edu/lro/lro-l-lola-3-rdr-v1/lrolol_1xxx/data/lola_gdr/cylindrical/float_img/ldem_128_float.lbl"
LOLA global DEM, 128 px/degree (~237 m/px at the equator), float32 metres
above the IAU 2015 Moon reference sphere. Detached PDS3 label; GDAL's PDS
driver resolves the sibling .img over HTTPS. Last verified 2026-07-20.
SLDEM2015_URL
module-attribute
¶
SLDEM2015_URL = "https://pds-geosciences.wustl.edu/lro/lro-l-lola-3-rdr-v1/lrolol_1xxx/data/sldem2015/global/float_img/sldem2015_128_60s_60n_000_360_float.lbl"
SLDEM2015: LOLA + Kaguya Terrain Camera co-registered DEM, 128 px/degree,
float32 metres. Source coverage is 60S-60N only (not a bug); windows outside
that band read back with mask all False. Last verified 2026-07-20.
Errors¶
astrofetch.errors ¶
AstroFetch exception hierarchy.
Public errors live here so callers can except astrofetch.errors.EndpointError
without importing internal modules.
AstroFetchError ¶
Bases: Exception
Base class for every error AstroFetch raises on purpose.
EndpointError ¶
Bases: AstroFetchError
A remote archive endpoint failed or returned something unusable.
Carries the offending endpoint (a collection id, URL, or asset href) and
always points back at astrofetch/data/endpoints.py — the single place
external URLs are configured — so a moved or flaky service is easy to trace.