# -*- coding: utf-8 -*-
"""L78 ダム基本情報・維持管理情報 単独 3 研究例分析
       — 広島県管理 12 ダム / 7 水系 / 6 完成年代を 3 角度で深掘り

カバー宣言:
  本記事は DoBoX のシリーズ「ダム基本情報・維持管理情報」 1 件
  (dataset_id = 21) を <b>単独</b>で取り上げ、
  広島県が管理する <b>治水ダム 12 基</b>を
  3 つの独立した研究角度 (RQ1 / RQ2 / RQ3) で並列に分析する。
  「県の最大水管理装置」 を構造・流域支配・老朽化の 3 軸から立体的に描く。

  「ダム」 とは:
    河川の流れを<b>堰き止めて貯水</b>する大規模構造物。河川管理者 (国・県・市町・
    電力会社) が建設する公共土木施設で、 治水 (洪水調節) ・利水 (飲料水・農業
    用水・工業用水) ・発電の 3 大目的を持つ。 河川法第 26 条の<b>河川区域内
    工作物</b>として位置付けられ、 堤体の<b>型式</b> (= 構造分類: 重力式コンクリート
    / アーチ式 / フィル / ロックフィル等) が安全性・コスト・地盤適合性を規定する。
    本データは広島県が管理する<b>治水ダム 12 基</b>のみを収録 (= 県管理。 国管理の
    弥栄ダム等や中国電力管理の発電ダムは含まれない)。

  本記事は L07 (河川浸水 × 4 種インフラ) / L31 (観測情報 5 種類) /
  S39 (ダム基本情報 12 基台帳俯瞰) と<b>厳密に区別</b>:
    L07 = 浸水ハザードと<b>4 種インフラ</b> (橋梁/トンネル/ダム/ため池) の
          交差判定。 ダムは「14 件中 0 件浸水」 という単純判定で扱われる。
    L31 = 観測情報 5 種類 (雨量/水位/<b>ダム水位</b>/潮位/風) の<b>観測網</b>
          分析。 「ダム水位」 観測点は扱うが、 ダム本体の構造は扱わない。
    S39 = ダム基本情報の<b>3 図 + 4 集計の台帳俯瞰</b>。 完成年代・水系・診断
          結果の単純集計に留まる。 本記事の前段に位置付けられる入門編。
    L78 = ダム<b>単独の研究水準深掘り</b>。 7 図 + 11 表 + 5 仮説検証で
          「構造」 「流域支配」 「老朽化」 の 3 RQ を並列に解く。
    L78 は S39 を<b>超える深さ</b>を 3 RQ × 5 仮説 × 11 表 × 7 図で実現する。

研究の問い (3 RQ):
  RQ1 (主研究): 広島県の<b>ダムの構造 — 型式・規模・地理分布</b>はどう描けるか?
       12 基を <b>型式 / 堤高 / 堤頂長 / 堤体積 / 総貯水容量 / 集水面積 /
       水系 / 完成年代 / 診断結果</b>の 9 軸で集計し、
       「県の最大水管理装置の物理形状」 を初めて系統的に記述する。
       特に「型式は重力式コンクリート 1 種類しか採用されていない」 という
       <b>構造選択の偏り</b>を量的に示す。 H1 = 県管理ダムは<b>型式 100% が
       重力式コンクリート</b>仮説、 H2 = 堤高は<b>30-80m 帯に集中</b>仮説。

  RQ2 (副研究 1): ダムの<b>流域支配 — 上流集水と下流被災ポテンシャル</b>はどう描けるか?
       集水面積 (km²) ・総貯水容量 (千 m³) ・有効貯水容量比から、 各ダムが
       「どれだけの上流流域を支配し、 どれだけの水を貯められるか」 を量化、
       <b>本記事独自の指標「貯水深 (容量 / 集水面積)」 「容量充填率 (有効/総)」
       「単位集水面積あたり貯水容量」</b>を導入し、 12 基をランキングする。
       下流被災ポテンシャルは「下流人口・建物数」 が公開データに含まれないため、
       <b>市町別の存在</b>と<b>周辺の人口集中地区 (DID) 距離</b>で代用する。
       H3 = 容量/集水面積比 (= 貯水深) で<b>魚切ダムが県内 Top 3</b>仮説、
       H4 = 「集水面積 vs 総貯水容量」 は両対数で<b>r &gt;= 0.7</b>の強相関仮説。

  RQ3 (副研究 2): ダムの<b>老朽化と更新ピーク — 1960-70s ダムの再評価</b>は
       どう描けるか? 完成年月 (S39.6 〜 H28.8 = 1964 〜 2016) から経過年を計算し、
       <b>築 50 年以上 (= 1974 年以前)</b>の老朽ダムを抽出。 国の<b>インフラ長寿命化
       基本計画 (2014)</b>が想定する更新ピーク到来の実態を、 12 基という小さな母集団
       でも個体名で語れる粒度で描く。 H5 = 築 50 年以上が<b>3 基以上</b>かつ
       それらは S30-40 年代 (1955-74) の<b>高度成長期初期</b>に集中する仮説。

仮説 (5):
  H1 (RQ1, 型式単一): 県管理ダム 12 基は<b>型式 100% が重力式コンクリート</b>。
       他の型式 (アーチ・フィル・ロックフィル) は<b>0 件</b>。 これは広島県の地盤・
       地形・コスト条件下では重力式が最適選択であり続けた制度的選択仮説。

  H2 (RQ1, 堤高集中): 堤高は<b>30-80m 帯</b>に <b>10 基以上</b>が集中。
       100m 超の超高ダムは無く、 30m 未満の小規模ダムも無い。 これは「県管理ダム
       は治水中規模帯」 という<b>規模クラスタの単一性</b>仮説。

  H3 (RQ2, 貯水深 Top 3): 貯水深 (= 総貯水容量 ÷ 集水面積) で<b>魚切ダムが
       県内 Top 3</b>。 集水面積 38.4 km² から 8,460 千 m³ を貯める設計は、
       他ダムより<b>単位集水面積あたり貯水量が多い</b> = 大都市直下流の治水・
       利水ダムとしての<b>過剰設計</b>の物理的証拠仮説。

  H4 (RQ2, 集水 ↔ 容量 強相関): 集水面積 (km²) と総貯水容量 (千 m³) の
       両対数 Pearson r &gt;= <b>0.7</b>。 大流域ほど大容量という直観の量的検証。
       強相関なら「流域 = 容量決定要因」、 弱相関なら「設計目的 (治水/利水)
       が容量を決める」 という対立構造の仮説。

  H5 (RQ3, 老朽集中): 築 50 年以上 (= 1974 年以前架設) のダムが<b>3 基以上</b>。
       それらは S30-40 年代 (1955-74) の<b>高度成長期初期</b>に集中する。
       国の<b>インフラ長寿命化基本計画 (2014)</b>が想定する更新ピーク (= 2030
       年代) が県管理ダムにも到達することを実データで示す仮説。

要件 S 準拠 (1 分以内完走):
  - 全データ 12 件 / 21 列 → 超軽量
  - POINT geometry のみ (緯度経度から直接生成) → sjoin 高速
  - 重い前処理は無し。本スクリプト 1 本で完結 (~10 秒目標)
  - L44 既キャッシュ admin_diss.gpkg (市町 polygon) を再利用

要件 T 準拠 (位置情報あり = 地図必須):
  - RQ1: 県全域 型式 + 規模バブルマップ、 水系別色分けマップ
  - RQ2: 集水面積 vs 総貯水容量 散布図 + 流域支配バブルマップ
  - RQ3: 完成年代 timeline + 老朽ダム強調マップ

要件 Q 準拠: 図 7 / 表 11 (3 RQ × 多角度: 構造 / 流域 / 老朽化)

データ仕様:
  - dataset 21: ダム基本情報・維持管理情報 (CSV)
  - 形式: CSV (UTF-8 BOM), 12 行 × 21 列
  - 列: ダムコード, 建設(支)局コード, 分類, 水系名, 河川名, 施設区分, ダム名,
       管理者, 都道府県, 位置, 緯度, 経度, 完成年月, 集水面積_km2, 堤体積_千m3,
       総貯水容量_千m3, 有効貯水容量_千m3, 型式, 堤高_m, 堤頂長_m, 診断結果
  - 全件 河川法上の<b>河川ダム / 治水ダム</b>区分
  - 全件 管理者 = 広島県 (国/電力会社管理ダムは含まれない)
  - ライセンス: クリエイティブ・コモンズ表示 (CC-BY)

メモリ対策: Figure ごとに plt.close('all') で確実に解放。
"""
from __future__ import annotations
import sys, time, re
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))
from _common import ROOT, ASSETS, LESSONS, render_lesson, code, figure

import numpy as np
import pandas as pd
import geopandas as gpd
from shapely.geometry import Point
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt

plt.rcParams["font.family"] = "Yu Gothic"
plt.rcParams["axes.unicode_minus"] = False

t_all = time.time()
print("=== L78 ダム基本情報・維持管理情報 単独 3 研究例分析 ===", flush=True)

# =============================================================================
# 0. 定数・パス
# =============================================================================
TARGET_CRS = "EPSG:6671"   # 平面直角第 III 系 (m 単位、 距離計算で正確)
DATASET_ID = 21
SRC_CSV = ROOT / "data" / "extras" / "dam_basic.csv"

# 行政界キャッシュ (L44 から共有)
ADMIN_GPKG = ROOT / "data" / "extras" / "L44_storm_surge" / "_cache" / "admin_diss.gpkg"

# 老朽閾値 (本記事独自)
OLD_THRESHOLD_YEARS = 50   # 築 50 年以上 = 老朽
NOW_YEAR = 2024            # 解析基準年 (執筆時点 + 1 年)

# 水系の表示色 (S39 と統一)
WS_COLORS = {
    "小瀬川": "#0969da",
    "沼田川": "#1f883d",
    "野呂川": "#bf8700",
    "八幡川": "#cf222e",
    "芦田川": "#8250df",
    "賀茂川": "#d4a72c",
    "江の川": "#1b6c8c",
}

# 完成年代のバンド色 (古いほど赤系)
ERA_COLORS = {
    "1960年代": "#cf222e",   # 高度成長期初期 = 赤
    "1970年代": "#cf6f00",   # 同期後期 = 橙
    "1980年代": "#bf8700",   # = 黄
    "1990年代": "#1a7f37",   # 平成初期 = 緑
    "2000年代": "#0969da",   # 平成中期 = 青
    "2010年代": "#8250df",   # 平成末〜令和 = 紫
}
ERA_ORDER = ["1960年代", "1970年代", "1980年代",
             "1990年代", "2000年代", "2010年代"]

# 規模クラス境界 (本記事独自)
def size_class(v_kk_m3):
    """総貯水容量 (千 m³) → 規模クラス"""
    if pd.isna(v_kk_m3):
        return "(不明)"
    if v_kk_m3 >= 8000:
        return "大 (>=8000 千 m³)"
    if v_kk_m3 >= 1500:
        return "中 (1500-8000 千 m³)"
    return "小 (<1500 千 m³)"

# 元号 → 西暦変換 (S39.6 → (1964, 6), H21.10 → (2009, 10))
ERA_RE = re.compile(r"^([SH])(\d+)\.(\d+)$")
SHOWA_BASE = 1925   # S 元年 = 1926, S39 = 1964
HEISEI_BASE = 1988  # H 元年 = 1989, H21 = 2009

def parse_era(s):
    m = ERA_RE.match(str(s).strip())
    if not m:
        return None, None
    g, yy, mm = m.groups()
    yy, mm = int(yy), int(mm)
    if g == "S":
        year = SHOWA_BASE + yy
    elif g == "H":
        year = HEISEI_BASE + yy
    else:
        return None, None
    decade = f"{(year // 10) * 10}年代"
    return year, decade


# =============================================================================
# 1. データ読込み
# =============================================================================
print("\n[1] CSV 読込み", flush=True)
t1 = time.time()
df = pd.read_csv(SRC_CSV, encoding="utf-8-sig")
df = df.dropna(subset=["ダム名"]).reset_index(drop=True)
df = df[df["ダム名"].astype(str).str.strip() != ""].reset_index(drop=True)
print(f"  ダム件数: {len(df)}", flush=True)
assert len(df) == 12, f"想定 12 件、 実際 {len(df)} 件"

# 数値列の型変換
for c in ["緯度", "経度", "集水面積_km2", "堤体積_千m3",
          "総貯水容量_千m3", "有効貯水容量_千m3", "堤高_m", "堤頂長_m"]:
    df[c] = pd.to_numeric(df[c], errors="coerce")

# 派生列
_eras = df["完成年月"].map(parse_era).tolist()
df["完成西暦"] = pd.Series([t[0] for t in _eras], dtype="Int64")
df["完成年代"] = [t[1] for t in _eras]
df["経過年"] = NOW_YEAR - df["完成西暦"].astype(float)
df["規模クラス"] = df["総貯水容量_千m3"].map(size_class)
df["is_old"] = df["経過年"] >= OLD_THRESHOLD_YEARS

# 流域支配指標 (本記事独自)
df["貯水深_mm"] = (df["総貯水容量_千m3"] * 1000.0
                   / (df["集水面積_km2"] * 1_000_000.0)) * 1000.0
# = 総貯水容量 (m³) / 集水面積 (m²) を mm に変換
# = 「集水面積全体に均等に水深 X mm 分の貯水ができる」 という直感量
df["容量充填率"] = (df["有効貯水容量_千m3"]
                    / df["総貯水容量_千m3"] * 100).round(1)
df["延べ堤体積指標"] = (df["堤体積_千m3"] * 1.0
                         / (df["堤高_m"] * df["堤頂長_m"]) * 1000)
# = 堤体積 (m³) / (堤高 × 堤頂長) → 堤体厚みの代理指標 (m)

# 単位集水面積あたり貯水容量 (1km² あたり何千 m³ を貯められるか)
df["容量集水比"] = (df["総貯水容量_千m3"]
                    / df["集水面積_km2"]).round(1)

n_dams = len(df)
print(f"  ({time.time()-t1:.2f}s)", flush=True)


