Googleマップ表示とマーカークラスタリングと情報ウインドウからのページ遷移
単一ファイルコンポーネントでGoogleマップを表示して、複数マーカーをまとめて表示(マーカークラスタリング)したうえで、情報ウインドウ内から非同期でページ遷移する(vue-router)
Vueの単一ファイルコンポーネントでGoogleMapsAPIを利用した地図を表示します。
npmに vue2-google-maps
というのがあるので、単純な地図ならこれでよいのですが
今回は
- 複数のマーカーをまとめて表示(マーカークラスタリング)をしたい
- vue-routerを使っていてシングルページアプリケーションとして作成
- マップ上の情報ウインドウ(infoWindow)の中からのリンクも非同期にページ遷移させたかった。
等々ありましたので、自前でコンポーネントを作成しました。
それについてのメモです。
環境
- サーバサイド:Laravel5.5
- フロントエンド:Vue.js
- 単一ファイルコンポーネントのコンパイル:Laravel Mix
準備
GoogleMapの読み込み
<script src="https://maps.googleapis.com/maps/api/js?key=[API_KEY]"></script>
いつものGoogleMapAPIのやつです。
マーカークラスタリング
マーカーを沢山表示するときに、近くのマーカーが重なって見えにくくなってしまうのを回避する手段です。
詳しくは
MarkerClusterer
npm からインストール
$ npm i js-marker-clusterer
ライブラリを読み込んでおきます。
app.js
import MarkerClusterer from 'js-marker-clusterer';
前提条件
- GoogleマップのinfoWindow(マーカーから出る吹き出しみたいなやつ)から、リンクを貼って、詳細ページへ遷移させたい。
- 詳細ページのURLは
/999
みたいな詳細を表すID
vue-routerを使って事前にルーティング
app.js
const router = new VueRouter({
mode: 'history',
routes: [
{
path: '/',
name:'home',
component: require('./components/Home.vue')
},
{
path: '/:post_id(\\d+)',
name: 'description',
component: require('./components/Description.vue')
}
// ↑こういう感じでIDをURLにした詳細ページ的なのをつくると仮定して進めます
]
});
こんな感じでルーティングしています。
コンポーネントファイル
Map.vue
<template lang="html">
<div>
<!-- テンプレートは単純にマップだけ -->
<div id="map"></div>
</div>
</template>
<script>
export default {
data: function () {
return {
mapName: "map", //mapを紐付ける要素のID
map: null, //GoogleMapのインスタンスを入れる変数
markers: [], //マーカー群を入れる配列
// サンプルデータです。指している場所には意味はありません。
// ロケーション(緯度・軽度)と、id、タイトルです。
locations: [
{
location:{lat: 35.681, lng: 139.761},
id:1,
title:"たいとる1"
},
{
location:{lat: 35.682, lng: 139.762},
id:2,
title:"たいとる2"
},
{
location:{lat: 35.683, lng: 139.763},
id:3,
title:"たいとる3"
},
{
location:{lat: 35.684, lng: 139.764},
id:4,
title:"たいとる4"
},
{
location:{lat: 35.685, lng: 139.765},
id:5,
title:"たいとる5"
}
],
}
},
methods: {
// マップを表示するメソッド
initMap: function () {
var my_this = this; //thisを別名で退避しておく(あとでつかうので)
// 中心データ
var center_lat = 35.6810409;
var center_lng = 139.7670516;
var zoom = 16;
// マップを初期化
this.map = new google.maps.Map(document.getElementById(this.mapName), {
center: {lat:center_lat, lng: center_lng},
zoom: zoom
});
// this.locations をループしてマーカー群を作成
this.markers = this.locations.map( function(data, i) {
var marker = new google.maps.Marker({
position: data.location,
});
// 情報ウインドウ(infoWindow)を作成 idとデータのID番号を設定しておく
var infowindow = new google.maps.InfoWindow({
content: '<div id="infobox_' + data.id + '" data-item-id="'+ data.id +'">' + data.title + '</div>',
maxWidth: 300
});
// 情報ウインドウ(infoWindow)にイベントを設定
// 情報ウインドウにclickイベントが設置できなさそうなので、読み込み時に、中身にクリックイベントを設置する
google.maps.event.addListener(infowindow, 'domready', function(){
//infoWindowが持っている内部文字列をDOMに変換
var dumy_dom = document.createElement('div');//ダミーの要素を作成
dumy_dom.innerHTML = this.content;//ダミー要素にinfoWindow内のHTML文字列を読み込ませてDOMに変換
// 内部のDOMを取得
var id_name = dumy_dom.children[0].id; //infoWindow内の要素のID名を取得
var inner_dom = document.getElementById(id_name); //ID名からinfoWindow内の要素オブジェクトを取得
// アイテムのid番号を取得
var item_id = inner_dom.dataset.itemId; //同じくリンク先用の番号を取得(data-item-idの値)
// infoWindow内の要素にクリックイベントを設置
inner_dom.addEventListener('click', function(){
my_this.go(item_id);
});
});
// infoWindowをマーカーに紐付け
marker.addListener('click', function() {
infowindow.open(this.map, marker);
});
return marker;
});
// マーカークラスタ設定
var markerCluster = new MarkerClusterer(this.map, this.markers,
{imagePath: '/images/markerclusterer/m'} // マーカークラスタ画像のパス(予めDLしておく)
);
},
// Vue-routeをつかって非同期にページ遷移させるメソッド
go: function(item_id){
this.$router.push({ name: 'description', params: { post_id: item_id }});
}
},
mounted: function () {
this.initMap();
}
}
</script>
<style lang="css">
#map {
width:100%;
height:400px;
}
</style>
ポイント(というか詰まったところ)
- infoWindow内の要素からリンクさせたかったのですが、infoWindowのcontentで、vue-routerを使う方法がわかりませんでした。なので、infoWindowの読み込みイベントから、内部DOMを取得してクリックイベントを設定する。という強引な方法で実現しました。
- たぶん、もっとスマートなVueっぽい方法があるのではないかと思案中。
- マーカークラスタ用の画像については、下記からダウンロードできますが、画像ファイルなだけなので、自作するとよいかも。
- そこまでしてSPAにこだわらなくても・・というのは言わないで。
## 参考
- Google Maps API マーカークラスタリング
- Google Maps API infoWindow
- github:js-marker-clusterer ※マーカークラスタの画像はここから入手できます。
- npm js-marker-clusterer ※npm
- vue-router