QA¶
geotoolz.qa provides sensor-specific QA-bit decoders layered on top of the generic primitives in geotoolz.cloud. The two modules share one decoder implementation:
geotoolz.cloud._src.array.mask_from_qa_bits— single-bit-flag decoding (OR of bits).geotoolz.cloud._src.array.mask_from_scl— categorical class membership.geotoolz.qa._src.array.mask_from_bit_field— contiguous multi-bit field decoding (needed for MODIS).
Pick geotoolz.cloud.MaskFromQABits / MaskFromSCL when you have an explicit list of bits / classes. Pick geotoolz.qa.LandsatQA_PIXEL / S2QA60 / S2SCL / MODISStateQA when you want the published-spec defaults.
geotoolz.qa
¶
QA bit decoding and sensor mask presets.
The operators in this module return boolean GeoTensor masks where
True means "mask this pixel out". Use them with geotoolz.cloud.ApplyMask
or downstream mask-combination operators.
CloudSEN12
¶
Bases: Operator
Placeholder for the optional ML-based CloudSEN12 detector.
Raises ImportError on call.
Examples:
>>> from geotoolz.qa import CloudSEN12
>>> CloudSEN12(checkpoint="default").get_config()
{'checkpoint': 'default'}
Source code in src/geotoolz/qa/_src/operators.py
DecodeBitmask
¶
Bases: Operator
Unpack a QA bitmask into named boolean mask layers.
A more general sibling of geotoolz.cloud.MaskFromQABits: instead
of returning a single OR-ed mask, this operator returns a stacked
multi-band boolean carrier with one layer per named entry in
bits.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bits
|
Mapping[str, Sequence[int]]
|
Mapping from output-layer name to bit positions. |
required |
mode
|
str
|
|
'any'
|
qa_band
|
BandSelector
|
Optional integer or named band selector. When omitted, the input carrier itself is treated as the QA band. |
None
|
axis
|
int
|
Position of the band axis when |
0
|
Returns:
| Type | Description |
|---|---|
|
A multi-band boolean |
|
|
|
|
|
is set from the keys of |
Examples:
>>> from geotoolz.qa import DecodeBitmask
>>> # Landsat-8 QA_PIXEL — one band per flag.
>>> op = DecodeBitmask(
... bits={"cloud": [3], "cirrus": [2], "shadow": [4]},
... qa_band="QA_PIXEL",
... )
>>> layers = op(landsat_stack) # (3, H, W) bool GeoTensor
Source code in src/geotoolz/qa/_src/operators.py
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 | |
LandsatQA_PIXEL
¶
Bases: Operator
Landsat Collection-2 QA_PIXEL mask preset.
Returns True where any requested targets flag is set in
QA_PIXEL. Defaults to cloud, cloud shadow, and cirrus targets
(the standard "drop cloudy" mask for L8/L9).
Sensor selection: pass sensor="l7" for Landsat 4-7
Collection-2 — same bit layout as L8/L9 except bit 2 ("cirrus") is
unused on TM/ETM+. For sensor="l89" (default) the full L8/L9
layout is used.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
qa_band
|
int | str
|
Band selector for QA_PIXEL. |
'QA_PIXEL'
|
targets
|
Sequence[str] | None
|
Target flag names to OR together. See
|
None
|
sensor
|
str
|
|
'l89'
|
axis
|
int
|
Band axis position. |
0
|
Examples:
>>> from geotoolz.qa import LandsatQA_PIXEL
>>> # L8/L9 default "drop everything not clear".
>>> mask = LandsatQA_PIXEL()(landsat8_stack)
>>> # L7 — same but no cirrus bit.
>>> mask = LandsatQA_PIXEL(
... sensor="l7", targets=["cloud", "cloud_shadow"]
... )(landsat7_stack)
References
USGS, "Landsat 8-9 Collection 2 Level-2 Science Product Guide", LSDS-1619, 2022. USGS, "Landsat 4-7 Collection 2 Level-2 Science Product Guide", LSDS-1618, 2022.
Source code in src/geotoolz/qa/_src/operators.py
707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 | |
MODISStateQA
¶
Bases: Operator
MODIS state_1km (or state_500m) QA mask preset.
Returns True where any requested targets is set in the MODIS
State QA band. Defaults to cloud and cloud-shadow targets.
The cloud and cirrus targets are decoded as 2-bit fields, not
independent bit flags: bits [0, 1] are the cloud state
(0=clear, 1=cloudy, 2=mixed, 3=not-set) and bits [8, 9] are
cirrus level (0=none, 1=small, 2=average, 3=high). The default
"cloud" target matches cloudy + mixed; "cirrus" matches
small/average/high.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
qa_band
|
int | str
|
Band selector for the state QA band. |
'state_1km'
|
targets
|
Sequence[str]
|
Target flag names to OR together. See
|
('cloud', 'cloud_shadow')
|
axis
|
int
|
Band axis position. |
0
|
Examples:
>>> from geotoolz.qa import MODISStateQA
>>> # Default cloud + cloud-shadow mask.
>>> mask = MODISStateQA()(modis_state_band)
>>> # Include cirrus too.
>>> mask = MODISStateQA(targets=["cloud", "cloud_shadow", "cirrus"])(state)
References
Vermote, E. F., "MODIS Surface Reflectance User's Guide", 2015, Table 12.
Source code in src/geotoolz/qa/_src/operators.py
MaskCirrus
¶
MaskCloudShadow
¶
MaskClouds
¶
MaskNoData
¶
Bases: Operator
Return True where pixels are no-data by QA value or carrier fill value.
Two operating modes:
- QA-driven: pass
qa_band/bits/valuesto decode no-data from a dedicated QA band. - Fill-driven (default): without QA arguments, pixels equal to
the carrier's
fill_value_defaultin any band are marked.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
qa_band
|
BandSelector
|
Optional QA band selector. |
None
|
bits
|
Sequence[int] | None
|
Bit positions that mark no-data. |
None
|
values
|
Sequence[int] | None
|
Categorical values that mark no-data (e.g. SCL=0). |
None
|
axis
|
int
|
Position of the band axis. |
0
|
Returns:
| Type | Description |
|---|---|
|
Boolean |
Examples:
>>> from geotoolz.qa import MaskNoData
>>> # Sentinel-2 SCL: class 0 is NO_DATA.
>>> nodata = MaskNoData(qa_band="SCL", values=[0])(s2_l2a)
>>> # Fill-value fallback when no QA band is available.
>>> nodata = MaskNoData()(carrier_with_fill_value)
Source code in src/geotoolz/qa/_src/operators.py
MaskSaturated
¶
Bases: Operator
Return True where pixels equal a saturation value.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
qa_band
|
BandSelector
|
Optional band selector. When omitted, all bands are checked and the per-pixel OR-reduction across bands is returned. |
None
|
saturation_value
|
float | int | None
|
Explicit saturation value. If omitted for integer arrays, the dtype maximum is used; float arrays require an explicit value. |
None
|
axis
|
int
|
Position of the band axis (used for the cross-band
reduction when |
0
|
Returns:
| Type | Description |
|---|---|
|
Boolean |
Examples:
>>> from geotoolz.qa import MaskSaturated
>>> # uint16 Sentinel-2 — saturation_value defaults to 65535.
>>> sat = MaskSaturated()(s2_uint16_stack)
>>> # Explicit value for reflectance ratios.
>>> sat = MaskSaturated(saturation_value=1.0)(reflectance_stack)
Source code in src/geotoolz/qa/_src/operators.py
MaskSnow
¶
MaskWater
¶
OmniCloudMask
¶
Bases: Operator
Placeholder for the optional ML-based OmniCloudMask detector.
Raises ImportError on call.
Examples:
>>> from geotoolz.qa import OmniCloudMask
>>> OmniCloudMask(checkpoint="default").get_config()
{'checkpoint': 'default'}
Source code in src/geotoolz/qa/_src/operators.py
S2Cloudless
¶
Bases: Operator
Placeholder for the optional ML-based s2cloudless mask.
Raises ImportError on call. Reserved for the future
[cloud-ml] extra so that pipelines can be configured today and
light up once the dependency is installed.
Examples:
>>> from geotoolz.qa import S2Cloudless
>>> S2Cloudless(threshold=0.4).get_config()
{'threshold': 0.4}
Source code in src/geotoolz/qa/_src/operators.py
S2QA60
¶
Bases: Operator
Sentinel-2 L1C QA60 cloud + cirrus mask preset.
QA60 (per ESA's S2 L1C product specification) encodes opaque clouds in bit 10 and cirrus in bit 11. Returns True where either is set.
Note: QA60 is unreliable / zeroed-out on newer processing baselines
(≥ 04.00). Prefer the L2A SCL band (S2SCL) or an ML-based
detector when available.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
qa_band
|
int | str
|
Band selector for QA60 within the input stack. |
'QA60'
|
axis
|
int
|
Band axis position. |
0
|
Examples:
Source code in src/geotoolz/qa/_src/operators.py
S2SCL
¶
Bases: Operator
Sentinel-2 L2A SCL preset that masks pixels outside keep classes.
Returns True for pixels to mask: every SCL class except those
named in keep. By default vegetation, soil, and water are kept.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
qa_band
|
int | str
|
Band selector for the SCL band. |
'SCL'
|
keep
|
Sequence[str]
|
Class names to keep (do not mask). See
|
('vegetation', 'soil', 'water')
|
axis
|
int
|
Band axis position. |
0
|
Examples:
>>> from geotoolz.qa import S2SCL
>>> # Default: mask everything that isn't vegetation/soil/water.
>>> mask = S2SCL()(s2_l2a_with_scl)
>>> # Custom: keep only vegetation.
>>> mask = S2SCL(keep=["vegetation"])(s2_l2a_with_scl)
Source code in src/geotoolz/qa/_src/operators.py
Sensor registry¶
| Preset | QA source | Default mask targets | Reference |
|---|---|---|---|
S2QA60 |
Sentinel-2 L1C QA60 bitmask (bit 10 cloud, 11 cirrus) |
cloud + cirrus | ESA S2 L1C product spec |
S2SCL |
Sentinel-2 L2A SCL classes |
mask everything except vegetation (4), soil (5), water (6) | Sen2Cor product spec |
LandsatQA_PIXEL (sensor=l89) |
Landsat 8/9 C2 QA_PIXEL |
cloud (bit 3), cloud shadow (bit 4), cirrus (bit 2) | USGS LSDS-1619 |
LandsatQA_PIXEL (sensor=l7) |
Landsat 4-7 C2 QA_PIXEL (no cirrus bit) |
cloud (bit 3), cloud shadow (bit 4) | USGS LSDS-1618 |
MODISStateQA |
MODIS state_1km / state_500m |
cloud (bits [0,1] field, values 1,2) + cloud shadow (bit 2) | MOD09 User's Guide, Table 12 |
All QA mask operators return boolean GeoTensor masks with the original CRS and transform preserved and fill_value_default=False. The convention is True means "mask this pixel out".
MODIS bit-field semantics¶
MODIS state_1km packs categorical fields into multi-bit slots:
- Bits
[0, 1](2 bits): cloud state —0=clear,1=cloudy,2=mixed,3=not-set. - Bit
2: cloud shadow. - Bits
[8, 9](2 bits): cirrus level —0=none,1=small,2=average,3=high.
OR-ing the bits individually (the standard Landsat semantics) would flag value 3 (not-set) as cloudy, which is wrong. MODISStateQA therefore decodes these as field values via mask_from_bit_field.