# =============================================================================
# 2. GeoDataFrame 化
# =============================================================================
print("\n[2] GeoDataFrame 化", flush=True)
t2 = time.time()
geom = [Point(lon, lat) for lon, lat in zip(df["経度"], df["緯度"])]
gdf = gpd.GeoDataFrame(df, geometry=geom, crs="EPSG:4326").to_crs(TARGET_CRS)
print(f"  ({time.time()-t2:.2f}s)", flush=True)


# =============================================================================
# 3. 行政界 sjoin (市町判定)
# =============================================================================
print("\n[3] 行政界 sjoin", flush=True)
t3 = time.time()
admin = gpd.read_file(ADMIN_GPKG).to_crs(TARGET_CRS)

# CITY_CD → 市町名 (L77 から借用)
CITY_NAME = {
    101: "広島市中区", 102: "広島市東区", 103: "広島市南区", 104: "広島市西区",
    105: "広島市安佐南区", 106: "広島市安佐北区", 107: "広島市安芸区", 108: "広島市佐伯区",
    202: "呉市", 203: "竹原市", 204: "三原市", 205: "尾道市", 207: "福山市",
    208: "府中市", 209: "三次市", 210: "庄原市", 211: "大竹市", 212: "東広島市",
    213: "廿日市市", 214: "安芸高田市", 215: "江田島市",
    302: "府中町", 304: "海田町", 307: "熊野町", 309: "坂町",
    368: "安芸太田町", 369: "安芸太田町", 412: "北広島町",
    462: "世羅町", 545: "神石高原町", 543: "神石高原町",
}
admin["市町名"] = admin["CITY_CD"].map(CITY_NAME).fillna(
    admin["CITY_CD"].astype(str))

# CSV の「位置」 列をそのまま使うが、 空間判定で補強
gdf["seg_id"] = [f"L78_{i:02d}" for i in range(len(gdf))]
joined = gpd.sjoin(gdf[["seg_id", "geometry"]],
                   admin[["市町名", "geometry"]],
                   how="left", predicate="within")
joined = joined.drop_duplicates("seg_id")
gdf["市町_空間"] = gdf["seg_id"].map(
    joined.set_index("seg_id")["市町名"]).fillna("不明")
print(f"  市町 sjoin: 全 {n_dams} 件",
      f"不明 {(gdf['市町_空間']=='不明').sum()} 件", flush=True)
print(f"  ({time.time()-t3:.2f}s)", flush=True)


# =============================================================================
# 4. RQ1 集計: 構造 (型式・規模・地理分布)
# =============================================================================
print("\n[4] RQ1 集計 — 構造", flush=True)
t4 = time.time()

# (1) 型式別
T_form = (df.groupby("型式")
          .agg(基数=("ダム名", "count"),
               堤高平均=("堤高_m", "mean"),
               総貯水容量合計_千m3=("総貯水容量_千m3", "sum"))
          .round(1).reset_index())
T_form["シェア_%"] = (T_form["基数"] / n_dams * 100).round(1)
T_form = T_form.sort_values("基数", ascending=False).reset_index(drop=True)

n_gravity = int(T_form.loc[T_form["型式"] == "重力式コンクリート", "基数"].iloc[0]) \
            if (T_form["型式"] == "重力式コンクリート").any() else 0
share_gravity = round(100 * n_gravity / n_dams, 1)
h1_ok = share_gravity == 100.0

# (2) 規模クラス別
T_size = (df.groupby("規模クラス")
          .agg(基数=("ダム名", "count"),
               堤高平均=("堤高_m", "mean"),
               堤頂長平均=("堤頂長_m", "mean"),
               総貯水容量合計_千m3=("総貯水容量_千m3", "sum"))
          .round(1).reset_index())
T_size["シェア_%"] = (T_size["基数"] / n_dams * 100).round(1)
size_order = ["大 (>=8000 千 m³)", "中 (1500-8000 千 m³)", "小 (<1500 千 m³)"]
T_size["順"] = T_size["規模クラス"].apply(
    lambda x: size_order.index(x) if x in size_order else 99)
T_size = T_size.sort_values("順").drop(columns="順").reset_index(drop=True)

# (3) 堤高ビン別
height_bins = [0, 30, 50, 80, 100]
height_labels = ["~30m", "30-50m", "50-80m", "80m+"]
df["堤高帯"] = pd.cut(df["堤高_m"], bins=height_bins,
                       labels=height_labels, include_lowest=True)
T_height = (df.groupby("堤高帯", observed=False)
            .agg(基数=("ダム名", "count")).reset_index())
T_height["シェア_%"] = (T_height["基数"] / n_dams * 100).round(1)
n_30_80 = int(T_height.loc[T_height["堤高帯"].isin(["30-50m", "50-80m"]),
                              "基数"].sum())
h2_ok = n_30_80 >= 10

# (4) 水系別
T_water = (df.groupby("水系名")
           .agg(基数=("ダム名", "count"),
                総貯水容量合計_千m3=("総貯水容量_千m3", "sum"),
                集水面積合計_km2=("集水面積_km2", "sum"))
           .round(1).reset_index())
T_water["シェア_%"] = (T_water["基数"] / n_dams * 100).round(1)
T_water = T_water.sort_values("基数", ascending=False).reset_index(drop=True)

# (5) 市町別 (空間 sjoin 結果)
T_city = (gdf.groupby("市町_空間")
          .agg(基数=("ダム名", "count"),
               総貯水容量合計_千m3=("総貯水容量_千m3", "sum"),
               代表ダム=("ダム名", lambda s: "・".join(s.tolist())))
          .reset_index())
T_city = T_city.sort_values("基数", ascending=False).reset_index(drop=True)

print(f"  型式: {n_gravity}/12 が重力式コンクリート ({share_gravity}%)",
      flush=True)
print(f"  堤高 30-80m: {n_30_80}/12 ({round(100*n_30_80/n_dams,1)}%)",
      flush=True)
print(f"  H1 (型式 100% 重力式): {h1_ok}", flush=True)
print(f"  H2 (堤高 30-80m に 10+ 基): {h2_ok}", flush=True)
print(f"  ({time.time()-t4:.2f}s)", flush=True)


# =============================================================================
# 5. RQ2 集計: 流域支配 (集水面積・容量・貯水深)
# =============================================================================
print("\n[5] RQ2 集計 — 流域支配", flush=True)
t5 = time.time()

# (1) 集水面積 vs 総貯水容量 の Pearson 相関 (両対数)
mask_pos = (df["集水面積_km2"] > 0) & (df["総貯水容量_千m3"] > 0)
log_a = np.log10(df.loc[mask_pos, "集水面積_km2"].astype(float))
log_v = np.log10(df.loc[mask_pos, "総貯水容量_千m3"].astype(float))
r_log = float(np.corrcoef(log_a, log_v)[0, 1])
# 線形回帰 (log-log の傾き) — べき乗則の指数
slope, intercept = np.polyfit(log_a, log_v, 1)
r2 = r_log ** 2
h4_ok = r_log >= 0.7

# (2) 流域支配ランキング (貯水深 = 容量/集水面積)
T_dom = df[["ダム名", "水系名", "集水面積_km2",
            "総貯水容量_千m3", "有効貯水容量_千m3",
            "容量集水比", "貯水深_mm", "容量充填率",
            "完成年代"]].copy()
# 市町は gdf から merge
T_dom = T_dom.merge(
    gdf[["ダム名", "市町_空間"]].drop_duplicates("ダム名"),
    on="ダム名", how="left")
T_dom = T_dom.sort_values("貯水深_mm", ascending=False).reset_index(drop=True)
T_dom = T_dom.round({"貯水深_mm": 0, "容量集水比": 1,
                     "容量充填率": 1, "集水面積_km2": 2,
                     "総貯水容量_千m3": 0, "有効貯水容量_千m3": 0})

# 魚切ダムの貯水深ランク
fish_rank = int(T_dom.index[T_dom["ダム名"] == "魚切ダム"].tolist()[0]) + 1
fish_depth = float(T_dom.loc[T_dom["ダム名"] == "魚切ダム", "貯水深_mm"].iloc[0])
h3_ok = fish_rank <= 3

# (3) 容量充填率の分布
T_filling = df[["ダム名", "総貯水容量_千m3",
                "有効貯水容量_千m3", "容量充填率"]].copy()
T_filling = T_filling.sort_values("容量充填率",
                                  ascending=False).reset_index(drop=True)

# (4) 集水面積 × 容量 ジョイント分布要約
T_watershed = pd.DataFrame([
    {"統計": "集水面積 平均 (km²)", "値": round(df["集水面積_km2"].mean(), 2)},
    {"統計": "集水面積 中央 (km²)", "値": round(df["集水面積_km2"].median(), 2)},
    {"統計": "集水面積 最大 (km²)", "値": round(df["集水面積_km2"].max(), 2)},
    {"統計": "集水面積 最小 (km²)", "値": round(df["集水面積_km2"].min(), 2)},
    {"統計": "総貯水容量 合計 (千 m³)",
     "値": int(df["総貯水容量_千m3"].sum())},
    {"統計": "総貯水容量 平均 (千 m³)",
     "値": int(df["総貯水容量_千m3"].mean())},
    {"統計": "総貯水容量 中央 (千 m³)",
     "値": int(df["総貯水容量_千m3"].median())},
    {"統計": "総貯水容量 最大 (千 m³)",
     "値": int(df["総貯水容量_千m3"].max())},
    {"統計": "総貯水容量 最小 (千 m³)",
     "値": int(df["総貯水容量_千m3"].min())},
    {"統計": "log-log Pearson r (集水 vs 容量)", "値": round(r_log, 3)},
    {"統計": "log-log 傾き (べき乗則指数)", "値": round(slope, 3)},
    {"統計": "決定係数 R²", "値": round(r2, 3)},
])

print(f"  log-log Pearson r = {r_log:.3f}", flush=True)
print(f"  log-log slope = {slope:.3f}", flush=True)
print(f"  魚切ダム 貯水深ランク: {fish_rank}/{n_dams} "
      f"({fish_depth:.0f} mm)", flush=True)
print(f"  H3 (魚切ダム 貯水深 Top3): {h3_ok}", flush=True)
print(f"  H4 (log-log r >= 0.7): {h4_ok}", flush=True)
print(f"  ({time.time()-t5:.2f}s)", flush=True)


# =============================================================================
# 6. RQ3 集計: 老朽化と更新ピーク
# =============================================================================
print("\n[6] RQ3 集計 — 老朽化", flush=True)
t6 = time.time()

# (1) 完成年代別
T_era = (df.groupby("完成年代")
         .agg(基数=("ダム名", "count"),
              総貯水容量合計_千m3=("総貯水容量_千m3", "sum"),
              平均堤高=("堤高_m", "mean"),
              代表ダム=("ダム名", lambda s: "・".join(s.tolist())))
         .round(1).reset_index())
T_era["シェア_%"] = (T_era["基数"] / n_dams * 100).round(1)
T_era["順"] = T_era["完成年代"].apply(
    lambda x: ERA_ORDER.index(x) if x in ERA_ORDER else 99)
T_era = T_era.sort_values("順").drop(columns="順").reset_index(drop=True)

# (2) 老朽ダム抽出 (築 50 年以上)
T_old = df[df["is_old"]][[
    "ダム名", "水系名", "完成年月", "完成西暦", "経過年",
    "堤高_m", "総貯水容量_千m3", "診断結果"
]].copy()
T_old = T_old.sort_values("完成西暦").reset_index(drop=True)
T_old["経過年"] = T_old["経過年"].astype(int)
T_old["完成西暦"] = T_old["完成西暦"].astype(int)
n_old = len(T_old)
h5_ok = n_old >= 3 and all(1955 <= y <= 1974 for y in T_old["完成西暦"])

# (3) 更新ピーク予測 (本記事独自指標)
# 国の長寿命化計画は「築 60 年で大規模更新」 を標準想定
# → 各ダムの「次回更新年」 = 完成西暦 + 60
RENEWAL_LIFE = 60
df["次回更新年"] = df["完成西暦"].astype(float) + RENEWAL_LIFE
T_renewal = (df.groupby(pd.cut(df["次回更新年"],
                                bins=[2010, 2030, 2050, 2070, 2090],
                                labels=["2010s-2020s",
                                        "2030s-2040s",
                                        "2050s-2060s",
                                        "2070s-2080s"]),
                          observed=True)
             .agg(基数=("ダム名", "count"),
                  総貯水容量合計_千m3=("総貯水容量_千m3", "sum"),
                  代表ダム=("ダム名", lambda s: "・".join(s.tolist())))
             .reset_index())
T_renewal = T_renewal.rename(columns={"次回更新年": "更新ピーク帯"})

# (4) 診断結果別
T_diag = (df.groupby("診断結果")
          .agg(基数=("ダム名", "count"),
               平均経過年=("経過年", "mean"),
               該当ダム=("ダム名", lambda s: "・".join(s.tolist())))
          .round(1).reset_index())
T_diag["シェア_%"] = (T_diag["基数"] / n_dams * 100).round(1)
T_diag = T_diag.sort_values("基数", ascending=False).reset_index(drop=True)

print(f"  築 50 年以上: {n_old}/{n_dams} 基", flush=True)
print(f"  H5 (老朽 3+ 基 かつ S30-40 年代): {h5_ok}", flush=True)
print(f"  ({time.time()-t6:.2f}s)", flush=True)


# =============================================================================
# 7. CSV 出力
# =============================================================================
print("\n[7] CSV 出力", flush=True)
t7 = time.time()

OUT_OVERVIEW = ASSETS / "L78_dam_overview.csv"
gdf.drop(columns=["geometry"]).to_csv(OUT_OVERVIEW,
                                      index=False, encoding="utf-8-sig")

T_form.to_csv(ASSETS / "L78_form_summary.csv",
              index=False, encoding="utf-8-sig")
T_size.to_csv(ASSETS / "L78_size_summary.csv",
              index=False, encoding="utf-8-sig")
