Mapbox から Google Maps API へのリプレイスで気になった３選

## 背景

入社当時は地図コンポーネントが [Google Maps API](https://developers.google.com/maps/documentation/javascript/) と [Mapbox.js](https://www.mapbox.com/mapbox.js/api/)([Leaflet](http://leafletjs.com/reference-1.2.0.html)) が両方存在していたのを、色々あって Google に一本化する時に「え〜！この機能無いの〜？そんな〜」って気持ちになった３選を紹介します。

### こんな人向け

- OSS の地図ライブラリが好きで頑なに Google Maps API を触ってこなかった**ひねくれ者**

ということで、かなり地味な差で恐縮ですが、３選を紹介していきます。

## (1) `Map.fitBounds()` が fit しない

`fitBounds()` というのは地図コンテナを作るための [`google.maps.Map`](https://developers.google.com/maps/documentation/javascript/3.exp/reference#Map) クラスが持つメソッドで、引数に範囲を指定して、その範囲が収まるように地図を **fit** するための機能です。 

```js
// bounds (範囲) を作って
const southWest = new google.maps.LatLng(minLat, minlng);
const northEast = new google.maps.LatLng(maxLat, maxLngs);
const bounds = new google.maps.LatLngBounds(southWest, northEast);

// 作った範囲に地図を fit させる
map.fitBounds(bounds);
```

これが見出しにあるように、fit しないんですね。
しないというより、やや引き気味な印象。
一般的に Web 地図は表示できる縮尺がズームレベルごとに決まってるので、そもそも完全に fit することはほぼありえません。
ただ、それでもなんか体感より引き過ぎてる感。

これはバグでも何でもなく**仕様らしいです**。

Google マップは地図上にズームボタンとか検索ボックスとかのコントロールが配置されてますが、これに重ならないように fit してくれるっぽいです。~~ご丁寧に、余計なことを。~~

わかりやすい例としてサンプル貼っておきます。

- [Google の場合](https://jsfiddle.net/qw4xftnL/1/)
- [Mapbox の場合](https://jsfiddle.net/tvwkgo34/40/)

頑張って対処してる記事があったのでこれも貼っておきます。

- [Google Maps JavaScript API V3のfitBounds()が全然フィットしてくれない件](http://ofsilvers.hateblo.jp/entry/google-maps-fitbounds-does-not-fit)

お疲れ様です。


## (2) `InfoWindow` に `close` イベントが存在しない

`InfoWindow` というのはクリックしたマーカーの属性情報を表示するためのポップアップのクラス ([`google.maps.InfoWindow`](https://developers.google.com/maps/documentation/javascript/3.exp/reference#InfoWindow)) です。

通常は地図上のマーカーをクリックすると出てくるのが基本ですが、テーブルと連動させて、選択した行に対応するマーカーのポップアップを開いたり閉じたりする、あるいはその逆みたいなのをやりたいことあると思います。
ポップアップの x ボタンか地図をクリックすると閉じますが、そのときにテーブルの選択状態も解除したい。
当たり前ですが、これを実現するには `InfoWindow` が閉じたことを知るためのイベントが必要です。
しかし、Google Maps API のリファレンスをどう舐め回しても `close` イベントがない！？
ちなみに `closeclick` イベントはあるんです。

```js
// 地図をクリックしたらポップアップが閉じたことをハンドルする関数を実行
infoWindow.addListener('closeclick', handleCloseInfoWindow);
map.addListener('click', handleCloseInfoWindow);
```

ポップアップはポップアップ上の x ボタンか地図をクリックすると閉じます。
なので、こういうつまらないコードを書く必要があるんですね。お疲れ様です。

## (3) マーカーとポップアップの疎結合感

Mapbox.js だと、情報を表示したいマーカーとポップアップがコード上で結合できて直感的な気がします。`Marker.bindPopup()` すれば、マーカーをクリックした時にポップアップを呼び出す処理が書けます。

```js
marker.bindPopup(/* ポップアップに表示する DOM 文字列 */);
```

一方、Google はマーカーのクリックイベント拾ってポップアップを呼び出す処理をコールバック関数に書きます。

```js
marker.addListener('click', () => {
  infoWindow.setContent(/* ポップアップに表示する DOM 文字列 */);
  infoWindow.open(map, marker);
});
```

これは好みもありそうですが、もう少し言うと Mapbox.js はチェーンメソッドで書けるので、マーカーに対して行いたい処理をこんな感じにまとめられると言うところもあります。

```js
marker.bindPopup(/* ポップアップに表示する DOM 文字列 */)
  .on('popupopen', toggleSelect) // ポップアップが開いた時のイベントハンドラー
  .on('popupclose', toggleSelect) // ポップアップが閉じた時のイベントハンドラー
  .openPopup() // 初期表示時にポップアップを開く
  .addTo(map); // マーカーを地図に追加
```

ここまで来ると好みだな。。

## 終わりに

といった感じで、Web 地図ライブラリって基本的には概念はそっくりで似たり寄ったりなんですが、地味に欲しい機能があったりなかったりします。
逆に Google Maps API でよかった！と思ったのは、ポリラインのようなベクターも Canvas でレンダリングしてくれるので、頂点数が増えても操作パフォーマンスが落ちにくいところはさすがと思ったり。
ストリートビュー使えるのは言うまでもないですし。
こればかりは一長一短ですので、細かいことには目をつぶって乗り切りましょう。

来年は全部忘れて [deck.gl](https://uber.github.io/deck.gl/#/) やりたいです。

明日は [takanorip](https://qiita.com/takanorip) がさいきょうのLP開発環境を教えてくれるらしいです。個人として即採用したいくらい期待しておこう。