はじめに
真夏の東京を歩くと、「最短」より「少し遠回りしても日陰が多い道」の方が楽なことがあります。
韓国にはその発想のアプリ(그늘로 / 그늘길)があります。今回はその考え方を参考にしつつ、日本のオープンデータだけで渋谷区パイロットを作りました。
いま動いているもの:
- 時刻を指定すると、建物の影が地図に出る
- 出発・到着をタップすると、最短ルートと日陰が多いルートを比較できる
- 距離・所要時間・日陰% が見える
この記事では「何を作ったか」より、どのデータで、どう影と経路を計算したかを中心に書きます。
画面イメージ
図1. 2026-08-14 12:00 の建物影。PLATEAUの高さから計算した影を国土地理院地図の上に重ねている。日時を変えると影の向き・長さが変わる。
図2. 地図をタップして出発を指定した直後。「出発」ピンが立ち、次に到着地点の入力を促す。
図3. 12:00 の経路比較。青が最短(315 m・日陰 39%)、茶が日陰優先(316 m・日陰 80%)。1 m の遠回りで日陰割合が大きく上がる例。
図4. 同じ2点を 16:00 に再計算した画面。影が長くなり、UI上は「同じルートです」と出る(最短と日陰が一致)。
図5. 22:00 の夜間表示。「夜間」バッジと「夜間のため日陰ルートはありません」の案内。最短ルートのみを返す。

図6. 8月17日 の渋谷駅から神神宮前までをルート検索した時のルート結果
図7. 給水スポットを追加!
いま作っているもの
| 項目 | 内容 |
|---|---|
| 対象エリア | 渋谷区 |
| 移動手段 | 徒歩 |
| 影 | 建物の影(PLATEAU LOD1) |
| 経路 | 最短 / 日陰優先の2本 |
| UI | 日本語 Web(MapLibre + 国土地理院タイル) |
まずは 建物の影 × 歩道グラフ に絞って動かしています。街路樹や公共交通などは、あとから足していく想定です。
データ選定
影付きルートに必要なものは、ざっくり次の3つです。
- 建物の位置と高さ → 影ポリゴン
- 歩ける道のネットワーク → 経路探索
- その時刻の太陽の向き → 影の長さと方向
今回使っているのは次のとおりです。
使っているデータ
| データ | 用途 |
|---|---|
| Project PLATEAU(渋谷区・建築物 LOD1) | 建物ポリゴン + measuredHeight
|
| OpenStreetMap(歩行ネットワーク) | 区全域の歩道グラフ |
| 太陽位置(ライブラリでローカル計算) | altitude / azimuth |
| 国土地理院 標準地図タイル | 背景地図 |
太陽位置は「公共のリアルタイム API」ではなく、日時と代表座標からアプリ側で計算します。安定・低コストです。
これから足したいデータ
| データ | 想定する使い方 |
|---|---|
| ほこナビDP(歩行空間ネットワーク) | 品質の良い歩行 Node/Link。いまは駅周辺などの部分公開なので、区全域の OSM に駅周辺だけマージする候補 |
| 東京都 街路樹 | 木陰の追加。樹冠の大きさ・高さが揃えば影ポリゴンに使える |
| Cool Share / Cooling Shelter / 公園 | 経路まわりの休憩・暑さ対策スポット表示 |
| GSI DEM | 坂・高低差を経路コストに反映 |
要点: 多くの「公共 API」は 経路を返す API ではなく、ダウンロード型のオープンデータです。カタログ API はファイル一覧用で、影ルート計算そのものではありません。
アルゴリズム
処理の流れは一本道です。
日時
→ 太陽の高度・方位
→ 各建物の影ポリゴン
→ 各歩道エッジの「日陰長さ / 日なた長さ」
→ ダイクストラ
・最短: 重み = 長さ
・日陰: 重み = D_shade + 3 × D_sun
1. 太陽
- 代表点: 渋谷付近(例: 139.7016, 35.6580)
- 高度 > 0 → 昼、≤ 0 → 夜
- 方位: 北 0°・時計回りに統一(ライブラリの規約は変換)
2. 建物の影
仮定(いまの実装):
- 建物はフットプリントを高さ
Hまで押し上げた柱 - 太陽光は平行光
- 地面は水平(坂・自己遮蔽の精密計算はこれから)
影の長さ:
L = H / tan(altitude)
低高度で L が発散しないよう、高度 5° 未満は tan(5°) でキャップします。
各建物について、フットプリントを太陽の反対方向に L だけずらした凸包を影とします。高さ 2 m 未満は除外。
3. エッジの日陰長さ
歩道エッジ e について:
D_shade = 影と交差する部分の長さ(近似可)
D_sun = max(0, length(e) - D_shade)
実装では、厳密な線×ポリゴン交差だと夕方(影が長い時間帯)に交差ペアが爆発して応答が遅くなりました。
そのためエッジを 5 m 間隔のサンプル点で打ち、点が影内かどうかで D_shade を近似しています(エッジあたり誤差 ≤ 5 m 程度)。
4. 経路の重み
W_short(e) = length(e)
W_shade(e) = D_shade(e) + 3 × D_sun(e)
α = 3 は固定です。「日なた 1 m ≈ 日陰 3 m」くらいの好みです。
ハードキャップではなく、結果として日陰ルートが最短の 1.5 倍を超えたら UI で警告するだけです。
夜は影を計算せず、最短のみ返します。
5. スナップと範囲
- タップ点 → 最寄りグラフノード(75 m 超は失敗)
- 直線 3 km 超は拒否
- 渋谷区外は拒否
アーキテクチャ
[ブラウザ]
React + Vite + MapLibre
国土地理院タイル
│ JSON
▼
[API]
FastAPI
・/boundary
・/shadows?datetime=&bbox=
・/routes
│
▼
[前処理済みデータ]
shibuya-buildings.geojson
shibuya-walk-graph.json
shibuya-boundary.geojson
ランタイムで CityGML や PBF は読みません。PLATEAU / OSM は バッチ前処理で GeoJSON / JSON にしておきます。
実データの規模感(渋谷区):
- 建物: 約 4.1 万棟
- 歩道: 約 1 万ノード / 1.5 万エッジ
全区の影を毎リクエストで union すると数十秒〜タイムアウトになります。対策は次のとおりです。
| 対策 | 内容 |
|---|---|
| 経路探索の範囲 | 最短ルート周辺 + 余裕(迂回想定)にグラフを絞る |
| 影の生成範囲 | その bbox + 影の長さ分だけ建物を選ぶ |
| 画面の影 |
/shadows に地図の bbox を渡し、表示用だけ計算・キャッシュ |
| エッジ日陰 | 5 m サンプリング + STRtree |
クライアントは localhost / 127.0.0.1 のどちらでも叩けるよう CORS を許可しています。
実装で詰まったところ
CityGML の座標順
PLATEAU CityGML の posList が lat, lon 順で入っているケースがあり、素朴に x,y と読むと建物が 0 件になります。東京付近の数値範囲で順序を判定して入れ替える処理が必要でした。
「全部の建物」は無理
最初は「渋谷全区の影を毎回作る」実装でした。実データでは /routes がブラウザの Failed to fetch になります(サーバが重い・応答が返らない)。
全区を正確にやるより、経路に効く範囲だけ正確にやる方が、この規模では現実的でした。
MapLibre のマーカー
カスタム要素に className = "pin" を上書きすると、MapLibre が付ける位置用クラスが消えて、ラベルが画面幅いっぱいに伸びるバグになりました。
マーカーのルート要素とラベル用 div は分けるのが安全です。
動作確認
次のような観点で、ローカルで動かして確認しています。
- 区外・遠すぎる2点・道から遠い点は日本語で拒否される
- 昼は建物影が出て、10:00 と 16:00 で向き・長さが違う
- 昼は最短と日陰の2経路(同じでもよい)、夜は最短のみ
- 出典(国土地理院 / PLATEAU / OSM)が表示される
- 公開 Nominatim / OSRM デモは叩かない
例(2026-08-14 12:00 JST、ハチ公付近 → 代々木公園東側):
- 最短: 約 1430 m、日陰 0%
- 日陰: 約 1814 m、日陰 42%
同じ日の 16:00 では影が長くなり、短い区間でも日陰%が大きく上がりやすいです。
自動テストは API(pytest)と Web(Vitest)も通しています。
まとめと今後
いまわかったこと
- 「影ルート」の核は 高さ付き建物 + 歩道グラフ + 太陽 の3点で足りる
- 日本の公共データは揃っているが、全区をカバーするかで採用が分かれる(ほこナビは品質◎・カバー△)
- 幾何計算は正しさだけでなく、都市スケールでの計算量がボトルネックになる
これからやりたいこと
- スマホブラウザでも使いやすい UI(モバイル対応)
- 渋谷区だけでなく、東京全域で使えるようにエリアを広げる
- ほこナビを駅周辺だけ OSM にマージ
- 街路樹・公園 / Cool Share のオーバーレイ
- DEM で坂をコストに入れる
- 場所検索、地下街、鉄道・バスなど移動手段の拡張
コードと仕様(要件・アルゴリズム・受け入れ)はリポジトリにまとめてあります。
- GitHub: https://github.com/jungyeounjae/hikage-navi
- データ・計算の定義:
docs/04-data-algorithm.md - ローカル起動の概要: README
- デモ版:https://hikage-navi.vercel.app/





