はじめに
都道府県ごとに色を塗り分ける日本地図をつくってみました
営利で使えるか、利用条件がはっきりした素材を使いたいと思い、国土数値情報の行政区域データ(N03)から、AI(Claude Code)に相談しながら作りました
この記事では、次の3つを書きます
- N03 から 143KB の GeoJSON を作った手順
- GeoJSON を SVG にして、県ごとに色を塗る方法
- 沖縄を枠に入れて拡大するなど、見た目の工夫
動作確認した環境
| 項目 | 内容 |
|---|---|
| OS | Windows 11 |
| 実行環境 | Node.js |
| 地図の加工 | mapshaper(npm) |
| 元データ | 国土数値情報 行政区域データ N03-20250101 |
作るもの
- N03 のシェープファイル(約600MB)を用意する
- スクリプトを1回実行して、
prefectures.geojson(約143KB)を作る - ブラウザで、GeoJSON から47県の SVG を描く
- JavaScript から
fillを変えて、県ごとに塗り分ける
地図素材の選び方
営利で使う場合も想定して、利用条件で候補を絞りました
| 候補 | 判断 |
|---|---|
| 国土数値情報 N03 | 出典と加工した旨を書けばよく、報告や申請は不要でした |
| dataofjapan/land | 営利利用では出典の明記と利用報告が必要なので、見送りました |
| jpn-atlas | README にライセンスの記載がなく、確認できなかったので見送りました |
N03 の利用規約は、公開前に原文で確認することをおすすめします
N03 から軽い GeoJSON を作る
N03 は市区町村ごとのデータで、全国で約12万ポリゴンあります。そのままでは重いので、mapshaper で簡略化します。
// scripts/build-prefectures.mjs(抜粋)
execFileSync(
process.execPath,
[
"--max-old-space-size=12288",
mapshaper, input, "encoding=utf8",
"-each", 'id = +(N03_007 || "0").slice(0, 2); nam_ja = N03_001',
"-filter-fields", "id,nam_ja",
"-simplify", "dp", `interval=${SIMPLIFY_INTERVAL_M}`,
"-dissolve", "id", "copy-fields=nam_ja",
"-sort", "id",
"-o", dissolved, "format=geojson", "precision=0.001",
],
{ stdio: "inherit" },
);
-
N03_007(全国地方公共団体コード)の上2桁を、県コードidにします - 必要な項目(
idとnam_ja)だけ残します - 海岸線を 1000m 間隔で簡略化します
- 県ごとにまとめます(
-dissolve)
そのあとスクリプトで、10km² より小さい島を落とします。ただし、各県で最大の陸地は必ず残します
const kept = sized.filter((s) => s.km2 >= MIN_ISLAND_KM2 || s.km2 === biggest).map((s) => s.p);
最後に、県が47件あることを確かめてから書き出します
if (geojson.features.length !== 47) throw new Error(`都道府県が47件ではありません: ${geojson.features.length}`);
GeoJSON を SVG にする
地図は、47県の path を持つインライン SVG にしました。地図ライブラリは使わず、緯度経度を平面の座標に直して、path の d 属性を組み立てています(このコードもAIと一緒に作りました)
const LON_SCALE = Math.cos((36 * Math.PI) / 180);
function project(id, [lon, lat]) {
// 沖縄は枠の中へ移して拡大する(後述)
return [lon * LON_SCALE, -lat];
}
- 経度には、緯度に応じた縮み(本州の中心付近の36度で
cos)をかけます。縦横比が自然になります - SVG は上が小さい座標なので、緯度の符号は反転します
県ごとに <g> を作り、色を塗る path と、斜線を重ねる path を入れます
const g = svgElement("g", { class: "pref", "data-code": code, tabindex: 0, role: "button", "aria-label": feature.properties.nam_ja });
const base = svgElement("path", { class: "base", d, fill: NO_DATA_COLOR });
const hatch = svgElement("path", { class: "hatch", d, fill: "url(#hatch)" });
色を変えるときは、fill を書き換えるだけです
base.setAttribute("fill", colorOfPref(d));
tabindex と role="button" を付けたので、キーボードの Enter・Space でも県を選べます
見た目の工夫
沖縄を枠に入れて、2倍にする
実際の位置のままだと、地図が縦に長くなります。さらに、沖縄は島が細くて、ほとんど見えません。そこで、九州の南東の空白に枠で囲んで移し、2倍に拡大しました
const OKINAWA_ANCHOR = { lon: 127.5, lat: 26.5 };
const OKINAWA_SCALE = 2;
const OKINAWA_SHIFT = { lon: 11, lat: 2 };
拡大の中心から OKINAWA_SCALE 倍して、その中心が OKINAWA_SHIFT だけ動いた位置に来るようにしています
小笠原諸島は描かない
東京都の南の離島(小笠原)まで描くと、地図が縦に伸びてしまいます
緯度30度より南のポリゴンは、描画から外しました
return feature.properties.id === TOKYO ? polys.filter((p) => p[0][0][1] > 30) : polys;
拡大・縮小・移動
viewBox を書き換えて実現しています(最大8倍)
拡大しても県境の線が太くならないように、CSS で vector-effect: non-scaling-stroke を付けました
同じ GeoJSON で位置判定もする
この GeoJSON は、ブラウザの位置情報から県を判定するのにも使えます
緯度経度を外に出さず、ブラウザの中で、点が多角形の中にあるかを判定します(point-in-polygon)
- 簡略化した海岸線とのずれを吸収するため、陸地の外でも海岸線から約0.05度(約5km)以内なら、最も近い県とみなします
- それより遠い海上は、判定不能として手動で選んでもらいます
地図の描画と位置判定で1つのファイルを使えるので、データの管理が楽になりました
出典の書き方
N03 を使う場合は、出典と加工した旨を書く必要があります。次のような書き方になります
国土交通省「国土数値情報(行政区域データ N03-20250101)」を加工して作成
まとめ
- N03 は、市区町村単位で簡略化してから県にまとめると、約143KB まで小さくできました
- SVG は
fillを変えるだけで塗り分けられるので、ライブラリなしでも十分でした- 沖縄の移設や小笠原の省略など、見やすさの工夫は自分で決められます

