LoginSignup
2
2

GoogleMapのMarkerClustererを最適化する

Posted at

前回の続きで、
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

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を消す処理を書く

boundsundefind になる事があったため、エラー回避のため、if文をかまします

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:

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

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

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

あれ?なんか前回、表示外の処理勝手にしてくれるって喜んでなかった?w

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

if (map.getZoom() >= this.maxZoom) {
  return {
    clusters: this.noop({
      markers,
    }),
    changed,
  };
}

ん?これって、maxZoom値が現在のzoom値より大きいと、Markerはnoopするって処理やん
※noopってのはCluster化せず全部表示するって処理っぽい
:whale: ドキュメント的にはこのアルゴリズム

ほほう、どおりで地図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:

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2