asp-plot 1.12.1__tar.gz → 1.14.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.
Files changed (30) hide show
  1. {asp_plot-1.12.1 → asp_plot-1.14.0}/.gitignore +1 -0
  2. {asp_plot-1.12.1 → asp_plot-1.14.0}/CHANGELOG.md +37 -0
  3. {asp_plot-1.12.1 → asp_plot-1.14.0}/PKG-INFO +1 -1
  4. {asp_plot-1.12.1 → asp_plot-1.14.0}/asp_plot/alignment.py +108 -2
  5. {asp_plot-1.12.1 → asp_plot-1.14.0}/asp_plot/altimetry.py +918 -191
  6. {asp_plot-1.12.1 → asp_plot-1.14.0}/asp_plot/cli/asp_plot.py +273 -2
  7. {asp_plot-1.12.1 → asp_plot-1.14.0}/asp_plot/report.py +246 -35
  8. {asp_plot-1.12.1 → asp_plot-1.14.0}/pyproject.toml +1 -1
  9. {asp_plot-1.12.1 → asp_plot-1.14.0}/.flake8 +0 -0
  10. {asp_plot-1.12.1 → asp_plot-1.14.0}/.github/workflows/release.yml +0 -0
  11. {asp_plot-1.12.1 → asp_plot-1.14.0}/.github/workflows/run-tests.yml +0 -0
  12. {asp_plot-1.12.1 → asp_plot-1.14.0}/.pre-commit-config.yaml +0 -0
  13. {asp_plot-1.12.1 → asp_plot-1.14.0}/.readthedocs.yaml +0 -0
  14. {asp_plot-1.12.1 → asp_plot-1.14.0}/LICENSE +0 -0
  15. {asp_plot-1.12.1 → asp_plot-1.14.0}/README.md +0 -0
  16. {asp_plot-1.12.1 → asp_plot-1.14.0}/asp_plot/__init__.py +0 -0
  17. {asp_plot-1.12.1 → asp_plot-1.14.0}/asp_plot/bundle_adjust.py +0 -0
  18. {asp_plot-1.12.1 → asp_plot-1.14.0}/asp_plot/cli/__init__.py +0 -0
  19. {asp_plot-1.12.1 → asp_plot-1.14.0}/asp_plot/cli/csm_camera_plot.py +0 -0
  20. {asp_plot-1.12.1 → asp_plot-1.14.0}/asp_plot/cli/request_planetary_altimetry.py +0 -0
  21. {asp_plot-1.12.1 → asp_plot-1.14.0}/asp_plot/cli/stereo_geom.py +0 -0
  22. {asp_plot-1.12.1 → asp_plot-1.14.0}/asp_plot/csm_camera.py +0 -0
  23. {asp_plot-1.12.1 → asp_plot-1.14.0}/asp_plot/processing_parameters.py +0 -0
  24. {asp_plot-1.12.1 → asp_plot-1.14.0}/asp_plot/scenes.py +0 -0
  25. {asp_plot-1.12.1 → asp_plot-1.14.0}/asp_plot/stereo.py +0 -0
  26. {asp_plot-1.12.1 → asp_plot-1.14.0}/asp_plot/stereo_geometry.py +0 -0
  27. {asp_plot-1.12.1 → asp_plot-1.14.0}/asp_plot/stereopair_metadata_parser.py +0 -0
  28. {asp_plot-1.12.1 → asp_plot-1.14.0}/asp_plot/utils.py +0 -0
  29. {asp_plot-1.12.1 → asp_plot-1.14.0}/conda-forge-recipe/meta.yaml +0 -0
  30. {asp_plot-1.12.1 → asp_plot-1.14.0}/environment.yml +0 -0
@@ -143,3 +143,4 @@ CLAUDE*
143
143
  scripts/
