Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions .github/workflows/docker-era5-sidecar.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
name: "Docker: era5-sidecar"

on:
push:
branches:
- main
tags:
- "v*.*.*"
paths:
- '.github/workflows/docker-era5-sidecar.yaml'
- 'deployment/Dockerfile.era5-sidecar'
- 'scripts/era5/**'

pull_request:
branches:
- "main"
paths:
- '.github/workflows/docker-era5-sidecar.yaml'
- 'deployment/Dockerfile.era5-sidecar'
- 'scripts/era5/**'

permissions:
contents: read
packages: write

jobs:
docker:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 0
persist-credentials: false

-
name: Get Git information
id: git-info
run: |
echo "branch=${GITHUB_REF#refs/heads/}" >> "$GITHUB_OUTPUT"
echo "short_sha=${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"
echo "timestamp=$(date +%s)" >> "$GITHUB_OUTPUT"
shell: bash

-
name: Docker meta
id: meta
uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0
with:
images: |
ghcr.io/${{ github.repository_owner }}/era5-sidecar
tags: |
type=raw,value=${{ steps.git-info.outputs.branch }}-${{ steps.git-info.outputs.short_sha }}-${{ steps.git-info.outputs.timestamp }}
type=raw,value=latest
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=sha

-
name: Set up QEMU
uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0

-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
with:
platforms: linux/amd64,linux/arm64

-
name: Login to GitHub Container Registry
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

-
name: Build and push
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
with:
context: .
file: ./deployment/Dockerfile.era5-sidecar
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
platforms: linux/amd64,linux/arm64
108 changes: 108 additions & 0 deletions .github/workflows/docker-era5-website.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
name: "Docker: website-era5"

on:
push:
branches:
- main
tags:
- "v*.*.*"
paths:
- '.github/workflows/docker-era5-website.yaml'
- '.dockerignore'
- 'deployment/Dockerfile.website.era5'
- 'deployment/default.conf.era5.template'
- 'assets/**'
- 'code/**'
- 'pages/**'
- 'public/**'
- 'styles/**'
- '*config.js'
- '.config/**'
- 'package*.json'
- 'yarn.lock'

pull_request:
branches:
- "main"
paths:
- '.github/workflows/docker-era5-website.yaml'
- '.dockerignore'
- 'deployment/Dockerfile.website.era5'
- 'deployment/default.conf.era5.template'
- 'assets/**'
- 'code/**'
- 'pages/**'
- 'public/**'
- 'styles/**'
- '*config.js'
- '.config/**'
- 'package*.json'
- 'yarn.lock'

permissions:
contents: read
packages: write

jobs:
docker:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 0
persist-credentials: false

-
name: Get Git information
id: git-info
run: |
echo "branch=${GITHUB_REF#refs/heads/}" >> "$GITHUB_OUTPUT"
echo "short_sha=${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"
echo "timestamp=$(date +%s)" >> "$GITHUB_OUTPUT"
shell: bash

-
name: Docker meta
id: meta
uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0
with:
images: |
ghcr.io/${{ github.repository_owner }}/website-era5
tags: |
type=raw,value=${{ steps.git-info.outputs.branch }}-${{ steps.git-info.outputs.short_sha }}-${{ steps.git-info.outputs.timestamp }}
type=raw,value=latest
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=sha

-
name: Set up QEMU
uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0

-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
with:
platforms: linux/amd64,linux/arm64

-
name: Login to GitHub Container Registry
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

-
name: Build and push
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
with:
context: .
file: ./deployment/Dockerfile.website.era5
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
platforms: linux/amd64,linux/arm64
56 changes: 56 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,3 +180,59 @@ For the first case we suggest to load the data directly from the `data` folder.
For the second case we provide a [Datasette](https://datasette.io/) API that serves all the data in this repository.

> TODO: provide the infrastructure and examples of how to load data from the Datasette API.

## ERA5 Live Climate Data (Ali je vroče? — ERA5)

The `/ali-je-vroce-era5/` page shows live ERA5-Land climate trends for Slovenia powered by a Python sidecar service (`scripts/era5/`). The sidecar fetches daily ERA5-Land data from [Open-Meteo](https://open-meteo.com/), pre-computes statistics into SQLite, and serves them at `/api/live/*` — proxied through nginx so the frontend needs no separate origin.

### Sidecar files

| File | Purpose |
|------|---------|
| `scripts/era5/mk_collect.py` | Fetches ERA5-Land CSVs from Open-Meteo (18 Slovenian stations) |
| `scripts/era5/mk_precompute.py` | Builds `era5-slovenia.db` SQLite from CSVs |
| `scripts/era5/mk_sidecar.py` | Flask API on port 5052 — all `/api/live/*` routes |
| `scripts/era5/si.yaml` | Station list and feature flags |
| `scripts/era5/pyproject.toml` | Python dependencies (managed by `uv`) |
| `deployment/Dockerfile.era5-sidecar` | Sidecar container image |
| `deployment/Dockerfile.website.era5` | Website image with nginx ERA5 proxy |
| `deployment/default.conf.era5.template` | nginx config — proxies `/api/live/` to sidecar |

### Running the full ERA5 stack locally with Docker

```bash
# Build both images
docker build -f deployment/Dockerfile.era5-sidecar -t podnebnik/era5-sidecar .
docker build -f deployment/Dockerfile.website.era5 -t podnebnik/website-era5 .

# Shared network + persistent data volume
docker network create era5-net
docker volume create era5-data

# Start the sidecar (collects data on first run — ~10 min)
docker run -d --name era5-sidecar \
--network era5-net \
-v era5-data:/app/data/si \
podnebnik/era5-sidecar

# Start the website (proxies /api/live/* to the sidecar)
docker run -d --name era5-website \
--network era5-net -p 8090:80 \
-e ERA5_SIDECAR_HOST=era5-sidecar \
podnebnik/website-era5
```

Open http://localhost:8090/ali-je-vroce-era5/.

### Charts

- **Today status** — national temperature percentile vs ERA5 history (1950–present), with last-7-days strip
- **Regression chart** — per-station Theil-Sen trend + TFPW Mann-Kendall significance, with projection to 2050
- **Season heatmap** — seasonal anomaly calendar across all stations
- **SPEI heatmap + drought trend** — standardised precipitation-evapotranspiration index, per station and season
- **Tropical days / nights** — annual event counts above a user-chosen threshold, fitted with Negative Binomial GLM
- **Sea level — Koper** — IPCC AR6 rise projections (SSP2-4.5 / SSP5-8.5) with interactive flood-zone overlay for the northern Adriatic

### Static flood data

The sea-level widget requires pre-rendered flood-zone images served from `public/data/flood/` (25 PNGs at 10 cm increments, 10–250 cm) and `public/data/flood-stats.json` (affected area and building counts per level). These files are checked into the repository and are bundled into the website image at build time.
Loading
Loading