asp-plot 1.19.0__tar.gz → 2.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- asp_plot-2.0.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
- asp_plot-2.0.0/.github/ISSUE_TEMPLATE/problem-report.yml +44 -0
- asp_plot-2.0.0/.github/workflows/claude-code-review.yml +39 -0
- asp_plot-2.0.0/.github/workflows/claude.yml +49 -0
- {asp_plot-1.19.0 → asp_plot-2.0.0}/.gitignore +3 -3
- {asp_plot-1.19.0 → asp_plot-2.0.0}/.readthedocs.yaml +1 -0
- asp_plot-2.0.0/AGENTS.md +79 -0
- asp_plot-1.19.0/AGENTS.md → asp_plot-2.0.0/ARCHITECTURE.md +36 -149
- {asp_plot-1.19.0 → asp_plot-2.0.0}/CHANGELOG.md +24 -0
- asp_plot-2.0.0/CLAUDE.md +1 -0
- {asp_plot-1.19.0 → asp_plot-2.0.0}/PKG-INFO +5 -5
- {asp_plot-1.19.0 → asp_plot-2.0.0}/README.md +3 -3
- asp_plot-1.19.0/asp_plot/cli/asp_plot.py → asp_plot-2.0.0/asp_plot/cli/asp_report.py +2 -2
- {asp_plot-1.19.0 → asp_plot-2.0.0}/asp_plot/cli/request_planetary_altimetry.py +3 -3
- {asp_plot-1.19.0 → asp_plot-2.0.0}/asp_plot/cli/stereo_geom.py +1 -1
- {asp_plot-1.19.0 → asp_plot-2.0.0}/asp_plot/report.py +13 -5
- {asp_plot-1.19.0 → asp_plot-2.0.0}/asp_plot/report_captions.py +2 -2
- {asp_plot-1.19.0 → asp_plot-2.0.0}/asp_plot/report_pipeline.py +60 -41
- asp_plot-2.0.0/asp_plot/scenes.py +296 -0
- {asp_plot-1.19.0 → asp_plot-2.0.0}/asp_plot/selections.py +1 -1
- {asp_plot-1.19.0 → asp_plot-2.0.0}/asp_plot/sensors.py +325 -13
- {asp_plot-1.19.0 → asp_plot-2.0.0}/asp_plot/stereo.py +261 -99
- {asp_plot-1.19.0 → asp_plot-2.0.0}/asp_plot/stereo_geometry.py +148 -32
- {asp_plot-1.19.0 → asp_plot-2.0.0}/asp_plot/utils.py +198 -46
- {asp_plot-1.19.0 → asp_plot-2.0.0}/conda-forge-recipe/meta.yaml +2 -2
- {asp_plot-1.19.0 → asp_plot-2.0.0}/pyproject.toml +5 -3
- asp_plot-1.19.0/asp_plot/scenes.py +0 -193
- {asp_plot-1.19.0 → asp_plot-2.0.0}/.flake8 +0 -0
- {asp_plot-1.19.0 → asp_plot-2.0.0}/.github/workflows/release.yml +0 -0
- {asp_plot-1.19.0 → asp_plot-2.0.0}/.github/workflows/run-tests.yml +0 -0
- {asp_plot-1.19.0 → asp_plot-2.0.0}/.pre-commit-config.yaml +0 -0
- {asp_plot-1.19.0 → asp_plot-2.0.0}/LICENSE +0 -0
- {asp_plot-1.19.0 → asp_plot-2.0.0}/asp_plot/__init__.py +0 -0
- {asp_plot-1.19.0 → asp_plot-2.0.0}/asp_plot/alignment.py +0 -0
- {asp_plot-1.19.0 → asp_plot-2.0.0}/asp_plot/altimetry.py +0 -0
- {asp_plot-1.19.0 → asp_plot-2.0.0}/asp_plot/altimetry_plots.py +0 -0
- {asp_plot-1.19.0 → asp_plot-2.0.0}/asp_plot/altimetry_source.py +0 -0
- {asp_plot-1.19.0 → asp_plot-2.0.0}/asp_plot/asp_log.py +0 -0
- {asp_plot-1.19.0 → asp_plot-2.0.0}/asp_plot/bodies.py +0 -0
- {asp_plot-1.19.0 → asp_plot-2.0.0}/asp_plot/bundle_adjust.py +0 -0
- {asp_plot-1.19.0 → asp_plot-2.0.0}/asp_plot/cli/__init__.py +0 -0
- {asp_plot-1.19.0 → asp_plot-2.0.0}/asp_plot/cli/csm_camera_plot.py +0 -0
- {asp_plot-1.19.0 → asp_plot-2.0.0}/asp_plot/cli/gallery.py +0 -0
- {asp_plot-1.19.0 → asp_plot-2.0.0}/asp_plot/csm_analysis.py +0 -0
- {asp_plot-1.19.0 → asp_plot-2.0.0}/asp_plot/csm_camera.py +0 -0
- {asp_plot-1.19.0 → asp_plot-2.0.0}/asp_plot/csm_io.py +0 -0
- {asp_plot-1.19.0 → asp_plot-2.0.0}/asp_plot/gallery.py +0 -0
- {asp_plot-1.19.0 → asp_plot-2.0.0}/asp_plot/icesat2_source.py +0 -0
- {asp_plot-1.19.0 → asp_plot-2.0.0}/asp_plot/mapproject.py +0 -0
- {asp_plot-1.19.0 → asp_plot-2.0.0}/asp_plot/planetary_source.py +0 -0
- {asp_plot-1.19.0 → asp_plot-2.0.0}/asp_plot/processing_parameters.py +0 -0
- {asp_plot-1.19.0 → asp_plot-2.0.0}/asp_plot/stereopair_metadata_parser.py +0 -0
- {asp_plot-1.19.0 → asp_plot-2.0.0}/environment.yml +0 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
name: Problem report
|
|
2
|
+
description: Something in asp_plot is broken, wrong, or unclear
|
|
3
|
+
body:
|
|
4
|
+
- type: input
|
|
5
|
+
id: location
|
|
6
|
+
attributes:
|
|
7
|
+
label: Where did you hit this?
|
|
8
|
+
description: A docs page URL, a CLI command, an example notebook, or a module/function.
|
|
9
|
+
placeholder: e.g. `asp_report --directory ...`, or the stereo_geom example notebook
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
- type: textarea
|
|
13
|
+
id: what-happened
|
|
14
|
+
attributes:
|
|
15
|
+
label: What happened?
|
|
16
|
+
description: What you did, what you expected, and what happened instead. Paste the full command and error output as text where possible.
|
|
17
|
+
validations:
|
|
18
|
+
required: true
|
|
19
|
+
- type: input
|
|
20
|
+
id: version
|
|
21
|
+
attributes:
|
|
22
|
+
label: asp_plot version
|
|
23
|
+
description: From `python -c "import asp_plot; print(asp_plot.__version__)"`.
|
|
24
|
+
placeholder: e.g. 1.1.0
|
|
25
|
+
validations:
|
|
26
|
+
required: false
|
|
27
|
+
- type: dropdown
|
|
28
|
+
id: install
|
|
29
|
+
attributes:
|
|
30
|
+
label: How did you install asp_plot?
|
|
31
|
+
options:
|
|
32
|
+
- conda-forge
|
|
33
|
+
- pip (PyPI)
|
|
34
|
+
- From source (git)
|
|
35
|
+
- Just reading the docs
|
|
36
|
+
validations:
|
|
37
|
+
required: false
|
|
38
|
+
- type: textarea
|
|
39
|
+
id: extra
|
|
40
|
+
attributes:
|
|
41
|
+
label: Anything else?
|
|
42
|
+
description: Screenshots, log files, links, ASP version.
|
|
43
|
+
validations:
|
|
44
|
+
required: false
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
name: Claude Code Review
|
|
2
|
+
|
|
3
|
+
# Runs once when a draft PR is marked ready for review.
|
|
4
|
+
# For further reviews, mention @claude in a PR comment (see claude.yml).
|
|
5
|
+
on:
|
|
6
|
+
pull_request:
|
|
7
|
+
types: [ready_for_review]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
claude-review:
|
|
11
|
+
# Optional: Filter by PR author
|
|
12
|
+
# if: |
|
|
13
|
+
# github.event.pull_request.user.login == 'external-contributor' ||
|
|
14
|
+
# github.event.pull_request.user.login == 'new-developer' ||
|
|
15
|
+
# github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR'
|
|
16
|
+
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
permissions:
|
|
19
|
+
contents: read
|
|
20
|
+
pull-requests: read
|
|
21
|
+
issues: read
|
|
22
|
+
id-token: write
|
|
23
|
+
|
|
24
|
+
steps:
|
|
25
|
+
- name: Checkout repository
|
|
26
|
+
uses: actions/checkout@v4
|
|
27
|
+
with:
|
|
28
|
+
fetch-depth: 1
|
|
29
|
+
|
|
30
|
+
- name: Run Claude Code Review
|
|
31
|
+
id: claude-review
|
|
32
|
+
uses: anthropics/claude-code-action@v1
|
|
33
|
+
with:
|
|
34
|
+
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
35
|
+
plugin_marketplaces: 'https://github.com/anthropics/claude-code.git'
|
|
36
|
+
plugins: 'code-review@claude-code-plugins'
|
|
37
|
+
prompt: '/code-review:code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }}'
|
|
38
|
+
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
|
|
39
|
+
# or https://code.claude.com/docs/en/cli-reference for available options
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
name: Claude Code
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
issue_comment:
|
|
5
|
+
types: [created]
|
|
6
|
+
pull_request_review_comment:
|
|
7
|
+
types: [created]
|
|
8
|
+
issues:
|
|
9
|
+
types: [opened, assigned]
|
|
10
|
+
pull_request_review:
|
|
11
|
+
types: [submitted]
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
claude:
|
|
15
|
+
if: |
|
|
16
|
+
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
|
|
17
|
+
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
|
|
18
|
+
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
|
|
19
|
+
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
permissions:
|
|
22
|
+
contents: read
|
|
23
|
+
pull-requests: read
|
|
24
|
+
issues: read
|
|
25
|
+
id-token: write
|
|
26
|
+
actions: read # Required for Claude to read CI results on PRs
|
|
27
|
+
steps:
|
|
28
|
+
- name: Checkout repository
|
|
29
|
+
uses: actions/checkout@v4
|
|
30
|
+
with:
|
|
31
|
+
fetch-depth: 1
|
|
32
|
+
|
|
33
|
+
- name: Run Claude Code
|
|
34
|
+
id: claude
|
|
35
|
+
uses: anthropics/claude-code-action@v1
|
|
36
|
+
with:
|
|
37
|
+
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
38
|
+
|
|
39
|
+
# This is an optional setting that allows Claude to read CI results on PRs
|
|
40
|
+
additional_permissions: |
|
|
41
|
+
actions: read
|
|
42
|
+
|
|
43
|
+
# Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it.
|
|
44
|
+
# prompt: 'Update the pull request description to include a summary of changes.'
|
|
45
|
+
|
|
46
|
+
# Optional: Add claude_args to customize behavior and configuration
|
|
47
|
+
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
|
|
48
|
+
# or https://code.claude.com/docs/en/cli-reference for available options
|
|
49
|
+
# claude_args: '--allowed-tools Bash(gh pr *)'
|
|
@@ -138,14 +138,14 @@ dmypy.json
|
|
|
138
138
|
scratch/
|
|
139
139
|
notebooks/**/*.parquet
|
|
140
140
|
notebooks/**/*.csv
|
|
141
|
-
CLAUDE
|
|
141
|
+
CLAUDE/
|
|
142
142
|
.claude/**
|
|
143
143
|
scripts/
|
|
144
144
|
/*.parquet
|
|
145
145
|
reports/regenerate_reports.sh
|
|
146
|
-
# Figure-selections sidecars written next to each report by `
|
|
146
|
+
# Figure-selections sidecars written next to each report by `asp_report`. These are
|
|
147
147
|
# regeneration byproducts that hardcode absolute local paths (dem_filename,
|
|
148
148
|
# parquet_cache), so they are not committed; a sanitized example is documented in
|
|
149
|
-
# docs/cli/
|
|
149
|
+
# docs/cli/asp_report.md instead.
|
|
150
150
|
reports/*_figure_selections.yml
|
|
151
151
|
*.csv
|
|
@@ -8,6 +8,7 @@ build:
|
|
|
8
8
|
pre_build:
|
|
9
9
|
- mkdir -p docs/examples/notebooks
|
|
10
10
|
- cp notebooks/WorldView/*.ipynb docs/examples/notebooks/
|
|
11
|
+
- cp notebooks/Pleiades/*.ipynb docs/examples/notebooks/
|
|
11
12
|
- cp notebooks/ASTER/*.ipynb docs/examples/notebooks/
|
|
12
13
|
- cp notebooks/LRO_NAC/*.ipynb docs/examples/notebooks/
|
|
13
14
|
- cp notebooks/Mars_MGS/*.ipynb docs/examples/notebooks/
|
asp_plot-2.0.0/AGENTS.md
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
Guidance for AI coding agents (Claude Code and others) and new contributors. This file holds only what you can't infer from the code: commands, gotchas, external dependencies, and process. For the module-by-module codebase map and design rationale, read [ARCHITECTURE.md](ARCHITECTURE.md) on demand — and keep both files in sync with the code.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
`asp_plot` is a Python package for visualizing output from the NASA Ames Stereo Pipeline (ASP): diagnostic plots and comprehensive PDF reports covering stereo DEM results, bundle adjustment, CSM camera models, stereo acquisition geometry, DEM galleries, and altimetry comparison/alignment (ICESat-2 for Earth, LOLA for Moon, MOLA for Mars). Handles terrestrial and planetary sensors. Requires Python >= 3.11; published on PyPI and conda-forge. The version lives in `pyproject.toml` (exposed as `asp_plot.__version__`).
|
|
8
|
+
|
|
9
|
+
## Development Commands
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Environment (installs the package editable with dev+docs extras)
|
|
13
|
+
conda env create -f environment.yml
|
|
14
|
+
conda activate asp_plot
|
|
15
|
+
pre-commit install # REQUIRED for development
|
|
16
|
+
|
|
17
|
+
# Tests
|
|
18
|
+
pytest # all tests; add -s to see print output
|
|
19
|
+
pytest tests/test_stereo.py::test_name # one file / one test
|
|
20
|
+
|
|
21
|
+
# Lint/format (pre-commit runs these on commit; manually:)
|
|
22
|
+
pre-commit run --all-files # black + flake8 + isort (profile: black)
|
|
23
|
+
|
|
24
|
+
# Rebuild after changing CLI tools or entry points
|
|
25
|
+
pip install -e ".[dev]"
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Flake8 config is in `.flake8` (extends ignore: E203, E701); pre-commit further ignores E501, E722, E207.
|
|
29
|
+
|
|
30
|
+
To build the docs locally (Sphinx + MyST; hosted on ReadTheDocs, auto-built on push to `main`):
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# One-time: copy notebooks, reports, and figures for local preview
|
|
34
|
+
mkdir -p docs/examples/notebooks && cp notebooks/**/*.ipynb docs/examples/notebooks/
|
|
35
|
+
mkdir -p docs/_static/reports && cp reports/*.pdf docs/_static/reports/
|
|
36
|
+
mkdir -p docs/_extra/examples/figures && cp notebooks/figures/* docs/_extra/examples/figures/
|
|
37
|
+
|
|
38
|
+
sphinx-autobuild docs docs/_build/html --open-browser # or sphinx-build for a one-off
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Gotchas
|
|
42
|
+
|
|
43
|
+
- **ASP tools must be on PATH** for the workflows that wrap them: `stereo`/`parallel_stereo`, `bundle_adjust`, `point2dem`, `pc_align`, `geodiff`, `dg_mosaic` (called via `run_subprocess_command()`). **Append** the ASP bin directory to PATH rather than prepending: the ASP release bundles its own `python`, which would shadow your environment's interpreter and break imports.
|
|
44
|
+
- **Internet is required** for basemaps (contextily/Esri tiles), ICESat-2 requests (SlideRule), and ESA WorldCover sampling (public AWS S3 COGs). Tests must not depend on the network — basemap fetches are stubbed (#151).
|
|
45
|
+
- **Mars altimetry needs the `*_pts_csv.csv`** (with `PLANET_RAD`), never the `*_topo_csv.csv`: MOLA TOPOGRAPHY is referenced to the oblate areoid while ASP DEMs use the spherical IAU datum — a latitude-dependent offset up to ~10 km that pc_align cannot remove. The loader rejects the topo file with an explanatory error.
|
|
46
|
+
- **ASP's `mapproject` writes no log file**; its command is reconstructed from output GeoTIFF metadata (`mapproject.py`), not parsed from logs like the other tools.
|
|
47
|
+
- **Attribution vs sensor naming is deliberate**: copyright/attribution names the rights-holder (`detect_satellite_attribution()` → `"Vantor"` or `"Airbus DS"`), while readers in `sensors.py` are named for the satellite family (WorldView, Pleiades). Don't reconcile them into one name (#137).
|
|
48
|
+
- **Package vs CLI naming is a deliberate split** (v2.0.0, #165): the *package* is `asp_plot` (`import asp_plot`, `pip install asp-plot`), the report *command* is `asp_report` (`asp_plot/cli/asp_report.py`). There is no `asp_plot` console script and no alias — don't "fix" either name to match the other. The other four CLIs (`stereo_geom`, `csm_camera_plot`, `request_planetary_altimetry`, `gallery`) are unprefixed and unchanged.
|
|
49
|
+
- **Airbus DIMAP quaternions are scalar-first** (`Q0` = scalar); they are reordered to the scalar-last `q1..q4` layout the roll/pitch/yaw code expects in `PleiadesMetadata.getAtt_df()`. Don't "fix" the reorder.
|
|
50
|
+
- **ASP multiview triangulation of mapprojected images needs `ISISROOT`** (observed with ASP 3.8.0-alpha, non-ISIS `-t pleiades` session): the joint triangulation aborts with an uncatchable `Isis::IException` (`$ISISROOT/IsisPreferences was not found`) that surfaces as a generic "Failed to run"/killed job. Workaround: `export ISISROOT=<ASP install root>` (the release bundles `IsisPreferences` there). Pair runs and raw-image multiview runs are unaffected; full write-up in a PR #155 comment.
|
|
51
|
+
|
|
52
|
+
## External Data Sources
|
|
53
|
+
|
|
54
|
+
- **ICESat-2 ATL06-SR** (Earth): requested through the SlideRule API; results cached as parquet next to the report.
|
|
55
|
+
- **LOLA (Moon) / MOLA (Mars)**: async queries to the ODE GDS REST API via the `request_planetary_altimetry` CLI — the user gets a download link by email, then passes the CSV to `asp_report --altimetry_csv`. Coordinates are east-positive 0–360 longitude, planetocentric latitude.
|
|
56
|
+
|
|
57
|
+
## Testing
|
|
58
|
+
|
|
59
|
+
Tests are in `tests/` with sample data in `tests/test_data/` (synthetic rasters, XML camera files, BA CSVs, ICESat-2 parquet, pc_align outputs, jitter data). Most modules have a matching `tests/test_<module>.py`; `test_imports.py` verifies everything imports. Some fixture derivatives (e.g. match-point CSVs next to `.match` files) are gitignored and regenerate during test runs — untracked files appearing under `tests/test_data/` after `pytest` are expected, don't commit them. Example notebooks in `notebooks/` are organized by sensor (WorldView, Pleiades, ASTER, LRO_NAC, Mars_MGS, Mars_MRO) — see ARCHITECTURE.md for what each demonstrates.
|
|
60
|
+
|
|
61
|
+
## Versioning and Release Process
|
|
62
|
+
|
|
63
|
+
Follow semantic versioning. To release:
|
|
64
|
+
1. Update version in `pyproject.toml`
|
|
65
|
+
2. Update `CHANGELOG.md` with version and date
|
|
66
|
+
3. Merge to `main`
|
|
67
|
+
|
|
68
|
+
The rest is automated: `release.yml` detects the version bump, creates a GitHub Release + tag, and publishes to PyPI via OIDC trusted publishing; conda-forge's autotick bot then opens a feedstock PR. PyPI trusted publishing and the conda-forge feedstock are already configured (reference recipe in `conda-forge-recipe/meta.yaml`).
|
|
69
|
+
|
|
70
|
+
## Common File Patterns
|
|
71
|
+
|
|
72
|
+
ASP output files follow specific naming patterns (find them with the `glob_file()` utility):
|
|
73
|
+
- DEMs: `*-DEM.tif` or `*_dem.tif`
|
|
74
|
+
- Disparity: `*-F.tif`
|
|
75
|
+
- Match files: `*.match`
|
|
76
|
+
- Bundle adjust residuals: `*-initial_residuals_pointmap.csv`, `*-final_residuals_pointmap.csv`
|
|
77
|
+
- Log files: `*log-bundle_adjust*.txt`, `*log-stereo*.txt`, `*log-point2dem*.txt`
|
|
78
|
+
|
|
79
|
+
**Multi-view (>2 scene) runs** keep only the joint products (`*-PC.tif`, `*-DEM.tif`, `*-IntersectionErr.tif`) at the stereo-directory top level; the per-pair intermediates live one level down in `<prefix>-pairN/` (`N-L_sub.tif`, `N-R_sub.tif`, `N-D_sub.tif`, the `.match` file, `N-align-{L,R}.txt`, and an `N-stereo.default` config copy naming that pair's images). Discover them with `find_pair_directories()` rather than globbing — code that assumes the flat pair layout silently degrades to "missing files" placeholders (#160).
|
|
@@ -1,88 +1,8 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Architecture
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A module-by-module map of `asp_plot` and its design patterns, for contributors and AI coding agents. Read this on demand — when deciding where a change belongs, or why a module is shaped the way it is. Day-to-day commands, gotchas, and process live in [AGENTS.md](AGENTS.md). Keep this file in sync with the code when you add or restructure a module.
|
|
4
4
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
`asp_plot` (current version: 1.18.0) is a Python package for visualizing output from the NASA Ames Stereo Pipeline (ASP). It processes stereo satellite imagery results (both terrestrial and planetary), generates diagnostic plots, and creates comprehensive PDF reports similar to those from commercial SfM software like Agisoft Metashape. Requires Python >= 3.11. Published on PyPI and conda-forge.
|
|
8
|
-
|
|
9
|
-
The package supports:
|
|
10
|
-
- Stereo DEM processing visualization
|
|
11
|
-
- Bundle adjustment analysis
|
|
12
|
-
- CSM camera model comparisons (for tools like bundle_adjust and jitter_solve)
|
|
13
|
-
- Altimetry comparisons: ICESat-2 ATL06-SR (Earth), LOLA (Moon), MOLA (Mars)
|
|
14
|
-
- Stereo geometry visualization from satellite XML metadata (Earth-based sensors)
|
|
15
|
-
- Non-terrestrial/planetary processing (tested with LRO NAC, Mars MRO CTX/HiRISE, Mars MGS MOC, and ASTER)
|
|
16
|
-
- Gallery plotting of many DEMs as a grid of thumbnails sharing one color scale
|
|
17
|
-
|
|
18
|
-
## Development Commands
|
|
19
|
-
|
|
20
|
-
### Environment Setup
|
|
21
|
-
```bash
|
|
22
|
-
# Create and activate conda environment (installs package with dev+docs extras)
|
|
23
|
-
conda env create -f environment.yml
|
|
24
|
-
conda activate asp_plot
|
|
25
|
-
|
|
26
|
-
# Install pre-commit hooks (REQUIRED for development)
|
|
27
|
-
pre-commit install
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
### Testing
|
|
31
|
-
```bash
|
|
32
|
-
# Run all tests
|
|
33
|
-
pytest
|
|
34
|
-
|
|
35
|
-
# Run tests with output visible (useful for debugging)
|
|
36
|
-
pytest -s
|
|
37
|
-
|
|
38
|
-
# Run a specific test file
|
|
39
|
-
pytest tests/test_stereo.py
|
|
40
|
-
|
|
41
|
-
# Run a specific test function
|
|
42
|
-
pytest tests/test_stereo.py::test_function_name
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
### Linting and Formatting
|
|
46
|
-
Pre-commit hooks automatically run on commit. To run manually:
|
|
47
|
-
```bash
|
|
48
|
-
# Run all pre-commit hooks
|
|
49
|
-
pre-commit run --all-files
|
|
50
|
-
|
|
51
|
-
# Format code with black
|
|
52
|
-
black asp_plot/ tests/
|
|
53
|
-
|
|
54
|
-
# Lint with flake8
|
|
55
|
-
flake8 asp_plot/ tests/
|
|
56
|
-
|
|
57
|
-
# Sort imports with isort (profile: black)
|
|
58
|
-
isort --profile black asp_plot/ tests/
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
Flake8 configuration is in `.flake8` (extends ignore: E203, E701). Pre-commit further extends ignore to: E501, E722, E203, E207.
|
|
62
|
-
|
|
63
|
-
### Rebuilding the Package
|
|
64
|
-
When making changes to CLI tools or entry points:
|
|
65
|
-
```bash
|
|
66
|
-
pip install -e ".[dev]"
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
### Building Docs Locally
|
|
70
|
-
```bash
|
|
71
|
-
# One-time: copy notebooks, reports, and figures for local preview
|
|
72
|
-
mkdir -p docs/examples/notebooks && cp notebooks/**/*.ipynb docs/examples/notebooks/
|
|
73
|
-
mkdir -p docs/_static/reports && cp reports/*.pdf docs/_static/reports/
|
|
74
|
-
mkdir -p docs/_extra/examples/figures && cp notebooks/figures/* docs/_extra/examples/figures/
|
|
75
|
-
|
|
76
|
-
# Build once
|
|
77
|
-
sphinx-build docs docs/_build/html
|
|
78
|
-
|
|
79
|
-
# Or live-reloading preview
|
|
80
|
-
sphinx-autobuild docs docs/_build/html --open-browser
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
## Code Architecture
|
|
84
|
-
|
|
85
|
-
### Core Module Structure
|
|
5
|
+
## Core Module Structure
|
|
86
6
|
|
|
87
7
|
**`__init__.py`** exports `__version__` via `importlib.metadata` (reads from `pyproject.toml` at install time, falls back to `"unknown"`).
|
|
88
8
|
|
|
@@ -104,16 +24,17 @@ The package is organized by functionality, with each module focused on a specifi
|
|
|
104
24
|
- `get_bounds()`: Returns bounds using `self.ds.bounds` (rasterio), with optional lat/lon transform
|
|
105
25
|
- `_mask_nodata()`: Private helper that handles both undeclared nodata (inferred from pixel data) and invalid values (inf/nan) — needed because rasterio's `masked=True` alone doesn't catch undeclared nodata in ASP outputs
|
|
106
26
|
- `_load_and_diff_rasters_da()`: Private static method returning xarray DataArray for raster differencing; used internally by both `load_and_diff_rasters()` (converts to numpy) and `compute_difference()` (uses `.rio.to_raster()` for saving)
|
|
107
|
-
- `Plotter`: Base class for all plotting classes with common matplotlib setup. The scaffold added in #129 centralizes the boilerplate that was copy-pasted across plot methods: `save(fig, save_dir, fig_fn, ...)` (the `tight_layout()` + conditional-save tail), `plot_missing(ax, message)` (the "required files are missing" placeholder), and `plot_array(..., copyright=True)` which draws the
|
|
27
|
+
- `Plotter`: Base class for all plotting classes with common matplotlib setup. The scaffold added in #129 centralizes the boilerplate that was copy-pasted across plot methods: `save(fig, save_dir, fig_fn, ...)` (the `tight_layout()` + conditional-save tail), `plot_missing(ax, message)` (the "required files are missing" placeholder), and `plot_array(..., copyright=True)` which draws the rights-holder overlay from `self.attribution` (a string like `"Vantor"` or `"Airbus DS"`, or None) so the check no longer threads through every call site
|
|
108
28
|
- `ColorBar`: Handles colorbar creation and formatting. `get_norm(clim=...)` takes an explicit clim so callers (e.g. `plot_geodataframe`) keep clim local instead of mutating `self.cb.clim`
|
|
109
|
-
- File utilities: `glob_file()
|
|
29
|
+
- File utilities: `glob_file()` (with `quiet=` for lookups whose absence is an expected layout), `save_figure()`, `show_existing_figure()`
|
|
30
|
+
- Multi-view layout helpers (issue #160): `find_pair_directories()` finds the `<prefix>-pairN/` subdirectories an ASP multi-view run keeps its per-pair products in; `get_pair_images()` recovers a pair's left/right image names from its `N-stereo.default` config copy (the `N-info.txt` lists *all* images of the joint run, so it is deliberately not used); `describe_pair()` builds the `"Pair N: <left> ↔ <right>"` label used on per-pair figures
|
|
110
31
|
- Coordinate utilities: `get_utm_epsg()` for determining UTM EPSG from lon/lat, `get_planetary_bounds()` for DEM bounds in planetocentric 0-360 lon/lat
|
|
111
32
|
- Planetary body detection: `detect_planetary_body(dem_fn)` returns `"earth"`, `"moon"`, or `"mars"` by inspecting CRS WKT DATUM/ELLIPSOID fields
|
|
112
33
|
- Subprocess utilities: `run_subprocess_command()`
|
|
113
|
-
-
|
|
114
|
-
- `
|
|
115
|
-
- `add_copyright_overlay(ax)`: Adds "©
|
|
116
|
-
- Scene metadata: `get_acquisition_dates(directory, extra_dirs=None)` reads `FIRSTLINETIME` from WorldView/Maxar XMLs and parses the capture timestamp from `AST_L1A_...` file/directory names. Returns a sorted, deduplicated list of `"YYYY-MM-DD HH:MM:SS UTC"` strings; empty if nothing is found. Used by the CLI to populate `ReportMetadata.acquisition_dates`.
|
|
34
|
+
- Copyright/attribution utilities (an **attribution** concern — named for the rights-holder — kept deliberately distinct from sensor/reader **identity** in `sensors.py`; #137):
|
|
35
|
+
- `detect_satellite_attribution(directory)`: returns the rights-holder name or None — `"Vantor"` when an XML camera file's `SATID` matches `VANTOR_SATID_PREFIXES` (the DigitalGlobe→Maxar→Vantor heritage: WorldView `WV*` incl. Legion `WVLG`, GeoEye `GE*`, QuickBird `QB*`, IKONOS `IK*`), `"Airbus DS"` when an Airbus DIMAP document (root tag `Dimap_Document`; Pléiades/Pléiades Neo/SPOT) is present. Gates the copyright overlay via `Plotter.attribution`. `detect_vantor_satellite(directory)` remains as a bool wrapper for backward compatibility
|
|
36
|
+
- `add_copyright_overlay(ax, attribution="Vantor")`: Adds "© {attribution} {year}" text overlay to bottom-right of matplotlib axes
|
|
37
|
+
- Scene metadata: `get_acquisition_dates(directory, extra_dirs=None)` reads `FIRSTLINETIME` from WorldView/Maxar XMLs, falls back to the DIMAP refined-model start time (`Refined_Model/Time/Time_Range/START`, via `_dimap_acquisition_datetime()`) for Airbus products, and parses the capture timestamp from `AST_L1A_...` file/directory names. Returns a sorted, deduplicated list of `"YYYY-MM-DD HH:MM:SS UTC"` strings; empty if nothing is found. Used by the CLI to populate `ReportMetadata.acquisition_dates`.
|
|
117
38
|
|
|
118
39
|
**`selections.py`** - Reproducible "figure selections" for run-to-run comparison (issue #121)
|
|
119
40
|
- `FigureSelections` dataclass mirroring the YAML sidecar schema (`detailed_hillshade` clips + `icesat2` track/segments/parquet/request); `to_dict()`/`from_dict()`
|
|
@@ -121,7 +42,7 @@ The package is organized by functionality, with each module focused on a specifi
|
|
|
121
42
|
- `pixel_window_to_bbox()` / `bbox_to_pixel_offset()`: convert a detailed-hillshade clip between a DEM-CRS map bbox and a top-left pixel offset. `bbox_to_pixel_offset` uses `rowcol(..., op=round)` (round-to-nearest, **not** floor) so a replayed clip doesn't drift by a pixel from floating-point error
|
|
122
43
|
- `reproject_bbox(bbox, src_crs, dst_crs)`: reprojects a clip bbox between CRSs (via `rasterio.warp.transform_bounds`) so clip reuse works across stereo variants in different projections (e.g. MOC non-mapproj Stereographic vs mapproj Sinusoidal). `plot_detailed_hillshade(clip_windows_crs=...)` passes the manifest's `dem_crs`; reprojection is a no-op when CRSs match
|
|
123
44
|
- Deliberately imports nothing from `report.py` / `fpdf`, so it is safe to use from notebooks
|
|
124
|
-
- The `
|
|
45
|
+
- The `asp_report` CLI always writes `<report_stem>_figure_selections.yml` next to the report; `--reuse_selections PATH` replays a prior run's choices
|
|
125
46
|
|
|
126
47
|
**`report.py`** - PDF report generation using fpdf2
|
|
127
48
|
- `ReportSection`: Dataclass representing a report figure (title, image path, caption)
|
|
@@ -135,11 +56,12 @@ The package is organized by functionality, with each module focused on a specifi
|
|
|
135
56
|
- Page order: title + DEM summary → Processing Parameters → diagnostic figures → (if `--pc_align` ran) alignment report page + aligned-DEM figures.
|
|
136
57
|
- `report.py` is fed declaratively by `report_pipeline.py`; it was **not** rewritten in #128.
|
|
137
58
|
|
|
138
|
-
**`report_pipeline.py`** - Declarative report pipeline behind the `
|
|
59
|
+
**`report_pipeline.py`** - Declarative report pipeline behind the `asp_report` CLI (issue #128)
|
|
139
60
|
- `ReportConfig`: dataclass packing the ~18 CLI options into one Click-free object (field names/defaults mirror the options one-for-one, guarded by a test)
|
|
140
61
|
- `run_report(config)`: importable/callable from notebooks and tests with no Click context; returns the written PDF path
|
|
141
62
|
- A declarative section registry (`REPORT_SECTIONS`) of `ReportSpec`s replaces the old inline plot-and-append wall: each spec pairs an `enabled(ctx)` predicate with a `build(ctx)` function returning the sections to append. `--plot_geometry` / `--plot_altimetry` / `--pc_align` gating are predicates; figure numbering comes from a per-run counter on the shared `ReportContext`, so section order and numbering are data, not source-line position. The alignment "Page B/C/D" follow-ups are one spec emitting several sections
|
|
142
63
|
- Section builders: `_build_input_scenes`, `_build_stereo_geometry`, `_build_match_points`, `_build_bundle_adjust`, `_build_disparity`, `_build_dem_results`, `_build_detailed_hillshade`, `_build_altimetry` (→ `_build_altimetry_earth` / `_build_altimetry_planetary`)
|
|
64
|
+
- `_numbered_sections()`: shared helper turning a plotter's saved-filename list into one `ReportSection` per figure (first gets the caption, continuations are titled "... (continued)") — used by the scenes/geometry/match/disparity builders, whose plotters save one figure per pair on multi-view runs (issue #160)
|
|
143
65
|
|
|
144
66
|
**`report_captions.py`** - Caption + description text for the report sections (issue #128)
|
|
145
67
|
- All section captions and the Earth/planetary alignment descriptions as module constants / small builder functions, moved out of the CLI as a data module (verified byte-identical to the pre-refactor strings via AST comparison)
|
|
@@ -162,7 +84,8 @@ The package is organized by functionality, with each module focused on a specifi
|
|
|
162
84
|
**`sensors.py`** - Sensor-specific scene metadata readers (issue #25)
|
|
163
85
|
- `SensorMetadata` ABC defining the reader interface (`detect` + `detect_files` + `get_scene_dicts`) and the sensor-agnostic scene-dict schema, mirroring the `bodies.py` registry pattern so adding ASTER/HiRISE/etc. is a new subclass with no change to the geometry code
|
|
164
86
|
- `WorldViewMetadata(SensorMetadata)`: the WorldView/Maxar XML logic (file discovery, `dg_mosaic` tiling, per-scene extraction, ephemeris/attitude/footprint) moved verbatim out of the parser. Named after the satellite family rather than the (twice-renamed: DigitalGlobe → Maxar → Vantor) company; the same format also covers GeoEye-1/QuickBird/IKONOS. Constructible from either a `directory=` (recursive discovery) or an explicit `image_list=` (already-resolved XML paths); non-camera files (`README.XML`, ortho) are filtered out either way
|
|
165
|
-
- `
|
|
87
|
+
- `PleiadesMetadata(SensorMetadata)`: Airbus Pléiades / Pléiades Neo DIMAP v2 reader. One scene per `DIM_*.XML` product (no tiling/mosaicking); `RPC_*.XML` sidecars share the DIMAP root tag but are filtered out by `METADATA_SUBPROFILE != PRODUCT` (cheap `iterparse` check). Mean view/sun angles and GSD are averaged over the nine `Located_Geometric_Values` blocks; `meansatel` = 90° − mean incidence. Ephemeris `Point_List` carries per-point `TIME` values (no fixed interval like WorldView). Airbus quaternions are **scalar-first** (`Q0`) and are reordered to the scalar-last `q1..q4` layout the roll/pitch/yaw code expects. DIMAP has no ephemeris/attitude covariance and no scan direction/TDI: `cov_*` columns are NaN and `scandir`/`tdi` are None (consumers treat these as "not provided")
|
|
88
|
+
- `SENSORS` registry + two detection entry points: `sensor_for_directory()` (scans a directory) and `sensor_for_inputs()` (accepts a mixed list of files/dirs/globs). Directory selection is **two-pass** — every sensor gets a shallow (top-level) `detect()` first, then a recursive pass — so a sensor whose metadata sits at the top level wins over one matching only inside a nested delivery, regardless of registry order; within a pass, order matters (`PleiadesMetadata` before the any-XML-matching `WorldViewMetadata`). `sensor_for_inputs()` routes through `resolve_xml_inputs()`, which expands directories (recursively), globs, and plain paths into a deduped, sorted XML list — this is what lets `stereo_geom *.XML` / explicit files / a delivery dir all work without a fixed structure
|
|
166
89
|
|
|
167
90
|
**`stereopair_metadata_parser.py`** - `StereopairMetadataParser` class
|
|
168
91
|
- Now a **sensor-agnostic orchestrator** (issue #25): detects a reader via `sensor_for_directory()` (from `directory=`) or `sensor_for_inputs()` (from a mixed `inputs=` list of files/dirs/globs), delegates scene discovery/extraction to it, and keeps only the pair-level geometry
|
|
@@ -178,7 +101,8 @@ The package is organized by functionality, with each module focused on a specifi
|
|
|
178
101
|
- Supports both map-projected and raw (non-georeferenced) imagery
|
|
179
102
|
- Detects map-projection status via `Raster.transform` check
|
|
180
103
|
- For non-mapprojected scenes: match points are overlaid on images using alignment transform matrices (`run-align-{L,R}.txt` loaded via `np.loadtxt`), and disparity plots use pixel-unit scalebar instead of GSD-based
|
|
181
|
-
-
|
|
104
|
+
- **Multi-view aware** (issue #160): `StereoFiles` resolves each `<prefix>-pairN/` subdirectory into a `PairStereoFiles` (field names mirror the top-level attributes so the per-figure helpers consume either, duck-typed); `plot_match_points()` / `plot_disparity()` render one figure per pair (`<stem>_pairN.png`) and return the saved filename list — `[fig_fn]` for a standard run — mirroring the `stereo_geom_plot()` N-scene contract
|
|
105
|
+
- Detects the imagery rights-holder via the `attribution` attribute (`detect_satellite_attribution`); adds the copyright overlay to optical imagery in `plot_match_points()` and `plot_detailed_hillshade()`
|
|
182
106
|
- `plot_detailed_hillshade()` auto-selects three subset clips from intersection-error variance (low/medium/high) via `_auto_hillshade_clip_offsets()`. Accepts `clip_windows` (DEM-CRS bboxes) + `clip_windows_crs` to pin/replay clips for run-to-run comparison (issue #121); records the boxes it drew on `self.detailed_hillshade_clips`. Out-of-bounds pinned boxes warn and fall back to auto.
|
|
183
107
|
- Key methods: `plot_dem_results()`, `plot_disparity()`, `plot_match_points()`, `plot_detailed_hillshade()`
|
|
184
108
|
|
|
@@ -251,16 +175,18 @@ The package is organized by functionality, with each module focused on a specifi
|
|
|
251
175
|
**`stereo_geometry.py`** - `StereoGeometryPlotter` class
|
|
252
176
|
- **Composes** a `StereopairMetadataParser` via `self.parser` rather than subclassing it (issue #25); imports `estim_satellite_orientation` from `csm_io` directly
|
|
253
177
|
- Visualizes stereo acquisition geometry from XML metadata
|
|
254
|
-
- `
|
|
255
|
-
- `
|
|
178
|
+
- `camera_files_from_stereo_run()`: recovers the camera metadata files named in a stereo run's command (parsed from the newest `*log-stereo*.txt` via `AspLog`, so a rerun into the same directory wins) so the report's geometry section covers only the run's scenes, not every camera file in the directory; returns None (→ directory fallback) when fewer than two cameras resolve (CSM `.json` cameras, unresolvable paths)
|
|
179
|
+
- `stereo_geom_plot()`: Creates skyplot (satellite viewing angles) and map view (footprints)
|
|
180
|
+
- `satellite_position_orientation_plot()`: Creates a 3-row × N-column figure (one column per scene) showing position covariance, roll/pitch/yaw orientation, and attitude covariance. Sensors without covariance data (e.g. Pléiades DIMAP) get plain position markers and an annotated "not provided" panel; scene labels omit scan/TDI when the sensor has none
|
|
256
181
|
|
|
257
182
|
**`scenes.py`** - `ScenePlotter` class (inherits from `Plotter`)
|
|
258
183
|
- Plots individual input scenes (satellite/spacecraft images)
|
|
259
184
|
- Works with any sensor (terrestrial or planetary)
|
|
260
185
|
- Automatically detects and indicates whether scenes are map-projected or raw
|
|
261
186
|
- Displays filenames rather than sensor-specific metadata
|
|
262
|
-
- Detects
|
|
263
|
-
- Key method: `plot_scenes()` (formerly `plot_orthos()` prior to v1.2.0)
|
|
187
|
+
- Detects the imagery rights-holder via the `attribution` attribute (`detect_satellite_attribution`); adds the copyright overlay to scene images in `plot_scenes()`
|
|
188
|
+
- Key method: `plot_scenes()` (formerly `plot_orthos()` prior to v1.2.0); returns the saved filename list
|
|
189
|
+
- **Multi-view aware** (issue #160): when `<prefix>-pairN/` subdirectories are present, `SceneFiles` resolves per-pair `PairSceneFiles` from them (pairs win over any stale top-level sub-sampled scenes, matching `StereoFiles`) and `plot_scenes()` renders one figure per pair ("Left (reference)" panel labeling); the "missing files" placeholder remains the fallback
|
|
264
190
|
- Used in comprehensive reports to show source imagery
|
|
265
191
|
|
|
266
192
|
**`gallery.py`** - `GalleryPlotter` class (inherits from `Plotter`)
|
|
@@ -271,13 +197,13 @@ The package is organized by functionality, with each module focused on a specifi
|
|
|
271
197
|
- Per-panel titles use the full filename, auto-shrunk to fit the panel by *measuring* rendered text width with an Agg renderer (`_fit_titles()`); falls back to a character-count heuristic (`_fit_title_fontsize()`) if measurement fails
|
|
272
198
|
- Output detail is read at ~`GALLERY_TARGET_PX` (1200) px/panel and save dpi is matched to it for crisp zoom, then dpi is capped to a pixel budget so the PNG stays under `max_filesize_mb` (default 10) regardless of raster count
|
|
273
199
|
- Key method: `plot_gallery()`; static helper `_grid_shape(n, aspect)` picks the most square-in-display grid
|
|
274
|
-
- Not wired into the main `
|
|
200
|
+
- Not wired into the main `asp_report` PDF report (standalone class + `gallery` CLI)
|
|
275
201
|
|
|
276
|
-
|
|
202
|
+
## CLI Tools
|
|
277
203
|
|
|
278
204
|
All CLI tools are in `asp_plot/cli/` and use Click for argument parsing:
|
|
279
205
|
|
|
280
|
-
**`
|
|
206
|
+
**`asp_report.py`** - Main CLI tool (`asp_report` command)
|
|
281
207
|
- Generates comprehensive PDF reports of ASP processing
|
|
282
208
|
- A **thin Click wrapper** (issue #128): parses the ~18 options into a `ReportConfig` and calls `report_pipeline.run_report(config)`. All orchestration lives in `report_pipeline.py` (see the module structure section); the CLI file itself is now just option definitions + the `ReportConfig` build
|
|
283
209
|
- Accepts directories for stereo and bundle_adjust outputs
|
|
@@ -299,8 +225,8 @@ All CLI tools are in `asp_plot/cli/` and use Click for argument parsing:
|
|
|
299
225
|
- `--plot_geometry`: Plot stereo geometry (default: True; disable for planetary missions)
|
|
300
226
|
- `--subset_km`: Hillshade subset size in km (default: 1.0)
|
|
301
227
|
- `--atl06sr_time_range`: Time range for ICESat-2 ATL06-SR requests. `"all"` (default) for full mission, `"auto"` for scene metadata ±1 year, `"START,END"` for a custom range, or a single date (buffered by ±1 year).
|
|
302
|
-
- `--reuse_selections`: Path to a `*_figure_selections.yml` from a prior run. Replays that run's ICESat-2 points (parquet), profile track, best/worst segments, and detailed-hillshade clips so re-processing runs (e.g. mapproj vs non-mapproj) are directly comparable (issue #121). Every run always writes `<report_stem>_figure_selections.yml` next to the report (the `regenerate_reports.sh` paired variants use this to reuse each other). Generated sidecars are gitignored (they hardcode absolute local paths); a sanitized example is in `docs/cli/
|
|
303
|
-
- `--report_filename`: PDF report filename or path. A bare filename saves in the stereo directory; a path (e.g. `reports/report.pdf`) is used as-is
|
|
228
|
+
- `--reuse_selections`: Path to a `*_figure_selections.yml` from a prior run. Replays that run's ICESat-2 points (parquet), profile track, best/worst segments, and detailed-hillshade clips so re-processing runs (e.g. mapproj vs non-mapproj) are directly comparable (issue #121). Every run always writes `<report_stem>_figure_selections.yml` next to the report (the `regenerate_reports.sh` paired variants use this to reuse each other). Generated sidecars are gitignored (they hardcode absolute local paths); a sanitized example is in `docs/cli/asp_report.md`.
|
|
229
|
+
- `--report_filename`: PDF report filename or path. A bare filename saves in the stereo directory; a path (e.g. `reports/report.pdf`) is used as-is. Default is auto-generated as `asp_report_<report_title>_<UTC timestamp>.pdf`
|
|
304
230
|
- `--report_title`: Custom report title (default: directory name)
|
|
305
231
|
|
|
306
232
|
**`csm_camera_plot.py`** - CSM camera comparison tool (`csm_camera_plot` command)
|
|
@@ -315,14 +241,14 @@ All CLI tools are in `asp_plot/cli/` and use Click for argument parsing:
|
|
|
315
241
|
- `--email`: Email for notification when query finishes (required)
|
|
316
242
|
- `--channels`: LOLA detector channels (Moon only, default `tffff` = channel 1 only)
|
|
317
243
|
- Saves request metadata as `altimetry_request_info.yml` alongside the DEM
|
|
318
|
-
- Workflow: submit query → receive email → download/unzip → pass `*_pts_csv.csv` (Mars) or `*_topo_simple_csv.csv`/`*_pts_csv.csv` (Moon) to `
|
|
244
|
+
- Workflow: submit query → receive email → download/unzip → pass `*_pts_csv.csv` (Mars) or `*_topo_simple_csv.csv`/`*_pts_csv.csv` (Moon) to `asp_report --altimetry_csv`
|
|
319
245
|
|
|
320
246
|
**`stereo_geom.py`** - Stereo geometry visualization tool (`stereo_geom` command)
|
|
321
247
|
- Wrapper for `StereoGeometryPlotter`
|
|
322
248
|
- Creates skyplot and map view from XML camera files
|
|
323
249
|
- Supports multiple XMLs with automatic mosaicking
|
|
324
250
|
- Accepts positional `INPUTS` (any mix of XML files, directories, and globs — e.g. `stereo_geom *.XML`); falls back to `--directory` when no positional inputs are given. Both paths funnel into the same plotter (`inputs=` vs `directory=`)
|
|
325
|
-
- **N-scene output** (issue #73): two scenes → one `<name>_stereo_geom.png` (unchanged). More than two → one color-coded overview figure (`_overview.png`) plus one figure per pair (`_<catidA>_<catidB>.png`, or `_pairN.png` if a CATID is missing), each with full pairwise stats. `
|
|
251
|
+
- **N-scene output** (issue #73): two scenes → one `<name>_stereo_geom.png` (unchanged). More than two → one color-coded overview figure (`_overview.png`) plus one figure per pair (`_<catidA>_<catidB>.png`, or `_pairN.png` if a CATID is missing), each with full pairwise stats. `stereo_geom_plot()` dispatches on scene count and returns the list of saved filenames; `StereoGeometryPlotter` adds `_render_pair`/`_render_overview`. Map views keep the full (autoscaled) extent so all satellite ephemeris tracks are visible (off-nadir scenes have tracks tens to hundreds of km from the footprints), with tracks drawn on top of the semi-transparent footprints; `_add_basemap_safe` falls back to a coarse zoom so the wide-extent basemap fetch doesn't fail on contextily's negative auto-zoom
|
|
326
252
|
|
|
327
253
|
**`gallery.py`** - DEM gallery tool (`gallery` command)
|
|
328
254
|
- Wrapper for `GalleryPlotter`; lays out many DEMs as a grid sharing one color scale
|
|
@@ -348,7 +274,7 @@ docs/
|
|
|
348
274
|
conf.py # Sphinx configuration
|
|
349
275
|
index.md # Landing page with sphinx-design cards
|
|
350
276
|
installation.md # conda/pip/source install
|
|
351
|
-
cli/ # CLI tool docs (
|
|
277
|
+
cli/ # CLI tool docs (asp_report, stereo_geom, csm_camera_plot, request_planetary_altimetry, gallery)
|
|
352
278
|
examples/
|
|
353
279
|
index.md # Notebook gallery with cards by sensor
|
|
354
280
|
reports.md # PDF reports embedded as iframes
|
|
@@ -369,7 +295,7 @@ docs/
|
|
|
369
295
|
- **`html_extra_path`** is used to serve `notebooks/figures/` at the correct relative path for notebook `<img src>` references.
|
|
370
296
|
- **`docs/` is excluded from sdist** in `pyproject.toml` so docs never ship in the PyPI/conda package.
|
|
371
297
|
- **Changelog** uses `{include} ../CHANGELOG.md` so there's one source of truth.
|
|
372
|
-
- **Selective notebook exclusion**: `.readthedocs.yaml`'s `pre_build` copies every `notebooks/**/*.ipynb` into `docs/examples/notebooks/`, but specific notebooks can be dropped from the build by listing them in `exclude_patterns` in `docs/conf.py` (e.g., `worldview_utqiagvik_stereo.ipynb`). Inter-notebook links in the WorldView examples use fully-qualified `https://asp-plot.readthedocs.io/en/latest/...` URLs so they resolve both on RTD and in raw notebook previews. Report-link convention: each notebook's "Full Report Generation" section ends with a `#### See the resulting [report](https://asp-plot.readthedocs.io/en/latest/_static/reports/<filename>.pdf).` line that links directly to the PDF served from `_static/reports/`. The `<filename>` must match what the cell's `!
|
|
298
|
+
- **Selective notebook exclusion**: `.readthedocs.yaml`'s `pre_build` copies every `notebooks/**/*.ipynb` into `docs/examples/notebooks/`, but specific notebooks can be dropped from the build by listing them in `exclude_patterns` in `docs/conf.py` (e.g., `worldview_utqiagvik_stereo.ipynb`). Inter-notebook links in the WorldView examples use fully-qualified `https://asp-plot.readthedocs.io/en/latest/...` URLs so they resolve both on RTD and in raw notebook previews. Report-link convention: each notebook's "Full Report Generation" section ends with a `#### See the resulting [report](https://asp-plot.readthedocs.io/en/latest/_static/reports/<filename>.pdf).` line that links directly to the PDF served from `_static/reports/`. The `<filename>` must match what the cell's `!asp_report --report_filename` writes (so the URL on RTD actually resolves).
|
|
373
299
|
|
|
374
300
|
### Dependencies
|
|
375
301
|
|
|
@@ -387,17 +313,7 @@ Docs dependencies are in `pyproject.toml` under `[project.optional-dependencies]
|
|
|
387
313
|
|
|
388
314
|
**External Tool Integration**: The package wraps ASP command-line tools (pc_align, geodiff, dg_mosaic) via `run_subprocess_command()`.
|
|
389
315
|
|
|
390
|
-
**Report Generation**: The main `
|
|
391
|
-
|
|
392
|
-
## ASP Tool Dependencies
|
|
393
|
-
|
|
394
|
-
This package is designed to work with outputs from the NASA Ames Stereo Pipeline. Key ASP tools used:
|
|
395
|
-
- `stereo` / `parallel_stereo`: Main stereo processing (generates DEMs, disparity maps, match files)
|
|
396
|
-
- `bundle_adjust`: Camera optimization (generates residual pointmaps)
|
|
397
|
-
- `pc_align`: Point cloud alignment (used for DEM-to-altimetry alignment)
|
|
398
|
-
- `geodiff`: Generates difference statistics between DEMs
|
|
399
|
-
- `dg_mosaic`: Mosaics multiple XML files for tiled imagery
|
|
400
|
-
- `point2dem`: Converts point clouds to DEMs
|
|
316
|
+
**Report Generation**: The main `asp_report` CLI uses `compile_report()` to combine individual plots into a single PDF with metadata tables.
|
|
401
317
|
|
|
402
318
|
## External Data Sources
|
|
403
319
|
|
|
@@ -411,41 +327,12 @@ This package is designed to work with outputs from the NASA Ames Stereo Pipeline
|
|
|
411
327
|
|
|
412
328
|
**Basemaps**: Uses `contextily` to fetch Esri WorldImagery tiles (requires internet). Can be disabled with `--add_basemap False`. Automatically skipped for planetary (non-Earth) DEMs.
|
|
413
329
|
|
|
414
|
-
##
|
|
415
|
-
|
|
416
|
-
Tests are in `tests/` with sample data in `tests/test_data/`. Most modules have corresponding test files (e.g., `test_stereo.py` for `stereo.py`). Test data includes synthetic rasters, XML camera files, bundle adjustment CSVs, ICESat-2 parquet files, pc_align outputs, and jitter correction data. There is also a `test_imports.py` that verifies all modules can be imported.
|
|
330
|
+
## Example Notebooks
|
|
417
331
|
|
|
418
332
|
Example notebooks demonstrating modular usage are organized by sensor type:
|
|
419
|
-
- `notebooks/WorldView/` -
|
|
333
|
+
- `notebooks/WorldView/` - WorldView examples (Earth-based). `worldview_spacenet_ucsd_stereo.ipynb` is the classic two-scene pair example; `worldview_utqiagvik_stereo.ipynb` (excluded from the docs build) and `worldview_uyuni_jitter_plots.ipynb` cover comprehensive modular usage and jitter plots; the two `*_scene_selection.ipynb` notebooks pick which scenes to process with `stereo_geom` before any ASP run. The Atlanta example (`worldview_spacenet_atlanta_mvs.ipynb`) is a 3-scene same-pass multi-view run (`parallel_stereo --alignment-method affineepipolar` on the `wv_correct`ed `*_corr.tif` images, nadir13 reference) compared against the ASP-docs-recommended alternative: the three pairwise runs merged with `dem_mosaic` (coverage, DEM difference, ICESat-2 residuals). Two crop-window tricks keep the ROIs matched: the reference crop window in mapprojected/ground coordinates converts to image pixels via GDAL's inverse RPC transform on the *original* `.tif` (the `wv_correct` `_corr.tif` carries no RPC metadata) — `gdal.Transformer(["METHOD=RPC", "RPC_HEIGHT=<m>"])` + `TransformPoint(1, lon, lat, h)` on the corners; and the `--left-image-crop-win` for a pair whose left image is *not* the reference is derived from the `bundle_adjust` clean match points that fall inside the reference crop window (no RPCs needed at all).
|
|
334
|
+
- `notebooks/Pleiades/` - Airbus Pléiades Neo tri-stereo example over Marseille (Earth-based): DIMAP stereo-geometry analysis, bundle adjustment of all three scenes, and a 3-image multi-view stereo run (nadir reference, `--left-image-crop-win` derived from the DIMAP `Dataset_Extent` vertices; the ASP docs discourage MVS in favor of pairwise + `dem_mosaic`, which the notebook cites)
|
|
420
335
|
- `notebooks/ASTER/` - ASTER examples with map-projection and jitter correction (Earth-based)
|
|
421
336
|
- `notebooks/LRO_NAC/` - Lunar Reconnaissance Orbiter Narrow Angle Camera examples (Lunar)
|
|
422
337
|
- `notebooks/Mars_MGS/` - Mars Global Surveyor MOC NA examples (Mars). Single notebook covers both mapprojected (`cam2map4stereo.py` first) and non-mapprojected (`--alignment-method affineepipolar`) variants of the M0100115 / E0201461 pair, mirroring the ASTER mapproj/non-mapproj layout
|
|
423
338
|
- `notebooks/Mars_MRO/` - Mars Reconnaissance Orbiter CTX and HiRISE examples (Mars)
|
|
424
|
-
|
|
425
|
-
## Versioning and Release Process
|
|
426
|
-
|
|
427
|
-
Follow semantic versioning (MAJOR.MINOR.PATCH). To release:
|
|
428
|
-
1. Update version in `pyproject.toml`
|
|
429
|
-
2. Update `CHANGELOG.md` with version and date
|
|
430
|
-
3. Merge to `main`
|
|
431
|
-
|
|
432
|
-
The rest is automated:
|
|
433
|
-
- `release.yml` detects the version bump, creates a GitHub Release + tag, then builds and publishes to PyPI via OIDC trusted publishing (uses `softprops/action-gh-release@v2`)
|
|
434
|
-
- conda-forge's `regro-cf-autotick-bot` detects the new PyPI version and opens a feedstock PR automatically
|
|
435
|
-
|
|
436
|
-
**One-time setup** (already completed):
|
|
437
|
-
- PyPI trusted publisher configured at pypi.org for `release.yml` (environment name left blank)
|
|
438
|
-
- conda-forge feedstock created via `staged-recipes` PR (reference recipe in `conda-forge-recipe/meta.yaml`)
|
|
439
|
-
- All runtime dependencies declared in `pyproject.toml` (`pip install asp-plot` installs all deps)
|
|
440
|
-
- Documentation hosted on ReadTheDocs (auto-builds on push to `main`)
|
|
441
|
-
|
|
442
|
-
## Common File Patterns
|
|
443
|
-
|
|
444
|
-
ASP output files follow specific naming patterns:
|
|
445
|
-
- DEMs: `*-DEM.tif` or `*_dem.tif`
|
|
446
|
-
- Disparity: `*-F.tif` (disparity map)
|
|
447
|
-
- Match files: `*.match`
|
|
448
|
-
- Bundle adjust residuals: `*-initial_residuals_pointmap.csv`, `*-final_residuals_pointmap.csv`
|
|
449
|
-
- Log files: `*log-bundle_adjust*.txt`, `*log-stereo*.txt`, `*log-point2dem*.txt`
|
|
450
|
-
|
|
451
|
-
Use `glob_file()` utility to find files matching these patterns.
|