Enludus
お問い合わせ

GPS が届かない地下で道を案内する、梅田地下ナビを作っています

梅田の地下街専用の道案内 PWA、梅田地下ナビを作っている途中の記録です。現在地の決め方、地図を真上から見た平面に描き直した理由、案内中の一人称の画面、LP Anatomy から当てたことを書きます。

梅田地下ナビは、梅田の地下街と駅の連絡通路だけを対象にした道案内のアプリです。ブラウザで開ける PWA で、React と Vite で作っています。umeda.enludus.com で公開していますが、まだ作っている途中です。地図のデータは現地で確かめておらず、スマホの実機での確認もこれからです。

この記事では、何をするアプリなのか、どう案内するのか、最近入れた地図と案内画面の変更について書きます。

何をするアプリか

地下では GPS がほとんど当てになりません。梅田地下ナビは、GPS が使えない前提 で作っています。

そのかわりに、入った出入口や、近くに見えるお店から、現在地を一度だけ推定 します。そこから先は、目的地までの経路を区間ごとに案内します。歩いているあいだに位置を追いかけることはしません。ずれたと思ったら「迷った」を押して、もう一度推定し直します。

位置情報が使える場合は、起動時に一度だけ大まかな位置を地図に出します。これも単発で、追跡はしません。

使い方

  1. 上の検索欄に、店名・出口番号(例: H-8)・地上の目印を入れる。地図のピンや建物を押してもよい
  2. 「ここへ行く」を押す。現在地が決まっていなければ、現在地を確かめるシートが開く
  3. よく使う目印、入った出入口、近くに見えるお店(店名で検索)を選んで「現在地を推定する」→「この位置で案内開始」
  4. 「次へ / 前へ」で次の動きを確かめながら進む。着いたら「終了」
現在地を確かめるシート。入った出入口の選択、近くに見えるお店の検索欄、「現在地を推定する」のボタンの下に、推定した位置と確度、「この位置で案内開始」のボタンが出ている
現在地を推定したところ。よく使う目印を選んで「現在地を推定する」を押すと、推定位置と「この位置で案内開始」が出る

案内中は「階段を避ける」と「音声案内」を切り替えられます。音声はブラウザ標準の Web Speech API で読み上げ、対応していないブラウザではボタンを出しません。

地下の経路が、地上をまっすぐ歩くよりかなり遠回りになる場合は、Google マップの徒歩ルートへのリンクを出します。目安は「地下の経路が直線距離の 1.6 倍を超え、直線でも 150m 以上ある」ときです。カードには「日差しや雨を避けたいときは地下のまま」という注記も添えています。

ほかに、こんなことに対応しています。

  • 地下 2 階・地下 1 階・地上 1 階を切り替えて表示する。案内中は、いまのステップの階に合わせる
  • 夜間に閉まる通路や設備は、その時間には経路に使わない。23:00〜6:00 は「閉まっている通路があるかもしれない」と注意を出す
  • 日本語・英語・簡体字中国語・韓国語の画面(地名・店名は日本語と英語のデータしかないので、中国語・韓国語では英語表記)
  • よく行く場所をお気に入りにする

夜間の通路は、時間が分かっているものがまだ少なく、今はエスカレーターとエレベーターの一部だけです。

端末の中で完結させる

地図はアプリにまとめて入れておき、経路探索(Dijkstra 法)も現在地の推定も、すべて端末の中で計算 します。地下の電波の状態に左右されないようにするためです。PWA として Service Worker でキャッシュもしています。

位置情報・お気に入り・言語の設定は端末の中だけで使い、どこにも送りません。

地図を真上から見た平面にした

最初の地図は、斜め上から見下ろす立体の表示(アイソメトリック)でした。ところが、この描き方だと地図が 45 度回って縦につぶれます。北がどちらか、地上のどの通りと対応しているかが分かりにくい という問題がありました。

