1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

国土数値情報(N03)から軽い日本地図を作って、SVGで塗り分けた

1
Posted at

はじめに

都道府県ごとに色を塗り分ける日本地図をつくってみました

営利で使えるか、利用条件がはっきりした素材を使いたいと思い、国土数値情報の行政区域データ(N03)から、AI(Claude Code)に相談しながら作りました

この記事では、次の3つを書きます

  • N03 から 143KB の GeoJSON を作った手順
  • GeoJSON を SVG にして、県ごとに色を塗る方法
  • 沖縄を枠に入れて拡大するなど、見た目の工夫

動作確認した環境

項目 内容
OS Windows 11
実行環境 Node.js
地図の加工 mapshaper(npm)
元データ 国土数値情報 行政区域データ N03-20250101

作るもの

  1. N03 のシェープファイル(約600MB)を用意する
  2. スクリプトを1回実行して、prefectures.geojson(約143KB)を作る
  3. ブラウザで、GeoJSON から47県の SVG を描く
  4. JavaScript から fill を変えて、県ごとに塗り分ける

map.png

地図素材の選び方

営利で使う場合も想定して、利用条件で候補を絞りました

候補 判断
国土数値情報 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" },
);
  1. N03_007(全国地方公共団体コード)の上2桁を、県コード id にします
  2. 必要な項目(id と nam_ja)だけ残します
  3. 海岸線を 1000m 間隔で簡略化します
  4. 県ごとにまとめます(-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 を変えるだけで塗り分けられるので、ライブラリなしでも十分でした
    • 沖縄の移設や小笠原の省略など、見やすさの工夫は自分で決められます
1
0
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
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?