1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

国土数値情報の洪水浸水想定区域データ(GeoJSON)をLeafletで可視化する

1
Last updated at Posted at 2026-03-15

はじめに

国土数値情報では、洪水浸水想定区域データが公開されています。
このデータは洪水ハザードマップの基礎となるもので、GISソフトだけでなく Web 地図でも活用できます。

この記事では、国土数値情報の 洪水浸水想定区域データ(河川単位) を使い、
Leaflet 上に簡易的なハザードマップを表示する方法を確認します。

今回は東京都のデータを例に、GeoJSON を読み込み、
浸水深区分に応じた色分け表示を行います。

今回使用するデータ

今回使用するのは、国土数値情報で公開されている
洪水浸水想定区域データ(河川単位)です。

GeoJSON 形式でダウンロードすると、
洪水浸水想定区域データにはいくつかのレイヤーが含まれます。

この記事では 想定最大規模 のレイヤーを使用します。

ダウンロードしたデータを解凍すると、
東京都の場合は次のような構造になっていました。

20_想定最大規模  
  A31a-20-24_13_1300020001_10.geojson  
  A31a-20-24_13_1300100001_10.geojson  
  A31a-20-24_13_8303040032_10.geojson  
  A31a-20-24_13_8303040044_10.geojson  
  A31a-20-24_13_8303050003_10.geojson  
  A31a-20-24_13_8303060001_10.geojson  
  A31a-20-24_13_8303060010_10.geojson  

GeoJSON は複数ファイルに分割されており、
今回確認した東京都のデータでは河川単位で作成されていました。

なお、都道府県全体を扱う場合は
GeoJSON を統合したり軽量化したりする必要が出てくる場合があります。

このあたりについては以前の記事で紹介しています。

LeafletでGeoJSONを表示する

まずは GeoJSON をそのまま Leaflet に読み込み、地図上に表示してみます。

31dc9ef8-4805-4780-9f6d-dcfd598e0ca4-1.png

単純にポリゴンを表示するだけでも、
洪水想定区域が広い範囲に分布していることが分かります。

ただし、この状態では浸水深の違いは読み取れません。
次に、洪水データの属性を確認していきます。

洪水データの属性構造

洪水浸水想定区域データの GeoJSON には、
浸水想定区域に関する属性が properties に格納されています。

主な項目は次のとおりです。

属性 意味
A31a_201 河川コード
A31a_202 河川名
A31a_203 都道府県コード
A31a_204 都道府県名
A31a_205 浸水深区分

今回の可視化で重要なのは 浸水深区分(A31a_205) です。

浸水深区分は次のように定義されています。

コード 浸水深 内容
1 0~0.5m未満 一般的な住宅において床下程度の浸水
2 0.5~3.0m未満 床上から1階が浸水
3 3.0~5.0m未満 2階部分が浸水
4 5.0~10.0m未満 2階部分が水没
5 10.0~20.0m未満 津波基準水位を表現する
6 20.0m以上

この値を使うことで、洪水区域を深さごとに色分けできます。

浸水深区分で色分けする

ここでは、浸水深区分に応じてポリゴンの色を変更します。

次のスクリプトでは、GeoJSON を読み込み、
浸水深区分(A31a_205)を使って色分け表示を行っています。

<!doctype html>
<html lang="ja">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>洪水浸水想定区域 GeoJSON サンプル</title>
  <link
    rel="stylesheet"
    href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
    integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
    crossorigin=""
  />
  <style>
    html, body {
      height: 100%;
      margin: 0;
      font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
    }

    .layout {
      display: grid;
      grid-template-rows: auto 1fr;
      height: 100%;
    }

    .toolbar {
      padding: 12px 16px;
      border-bottom: 1px solid #ddd;
      background: #fafafa;
    }

    .toolbar h1 {
      margin: 0 0 8px;
      font-size: 18px;
    }

    .toolbar p {
      margin: 4px 0;
      font-size: 14px;
      color: #333;
    }

    #map {
      width: 100%;
      height: 100%;
    }

    .error {
      color: #b00020;
      font-weight: 600;
    }

    .leaflet-popup-content {
      line-height: 1.5;
    }

    .popup-table {
      border-collapse: collapse;
      width: 100%;
      font-size: 12px;
    }

    .popup-table th,
    .popup-table td {
      border: 1px solid #ddd;
      padding: 4px 6px;
      vertical-align: top;
      text-align: left;
    }

    .popup-table th {
      width: 40%;
      background: #f5f5f5;
    }
  </style>
