Skip to content

Spectral Matching Processors

Functions behind spectral material matching, the algorithm layer called by the texture pipeline. They fetch Sentinel-2 reflectance, cluster and match it against a material library with the Spectral Angle Mapper, and (de)serialize the matched-material result. For more information see Spectral material matching and Spectral Matching config.

Sentinel-2 acquisition

s2gos_generator.processors.spectral.sentinel2.fetch_s2_reflectance

fetch_s2_reflectance(
    center_lat: float,
    center_lon: float,
    target_res_m: float,
    acquisition_date: str,
    search_window_days: int,
    bands: Sequence[str],
    max_cloud_cover: float,
    stac_url: str,
    scene_crs_wkt: str,
    grid_y: ndarray,
    grid_x: ndarray,
    aoi_polygon,
    credential_id: Optional[str] = None,
) -> xr.DataArray

Fetch a Sentinel-2 reflectance composite on the scene grid.

Parameters:

Name Type Description Default
center_lat, center_lon

Scene centre (WGS84 degrees).

required
target_res_m float

Stacking resolution (10 m for the supported bands).

required
acquisition_date str

Anchor date YYYY-MM-DD.

required
search_window_days int

± days around the anchor to search.

required
bands Sequence[str]

Sentinel-2 bands, defining the returned band order.

required
max_cloud_cover float

Maximum eo:cloud_cover percent.

required
stac_url str

STAC catalog endpoint.

required
scene_crs_wkt str

Scene oblique-Mercator CRS as WKT.

required
grid_y, grid_x

Scene grid coordinates (from the landcover raster) to regrid onto, guaranteeing coordinate alignment.

required
aoi_polygon

AOI polygon in WGS84 (shapely) for the STAC query.

required
credential_id Optional[str]

Id of an s3 credential (.secrets.yaml) for the Copernicus 'eodata' bucket; None falls back to ambient AWS_* env.

None

Returns:

Type Description
DataArray

(band, y, x) reflectance DataArray; band coord equals bands, y

DataArray

ascending (south-row-0), reflectance clipped to >= 0.

Spectral Angle Mapper

s2gos_generator.processors.spectral.sam.spectral_angle

spectral_angle(v1: ndarray, v2: ndarray) -> float

Spectral angle.

Brightness-invariant: depends only on spectral shape. Returns pi/2 (maximally dissimilar) when either vector has zero norm.

s2gos_generator.processors.spectral.sam.cluster_class_reflectance

cluster_class_reflectance(
    refl: ndarray,
    class_mask: ndarray,
    n_clusters: int,
    random_seed: Optional[int] = None,
) -> Tuple[np.ndarray, np.ndarray]

Cluster the reflectance of masked pixels with k-means.

Parameters:

Name Type Description Default
refl ndarray

(band, ny, nx) reflectance (band-sorted, same order as the candidate library vectors).

required
class_mask ndarray

(ny, nx) bool — pixels of the target landcover class.

required
n_clusters int

Number of clusters (k).

required
random_seed Optional[int]

Seed for reproducible clustering.

None

Returns:

Type Description
ndarray

(label_map, palette) where label_map is (ny, nx) int with

ndarray

data:NO_CLUSTER outside the mask / for non-finite pixels, and

Tuple[ndarray, ndarray]

palette is (n_clusters, band) of mean original reflectance per

Tuple[ndarray, ndarray]

cluster (rows are NaN for empty clusters).

s2gos_generator.processors.spectral.sam.match_clusters_to_library

match_clusters_to_library(
    palette: ndarray,
    library: List[CandidateSpectrum],
    max_sam_angle_deg: Optional[float] = None,
) -> List[Optional[CandidateSpectrum]]

Match each non-empty cluster to its closest library material by SAM.

Parameters:

Name Type Description Default
palette ndarray

(n_clusters, band) mean reflectance per cluster.

required
library List[CandidateSpectrum]

Candidate materials with band-aligned vectors.

required
max_sam_angle_deg Optional[float]

If set, clusters whose best angle exceeds this keep no match (None in the returned list), so the caller can fall back to the base landcover material.

None

Returns:

Type Description
List[Optional[CandidateSpectrum]]

List aligned with palette rows; entry is the matched

List[Optional[CandidateSpectrum]]

class:CandidateSpectrum or None (empty cluster or rejected match).

Material library

s2gos_generator.processors.spectral.library.load_candidate_library

load_candidate_library(
    material_library: PathLike, bands: List[str]
) -> List[CandidateSpectrum]

Load diffuse candidate materials and their Sentinel-2 reflectance vectors.

Parameters:

Name Type Description Default
material_library PathLike

Path to a materials.json-style file.

required
bands List[str]

Sentinel-2 bands defining the vector ordering (e.g. ["B02", "B03", "B04", "B08"]).

required

Returns:

Name Type Description
One List[CandidateSpectrum]

class:CandidateSpectrum per usable diffuse material. Non-diffuse

List[CandidateSpectrum]

materials, RGB-uniform diffuse materials, and spectra that do not span the

List[CandidateSpectrum]

requested bands are skipped (logged).

s2gos_generator.processors.spectral.library.CandidateSpectrum dataclass

CandidateSpectrum(
    material_id: str,
    material_def: Dict[str, Any],
    s2_vector: ndarray,
)

A diffuse material reduced to a Sentinel-2 reflectance vector.

Attributes:

Name Type Description
material_id str

Stable id from the library (used for dedup and indexing).

material_def Dict[str, Any]

The material definition dict (Material.to_dict form, with resolved spectral paths) for registration in the SceneDescription.

s2_vector ndarray

Reflectance sampled at the requested band centres, ordered to match the bands passed to :func:load_candidate_library.

Diversification & sidecar

s2gos_generator.processors.spectral.diversify.diversify_selection_texture

diversify_selection_texture(
    texture_2d: ndarray,
    landcover_2d: ndarray,
    refl: ndarray,
    cfg: SpectralMatchingConfig,
    base_index_map: Dict[str, int],
    library: List[CandidateSpectrum],
) -> Tuple[
    np.ndarray, Dict[str, Dict[str, Any]], Dict[str, int]
]

Paint spectrally-matched materials into texture_2d.

All arrays must share the same (ny, nx) grid and orientation (south-row-0, matching the on-disk selection texture and the landcover .values).

Parameters:

Name Type Description Default
texture_2d ndarray

(ny, nx) uint8 selection indices (modified in place).

required
landcover_2d ndarray

(ny, nx) ESA WorldCover class codes.

required
refl ndarray

(band, ny, nx) reflectance, band order matching library vectors.

required
cfg SpectralMatchingConfig

Spectral diversification configuration.

required
base_index_map Dict[str, int]

{material_name: index} for landcover + overlay materials.

required
library List[CandidateSpectrum]

Candidate diffuse materials.

required

Returns:

Type Description
ndarray

(texture_2d, material_defs, material_indices) where material_defs

Dict[str, Dict[str, Any]]

maps each newly introduced material id to its definition dict, and

Dict[str, int]

material_indices maps every matched material id to its texture index.

s2gos_generator.processors.spectral.diversify.matched_materials_to_sidecar

matched_materials_to_sidecar(
    material_defs: Dict[str, Any],
    material_indices: Dict[str, int],
    landcover_classes,
) -> dict

Serialize spectral-matching results to the matched-materials sidecar (schema v1).

s2gos_generator.processors.spectral.diversify.matched_materials_from_sidecar

matched_materials_from_sidecar(data: dict) -> dict

Read the matched-materials sidecar into {"materials", "material_indices"}.

Returns an empty dict for an unrecognised schema version.