T_height.to_csv(ASSETS / "L78_height_summary.csv",
                index=False, encoding="utf-8-sig")
T_water.to_csv(ASSETS / "L78_watershed_summary.csv",
               index=False, encoding="utf-8-sig")
T_city.to_csv(ASSETS / "L78_city_summary.csv",
              index=False, encoding="utf-8-sig")
T_dom.to_csv(ASSETS / "L78_dominance_ranking.csv",
             index=False, encoding="utf-8-sig")
T_filling.to_csv(ASSETS / "L78_filling_ratio.csv",
                 index=False, encoding="utf-8-sig")
T_watershed.to_csv(ASSETS / "L78_watershed_stats.csv",
                   index=False, encoding="utf-8-sig")
T_era.to_csv(ASSETS / "L78_era_summary.csv",
             index=False, encoding="utf-8-sig")
T_old.to_csv(ASSETS / "L78_old_dams.csv",
             index=False, encoding="utf-8-sig")
T_renewal.to_csv(ASSETS / "L78_renewal_peak.csv",
                 index=False, encoding="utf-8-sig")
T_diag.to_csv(ASSETS / "L78_diagnosis_summary.csv",
              index=False, encoding="utf-8-sig")

print(f"  ({time.time()-t7:.2f}s)", flush=True)


# =============================================================================
# 8. 図の生成
# =============================================================================
print("\n[8] 図の生成", flush=True)
t8 = time.time()

# 県全域 表示 bbox
XMIN, YMIN = -15000, -220000
XMAX, YMAX = 125000, -90000


def save_fig(name, dpi=120):
    p = ASSETS / name
    plt.savefig(p, dpi=dpi, bbox_inches="tight", facecolor="white")
    plt.close('all')
    return p


# ---- 図 1 (RQ1): 12 ダム 配置マップ — 水系×規模バブル ----
print("  fig1: 配置マップ", flush=True)
fig, ax = plt.subplots(figsize=(13, 8))
admin.plot(ax=ax, color="#fff8e8", edgecolor="#888",
           linewidth=0.4, alpha=0.55)
for ws, sub in gdf.groupby("水系名"):
    sub.plot(ax=ax, color=WS_COLORS.get(ws, "#888"),
             markersize=sub["総貯水容量_千m3"].fillna(500) * 0.025 + 60,
             edgecolor="#222", linewidth=1.0, alpha=0.85,
             label=f"{ws} 水系 ({len(sub)} 基)", zorder=4)
# ダム名 ラベル
for _, r in gdf.iterrows():
    ax.annotate(f"{r['ダム名']}\n[{r['診断結果']}]",
                (r.geometry.x, r.geometry.y),
                xytext=(8, 5), textcoords="offset points",
                fontsize=8.5, color="#333",
                fontweight=("bold" if r["診断結果"] == "B2" else "normal"))
# B2 ダムを赤丸で強調
b2 = gdf[gdf["診断結果"] == "B2"]
if len(b2) > 0:
    b2.plot(ax=ax, facecolor="none", edgecolor="#cf222e",
            linewidth=2.5, markersize=400, zorder=6,
            label="B2 判定 (要対策)")

ax.set_xlim(XMIN, XMAX)
ax.set_ylim(YMIN, YMAX)
ax.set_aspect("equal")
ax.set_title(f"図 1 (RQ1): 県管理ダム {n_dams} 基の地理配置 — "
             f"円サイズ = 総貯水容量、 色 = 水系", fontsize=11)
ax.set_xlabel("X (m, EPSG:6671)")
ax.set_ylabel("Y (m, EPSG:6671)")
ax.legend(loc="lower left", fontsize=9, title="水系", framealpha=0.95)
ax.grid(True, linestyle="--", alpha=0.3)
plt.tight_layout()
save_fig("L78_fig1_dam_map.png")


# ---- 図 2 (RQ1): 型式 + 堤高 + 規模 3 panels ----
print("  fig2: 型式 + 堤高 + 規模", flush=True)
fig, axes = plt.subplots(1, 3, figsize=(17, 5.5))

# 左: 型式別 円グラフ
ax = axes[0]
form_counts = T_form.set_index("型式")["基数"]
ax.pie(form_counts.values,
       labels=[f"{f}\n({c} 基)" for f, c in form_counts.items()],
       colors=["#0969da"],
       autopct="%d%%", startangle=90,
       textprops={"fontsize": 11},
       wedgeprops={"edgecolor": "#222", "linewidth": 1.5})
ax.set_title(f"型式別 基数比率 — 100% 重力式コンクリート",
             fontsize=11)

# 中: 堤高ビン別
ax = axes[1]
xs = np.arange(len(height_labels))
counts_h = [int(T_height.loc[T_height["堤高帯"] == h, "基数"].iloc[0])
            if (T_height["堤高帯"] == h).any() else 0
            for h in height_labels]
bars = ax.bar(xs, counts_h, color="#0969da",
              edgecolor="#222", linewidth=0.6)
for x, v in zip(xs, counts_h):
    ax.text(x, v + 0.15, str(v), ha="center",
            fontsize=12, fontweight="bold")
ax.set_xticks(xs)
ax.set_xticklabels(height_labels, fontsize=10)
ax.set_ylabel("基数")
ax.set_ylim(0, max(counts_h) * 1.25)
ax.set_title(f"堤高帯別 基数 — 30-80m 帯に {n_30_80}/{n_dams} 基集中",
             fontsize=11)
ax.grid(True, axis="y", alpha=0.3)

# 右: 規模クラス
ax = axes[2]
size_xs = np.arange(len(size_order))
counts_s = [int(T_size.loc[T_size["規模クラス"] == s, "基数"].iloc[0])
            if (T_size["規模クラス"] == s).any() else 0
            for s in size_order]
size_cols = ["#cf222e", "#cf6f00", "#1a7f37"]
ax.bar(size_xs, counts_s, color=size_cols,
       edgecolor="#222", linewidth=0.6)
for x, v in zip(size_xs, counts_s):
    if v > 0:
        ax.text(x, v + 0.15, f"{v}\n({100*v/n_dams:.1f}%)",
                ha="center", fontsize=10, fontweight="bold")
ax.set_xticks(size_xs)
ax.set_xticklabels([s.split(" ")[0] for s in size_order], fontsize=10)
ax.set_ylabel("基数")
ax.set_ylim(0, max(counts_s) * 1.35 if max(counts_s) > 0 else 1)
ax.set_title("規模クラス別 基数 (総貯水容量で 3 区分)", fontsize=11)
ax.grid(True, axis="y", alpha=0.3)

fig.suptitle("図 2 (RQ1): 型式 × 堤高 × 規模 — 県管理ダムの構造プロファイル",
             fontsize=12.5, y=1.02)
plt.tight_layout()
save_fig("L78_fig2_structure.png")


# ---- 図 3 (RQ2): 集水面積 vs 総貯水容量 (両対数 散布) ----
print("  fig3: 集水 vs 容量 散布", flush=True)
fig, ax = plt.subplots(figsize=(11, 7.5))
for ws, sub in df.groupby("水系名"):
    ax.scatter(sub["集水面積_km2"], sub["総貯水容量_千m3"],
               s=sub["堤高_m"] * 4 + 60,
               c=WS_COLORS.get(ws, "#888"),
               edgecolor="#222", linewidth=0.8,
               alpha=0.85, label=f"{ws} 水系")
for _, r in df.iterrows():
    ax.annotate(r["ダム名"],
                (r["集水面積_km2"], r["総貯水容量_千m3"]),
                xytext=(7, 5), textcoords="offset points",
                fontsize=9, color="#333")

# 回帰線 (両対数)
log_a_arr = np.linspace(log_a.min() - 0.1, log_a.max() + 0.1, 50)
log_v_pred = slope * log_a_arr + intercept
ax.plot(10 ** log_a_arr, 10 ** log_v_pred,
        color="#cf222e", linestyle="--", linewidth=1.5,
        label=f"回帰: log V = {slope:.2f}·log A + {intercept:.2f}\n"
              f"(r = {r_log:.3f}, R² = {r2:.3f})")
ax.set_xscale("log")
ax.set_yscale("log")
ax.set_xlabel("集水面積 (km²)", fontsize=11)
ax.set_ylabel("総貯水容量 (千 m³)", fontsize=11)
ax.set_title(f"図 3 (RQ2): 集水面積 × 総貯水容量 (両対数) — "
             f"r = {r_log:.3f}, べき乗指数 = {slope:.2f}",
             fontsize=11)
ax.legend(loc="lower right", fontsize=9, framealpha=0.95)
ax.grid(True, which="both", linestyle="--", alpha=0.3)
plt.tight_layout()
save_fig("L78_fig3_watershed_capacity.png")


# ---- 図 4 (RQ2): 流域支配ランキング (貯水深 + 容量集水比) ----
print("  fig4: 流域支配ランキング", flush=True)
fig, axes = plt.subplots(1, 2, figsize=(15, 6.5))

# 左: 貯水深 ランキング
ax = axes[0]
T_dom_sorted = T_dom.sort_values("貯水深_mm", ascending=True).reset_index(drop=True)
ys = np.arange(len(T_dom_sorted))
labels_y = T_dom_sorted["ダム名"].tolist()
vals = T_dom_sorted["貯水深_mm"].astype(float).tolist()
cols = [WS_COLORS.get(w, "#888") for w in T_dom_sorted["水系名"].tolist()]
ax.barh(ys, vals, color=cols, edgecolor="#222", linewidth=0.5)
for y, v in zip(ys, vals):
    ax.text(v + max(vals) * 0.02, y, f"{int(v):,} mm",
            va="center", fontsize=9, fontweight="bold")
# 魚切ダム 強調
for y, name in zip(ys, labels_y):
    if name == "魚切ダム":
        ax.barh(y, vals[y], facecolor="none",
                edgecolor="#cf222e", linewidth=2.5)
ax.set_yticks(ys)
ax.set_yticklabels(labels_y, fontsize=9.5)
ax.set_xlabel("貯水深 (mm)")
ax.set_title(f"貯水深 ランキング (= 総貯水容量 ÷ 集水面積)\n"
             f"魚切ダム = {fish_rank} 位 ({fish_depth:.0f} mm)",
             fontsize=10.5)
ax.grid(True, axis="x", alpha=0.3)

# 右: 容量充填率 (有効/総) ランキング
ax = axes[1]
T_fill_sorted = T_filling.sort_values("容量充填率",
                                      ascending=True).reset_index(drop=True)
ys = np.arange(len(T_fill_sorted))
labels_y = T_fill_sorted["ダム名"].tolist()
vals_f = T_fill_sorted["容量充填率"].astype(float).tolist()
ax.barh(ys, vals_f, color="#1a7f37",
        edgecolor="#222", linewidth=0.5)
for y, v in zip(ys, vals_f):
    ax.text(v + 0.5, y, f"{v}%",
            va="center", fontsize=9, fontweight="bold")
ax.set_yticks(ys)
ax.set_yticklabels(labels_y, fontsize=9.5)
ax.set_xlabel("容量充填率 (= 有効貯水 / 総貯水, %)")
ax.set_xlim(70, 100)
ax.set_title(f"容量充填率 ランキング — "
             f"平均 {df['容量充填率'].mean():.1f}%",
             fontsize=10.5)
ax.grid(True, axis="x", alpha=0.3)

fig.suptitle("図 4 (RQ2): 流域支配指標ランキング (本記事独自指標)",
             fontsize=12.5, y=1.02)
plt.tight_layout()
save_fig("L78_fig4_dominance.png")


# ---- 図 5 (RQ3): 完成年代分布 timeline ----
print("  fig5: 完成年代 timeline", flush=True)
fig, axes = plt.subplots(1, 2, figsize=(15, 6))

# 左: 完成年代別 基数
ax = axes[0]
era_xs = np.arange(len(ERA_ORDER))
counts_e = [int(T_era.loc[T_era["完成年代"] == e, "基数"].iloc[0])
            if (T_era["完成年代"] == e).any() else 0
            for e in ERA_ORDER]
era_cols = [ERA_COLORS[e] for e in ERA_ORDER]
ax.bar(era_xs, counts_e, color=era_cols,
       edgecolor="#222", linewidth=0.6)
for x, v in zip(era_xs, counts_e):
    if v > 0:
        ax.text(x, v + 0.1, f"{v} 基",
                ha="center", fontsize=10, fontweight="bold")
ax.set_xticks(era_xs)
ax.set_xticklabels(ERA_ORDER, fontsize=9.5, rotation=20)
ax.set_ylabel("基数")
ax.set_ylim(0, max(counts_e) * 1.3 if max(counts_e) > 0 else 1)
ax.set_title("完成年代別 基数分布", fontsize=11)
ax.grid(True, axis="y", alpha=0.3)

# 右: 完成西暦 timeline (個体散布)
ax = axes[1]
df_sorted = df.sort_values("完成西暦").reset_index(drop=True)
xs = df_sorted["完成西暦"].astype(float).tolist()
ys_v = df_sorted["総貯水容量_千m3"].astype(float).tolist()
sizes = (df_sorted["堤高_m"].astype(float) * 4 + 50).tolist()
cols_pt = [ERA_COLORS.get(e, "#888")
           for e in df_sorted["完成年代"].tolist()]
ax.scatter(xs, ys_v, s=sizes, c=cols_pt,
           edgecolor="#222", linewidth=0.8, alpha=0.85)
for x, y, name, age in zip(xs, ys_v,
                            df_sorted["ダム名"].tolist(),
                            df_sorted["経過年"].astype(int).tolist()):
    ax.annotate(f"{name}\n({int(x)}, 築 {age} 年)",
                (x, y), xytext=(6, 5),
                textcoords="offset points",
                fontsize=8.5, color="#333")

# 老朽閾値線 (1974 = 築 50 年)
ax.axvline(NOW_YEAR - OLD_THRESHOLD_YEARS,
           color="#cf222e", linestyle="--", linewidth=1.5,
           label=f"築 {OLD_THRESHOLD_YEARS} 年閾値 ({NOW_YEAR-OLD_THRESHOLD_YEARS})")
