Sentinel-2 Composites for Any Map View
Pick three bands and a date range, and this canvas builds a 10 m Sentinel-2 composite of the current map view. The view is split into blocks that run on up to 50 machines at once. Each finished block is saved as a COG, so panning and zooming afterwards only stream tiles.
Try it out
The dashboard runs on load using the area in view. Start zoomed in on a city; a view already computed with the same settings loads straight from storage.
1 · Dashboard
A MapLibre map with a control panel:
- Bands: any three Sentinel-2 bands, with presets for true colour, false colour (
nir,red,green) and SWIR (swir22,nir,red). - Dates and Quality: date range, max cloud cover, max scenes per block, and the composite method (
median,mean,min,max,first). - Run composite uses the current map view. Pan or zoom, then run again.
A table below the map lists the matching scenes; Footprints draws their outlines.
2 · Scene search
s2_scenes queries the tge-labs Sentinel-2 L2A STAC-GeoParquet mirror with DuckDB, filtering by bounding box, date range and cloud cover. It returns one row per scene with a href_<band> COG URL for each band.
curl "https://udf.ai/fc_fused/Sentinel2_Composite/s2_scenes/run/file?format=csv&aoi=-74.05,40.68,-73.90,40.82&date_start=2025-06-01&date_end=2025-09-30&max_cloud=10&limit=5"
3 · Composite blocks
The dashboard splits the view into zoom-12 tiles of 1024 px each and sends every block to s2_composite_block, up to 50 in parallel. Each call loads its scenes onto the block's grid with odc.stac.load, reduces over time with xarray, and writes the result as a COG:
# doctest: skip
da = ds.to_array("band").where(lambda v: v > 0)
comp = getattr(da, method)("time", skipna=True) # median / mean / min / max
Blocks are stored under fd://s2_composite_blocks/<job>/, where the job ID is a hash of the settings. Re-running the same settings skips blocks that already exist.
4 · Tile server
s2_composite_tiles serves the stored blocks as XYZ tiles with rio-tiler. It only reads the saved COGs, never the source imagery. Pass job=all for every stored composite, or a job ID for one:
curl -o tile.png "https://udf.ai/fc_fused/Sentinel2_Composite/s2_composite_tiles/run/tiles/12/1206/1539?dtype_out_raster=png&job=all"
About the data
| Property | Value |
|---|---|
| Imagery | Sentinel-2 Collection 1 L2A COGs, AWS Open Data (anonymous) |
| Scene catalog | tge-labs s2-stac-geoparquet on Source Cooperative |
| Bands | blue … swir22 (B02–B12), any three at a time |
| Output | 10 m RGBA COGs, served as XYZ tiles |
See also
- Monthly Median Composite: one composite per calendar month, each on its own machine.
- NDVI Time Series for Farm Fields: Sentinel-2 sampled per field as time series.
- STAC catalogs guide: how Fused reads Sentinel-2 scenes through STAC.