はじめに
国土数値情報では、洪水浸水想定区域データが公開されています。
このデータは洪水ハザードマップの基礎となるもので、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 に読み込み、地図上に表示してみます。
単純にポリゴンを表示するだけでも、
洪水想定区域が広い範囲に分布していることが分かります。
ただし、この状態では浸水深の違いは読み取れません。
次に、洪水データの属性を確認していきます。
洪水データの属性構造
洪水浸水想定区域データの 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: '© 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
};
}
このように浸水深区分で色分けすることで、
単純なポリゴン表示から簡易的なハザードマップとして利用できるようになります。
まとめ
国土数値情報の洪水浸水想定区域データは、GeoJSON 形式で取得できるため
Leaflet などの Web 地図ライブラリでも比較的簡単に扱うことができます。
今回のように
- GeoJSON を読み込む
- 浸水深区分で色分けする
だけでも、簡易的なハザードマップとして十分に機能します。
都道府県全体を扱う場合は、GeoJSON の統合や軽量化も検討するとよさそうです。
このあたりは別の記事でも紹介していますので、興味があればそちらも参照してみてください。

