CMU Water Center marked the October 2024 high-water line at ~4,900 places in Chiang Mai and Lamphun (depth above the road while P.1 stood at 5.30 m). - src/static/flood-marks-2024.json (position and depth, 110 KB), built by scripts/build_flood_marks.py from CMU's GeoJSON. - "Flood marks 2024" layer: canvas dots from zoom 12 in CMU's five depth classes, legend row, CMU credit; turning it on below zoom 12 flies to the city. A mark's popup gives the depth and its flood zone. - Zone and marker popups name the nearest mark within 250 m (loaded with the zones). - No depth estimate for other P.1 levels: the marks do not scale one-to-one with P.1 (marks in the 5.30 m zone stood a median 65 cm deep in 2024). - The layer bar wraps on narrow maps; popups pan clear of it. - Docs: DATA_SOURCES 5.1, DASHBOARD_REDESIGN, README.
72 lines
2.6 KiB
Python
72 lines
2.6 KiB
Python
"""Build src/static/flood-marks-2024.json from CMU Water Center's flood marks.
|
|
|
|
After the October 2024 flood (P.1 peaked at 5.30 m on 5 October), CMU Water
|
|
Center marked the high-water line at about 5,000 places in Chiang Mai and
|
|
Lamphun (CMFlood, "Flood marks 2024"): the depth above the road surface, in
|
|
cm. This keeps position and depth only, in a compact array form:
|
|
|
|
uv run python scripts/build_flood_marks.py # fetch from CMU
|
|
uv run python scripts/build_flood_marks.py --geojson F # or a saved copy
|
|
"""
|
|
|
|
import argparse
|
|
import json
|
|
import urllib.request
|
|
from pathlib import Path
|
|
|
|
REPO = Path(__file__).resolve().parents[1]
|
|
OUT = REPO / "src" / "static" / "flood-marks-2024.json"
|
|
URL = "https://watercenter.cmu.ac.th/data/New_floodMark_2025_fixed_like_pole.geojson"
|
|
SOURCE = (
|
|
"CMU Water Center (ศูนย์วิชาการสนับสนุนการบริหารจัดการน้ำ มหาวิทยาลัยเชียงใหม่), "
|
|
"flood marks of October 2024, "
|
|
"https://watercenter.cmu.ac.th/chiangmai/cmflood/warning/floodmark"
|
|
)
|
|
|
|
|
|
def load(path: Path | None) -> dict:
|
|
if path:
|
|
return json.loads(path.read_text(encoding="utf-8"))
|
|
req = urllib.request.Request(URL, headers={"User-Agent": "Mozilla/5.0"})
|
|
with urllib.request.urlopen(req, timeout=120) as r:
|
|
return json.loads(r.read().decode("utf-8"))
|
|
|
|
|
|
def main() -> None:
|
|
ap = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
|
ap.add_argument("--geojson", type=Path, help="read CMU's GeoJSON from here")
|
|
args = ap.parse_args()
|
|
|
|
marks, skipped = [], 0
|
|
for f in load(args.geojson)["features"]:
|
|
p = f.get("properties") or {}
|
|
lon, lat, depth = p.get("longitude"), p.get("latitude"), p.get("water_level")
|
|
if lon is None or lat is None or depth is None:
|
|
skipped += 1
|
|
continue
|
|
# 18-19.5 N, 98.5-99.5 E: a swapped or empty coordinate is skipped
|
|
if not (18.0 < lat < 19.5 and 98.5 < lon < 99.5) or depth <= 0:
|
|
skipped += 1
|
|
continue
|
|
marks.append([round(lon, 5), round(lat, 5), round(depth)])
|
|
marks.sort(key=lambda m: (m[1], m[0]))
|
|
doc = {
|
|
"source": SOURCE,
|
|
"p1_level": 5.30,
|
|
"date": "2024-10-05",
|
|
"fields": ["lon", "lat", "depth_cm_above_road"],
|
|
"marks": marks,
|
|
}
|
|
OUT.write_text(
|
|
json.dumps(doc, ensure_ascii=False, separators=(",", ":")) + "\n",
|
|
encoding="utf-8",
|
|
)
|
|
print(
|
|
f"wrote {OUT.relative_to(REPO)}: {len(marks)} marks, {skipped} skipped, "
|
|
f"{OUT.stat().st_size / 1024:.0f} KB"
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|