layers

Pure configuration objects for describing raster layers — categorical and continuous.

CategoricalLayer

A CategoricalLayer describes where categorical raster data lives and how to access it.

Key insight: Categorical data can be encoded temporally in two ways: - band-per-year (MapBiomas): Each year is a separate band in a single ee.Image - collection (Dynamic World): An ee.ImageCollection filtered by date

The temporal_mode field makes this explicit.

This object contains no Earth Engine calls, no geometry, no statistics. Just descriptors.


source

CategoricalLayer


def CategoricalLayer(
    asset_id:str, temporal_mode:Literal='band', band_pattern:Optional=None, band:Optional=None, scale:int=30,
    class_map:Optional=None, palette:Optional=None
)->None:

A descriptor for a categorical raster layer.

Supports two temporal modes: - ‘band’: Each year is a band in a single Image (e.g., MapBiomas) - ‘collection’: An ImageCollection filtered by date (e.g., Dynamic World)

Attributes: asset_id: GEE asset path temporal_mode: How time is encoded - ‘band’ or ‘collection’ band_pattern: For ‘band’ mode - pattern with {} for year (e.g., ‘classification_{}’) band: For ‘collection’ mode - the band name to select (e.g., ‘label’) scale: Pixel resolution in meters (default 30) class_map: Optional mapping of class values to names palette: Optional mapping of class values to hex colors

ContinuousLayer

A ContinuousLayer describes continuous-valued raster time series (e.g., NDVI, EVI, temperature).

Key features: - Multi-band support: Extract one or more bands in a single call - Preprocessing hook: Optional preprocess function for derived indices, cloud masking, scaling - Dataset-agnostic: The extraction logic knows nothing about specific datasets

The preprocess function receives an ee.Image and returns an ee.Image with the bands you want to extract. This is where dataset-specific logic (cloud masking, index computation) lives.


source

ContinuousLayer


def ContinuousLayer(
    collection_id:str, bands:List, scale:int=10, preprocess:Optional=None
)->None:

A descriptor for a continuous raster time series.

Used for floating-point data like vegetation indices, temperature, or any continuous signal that varies over time.

Attributes: collection_id: GEE ImageCollection ID bands: List of band names to extract (output columns in DataFrame) scale: Pixel resolution in meters (default 10) preprocess: Optional function (ee.Image -> ee.Image) for preprocessing. Use this for cloud masking, scaling, computing derived indices. Must return an image containing all bands listed in bands.

Example: # Simple - extract existing band ContinuousLayer(collection_id=‘…’, bands=[‘B4’])

# With preprocessing - compute NDVI
ContinuousLayer(
    collection_id='COPERNICUS/S2_SR_HARMONIZED',
    bands=['NDVI', 'EVI'],
    preprocess=compute_s2_indices  # defined in datasets/sentinel2.py
)

Examples

CategoricalLayer - Band Mode (MapBiomas style)

# Band-per-year mode (like MapBiomas)
mapbiomas_style = CategoricalLayer(
    asset_id="projects/mapbiomas-public/assets/brazil/lulc/collection9/...",
    temporal_mode="band",
    band_pattern="classification_{}",
    scale=30
)

print(mapbiomas_style)
print(f"Band for 2020: {mapbiomas_style.band_name(2020)}")
CategoricalLayer(asset_id='projects/mapbiomas-public/assets/brazil/lulc/collection9/...', temporal_mode='band', band_pattern='classification_{}', band=None, scale=30, class_map=None, palette=None)
Band for 2020: classification_2020

CategoricalLayer - Collection Mode (Dynamic World style)

# Collection mode (like Dynamic World) - filter by date, not bands
dynamic_world_style = CategoricalLayer(
    asset_id="GOOGLE/DYNAMICWORLD/V1",
    temporal_mode="collection",
    band="label",
    scale=10,
    class_map={
        0: "Water",
        1: "Trees",
        2: "Grass",
        3: "Flooded Vegetation",
        4: "Crops",
        5: "Shrub & Scrub",
        6: "Built Area",
        7: "Bare Ground",
        8: "Snow & Ice",
    }
)

print(dynamic_world_style)
print(f"Class 1: {dynamic_world_style.class_name(1)}")
CategoricalLayer(asset_id='GOOGLE/DYNAMICWORLD/V1', temporal_mode='collection', band_pattern=None, band='label', scale=10, class_map={0: 'Water', 1: 'Trees', 2: 'Grass', 3: 'Flooded Vegetation', 4: 'Crops', 5: 'Shrub & Scrub', 6: 'Built Area', 7: 'Bare Ground', 8: 'Snow & Ice'}, palette=None)
Class 1: Trees

ContinuousLayer (NDVI, EVI, etc.)

# Simple continuous layer - extract existing bands
temperature_layer = ContinuousLayer(
    collection_id="MODIS/006/MOD11A1",
    bands=["LST_Day_1km"],
    scale=1000
)
print(temperature_layer)

# With preprocessing - the preprocess function is defined in dataset modules
# from gee_polygons.datasets.sentinel2 import preprocess_s2_indices
# 
# ndvi_evi_layer = ContinuousLayer(
#     collection_id="COPERNICUS/S2_SR_HARMONIZED",
#     bands=["NDVI", "EVI"],
#     preprocess=preprocess_s2_indices
# )
ContinuousLayer(collection_id='MODIS/006/MOD11A1', bands=['LST_Day_1km'], scale=1000, preprocess=None)

Both layer types are intentionally simple configuration objects. Dataset-specific presets (MapBiomas, Dynamic World, Sentinel-2) are defined in gee_polygons.datasets modules.