GoogleMapのMarkerClustererを最適化する

[前回の続き](https://qiita.com/n739dev/items/2a2eba4d442cc919f8db)で、
MarkerClustererをnewした状態からです
公式ドキュメントのdemoよりなんだか動作が遅いので
表示範囲外のMarkerを非表示処理するのに時間がかかったのでメモです

## 作業環境
* Nuxt.js (v2.15.8)
* `@googlemaps/js-api-loader` (v1.15.2) を使用
* `@googlemaps/markerclusterer` (v2.1.3)　を使用

## MarkerClustererをnewします
前回の記事から違うのは、
`algorithm`のオプションに `maxZoom: 12` を追加しました
この設定は、GoogleMapのzoomが12より大きくなった時は、Cluster化しないという設定です

:whale: デフォルトは `16` みたいです
https://github.com/googlemaps/js-markerclusterer/blob/b4cb8f3/src/algorithms/core.ts#L60

```js
this.markerCluster = new MarkerClusterer({ 
    map: this.map,
    // markers: this.markers,
    algorithm: new GridAlgorithm({ maxZoom: 12, maxDistance: 4000 }),
    renderer: {
        render: ({ count, position }) =>
            new google.maps.Marker({
                icon: {
                    // アイコン画像を使う場合はここにその情報を書く
                },
                label: {
                    text: String(count),
                },
                position,
                // 他のマーカーの上になるよう調整
                zIndex: Number(google.maps.Marker.MAX_ZINDEX) + count,
        }),
    },
});
```
`markers: this.markers,` をコメントアウトしているのは
後に画面外のピンを非表示するからです

## 表示外のMarkerを消す処理を書く
`bounds` が `undefind` になる事があったため、エラー回避のため、if文をかまします
```js
updateDisplayVisibleMarker() {
    const map = this.map;
    const markers = this.markers;
    const bounds = map.getBounds();
    const zoom = map.getZoom();
    const setMaxZoom = 12; // `algorithm`のオプションに設定した値です、今回はしてないけど変数化しとくとGood
    if (bounds) {
        if (zoom < setMaxZoom) {
            // クラスター化している場合
            this.markerCluster.clearMarkers();
            const visibleMarkers = markers.filter(marker =>
                marker.getVisible() && bounds.contains(marker.getPosition())
            )
            this.markerCluster.addMarkers(visibleMarkers);
        } else {
            // クラスター化していない場合
            for (let i = 0; i < markers.length; i++) {
                if (bounds.contains(markers[i].getPosition())) {
                    // 表示内マーカーを表示
                    markers[i].setMap(map);
                } else {
                    // 表示外マーカーを非表示
                    markers[i].setMap(null);
                }
            }
        }
    }
}
```

## 表示外のMarkerを消す処理を実行する
1) `new MarkerClusterer()`した後
2) GoogleMapのzoom値が変更された時
3) GoogleMapをドラッグ移動終了時

GoogleMapイベントは、②と③の2つのタイミングで実行すればOKかなと思いますが、
タイル読み込み完了時にも入れておくと安心
理由は `updateDisplayMarker()` 内で、`map.getBounds()` が `undefind` になるタイミングがあるため…
丁寧に書くなら、`map.getBounds()` が `undefind` になったかどうかのフラグ管理をすれば、
タイル読み込み完了時に再度重複処理が省けるかと（今回はその記述は書いてない）

本当は、GoogleMap表示完了のイベントがあればいいんですが、
見つからなかったので（良い方法あれば教えて…… :rolling_eyes:

```js
// zoom表示変更
this.map.addListener('zoom_changed', () => {
    this.updateDisplayVisibleMarker()
});

// ドラッグ移動時
this.map.addListener('dragend', () => {
    this.updateDisplayVisibleMarker()
});

// タイル読み込み完了時（1回のみ)
google.maps.event.addListenerOnce(this.map, 'tilesloaded', () => {
    this.updateDisplayVisibleMarker()
});
```

## あれ？なんか前回、表示外の処理勝手にしてくれるって喜んでなかった？ｗ
Markerにクリックイベントを追加したりしていたら
なんだか表示処理遅いし、表示範囲外もMarkerが刺さってるな:rage:
という事があり、色々と見まくっていたら `GridAlgorithm` でこんな処理見つけたんですな

https://github.com/googlemaps/js-markerclusterer/blob/b4cb8f3df6ceb2c6462ef0cda41e37786ac50115/src/algorithms/grid.ts#L76-L83
```js
if (map.getZoom() >= this.maxZoom) {
  return {
    clusters: this.noop({
      markers,
    }),
    changed,
  };
}
```
ん？これって、maxZoom値が現在のzoom値より大きいと、Markerはnoopするって処理やん
※noopってのはCluster化せず全部表示するって処理っぽい
:whale: ドキュメント的には[このアルゴリズム](https://googlemaps.github.io/js-markerclusterer/classes/NoopAlgorithm.html)

ほほう、どおりで地図zoomしたらなんか重たいな！って思うわけだわ
結局自分で表示範囲外のMarker処理書かないかんのかい・・！です

## 参考（コピペ）したサイト
本当、助かりマンモス
https://zenn.dev/wintyo/articles/dfe08d919f2ce7
https://www.mugo.ca/Blog/Making-your-custom-embedded-Google-Maps-more-efficient-with-marker-clusters

## まとめ
頼むぜGoogleMapさん、ちゃんと表示外は消すんだったら、
Clusterしてない状態もその処理してよん :head_bandage: