はじめに
MapLibre と deck.gl を使って、
警察庁の交通事故統計オープンデータをもとに事故発生地点を可視化します。
今回は、deck.gl の HexagonLayer を使用し、事故の発生密度を3次元で表現します。
データの準備
交通事故統計オープンデータ(本票CSV)には、
緯度・経度が度分秒(DMS)形式の右詰め数値で記録されています。
以下のスクリプトで 10 進度に変換し、東京都のみを抽出します。
import pandas as pd
# === 設定 ===
CSV_FILE = "honhyo_2024.csv" # 警察庁オープンデータ
ENCODING = "shift_jis"
PREF_CODE = 30 # 東京都
OUTPUT_JSON = "accidents_tokyo.json"
# === DMS → 10進度変換関数 ===
def dms_to_deg(value: int | str) -> float:
s = str(int(value)).rjust(10, "0") # 緯度9桁→10桁右詰め補完
sec = int(s[-5:]) / 1000
minute = int(s[-7:-5])
deg = int(s[:-7])
return deg + minute / 60 + sec / 3600
# === CSV 読み込みと変換 ===
df = pd.read_csv(CSV_FILE, encoding=ENCODING)
df = df[df["都道府県コード"] == PREF_CODE]
# 緯度経度カラムの変換
df["lat"] = df["地点 緯度(北緯)"].apply(dms_to_deg)
df["lon"] = df["地点 経度(東経)"].apply(dms_to_deg)
# === 出力 ===
records = df[["lon", "lat"]].to_dict(orient="records")
pd.DataFrame(records).to_json(OUTPUT_JSON, orient="records", force_ascii=False, indent=2)
print(f"{len(records)} 件を {OUTPUT_JSON} に出力しました。")
これで、accidents_tokyo.json が作成されます。
サンプルコード
MapLibre を背景に、deck.gl の HexagonLayer で事故発生地点を 3D 表現します。
最小構成では、背景スタイルを指定し、accidents_tokyo.json を読み込むだけで動作します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<title>交通事故データ 3D分布</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- MapLibre -->
<script src="https://unpkg.com/maplibre-gl@3.6.0/dist/maplibre-gl.js"></script>
<link href="https://unpkg.com/maplibre-gl@3.6.0/dist/maplibre-gl.css" rel="stylesheet" />
<!-- deck.gl -->
<script src="https://unpkg.com/deck.gl@8.9.36/dist.min.js"></script>
<style>
html,
body {
margin: 0;
height: 100%;
overflow: hidden;
}
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
.maplibregl-canvas {
filter: brightness(80%) contrast(110%);
}
</style>
</head>
<body>
<div id="map"></div>
<script>
const map = new maplibregl.Map({
container: "map",
style: "https://basemaps.cartocdn.com/gl/positron-gl-style/style.json",
center: [139.76, 35.68],
zoom: 10,
pitch: 45,
bearing: -15,
});
let deckOverlay;
fetch("accidents_tokyo.json")
.then((res) => res.json())
.then((data) => {
const createLayer = (radius, elevationScale) =>
new deck.HexagonLayer({
id: "accidents-hex",
data,
getPosition: (d) => [d.lon, d.lat],
radius, // 六角形の平面サイズ
elevationScale, // ズームに応じて高さも調整
elevationRange: [0, 4000],
extruded: true,
opacity: 0.75,
pickable: true,
parameters: {
depthTest: true, // 深度テストを有効化
blend: true,
blendFunc: [770, 771], // SRC_ALPHA, ONE_MINUS_SRC_ALPHA
blendEquation: 32774,
},
colorRange: [
[0, 0, 255],
[0, 128, 255],
[0, 255, 128],
[255, 255, 0],
[255, 128, 0],
[255, 0, 0],
],
onHover: (info) => {
map.getCanvas().style.cursor = info.object ? "pointer" : "";
},
});
// 初期設定
let radius = 400;
let elevationScale = 5; // 実際に採用される値に合わせる
let hexLayer = createLayer(radius, elevationScale);
deckOverlay = new deck.MapboxOverlay({
interleaved: true,
layers: [hexLayer],
});
map.addControl(deckOverlay);
// === ズームレベルに応じて radius と elevationScale を連動調整 ===
map.on("zoom", () => {
const z = map.getZoom();
let radius, elevationScale;
if (z >= 14) {
radius = 20; // 交差点レベル
elevationScale = 0.25; // 高さを低く(詳細表示)
} else if (z >= 12) {
radius = 100; // 市街レベル
elevationScale = 1.0; // 中程度の高さ
} else if (z >= 10) {
radius = 200; // 区レベル
elevationScale = 2; // 標準の高さ
} else {
radius = 400; // 広域傾向
elevationScale = 5; // 高く(概要表示)
}
const newLayer = createLayer(radius, elevationScale);
// レイヤーを完全に更新
deckOverlay.setProps({
layers: [newLayer],
// 強制的にレンダリングをリセット
_getViewState: () => map.transform,
});
// 追加:少し遅延させて再描画を確実にする
setTimeout(() => {
map.triggerRepaint();
}, 50);
});
});
</script>
</body>
</html>
地図を開くと、東京都内の事故発生地点が六角形の立体として描画されます。
radius を調整することで、平面上の密度感を変えることができます。
このコードでは、地図のズーム操作に応じて
六角形のサイズ(radius)と高さ倍率(elevationScale)を自動的に調整しています。
広域では密度の傾向を俯瞰でき、ズームインすると詳細な分布が確認できます。
背景地図には Carto 提供のベクトルスタイル
https://basemaps.cartocdn.com/gl/positron-gl-style/style.json を使用しています。
パラメータ例
HexagonLayer の主なパラメータと、その役割をまとめます。
視認性はブラウザの表示倍率や地図範囲にも左右されるため、
以下の設定値はあくまで一例です。
| パラメータ | 役割 | 設定例 |
|---|---|---|
radius |
六角形の平面サイズ(m) | 300 |
elevationScale |
柱の高さ倍率 | 3 |
elevationRange |
高さの最小・最大範囲 | [0, 4000] |
opacity |
不透明度(背景地図とのバランス調整) | 0.75 |
colorRange |
密度に応じた配色。以下のようなRGB指定が可能 | 6段階グラデーション(青→赤)[[0,0,255], [0,128,255], [0,255,128], [255,255,0], [255,128,0], [255,0,0]]
|
depthTest |
柱の前後関係を正しく描画する設定 | true |
blendFunc |
透過合成の方式を指定 | [SRC_ALPHA, ONE_MINUS_SRC_ALPHA] |
配色は [R,G,B] の数値配列で指定します。
低密度域を寒色、高密度域を暖色で表現すると直感的に理解しやすくなります。
まとめ
本記事では、MapLibre と deck.gl を組み合わせて、
警察庁の交通事故統計オープンデータの事故発生場所を 3D 表現しました。
HexagonLayer を用いて事故発生地点の分布を高さで表し、
密度の差を視覚的に把握できるようになります。
表示の見やすさは radius や opacity の調整によって大きく変化します。
まずは最小構成で試し、目的に応じて設定を調整するとよいでしょう。
実際に可視化してみると、
特定の地域で事故が集中する様子を空間的に捉えられ、
データを「数値」ではなく「地理的パターン」として直感的に理解できるようになりました。
また、同じデータでも表示スケールを変えることで
”局所的な多発地点” と ”広域的な傾向” の両方を確認でき、
単一視点では得られない気づきが得られます。
可視化の目的が明確であれば、
3D表現はデータの特徴をより多面的に把握する手段となります。
交通事故統計オープンデータ(警察庁):
https://www.npa.go.jp/publications/statistics/koutsuu/opendata/index_opendata.html

