Skip to content

Commit 97a2423

Browse files
maps
1 parent 1cb6e30 commit 97a2423

6 files changed

Lines changed: 156 additions & 14 deletions

File tree

cybench/runs/analysis/collect_walk_forward_results.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,11 @@ def write_model_comparison_dashboard(
455455
map_payload = build_region_map_payload(output_dir, summary_rows)
456456
html_dir = str(output_dir)
457457
if bundle_assets:
458+
records = bundle_referenced_assets(
459+
records=records,
460+
output_dir=html_dir,
461+
assets_dirname="assets",
462+
)
458463
if map_payload.get("datasets"):
459464
map_payload = bundle_region_map_assets(
460465
map_payload, output_dir, assets_dirname="assets"
@@ -463,11 +468,6 @@ def write_model_comparison_dashboard(
463468
write_region_map_sidecar(output_dir, map_payload)
464469
if map_payload.get("datasets") and map_payload.get("geojson_by_country"):
465470
records = strip_map_pngs_from_records(records)
466-
records = bundle_referenced_assets(
467-
records=records,
468-
output_dir=html_dir,
469-
assets_dirname="assets",
470-
)
471471
elif map_payload.get("datasets") and map_payload.get("geojson_by_country"):
472472
records = strip_map_pngs_from_records(records)
473473
html_path = output_dir / "compare_models.html"

cybench/runs/slurm/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,12 @@ poetry run python data_preparation/fetch_zenodo_data.py --geometries
453453
# creates cybench/data/polygons/DE/DE.shp, NL/NL.shp, ...
454454
```
455455

456+
For map geometry only (no 6.2 GB cybench-data.zip):
457+
458+
```bash
459+
poetry run python data_preparation/fetch_zenodo_data.py --geometries-only
460+
```
461+
456462
**World outline** (grey background in map panels): bundled at
457463
``data_preparation/ne_50m_admin_0_countries/`` (default). Falls back to 110m if missing.
458464
Override with ``CYBENCH_WORLD_MAP_SCALE=10|50|110``.

cybench/runs/viz/dashboard_template.html

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -324,13 +324,19 @@ <h3>Drill-down plots</h3>
324324
if (geoCache.has(country)) return geoCache.get(country);
325325
const href = (MAP_DATA.geojson_by_country || {})[country];
326326
if (!href) return null;
327-
const geo = await d3.json(href);
328-
const features = (geo.features || []).map(f => ({
329-
...f,
330-
properties: { ...f.properties, loc: String(f.properties.loc ?? "") },
331-
}));
332-
geoCache.set(country, features);
333-
return features;
327+
try {
328+
const geo = await d3.json(href);
329+
if (!geo || !geo.features) return null;
330+
const features = geo.features.map(f => ({
331+
...f,
332+
properties: { ...f.properties, loc: String(f.properties.loc ?? "") },
333+
}));
334+
geoCache.set(country, features);
335+
return features;
336+
} catch (err) {
337+
console.warn("GeoJSON load failed:", href, err);
338+
return null;
339+
}
334340
}
335341

336342
function yieldColor(value, extent) {
@@ -822,9 +828,12 @@ <h3>Drill-down plots</h3>
822828
renderDynamicMaps(selectedCell.dataset, selectedCell.model).then(ok => {
823829
if (!ok) {
824830
showMapMode(false);
831+
const href = ((MAP_DATA.geojson_by_country || {})[
832+
(MAP_DATA.datasets[selectedCell.dataset] || {}).country
833+
]) || "assets/regions_*.geojson";
825834
missingNote.textContent =
826-
"Could not render dynamic maps (missing geometry or preds). "
827-
+ "Re-run collect to export preds/ and ensure polygons exist.";
835+
`Could not load map geometry (${href} missing or failed). `
836+
+ "Re-run collect + publish so regions_*.geojson is bundled in assets/.";
828837
}
829838
}).catch(err => {
830839
showMapMode(false);

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ transformers = ">=4.55.0,<5.0.0"
4242
tabpfn = ">=6.4.0,<7.0.0"
4343
tabicl = ">=2.1.0"
4444
tabdpt = ">=1.2.0"
45+
zenodo-get = "^1.6.1"
4546

4647
[build-system]
4748
requires = ["poetry-core"]
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
"""Tests for Zenodo dataset fetch helper."""
2+
3+
from __future__ import annotations
4+
5+
import zipfile
6+
from pathlib import Path
7+
8+
import pytest
9+
10+
from data_preparation.fetch_zenodo_data import (
11+
DEFAULT_DOI,
12+
extract_geometry_zip,
13+
fetch,
14+
)
15+
16+
17+
def _write_geometry_zip(dest: Path, folder_name: str, country: str = "DE") -> None:
18+
with zipfile.ZipFile(dest, "w") as zf:
19+
zf.writestr(f"{folder_name}/{country}/readme.txt", "stub shapefile tree")
20+
21+
22+
def test_extract_geometry_zip(tmp_path: Path):
23+
zip_path = tmp_path / "polygons.zip"
24+
data_dir = tmp_path / "data"
25+
_write_geometry_zip(zip_path, "polygons", "DE")
26+
extract_geometry_zip(zip_path, data_dir, "polygons")
27+
assert (data_dir / "polygons" / "DE" / "readme.txt").is_file()
28+
29+
30+
def test_fetch_geometries_only_skip_download(tmp_path: Path):
31+
staging = tmp_path / "staging"
32+
data_dir = tmp_path / "data"
33+
staging.mkdir()
34+
_write_geometry_zip(staging / "centroids.zip", "centroids", "NL")
35+
_write_geometry_zip(staging / "polygons.zip", "polygons", "NL")
36+
37+
fetch(
38+
data_dir,
39+
staging_dir=staging,
40+
include_geometries=True,
41+
geometries_only=True,
42+
skip_download=True,
43+
)
44+
assert (data_dir / "polygons" / "NL" / "readme.txt").is_file()
45+
assert (data_dir / "centroids" / "NL" / "readme.txt").is_file()
46+
47+
48+
def test_geometries_only_requires_geometry_zips_when_skip_download(tmp_path: Path):
49+
staging = tmp_path / "staging"
50+
staging.mkdir()
51+
with pytest.raises(FileNotFoundError, match="geometry zips missing"):
52+
fetch(
53+
tmp_path / "data",
54+
staging_dir=staging,
55+
include_geometries=True,
56+
geometries_only=True,
57+
skip_download=True,
58+
)
59+
60+
61+
def test_default_doi_is_concept_record():
62+
assert DEFAULT_DOI == "10.5281/zenodo.11502142"

tests/runs/test_region_map_lib.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,67 @@ def test_strip_map_pngs_from_records():
9595
assert "map_actual" not in records[0]["images"]
9696
assert "map_pred" not in records[0]["images"]
9797
assert records[0]["images"]["scatter"] == "c.png"
98+
99+
100+
def test_bundle_region_map_survives_referenced_assets(tmp_path: Path, monkeypatch):
101+
"""GeoJSON must be written after bundle_referenced_assets (which wipes assets/)."""
102+
from cybench.runs.analysis.collect_walk_forward_results import (
103+
write_model_comparison_dashboard,
104+
)
105+
106+
output_dir = tmp_path / "paper_walk_forward_us_eos_v2"
107+
preds = output_dir / "preds" / "ridge_eos"
108+
preds.mkdir(parents=True)
109+
pd.DataFrame(
110+
{
111+
"adm_id": ["US-01-001"],
112+
"year": [2020],
113+
"yield": [10.0],
114+
"Ridge": [9.5],
115+
}
116+
).to_csv(preds / "maize_US_h10_year_2020.csv", index=False)
117+
118+
scatter_src = preds / "report_assets"
119+
scatter_src.mkdir(parents=True)
120+
scatter_src.joinpath("maize_US_scatter.png").write_bytes(b"png")
121+
122+
summary_rows = [
123+
{
124+
"dataset": "maize_US",
125+
"model": "ridge",
126+
"horizon": "eos",
127+
"model_col": "Ridge",
128+
"nrmse": 0.1,
129+
"r2": 0.8,
130+
"r_spatial": 0.5,
131+
"r_spatial_agg": 0.5,
132+
"r_temporal": 0.4,
133+
"r_temporal_agg": 0.4,
134+
"r_res": 0.3,
135+
"r2_res": 0.2,
136+
"n_regions": 1,
137+
"n_years": 1,
138+
"n_samples": 1,
139+
}
140+
]
141+
142+
def _fake_export(country_code: str, dest: Path, **kwargs):
143+
dest.parent.mkdir(parents=True, exist_ok=True)
144+
dest.write_text(
145+
'{"type":"FeatureCollection","features":[{"type":"Feature",'
146+
'"properties":{"loc":"US-01-001"},"geometry":{"type":"Point","coordinates":[0,0]}}]}',
147+
encoding="utf-8",
148+
)
149+
return dest
150+
151+
monkeypatch.setattr(
152+
"cybench.runs.viz.region_map_lib.export_region_geojson",
153+
_fake_export,
154+
)
155+
156+
write_model_comparison_dashboard(output_dir, summary_rows, bundle_assets=True)
157+
geojson = output_dir / "assets" / "regions_US.geojson"
158+
assert geojson.is_file(), "regions_US.geojson must survive asset bundling"
159+
html = (output_dir / "compare_models.html").read_text(encoding="utf-8")
160+
assert "regions_US.geojson" in html
161+
assert (output_dir / "assets" / "maize_US_scatter.png").is_file()

0 commit comments

Comments
 (0)