This repository contains the processing stack used to monitor urban green areas in Athens from Earth Observation data (Sentinel-2). It combines:
- an actinia + GRASS GIS processing backend,
- custom GRASS add-ons for Sentinel-2 scene filtering, Sentinel-2 scene download and STAC publishing,
- process chain templates for NDVI/NDWI production and classification.
The main output is a set of geospatial products (NDVI/NDWI and classified versions) plus STAC item updates for catalog publication.
Note: This workflow was designed to be integrated with the satellite.cityofathens.gr repository. Adding STAC items of processed raster layers to STAC collection might not work properly if running this worklow independently (needs correct STAC catalog setup).
The workflow in this repository is designed to:
- Filter Sentinel-2 scenes by date range, AOI, tile id, and cloud cover.
- Download and import selected scenes into GRASS GIS.
- Use L2A cloud probability layer to mask out clouds. Cloud proabability threshold is set to 65 %. (Could be changed in
processing/templates/template_S2_download_import.json). See also i.sentinel.import. - Compute NDVI and NDWI maps.
- Classify NDVI/NDWI outputs using configured categories for vegetation health
assessment. The threshold for the categorization is set in
processing/input/index_classification/*classes. - Export raster outputs as COG.
- Create and publish STAC items to an existing STAC catalog/collection and update collection extent metadata.
In ./grass-gis-addons/
i.s2_id.filter- Module to query Sentinel-2 metadata via
EODAGand return scene IDs based on filters (date, AOI, tile id, cloud cover).
- Module to query Sentinel-2 metadata via
i.s2_id.download- Module to download selected Sentinel-2 scenes using
EODAG. Imports bands usingi.sentinel.importfor processing.
- Module to download selected Sentinel-2 scenes using
i.create.stac- Module to create a STAC item from exported assets and publish it to an
existing STAC catalog/collection. Also updates collection extents. Uses
pystacandrio-stac.
- Module to create a STAC item from exported assets and publish it to an
existing STAC catalog/collection. Also updates collection extents. Uses
In ./processing/scripts/
create_export_subfolders.py- utility script to create export directory for exported COGs. Directory is parsed in process chain template.
remove_data.py- cleanup script to remove downloaded Sentinel-2 SAFE files.
rename_bands.py- utility script to rename imported raster bands in GRASS location for further processing.
Jinja2 templates in ./processing/templates/. Variables are parsed in
run_service.py and rendered as JSON before submission to actinia.
process_chain_filter_S2_scenes.json.j2- process chain to run
i.s2_id.filterand return filtered scene ids.
- process chain to run
process_chain_S2_processing.json.j2- process chain to trigger processing for parsed Sentinel-2 scene ids.
Actinia module templates in ./processing/templates/ are used by
process_chain_S2_processing.json.j2.
template_S2_processing.json- process template to run the main processing steps for a Sentinel-2 scene: import, NDVI/NDWI calculation, classification, export, and STAC item creation. Calls the following templates for specific steps:
template_S2_download_import.json- process template to download Sentinel-2 scenes using
i.s2_id.downloadand to import bands usingi.sentinel.import.
- process template to download Sentinel-2 scenes using
template_calc_NDVI.json- process template to calculate NDVI and categorize it. Exports layers as COG.
template_calc_NDWI.json- process template to calculate NDWI and categorize it. Exports layers as COG.
processing/run_service.py- script to run the whole workflow. Parses parameters, renders process chain templates, submits to actinia, and monitors execution.
All Docker commands below are run from the docker/ directory. For further
docker instructions see docker/README.md.
Create docker/.env with actinia and CDSE
credentials:
ACTINIA_USER=<your_user>
ACTINIA_PW=<your_password>
EODAG_USER=<your_CDSE_user>
EODAG_PW=<your_CDSE_password>Do not commit real credentials in docker/.env.
docker compose -f docker-compose.yml -p athen_urban-green buildFor a full rebuild after dependency changes:
docker compose -f docker-compose.yml -p athen_urban-green build --no-cacheFor a Local Setup: you need to mount a local directory for data export. Add
to volumes for actinia /path/to/local/export/dir:/src/export_dir, then:
docker compose -f docker-compose.yml -p athen_urban-green up- Version endpoint:
http://localhost:8088/api/v3/version - Locations endpoint:
http://localhost:8088/api/v3/locations
docker compose -f docker-compose.yml -p athen_urban-green downThe script in processing/run_service.py starts the whole workflow.
From repository root folder:
python processing/run_service.pyImportant script parameters to adapt before production runs:
Sentinel-2 query parameters:
-
Time range (
START_TIME,END_TIME, or automatic time range mode) for filtering Sentinel-2 scenes.Options:
- manual time range: Set
START_TIMEandEND_TIMEe.g.START_TIME = "2026-04-05"andEND_TIME = "2026-04-10" - automatic time range: Queries given STAC collection
STAC_COLLECTION_URLfor latest item and setsSTART_TIMEaccordingly andEND_TIMEto current time. For the current settings, the collectionndvi-athis used which is updated with each workflow run. Check with names defined inSTAC_COLLECTIONS.
- manual time range: Set
-
TILE_ID: Sentinel-2 tile identifier e.g.34SGHfor Athens area -
MAX_CLOUD_COVER: Max. cloud cover threshold -
AOI: By default, the script uses a predefined AOI for Athens in
processing/input/athens_aoi.geojson.- To use a different AOI, replace this file with a new GeoJSON containing the desired AOI geometry (and rebuild image).
- Or specify a bounding box by setting
LONMIN,LONMAX,LATMIN,LATMAX.- Note: Currently
-aflag is set for i.s2_id.filter in process chain. This enables AOI filtering based on predifined AOI. For bounding box filtering-aflag must be removed. --> TODO: implement automatic flag removal when bounding box parameters are set.
- Note: Currently
actinia process parameters:
It should not be necessary to change these parameters. To be able to reach
actinia a correct actinia base URL (ACTINIA_BASE_URL) is required. The default
of this setup is http://localhost:8088/.
STAC parameters:
STAC_CATALOG_URL: Should link to the STAC catalog"http://pycsw:8000/stac/"STAC_COLLECTIONS: Names of the STAC collections, where the created items are registered. For this workflow four collections for each product are used:"ndvi-ath,ndvi-cat-ath,ndwi-ath,ndwi-cat-ath"PRODUCT_NAMES: Names used for the STAC items of the four products (same order asSTAC_COLLECTIONS):"NDVI,NDVI_categorized,NDWI,NDWI_categorized"STAC_ITEM_ID_PREFIX: Defines a prefix for the STAC item IDs: e.g."athen_urban_green"so the STAC item ID will be like thisathen_urban_green_NDWI_categorized_20260218T091031STAC_ITEM_TITLE: Title for STAC item. Additionally, product name and date are added to the title. E.g.Urban Green Monitoring Athens- NDWI_categorized - 2026-02-18 09:10:31+00:00STAC_ITEM_DESCRIPTION: Description for STAC item. Currently it is the same text for all products.
Note for a local setup: Adding STAC item to a collection only works if you have write access to the collection.
Required python dependencies for run_service.py are listed in requirements.txt
and include:
requestsdotenvjinja2
The main output layers are exported as Cloud Optimized GeoTIFFs (COG):
- NDVI (Normalized Difference Vegetation Index)
- Categorized NDVI
- NDWI (Normalized Difference Water Index)
- Categorized NDWI
Categorization thresholds for NDVI and NDWI are defined in processing/input/index_classification/ndvi_4_classes and processing/input/index_classification/ndwi_3_classes.
Uses equations:
- NDVI = (NIR - Red) / (NIR + Red)
- NDWI = (( NIR - SWIR ) / ( NIR + SWIR ))
NDVI classes (raster values in brackets):
- no vegetation (1): -1 to 0.1
- bare soil (2): 0.1 to 0.2
- sparse/stressed vegetation (3): 0.2 to 0.5
- dense/healthy vegetation (4): 0.5 to 1.0
NDWI classes (raster values in brackets):
- barren soil (1): -1000 to -200
- strong water stress (2): -200 to 0
- medium water stress (3): 0 to 100
- low water stress (4): 100 to 200
- no water stress (5): 200 to 1000
- Authentication failures:
- verify
ACTINIA_USER,ACTINIA_PW,EODAG_USER,EODAG_PWindocker/.env.
- verify
- No scenes returned:
- widen time window, increase cloud threshold, verify tile ID and AOI.
- Process polling errors:
- inspect actinia status URL and container logs for detailed module errors.
.
├── docker
│ └── ...
├── grass-gis-addons
│ ├── i.create.stac
│ │ └── ...
│ ├── i.s2_id.download
│ │ └── ...
│ └── i.s2_id.filter
│ └── ...
├── LICENSES
│ └── GPL-3.0-or-later.txt
├── processing
│ ├── input
│ │ ├── aoi
│ │ │ └── Athens_aoi.geojson
│ │ └── index_classification
│ │ └── ...
│ ├── run_service.py
│ ├── scripts
│ │ ├── create_export_subfolders.py
│ │ ├── remove_data.py
│ │ └── rename_bands.py
│ └── templates
│ ├── process_chain_filter_S2_scenes.json
│ ├── process_chain_filter_S2_scenes.json.j2
│ ├── process_chain_S2_processing.json
│ ├── process_chain_S2_processing.json.j2
│ ├── template_calc_NDVI.json
│ ├── template_calc_NDWI.json
│ ├── template_S2_download_import.json
│ └── template_S2_processing.json
├── README.md
├── requirements.txt
└── ...
This project is licensed under GPL-3.0-or-later. See LICENSE and
LICENSES/GPL-3.0-or-later.txt.