Skip to content
Merged
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
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ primary law and reproducible methods that can support legal action when needed.
| Wetlands | Five federal inventories of protected wetlands |
| Sources and groundwater | Groundwater bodies, headwaters, sub-catchments and drinking-water protection zones |
| Monitoring duties | Federal duties and a 26-canton public-evidence audit; evidence and freshness, not a compliance score |
| Water budget | BAFU's annual national and catchment balance, the stores in ice, lakes, groundwater, soil and reservoirs, public drinking-water supply, and the latest complete month of outflow, groundwater, lake levels and drought index; a separate [page](https://opengovclimate.ch/riverflow/budget.html) that also lists the missing data and who holds it |

This is not a forecast, a finding of breach or legal advice. It is also not a
complete account of Swiss water. Important omissions are listed on the
Expand Down Expand Up @@ -144,6 +145,8 @@ node build/12-wetlands.mjs
node build/13-basins.mjs
node build/17-quality.mjs
node build/18-monitoring.mjs
node build/19-budget.mjs
node build/20-conditions.mjs
node build/08-vintage.mjs
python3 build/14-pages.py build
node build/15-social-card.mjs
Expand Down
14 changes: 7 additions & 7 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,16 @@ A new layer is ready only when it has:

| Pathway | Current state | Next evidence needed |
|---|---|---|
| Precipitation | Named in the cycle; not mapped | MeteoSwiss gridded precipitation and station provenance |
| Precipitation | BAFU's annual modelled national and catchment totals on the budget page | MeteoSwiss gridded precipitation, a citable national monthly series and station provenance |
| Snow | Named; glacier ice mapped | Operational snow-water equivalent and uncertainty |
| Glacier ice | Six dated inventories | Annual mass balance and basin contribution |
| Soil and infiltration | Gap disclosed | Soil moisture and an explicit recharge model |
| Groundwater | Bodies shown as WMS context | NAQUA levels and quality, spring discharge, recharge |
| Glacier ice | Six dated inventories; national volume from the GLAMOS bulletin | Annual mass balance and volume as an open series, and basin contribution |
| Soil and infiltration | Modelled soil moisture in the weekly drought index | Measured soil moisture and an explicit recharge model |
| Groundwater | Bodies shown as WMS context; monthly levels against each site's own record | Storage, NAQUA quality, spring discharge, recharge |
| Rivers | Live gauges plus network estimates | Small streams, travel time, diversions and uncertainty |
| Lakes | Context only | Levels, profiles, quality and residence time |
| Lakes | Monthly levels against each site's own record | Profiles, quality and residence time |
| Wetlands | Five federal inventories | Hydrological condition, buffer pressures and restoration |
| Storage and use | Reservoir series and registers | Current permits, withdrawals, return flows and concessions |
| Evapotranspiration | Gap disclosed | Observed or modelled basin water loss |
| Storage and use | Reservoir series, registers, store estimates and annual drinking-water supply | Current permits, withdrawals by sector, return flows and concessions |
| Evapotranspiration | BAFU's annual modelled national and catchment totals | Current gridded actual evapotranspiration |
| Borders | Swiss context | Upstream and downstream basin continuity |

