【Google MAP】クリックによる多地点間の距離を取得する(ルート考慮)  

# はじめに

前回、以下の投稿をしました。

https://qiita.com/yoshi_yast/items/1cc5cc4ca1ef74de6170

今回は続きで、徒歩や自動車などの移動手段をもとにルートを考慮して、地点間の距離を求めてみたいと思います。参考にさせていただいたURLのほとんど載っていましたが、勉強のためカスタマイズさせながら作成したいと思います（感謝）

実際の移動に際しての距離とおおよその時間がわかるようになります。
もちろん、普通にGoogle MAPのサイトで経路検索をやればできますが、より簡素にできるようにするといった目的です。（車は検索時の渋滞情報も出してくれたり、複数候補を出してくれたりするので、より詳しい情報はそちらのほうが断然よいとは思いますが）

# 作成するもの
- クリックにより指定した(最大10カ所まで可能)多地点間の距離を求めます。
- 徒歩・自転車・車の場合、それぞれの距離と時間を表示します。
- いつでもボタン押下でクリア可能とします。

# プログラム
[作成したデモサイト](https://anotherskyjp.site/devapps/googlemapdev/calc_route_distance.html)

![Screenshot_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167772/fc5187fc-2690-f7a5-d94e-f18d67b0077f.png)

#### ポイント
The Google Directions APIは、指定した出発地点から目的地点までのルート検索ができるサービスで、交通手段は4種類あります。
- 徒歩：google.maps.DirectionsTravelMode.WALKING
- 自転車：google.maps.DirectionsTravelMode.BICYCLING
- 自動車：google.maps.DirectionsTravelMode.DRIVING
- 電車：google.maps.DirectionsTravelMode.TRANSIT


https://developers.google.com/maps/documentation/javascript/directions


今回は、徒歩・自転車・自動車を使用します。トラベルモードを以下のように設定します。

~~~ sample.js
switch (travelMode) {
  case "walking":
    request.travelMode = google.maps.DirectionsTravelMode.WALKING;
    break;
  case "cycling":
    request.travelMode = google.maps.DirectionsTravelMode.BICYCLING;
    break;
  case "driving":
    request.travelMode = google.maps.DirectionsTravelMode.DRIVING;
    break;
}
~~~

# コード
<details><summary>全体のコード</summary>
<div>

~~~ googlemap_sample.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <title>Google Maps Distance</title>
    <meta charset="utf-8">
    <style>
      #title {
        font-size:20px;
        margin:0 auto;
      }
      #map {
        position:absolute;
        top:80px; right:8px; bottom:5px; left:8px;
        width:auto;
        height:auto;
      }
    </style>
  </head>

    <body>
      <div id="title">
        ＜クリック点連結ルートと距離を求める＞
        <span>
          <label><input type="radio" name="mode" value="walking" onchange="calcResult()" checked>徒歩</label>
          <label><input type="radio" name="mode" value="cycling" onchange="calcResult()">自転車</label>
          <label><input type="radio" name="mode" value="driving" onchange="calcResult()">自動車</label>
        </span>
        <br>
        <span>【距離】</span><span id="distance"></span><span>  【時間】</span><span id="duration"></span>
        <span><button id="clear">クリア</button></span>
      </div>
      <div id="map"></div>

      <script src="https://maps.googleapis.com/maps/api/js?language=ja&region=JP&key=AIzaSyBTNc9rXA17QLC0AMKQICSak9bmSLPGBZQ&callback=initMap" async defer></script>

      <script>

        var elevationService = null;
        var directionsService = null;
        var markerPts = [];
        var polyline = null;
        var elevations = null;
        var SAMPLES = 256;

        function initMap() {

          // マップ初期表示
          map = new google.maps.Map(document.getElementById("map"), {
            center: {lat: 35.681167, lng: 139.767052},
            zoom: 10,
            scaleControl: true
          });

          // サービス設定
          elevationService = new google.maps.ElevationService();
          directionsService = new google.maps.DirectionsService();

          // マップクリック時イベント
          google.maps.event.addListener(map, 'click', function(e) {
            addMarker(e.latLng);
          });

          // 結果クリアボタン押下時イベント
          document.getElementById('clear').addEventListener('click', function() {
            reset();
          });

        }

        function addMarker(latlng) {

          // 地点は10箇所までクリック可能
          if (markerPts.length < 10) {

            // マーカーのセット
            var markerPt = new google.maps.Marker({
              position: latlng,
              draggable: true,
              label: String(markerPts.length),
              map: map
            })

            markerPts.push(markerPt);

            // 距離計算
            calcResult();

          } else {
            alert("マーカーセットは10カ所まで可能です");
          }

        };

        // 距離計算
        function calcResult() {

          // マーカーが1よりも大きい場合
          if (markerPts.length > 1) {

            var mode = document.getElementsByName("mode");
            var travelMode = "";

            for (var i = 0; i < mode.length; i++) {
              if (mode[i].checked){
                travelMode = mode[i].value;
              }
            }

            // request値を設定
            var origin = markerPts[0].getPosition();
            var destination = markerPts[markerPts.length - 1].getPosition();
            var waypoints = [];
            for (var i = 1; i < markerPts.length - 1; i++) {
              waypoints.push({
                location: markerPts[i].getPosition(),
                stopover: true
              });
            }

            var request = {
              origin: origin,
              destination: destination,
              waypoints: waypoints
            };

            // トラベルモードの設定
            switch (travelMode) {
              case "walking":
                request.travelMode = google.maps.DirectionsTravelMode.WALKING;
                break;
              case "cycling":
                request.travelMode = google.maps.DirectionsTravelMode.BICYCLING;
                break;
              case "driving":
                request.travelMode = google.maps.DirectionsTravelMode.DRIVING;
                break;
              }

            directionsService.route(request, function(response, status) {

              if (status == google.maps.DirectionsStatus.OK) {

                var dist = 0;
                var duration = 0;

                // 総距離・総時間の計算
                for (var i = 0; i < markerPts.length - 1; i++) {
                  dist += response.routes[0].legs[i].distance.value / 1000;
                  duration += response.routes[0].legs[i].duration.value / 60;
                }

                // 総距離・総時間の表示
                document.getElementById('distance').innerHTML = dist.toFixed(1) + " km、";
                document.getElementById('duration').innerHTML = duration.toFixed(1) + " 分";

                var path2 =[];
                path2 = response.routes[0].overview_path;
                elevationService.getElevationAlongPath({path: path2, samples: SAMPLES}, plotElevation);

              } else if (status == google.maps.DirectionsStatus.ZERO_RESULTS) {
                alert("Could not find a route between these points");
              } else {
                alert("Directions request failed");
              }

            });

          }

        };

        // ルート線の描画
        function plotElevation(results) {

          elevations = results;

          var path = [];

          for (var i = 0; i < results.length; i++) {
            path.push(elevations[i].location);
          }

          if (polyline) {
            polyline.setMap(null);
          }

          polyline = new google.maps.Polyline({
            path: path,
            map: map,
            strokeColor: "red",
            strokeOpacity: 0.7,
            strokeWeight: 3
          });

        };

        // 結果クリアボタン押下時イベント
        function reset() {

          if (polyline) {
            polyline.setMap(null);
          }

          for (var i in markerPts) {
            markerPts[i].setMap(null);
          }

          markerPts = [];

          document.getElementById('distance').innerHTML = "";
          document.getElementById('duration').innerHTML = "";

        };

      </script>

    </body>
  </html>
~~~

</div></details>

# まとめ
- ルート(徒歩や自動車など)を考慮した多地点間の距離を求めて表示することができました。
- APIを活用すると交通手段もいい感じに考慮してくれるなんて便利すぎる、という感想しかありません。

# 参考URL

https://kurage.ready.jp/w_map/map-gv3/d-route.html

https://phpjavascriptroom.com/?t=ajax&p=googlemapsapiv3_directions