The BlueGreenCity-KI pipeline processes urban tree data, computes risk scores across multiple criteria, and writes results to files and PostGIS. It can be run:
All methods use the same CLI entry point and configuration file.
conda activate <project-conda-env>
Set-Location <path-to-repository>
Config file config/config.yaml must be present and valid (data sources, criteria weights, output paths configured).
PostGIS must be reachable if PostGIS inputs or outputs are configured (see config/config.yaml → inputs / output.postgis).
| Runner | Entry Point | Description |
|---|---|---|
| Risk Scoring (static) | bluegreencity_ki.runners.cli |
Rule-based multi-criteria drought risk scoring |
All runners read from config/config.yaml unless another path is passed via --config.
| Flag | Description |
|---|---|
--config <path> |
Path to the YAML config (required). |
--output <path> |
Override the GeoPackage output path. |
--train |
Force retraining even if a model for the configured window already exists. |
--skip-holdout-validation |
Always skip stage-2 holdout validation. |
--force-holdout-validation |
Force stage-2 holdout validation even when the model was only loaded (not retrained). |
--skip-validation |
Skip run-monitoring validation against drought-damage ground truth. |
--shap <scope> |
SHAP scope for operative predictions: high-risk (top-N trees), risk-classes (all trees in the configured risk classes), full (all trees), or off. Default from ml.shap.scope. |
Stage 2 (holdout validation) writes pure model-evaluation artefacts —
leaderboards/, feature_importances/, temporal_holdout/,
predictions_<year>.gpkg, layout.json. These change only when the model is
retrained, so on an operational run that merely loads an existing model the
stage is skipped automatically. It runs when the model was just (re)trained
(--train) or when explicitly forced with --force-holdout-validation.
risk-classes (incremental)--shap risk-classes computes SHAP drivers for the configured risk classes
(ml.shap.risk_classes_include, default critical, high, medium). Set
ml.shap.risk_classes_max_rows_per_class to a positive number for a
deterministic, class-stratified sample; 0 explains every matching tree.
Results are written incrementally per batch to two row-aligned files:
risk_classes_shap_values.csv contains the contributions and
risk_classes_feature_values.csv contains the original feature values,
prediction score, and risk class required for a color-coded beeswarm. The
*_drivers_shap columns are also UPDATEd in the database, so progress is
visible during the run and partial results survive an interruption. A restarted
run resumes by skipping trees that already carry SHAP drivers for the
current run_date. Batch size is ml.shap.batch_size.
Runs in the current terminal window. Output is printed directly to the console. Use this during development or when you want to observe the output in real time.
conda activate <project-conda-env>
python -m bluegreencity_ki.runners.cli --config config/config.yaml
Optional: Override the output path without changing the config:
python -m bluegreencity_ki.runners.cli --config config/config.yaml --output data/derived/my_run.gpkg
When to use:
Starts the pipeline as a fully detached Windows process via WMI (Win32_Process.Create). The process runs in session 0 under the Services context and is therefore not tied to the VS Code terminal session — it keeps running when VS Code or the terminal is closed. The full output (stdout + stderr) is written unbuffered to a timestamped log file in logs/.
Why not
Start-Processfrom the VS Code terminal? Processes started withStart-Processfrom a VS Code terminal belong to the terminal’s job object and are killed when the VS Code window is closed.Start-Process -RedirectStandardOutputadditionally buffers all output, so the log file stays empty during the run.Why
conda runinstead of callingpython.exedirectly? Callingenvs\<env>\python.exedirectly leavesLibrary\binof the environment off thePATH. psycopg2/libpq then loads incompatible DLLs from the systemPATH, which crashes the process with an access violation (0xc0000005) at PostGIS writer initialisation.conda run -n <env> --no-capture-outputsets up the environment correctly and streams output unbuffered.
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$cmd = "cmd.exe /c set PYTHONUNBUFFERED=1&& C:\ProgramData\anaconda3\condabin\conda.bat run -n <project-conda-env> --no-capture-output python -u -m bluegreencity_ki.runners.cli --config config/config.yaml > logs\pipeline_run_$timestamp.log 2>&1"
$r = Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{
CommandLine = $cmd
CurrentDirectory = '<path-to-repository>'
}
Write-Host "Pipeline gestartet - PID $($r.ProcessId) - Log: logs\pipeline_run_$timestamp.log"
When to use:
--shap risk-classes, multiple days)Monitor progress from a second terminal:
# Check if the process is still running (session-0 processes are not always
# visible to Get-Process filters - use tasklist)
tasklist /FI "IMAGENAME eq python.exe" /FO TABLE
# Tail the log in real time
Get-Content logs/pipeline_run_<timestamp>.log -Wait -Tail 30
Stop the detached process:
# Find the PID via tasklist (see above), then:
Stop-Process -Id <pid> -Force
All pipeline logs are written to logs/ in the project root. Log files are not committed to Git (.gitignore).
| File pattern | Description |
|---|---|
pipeline_run_YYYYMMDD_HHMMSS.log |
Full output of a Start-Process run (stdout + stderr combined) |
A successful run produces output in three phases:
Warnings like the following are non-critical and can be ignored:
FutureWarning: Neither ogr.UseExceptions() nor ogr.DontUseExceptions() has been explicitly called.
Errors that indicate a failed run include:
RuntimeError: Criterion '...' returned None → criterion implementation issueNo raster data available for ... spatial threshold computation → configured raster file or PostGIS table not foundAll outputs are written to data/derived/ (default) or the path configured under output.path in config/config.yaml.
| File | Description |
|---|---|
trees_at_risk_<area>.gpkg |
GeoPackage with all risk scores (for QGIS) |
trees_at_risk_<area>.csv |
Flat CSV with all risk scores |
trees_at_risk_run_report.md |
Human-readable run summary (stats per criterion) |
trees_at_risk_run_stats.json |
Machine-readable stats (for downstream processing) |
report/validation/ |
Validation CSVs, charts, top-k and decile tables for static and ML |
trees_at_risk_species_counts.csv |
Species counts for prioritizing tolerance factor research |
trees_at_risk_dists/*.png |
Score distribution histograms per criterion |
If output.postgis.enabled: true, results are additionally written to the PostGIS table bgcki.analysis_tree_risk_scores.
| Symptom | Likely cause | Fix |
|---|---|---|
No raster data available for day spatial threshold computation |
airtemp_raster_14h configured as PostGIS but table missing, or configured file path does not exist |
Check config/config.yaml → inputs.airtemp_raster_14h; switch to type: file if PostGIS table is not populated |
No raster data available for land use |
landuse_raster PostGIS table missing or file not found |
Check inputs.landuse_raster in config |
| Log file contains only the GDAL FutureWarning, nothing else | Python output is still buffered (process still running) | Check with Get-Process -Name python; wait for completion |
Output files not in data/output/ |
Default output path is data/derived/ |
Check config/config.yaml → output.path |
| PostGIS write fails | DB not reachable or table schema mismatch | Verify connection; check output.postgis config |
risk_assessment_ml/archive/)