はじめに
Flutter webでGoogleマップ上のクラスタリングしようと思うと、google_maps_cluster_managerやgoogle_maps_cluster_manager_2を使うことになるかなと思います(ネイティブ系はGoogle Mapか何かでカバーされているため)。
ただ、このクラスタリングがデフォルトでメッシュベースのため、どう頑張ってもやや不自然なクラスタリングをします。
どうにかならないかなーと内部実装を見ていたら、距離ベースでいい感じにクラスタリングしてくれる隠し機能?公式Docsに載っていないクラスタリングアルゴリズムがあったので紹介します。
ちなみに私はgoogle_maps_cluster_manager_2で確認していますが、フォーク元のgoogle_maps_cluster_managerも同じ可能性があります。
デフォルトアルゴリズム、GeoHashの問題点
デフォルトのアルゴリズム(GeoHash)は非常に高速ですが、メッシュ状に領域を区切って計算するため、「すぐ近くにピンがあるのに、境界線をまたいでいるせいで別々のクラスターになってしまう」という不自然な挙動が発生することがあります。

↑本当は近くにある左の二つのPlaceが一つのクラスターになって欲しい!
見つけたアルゴリズム、maxDistとは?
公式Docsには載っていないのに、実装されていたのが maxDist アルゴリズムです。
maxDist は、座標間の純粋な「距離」に基づいてクラスタリングを行うアルゴリズムです。
そのため、メッシュ境界線に縛られず、見た目に「自然な」クラスタリングができますが、GeoHashよりも計算負荷が高い(アイテム数が多いと重くなる)くなりがちです。
使い方はClusterManager の引数に以下を追加するだけで切り替え可能です。
final clusterManager = ClusterManager<Place>(
_items,
_updateMarkers,
markerBuilder: _markerBuilder,
// アルゴリズムを明記(デフォルトは geoHash)
clusterAlgorithm: ClusterAlgorithm.maxDist,
// 距離のしきい値を指定(デフォルトは 20)
maxDistParams: MaxDistParams(44.0),
);
注意点
① 自動フォールバック機能
可視範囲のアイテムが 200件以上 になると、maxDist を指定していても自動的に高速な geoHash に切り替わります。「設定したのに挙動が変わらない?」と思ったら、この閾値を疑ってください。(maxItemsForMaxDistAlgo 引数でこの200件という値は調整可能です)
② 同一座標の扱い
maxDist の場合、全く同じ座標にあるマーカー同士はクラスタ化されないという挙動があります。重なりが気になる場合は注意が必要です。
③ Web版でのズームレベル
Web環境では zoomLevel は整数値として扱われます。
アルゴリズム比較まとめ
| 特徴 | GeoHash (デフォルト) | maxDist |
|---|---|---|
| 計算量 | $O(n)$ 〜 $O(n^2)$ | $O(n^2)$ 〜 $O(n^3)$ |
| 見た目 | 自然じゃないことがある(メッシュ境界で割れる) | 自然な集まり |
| 得意なケース | 大量(数千〜)のデータ | 精度重視の少量データ |
隠し機能?なのでご使用の際は自己責任でお願いいたします。
ご参考になれば幸いです!