lunartrace.projection
Cross-client projection models for LunarTrace derived products.
This module defines the pydantic models that serialise Python-computed results for other
clients (TypeScript, the standalone validator, the web UI). Python is the canonical
scientific authority: tools/export_contract.py exports the JSON Schema of these models,
from which the TypeScript types and the standalone validator are generated. Clients render
a projection and never recompute it.
Three projection families are defined here, each carrying a schema identifier:
lunartrace.projection.slope_quality/v0(SlopeQualityProjection): a slope-quality result, its standing and provenance pointers.lunartrace.projection.slope_raster/v0(SlopeRasterProjection): per-cell rasters as a JSON manifest plus binary sidecar files.lunartrace.projection.display_sun/v0(DisplaySunProjection): a display-only geometric Sun direction at an explicit UTC epoch.
Nothing here imports the science extra.
Authority and standing
Each projection carries its own proof ceiling in standing (one of Standing:
METHOD_EXECUTED, NUMERIC_REPRODUCED, SCIENCE_VERTICAL_QUALIFIED, BLOCKED).
A client may display no stronger claim than that standing. NUMERIC_REPRODUCED means
the numbers were reproduced; it is not a scientific admission. The ordering and the
precise meaning of each value are defined by the governing research and governance
files, not by this module.
Units, CRS and nulls
Slope values and thresholds are in degrees; grid transforms are in metres of the
native projected CRS named by crs_proj4. included_fraction is a valid-cell
fraction and never a physical area. Missing is never zero: a value that is not defined
is None (or NaN in binary rasters), never 0.
SlopeQuantiles
Section titled “SlopeQuantiles”class
SlopeQuantilesInverted-CDF quantiles of a slope sample, in degrees.
Each field is the quantile named by its key (p05 is the 0.05 quantile, and so on).
Because the quantiles are inverted-CDF quantiles, every value is an observed slope sample
and no interpolation is applied.
Attributes
Section titled “Attributes”| Name | Type | Description |
|---|---|---|
p05, p25, p50, p75, p95 | float | Slope quantiles in degrees, serialised under the aliases "0.05", "0.25", "0.5", "0.75" and "0.95". |
SlopeQualityRow
Section titled “SlopeQualityRow”class
SlopeQualityRowSlope-quality summary for one ROI (or the union of ROIs) at one threshold.
Attributes
Section titled “Attributes”| Name | Type | Description |
|---|---|---|
roi | str | ROI index as a string, or "union". |
tau | float | Inclusion threshold on slope RMS, in degrees; non-negative. |
roi_cells, valid_slope_cells, valid_err_cells, joint_cells, included_cells | int | Non-negative cell counts. joint_cells counts cells that are valid in both the slope and slope-RMS layers; included_cells counts the jointly valid cells that pass tau. |
included_fraction | float or None | included_cells / joint_cells, in [0, 1]. This is a valid-cell fraction, not an area. None where it is not defined; it is never encoded as 0. |
slope_quantiles | SlopeQuantiles or None | Inverted-CDF slope quantiles in degrees, or None when not defined. |
slope_mean | float or None | Mean slope in degrees, or None when not defined. |
slope_histogram | list of int | Exactly 45 counts over bin edges 0, 2, …, 90 degrees. The last bin is closed. |
SlopeQualityInputs
Section titled “SlopeQualityInputs”class
SlopeQualityInputsSHA-256 digests (64 lowercase hex characters) of the three inputs to the result.
The digests pin the input bytes so that a client can confirm which inputs produced a result. They are recorded by the producer and are not recomputed here.
Attributes
Section titled “Attributes”| Name | Type | Description |
|---|---|---|
slope_sha256 | str | Digest of the slope input. |
slope_rms_sha256 | str | Digest of the slope RMS input. |
roi_shp_sha256 | str | Digest of the ROI shapefile. |
GridSpec
Section titled “GridSpec”class
GridSpecRaster grid geometry.
Attributes
Section titled “Attributes”| Name | Type | Description |
|---|---|---|
shape | list of int | [rows, cols], both strictly positive. |
transform | list of float | Six affine coefficients (a, b, c, d, e, f) in rasterio order, mapping cell corners to the native CRS: x = a*col + b*row + c and y = d*col + e*row + f. The units are those of the native CRS (metres for the Site01 fixture). |
SlopeQualityResult
Section titled “SlopeQualityResult”class
SlopeQualityResultCanonical slope-quality result as emitted by the primary implementation.
Its method is fixed to lunartrace.slope_quality/v1. results is a list of
SlopeQualityRow, each identified by its roi and tau.
Attributes
Section titled “Attributes”| Name | Type | Description |
|---|---|---|
method | str | Always lunartrace.slope_quality/v1. |
implementation | str | Identifier of the implementation that emitted the result. |
inputs | SlopeQualityInputs | Digests of the three inputs. |
grid | GridSpec | Geometry of the raster grid. |
results | list of SlopeQualityRow | One row per ROI (or union) and threshold tau. |
SlopeQualityProjection
Section titled “SlopeQualityProjection”class
SlopeQualityProjectionWhat a client may load: the result, its standing and its provenance pointers.
Attributes
Section titled “Attributes”| Name | Type | Description |
|---|---|---|
schema_id | str | Serialised as schema; always lunartrace.projection.slope_quality/v0. |
standing | Standing | The proof ceiling for result. |
source_witness_id | str | Identifier string of the source witness; this module does not interpret it. |
result_sha256 | str | 64-character lowercase hex SHA-256 digest pinned to result. |
citation | str | Citation text for the result. |
crs_proj4 | str | Native CRS of the grid as a PROJ string. Clients reproject for display. |
result | SlopeQualityResult | The slope-quality result itself. |
ValueRasterLayer
Section titled “ValueRasterLayer”class
ValueRasterLayerNative per-cell slope or slope RMS samples, in degrees, as little-endian float32.
nodata is the string "NaN": nodata cells hold IEEE-754 NaN in the sidecar (JSON
cannot carry NaN), so a missing sample is never stored as zero. Non-NaN samples are the
native values as loaded by lunartrace.slope_quality/v1. Samples outside the method’s
validity domain are kept as-is and are classified by the inclusion layers, not here.
Attributes
Section titled “Attributes”| Name | Type | Description |
|---|---|---|
kind | {slope, slope_rms} | Which layer this is. |
unit | {deg} | Unit of the samples: degrees. |
dtype | {float32} | Sample type of the sidecar. |
nodata | {NaN} | Marker for missing samples, which are stored as IEEE-754 NaN. |
byte_order | {little} | Byte order of the sidecar samples. |
layout | str | Raster layout of the sidecar. |
uri | str | Sidecar file name, relative to the JSON document. |
sha256 | str | Digest of the sidecar bytes. |
byte_length | int | Size of the sidecar in bytes. |
byte_length must equal rows * cols * 4 for the grid. A client verifies
byte_length and sha256 against the sidecar bytes before use.
InclusionRasterLayer
Section titled “InclusionRasterLayer”class
InclusionRasterLayerPer-cell inclusion at one tau under the slope_quality/v1 rule, as uint8 codes.
1 = included (jointly valid and slope_rms <= tau); 0 = excluded (jointly valid and
slope_rms > tau); 255 = nodata (slope or slope_rms invalid under v1, so no decision).
The layer covers the whole grid and is not ROI-limited. unit is null: the codes are
categorical, not a quantity. tau is in degrees and is compared with slope RMS.
byte_length must equal rows * cols * 1 for the grid.
Attributes
Section titled “Attributes”| Name | Type | Description |
|---|---|---|
kind | {inclusion} | Layer kind. |
tau | float | Threshold on slope RMS, in degrees; non-negative. |
unit | None | Always None: the codes are categorical. |
dtype | {uint8} | Sample type of the sidecar. |
nodata | {255} | Code for cells with no decision. |
byte_order | {little} | Byte order of the sidecar samples. |
layout | str | Raster layout of the sidecar. |
uri | str | Sidecar file name, relative to the JSON document. |
sha256 | str | Digest of the sidecar bytes. |
byte_length | int | Size of the sidecar in bytes. |
SlopeRasterProjection
Section titled “SlopeRasterProjection”class
SlopeRasterProjectionPer-cell slope, slope RMS and per-tau inclusion rasters for one grid.
The JSON Schema enforces each layer’s shape per kind (tau required for inclusion and
forbidden otherwise). The model validator additionally enforces, in Python, that every
byte_length equals rowscolsitemsize and that layers are unique per (kind, tau);
clients re-check byte_length and sha256 against the fetched bytes.
Attributes
Section titled “Attributes”| Name | Type | Description |
|---|---|---|
schema_id | str | Serialised as schema; always lunartrace.projection.slope_raster/v0. |
standing | Standing | Proof ceiling for the rasters. |
source_witness_id | str | Identifier string of the source witness; not interpreted by this module. |
citation | str | Citation text. |
crs_proj4 | str | Native CRS of the grid as a PROJ string. |
grid | GridSpec | Raster grid geometry. |
layers | list of RasterLayer | Value and inclusion layers; at least one. |
Raises
Section titled “Raises”pydantic.ValidationError— If a layer shape is wrong, if two layers share the same (kind, tau), or if abyte_lengthdoes not match the grid and dtype.ValidationErroris aValueErrorsubclass.
Each sidecar is raw little-endian samples in row-major order, row 0 at the affine origin. No raster data is embedded in the JSON document.
DisplayKernel
Section titled “DisplayKernel”class
DisplayKernelPinned NAIF source bytes used by the display generator.
Attributes
Section titled “Attributes”| Name | Type | Description |
|---|---|---|
name | str | File name of the kernel; letters, digits, ., _ and - only. |
url | str | Source URL; must begin with https://naif.jpl.nasa.gov/pub/naif/. |
sha256 | str | 64-character lowercase hex SHA-256 digest of the kernel bytes. |
bytes | int | Exact size of the kernel in bytes; strictly positive. |
DisplaySite
Section titled “DisplaySite”class
DisplaySiteSpherical, east-positive geographic display location, not observation provenance.
Attributes
Section titled “Attributes”| Name | Type | Description |
|---|---|---|
name | str | Human-readable label for the location. |
latitude_deg | float | Geographic latitude in degrees, in [-90, 90]. Finite values only. |
longitude_deg | float | East-positive longitude in degrees, in [-180, 180]. Finite values only. |
The location is a spherical model of the Moon’s surface used for display. It carries no observation provenance.
DisplaySunProjection
Section titled “DisplaySunProjection”class
DisplaySunProjectionGeometric display light at an explicit UTC epoch; never an illumination observation.
Attributes
Section titled “Attributes”| Name | Type | Description |
|---|---|---|
schema_id | str | Serialised as schema; always lunartrace.projection.display_sun/v0. |
display_only | bool | Always True. |
epoch_utc | str | ISO-8601 UTC instant ending in Z, with optional fractional seconds. |
frame | str | Always MOON_ME_DE421 (the DE421 Moon mean-Earth frame). |
aberration_correction | str | Always NONE. |
direction | list of float | Unit XYZ vector from the Moon centre toward the Sun, in frame. Dimensionless. |
site | DisplaySite | Spherical display location. |
elevation_deg | float | Solar elevation at site in degrees, in [-90, 90]. |
azimuth_deg | float | Solar azimuth at site in degrees, in [0, 360); north is 0 and east is 90. |
kernels | list of DisplayKernel | Pinned NAIF kernels used to generate the result, in load order. |
generator | str | Generator identifier string. |
Raises
Section titled “Raises”ValueError— If the squared norm ofdirectiondiffers from 1 by more than an absolute tolerance of1e-12. Pydantic validation failures are alsoValueErrorsubclasses.
Elevation uses the site-to-Sun vector, including the parallax of the site. Azimuth is measured on the spherical local horizon with no terrain shadowing.