そこで、真上から見た平面(北が上) に描き直しました(PR #27)。

  • 地下街の床の形を、OpenStreetMap の地下街の形から取って、地下街ごとの色で塗る
  • 地上の道路と建物の輪郭を、下地として薄く重ねる
  • 通路は立体の板をやめて、実際の幅に近い太さの線にする
  • 地上の目印は立方体をやめて、平らな目印にする

地上の下地を重ねたのは、いまどの通りの下にいるかを分かるようにするため です。

北が上の平面の地図。ベージュの地上の道路と建物の上に、地下街の床が地下街ごとの色(緑・橙など)で塗られ、通路が青い線で描かれている。改札や建物はアイコンで出ている
北が上の平面の地図。地上の道路と建物を下地に、地下街の床を地下街ごとの色で塗っている(広場のあたり)

下地は、通路のまわり 150m の範囲を取り出しています。建物 2,294・道路 501・地下街 4 つ分の形があり、線を間引いてから 1 つのファイルにまとめました。要素が多いので、種類ごとに 1 本のパスにまとめて描いています。その分、ビルドした JS は 666KB(gzip で 194KB)に増えました。

地図は外部の地図タイルを使わず、アプリの中で SVG として描いています。

大事な目印を先に出す

平面にしてみると、別の問題が目につきました。出口番号・分岐・店名が同じ強さで並んで、目的地・改札・広場のような大事な目印が埋もれていた のです。梅田の地下には出口がとても多く、ラベルが重なったときに出口番号をいちばん優先していたので、画面が出口番号のバッジで埋まっていました。

そこで、重なったときの優先順位を次のように変えました(PR #28)。

広場 > 改札 > 地下街の名前 > 案内中の沿道の店名 > 出口番号 > 建物名 > 店名 > 設備

あわせて、細かい情報は必要なときだけ 出すようにしました。

  • 中くらいの拡大では、出口番号を出すのは経路の上と、選んだ場所・現在地・目的地から 60m 以内のものだけ。ほかは点にする
  • 設備のチップも、中くらいの拡大では経路の上だけ
  • いちばん拡大したときに、すべての出口番号・店の点・設備を出す

これで、中くらいの拡大でも広場や改札、建物が読み取れるようになりました。その後、建物・広場・改札は文字からアイコンに変え(改札は路線の色の札)、地図の文字をさらに減らしています。

中くらいの拡大の地図。広場と地下街の名前、改札の路線名のバッジ、建物やお店のアイコンが出ていて、出口番号は点になっている
中くらいの拡大。広場・改札・地下街の名前を優先し、建物はアイコン、出口番号は点にしている

案内中は一人称を主役に

案内が始まると、地図と検索欄を隠して、画面いっぱいに一人称の通路の絵 を出します。進む向きを正面に、経路に沿って 100m 先までの床・壁・天井を透視投影で描くもので、曲がり角の先も見えます。床には経路の青い帯、曲がる所には矢印が出ます。壁には店ごとの店先(種類の色の看板)、出口は黄色の看板、脇道は開口で描いています。

左右は、地図データの位置から計算しています。進む向きに対して左か右かを判定し、ほぼ真正面(1.5m 未満)のものは「道沿い」とします。通路の絵は写真ではなく、左右の並びを見せるための模式図 です。

最初の版では、一人称の表示は画面の上の一部だけで、絵の下に左右の店・出口・脇道の一覧を文字で並べていました。PR #29 と #30 で、今の 画面いっぱいの主役の表示 にしています。

  • 左上に小さな地図(132px)を出す。いまの区間の起点を中心に、進む向きが上になるように回して 描く
  • 小さな地図には、まわりの通路、経路(薄い青)、いまの区間(濃い青)、目的地、現在地の矢印を描く
  • 小さな地図を押すと、北が上の全体の地図に切り替わる
  • 小さな地図の右に、次の動き(「77m 左へ」など)を矢印と一緒に大きく出す
  • 下の道案内のシートに「1 / 5」のようなステップ番号と「次へ / 前へ」がある。シートは画面の 30% の高さで開き、通路の絵を隠さない
  • 左右の店の文字の一覧は画面から外し、読み上げ用にだけ残している
案内中の一人称の画面。左上に進む向きが上の小さな地図、その右に左折の矢印と「77m 左へ」、画面いっぱいに通路の絵があり、床の青い帯と曲がる所の矢印、左右の壁に店の看板(種類のアイコンつき)が並び、下に道案内のシート
案内中は通路の絵が画面いっぱいに出る。左上の小さな地図は進む向きが上

全体の地図は北が上、案内中の小さな地図は進む向きが上です。左右の判定は地図データの店の位置に頼っているので、データの位置がずれていると左右も違ってしまうことがあります。

LP Anatomy から当てたこと

私は別のリポジトリで、LP Anatomy という学習ノートをつけています。良いサイトをソースから読んで、型を品質基準に書き足していくものです。そこで得た基準を、このアプリにも 4 つ当てました(PR #17〜#20)。

  • ホバーの色はマウスのときだけ。 :hover を @media (hover: hover) and (pointer: fine) の中に入れました。スマホで検索結果を押したあとに、ホバーの色が残らなくなります。「動きを減らす」設定のときは、推定位置のカードへのスクロールもアニメーションなしにしました
  • 色を CSS 変数にまとめる。 経路の青と選択の黄が直書きされていたので、--route や --select にまとめました。値は変えていないので、変更前後のスクリーンショットはピクセル単位で一致しています
  • データセーバーのときは常に動くものを止める。 このアプリで常に動いているのは、選んだ場所の目印の点滅だけです。navigator.connection.saveData がオンなら、それを止めます
  • 数字の桁幅をそろえる。 「次へ」を押すたびに距離やステップ番号が変わるので、font-variant-numeric: tabular-nums を指定しました。変更前は「111」と「888」で幅が違っていたのが、同じ幅になりました

LP のための基準ですが、スマホで使う道案内にもそのまま当てはまるものがありました。

地図データの出典

通路・出口の番号と位置・店・地上の目印の位置は、OpenStreetMap(2026-10-20 に抽出)から作っています。最初は手作業で作った骨格のデータでしたが、OpenStreetMap と重ねると 100m 以上ずれる所が多かったので、OpenStreetMap から生成する形に切り替えました。生成のスクリプトはリポジトリの scripts/osm/ にあります。

  • 地図データ(src/data/generated/)は OpenStreetMap の派生データなので ODbL 1.0 で提供しています。アプリのメニューにも帰属表示を出しています
  • OpenStreetMap にない店の一部は、各地下街の公式サイトに載っている店名などの事実情報から入れています
  • 改札・広場・区画の名前と一部の位置は手作業で、根拠を scripts/osm/overlay.mjs に書いています
  • コードは MIT License です
  • 他のサイトの画像・地図タイル・ロゴは使っていません

データは OpenStreetMap の記載どおりで、現地で確かめてはいません。お店や出口の情報が実際と違っていたら、アプリの「メニュー → 間違いを報告する」から送ってもらえるようにしています。

これから

まだ作っている途中なので、やることが残っています。

  • 地図のデータを現地で確かめる(改札の細かい位置や、一部の通路の形は推定です)
  • スマホの実機で、タップ・移動・拡大縮小と、ホーム画面への追加を確かめる
  • OpenStreetMap に描かれていない地下の通路を現地で確かめ、OpenStreetMap に描き足してから地図を作り直す
  • 通路の開放時間を問い合わせて、夜間に閉まる通路を増やす

梅田の地下で迷いそうなときに、試しに開いてみてください。違っているところがあれば、報告してもらえると助かります。

更新(2026-10-25)

書いたあとに入れた変更です。上の「これから」から、README のスクリーンショットの撮り直しは外しました(地図・道案内・一人称の 3 枚に撮り直しています)。

  • 一人称の画面を、画面いっぱいの通路の絵にしました。 案内が始まると地図と検索欄を隠し、経路に沿って 100m 先までの床・壁・天井を透視投影で描きます。曲がり角の先も見え、床に経路の青い帯、曲がる所に矢印が出ます。壁には店ごとの店先(種類の色の看板とガラス)、出口は黄色の看板、脇道は開口で描いています。左右の文字の一覧は画面から外し、読み上げ用にだけ残しました(PR #30)。
  • 階の移動と店の位置。 床の高さを階ごとに 4.5m として描き、階段とエスカレーターは段の縞、エレベーターは短い上り下りで表します。「エスカレーターで B1 へ」のような札は 4 言語です。店は OpenStreetMap の位置を経路に落とした位置に、左右に分けて置いています(PR #35)。
  • いろいろな経路で確かめました。 実データの 160 組の出発地と目的地を、昼と深夜で、全 802 区間の一人称の絵を作って確かめるテストを足しました。そこで見つけた直しが 3 つあります。(1) 地図データの細かい折れで「1m 先で右 → 7m 先で左」のような案内が出ていたので、5m 未満で続く曲がりはまとめるか打ち消し、出発から 3m 未満の曲がりは案内しないようにしました。(2) 深夜に、地図と一人称とシートで経路が違うことがあったので、経路を決める場所を 1 か所にして同じ時刻を使います。(3) 一人称の下のシートが、地上の注意で押し下げられていました。
  • 駅として行き先を選べるように。 「大阪駅」で検索すると駅前のビルとその店が並び、JR の改札が上位に出ませんでした。そこで 7 つの駅(JR 大阪・阪急・阪神・御堂筋線梅田・東梅田・西梅田・JR 北新地)を名前から見つけて検索の先頭に「駅」として出し、いまいる所から経路がいちばん短い改札へ案内します。たとえば西梅田からの「大阪駅」は JR 大阪駅の桜橋口になります(PR #36)。
  • 見た目も整えました。 地図の文字を減らして建物・広場・改札をアイコンにし(改札は路線の色の札)、地図の色を暖かくしました。文字の大きさは 6 段のトークンにまとめ、シートとカードは「温かい飲食」の型(紙の色 2 色・枠線なしの茶の影・焦げ茶の文字)にそろえています。数字と英字には、Fredoka を英数字だけに絞った 7.7KB の丸い字を使っています。
  • 共有カードを設定しました。 OGP と X の大きな画像カードを設定しています。

この記事を書いた時点で「これから」に残っていた、地図データの現地確認とスマホ実機での確認は、まだです。