日本の標準地域メッシュコードと PostGIS で都市環境を定量化する
はじめに
「この地点は住みやすいのか?」という問いに、データで答えようとしたことはありますか?
日本には、国土を均一なグリッドで分割する標準地域メッシュという仕組みがあります。JIS C 6304 で規格化されたこの体系は、国勢調査・気象観測・環境アセスメントなど公的統計の空間集計基盤として 50 年以上運用されています。
この記事では、標準地域メッシュの構造を解説し、PostGIS と組み合わせることで「都市環境を定量的にスコアリングする」実装パターンを紹介します。不動産テック、都市計画、ロケーションインテリジェンスなど、空間分析を必要とするプロダクト開発者向けの内容です。
標準地域メッシュとは
階層構造
標準地域メッシュは、日本全土を段階的に細分化するグリッドシステムです。上位メッシュを4分割して下位メッシュを生成する再帰的な構造を持ちます。
| レベル | 名称 | 辺の長さ(概算) | コード桁数 | メッシュ数(全国) |
|---|---|---|---|---|
| 1次メッシュ | 地域メッシュ | 約80km | 4桁 | 約176 |
| 2次メッシュ | 統合地域メッシュ | 約10km | 6桁 | 約4,500 |
| 3次メッシュ | 基準地域メッシュ | 約1km | 8桁 | 約380,000 |
| 4次メッシュ | 2分の1地域メッシュ | 約500m | 9桁 | 約1,520,000 |
| 5次メッシュ | 4分の1地域メッシュ | 約250m | 10桁 | 約6,080,000 |
コード体系
メッシュコードは緯度・経度から一意に決定されます。上位メッシュのコードが下位メッシュの接頭辞になるため、文字列前方一致で親子関係を判定できます。
1次メッシュ(4桁):
コード = [緯度×1.5の整数部(2桁)] + [経度-100の整数部(2桁)]
例:東京駅 (35.6812, 139.7671)
→ 緯度部: floor(35.6812 × 1.5) = floor(53.5218) = 53
→ 経度部: 139 - 100 = 39
→ 1次メッシュコード: 5339
2次メッシュ(6桁):1次メッシュを緯度方向8等分・経度方向8等分して 64 分割。
コード = [1次(4桁)] + [緯度8分割位置(1桁)] + [経度8分割位置(1桁)]
例:東京駅 → 533945
3次以降:さらに 10 等分(3次)→ 2等分(4次)→ 2等分(5次)と再帰的に細分化。
なぜメッシュコードが優れているか
- 座標から一意計算:テーブル参照不要、算術計算のみで変換できる
- 階層的な包含関係:コード前方一致で空間包含を高速判定
- 均一グリッド:面積が統一されているため、密度比較が直感的
- 公的統計との親和性:e-Stat、国土数値情報など日本の公的データの多くがメッシュ単位で公開
PostGIS でメッシュを扱う
テーブル設計
メッシュ単位で都市環境データを格納する基本的なテーブル設計です。
CREATE TABLE mesh_metrics (
mesh_code VARCHAR(10) PRIMARY KEY, -- 250mメッシュ
geom GEOMETRY(Polygon, 4326), -- メッシュ境界ポリゴン
population INTEGER, -- 人口(国勢調査)
elevation_avg REAL, -- 平均標高
hazard_flags JSONB, -- 災害リスクフラグ
env_scores JSONB, -- 環境スコア
updated_at TIMESTAMPTZ DEFAULT now()
);
-- 空間インデックス
CREATE INDEX idx_mesh_geom ON mesh_metrics USING GIST(geom);
-- コード前方一致検索用
CREATE INDEX idx_mesh_code ON mesh_metrics (mesh_code text_pattern_ops);設計ポイント:
mesh_codeを主キーにすることで、座標→コード変換後に PK 直引きが可能(O(1) ルックアップ)JSONBカラムで可変の指標群を柔軟に格納text_pattern_opsインデックスで前方一致(LIKE '5339%')を高速化
座標からメッシュコードへの変換
PostGIS の関数として実装すると、SQL クエリ内で直接利用できます。
CREATE OR REPLACE FUNCTION lat_lng_to_mesh_250m(
lat DOUBLE PRECISION,
lng DOUBLE PRECISION
) RETURNS VARCHAR(10) AS $$
DECLARE
lat_idx INTEGER; lng_idx INTEGER;
m1_lat INTEGER; m1_lng INTEGER;
m2_lat INTEGER; m2_lng INTEGER;
m3_lat INTEGER; m3_lng INTEGER;
m4 INTEGER; m5 INTEGER;
rem_lat DOUBLE PRECISION; rem_lng DOUBLE PRECISION;
BEGIN
-- 1次メッシュ
m1_lat := floor(lat * 1.5)::INTEGER;
m1_lng := (floor(lng) - 100)::INTEGER;
rem_lat := lat * 1.5 - m1_lat;
rem_lng := lng - floor(lng);
-- 2次メッシュ (8分割)
m2_lat := floor(rem_lat * 8)::INTEGER;
m2_lng := floor(rem_lng * 8)::INTEGER;
rem_lat := rem_lat * 8 - m2_lat;
rem_lng := rem_lng * 8 - m2_lng;
-- 3次メッシュ (10分割)
m3_lat := floor(rem_lat * 10)::INTEGER;
m3_lng := floor(rem_lng * 10)::INTEGER;
rem_lat := rem_lat * 10 - m3_lat;
rem_lng := rem_lng * 10 - m3_lng;
-- 4次 (2分割)
m4 := floor(rem_lat * 2)::INTEGER * 2 + floor(rem_lng * 2)::INTEGER + 1;
rem_lat := rem_lat * 2 - floor(rem_lat * 2);
rem_lng := rem_lng * 2 - floor(rem_lng * 2);
-- 5次 (2分割)
m5 := floor(rem_lat * 2)::INTEGER * 2 + floor(rem_lng * 2)::INTEGER + 1;
RETURN lpad(m1_lat::TEXT, 2, '0')
|| lpad(m1_lng::TEXT, 2, '0')
|| m2_lat::TEXT || m2_lng::TEXT
|| m3_lat::TEXT || m3_lng::TEXT
|| m4::TEXT || m5::TEXT;
END;
$$ LANGUAGE plpgsql IMMUTABLE STRICT;これにより、任意の座標からメッシュデータを直接引ける:
SELECT env_scores, hazard_flags
FROM mesh_metrics
WHERE mesh_code = lat_lng_to_mesh_250m(35.6812, 139.7671);階層集計
メッシュコードの前方一致を利用すると、異なるスケールの集計が簡単に実現できます。
-- 1km メッシュ単位(3次メッシュ)で人口を集計
SELECT
left(mesh_code, 8) AS mesh_1km,
SUM(population) AS total_pop,
AVG((env_scores->>'convenience')::REAL) AS avg_convenience
FROM mesh_metrics
WHERE mesh_code LIKE '5339%' -- 東京周辺の1次メッシュ
GROUP BY left(mesh_code, 8);都市環境スコアリングのアーキテクチャ
多層データ統合
メッシュを空間集計の単位とし、複数のデータソースを統合するアーキテクチャを紹介します。
データソース別の結合パターン
| データ種別 | 結合方法 | 例 |
|---|---|---|
| 点データ(POI) | メッシュ中心点からの半径カウント | 保育園・病院・コンビニ |
| 面データ(ハザード) | ST_Intersects で重なり判定 |
浸水想定区域・土砂災害 |
| メッシュ統計 | コード直結合 | 国勢調査人口・世帯構成 |
| ラスタデータ | ST_Value でメッシュ中心値を抽出 |
地表面温度・標高 |
| ネットワーク | 外部エンジンで最寄り距離を計算 | 駅・IC への距離 |
百分位ランクによる正規化
異なる単位・スケールの指標を比較可能にするため、百分位ランク(percentile rank)を用います。
-- 各メッシュの指標を百分位ランクに変換
WITH ranked AS (
SELECT
mesh_code,
(env_scores->>'convenience')::REAL AS convenience,
PERCENT_RANK() OVER (
ORDER BY (env_scores->>'convenience')::REAL
) AS pct_rank
FROM mesh_metrics
WHERE mesh_code LIKE '5339%'
)
SELECT mesh_code, convenience, round(pct_rank * 100) AS percentile
FROM ranked
WHERE mesh_code = '5339451234';利点:
- 「上位 20% に入る利便性」のような直感的な表現が可能
- 都道府県内・広域圏内など、比較母集団を切り替えることで相対評価のスケールを調整できる
- 外れ値に頑健(min-max 正規化と異なり極端な値の影響を受けない)
パフォーマンス最適化
プリコンピュート vs リアルタイム
メッシュベースのスコアリングでは、プリコンピュート(事前計算) が圧倒的に有利です。
| 戦略 | レスポンス | 用途 |
|---|---|---|
| プリコンピュート(PK直引き) | < 5ms | 変化の遅い指標(人口・災害・環境) |
| リアルタイム空間検索 | 30-80ms | ユーザー指定の半径・動的な条件 |
| 混合 | 10-30ms | KNN + プリコンピュート結合 |
メッシュの利点は、座標→コード変換が算術計算のみで完結するため、空間インデックスを経由せずにプリコンピュート結果を PK で直引きできる ことです。これは ST_Contains や ST_DWithin による空間検索よりも桁違いに高速です。
首都圏スケールでの実績
250m メッシュで首都圏(東京・神奈川・千葉・埼玉)をカバーする場合、約 117,000 メッシュが対象になります。
- メッシュテーブルのサイズ:約 50MB(インデックス含む)
- PK 直引きレスポンス:< 1ms
- 全メッシュスキャン(集計クエリ):< 500ms
- バッチ更新(全メッシュ再計算):数分〜十数分
このスケールであれば、PostgreSQL の単一テーブルで十分に実用的です。
応用例
1. 住環境総合スコア
複数指標の百分位ランクを重み付き平均して、メッシュ単位の「住みやすさスコア」を算出。
SELECT
mesh_code,
(
(env_scores->>'convenience')::REAL * 0.3 +
(env_scores->>'safety')::REAL * 0.25 +
(env_scores->>'childcare')::REAL * 0.2 +
(env_scores->>'transit')::REAL * 0.25
) AS livability_score
FROM mesh_metrics
ORDER BY livability_score DESC
LIMIT 100;2. エリア比較
メッシュコードの階層を利用して、市区町村レベルの比較を即座に生成。
-- 行政区画 × メッシュの空間結合で区別集計
SELECT
a.city_name,
AVG((m.env_scores->>'convenience')::REAL) AS avg_convenience,
AVG((m.env_scores->>'safety')::REAL) AS avg_safety,
COUNT(*) AS mesh_count
FROM mesh_metrics m
JOIN admin_boundary a ON ST_Contains(a.geom, m.geom)
WHERE a.prefecture = '東京都'
GROUP BY a.city_name
ORDER BY avg_convenience DESC;3. 時系列変化の検出
同じメッシュコードで異なる時点のデータを比較することで、都市の変化を定量的に追跡できます。メッシュコードは座標から一意に決まるため、時系列での突合が容易です。
実装上の注意点
メッシュ境界の扱い
メッシュの境界線上にある点は、どちらのメッシュに帰属させるかの一貫したルールが必要です。JIS 規格では「北端・東端は含まない(左下閉・右上開)」が原則ですが、floor 関数で変換する場合はこの挙動が自然に実現されます。
海域・離島の除外
250m メッシュを全国生成すると海域メッシュが大量に発生します。陸域のみを対象とする場合は、海岸線ポリゴンとの ST_Intersects でフィルタリングするか、人口 > 0 のメッシュのみを扱うことで回避できます。
データソースの更新頻度
| データ | 更新周期 | 対応 |
|---|---|---|
| 国勢調査 | 5年 | 次回データ公開時にバッチ更新 |
| ハザードマップ | 不定期 | 公開通知時に手動トリガー |
| POI | 月次〜年次 | 定期バッチ + 差分更新 |
| 衛星データ | 日次〜月次 | 自動パイプライン |
まとめ
日本の標準地域メッシュは、50 年の運用実績を持つ空間集計の国家規格です。PostGIS と組み合わせることで:
- 座標→メッシュコード変換が算術計算のみで完結し、PK 直引きで < 5ms のレスポンスを実現
- 階層的なコード体系により、250m から 80km まで同一のデータ構造でマルチスケール分析が可能
- 公的統計との直接結合により、人口・災害・環境データを統一フレームワークで統合
不動産テック、都市計画、ロケーションインテリジェンスなどの空間分析プロダクトにおいて、メッシュベースのアーキテクチャは「事前計算 + PK 直引き」による高速レスポンスと、階層集計の柔軟性を両立する強力な選択肢です。
この記事の内容は、筆者が GIS ベースの不動産分析サービスを開発する過程で得た知見に基づいています。