ax.set_xlabel("完成西暦")
ax.set_ylabel("総貯水容量 (千 m³)")
ax.set_yscale("log")
ax.set_title(f"完成 timeline + 老朽閾値 — 築 50 年以上 = {n_old} 基",
             fontsize=11)
ax.legend(loc="upper right", fontsize=10)
ax.grid(True, which="both", linestyle="--", alpha=0.3)

fig.suptitle("図 5 (RQ3): 完成年代分布と老朽閾値",
             fontsize=12.5, y=1.02)
plt.tight_layout()
save_fig("L78_fig5_era_timeline.png")


# ---- 図 6 (RQ3): 老朽ダム強調マップ ----
print("  fig6: 老朽ダム強調マップ", flush=True)
fig, ax = plt.subplots(figsize=(13, 8))
admin.plot(ax=ax, color="#fff8e8", edgecolor="#888",
           linewidth=0.4, alpha=0.55)
# 経過年で色付け
for _, r in gdf.iterrows():
    age = float(r["経過年"]) if pd.notna(r["経過年"]) else 0
    color = "#cf222e" if r["is_old"] else "#0969da"
    size = 200 + age * 4
    ax.scatter(r.geometry.x, r.geometry.y,
               s=size, c=color, edgecolor="#222", linewidth=1.0,
               alpha=0.85, zorder=4)
    ax.annotate(f"{r['ダム名']}\n築 {int(age)} 年 ({int(r['完成西暦'])})",
                (r.geometry.x, r.geometry.y),
                xytext=(8, 5), textcoords="offset points",
                fontsize=8.5,
                fontweight=("bold" if r["is_old"] else "normal"),
                color=("#cf222e" if r["is_old"] else "#333"))

# 凡例
from matplotlib.lines import Line2D
legend_handles = [
    Line2D([0], [0], marker='o', color='w',
           markerfacecolor='#cf222e', markeredgecolor='#222',
           markersize=14, label=f'築 {OLD_THRESHOLD_YEARS} 年以上 ({n_old} 基)'),
    Line2D([0], [0], marker='o', color='w',
           markerfacecolor='#0969da', markeredgecolor='#222',
           markersize=14, label=f'築 {OLD_THRESHOLD_YEARS} 年未満 ({n_dams - n_old} 基)'),
]
ax.legend(handles=legend_handles, loc="lower left",
          fontsize=10, framealpha=0.95)

ax.set_xlim(XMIN, XMAX)
ax.set_ylim(YMIN, YMAX)
ax.set_aspect("equal")
ax.set_title(f"図 6 (RQ3): 老朽化マップ — 円サイズ = 経過年、 "
             f"赤 = 築 50 年以上, 青 = 築 50 年未満",
             fontsize=11)
ax.set_xlabel("X (m, EPSG:6671)")
ax.set_ylabel("Y (m, EPSG:6671)")
ax.grid(True, linestyle="--", alpha=0.3)
plt.tight_layout()
save_fig("L78_fig6_aging_map.png")


# ---- 図 7 (RQ3): 更新ピーク予測 + 診断 ----
print("  fig7: 更新ピーク + 診断", flush=True)
fig, axes = plt.subplots(1, 2, figsize=(15, 6))

# 左: 更新ピーク帯別 基数 + 容量
ax = axes[0]
peak_labels = T_renewal["更新ピーク帯"].astype(str).tolist()
peak_xs = np.arange(len(peak_labels))
peak_counts = T_renewal["基数"].astype(int).tolist()
peak_caps = (T_renewal["総貯水容量合計_千m3"].astype(float) / 1000).tolist()  # M m³
peak_colors = ["#cf222e", "#cf6f00", "#1a7f37", "#0969da"][:len(peak_labels)]
ax.bar(peak_xs, peak_counts, color=peak_colors,
       edgecolor="#222", linewidth=0.6)
for x, v, cap in zip(peak_xs, peak_counts, peak_caps):
    if v > 0:
        ax.text(x, v + 0.1,
                f"{v} 基\n({cap:.1f} M m³)",
                ha="center", fontsize=9.5, fontweight="bold")
ax.set_xticks(peak_xs)
ax.set_xticklabels(peak_labels, fontsize=9.5, rotation=15)
ax.set_ylabel("基数")
ax.set_ylim(0, max(peak_counts) * 1.4 if max(peak_counts) > 0 else 1)
ax.set_title(f"更新ピーク帯別 基数 (= 完成西暦 + {RENEWAL_LIFE} 年)",
             fontsize=11)
ax.grid(True, axis="y", alpha=0.3)

# 右: 診断結果別 円
ax = axes[1]
diag_counts = T_diag.set_index("診断結果")["基数"]
diag_colors = ["#1a7f37" if d == "C" else "#cf222e"
               for d in diag_counts.index]
ax.pie(diag_counts.values,
       labels=[f"{d}\n({c} 基)" for d, c in diag_counts.items()],
       colors=diag_colors, autopct="%d%%", startangle=90,
       textprops={"fontsize": 11},
       wedgeprops={"edgecolor": "#222", "linewidth": 1.2})
ax.set_title("診断結果別 基数比率\n(C = 健全, B2 = 要対策)",
             fontsize=11)

fig.suptitle("図 7 (RQ3): 更新ピーク予測 × 診断結果",
             fontsize=12.5, y=1.02)
plt.tight_layout()
save_fig("L78_fig7_renewal_diagnosis.png")


print(f"  図 7 枚生成 ({time.time()-t8:.2f}s)", flush=True)


# =============================================================================
# 9. 仮説検証
# =============================================================================
print("\n[9] 仮説検証", flush=True)

T_hyp = pd.DataFrame([
    {"仮説": "H1 (RQ1, 型式単一)",
     "閾値": "型式 100% が重力式コンクリート",
     "実測": f"{n_gravity}/{n_dams} 基 ({share_gravity}%)",
     "判定": "支持" if h1_ok else "不支持"},
    {"仮説": "H2 (RQ1, 堤高集中)",
     "閾値": "30-80m 帯に 10 基以上",
     "実測": f"{n_30_80}/{n_dams} 基 ({100*n_30_80/n_dams:.1f}%)",
     "判定": "支持" if h2_ok else "不支持"},
    {"仮説": "H3 (RQ2, 魚切ダム 貯水深 Top 3)",
     "閾値": "ランク <= 3",
     "実測": f"{fish_rank} 位 ({fish_depth:.0f} mm)",
     "判定": "支持" if h3_ok else "不支持"},
    {"仮説": "H4 (RQ2, log-log 強相関)",
     "閾値": "Pearson r >= 0.7",
     "実測": f"r = {r_log:.3f} (R² = {r2:.3f}, 傾き {slope:.2f})",
     "判定": "支持" if h4_ok else "不支持"},
    {"仮説": "H5 (RQ3, 老朽集中)",
     "閾値": "築 50 年以上 3 基以上 かつ 1955-74 集中",
     "実測": (f"{n_old} 基 (完成西暦: "
              f"{', '.join(map(str, T_old['完成西暦'].tolist()))})"
              if n_old > 0 else "0 基"),
     "判定": "支持" if h5_ok else "不支持"},
])
T_hyp.to_csv(ASSETS / "L78_hypothesis_check.csv",
             index=False, encoding="utf-8-sig")
print(T_hyp.to_string(index=False), flush=True)


# =============================================================================
# 10. HTML 生成
# =============================================================================
print("\n[10] HTML 生成", flush=True)
t10 = time.time()


def df_to_html(df_, max_rows=None):
    if max_rows is not None:
        df_ = df_.head(max_rows)
    return df_.to_html(index=False, classes="datatable",
                       border=0, escape=False, na_rep="—")


# データセット仕様表
T_dataset = pd.DataFrame([
    {"項目": "データセット ID", "値": f"DoBoX #{DATASET_ID}"},
    {"項目": "データセット名", "値": "ダム基本情報・維持管理情報"},
    {"項目": "公開組織", "値": "広島県 河川課ダムグループ"},
    {"項目": "リソース数", "値": "1 (CSV)"},
    {"項目": "ライセンス", "値": "クリエイティブ・コモンズ表示 (CC-BY)"},
    {"項目": "対象", "値": "広島県管理 治水ダム (河川ダム)"},
    {"項目": "対象基数", "値": f"{n_dams} 基"},
    {"項目": "対象水系", "値": f"{T_water['水系名'].nunique()} 水系"},
    {"項目": "完成年範囲", "値": (f"{int(df['完成西暦'].min())} 〜 "
                                    f"{int(df['完成西暦'].max())} ("
                                    f"{int(df['完成西暦'].max() - df['完成西暦'].min())} 年スパン)")},
    {"項目": "型式", "値": "重力式コンクリート (全件単一)"},
    {"項目": "ファイル形式", "値": "CSV (UTF-8 BOM, 12 行 × 21 列)"},
])

# ----- セクション 1: 学習目標と問い -----
sec1 = f"""
<div class="note">
  <b>本記事は単独データセット系 Format B</b>: 1 つのデータセット
  「ダム基本情報・維持管理情報」 を 3 つの独立した研究角度
  (RQ1 / RQ2 / RQ3) で並列に分析する。 RQ1 でダムの構造、
  RQ2 で流域支配、 RQ3 で老朽化と更新ピークを評価する 3 段階アプローチ。
</div>

<div class="warn">
  <b>本記事は S39 の上位互換</b>: S39 (3 図 + 4 集計の台帳俯瞰) の
  深掘り版として、 7 図 + 11 表 + 5 仮説検証で
  「構造 / 流域支配 / 老朽化」 の 3 RQ を並列に解く。 S39 では扱わなかった
  <b>貯水深 (容量 / 集水面積) ランキング</b>、 <b>log-log べき乗則</b>、
  <b>更新ピーク予測</b> 等を新規導入。
</div>

<h3>独自用語の定義 (本記事限定)</h3>
<ul>
  <li><b>ダム (河川法上の定義)</b>: 河川法第 26 条が規定する<b>河川区域内
      工作物</b>のうち、 河川を堰き止めて貯水する大規模構造物。 高さ 15 m 以上
      が「ダム」 と分類され、 それ未満は「堰」 と区別される (河川管理施設等
      構造令第 2 条)。 治水 (洪水調節) ・利水 (飲料水 / 農業 / 工業) ・発電の
      3 大目的を持つ。</li>
  <li><b>治水ダム (本記事の主対象)</b>: 主目的が<b>洪水調節</b>であるダム。
      梅雨・台風時に洪水を一時的に貯留して下流の被災を軽減する。 広島県管理
      ダム {n_dams} 基はすべて治水ダム (利水兼用が大半)。</li>
  <li><b>重力式コンクリートダム</b>: 堤体の自重 (= 重力) で水圧に抵抗する
      コンクリート製ダム。 安定性が高く、 地盤適合性が広く、 工事も標準的なため、
      日本の治水ダムで最も多い型式。 広島県管理ダムは<b>{share_gravity}%
      がこの型式</b>。</li>
  <li><b>アーチ式 (本記事では非該当)</b>: 堤体を曲線に造形して水圧を両岸の
      岩盤に逃がす型式。 黒部ダム (富山) が代表。 県管理ダム {n_dams} 基は
      <b>0 件</b>。</li>
  <li><b>フィル / ロックフィル (本記事では非該当)</b>: 土・砂・岩を
      盛り立てて造る土構造ダム。 アースフィル (= 土が主) と
      ロックフィル (= 岩石が主) の 2 種。 県管理ダムは<b>0 件</b>。</li>
  <li><b>堤高 (m)</b>: ダムの基礎地盤面から堤頂までの高さ。 構造令上
      <b>15 m 以上</b>がダム (それ未満は堰)。 県管理ダムは
      {df['堤高_m'].min():.1f} 〜 {df['堤高_m'].max():.1f} m。</li>
  <li><b>堤頂長 (m)</b>: 堤頂部の水平長 (= 川を横断する方向の長さ)。
      県管理ダムは {df['堤頂長_m'].min():.1f} 〜 {df['堤頂長_m'].max():.1f} m。</li>
  <li><b>総貯水容量 (千 m³)</b>: 設計上の最大貯水量。 洪水調節容量 +
      利水容量 + 堆砂容量 の合計。 県管理ダム合計は
      <b>{df['総貯水容量_千m3'].sum():,.0f} 千 m³ ≒ {df['総貯水容量_千m3'].sum()/1000:.1f} 百万 m³</b>。</li>
  <li><b>有効貯水容量 (千 m³)</b>: 総貯水容量から堆砂容量を除いた、
      実運用上利用可能な貯水容量。 「容量充填率 = 有効/総」 が高いほど
      設計の有効性が高い (本記事独自指標)。</li>
  <li><b>集水面積 (km²)</b>: ダム上流側でそのダムに流れ込む雨水の集水面の
      合計面積。 「ダムが支配する流域の広さ」。 県管理ダムは
      {df['集水面積_km2'].min():.2f} 〜 {df['集水面積_km2'].max():.2f} km²
      まで {df['集水面積_km2'].max() / df['集水面積_km2'].min():.0f} 倍の幅がある。</li>
  <li><b>貯水深 (mm) ※本記事独自指標</b>:
      <code>総貯水容量 (m³) ÷ 集水面積 (m²)</code> を mm 単位に変換した量。
      「集水面積全体に均等に何 mm 分の雨を貯められるか」 という直感量。
      数値が大きいほど<b>単位流域あたりの貯水力が高い (= 過剰設計または
      治水重視)</b>。</li>
  <li><b>容量充填率 (%) ※本記事独自指標</b>:
      <code>有効貯水容量 / 総貯水容量 × 100</code>。 堆砂が少なく実運用容量が
      残っているほど高い。 平均 <b>{df['容量充填率'].mean():.1f}%</b>。</li>
  <li><b>容量集水比 (千 m³ / km²) ※本記事独自指標</b>:
      <code>総貯水容量 (千 m³) ÷ 集水面積 (km²)</code>。 流域 1 km² あたりの
      貯水可能量。 貯水深と本質同等だが単位が異なる。</li>
  <li><b>診断結果 (公式分類)</b>: 県の維持管理情報で公表される<b>健全度判定</b>。
      C = 健全、 B2 = 要対策 (修繕・更新が必要)。 県管理ダム {n_dams} 基中
      <b>魚切ダム 1 基のみ B2</b>、 残り 11 基は C。</li>
  <li><b>緊急放流 (背景説明)</b>: ダム上流で計画想定を超える流入があった際、
      ダム本体の決壊を防ぐため、 流入量と同量の水を下流に放流する操作
      (= 異常洪水時防災操作)。 本データには操作実績は含まれないが、
      下流被災ポテンシャルを論じる上で重要概念。</li>
  <li><b>老朽 (本記事独自閾値)</b>: 完成から<b>築 {OLD_THRESHOLD_YEARS} 年
      以上経過</b>したダム。 国の<b>インフラ長寿命化基本計画 (2014)</b>が
      想定する<b>大規模更新の標準周期 = 60 年</b>から、 50 年経過時点で
      「更新検討開始」 が望ましいとされる。</li>
  <li><b>更新ピーク (本記事独自指標)</b>: 完成西暦 +
      <b>{RENEWAL_LIFE} 年</b>を「次回更新予測年」 として、 各ダムが
      属する 20 年帯を集計したピーク。 県の維持管理予算ピーク予測の代理。</li>
</ul>

<h3>研究の問い (3 RQ)</h3>
<ol>
  <li><b>RQ1 (主研究):</b> 広島県の<b>ダムの構造 — 型式・規模・地理分布</b>は
      どう描けるか? {n_dams} 基を<b>型式 / 堤高 / 堤頂長 / 堤体積 / 総貯水容量
      / 集水面積 / 水系 / 完成年代 / 診断結果</b>の 9 軸で集計し、
      「県の最大水管理装置の物理形状」 を初めて系統的に記述する。
      H1 (型式 100% 重力式) / H2 (堤高 30-80m に 10+ 基) を検証。</li>
  <li><b>RQ2 (副研究 1):</b> ダムの<b>流域支配 — 上流集水と下流被災ポテンシャル</b>は
      どう描けるか? 集水面積 (km²) ・総貯水容量 (千 m³) ・有効貯水容量比から、
      各ダムの「流域支配力」 を量化、 <b>本記事独自指標「貯水深 (容量 ÷ 集水
      面積)」 「容量充填率 (有効 ÷ 総)」</b> を導入し、 12 基をランキングする。
      H3 (魚切ダム 貯水深 Top 3) / H4 (log-log r &gt;= 0.7) を検証。</li>
  <li><b>RQ3 (副研究 2):</b> ダムの<b>老朽化と更新ピーク — 1960-70s ダムの
      再評価</b>はどう描けるか? 完成年月から経過年を計算し、 築 50 年以上の
      老朽ダムを抽出。 国の<b>インフラ長寿命化基本計画 (2014)</b>が想定する
      更新ピーク到来の実態を、 {n_dams} 基という小さな母集団でも個体名で
      語れる粒度で描く。 H5 (老朽 3+ 基 かつ S30-40 年代集中) を検証。</li>
</ol>

<h3>仮説 (5)</h3>
<ul>
  <li><b>H1</b> (RQ1, 型式単一): 県管理ダムは<b>型式 100% が重力式コンクリート</b>。
      他型式 0 件。</li>
  <li><b>H2</b> (RQ1, 堤高集中): 堤高は<b>30-80m 帯</b>に <b>10 基以上</b>。
      100m 超の超高ダムも 30m 未満の小型ダムも無い。</li>
  <li><b>H3</b> (RQ2, 魚切ダム 貯水深 Top 3): 貯水深 (= 容量 ÷ 集水面積) で
      <b>魚切ダムが県内 Top 3</b>。 大都市直下流の過剰設計の物理証拠仮説。</li>
  <li><b>H4</b> (RQ2, 集水 ↔ 容量 強相関): 集水面積と総貯水容量の両対数
      Pearson r &gt;= <b>0.7</b>。 大流域ほど大容量という直観の量的検証。</li>
  <li><b>H5</b> (RQ3, 老朽集中): 築 50 年以上が<b>3 基以上</b>、 それらは
      <b>S30-40 年代 (1955-74)</b>の高度成長期初期に集中。</li>
</ul>

<h3>到達点</h3>
<p>本記事を読み終えると、 (1) 県管理ダム {n_dams} 基の<b>構造プロファイル</b>を
型式・規模・地理の 3 軸で完全把握、 (2) 集水面積×総貯水容量の<b>べき乗則
(指数 {slope:.2f})</b>を初めて量的記述、 (3) 各ダムを<b>貯水深ランキング</b>で
12 個体について比較可能、 (4) 築 50 年以上の<b>{n_old} 基の老朽ダム</b>を
個体名で同定、 (5) <b>更新ピーク帯</b>を 20 年単位で予測、 という 5 段階の
知識が獲得できる。 これにより県の<b>水資源管理戦略</b>を構造・流域・時間軸
の 3 視点で研究者として論じられるようになる。</p>
"""


