ミライトアドベントカレンダー 8日目の記事です。
(忙しくて記事書く時間を取れず21日に書いてます...泣)
7日目は もずますさん の ターミナルでメモ管理 (Neovim, nb, zeno.zsh) という記事でした。
ちょっとした趣味で地図を使ったアプリを作ろうとしたのですが、Google Mapsだと請求アカウントの用意で手が止まったり、予期せぬアクセスで大量課金発生しないかでちょっと気軽に使えるものがないか探すとMap Tilerというものを見つけました。
(Google MapsもAPI制限かければ、請求にビビらなくて大丈夫だったのを後で知りました)
Google Maps も MapTiler もそれぞれあまり使ったことなかったので比較してみることにしました。
ざっくり比較
- Google Maps
- すぐ使える(SDK/APIs が充実)
- ズーム・ピン・ルート描画など UI コンポーネント完備
- データが最新で高品質(POI/住所精度)
- API 利用量に応じて高額になる可能性
- MapTiler
- タイルを自前でホスト可能(オンプレ/プライベート用途)
- ビジュアルを柔軟に変更できるスタイル
- UI 部分は別ライブラリ(Leaflet / MapLibre GL などと組み合わせる必要あり)
- Google に比べればコストは抑えやすい
標準的な地図表示だけであればMapTiler、
検索・ナビ・住所正規化を含めたい場合はGoogle Maps。
ルーティング/ナビ機能 (MapTiler Directions) はあるが、Google Maps と比較すると情報量と精度は落ちる
POI(店とかスポット検索)はやはりGoogle Mapsが強いので、総合的に地図サービスを使いたい場合は開発費や運用費用を考えるとGoogle Mapsが良いのかも。
シンプルな地図表示やデモアプリ作るとかだったらMapTilerが良いかも。
比較用サンプルアプリ
Google Maps と MapTiler を切り替えて表示する簡単なサンプルアプリを作りました。
Vite + TypeScript でフレームワークなし。
プロバイダ切り替え、住所検索、クリックでマーカー追加、初期表示は東京駅。
実装は src/map/*Provider.ts と src/ui/controls.ts と src/config.ts に寄せています。
ソースコードとデモ
GitHub: https://github.com/ucan-lab/map-compare
Vercel デモ: https://map-compare.vercel.app/
動かし方
cd map-compare
npm install
cat > .env <<'EOF'
VITE_GOOGLE_MAPS_API_KEY=your_google_key
VITE_MAPTILER_API_KEY=your_maptiler_key
EOF
npm run dev
上部のボタンで Google / MapTiler を切り替えて検索とクリックを試せます。
実装の設計
src/map/MapProvider.ts で共通インターフェースを定義。UI 層はここだけ触ります。
切り替え時は destroy() を必ず呼び、リスナーとマーカーを破棄します。
export interface MapProvider {
init(container: HTMLElement): Promise<void>
setCenter(lat: number, lng: number, zoom?: number): void
addMarker(lat: number, lng: number): void
geocode(address: string): Promise<{ lat: number; lng: number }>
destroy(): void
}
検索は geocode → setCenter → addMarker で固定した。
Google Maps 実装メモ
src/map/GoogleMapsProvider.ts で <script> を動的挿入。既に読み込んでいたら Promise を再利用する。
ジオコーディングは new google.maps.Geocoder().geocode({ address }) の先頭だけ使う。
クリックは map.addListener('click', ...) で座標を取り、その場にマーカー追加する。
const script = document.createElement('script')
script.src = `https://maps.googleapis.com/maps/api/js?key=${encodeURIComponent(GOOGLE_MAPS_API_KEY)}`
script.async = true
script.defer = true
document.head.append(script)
破棄時は listener.remove() と event.clearInstanceListeners、マーカーは setMap(null)。
MapTiler 実装メモ
@maptiler/sdk を import して maptilersdk.config.apiKey にキーを入れるだけ。
初期化は new maptilersdk.Map({ style: MAPTILER_STYLE, center: [lng, lat] })。中心は [lng, lat]。
ジオコーディングは REST を叩き、features[0].center を抜く。
const url = new URL(`https://api.maptiler.com/geocoding/${encodeURIComponent(address)}.json`)
url.searchParams.set('key', MAPTILER_API_KEY)
url.searchParams.set('limit', '1')
const response = await fetch(url.toString())
クリックは map.on('click', handler) で受け取り、event.lngLat から座標を取りマーカー追加する。
触ってみた感想
Google は POI(Point of Interest) の当たりが強い。住所検索の精度で困らなかった。
MapTiler は読み込みが軽い。スタイルを変える手数が少ない。
さいごに
POI 依存の案件は Google を選ぶ。見た目と軽さを取りたい案件は MapTiler。
今回のように両方試したいときは Provider パターンを使って実装すると切り替えが楽でした。
楽でしたって言ってもサンプルアプリはAIにお願いしたんですけどね。
ミライトアドベントカレンダー 9日目は tkek321 の SOLID原則のS【単一責任の原則】 という記事です。