Mars Global Surveyor (MGS) Mars Orbital Camera (MOC) Narrow Angle#

End-to-end stereo example for the M0100115 / E0201461 MOC NA pair from ASP Tutorial Section 4.2, with two variants of the same pair:

  1. Non-mapprojected — affine-epipolar alignment (the original ASP tutorial path).

  2. Mapprojectedcam2map4stereo.py first, then stereo with --alignment-method none (the Section 8.4 path).

Both DEMs are aligned to MOLA via pc_align. The notebook mirrors the ASTER notebook structure so the two ASP code paths (non-mapprojected vs mapprojected stereo) can be compared side by side.

A note on the elevation range. Both DEM Summary tables on the report title page report elevations of roughly −10,300 m to −10,000 m for this scene — far below MOLA topography for northern Elysium (~−4 km). This is not a stereo error. ASP DEMs store height above the spherical IAU 2000 Mars sphere (3,396,190 m, used at all latitudes), while MOLA TOPOGRAPHY is referenced to the oblate MOLA areoid. At lat 34°N the IAU 2000 Mars ellipsoid radius is only ~3,389,938 m — about 6.2 km below the IAU sphere purely from oblateness — so a real surface that sits ~4 km below the MOLA areoid reports as ~−10 km when expressed as “height above sphere”. The new MOLA loader in this repo uses the PLANET_RAD column (absolute planetary radius) to compute dh, so the difference reads correctly across all latitudes; pc_align then trims any remaining MOC pointing offset (~100 m on this scene). See the next_steps page in the ASP docs for more on Mars datums.

Data download and ISIS pre-processing#

Retrieve the .imq files from the ASP example files, then install ISIS and convert to .cub:

ISIS> mocproc from=M0100115.imq to=M0100115.cub Mapping=NO
ISIS> mocproc from=E0201461.imq to=E0201461.cub Mapping=NO

Shared variables for both variants#

directory = "~/Desktop/asp-plot-examples/mars_moc/"
stereo_directory = "out_stereo/"  # non-mapprojected
stereo_directory_proj = "out_stereo_proj/"  # mapprojected
mola_csv = f"{directory}/438115_mola/MolaPEDR_34N34N_142E142E_20260319T204814563_pts_csv.csv"

Variant 1: Non-mapprojected stereo#

Run parallel_stereo directly on the cubes with affine-epipolar alignment, then point2dem. Threading kept conservative (--processes 2 --threads 4, total 8 cores) to match the WorldView examples in this repo and avoid memory pressure on a laptop:

parallel_stereo \
  --stereo-algorithm asp_mgm --subpixel-mode 9 \
  --processes 2 --threads 4 \
  --alignment-method affineepipolar \
  E0201461.cub M0100115.cub \
  out_stereo/output

point2dem --auto-proj-center --errorimage out_stereo/output-PC.tif

MOLA altimetry data#

Request MOLA via request_planetary_altimetry. The CLI submits an async query to the ODE GDS REST API (query=molapedr) and saves an altimetry_request_info.yml next to the DEM:

request_planetary_altimetry \
  --dem out_stereo/output-DEM.tif \
  --email user@example.com

When the email arrives, download and unzip the result. Use the *_pts_csv.csv file (not *_topo_csv.csv) when passing --altimetry_csv. The pts file has the PLANET_RAD column — distance from Mars center in meters — which is converted to height above the IAU 2000 sphere (3,396,190 m) to match ASP DEM heights. The _topo_csv.csv only has TOPOGRAPHY (height above the oblate MOLA areoid), which differs from the ASP spherical datum by up to ~10 km depending on latitude and is rejected by asp_plot.altimetry for that reason.

Full report (with MOLA + automatic pc_align)#

--pc_align True (default) runs pc_align against MOLA after the unaligned dh plots, applies the translation, and appends a pre/post comparison.

!asp_plot \
  --directory $directory \
  --stereo_directory $stereo_directory \
  --subset_km 0.5 \
  --add_basemap False \
  --plot_geometry False \
  --altimetry_csv $mola_csv \
  --report_filename ../../reports/MOC-asp-plot-report.pdf

Variant 2: Mapprojected stereo#

Run cam2map4stereo.py first to mapproject both cubes onto a shared map projection, then parallel_stereo with --alignment-method none (the cubes are already aligned by mapprojection):

cam2map4stereo.py M0100115.cub E0201461.cub

parallel_stereo \
  --stereo-algorithm asp_mgm --subpixel-mode 9 \
  --processes 2 --threads 4 \
  --alignment-method none \
  E0201461.map.cub M0100115.map.cub \
  out_stereo_proj/output

point2dem --auto-proj-center --errorimage out_stereo_proj/output-PC.tif
!asp_plot \
  --directory $directory \
  --stereo_directory $stereo_directory_proj \
  --subset_km 0.5 \
  --add_basemap False \
  --plot_geometry False \
  --altimetry_csv $mola_csv \
  --report_filename ../../reports/MOC_mapproj-asp-plot-report.pdf

Individual plots#

Modular API examples using the non-mapprojected variant.

Processing Parameters#

%load_ext autoreload
%autoreload 2

from asp_plot.processing_parameters import ProcessingParameters

processing_parameters = ProcessingParameters(
    processing_directory=directory,
    stereo_directory=stereo_directory,
)
processing_parameters_dict = processing_parameters.from_log_files()

print(f"Processed on: {processing_parameters_dict['processing_timestamp']}\n")
print(f"Reference DEM: {processing_parameters_dict['reference_dem']}\n")
print(f"Bundle adjustment ({processing_parameters_dict['bundle_adjust_run_time']}):\n")
print(processing_parameters_dict["bundle_adjust"])
print(f"\nStereo ({processing_parameters_dict['stereo_run_time']}):\n")
print(processing_parameters_dict["stereo"])
print(f"\nPoint2dem ({processing_parameters_dict['point2dem_run_time']}):\n")
print(processing_parameters_dict["point2dem"])

Scene plots#

from asp_plot.scenes import ScenePlotter

ScenePlotter(directory, stereo_directory, title="Scenes").plot_scenes()

Stereo plots#

from asp_plot.stereo import StereoPlotter

plotter = StereoPlotter(directory, stereo_directory)
plotter.title = "Hillshade"
plotter.plot_detailed_hillshade()
plotter.title = "Stereo Match Points"
plotter.plot_match_points()
plotter.title = "Disparity (pixels)"
plotter.plot_disparity(unit="pixels", quiver=True)
plotter.title = "Stereo DEM Results"
plotter.plot_dem_results()

MOLA altimetry comparison + pc_align#

Modular API equivalent of what --pc_align does in the CLI report above.

from asp_plot.altimetry import Altimetry

dem_fn = f"{directory}/{stereo_directory}/output-DEM.tif"
alt = Altimetry(directory=directory, dem_fn=dem_fn)
alt.load_planetary_csv(mola_csv)
alt.planetary_to_dem_dh()
alt.planetary_points[["lon", "lat", "height", "dem_height", "altimetry_minus_dem"]].head(10)
alt.mapview_plot_planetary_to_dem()
alt.histogram_planetary_to_dem()
result = alt.align_and_evaluate_planetary()
print(f"Status:           {result.status}")
print(f"Improvement:      {result.improvement_pct:.1f}%" if result.improvement_pct else "Improvement:      n/a")
print(f"Aligned DEM:      {result.aligned_dem_fn}")
print(f"Message:          {result.message}")
result.alignment_report_df
alt.mapview_plot_planetary_to_dem(plot_aligned=True)
alt.histogram_planetary_to_dem(plot_aligned=True)