lunartrace.science.raster_projection
Emitter for the lunartrace.projection.slope_raster/v0 cross-client contract.
The contract model lives in lunartrace.projection. This module turns the SAME frozen
inputs that lunartrace.slope_quality/v1 reads into one JSON manifest plus raw sidecar
.bin layers (little-endian, row-major, row 0 at the affine origin). Inclusion layers are
computed only through lunartrace.science.slope_quality’s own validity and inclusion
functions, so a client can count included cells without re-deriving the rule.
Usage (real inputs, output is gitignored):
python -I -m lunartrace.science.raster_projection emit \ --manifest research/sources/pgda78-site01.manifest.json \ --store <store>/lt-source-store --out build/projections
python -I -m lunartrace.science.raster_projection synthetic-fixture --out contracts/fixturesAuthority and units: the emitter writes the lunartrace.projection.slope_raster/v0
contract. Its model lives in lunartrace.projection, and this module does not define a
separate schema. Slope and slope RMS layers are float32 in degrees, little-endian, row-major,
with NaN as nodata. Inclusion layers are one uint8 per cell, with value 0 for excluded, 1 for
included and 255 for nodata (no valid joint sample). The grid is referenced to its affine
transform and a CRS given as a PROJ string. Validity and inclusion are computed by
lunartrace.science.slope_quality, so the emitter adds no rule of its own. Standing is
recorded as supplied by the caller, except that NUMERIC_REPRODUCED is refused unless a
rerun reproduces a committed result hash (verify_reproduction), and
SCIENCE_VERTICAL_QUALIFIED is always refused. The synthetic fixture
is SYNTHETIC test data with no source provenance and must not be read as a scientific
result.
EncodedLayer
Section titled “EncodedLayer”class
EncodedLayerOne raster layer: its contract metadata and its encoded bytes.
Attributes
Section titled “Attributes”| Name | Type | Description |
|---|---|---|
spec | dict[str, object] | Layer metadata (kind, unit, dtype, nodata, byte_order, layout, uri) plus sha256 and byte_length once encode_layers has set them. |
data | bytes | The raw sidecar bytes written to uri. |
float32_le
Section titled “float32_le”function
def float32_le(values: npt.NDArray[np.float64], what: str) -> bytesEncode samples as little-endian float32 bytes in row-major order.
Parameters
Section titled “Parameters”| Name | Type | Description |
|---|---|---|
values | numpy.ndarray of float64 | Grid samples. NaN is allowed and is preserved as nodata. |
what | str | Layer name used in the error message. |
Returns
Section titled “Returns”bytes—valuescast to<f4and laid out C-contiguously.
Raises
Section titled “Raises”lunartrace.science.slope_quality.MethodInputError— If casting to float32 changes any sample (other than NaN, which compares equal).
The check is round-trip exactness, so no sample is rounded silently.
inclusion_codes
Section titled “inclusion_codes”function
def inclusion_codes( slope: sq.FloatGrid, rms: sq.FloatGrid, tau: float,) -> npt.NDArray[np.uint8]Compute per-cell inclusion codes for one RMS threshold.
Parameters
Section titled “Parameters”| Name | Type | Description |
|---|---|---|
slope | numpy.ndarray of float64 | Slope grid in degrees. |
rms | numpy.ndarray of float64 | Slope RMS grid in degrees, compared against tau. |
tau | float | RMS threshold in degrees. |
Returns
Section titled “Returns”numpy.ndarray of uint8— Same shape asslope. Codes:INCLUSION_NODATA(255) where the cell is not jointly valid,INCLUSION_EXCLUDED(0) where it is valid but excluded, andINCLUSION_INCLUDED(1) where it is included.
Joint validity is lunartrace.science.slope_quality.valid_slope and
lunartrace.science.slope_quality.valid_err. Inclusion is
lunartrace.science.slope_quality.inclusion_mask, which implements
. Missing values are
never coded as included.
encode_layers
Section titled “encode_layers”function
def encode_layers( slope: sq.Grid, slope_rms: sq.Grid, taus: Sequence[float], stem: str,) -> list[EncodedLayer]Encode the slope, slope RMS and per-tau inclusion layers with their metadata.
Parameters
Section titled “Parameters”| Name | Type | Description |
|---|---|---|
slope | lunartrace.science.slope_quality.Grid | Slope grid in degrees. |
slope_rms | lunartrace.science.slope_quality.Grid | Slope RMS grid in degrees, on the same lattice as slope. |
taus | Sequence[float] | RMS thresholds in degrees; validated by slope_quality.check_taus. |
stem | str | File-name prefix for the sidecar uri values. |
Returns
Section titled “Returns”list[EncodedLayer]— Layers in this order:slope(unitdeg),slope_rms(unitdeg), then oneinclusionlayer per tau (unitNone, dtypeuint8). Each spec carriessha256andbyte_lengthof its bytes.
Raises
Section titled “Raises”lunartrace.science.slope_quality.MethodInputError— If the grids are incompatible, the taus are invalid, or a float32 narrowing is lossy.
write_projection
Section titled “write_projection”function
def write_projection( out_dir: Path, stem: str, slope: sq.Grid, slope_rms: sq.Grid, taus: Sequence[float], *, standing: Standing, source_witness_id: str, citation: str, crs_proj4: str,) -> PathValidate the projection, then write <stem>.projection.json and its sidecars.
Parameters
Section titled “Parameters”| Name | Type | Description |
|---|---|---|
out_dir | pathlib.Path | Output directory, created if missing. Existing files with the same names are overwritten. |
stem | str | File-name prefix for the projection document and its .bin sidecars. |
slope | lunartrace.science.slope_quality.Grid | Slope grid in degrees. Its transform defines the grid’s affine placement. |
slope_rms | lunartrace.science.slope_quality.Grid | Slope RMS grid in degrees. |
taus | Sequence[float] | RMS thresholds in degrees. |
standing | Standing | Standing label, recorded as given (see lunartrace.projection.Standing). |
source_witness_id | str | Identifier of the source witness. |
citation | str | Citation text carried by the projection. |
crs_proj4 | str | CRS as a PROJ string. |
Returns
Section titled “Returns”pathlib.Path— Path to the written<stem>.projection.json.
Raises
Section titled “Raises”lunartrace.science.slope_quality.MethodInputError— From layer encoding (seeencode_layers).pydantic.ValidationError— If the projection model rejects the document. The validation runs before any file is written.
Sidecar files are written first, then the JSON document, using LF line endings.
emit_from_manifest
Section titled “emit_from_manifest”function
def emit_from_manifest( manifest_path: Path, store: Path, out_dir: Path, taus: Sequence[float] = sq.DEFAULT_TAUS, standing: Standing = 'METHOD_EXECUTED', reproduces: Path | None = None,) -> PathEmit slope-raster projection files from SOURCE_FROZEN CAS inputs.
Parameters
Section titled “Parameters”| Name | Type | Description |
|---|---|---|
manifest_path | pathlib.Path | A SOURCE_FROZEN source-witness manifest. |
store | pathlib.Path | Content-addressed store holding the manifest’s blobs. |
out_dir | pathlib.Path | Output directory for the projection and its sidecars. |
taus | Sequence[float] | RMS thresholds in degrees. Defaults to slope_quality.DEFAULT_TAUS. Default sq.DEFAULT_TAUS. |
standing | Standing | Standing label to record. Defaults to "METHOD_EXECUTED", which is an execution label and not a scientific standing. "NUMERIC_REPRODUCED" is recorded only when reproduces is given and the rerun matches it. "SCIENCE_VERTICAL_QUALIFIED" is never recorded by this function. Default 'METHOD_EXECUTED'. |
reproduces | pathlib.Path or None | A committed slope_quality/v0 projection to reproduce. Required for "NUMERIC_REPRODUCED". See verify_reproduction. Default None. |
Returns
Section titled “Returns”pathlib.Path— Path to the written<witness id>.slope-raster.projection.json.
Raises
Section titled “Raises”lunartrace.science.slope_quality.MethodInputError— If the manifest or its blobs fail checks, the manifest has no stringid, or the requested standing is not earned by a passing reproduction.
Every blob is SHA-256-verified before it is read. The slope and slope RMS grids are loaded with their expected digests.
verify_reproduction
Section titled “verify_reproduction”function
def verify_reproduction(manifest_path: Path, store: Path, reference: Path) -> strRerun slope_quality/v1 on the manifest’s inputs and check it against reference.
Parameters
Section titled “Parameters”| Name | Type | Description |
|---|---|---|
manifest_path | pathlib.Path | A SOURCE_FROZEN source-witness manifest. |
store | pathlib.Path | Content-addressed store holding the manifest’s blobs. |
reference | pathlib.Path | A committed lunartrace.projection.slope_quality/v0 document whose source_witness_id is the manifest’s id. |
Returns
Section titled “Returns”str— The matching hex SHA-256 of the rerun’s canonical result bytes.
Raises
Section titled “Raises”lunartrace.science.slope_quality.MethodInputError— If the reference names another witness or has no result hash, or if the rerun’s hash differs from it.
The thresholds are taken from the reference’s own rows, so the rerun asks the same
question the reference answered. This is the comparison that earns
NUMERIC_REPRODUCED; it is not a scientific admission.
synthetic_grids
Section titled “synthetic_grids”function
def synthetic_grids() -> tuple[sq.Grid, sq.Grid]Build the SYNTHETIC 3-row x 4-column slope and slope RMS grids used for the fixture.
Returns
Section titled “Returns”tuple[lunartrace.science.slope_quality.Grid, lunartrace.science.slope_quality.Grid]—(slope, slope_rms)sharing one affine transform (cell size 5 m) and the synthetic CRSSYNTHETIC_PROJ4(a south polar stereographic on a 1737400 m sphere, in metres).
Slope has a NaN (nodata) and an out-of-domain 95 degrees. Slope RMS has a NaN and a non-positive 0. Values are exact in float32. These grids are SYNTHETIC, with no source provenance, and are not source data.
write_synthetic_fixture
Section titled “write_synthetic_fixture”function
def write_synthetic_fixture(out_dir: Path) -> PathWrite the committed SYNTHETIC fixture projection to out_dir.
Parameters
Section titled “Parameters”| Name | Type | Description |
|---|---|---|
out_dir | pathlib.Path | Output directory, created if missing. |
Returns
Section titled “Returns”pathlib.Path— Path tosynthetic-4x3.slope-raster.projection.json.
Uses synthetic_grids, the default taus, standing "METHOD_EXECUTED", the
witness id synthetic-raster-4x3 and a citation that states there is no source. The
output is a test fixture, not a scientific result.
function
def main(argv: Sequence[str] | None = None) -> intCommand-line entry point with the emit and synthetic-fixture subcommands.
Parameters
Section titled “Parameters”| Name | Type | Description |
|---|---|---|
argv | Sequence[str] or None | Arguments without the program name. If None, sys.argv is used. Default None. |
Returns
Section titled “Returns”int—0on success. Method and input errors propagate as exceptions; argparse exits on usage errors.
emit takes --manifest, --store, --out, optional repeated --tau,
--standing (default METHOD_EXECUTED) and --reproduces, which
NUMERIC_REPRODUCED requires. synthetic-fixture takes --out.