lunar_toolkit.stereo

Stereo pair ranking by convergence, illumination similarity and overlap.

TangentPlane(lat, lon, radius_km=MOON_RADIUS_KM)

Gnomonic projection (km) about a centre point; great circles → lines.

ring(ring)

Project a lon/lat ring (deg) to plane (x, y) km (a Projection).

unit_vector(lat, lon)

Unit vector(s) (..., 3) of planetocentric latitude / longitude (deg).

angle_between(a, b)

Angle (deg) between vectors along the last axis (numerically stable).

azimuth_difference(a, b)

Smallest circular difference (deg, 0–180) between azimuths.

convergence_angle(ground, sc1, sc2, centre1=None, centre2=None, radius_km=MOON_RADIUS_KM)

Convergence angle and base/height ratio for two views of one point.

Parameters:
  • ground ((lat, lon)) –

    Ground point (deg) on the sphere of radius radius_km.

  • sc1 ((sub_spacecraft_lat, sub_spacecraft_lon, altitude_km)) –

    Spacecraft positions as sub-spacecraft point + altitude above the sphere, at the time the image centre was acquired.

  • sc2 ((sub_spacecraft_lat, sub_spacecraft_lon, altitude_km)) –

    Spacecraft positions as sub-spacecraft point + altitude above the sphere, at the time the image centre was acquired.

  • centre1 ((lat, lon), default: None ) –

    Image centres. Pushbroom images (NAC, ShadowCam) are tens of km long, so the spacecraft was elsewhere when it imaged a ground point away from the centre. When given, each spacecraft position is moved by the rotation that takes the image centre onto ground, i.e. the look direction is held fixed in the local frame (constant pointing along a circular orbit).

  • centre2 ((lat, lon), default: None ) –

    Image centres. Pushbroom images (NAC, ShadowCam) are tens of km long, so the spacecraft was elsewhere when it imaged a ground point away from the centre. When given, each spacecraft position is moved by the rotation that takes the image centre onto ground, i.e. the look direction is held fixed in the local frame (constant pointing along a circular orbit).

Returns:
  • (convergence_deg, b_h) –

    Angle between the look vectors spacecraft → ground, and the baseline |S1 - S2| divided by the mean slant range to the ground point.

convergence_angles(ground, sc1, sc2, centre1=None, centre2=None, radius_km=MOON_RADIUS_KM)

convergence_angle for arrays: each tuple element is an array over pairs.

Returns:
  • (convergence_deg, b_h) : ndarrays –

    NaN where an input is NaN.

observation_footprints(df)

Footprint rings, centre unit vectors and angular radii (deg) of observation rows.

Rings are lunar_toolkit.geometry.footprint_rings of each row (footprint_wkt, else the min_lat…lon_east box), None (centre 0, radius 0) where a row has no footprint polygon.

aoi_plane(aoi)

Tangent plane about an AOI, the AOI in it (km), and its centre / angular radius.

aoi is anything lunar_toolkit.geometry.load_aoi accepts, e.g. {"point": (lat, lon), "radius_km": r}, {"lat": (s, n), "lon": (w, e)}, a GeoJSON / WKT path or string.

Returns:
  • (TangentPlane, shapely geometry, ndarray, float) –

find_stereo_pairs(obs_df, aoi=None, convergence=(10.0, 60.0), ideal_convergence=(15.0, 45.0), max_incidence_diff=20.0, max_azimuth_diff=30.0, min_overlap=0.1, min_aoi_coverage=0.0, max_resolution_ratio=None, same_instrument=True, require_geometry=False, min_time_sep_s=60.0, weights=None)

Rank candidate stereo pairs in an observation table.

Parameters:
  • obs_df (DataFrame) –

    Observations in the common schema (extra columns are ignored).

  • aoi ((dict, str, Path or Aoi), default: None ) –

    Area of interest: {"point": (lat, lon), "radius_km": r}, {"lat": (s, n), "lon": (w, e)} or anything lunar_toolkit.geometry.load_aoi accepts (GeoJSON / WKT file or string). Overlaps are then clipped to the AOI, pairs not overlapping inside it are dropped and the convergence angle is evaluated at the centroid of the clipped overlap.

  • convergence ((min, max), default: (10.0, 60.0) ) –

    Hard limits on the convergence angle (deg). Pairs without spacecraft geometry are kept only if the emission bounds [|e1-e2|, e1+e2] (from centre emission angles, so approximate; 5° slack is allowed) intersect this range and require_geometry is False.

  • ideal_convergence ((min, max), default: (15.0, 45.0) ) –

    Range scored 1; the score falls linearly to 0 at convergence limits.

  • max_incidence_diff (float, default: 20.0 ) –

    Hard limits (deg) on |i1 - i2| and the circular sub-solar azimuth difference (azimuth is ignored where unknown).

  • max_azimuth_diff (float, default: 20.0 ) –

    Hard limits (deg) on |i1 - i2| and the circular sub-solar azimuth difference (azimuth is ignored where unknown).

  • min_overlap (float, default: 0.1 ) –

    Minimum overlap area as a fraction of the smaller footprint (with an aoi: of the smaller footprint-inside-AOI).

  • min_aoi_coverage (float, default: 0.0 ) –

    Minimum fraction of the AOI covered by the overlap (with aoi).

  • max_resolution_ratio (float, default: None ) –

    Maximum pixel_scale ratio (coarser / finer).

  • same_instrument (bool, default: True ) –

    Only pair images from the same instrument.

  • require_geometry (bool, default: False ) –

    Drop pairs whose convergence angle cannot be computed.

  • min_time_sep_s (float, default: 60.0 ) –

    Minimum start-time separation; also, NAC LE/RE halves of one observation are never paired (their convergence is ~0).

  • weights (dict, default: None ) –

    Score weights, keys as DEFAULT_WEIGHTS.

Returns:
  • DataFrame –

    One row per pair, best first, with index_1 / index_2 (row labels in obs_df), product_id_1 / _2, instrument, start_time_1 / _2, dt_days, convergence (deg, NaN if unknown), convergence_min / convergence_max (emission bounds), b_h (baseline / mean slant range, ≈ 2 sin(convergence / 2)), incidence_1 / _2, incidence_diff, azimuth_1 / _2, azimuth_diff, emission_1 / _2, pixel_scale_1 / _2, resolution_ratio, overlap_km2, overlap_frac, aoi_coverage (NaN without aoi), overlap_lat / overlap_lon (where convergence is evaluated) and score.

Notes

score is a weighted mean of terms in [0, 1]:

  • convergence — 1 inside ideal_convergence, linear to 0 at the convergence limits (0.5 when the angle is unknown);
  • overlap — aoi_coverage with an AOI, else overlap_frac;
  • incidence — 1 - incidence_diff / max_incidence_diff;
  • azimuth — 1 - azimuth_diff / max_azimuth_diff (0.5 if unknown);
  • resolution — finer / coarser pixel_scale (0.5 if unknown).

score_pairs(pairs, *, convergence=(10.0, 60.0), ideal_convergence=(15.0, 45.0), max_incidence_diff=20.0, max_azimuth_diff=30.0, weights=None, aoi=False)

The find_stereo_pairs score of pair rows (convergence, overlap_frac or aoi_coverage with aoi, incidence_diff, azimuth_diff, resolution_ratio).