4
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Google Mapsに複数のマーカーと情報ウィンドウを表示

Last updated at Posted at 2020-12-03

#はじめに
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を作成しました。

スクリーンショット 2020-12-03 1.58.29.png

#コード

googlemaps.html.erb
<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>

googlemaps.css
#map {
  width: 600px;
  height: 300px;
}
googlemaps.js

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]
    }
  })

##住所から座標を取得(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};
  })

マーカーを立てる場所の指定

取得したLatlang(緯度・経度)の地点にマーカーを表示します。


  const marker = new google.maps.Marker({
    position: new google.maps.LatLng(latlng),
    map: googleMap,
  });   

##複数のマーカーを画面内に表示


  // 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);
  });

#おわりに

誤字脱字やコードのアドバイスなどありましたらコメントいただけると嬉しいです。

Google Maps Platformは公式ドキュメントが充実していて、サンプルコードもたくさんあるので参考にしてみてください。

4
6
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
4
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?