</head>
<body>
  <div class="layout">
    <div class="toolbar">
      <h1>洪水浸水想定区域 GeoJSON サンプル</h1>
      <p>まずは 1 河川の GeoJSON をそのまま重ねるだけの最小構成です。</p>
      <p id="status">読み込み中...</p>
    </div>
    <div id="map"></div>
  </div>

  <script
    src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
    integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
    crossorigin=""
  ></script>
  <script>
    const map = L.map('map');

    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
      attribution: '&copy; OpenStreetMap contributors'
    }).addTo(map);

    const statusEl = document.getElementById('status');

    // 必要に応じてファイル名を差し替えてください
    const geojsonPath = './A31a-20-24_13_1300020001_10.geojson';

    const depthLabels = {
      1: '0.5m未満',
      2: '0.5〜3m',
      3: '3〜5m',
      4: '5〜10m',
      5: '10〜20m',
      6: '20m以上'
    };

    function getColor(depthClass) {
      switch (Number(depthClass)) {
        case 6:
          return '#3b0f70';
        case 5:
          return '#4b0082';
        case 4:
          return '#08519c';
        case 3:
          return '#2171b5';
        case 2:
          return '#6baed6';
        case 1:
          return '#c6dbef';
        default:
          return '#cccccc';
      }
    }

    function featureStyle(feature) {
      const depthClass = feature?.properties?.A31a_205;
      return {
        color: '#355c7d',
        weight: 0.6,
        opacity: 0.8,
        fillColor: getColor(depthClass),
        fillOpacity: 0.55
      };
    }

    function buildPopupContent(properties = {}) {
      const riverName = properties.A31a_202 ?? '(不明)';
      const prefectureName = properties.A31a_204 ?? '(不明)';
      const riverCode = properties.A31a_201 ?? '(不明)';
      const depthClass = properties.A31a_205;
      const depthLabel = depthLabels[depthClass] ?? `区分 ${depthClass ?? '(不明)'}`;

      return `
        <table class="popup-table">
          <tr><th>河川名</th><td>${riverName}</td></tr>
          <tr><th>河川コード</th><td>${riverCode}</td></tr>
          <tr><th>都道府県</th><td>${prefectureName}</td></tr>
          <tr><th>浸水深区分</th><td>${depthLabel}</td></tr>
        </table>
      `;
    }

    async function loadGeoJSON() {
      try {
        const response = await fetch(geojsonPath);
        if (!response.ok) {
          throw new Error(`HTTP ${response.status} ${response.statusText}`);
        }

        const geojson = await response.json();

        const layer = L.geoJSON(geojson, {
          style: featureStyle,
          onEachFeature(feature, layer) {
            layer.bindPopup(buildPopupContent(feature.properties));
          }
        }).addTo(map);

        addLegend();

        const bounds = layer.getBounds();
        if (bounds.isValid()) {
          map.fitBounds(bounds, { padding: [20, 20] });
        } else {
          map.setView([35.68, 139.76], 10);
        }

        const featureCount = Array.isArray(geojson.features) ? geojson.features.length : 0;
        statusEl.textContent = `読み込み完了: ${geojsonPath} / ${featureCount} feature(s)`;
      } catch (error) {
        console.error(error);
        statusEl.innerHTML = `<span class="error">読み込み失敗: ${error.message}</span>`;
        map.setView([35.68, 139.76], 10);
      }
    }

    function addLegend() {
      const legend = L.control({ position: 'bottomright' });

      legend.onAdd = function () {
        const div = L.DomUtil.create('div', 'info legend');
        div.style.background = 'white';
        div.style.padding = '10px 12px';
        div.style.border = '1px solid #ccc';
        div.style.borderRadius = '6px';
        div.style.boxShadow = '0 1px 4px rgba(0, 0, 0, 0.2)';
        div.style.lineHeight = '1.6';
        div.innerHTML = `
        <div style="font-weight: 700; margin-bottom: 6px;">浸水深区分</div>
        <div><span style="display:inline-block;width:12px;height:12px;background:${getColor(6)};margin-right:6px;"></span>20.0m以上</div>
        <div><span style="display:inline-block;width:12px;height:12px;background:${getColor(5)};margin-right:6px;"></span>10.0~20.0m未満</div>
        <div><span style="display:inline-block;width:12px;height:12px;background:${getColor(4)};margin-right:6px;"></span>5.0~10.0m未満</div>
        <div><span style="display:inline-block;width:12px;height:12px;background:${getColor(3)};margin-right:6px;"></span>3.0~5.0m未満</div>
        <div><span style="display:inline-block;width:12px;height:12px;background:${getColor(2)};margin-right:6px;"></span>0.5~3.0m未満</div>
        <div><span style="display:inline-block;width:12px;height:12px;background:${getColor(1)};margin-right:6px;"></span>0~0.5m未満</div>
        `;
        return div;
      };

      legend.addTo(map);
    }

    loadGeoJSON();
  </script>
</body>
</html>

このスクリプトのポイントは、style 関数です。

GeoJSON の各 feature に対してスタイルを指定することができ、
ここで properties.A31a_205 を参照しています。

function getColor(depthClass) {
  switch (Number(depthClass)) {
    case 6:
      return '#3b0f70';
    case 5:
      return '#4b0082';
    case 4:
      return '#08519c';
    case 3:
      return '#2171b5';
    case 2:
      return '#6baed6';
    case 1:
      return '#c6dbef';
    default:
      return '#cccccc';
  }
}

function featureStyle(feature) {
  const depthClass = feature?.properties?.A31a_205;
  return {
    color: '#355c7d',
    weight: 0.6,
    opacity: 0.8,
    fillColor: getColor(depthClass),
    fillOpacity: 0.55
  };
}

31dc9ef8-4805-4780-9f6d-dcfd598e0ca4-2.png

このように浸水深区分で色分けすることで、
単純なポリゴン表示から簡易的なハザードマップとして利用できるようになります。

まとめ

国土数値情報の洪水浸水想定区域データは、GeoJSON 形式で取得できるため
Leaflet などの Web 地図ライブラリでも比較的簡単に扱うことができます。

今回のように

  • GeoJSON を読み込む
  • 浸水深区分で色分けする

だけでも、簡易的なハザードマップとして十分に機能します。

都道府県全体を扱う場合は、GeoJSON の統合や軽量化も検討するとよさそうです。
このあたりは別の記事でも紹介していますので、興味があればそちらも参照してみてください。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?