## Monitoring and enforcement
Expand Down
51 changes: 51 additions & 0 deletions build/08-vintage.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,39 @@ const SOURCES = [
fixed: null, // filled from reservoirs.json below: the last row of the file
note: 'The file allows only map.geo.admin.ch to read it from a browser, so it is baked in at build time. The Pages workflow rebuilds weekly to keep the baked copy fresh.',
},
{
key: 'waterBalance', layer: null,
name: 'Natural water balance of Switzerland and its large catchments',
holder: 'BAFU', cls: 'modelled', cadence: 'annual; the table is replaced each year', live: false,
url: 'https://www.bafu.admin.ch/de/wasserhaushalt-heute',
links: [
{ label: 'method', url: 'https://www.bafu.admin.ch/dam/de/sd-web/r5wsqRim2y1u/WHH_CH_WEL_2017_3_Methode.pdf' },
],
licence: 'No licence stated on the file; federal website terms, with source attribution.',
fixed: null, // filled from budget.json below: the end of the balance year
note: 'PREVAH model output constrained by measured discharge. Read on the water budget page, which keeps every year it has read.',
},
{
key: 'drought', layer: null,
name: 'Drought platform, numeric data: discharge, groundwater, lake levels and drought index',
holder: 'BAFU, MeteoSwiss, swisstopo', cls: 'measured', cadence: 'daily, weekly and monthly files', live: false,
url: 'https://data.geo.admin.ch/ch.bafu.trockenheitsdaten-numerisch',
links: [
{ label: 'drought platform', url: 'https://www.trockenheit.admin.ch/' },
],
licence: 'FSDI general terms of use, free use with source attribution',
fixed: null, // filled from conditions.json below: the platform's own update date
note: 'Only complete weeks and months are used. The drought index is partly modelled: it combines measured precipitation and discharge with modelled soil moisture.',
},
{
key: 'drinkingWater', layer: null,
name: 'Public drinking-water supply by origin',
holder: 'BFS, from SVGW', cls: 'survey', cadence: 'annual', live: false,
url: 'https://dam-api.bfs.admin.ch/hub/api/dam/assets/36409183/master',
licence: 'BFS terms of use: free use with source attribution',
fixed: null, // filled from budget.json below: the end of the latest year
note: 'An extrapolation from the suppliers that report to the SVGW. Other uses are national estimates for 2006 or carry no year.',
},
{
key: 'dams', layer: 'ch.bfe.stauanlagen-bundesaufsicht',
name: 'Dams under federal supervision',
Expand Down Expand Up @@ -294,6 +327,21 @@ try {
qualityLatest = q.meta?.sourceLast ?? null;
} catch { /* quality not built yet; the field stays null and the page says so */ }

// The water budget dates itself by the period it describes: the end of the balance
// year, the end of the latest drinking-water year, and the drought platform's own
// update date.
let balanceEnd = null, drinkingEnd = null, droughtUpdated = null;
try {
const b = JSON.parse(await fs.readFile(new URL('../site/data/budget.json', import.meta.url), 'utf8'));
balanceEnd = b.year ? `${b.year}-12-31` : null;
const last = b.use?.drinking?.rows?.at(-1);
drinkingEnd = last ? `${last.y}-12-31` : null;
} catch { /* run build/19-budget.mjs first */ }
try {
const c = JSON.parse(await fs.readFile(new URL('../site/data/conditions.json', import.meta.url), 'utf8'));
droughtUpdated = c.meta?.updated ?? null;
} catch { /* run build/20-conditions.mjs first */ }

const sources = [];
// The protection zones have no data state of their own. A national picture made of
// 26 cantonal deliveries is exactly as current as the canton that delivered last,
Expand All @@ -315,6 +363,9 @@ for (const s of SOURCES) {
if (s.key === 'reservoirFill') ds = fillLatest;
if (s.key === 'quality') ds = qualityLatest;
if (s.key === 'zones') ds = zoneOldest;
if (s.key === 'waterBalance') ds = balanceEnd;
if (s.key === 'drinkingWater') ds = drinkingEnd;
if (s.key === 'drought') ds = droughtUpdated;
sources.push({
key: s.key,
name: s.key === 'zones' && zoneCt ? `${s.name} (oldest delivery: ${zoneCt})` : s.name,
Expand Down
14 changes: 7 additions & 7 deletions build/14-pages.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
"""Five pages in five languages, with German at the publication root.
"""Six pages in five languages, with German at the publication root.

The English files under site/ are the originals except for the landing page,
whose durable source is build/pages/index.en.html. The translated files are
Expand All @@ -15,7 +15,7 @@
reads "Law" cannot rewrite the word law in a paragraph below it.

python3 build/14-pages.py missing de [page ...] what German still owes
python3 build/14-pages.py build write the twenty files
python3 build/14-pages.py build write the twenty-four files
and the sitemap
"""
import json, os, re, subprocess, sys
Expand All @@ -24,7 +24,7 @@
HERE = os.path.dirname(os.path.abspath(__file__))
SITE = os.path.join(os.path.dirname(HERE), 'site')
TRANS = os.path.join(HERE, 'pages')
PAGES = ['index.html', 'about.html', 'method.html', 'law.html', 'sources.html']
PAGES = ['index.html', 'budget.html', 'about.html', 'method.html', 'law.html', 'sources.html']
LANGS = ['de', 'fr', 'it', 'rm']

# A chunk stops at any of these. Inline markup (b, i, em, a, code, sub, abbr, br)
Expand Down Expand Up @@ -185,7 +185,7 @@ def load(lang):
# --- the mechanical half ---------------------------------------------------
ASSETS = ('fonts.css', 'tokens.css', 'style.css', 'site.css', 'favicon.svg', 'i18n.js',
'i18n-data.js', 'i18n-map.js', 'theme.js', 'fmt.js', 'gschg31.js',
'legal-alerts.js', 'site.js', 'app.js')
'legal-alerts.js', 'site.js', 'budget.js', 'app.js')
BASE = 'https://opengovclimate.ch/riverflow/'


Expand Down Expand Up @@ -214,7 +214,7 @@ def relocate(raw, lang, page):
# English reading pages keep their stable root URLs, while the English
# map now lives at /en/.
if lang == 'en':
for target in ('method.html', 'sources.html', 'law.html', 'about.html'):
for target in ('method.html', 'sources.html', 'law.html', 'about.html', 'budget.html'):
raw = raw.replace('href="%s' % target, 'href="../%s' % target)
alts = [
'<link rel="alternate" hreflang="en" href="%s">' % ('./' if lang == 'en' else '../en/'),
Expand Down Expand Up @@ -252,7 +252,7 @@ def root_german(raw):
for attr in ('href', 'src'):
raw = re.sub(r'%s="\.\./%s' % (attr, re.escape(asset)),
'%s="%s' % (attr, asset), raw)
for target in ('method.html', 'sources.html', 'law.html', 'about.html'):
for target in ('method.html', 'sources.html', 'law.html', 'about.html', 'budget.html'):
raw = raw.replace('href="%s' % target, 'href="de/%s' % target)
alts = [
'<link rel="alternate" hreflang="en" href="en/">',
Expand Down Expand Up @@ -332,7 +332,7 @@ def build():
print('%s/%s: %d chunk(s) untranslated' % (lang, page, len(todo)))
owed += len(todo)
open(os.path.join(SITE, lang, page), 'w', encoding='utf-8').write(out)
print('%s: five pages written' % lang)
print('%s: %d pages written' % (lang, len(PAGES)))
open(os.path.join(SITE, 'index.html'), 'w', encoding='utf-8').write(
root_german(open(os.path.join(SITE, 'de', 'index.html'), encoding='utf-8').read()))
sitemap()
Expand Down
Loading
Loading