# ----- セクション 2: 使用データ -----
sec2 = f"""
<p>本研究で使う <b>1 つの dataset (1 リソース)</b> を以下の表に示す。
本データセットは 12 行の<b>軽量フラット CSV</b> として公開されており、
学習者が手元で完全に扱える。</p>

<h3>データセット仕様</h3>
{df_to_html(T_dataset)}

<h3>CSV 列定義 (21 列)</h3>
<table>
<tr><th>列名</th><th>例</th><th>意味</th></tr>
<tr><td>ダムコード</td><td>1, 2, 91 等</td>
    <td>県内ダム識別子 (連番に近いが穴あき: 8 → 91 にジャンプ)</td></tr>
<tr><td>分類</td><td>河川</td>
    <td>河川法上の分類 (全件 河川ダム)</td></tr>
<tr><td>水系名</td><td>小瀬川 / 八幡川 / 芦田川 等</td>
    <td>ダムが属する一級または二級水系の名称 ({T_water['水系名'].nunique()} 水系)</td></tr>
<tr><td>河川名</td><td>小瀬川 / 椋梨川 等</td>
    <td>水系内の本流または支流の名称</td></tr>
<tr><td>施設区分</td><td>ダム</td>
    <td>施設の区分 (全件 ダム)</td></tr>
<tr><td>ダム名</td><td>小瀬川ダム / 魚切ダム 等</td>
    <td>ダムの正式名称</td></tr>
<tr><td>管理者</td><td>広島県</td>
    <td>ダムの管理組織 (全件 広島県)</td></tr>
<tr><td>位置</td><td>廿日市市浅原 / 東広島市河内町 等</td>
    <td>所在地 (市町 + 大字レベル)</td></tr>
<tr><td>緯度 / 経度</td><td>34.308 / 132.124 等</td>
    <td>WGS84 10 進数</td></tr>
<tr><td>完成年月</td><td>S39.6 / H21.10 等</td>
    <td>元号 + 年.月 形式 (S = 昭和、 H = 平成)</td></tr>
<tr><td>集水面積_km2</td><td>3.5 〜 160</td>
    <td>ダム上流の集水面の合計面積</td></tr>
<tr><td>堤体積_千m3</td><td>25,800 〜 317,000</td>
    <td>堤体 (堰堤) のコンクリート体積</td></tr>
<tr><td>総貯水容量_千m3</td><td>560 〜 11,400</td>
    <td>設計上の最大貯水量</td></tr>
<tr><td>有効貯水容量_千m3</td><td>494 〜 9,900</td>
    <td>実運用上利用可能な貯水量 (堆砂容量を除く)</td></tr>
<tr><td>型式</td><td>重力式コンクリート</td>
    <td>堤体の構造分類 (全件 重力式コンクリート)</td></tr>
<tr><td>堤高_m</td><td>{df['堤高_m'].min():.1f} 〜 {df['堤高_m'].max():.1f}</td>
    <td>基礎地盤から堤頂までの高さ</td></tr>
<tr><td>堤頂長_m</td><td>{df['堤頂長_m'].min():.1f} 〜 {df['堤頂長_m'].max():.1f}</td>
    <td>堤頂部の水平長</td></tr>
<tr><td>診断結果</td><td>C / B2</td>
    <td>健全度判定 (C = 健全, B2 = 要対策)</td></tr>
</table>

<h3>形式特性の注意点</h3>
<ul>
  <li><b>1 行 = 1 ダム</b>: 12 行のフラット表で完結。 軽量。</li>
  <li><b>緯度経度は堤体の位置</b>: 貯水池やダム湖の中心ではなく、 堤体
      (= 堰堤本体) の位置点。 本記事の「市町判定」 は堤体所在市町を表す。</li>
  <li><b>ダムコードに穴あき</b>: 1〜8 の連番のあと 91, 92, 93, 94 と
      ジャンプ。 9〜90 のコードは廃止または欠番。</li>
  <li><b>完成年月の元号表記</b>: S = 昭和、 H = 平成。 R (令和) は本データ
      には含まれない (= 令和元年以降に完成したダムは無い、 または未収録)。</li>
  <li><b>型式が単一</b>: 12 基すべて<b>重力式コンクリート</b>。 これは
      仮説 H1 の量的根拠だが、 同時に「他県との比較研究」 の重要な
      出発点でもある (発展課題 5 参照)。</li>
  <li><b>診断結果は 2 値</b>: C (健全) / B2 (要対策) のみ。 中間段階
      (B1, B0 等) や A (新規) は本データに含まれない。</li>
  <li><b>管理者は全件 広島県</b>: 国土交通省管理の<b>弥栄ダム</b> (一級水系
      小瀬川の本ダム) や、 中国電力の<b>発電ダム</b>群は含まれない =
      本記事は<b>県管理治水ダム</b>の研究。</li>
</ul>
"""


# ----- セクション 3: ダウンロード -----
sec3 = f"""
<p>本記事の再現に必要な<b>すべて</b>を直リンクで提供する。
HTML だけ読めば学習者が完全再現できることが目標 (要件 A)。</p>

<h3>生データ (DoBoX 1 dataset, 1 リソース)</h3>
<ul class="kv">
  <li><b>dataset {DATASET_ID}</b>:
      <a href="https://hiroshima-dobox.jp/datasets/{DATASET_ID}" target="_blank">
      ダム基本情報・維持管理情報</a></li>
  <li><a href="../data/extras/dam_basic.csv" download>data/extras/dam_basic.csv</a>
      — 12 行 × 21 列 (UTF-8 BOM, ~3 KB)</li>
</ul>

<h3>このスクリプト本体</h3>
<ul class="kv">
  <li><a href="L78_dams.py" download>L78_dams.py</a>
      — 1 ファイルで完結 (7 図 + 11+ 表生成)</li>
</ul>

<h3>中間 CSV (本記事生成、 再利用可)</h3>
<ul class="kv">
  <li><a href="assets/L78_dam_overview.csv" download>L78_dam_overview.csv</a>
      — 全 {n_dams} 基 (基本属性 + 派生 11 列)</li>
  <li><a href="assets/L78_form_summary.csv" download>L78_form_summary.csv</a>
      — 型式別 集計 (RQ1)</li>
  <li><a href="assets/L78_size_summary.csv" download>L78_size_summary.csv</a>
      — 規模クラス別 集計 (RQ1)</li>
  <li><a href="assets/L78_height_summary.csv" download>L78_height_summary.csv</a>
      — 堤高帯別 集計 (RQ1)</li>
  <li><a href="assets/L78_watershed_summary.csv" download>L78_watershed_summary.csv</a>
      — 水系別 集計 (RQ1)</li>
  <li><a href="assets/L78_city_summary.csv" download>L78_city_summary.csv</a>
      — 市町別 集計 (RQ1, 空間 sjoin 経由)</li>
  <li><a href="assets/L78_dominance_ranking.csv" download>L78_dominance_ranking.csv</a>
      — 流域支配ランキング (貯水深 + 容量集水比, RQ2)</li>
  <li><a href="assets/L78_filling_ratio.csv" download>L78_filling_ratio.csv</a>
      — 容量充填率 ランキング (RQ2)</li>
  <li><a href="assets/L78_watershed_stats.csv" download>L78_watershed_stats.csv</a>
      — 集水 × 容量 統計サマリ (RQ2)</li>
  <li><a href="assets/L78_era_summary.csv" download>L78_era_summary.csv</a>
      — 完成年代別 集計 (RQ3)</li>
  <li><a href="assets/L78_old_dams.csv" download>L78_old_dams.csv</a>
      — 築 50 年以上 老朽ダム一覧 (RQ3)</li>
  <li><a href="assets/L78_renewal_peak.csv" download>L78_renewal_peak.csv</a>
      — 更新ピーク帯予測 (RQ3)</li>
  <li><a href="assets/L78_diagnosis_summary.csv" download>L78_diagnosis_summary.csv</a>
      — 診断結果別 集計 (RQ3)</li>
  <li><a href="assets/L78_hypothesis_check.csv" download>L78_hypothesis_check.csv</a>
      — H1〜H5 仮説検証結果</li>
</ul>

<h3>図 (PNG, 直 DL 可)</h3>
<ul class="kv">
  <li><a href="assets/L78_fig1_dam_map.png" download>fig1 ダム配置マップ (RQ1)</a></li>
  <li><a href="assets/L78_fig2_structure.png" download>fig2 型式 × 堤高 × 規模 (RQ1)</a></li>
  <li><a href="assets/L78_fig3_watershed_capacity.png" download>fig3 集水 × 容量 散布 (RQ2)</a></li>
  <li><a href="assets/L78_fig4_dominance.png" download>fig4 流域支配ランキング (RQ2)</a></li>
  <li><a href="assets/L78_fig5_era_timeline.png" download>fig5 完成年代 timeline (RQ3)</a></li>
  <li><a href="assets/L78_fig6_aging_map.png" download>fig6 老朽ダムマップ (RQ3)</a></li>
  <li><a href="assets/L78_fig7_renewal_diagnosis.png" download>fig7 更新ピーク + 診断 (RQ3)</a></li>
</ul>
"""