144
144
  /*.parquet
145
145
  reports/regenerate_reports.sh
146
+ *.csv
@@ -5,6 +5,43 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.14.0] - 2026-04-28
9
+
10
+ ### Added
11
+ - **Automatic `pc_align` step in the planetary altimetry block** ([#119](https://github.com/uw-cryo/asp_plot/pull/119)). The existing `--pc_align` CLI flag now also runs against MOLA (Mars) and LOLA (Moon) — previously Earth/ICESat-2 only. Mirrors the Earth pipeline: a single alignment-report page on `insufficient_points` / `no_improvement`, plus a pre/post mapview and pre/post histogram on `success`.
12
+ - **`Altimetry.align_and_evaluate_planetary(...)`**: planetary sibling of `align_and_evaluate`. Returns the same `AlignmentResult` dataclass; defaults `max_displacement=500` m (per ASAP-Stereo's CTX cookbook) and `minimum_points=20` (planetary tracks are sparse).
13
+ - **`Alignment.pc_align_dem_to_planetary_csv(...)`**: invokes ASP `pc_align` with `--csv-format '1:lon 2:lat 3:radius_m'` and `--datum D_MARS`/`D_MOON` (aligned with the [ASP `next_steps` documentation on MOLA alignment](https://stereopipeline.readthedocs.io/en/latest/next_steps.html)).
14
+ - **`Altimetry.to_csv_for_pc_align_planetary()`**: writes `lon, lat, radius_m` from `self.planetary_points` to drive `pc_align`.
15
+ - **`plot_aligned` kwarg** on `Altimetry.mapview_plot_planetary_to_dem` and `Altimetry.histogram_planetary_to_dem`: pre/post panels share color/bin scales when an aligned DEM is available.
16
+ - **Module-level constants** `MARS_IAU_SPHERE_RADIUS = 3_396_190.0` and `MOON_IAU_SPHERE_RADIUS = 1_737_400.0` so callers can reconstruct ASP-style "height above sphere" without magic numbers.
17
+
18
+ ### Changed
19
+ - **MOLA loader switched to `PLANET_RAD`**. `_load_mola_csv()` now reads the absolute planetary radius from the ODE GDS `*_pts_csv.csv` and computes `height = PLANET_RAD - 3,396,190` (IAU 2000 Mars sphere). The `*_topo_csv.csv` (TOPOGRAPHY only) is **rejected** with an explanatory error: TOPOGRAPHY is referenced to the **oblate** MOLA areoid while ASP DEMs use the **spherical** IAU 2000 datum, so dh from TOPOGRAPHY carries a latitude-dependent offset of up to ~10 km that `pc_align` cannot remove. Verified on the MOC NA tutorial scene at lat 34°N: signed median dh dropped from +6,000 m (TOPOGRAPHY path) → +99.74 m (PLANET_RAD path) → +3.13 m (after `pc_align`). Reference: [MOLA PEDR Software Interface Specification (PDS Geosciences)](https://pds-geosciences.wustl.edu/mgs/mgs-m-mola-3-pedr-l1a-v1/mgsl_21xx/document/pedrsis.pdf).
20
+ - **LOLA loader prefers `Pt_Radius` (km) when available**. The Point per Row LOLA RDR CSV (`results=p`) carries `Pt_Radius` in **kilometers**; the simple Topography CSV (`results=u`) carries Topography in meters. `_load_lola_csv()` auto-detects km by magnitude (< 10 000) and converts to meters, then writes both `height` (m above the IAU 1737.4 km lunar sphere) and `radius_m` to `self.planetary_points`. The Moon is essentially spherical (~1.4 km equatorial-vs-polar variation), so either CSV gives the same dh to ~1 m. Reference: [ODE GDS REST V2.0 manual](https://oderest.rsl.wustl.edu/GDS_REST_V2.0.pdf).
21
+ - **`Alignment.apply_dem_translation()` is body-aware**. Picks a body-centered geocentric "ECEF-equivalent" CRS from a new module-level `_GEOCENTRIC_PROJ` dict — Earth uses `EPSG:4978`; Mars/Moon use PROJ strings (`+proj=geocent +R=...`) because PROJ refuses to convert across celestial bodies. Without this fix, applying a `pc_align` translation to a Mars/Moon DEM raised `RuntimeError: Source and target ellipsoid do not belong to the same celestial body`.
22
+ - **`planetary_to_dem_dh()` also samples the aligned DEM** when `self.aligned_dem_fn` is set, populating `aligned_dem_height` and `altimetry_minus_aligned_dem` so pre/post plots share a single sample. Refactored shared interpolation into `_sample_dem_at_planetary_points()`.
23
+
24
+ ### Documentation
25
+ - **MOC NA notebook consolidated** into `notebooks/Mars_MGS/mars_mgs_orbital_camera.ipynb` covering both stereo variants of the M0100115 / E0201461 pair (the `mars_mgs_orbital_camera_narrow_angle.ipynb` notebook for a different scene pair was removed). Mirrors the ASTER mapproj/non-mapproj layout. Stereo commands match this repo's WorldView convention (`parallel_stereo --stereo-algorithm asp_mgm --subpixel-mode 9 --processes 2 --threads 4`, `--alignment-method affineepipolar` for non-mapprojected, `--alignment-method none` for mapprojected via `cam2map4stereo.py`). The notebook intro includes a callout explaining the spherical-vs-oblate elevation-range surprise. Reports: `MOC-asp-plot-report.pdf` and `MOC_mapproj-asp-plot-report.pdf`.
26
+ - **LRO NAC notebook reprocessed on the full 5000×5000 cubes** in [`LRONAC_example.tar`](https://github.com/NeoGeographyToolkit/StereoPipelineSolvedExamples/releases/download/LRONAC/LRONAC_example.tar) instead of the 900×973 sub-window the ASP "lightning fast" tutorial uses. Resulting DEM is 4720×4510 at 1.04 m GSD (~4.7 km × 4.7 km, vs the old ~1 km × 1 km), 95.88% valid pixels. LOLA query expanded to match: 1539 of 2044 LOLA points overlap the DEM (vs 12 of 19 on the old crop), enough for a meaningful `pc_align` and to bring out spacecraft jitter in the disparity panels.
27
+
28
+ ## [1.13.0] - 2026-04-20
29
+
30
+ ### Added
31
+ - **Automatic `pc_align` step in the Earth altimetry block**, gated by a new `--pc_align` CLI flag (default `True`; disabled automatically when `--plot_altimetry` / `--plot_icesat` is `False`). Runs `pc_align` against ICESat-2 ATL06-SR, evaluates whether the aligned DEM is worth keeping, and appends the outcome as one or more report pages:
32
+ - Always: an **alignment report page** with the parameters table, a single-row horizontal stats table (p16/p50/p84 beg/end, north/east/down shifts, translation magnitude, values to 2 sig figs), a description explaining what `pc_align` does and the meaning of every column in the tables above, and a bold status line for the outcome of this run.
33
+ - On success (p50 drops toward 0 by more than `improvement_threshold_pct`, default 5%, **and** `pc_align` actually wrote an aligned DEM): three additional full-page diagnostic figures against the aligned DEM — a pre-/post-alignment landcover histogram, the full profile, and the best/worst 1 km segments.
34
+ - On insufficient ATL06-SR coverage or no meaningful improvement: the aligned DEM on disk is cleaned up so its presence is a truthy signal that the alignment is worth using.
35
+ - **`Altimetry.align_and_evaluate(...)`** (new method) returning a plain `AlignmentResult` dataclass (`status ∈ {"insufficient_points", "no_improvement", "success"}`, `alignment_report_df`, `aligned_dem_fn`, `improvement_pct`, `message`, `parameters_used`). Does not import any `fpdf` / report dependencies, so it is safe to call from notebooks.
36
+ - **`plot_aligned` kwargs** on `Altimetry.histogram_by_landcover` and `Altimetry.plot_best_worst_segments`:
37
+ - `histogram_by_landcover(plot_aligned=True)` overlays the pre- and post-alignment distributions using shared bin edges and renders two vertically stacked per-landcover stats text boxes whose outline colors match the bar colors (color = legend).
38
+ - `plot_best_worst_segments(plot_aligned=True)` keeps segment selection fixed (based on the unaligned `dh` so segments are comparable), overlays aligned DEM heights on each segment, and appends aligned Median/NMAD to the segment titles.
39
+ - **`AlignmentReportPage`** dataclass in `asp_plot.report`: a report-section type that renders a kwargs table + single-row stats table + description + bold status line + optional figure with caption. Body text blocks render left-aligned to avoid justified word-spacing gaps.
40
+
41
+ ### Changed
42
+ - **Processing Parameters is now page 2 of the PDF**, immediately after the DEM Summary on the title page, instead of the trailing appendix. Page order is now: title + DEM summary → processing parameters → diagnostic figures → (if any) alignment results.
43
+ - **`plot_atl06sr_dem_profile(plot_aligned=True)`**: the lower `dh` panel now plots the post-alignment residuals (`icesat_minus_aligned_dem`) with Med/NMAD recomputed against the aligned DEM, with the legend entry tagged `"(Aligned DEM)"`. The upper elevation panel still overlays both the unaligned and aligned DEM for comparison. `plot_aligned=False` behavior is unchanged.
44
+
8
45
  ## [1.12.1] - 2026-04-14
9
46
 
10
47
  ### Changed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: asp_plot
3
- Version: 1.12.1
3
+ Version: 1.14.0
4
4
  Summary: Package for plotting outputs Ames Stereo Pipeline processing
5
5
  Project-URL: Homepage, https://github.com/uw-cryo/asp_plot
6
6
  Project-URL: Documentation, https://asp-plot.readthedocs.io
@@ -5,7 +5,23 @@ import re
5
5
  import numpy as np
6
6
  from osgeo import gdal, osr
7
7
 
8
- from asp_plot.utils import Raster, glob_file, run_subprocess_command
8
+ from asp_plot.utils import (
9
+ Raster,
10
+ detect_planetary_body,
11
+ glob_file,
12
+ run_subprocess_command,
13
+ )
14
+
15
+ # Body-centered geocentric ("ECEF-equivalent") CRS for each supported
16
+ # planet. Used by apply_dem_translation to convert pc_align's Cartesian
17
+ # translation vector into the DEM's projected coordinate system. The
18
+ # Earth case is keyed via EPSG; planets need a PROJ string because PROJ
19
+ # refuses to operate across celestial bodies.
20
+ _GEOCENTRIC_PROJ = {
21
+ "earth": None, # use EPSG:4978
22
+ "moon": "+proj=geocent +R=1737400 +units=m +no_defs",
23
+ "mars": "+proj=geocent +R=3396190 +units=m +no_defs",
24
+ }
9
25
 
10
26
  logging.basicConfig(level=logging.WARNING)
11
27
  logger = logging.getLogger(__name__)
@@ -134,6 +150,86 @@ class Alignment:
134
150
 
135
151
  run_subprocess_command(command)
136
152
 
153
+ def pc_align_dem_to_planetary_csv(
154
+ self,
155
+ planetary_csv,
156
+ body,
157
+ max_displacement=500,
158
+ max_source_points=10000000,
159
+ alignment_method="point-to-point",
160
+ output_prefix="pc_align/pc_align",
161
+ ):
162
+ """
163
+ Align DEM to MOLA/LOLA point cloud using pc_align.
164
+
165
+ Parameters
166
+ ----------
167
+ planetary_csv : str
168
+ Path to a CSV with ``lon``, ``lat``, ``radius_m`` columns
169
+ (planetary radius in meters from the body center). Produced by
170
+ :meth:`asp_plot.altimetry.Altimetry.to_csv_for_pc_align_planetary`.
171
+ body : str
172
+ ``"moon"`` or ``"mars"`` — selects the ASP ``--datum`` flag.
173
+ max_displacement : float, optional
174
+ Maximum expected displacement in meters. ASAP-Stereo's CTX
175
+ cookbook recommends ~500 m as a generic default for planetary
176
+ stereo where absolute pointing is uncertain.
177
+ max_source_points : int, optional
178
+ Maximum number of source DEM points pc_align will sample.
179
+ alignment_method : str, optional
180
+ ASP alignment method. Default ``point-to-point``.
181
+ output_prefix : str, optional
182
+ Prefix for pc_align output files, relative to ``self.directory``.
183
+
184
+ Notes
185
+ -----
186
+ Uses ``--csv-format '1:lon 2:lat 3:radius_m'`` so pc_align reads
187
+ the absolute planetary radius and avoids any datum/areoid
188
+ ambiguity. ``--datum`` is set to ``D_MARS`` (3,396,190 m) or
189
+ ``D_MOON`` (1,737,400 m) to match ASP DEM heights, which are
190
+ stored as ``radius - sphere``.
191
+ """
192
+ if not os.path.exists(planetary_csv):
193
+ raise ValueError(
194
+ f"\nPlanetary altimetry CSV not found: {planetary_csv}\n"
195
+ "Use Altimetry.to_csv_for_pc_align_planetary() to create it.\n"
196
+ )
197
+
198
+ datum = {"moon": "D_MOON", "mars": "D_MARS"}.get(body)
199
+ if datum is None:
200
+ raise ValueError(
201
+ f"Unsupported body for pc_align_dem_to_planetary_csv: {body}"
202
+ )
203
+
204
+ pc_align_folder = os.path.join(self.directory, output_prefix)
205
+
206
+ print(
207
+ f"Running pc_align on {self.dem_fn} and {planetary_csv}\n"
208
+ f" --datum {datum}, --max-displacement {max_displacement}\n"
209
+ f"Writing to {pc_align_folder}*"
210
+ )
211
+
212
+ command = [
213
+ "pc_align",
214
+ "--max-displacement",
215
+ str(max_displacement),
216
+ "--max-num-source-points",
217
+ str(max_source_points),
218
+ "--alignment-method",
219
+ alignment_method,
220
+ "--csv-format",
221
+ "1:lon 2:lat 3:radius_m",
222
+ "--datum",
223
+ datum,
224
+ "--compute-translation-only",
225
+ "--output-prefix",
226
+ pc_align_folder,
227
+ self.dem_fn,
228
+ planetary_csv,
229
+ ]
230
+
231
+ run_subprocess_command(command)
232
+
137
233
  def pc_align_report(self, output_prefix="pc_align/pc_align"):
138
234
  """
139
235
  Extract alignment statistics from pc_align log files.
@@ -281,8 +377,18 @@ class Alignment:
281
377
  llz_c = llz_c[i]
282
378
  llz_shift = llz_shift[i]
283
379
 
380
+ # pc_align's Cartesian translation lives in the body-centered
381
+ # frame defined by --datum, so the source CRS must match the
382
+ # body of the DEM. PROJ refuses to convert between celestial
383
+ # bodies, so use a body-specific geocentric PROJ string for
384
+ # Mars/Moon and EPSG:4978 for Earth.
385
+ body = detect_planetary_body(self.dem_fn)
284
386
  ecef_srs = osr.SpatialReference()
285
- ecef_srs.ImportFromEPSG(4978)
387
+ proj_string = _GEOCENTRIC_PROJ.get(body)
388
+ if proj_string is None:
389
+ ecef_srs.ImportFromEPSG(4978)
390
+ else:
391
+ ecef_srs.ImportFromProj4(proj_string)
286
392
 
287
393
  s_srs = ecef_srs
288
394
  src_c = ecef_c