#はじめに
Google Maps Platformを利用してGoogle Mapsに複数のマーカーと情報ウィンドウを表示する方法をまとめました。
テーマは「javascript初心者によるjavascript初心者のための解説」です。丁寧すぎたかも。
#前提
Maps JavaScript API と Geocoding API のAPI KEYを取得している前提で進めます。
未取得の方は下記などを参考にしてみてください。
##バージョン
ruby 2.6.3
Rails 6.0.3.3
※Rails5以前と6でJavaScript周りの仕様が変わったらしいので、Rails5の方はお気をつけください。
#成果物
以下のように、画面内に複数のマーカーがあり、マーカーをクリックすると店名を表示するGoogle Mapsを作成しました。
#コード
<div id='map' class="googlemaps"></div>
<%= javascript_pack_tag "googlemaps/application" %>
<script src="https://maps.googleapis.com/maps/api/js?key=<%= ENV["GOOGLEMAPS_API_KEY"]%>&callback=initMap&libraries=places&v=weekly" async defer ></script>
#map {
width: 600px;
height: 300px;
}
window.initMap = () => {}
window.onload = function() {
const addresses = [...document.querySelectorAll("#mapAddress")].map((node) => node.textContent);
const shopNames = [...document.querySelectorAll("#shop_name_js")].map((node) => node.textContent);
let data = shopNames.map(function(name, index) {
return {
name: name,
address: addresses[index]
}
})
const googleMapElement = document.getElementById('map');
const infowindow = new google.maps.InfoWindow();
if (addresses.length > 0) {
window.initMap = mappingPinToGoogleMap(addresses, googleMapElement)
}
function mappingPinToGoogleMap(addresses, googleMapElement) {
const googleMap = new google.maps.Map(googleMapElement, {
zoom: 16,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
const bounds = new google.maps.LatLngBounds();
data.forEach((data) => {
const geocoder = new google.maps.Geocoder();
geocoder.geocode({ 'address': data.address, 'region': 'jp' }, (result, status) => {
if(status == google.maps.GeocoderStatus.OK) {
const lat = result[0].geometry.location.lat();
const lng = result[0].geometry.location.lng();
const latlng = {lat,lng};
const marker = new google.maps.Marker({
position: new google.maps.LatLng(latlng),
map: googleMap,
});
if (addresses.length > 1) {
bounds.extend(marker.position);
googleMap.fitBounds(bounds,20);
const infowindow = new google.maps.InfoWindow({
content: data.name,
});
marker.addListener("click", () => {
infowindow.open(googleMap, marker);
});
} else {
googleMap.setCenter(latlng);
}
}
})
})
}
}
#解説
##GoogleMapsを表示
const googleMapElement = document.getElementById('map');
function mappingPinToGoogleMap(addresses, googleMapElement) {
const googleMap = new google.maps.Map(googleMapElement, {
zoom: 16,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
}
- 住所の配列とgoogleMapのHTML要素を受け取ってマッピングします。
- 参考:Maps JavaScript API(Google Maps Platform)
- mapTypeId: google.maps.MapTypeId.ROADMAP => マップタイプを指定します。ROADMAPは道路や建物などが表示される標準的な地図です。以下の公式にそのほかのタイプも記載されています。
- Map Types(Google Maps Platform)
##住所が存在するか判定してマップを表示
保守性とページの読み込み速度を保つため、住所があるページのみでGoogleMapsを表示します。
// 住所が存在するか判定してマップを表示
if (addresses.length > 0) {
window.initMap = mappingPinToGoogleMap(addresses, googleMapElement)
}
##要素を取得
ページ上の店名と住所をそれぞれ配列で取得 => 店名と住所をハッシュ化
住所に対応した情報ウィンドウを出すために、店名と住所を紐付けます。
// 住所を取得
const addresses = [...document.querySelectorAll("#mapAddress")].map((node) => node.textContent);
// 店名を取得
const shopNames = [...document.querySelectorAll("#shop_name_js")].map((node) => node.textContent);
// 取得した住所と店名をハッシュ化
let data = shopNames.map(function(name, index) {
return {
name: name,
address: addresses[index]
}
})
- document.querySelectorAll("#mapAddress") => 複数の要素を取得
- 参考 : querySelectorAll(MDN web docs)
- .textContent => テキストのみを取得
- 参考 : .textContent(MDN web docs)
- .map((node) => node.textContent); => 取得した全ての要素からテキストを呼び出して新しい配列を作成
- 参考 : .map(MDN web docs)
- [… ] => NodeListをArray(配列)に変換
- 参考 :[… ](hitode909の日記)
##住所から座標を取得(Geocoding/ジオコーディング)
マーカーを表示するためには、住所を緯度・経度に変換する必要があります。
//ジオコードオブジェクトを定義
const geocoder = new google.maps.Geocoder();
geocoder.geocode({ 'address': data.address, 'region': 'jp' }, (result, status) => {
if(status == google.maps.GeocoderStatus.OK) {
const lat = result[0].geometry.location.lat();
const lng = result[0].geometry.location.lng();
const latlng = {lat,lng};
})
- geocoder.geocode(){(... => リクエストを送信します。コールバック関数の引数resultsとstatusを利用して処理します。
- 参考:Geocoding API(Google Maps Platform)
- status == google.maps.GeocoderStatus.OK => ジオコーディングが機能している場合のみ{}内の処理をします。
- 参考:ジオコーディング(マッピィ)
- result[0].geometry.location => latitude(緯度)とlongitude(経度)を取得します。
- 参考:latLngオブジェクトからlat lngをとりだす。(空飛ぶヒヤヤッコ)
マーカーを立てる場所の指定
取得したLatlang(緯度・経度)の地点にマーカーを表示します。
const marker = new google.maps.Marker({
position: new google.maps.LatLng(latlng),
map: googleMap,
});
- google.maps.Marker({... => Markerオブジェクトを作成します。プロパティはpositionのみ必須です。そのほかは下記などを参考にしてください。
- Markers(Google Maps Platform)
- マーカー(マッピィ)
##複数のマーカーを画面内に表示
// bounds(地図の境界)を定義
const bounds = new google.maps.LatLngBounds();
bounds.extend(marker.position);
googleMap.fitBounds(bounds,20);
- bounds.extend(marker.position) => 地図表示領域をマーカー位置に合わせて拡大
- 参考:LatLngBounds.extend()(syncer)
- googleMap.fitBounds(bounds,20) => マーカーが画面内に収まるように調整。第二引数は省略可。大きい数字:広域、小さい数字:詳細 な地図になります。
- 参考:Map.fitBounds()(syncer)
##特定のマーカーのみを表示
googleMap.setCenter(latlng);
- .setCenter => 特定のマーカーを中心に表示します。複数のマーカーを画面内に表示するメソッドを使用すると、表示が詳細に寄ってしまい調整できなかったので処理を分けました。
- 参考:Map.setCenter()(Syncer)
##情報ウィンドウ(infowindow)を表示
// infowindowを定義
const infowindow = new google.maps.InfoWindow();
// infoWindowの表示設定
const infowindow = new google.maps.InfoWindow({
content: data.name,
});
marker.addListener("click", () => {
infowindow.open(googleMap, marker);
});
- content: data.name => 表示内容を指定できます。そのほか、maxWidth(infowindowの幅)などを指定できます。下記公式に載っていますので必要に応じて参考にしてください。
- marker.addListener("click", () => { ... => クリックするとinfowindowを表示します。
- 参考:MediaQueryList.addListener()(MDN web docs)
#おわりに
誤字脱字やコードのアドバイスなどありましたらコメントいただけると嬉しいです。
Google Maps Platformは公式ドキュメントが充実していて、サンプルコードもたくさんあるので参考にしてみてください。