# ----- セクション 4: RQ1 -----
sec4_code = '''
import pandas as pd
import numpy as np
import geopandas as gpd
from shapely.geometry import Point
import re

# (1) CSV 読込み (UTF-8 BOM, 12 行 × 21 列)
df = pd.read_csv("data/extras/dam_basic.csv", encoding="utf-8-sig")
df = df.dropna(subset=["ダム名"]).reset_index(drop=True)

# (2) 数値列の型変換
for c in ["緯度", "経度", "集水面積_km2", "堤体積_千m3",
          "総貯水容量_千m3", "有効貯水容量_千m3", "堤高_m", "堤頂長_m"]:
    df[c] = pd.to_numeric(df[c], errors="coerce")

# (3) 元号 → 西暦変換 (S39.6 → 1964, H21.10 → 2009)
ERA_RE = re.compile(r"^([SH])(\\d+)\\.(\\d+)$")
def parse_era(s):
    m = ERA_RE.match(str(s).strip())
    if not m: return None, None
    g, yy, mm = m.groups()
    yy = int(yy)
    year = (1925 + yy) if g == "S" else (1988 + yy)
    return year, f"{(year//10)*10}年代"
df["完成西暦"] = [parse_era(s)[0] for s in df["完成年月"]]
df["完成年代"] = [parse_era(s)[1] for s in df["完成年月"]]

# (4) GeoDataFrame 化 → 平面直角第 III 系
geom = [Point(lon, lat) for lon, lat in zip(df["経度"], df["緯度"])]
gdf = gpd.GeoDataFrame(df, geometry=geom,
                        crs="EPSG:4326").to_crs("EPSG:6671")

# (5) 型式別 集計
T_form = (df.groupby("型式")
          .agg(基数=("ダム名", "count"),
               堤高平均=("堤高_m", "mean"))
          .round(1).reset_index())
print(T_form)

# (6) 堤高ビン別
df["堤高帯"] = pd.cut(df["堤高_m"], bins=[0,30,50,80,100],
                       labels=["~30m","30-50m","50-80m","80m+"])
print(df.groupby("堤高帯", observed=False).size())

# (7) 規模クラス
def size_class(v):
    if pd.isna(v): return "(不明)"
    if v >= 8000: return "大 (>=8000 千 m³)"
    if v >= 1500: return "中 (1500-8000 千 m³)"
    return "小 (<1500 千 m³)"
df["規模クラス"] = df["総貯水容量_千m3"].map(size_class)
print(df.groupby("規模クラス").size())
'''

sec4 = f"""
<h3>狙い (RQ1)</h3>
<p>RQ1 では<b>「県の最大水管理装置の物理形状」</b>を初めて系統的に記述する。
具体的には {n_dams} 基を<b>型式 × 堤高 × 堤頂長 × 堤体積 × 総貯水容量 ×
集水面積 × 水系 × 完成年代 × 診断結果</b>の 9 軸で集計し、
「型式は何種類採用されているか / 堤高はどの帯に集中するか / 規模はどう分布するか」 を
1 枚で俯瞰できるようにする。 H1 (型式 100% 重力式) は「広島県の地盤・地形・コスト
条件下では重力式が制度的に最適選択であり続けた」 仮説、 H2 (堤高 30-80m に 10+ 基)
は「県管理ダム = 治水中規模帯」 という<b>規模クラスタの単一性</b>仮説。</p>

<h3>手法 — 4 ステップ</h3>
<ol>
  <li><b>STEP 1: CSV パース + 型変換</b><br>
      CSV (UTF-8 BOM, {n_dams} 行 × 21 列) を <code>read_csv()</code> で読込み、
      数値列 (集水面積 / 堤体積 / 総貯水容量 / 有効貯水容量 / 堤高 / 堤頂長 /
      緯度 / 経度) を <code>pd.to_numeric(errors="coerce")</code> で
      数値化。 ダム名空欄行を除外。</li>
  <li><b>STEP 2: 元号 → 西暦変換 (本記事独自関数)</b><br>
      完成年月列は <b>「S39.6」 「H21.10」</b> という<b>和暦 + 月</b>形式。
      正規表現 <code>^([SH])(\\d+)\\.(\\d+)$</code> でパースし、
      S = 1925 + 年、 H = 1988 + 年として西暦化。
      さらに 10 年単位で「1960年代」 等の<b>完成年代</b>列を生成。</li>
  <li><b>STEP 3: GeoDataFrame 化 + 平面直角投影</b><br>
      緯度経度 → <code>shapely.geometry.Point</code> → GeoDataFrame に変換、
      <code>to_crs("EPSG:6671")</code> で<b>平面直角第 III 系</b> (m 単位)
      に投影。 距離計算で正確な結果を得るため。</li>
  <li><b>STEP 4: 9 軸集計</b><br>
      型式・堤高ビン・規模クラス・水系・市町・年代・診断結果の 7 グループ集計、
      および型式 × 規模クラス、 完成年代 × 水系の 2 クロス集計を生成。</li>
</ol>

<h3>実装</h3>
<p>狙いと方法を踏まえた実装コードは以下の通り。 元号パース正規表現 +
GeoDataFrame 化 + 9 軸集計の 3 段構成。</p>
{code(sec4_code)}

<h3>結果と読み取り</h3>

<h4>(a) 12 ダム 配置マップ (図 1)</h4>
<p><b>なぜこの図か</b>: {n_dams} 基という小さな母集団は<b>個体名で全件を語れる</b>
唯一の DoBoX シリーズ。 棒グラフだけで終わらせず、 全 12 基を<b>水系色 + 容量
バブルサイズ + B2 強調</b>で 1 枚に描くことで、 「県の水管理装置がどこに分布
しているか」 を地理的に直感する (要件 T)。</p>

{figure("assets/L78_fig1_dam_map.png",
        f"図 1: 県管理ダム {n_dams} 基の地理配置 (バブル = 容量, 色 = 水系)")}

<p>{df_to_html(T_water)}</p>

<p><b>図 1 / 表 (水系別) から読み取れること</b>:</p>
<ul>
  <li>最多水系は<b>{T_water.iloc[0]['水系名']}</b> ({int(T_water.iloc[0]['基数'])} 基)
      で、 県内ダム {int(T_water.iloc[0]['基数'])}/{n_dams} 基 = 約
      {round(100*T_water.iloc[0]['基数']/n_dams)}% を占める。 これは県東部の
      重要水系であることを反映。</li>
  <li>{int(T_water['水系名'].nunique())} 水系に分散しており、 1 水系 1 基の
      水系も<b>{int((T_water['基数']==1).sum())} 件</b>。 県全域に
      ダム機能が広く配置されている設計。</li>
  <li>沿岸〜内陸まで広く分布: 最南端は呉市の野呂川ダム (緯度
      {df.loc[df['ダム名']=='野呂川ダム','緯度'].iloc[0]:.3f})、 最北端は
      庄原市の庄原ダム (緯度 {df.loc[df['ダム名']=='庄原ダム','緯度'].iloc[0]:.3f})。
      緯度差約 0.6 度 = 約 60km の南北幅で県管理ダムが配置。</li>
  <li><b>魚切ダム (B2 判定)</b> は広島市佐伯区の<b>都市直下流</b>に位置し、
      診断結果 B2 = 唯一の要対策ダムとして赤丸で強調。 後の RQ2 / RQ3 で
      個別に深掘りする。</li>
</ul>

<h4>(b) 型式 × 堤高 × 規模 3 連 (図 2)</h4>
<p><b>なぜこの図か</b>: H1 (型式単一) と H2 (堤高集中) を 1 枚で示すための
3 panel 構成。 円グラフ (型式) + 棒グラフ (堤高ビン) + 棒グラフ (規模クラス)
の 3 視点で<b>「県管理ダムの構造プロファイルが極めて画一的」</b>であることを
示す。</p>

{figure("assets/L78_fig2_structure.png",
        f"図 2: 型式 × 堤高 × 規模 — 県管理ダムの構造プロファイル")}

<p>{df_to_html(T_form)}</p>

<p>{df_to_html(T_height)}</p>

<p>{df_to_html(T_size)}</p>

<p><b>図 2 / 表から読み取れること</b>:</p>
<ul>
  <li>左パネル: <b>{share_gravity}% が重力式コンクリート</b>。 アーチ・フィル・
      ロックフィルは<b>0 件</b>。 H1 (型式単一) は<b>{'支持' if h1_ok else '不支持'}</b>。
      これは広島県の地盤 (固い基盤岩が多い) と工事費・維持管理性の総合判断
      で重力式が優先された制度的選択の物理証拠。</li>
  <li>中パネル: 堤高は<b>30-80m 帯に {n_30_80}/{n_dams} 基
      ({round(100*n_30_80/n_dams,1)}%)</b>が集中。 H2 (30-80m に 10+ 基) は
      <b>{'支持' if h2_ok else '不支持'}</b>。 100m 超は 0 件、 30m 未満も 0 件 =
      「中規模帯」 のダムだけが採用されている画一性が極めて高い。</li>
  <li>右パネル: 規模クラスは<b>「中 (1500-8000 千 m³)」 が最多</b>。 「大」
      ({int(T_size.loc[T_size['規模クラス']=='大 (>=8000 千 m³)','基数'].iloc[0]) if (T_size['規模クラス']=='大 (>=8000 千 m³)').any() else 0} 基) は
      容量上位の魚切・福富・小瀬川。 「小」
      ({int(T_size.loc[T_size['規模クラス']=='小 (<1500 千 m³)','基数'].iloc[0]) if (T_size['規模クラス']=='小 (<1500 千 m³)').any() else 0} 基) は
      支川や近年完成の小規模治水ダム。</li>
</ul>

<h4>(c) 市町分布 (空間 sjoin 結果)</h4>
<p><b>なぜこの表か</b>: CSV の「位置」 列は<b>市町 + 大字</b>形式の文字列で
集計しにくいため、 緯度経度から行政界 polygon を sjoin で空間判定し、
正規化された<b>市町別集計</b>を生成する。</p>

<p>{df_to_html(T_city)}</p>

<p><b>表から読み取れること</b>:</p>
<ul>
  <li>{int(T_city['市町_空間'].nunique())} 市町に {n_dams} 基が分散。 1 市町 1 基が
      <b>{int((T_city['基数']==1).sum())} 市町</b>。 県管理ダムは「1 市町 1 基」
      の設計が基本。</li>
  <li>2 基以上立地する市町 (例:
      <b>{T_city.iloc[0]['市町_空間']}</b> {int(T_city.iloc[0]['基数'])} 基) は
      地形上ダム適地が複数ある特殊例。</li>
</ul>
"""


# ----- セクション 5: RQ2 -----
sec5_code = '''
import numpy as np

# (1) 集水面積 vs 総貯水容量 の Pearson 相関 (両対数)
mask = (df["集水面積_km2"] > 0) & (df["総貯水容量_千m3"] > 0)
log_a = np.log10(df.loc[mask, "集水面積_km2"].astype(float))
log_v = np.log10(df.loc[mask, "総貯水容量_千m3"].astype(float))
r_log = np.corrcoef(log_a, log_v)[0, 1]
slope, intercept = np.polyfit(log_a, log_v, 1)
# べき乗則: V = 10^intercept * A^slope
print(f"log-log r = {r_log:.3f}, slope = {slope:.3f}")

# (2) 流域支配指標 (本記事独自)
# 貯水深 (mm) = 容量 (m³) ÷ 集水面積 (m²) * 1000
df["貯水深_mm"] = (df["総貯水容量_千m3"] * 1000.0
                   / (df["集水面積_km2"] * 1_000_000.0)) * 1000.0
# 容量集水比 = 容量 (千 m³) / 集水面積 (km²) = 流域 1km² あたりの貯水量
df["容量集水比"] = (df["総貯水容量_千m3"] / df["集水面積_km2"]).round(1)
# 容量充填率 = 有効/総 (堆砂率の逆数)
df["容量充填率"] = (df["有効貯水容量_千m3"] / df["総貯水容量_千m3"] * 100).round(1)

# (3) 流域支配ランキング (貯水深 ↓)
T_dom = (df[["ダム名", "水系名", "集水面積_km2",
              "総貯水容量_千m3", "貯水深_mm", "容量集水比", "容量充填率"]]
         .sort_values("貯水深_mm", ascending=False)
         .reset_index(drop=True))
print(T_dom)
'''

