エラー「MarkerClusterer is not defined」の対処 & MarkerClusterを新しいライブラリへ移行
GoogleMapsにMarkerClustererというライブラリを使ってクラスター表示を実装していたのですが、ある日クラスターが表示されていないことに気づきました。
気になってコンソールを覗くと、こんなエラーログが。
Uncaught (in promise) ReferenceError: MarkerClusterer is not defined
MarkerClustererが存在しない…?🤔
というわけで、この記事では上記エラーを解決するまでの道のりを備忘録がてらまとめていきます。
対象読者
- エラーについて調べてたら本記事に辿り着いた方
- MarkerClustererを使用している方
MarkerClustererとは
Google Maps Platformが提供しているGoogleMapsライブラリです。
このライブラリを使用すると、以下のようにGoogleMaps上に表示したマーカーをズームレベルに応じてクラスタリング(集約)表示することができます。
クラスター内部には、含まれるマーカーの数をカウントして表示してくれます。
エラーの原因
先に結論を言うと、「このライブラリは廃止したから新しいライブラリに移行してね!」 とのこと。
つまり、私のように廃止された古いライブラリを使っていると、本記事タイトルのようなエラーが発生します。
実際に古いライブラリを直接覗いてみると、あれこれの機能が綺麗さっぱりなくなっていて、たった一つのメッセージだけが残されてました。(以下URLからも本文見れます)
メッセージ本文
This version of the marker clusterer is no longer in service.
We recommend updating your code to use @googlemaps/markerclusterer instead.
More information is available at https://github.com/googlemaps/js-markerclusterer
and https://developers.google.com/maps/documentation/javascript/marker-clustering.
メッセージ本文を要約すると、「今度から@googlemaps/markerclusterer使ってね!」 と書いてました。
MarkerClustererの移行
そんなわけで、エラーを解消する為にはライブラリの移行作業が必要なのでやっていきます。
環境
- Laravel
- Vue2
ライブラリの切り替え
まずは最新のライブラリへと切り替えます。
今回、私の環境ではCDN(unpkg)経由で追加します。
npm経由でも追加できますが、今回は省略していますので、詳しい手順は以下を参考にしてみてください。
ライブラリをインポートしている箇所を以下のように変更します。
変更前
<script src="https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/markerclusterer.js"></script>
変更後
<script src="https://unpkg.com/@googlemaps/markerclusterer/dist/index.min.js"></script>
本実装を改修してクラスターを表示させる
ライブラリを切り替えたので、本実装部分を弄ります。
改修対象のコード
今回改修するjavascriptコードは以下の通りです。
const clusterStyles = [
{
textColor: 'white',
url: '/img/cluster-s.png',
height: 50,
width: 50
},
{
textColor: 'white',
url: '/img/cluster-m.png',
height: 53,
width: 53
},
{
textColor: 'white',
url: '/img/cluster-l.png',
height: 64,
width: 64
}
];
let markerCluster = new MarkerClusterer(
this.map,
this.markers,
{
styles: clusterStyles,
maxZoom: 20,
},
);
ただクラスターを生成するだけでなく、clusterStyles
にてオリジナルのクラスター画像を設定しています。
MarkerClustererを実装する
以下のように実装します。
サンプルとして位置情報は適当なものを使用しています。
// 適当な位置情報
const locations = [
{ lat: 35.681236, lng: 139.767125 }, // 東京駅
{ lat: 35.689634, lng: 139.700393 }, // 新宿駅
{ lat: 35.658034, lng: 139.701636 }, // 渋谷駅
{ lat: 35.728926, lng: 139.71038 }, // 池袋駅
{ lat: 35.628471, lng: 139.73876 }, // 品川駅
{ lat: 35.713768, lng: 139.777254 }, // 上野駅
{ lat: 35.671717, lng: 139.764857 }, // 銀座駅
];
// 位置情報をもとにマーカーを生成
let markers = [];
locations.forEach((location) => {
markers.push(new google.maps.Marker({
position: { lat: location.lat, lng: location.lng },
}));
});
// クラスターを生成
let markerCluster = new markerClusterer.MarkerClusterer({
map: this.map,
markers: markers,
});
実装にあたって、2つ注意点があります。
まず、以前のように位置パラメータとして引数が定義できなくなっています。
つまり、以下のような書き方はできません。
// これだとエラーになる
let markerCluster = new markerClusterer.MarkerClusterer(
this.map,
markers,
);
次に、CDN経由でMarkerClustererを使っている場合ですが、MarkerClustererを使うにはmarkerClusterer.MarkerClusterer
と書かないと使えません。
つまり、以下のような書き方はできません。
(ただし、npm経由でMarkerClustererを使っている場合、別途import文が必要ですが以下の書き方で実装できます)
// これだとエラーになる
let markerCluster = new markerClusterer({
map: this.map,
markers: markers,
});
とりあえずこれで、クラスターの実装自体はOKです。
この段階でGoogleMaps上にクラスターが表示されるはずです。
maxZoomとstylesを適用する
あとは、改修前に実装していたstylesとmaxZoomを組み込みます。
しかし、これが大穴でして、「以前と同じ実装でいけるだろ!」と思って実装しようとすると、失敗します。
ドキュメントを見てみると、そもそもstylesとmaxZoomを直接設定できる項目がなく、「どうやって設定すればいいんだ…?」となりました。
今までは直感的にstylesの定義ができていたのですが、最新のライブラリではできないみたいで、そういうissueもあがってました。
面倒ですが、stylesとmaxZoomを別の方法で適用してあげます。
maxZoomの設定
まずはmaxZoomを設定します。
以下ドキュメントによると、algorithmOptions
でmazZoomというパラメータが設定できます。
実際に書くとこんな感じ。
let markerCluster = new markerClusterer.MarkerClusterer({
map: this.map,
markers: this.markers,
algorithmOptions: {
maxZoom: 20,
},
});
stylesの設定
次にstylesを設定します。
新しいライブラリでは、renderer
に書いてあげれば適用できます。
google.maps.Marker
を用いてクラスターのデザインを適用します。
実際に書くとこんな感じ。
let markerCluster = new markerClusterer.MarkerClusterer({
map: this.map,
markers: markers,
algorithmOptions: {
maxZoom: 20,
},
renderer: {
render: ({count, position}) => {
return new google.maps.Marker({
position: position,
label: {
text: count.toString(),
color: 'white',
},
icon: {
url: '/img/cluster-l.png',
size: new google.maps.Size(64, 64),
},
});
},
},
});
これで、クラスターに任意の画像やstyleを設定することができます。
適用結果
こんな感じになりました。
クラスターには設定した画像が表示されています。
(ex)マーカーの数に応じてクラスターのアイコンを切り替える
ここからはオマケのコーナーです。
応用として、クラスターに内包するマーカーの数に応じてクラスターのアイコンを切り替える処理を実装してみました。
rendererの実装
const renderer = {
render: ({ count, position }) => {
return new google.maps.Marker({
position: position,
label: {
text: count.toString(),
color: 'white',
},
icon: this.getClusterIcon(count),
});
}
};
markerClusterの実装
let markerCluster = new markerClusterer.MarkerClusterer({
map: this.map,
markers: markers,
algorithmOptions: {
maxZoom: 20,
},
renderer: renderer,
});
マーカーの数に応じてクラスター画像を切り替える処理を実装
rendererから呼び出す実装です。
getClusterIcon(count) {
// マーカーの数に応じてクラスタアイコンを指定
if (count <= 2) {
return {
url: '/img/cluster-s.png',
size: new google.maps.Size(50, 50),
}
} else if (count <= 5) {
return {
url: '/img/cluster-m.png',
size: new google.maps.Size(53, 53),
}
} else {
return {
url: '/img/cluster-l.png',
size: new google.maps.Size(64, 64),
}
}
},
実装結果
GoogleMapsのマーカーの数に応じてクラスターの色を切り替えられるようになり、無事に改修完了です。
おわりに
ライブラリの切り替えだけで対応できると思っていましたが、実装も変えないとといけなかったり、ドキュメントの理解にも苦しんだり、と初見では色々と大変でした。
同じような事象で詰まっている方の参考になれば幸いです。
(クラスター表示で使ったMarkerも実は非推奨になってて、google.maps.marker.AdvancedMarkerElementってのに切り替えろ!って怒られてた…こっちもやらなきゃですね……うぅ…)
参考
Discussion