Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Legacy sensors

SPOT VGT and Proba-V

UNEP
IMEO
MARS

Modules:

  • georeader/readers/spotvgt_image_operational.py (389 LOC)

  • georeader/readers/probav_image_operational.py (701 LOC)

Role: read SPOT VGT and Proba-V — two coarse-resolution operational vegetation-monitoring sensors that pre-date the Sentinel-2 era. No ASCII diagrams in either file. They’re operational readers built before the docstring-illustration push.


1. Why these readers exist at all

SPOT VGT and Proba-V are vegetation-monitoring missions built around daily global coverage rather than high spatial resolution:

SensorOperatorYearsGSDBandsCoverage
SPOT VGTCNES + VITO1998–20141 kmBlue, Red, NIR, SWIRDaily global
Proba-VESA + VITO2013–2020100 m / 300 m / 1 kmBlue, Red, NIR, SWIRNear-daily global

Both are 1 km–100 m sensors with 4 bands (Blue, Red, NIR, SWIR) — vastly less rich than the Sentinel-2-era spectrometers. So why include them?

  1. Historical baseline. Pre-2015 vegetation studies almost all use VGT. To build a multi-decadal time series you need to read VGT data the same way you read S2 data.

  2. Continuity. Proba-V was the explicit gap-filler between VGT (2014 end-of-life) and Sentinel-3 (operational 2018). Some climate datasets stitch all three.

  3. Operational simplicity. These products are tiled in WGS84 with simple GeoTIFFs — no SAFE complexity, no GLT, no per-pixel coords. They’re useful as a “minimal sensor reader” reference.

The headers explicitly call out that these are unofficial readers — built by the package authors against published user manuals, not certified by the agencies. Behaviour matches the manual but isn’t guaranteed against future product-format changes.


2. The shared design pattern

Both readers expose the same minimal GeoData-shaped surface:

SpotVGT(path, ...)
ProbaV(path, ...)
ProbaVRadiometry(path, ...)   # subclass with band-level ToA reflectance
ProbaVSM(path, ...)            # subclass for the Status Map (QA mask)

Each class implements:

So you can drop a SpotVGT or ProbaV instance into any read.read_from_polygon(reader, ...) call. The GeoData protocol is the abstraction that makes this just work.


3. Internal helpers — read_band_toa

Both modules have a read_band_toa(dataset, band, slice_to_read) function:

Used internally by both readers — saves repeating the calibration boilerplate per call site.


4. SPOT VGT: SpotVGT

The single class is at spotvgt_image_operational.py:65. Standard layout:

SM bit decoding

The SPOT VGT Status Map encodes 8 flags in one byte. The functions decode bits like:

BitMeaning
0Clear
1Cloud shadow
2Undefined
3Cloud
4Ice / snow
5Water
6Land
7Mixed

sm_cloud_mask(sm, mask_undefined=True) returns a boolean mask of “definitely cloudy” pixels — cloud OR cloud_shadow [OR undefined].


5. Proba-V: ProbaV, ProbaVRadiometry, ProbaVSM

The Proba-V hierarchy is more elaborate (probav_image_operational.py):

The Status Map decoder functions sm_cloud_mask and mask_only_sm are duplicated between the two modules — same names, same logic, different bit conventions. Proba-V’s SM is a different format than VGT’s despite both coming from VITO; the readers can’t share decoder code.

The Proba-V file also has compression-availability checks (is_compression_available, assert_compression_available) — Proba-V uses LZW or DEFLATE per acquisition, and pre-2017 readers without recent libtiff would silently fail to decode some files. The check raises a clear error before reading.


6. Why the readers are similar but not identical

A code-deduplication argument would say “extract a shared LegacyVITOReader base.” The package doesn’t, for two pragmatic reasons:

  1. The user manuals describe them as separate products. A user navigating from the VGT manual to the code wants a SpotVGT class, not a LegacyVITOReader.create(sensor="vgt").

  2. Bit-flag conventions differ. Even though both Status Maps look superficially similar (8 bits per pixel, one bit per quality flag), the bit→meaning mapping is different. Sharing a decoder would require a configuration map that obscures more than it saves.

This is a recurring pattern in sensor readers: the surface area looks like it should generalise, but the per-sensor calibration / bit / metadata details bake in enough divergence that the de-duplication isn’t worth the indirection.


7. Function reference

SPOT VGT (spotvgt_image_operational.py)

Proba-V (probav_image_operational.py)


8. Sharp edges


9. Why no diagrams

The two modules predate the documentation push that added ASCII diagrams to the rest of the package. They’re stable, used by a handful of climate-baseline pipelines, and the maintainers haven’t added illustrations because there’s not much to illustrate — these are simple band-stack readers without the structural complexity of EMIT (GLT) or PRISMA (curvilinear) or S2 SAFE (multi-resolution + JP2 stacking).

Including them in this tutorial preserves the catalog completeness, but you can probably skip these if you’re not specifically working with VGT or Proba-V. The readers are stable, well-named, and self-documenting in their docstrings.


10. Connection to geotoolz

Neither sensor has a dedicated preset in geotoolz.md. Proba-V and SPOT VGT are unlikely to grow new operators because they’re discontinued. They’d live in geotoolz.presets.legacy if ever — a presets.legacy.PROBAV_NDVI operator would be Sequential([ProbaVRadiometry.load(), MaskClouds(via SM), NDVI(red_idx=1, nir_idx=2)]). Trivial to implement, but rare enough that no-one’s asked.

The general lesson: operators are sensor-agnostic; readers are sensor-specific. As long as a sensor’s reader returns a GeoData with band semantics that match a downstream operator’s red_idx= / nir_idx= arguments, the operator works without any sensor-specific code.

Next chapter: Query & download — query / catalog / download helpers (scihubcopernicus_query, download_pv_product, query_utils, tileserver, download_utils).