sec5 = f"""
<h3>狙い (RQ2)</h3>
<p>RQ2 では<b>「ダムの流域支配 — 上流集水と下流被災ポテンシャル」</b>を量化する。
「集水面積 (km²)」 と「総貯水容量 (千 m³)」 の組合せから、 各ダムの
<b>「流域 1 km² あたりの貯水力」</b>を独自指標<b>貯水深 (mm)</b>と
<b>容量集水比 (千 m³ / km²)</b>で算出し、 12 基をランキングする。
これにより「同じ容量でも狭い流域なら過剰設計、 広い流域なら不足設計」 という
<b>流域 ↔ 容量のトレードオフ</b>が可視化される。 さらに集水 ↔ 容量の<b>両対数
相関</b> (べき乗則の有無) を統計的に検証し、 「流域 = 容量決定要因」 仮説を
量的に検証する。</p>

<h3>手法 — 3 ステップ</h3>
<ol>
  <li><b>STEP 1: 流域支配指標の導出 (本記事独自)</b><br>
      <code>貯水深 (mm) = 容量 (m³) ÷ 集水面積 (m²) × 1000</code> という直感量を
      新規導入。 「集水面積全体に均等に何 mm 分の雨を貯められるか」 という
      解釈で、 単位流域あたりの貯水力を 12 基で比較可能にする。
      同様に<b>容量充填率 = 有効/総</b>と<b>容量集水比 = 容量/集水面積</b>
      も計算。</li>
  <li><b>STEP 2: 両対数 Pearson 相関 + 線形回帰</b><br>
      集水面積と総貯水容量を<b>底 10 の対数</b>に変換し、
      <code>numpy.corrcoef</code> で Pearson r、
      <code>numpy.polyfit(deg=1)</code> で回帰式を求める。 両対数空間で
      線形なら<b>べき乗則</b> V = c · A^b が成立し、 b (= 傾き) が「流域 ↔
      容量スケーリング指数」 となる。 b = 1 なら線形、 b &lt; 1 なら亜線形
      (大流域ほど過剰貯水)、 b &gt; 1 なら超線形 (大流域ほど不足貯水)。</li>
  <li><b>STEP 3: 流域支配ランキング</b><br>
      貯水深降順で 12 基をソート。 県内 Top 3 / Bottom 3 を強調表示し、
      魚切ダム (= 広島市直下流の都市治水ダム) の位置を注目。
      「都市直下流 → 過剰設計 (高貯水深) 」 仮説の検証根拠とする。</li>
</ol>

<h3>実装</h3>
<p>狙いと方法を踏まえた実装コードは以下の通り。 独自 3 指標 + log-log
相関 + ランキングの 3 段構成。</p>
{code(sec5_code)}

<h3>結果と読み取り</h3>

<h4>(a) 集水面積 × 総貯水容量 (両対数) (図 3)</h4>
<p><b>なぜこの図か</b>: 集水面積と総貯水容量の<b>べき乗則</b>を量的に確認する
ための両対数散布図。 線形軸では小流域・小容量のダムが原点付近に潰れて
見えなくなるため、 両対数軸が必須。 さらに各点を<b>水系色</b>で塗り分けて
水系内のサブクラスタも見えるようにする。</p>

{figure("assets/L78_fig3_watershed_capacity.png",
        f"図 3: 集水面積 × 総貯水容量 (両対数) — r = {r_log:.3f}")}

<p>{df_to_html(T_watershed)}</p>

<p><b>図 3 / 表から読み取れること</b>:</p>
<ul>
  <li>両対数 Pearson r = <b>{r_log:.3f}</b>、 R² = {r2:.3f}。
      H4 (r &gt;= 0.7) は<b>{'支持' if h4_ok else '不支持'}</b>。
      集水面積と総貯水容量の<b>強い正の相関</b>を量的に確認。</li>
  <li>べき乗則指数 (= log-log 傾き) <b>= {slope:.2f}</b>。 b ≈ 1 なら線形、
      b &lt; 1 なら亜線形 (= 大流域ほど単位面積あたりの貯水量が小さい =
      過剰貯水度合いが下がる)、 b &gt; 1 なら超線形。
      観測値 b = {slope:.2f} は<b>{'亜線形' if slope < 1 else ('超線形' if slope > 1 else '線形')}</b>で、
      {'小流域ほど集水面積に対する貯水量が大きい (= 都市直下流の治水重視ダムが集水面積に比べて過剰設計)' if slope < 1 else ('大流域ほど集水面積に比例以上の貯水量を確保している (= 大流域は治水も利水も重視)' if slope > 1 else 'スケール不変')}
      ことを示す。</li>
  <li>外れ値の同定: 回帰線から大きく外れる点は<b>独自設計</b>のダム。
      集水面積最大の小瀬川ダム (160 km²) と、 集水面積最小の梶毛ダム (3.5 km²)
      が両端を構成。 中間域は回帰線によく従う。</li>
  <li>水系色の偏り: 1 水系内で集水面積・容量がほぼ同じ範囲に集中する水系
      (例: 八幡川) と、 同じ水系でも分散する水系 (例: 芦田川) が並存。
      これは水系内の<b>本流ダム vs 支流ダムの設計の違い</b>を反映。</li>
</ul>

<h4>(b) 流域支配ランキング (図 4)</h4>
<p><b>なぜこの図か</b>: 「貯水深」 (左パネル) と「容量充填率」 (右パネル) の
2 ランキングを並置することで、 「単位流域あたりの貯水力」 と「実運用容量の
有効性」 の<b>両軸で 12 基を評価</b>する。 1 つのランキングだけだと
「過剰設計に見えても堆砂で実運用容量は少ない」 のような落とし穴を見逃す。</p>

{figure("assets/L78_fig4_dominance.png",
        f"図 4: 流域支配ランキング (貯水深 + 容量充填率)")}

<p>{df_to_html(T_dom.head(12))}</p>

<p><b>図 4 / 表から読み取れること</b>:</p>
<ul>
  <li>貯水深 Top 3 は <b>{', '.join(T_dom.head(3)['ダム名'].tolist())}</b>。
      最大の {T_dom.iloc[0]['ダム名']} は{int(T_dom.iloc[0]['貯水深_mm']):,} mm =
      集水面積全体に約 {T_dom.iloc[0]['貯水深_mm']/1000:.1f} m の水深に相当する量を
      貯められる設計。</li>
  <li>魚切ダムの貯水深ランクは<b>{fish_rank}/{n_dams} 位</b> ({fish_depth:.0f} mm)。
      H3 (Top 3) は<b>{'支持' if h3_ok else '不支持'}</b>。
      {('魚切ダムは集水面積 38.4 km² から 8,460 千 m³ を貯める設計 = 「都市直下流の治水・利水両用ダム」 として相対的に過剰設計の物理証拠。' if h3_ok else f'魚切ダムは {fish_rank} 位で Top 3 入りせず、 集水面積 38.4 km² に対する貯水深は中位帯。 都市直下流の治水ダムでも集水面積比では特異ではないことを示す。')}</li>
  <li>容量充填率は平均 {df['容量充填率'].mean():.1f}%、 最低
      {df['容量充填率'].min():.1f}% ({T_filling.iloc[-1]['ダム名']})、 最高
      {df['容量充填率'].max():.1f}% ({T_filling.iloc[0]['ダム名']})。
      古いダムほど堆砂が進み充填率が下がる傾向 (RQ3 と関連)。</li>
  <li>貯水深と容量集水比は同義 (単位違い) だが、 容量充填率は独立指標。
      両者を組合せると「貯水力高 + 充填率高 = 設計通り運用、
      貯水力高 + 充填率低 = 堆砂で実力低下、
      貯水力低 + 充填率高 = 小規模だが健全」 等の 4 象限で個別ダムを
      診断可能。</li>
</ul>
"""


# ----- セクション 6: RQ3 -----
sec6_code = '''
# RQ3: 老朽化と更新ピーク予測
import re
import pandas as pd

NOW_YEAR = 2024
OLD_THRESHOLD = 50    # 築 50 年以上 = 老朽
RENEWAL_LIFE = 60     # 国の長寿命化計画想定 (大規模更新周期)

# (1) 経過年と老朽フラグ
df["経過年"] = NOW_YEAR - df["完成西暦"].astype(float)
df["is_old"] = df["経過年"] >= OLD_THRESHOLD

# (2) 完成年代別 集計
T_era = (df.groupby("完成年代")
         .agg(基数=("ダム名", "count"),
              総貯水容量合計=("総貯水容量_千m3", "sum"),
              平均堤高=("堤高_m", "mean"))
         .round(1).reset_index())
print(T_era)

# (3) 老朽ダム抽出 (築 50 年以上)
T_old = (df[df["is_old"]][["ダム名", "水系名", "完成年月",
                              "完成西暦", "経過年", "堤高_m",
                              "総貯水容量_千m3", "診断結果"]]
         .sort_values("完成西暦").reset_index(drop=True))
print(T_old)

# (4) 更新ピーク予測 (本記事独自)
df["次回更新年"] = df["完成西暦"].astype(float) + RENEWAL_LIFE
T_renewal = (df.groupby(pd.cut(df["次回更新年"],
                                bins=[2010, 2030, 2050, 2070, 2090],
                                labels=["2010s-2020s","2030s-2040s",
                                        "2050s-2060s","2070s-2080s"]),
                          observed=True)
             .agg(基数=("ダム名", "count")).reset_index())
print(T_renewal)
'''

sec6 = f"""
<h3>狙い (RQ3)</h3>
<p>RQ3 では<b>「ダムの老朽化と更新ピーク」</b>を時間軸で量的に描く。 完成年月
(S39.6 〜 H28.8 = {int(df['完成西暦'].min())} 〜 {int(df['完成西暦'].max())} の
{int(df['完成西暦'].max() - df['完成西暦'].min())} 年スパン) から各ダムの
<b>経過年</b>を計算し、 築 50 年以上の<b>老朽ダム</b>を抽出。 さらに国の
<b>インフラ長寿命化基本計画 (2014)</b>が標準想定する<b>大規模更新周期 60 年</b>
を採用し、 各ダムの「次回更新予測年」 を計算 → 20 年帯ごとの更新ピーク予測表
を生成する。 これは県の維持管理予算ピーク予測の代理指標であり、
H5 (老朽 3+ 基 かつ S30-40 年代集中) を量的検証する。</p>

<h3>手法 — 3 ステップ</h3>
<ol>
  <li><b>STEP 1: 経過年と老朽フラグの計算</b><br>
      <code>経過年 = 解析基準年 ({NOW_YEAR}) − 完成西暦</code>。
      閾値 {OLD_THRESHOLD_YEARS} 年以上を「老朽」 と定義し、 ブール列
      <code>is_old</code> を追加。 50 年は国の長寿命化計画が「更新検討開始時期」
      として想定する標準閾値 (大規模更新周期 60 年 − 検討期間 10 年)。</li>
  <li><b>STEP 2: 完成年代別 + 老朽ダム抽出</b><br>
      10 年単位の年代列を <code>groupby</code> し、 各年代の基数・総貯水容量・
      平均堤高を集計。 さらに老朽ダム (is_old = True) を完成西暦昇順で
      個体名一覧として抽出する。</li>
  <li><b>STEP 3: 更新ピーク予測 (本記事独自)</b><br>
      <code>次回更新年 = 完成西暦 + {RENEWAL_LIFE}</code> として、
      <code>pd.cut</code> で 20 年帯 (2010s-2020s / 2030s-2040s / 2050s-2060s
      / 2070s-2080s) に分類。 各帯ごとに基数と総貯水容量を集計し、
      「県の維持管理予算がいつピークを迎えるか」 を可視化する。
      これは<b>本記事独自の予測指標</b>で、 県の公式更新計画ではない。</li>
</ol>

<h3>実装</h3>
<p>狙いと方法を踏まえた実装コードは以下の通り。 経過年計算 + 老朽抽出 +
更新ピーク予測の 3 段構成。</p>
{code(sec6_code)}

<h3>結果と読み取り</h3>

<h4>(a) 完成年代 timeline + 老朽閾値 (図 5)</h4>
<p><b>なぜこの図か</b>: 完成年代別の基数 (左) と、 完成西暦 × 総貯水容量
散布図に老朽閾値線を重ねた個体散布 (右) の 2 視点。 棒グラフだけでは
12 基という小母集団を「個体名で語る」 ことができないため、 個体名ラベル
付き散布図を併用する (要件 K, T)。</p>

{figure("assets/L78_fig5_era_timeline.png",
        f"図 5: 完成年代分布と老朽閾値")}

<p>{df_to_html(T_era)}</p>

<p>{df_to_html(T_old)}</p>

<p><b>図 5 / 表から読み取れること</b>:</p>
<ul>
  <li>完成年代別: 最多は<b>{T_era.iloc[T_era['基数'].idxmax()]['完成年代']}</b>
      ({int(T_era['基数'].max())} 基)。 これは
      {('1990s 平成期初期に整備が集中したことを示す。' if T_era.iloc[T_era['基数'].idxmax()]['完成年代']=='1990年代' else 'ダム整備のピーク年代')}</li>
  <li>築 50 年以上の老朽ダムは<b>{n_old} 基</b>: {', '.join(T_old['ダム名'].tolist())}。
      H5 (3+ 基 かつ S30-40 年代集中) は<b>{'支持' if h5_ok else '不支持'}</b>。
      {('完成西暦 ' + ', '.join(map(str, T_old['完成西暦'].tolist())) + ' = 高度成長期初期に集中。' if h5_ok else '老朽ダムは S30-40 年代に分散しているか、 数が閾値未満。')}</li>
  <li>右パネル: 完成西暦 × 総貯水容量の散布。 古いダム (1960s, 赤系) は
      容量上位 (魚切・椋梨・小瀬川 等)、 新しいダム (2010s, 紫) は
      容量小規模 (庄原・野間川 等)。
      これは「整備優先度の高い大流域に古くから建設、
      残った小流域に近年補完的整備」 という<b>歴史的整備順序</b>を反映。</li>
</ul>

<h4>(b) 老朽ダムマップ (図 6)</h4>
<p><b>なぜこの図か</b>: {n_old} 基の老朽ダムの<b>地理分布</b>を直感的に把握する
ため、 経過年で円サイズ・赤青色分けしたマップを描く。 リスト表よりマップの方が
「どの地域が老朽集中か」 を直感できるため (要件 T)。</p>

{figure("assets/L78_fig6_aging_map.png",
        f"図 6: 老朽化マップ — 円サイズ = 経過年, 赤 = 築 50 年以上")}

<p><b>図 6 から読み取れること</b>:</p>
<ul>
  <li>老朽ダム (赤円, {n_old} 基) は<b>県西部〜中部</b>に分布。
      最古のダムから順に: {', '.join(T_old['ダム名'].head(3).tolist())} と続く。
      {('これらは 1960-70 年代の高度成長期初期に整備された県内最古世代。' if h5_ok else '')}</li>
  <li>新しいダム (青円) は北部・東部に分布する近年完成のダム
      ({', '.join(df.sort_values('完成西暦', ascending=False).head(3)['ダム名'].tolist())} 等)。
      整備が遅かった地域への補完的設置を反映。</li>
  <li>個体名で語ると: 最古は<b>{T_old.iloc[0]['ダム名']} ({int(T_old.iloc[0]['完成西暦'])} 年完成、
      築 {int(T_old.iloc[0]['経過年'])} 年)</b>。 最新は
      <b>{df.sort_values('完成西暦', ascending=False).iloc[0]['ダム名']} ({int(df.sort_values('完成西暦', ascending=False).iloc[0]['完成西暦'])} 年完成、
      築 {int(df.sort_values('完成西暦', ascending=False).iloc[0]['経過年'])} 年)</b>。
      {int(df['完成西暦'].max() - df['完成西暦'].min())} 年スパン = 半世紀超に渡る整備史。</li>
</ul>

<h4>(c) 更新ピーク + 診断 (図 7)</h4>
<p><b>なぜこの図か</b>: 更新ピーク帯 (左) と診断結果 (右) を並置することで、
<b>「将来の更新計画」 と「現状の健全度」</b>を 1 枚で把握する。 棒+円の併用で
「いつ何基更新が必要か」 と「現時点で何基が要対策か」 を分離して見せる。</p>

{figure("assets/L78_fig7_renewal_diagnosis.png",
        f"図 7: 更新ピーク予測 × 診断結果")}

<p>{df_to_html(T_renewal)}</p>

<p>{df_to_html(T_diag)}</p>

<p><b>図 7 / 表から読み取れること</b>:</p>
<ul>
  <li>更新ピーク帯: 最多帯は<b>{T_renewal.iloc[T_renewal['基数'].idxmax()]['更新ピーク帯']}</b>
      ({int(T_renewal['基数'].max())} 基)。
      = 県の維持管理予算ピークがこの 20 年帯に到達する予測。</li>
  <li>診断結果: <b>C (健全) {int(T_diag.loc[T_diag['診断結果']=='C','基数'].iloc[0]) if (T_diag['診断結果']=='C').any() else 0} 基
      / B2 (要対策) {int(T_diag.loc[T_diag['診断結果']=='B2','基数'].iloc[0]) if (T_diag['診断結果']=='B2').any() else 0} 基</b>。
      {('B2 = 魚切ダム 1 基のみ。 これは堤高 79.8m と県内最高で、 1982 年完成 (築 ' + str(int(df.loc[df['ダム名']=='魚切ダム','経過年'].iloc[0])) + ' 年) で老朽閾値未満だが、 大規模ゆえに点検で要対策と判定された。' if (T_diag['診断結果']=='B2').any() else '全件 C 判定で要対策ダムは無い。')}</li>
  <li>注目すべきは「築 50 年未満だが診断 B2」 (= 魚切ダム) と
      「築 50 年以上だが診断 C」 ({n_old} 基中
      {sum(1 for _,r in T_old.iterrows() if r['診断結果']=='C')} 基)
      の<b>不一致</b>。 経過年と健全度は単純な線形関係ではなく、
      <b>規模 × 設計 × 維持管理</b>の総合判断が要対策判定を決める。</li>
</ul>
"""


