地図APIを取り払って世界地図を自分で描いた
カウチコロンブスは、はじめGoogle Maps JavaScript APIの上に作られていました。いまは地図を自分で 描いています。なぜ取り払い、何に置き換えたのかを書いておきます。
取り払った理由
Maps JavaScript APIで地図を一度表示することは、ページ読み込みごとに課金される項目 です。 月の無料枠があり、個人プロジェクトがそれを超えることは滅多にありませんが、問題は上限ではなく 構造のほうでした。このサービスで地図が果たしている仕事が、それだけの重さを正当化できていなかった のです。
必要だったのは、この程度でした。
- 世界の陸の塊を暗い色で塗る
- 国境線を細く引く
- 訪れた国だけ別の色で塗る
- その上に経路の曲線と飛行機の形を載せる
道路も、衛星写真も、リアルタイム交通も、拡大して見る店舗情報も使いません。それなのに、そのために 数百KBのSDKを受け取り、タイルを要求し、ページ読み込みごとに課金カウンターを上げていました。 おまけに大圏距離の計算のためにgeometryライブラリを呼ぶので、旅程を作るたびにMaps JSを丸ごと 受け取る必要がありました。
何に置き換えたか
図形 はNatural Earthを使います。パブリックドメインで、TopoJSONに圧縮された形が公開されて います。このプロジェクトが使う二つはこうです。
| ファイル | 原本 | gzip |
|---|---|---|
countries-110m.json(低解像度) |
105KB | 38KB |
countries-50m.json(高解像度) |
739KB | 225KB |
低解像度を先に描いて地図がすぐ出るようにし、高解像度は後ろで受け取って静かに差し替えます。 はじめから高解像度だけを受け取ると、遅い回線ではしばらく空白の画面が残ります。
投影 と 経路の計算 は d3-geo が担います。正距円筒図法で緯度経度を画面座標へ移し、
geoPath でキャンバスに直接引きます。大圏距離と補間も同じライブラリで行います。Maps JSはいま、
内蔵辞書にない場所を検索するときだけ読み込まれます。
描画 はキャンバス2Dです。SVGで国のポリゴンを数百個DOMに載せると、アニメーションのフレーム
ごとにそれを再計算することになります。キャンバスは一度に全部引いて、一度に消します。高解像度の
画面のために devicePixelRatio を掛けますが、2で打ち切ります。3倍で描くとピクセル数が2.25倍に
増えるのに、目では区別がつきません。
移すときに引っかかったこと
陸を塗るときに mesh を使ってはいけません。 TopoJSONには merge と mesh があり、mesh は
線(MultiLineString)を返します。見た目には国の形がすべて出るので正しそうに見えるのですが、その線を
fill すると、大陸を横切るくさび形のしみができます。塗りつぶしには、国々を一つの面に統合する
merge を使わねばなりません。mesh は国境線を引くときだけ使います。
正距円筒図法は高緯度を横に引き伸ばします。 伸びる比率は緯度のコサインの逆数です。
| 緯度 | 横の伸び |
|---|---|
| 0°(赤道) | 1.00倍 |
| 37.57°(ソウル) | 1.26倍 |
| 60° | 2.00倍 |
| 80° | 5.76倍 |
だからソウルからヨーロッパへ向かう大圏経路が、地図の上で大きく上へふくらんで見えます。線が 曲がったのではなく、地図がその場所を引き伸ばしたのです。
残った費用
地図そのものは、いまや無料です。有料で残っているのは目的地の検索だけで、それも366か所の内蔵辞書と 34,000か所の静的インデックスを先に探したあとで、ようやく外部APIへ渡ります。ほとんどの検索は そこまで行きません。
地図APIが悪いという話ではありません。経路案内やリアルタイム交通が必要なら、自分で作る理由は ありません。ただ、必要なものが「暗い背景に大陸の形」であるなら、公開された図形と20行ほどの キャンバスのコードで十分です。