# ----- セクション 7: 仮説検証総合 -----
sec7 = f"""
<h3>仮説検証総合表</h3>

{df_to_html(T_hyp)}

<h3>結果の総合解釈</h3>
<p>3 RQ × 5 仮説の検証結果から、 広島県管理ダム {n_dams} 基について以下の
<b>3 つの実証的知見</b>が得られた:</p>

<ol>
  <li><b>(RQ1 — 構造) 画一的な「中規模重力式」 プロファイル</b><br>
      {n_dams} 基すべてが<b>重力式コンクリート</b> (型式単一)、 堤高は
      <b>30-80m 帯に {n_30_80} 基</b>集中、 規模クラスも中規模が中心。
      これは広島県の<b>地盤・地形・コスト・管理体制</b>の総合判断で
      重力式中規模帯が制度的最適解となり続けた物理証拠。 H1, H2 ともに
      <b>{'支持' if h1_ok else '不支持'}/{('支持' if h2_ok else '不支持')}</b>。</li>
  <li><b>(RQ2 — 流域支配) 集水 ↔ 容量のべき乗則 (指数 {slope:.2f})</b><br>
      集水面積と総貯水容量は両対数で<b>r = {r_log:.3f}</b> の強相関。
      べき乗則指数 {slope:.2f} は{('亜線形 (大流域ほど単位面積あたりの貯水量が小さい)' if slope < 1 else ('超線形 (大流域ほど単位面積あたりの貯水量が大きい)' if slope > 1 else 'スケール不変'))}。
      流域支配ランキング (貯水深) で魚切ダム = {fish_rank} 位 ({fish_depth:.0f} mm)
      は{('Top 3 入りで都市直下流の過剰設計を物理証拠で確認' if h3_ok else '中位帯で都市直下流ダムでも特異ではないことを確認')}。
      H3, H4 = <b>{'支持' if h3_ok else '不支持'}/{('支持' if h4_ok else '不支持')}</b>。</li>
  <li><b>(RQ3 — 老朽化) 築 50 年以上 {n_old} 基の更新ピーク到来</b><br>
      経過年 50 年以上の老朽ダムが<b>{n_old} 基</b> ({', '.join(T_old['ダム名'].tolist()) if n_old > 0 else 'なし'})。
      更新ピーク (= 完成 + 60 年) は最多帯
      <b>{T_renewal.iloc[T_renewal['基数'].idxmax()]['更新ピーク帯']}</b> に
      {int(T_renewal['基数'].max())} 基集中。 国の<b>インフラ長寿命化基本計画 (2014)</b>
      が想定する更新ピーク到来が県管理ダムにも実データで確認された。
      H5 = <b>{'支持' if h5_ok else '不支持'}</b>。</li>
</ol>

<h3>3 RQ を統合した「県管理ダム」 の見立て</h3>
<p>RQ1 〜 RQ3 を統合すると、 広島県の管理ダム {n_dams} 基は<b>「画一構造 ×
強相関流域支配 × 更新ピーク到来」 の 3 重特性</b>として描ける:
(1) 構造は重力式コンクリート中規模帯に画一化、 (2) 流域 ↔ 容量はべき乗則で
強相関 (= 流域が容量を支配)、 (3) {int(df['完成西暦'].max() - df['完成西暦'].min())} 年スパンの整備史で
最古世代が更新ピークに到達。 これは単なる「ダム台帳」 ではなく、
<b>県の半世紀超の水管理戦略の物理形状</b>であり、 今後 20 年の維持管理
予算配分・更新計画策定の<b>定量的基礎</b>として価値がある。</p>

<p>3 つの研究角度 (構造 / 流域支配 / 老朽化) は完全に独立で、 単一の RQ では
見えないものを 3 RQ 並列で初めて立体的に見せる。 12 基という小さな母集団でも、
<b>個体名で全件を語れる粒度</b>と<b>独自指標 (貯水深 / 更新ピーク) 導入</b>で
研究水準の深掘りが可能であることを実証した。</p>
"""


# ----- セクション 8: 発展課題 -----
sec8 = f"""
<h3>結果から導かれる新たな問い</h3>

<h4>発展課題 1: 国管理ダム + 電力会社管理ダムの統合分析</h4>
<p><b>結果 X</b>: 本記事は<b>県管理 12 基</b>のみを扱った。 一方、 同じ広島県内に
は<b>国土交通省管理</b>の弥栄ダム (一級水系小瀬川の本ダム)、 中国電力管理の
<b>発電ダム群</b> (太田川水系の王泊ダム・樽床ダム等) が複数存在する。<br>
<b>新仮説 Y</b>: 県管理ダム (治水中心) と国・電力管理ダム (利水・発電中心) を
統合した「広島県内全ダム母集団」 で同じ 3 RQ を再走したら、 H1 (型式単一)
は<b>不支持</b>になる仮説 (国・電力ダムにはアーチ式・ロックフィル等が
含まれる可能性高)。<br>
<b>課題 Z</b>: (1) 国土交通省データセット (河川局 ダム諸量データベース) と
中国電力ダム諸元を取得。 (2) 県管理 12 基と統合し、 管理者軸を加えた
4 軸クロス (型式 × 管理者 × 規模 × 完成年代) を生成。 (3) 「県管理 vs 他管理」
で構造プロファイルがどう違うかを量的比較し、 <b>県の制度的選択の特異性</b>を
他主体との比較で相対化する。</p>

<h4>発展課題 2: 下流人口・建物影響推定 (緊急放流リスクの可視化)</h4>
<p><b>結果 X</b>: RQ2 で「下流被災ポテンシャル」 を扱う予定だったが、
公開データに<b>下流人口・建物数</b>列が無いため、 市町別存在と DID 距離で
代用した。<br>
<b>新仮説 Y</b>: ダム下流 5km 圏内の<b>人口集中地区 (DID)</b>カバレッジ
率は、 <b>魚切ダム (広島市直下流) が県内最高</b>仮説。 これは「都市直下流の
治水ダムは緊急放流リスクが最も高い (= 下流に都市を抱えるため)」 という
物理的事実の量的検証。<br>
<b>課題 Z</b>: (1) 国勢調査の <b>DID polygon</b> をダウンロード。 (2)
各ダム位置から下流方向 5 km / 10 km の<b>河道追跡バッファ</b>を作成
(地形+河川流路を考慮)。 (3) DID 重なり面積・人口推定値を 12 基で
ランキング。 (4) 緊急放流時の被害想定との対応関係を量的に評価。</p>

<h4>発展課題 3: 堆砂量・実運用容量の経年変化追跡</h4>
<p><b>結果 X</b>: RQ2 で容量充填率 (= 有効/総) を計算したが、 これは
<b>1 時点</b>の値。 ダムは経年で堆砂が進み有効容量が減るため、
時系列追跡が本質的に重要。<br>
<b>新仮説 Y</b>: 完成からの経過年と容量充填率の<b>低下勾配</b>は、
集水面積 (= 流入土砂量を規定) と相関する。 大流域ダムほど堆砂進行が
速く、 100 年で有効容量が当初の 70% 程度まで減少する仮説。<br>
<b>課題 Z</b>: (1) 県のダム管理資料から各ダムの<b>堆砂量年次データ</b>を
取得 (公開されている場合)。 (2) 経過年 × 容量充填率の時系列散布を
個別ダムで描画。 (3) 堆砂率の年勾配を集水面積と回帰分析。 (4)
将来 50 年の有効容量予測モデルを構築。</p>

<h4>発展課題 4: 魚切ダム B2 判定の深掘り (要対策の物理的根拠)</h4>
<p><b>結果 X</b>: RQ3 で診断結果 B2 = 魚切ダム 1 基のみと特定した。
しかし「なぜ B2 か」 の物理的根拠は本データには含まれない (具体的な
劣化箇所・損傷種類・補修内容は別資料)。<br>
<b>新仮説 Y</b>: 魚切ダムの B2 判定は、 (a) 県内最高の堤高 79.8m で<b>水圧
負荷が大きい</b>、 (b) 1982 年完成で<b>築 {int(df.loc[df['ダム名']=='魚切ダム','経過年'].iloc[0])} 年</b>
の運用年数 (= 中年期点検検出)、 (c) 都市直下流で<b>常時利水運用</b>による
水位変動疲労、 の 3 要因の総合判断による仮説。<br>
<b>課題 Z</b>: (1) 県のダム維持管理計画書 (公開可) から魚切ダムの個別
点検報告を取得。 (2) 他の C 判定 11 基と「堤高 / 経過年 / 運用パターン」
の差を多変量比較。 (3) <b>類似条件のダム (例: 同時期完成 + 同程度堤高)</b>を
他県から探し、 B2 判定の妥当性を相対化。 (4) 補修費用試算 (= 県の
維持管理予算 5 か年計画) との照合。</p>

<h4>発展課題 5: 他県との型式分布比較 — なぜ広島県は重力式単一か</h4>
<p><b>結果 X</b>: H1 で県管理ダム<b>{share_gravity}% が重力式コンクリート</b>を
量的確認した。 一方、 静岡県・新潟県・北海道等は<b>アーチ式・ロックフィル</b>も
混在する。<br>
<b>新仮説 Y</b>: 重力式単一は<b>地盤特性</b> (花崗岩主体の固い基盤岩が広島県に
多い) と<b>水系規模</b> (大規模水系が少ない = アーチ式の経済優位性が出ない) の
2 要因で説明可能仮説。<br>
<b>課題 Z</b>: (1) 国交省<b>ダム諸量データベース</b>から全国 2,700+ ダムの
型式 × 都道府県 × 地盤分類を取得。 (2) 「重力式比率」 を都道府県ランキング
+ 全国マップで可視化。 (3) <b>地質図 (産総研)</b>と重ね合わせて地盤分類との
関係を量的検証。 (4) 広島県と類似地盤の他県 (岡山・島根) を抽出し、
型式選択パターンを比較。 「県の制度的選択は地盤特性に強く規定される」 という
仮説を全国母集団で検証する。</p>
"""


# ----- セクション組み立て -----
sections = [
    ("学習目標と問い", sec1),
    ("使用データ", sec2),
    ("ダウンロード", sec3),
    ("【RQ1】構造研究 — 型式 × 規模 × 地理分布", sec4),
    ("【RQ2】流域支配研究 — 集水 × 容量 × 貯水深", sec5),
    ("【RQ3】老朽化研究 — 完成年代 × 経過年 × 更新ピーク", sec6),
    ("仮説検証総合", sec7),
    ("発展課題", sec8),
]

html = render_lesson(
    num=78,
    title="L78 ダム基本情報・維持管理情報 単独 3 研究例分析",
    tags=["ダム", "GIS", "オープンデータ", "流域支配", "老朽化"],
    time="30〜45 分",
    level="リテラシ〜中級",
    data_label=(f'<a href="https://hiroshima-dobox.jp/datasets/{DATASET_ID}" '
                f'target="_blank">ダム基本情報・維持管理情報 (1 dataset / 1 リソース) — '
                f'{n_dams} 基 / {T_water["水系名"].nunique()} 水系</a>'),
    sections=sections,
    script_filename="L78_dams.py",
)

OUT_HTML = LESSONS / "L78_dams.html"
OUT_HTML.write_text(html, encoding="utf-8")
print(f"  HTML 出力: {OUT_HTML}", flush=True)
print(f"  ({time.time()-t10:.2f}s)", flush=True)


# =============================================================================
# 11. 完了
# =============================================================================
print(f"\n=== L78 完了: {time.time()-t_all:.2f}s ===", flush=True)
print(f"  CSV (中間): {len(list(ASSETS.glob('L78_*.csv')))} ファイル", flush=True)
print(f"  PNG (図):   {len(list(ASSETS.glob('L78_*.png')))} ファイル", flush=True)
