Googleマップ ルート検索レンダリングと検索ルート上の座標を取得ツールを作成しました（Googleマップ＋Maps JavaScript API）

# 目的
[Google Maps Platform](https://cloud.google.com/maps-platform/?authuser=1)の勉強のため、[Maps JavaScript API](https://developers.google.com/maps/documentation/javascript/tutorial?hl=ja)を用いて、Googleマップのルート検索レンダリングと検索ルート上の座標を取得するツールを作成してみました。

なぜ、このようなツールを作成したのかですが、[こちらの記事](https://qiita.com/wakusyan/items/f0666a7958b1ec6e86df)はGoogle交通情報を抽出するツールの記事になりますが、Googleマップの検索ルート上の交差点の座標を正確に入力する必要があったため、Googleマップの検索ルート上の座標を取得できればいいなあと思い、ツールを作成しました。

### [Maps JavaScript API] (https://developers.google.com/maps/documentation/javascript/tutorial?hl=ja)
- 今回は、Google Maps Platformの中でも[「マップ」](https://developers.google.com/maps/documentation/?authuser=1)に分類される[「Maps JavaScript API」](https://developers.google.com/maps/documentation/javascript/tutorial?hl=ja)を使用しています。
- [Maps JavaScript API](https://developers.google.com/maps/documentation/javascript/tutorial?hl=ja)は、ウェブサイトにインタラクティブな地図を追加します。独自のコンテンツと画像でマップをカスタマイズできます。

# 実行環境
この記事ではJavaScript、HTML5、CSSを使っています。
コードを実行するためにはGoogle Maps PlatformのAPIキーが必要です。

>※Google Maps Platform APIキーの取得・発行についてはこちらを参照してください。
https://www.zenrin-datacom.net/business/gmapsapi/api_key/index.html

# 前提条件

### マップの中心
Googleマップの中心は[東京駅（35.6812362,139.7649307）](https://www.google.com/maps/place/%E6%9D%B1%E4%BA%AC%E9%A7%85/@35.6812362,139.7649307,17z/data=!3m1!4b1!4m5!3m4!1s0x60188bfbd89f700b:0x277c49ba34ed38!8m2!3d35.6812362!4d139.7671248)になります。
※JavaScript内に記述していますので、適宜、変更が可能です（変更箇所は後述します）。

# 入力設定

### HTML
- 下記のソースをテキストエディタにコピー＆ペーストし、*.html形式で保存してください。
- 7行目にAPIキーを入力してください。

```html:qiita.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="initial-scale=1.0, user-scalable=no">
    <title>Googleマップ ルート検索レンダリング＆検索ルート上の座標の取得</title>
    <script src="http://maps.google.com/maps/api/js?key=APIキーを入力してください"></script>
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
    <script type="text/javascript" src="DirectionsService.js"></script>
    <link rel="stylesheet" href="stylesheet.css">
</head>
<body onload="initialize();">
    <div id="container">
        <h1>Googleマップ ルート検索レンダリング＆検索ルート上の座標の取得</h1>
        <p>テキストフィールドに起点と終点の座標を入力すると、ルート検索を行います。</p>
        <p>※座標を入れてください。(例：35.6895967,139.6982244)</p>
        <table>
            <tr><th>起点：</th><td><input type="text" name="start" id="start" size="20" maxlength="100"></td></tr>
            <tr><th>終点：</th><td><input type="text" name="end" id="end" size="20" maxlength="100"></td></tr>
        </table>
        <p>
            <input type="button" value="検索" onclick="searchRoute()">
            <input type="button" value="検索マーカーすべて非表示" onclick="invisibleMarkers()">
            <input type="button" value="検索マーカーすべて表示" onclick="visibleMarkers()">
        </p>
        <p>Googleマップをクリックし、マーカーを設置してください。</p>
        <p>
            <input type="button" value="マーカー削除" onclick="deleteMarkers()">
            <input type="button" value="座標出力" onclick="csvOutput()">
            <input type="button" value="座標初期化" onclick="csvDelete()">
        </p>
    </div>
    <div id="gmap">
        <div id="map_canvas" style="width:100%; height:640px;"></div>
        <div id="csv" style="width: 30%; height: 640px; border: 1px solid Gray; overflow: auto;"></div>
    </div>
    <div id="dpanel">
        <div id="directionsPanel" style="width:100%; height:640px;"></div>
    </div>
</body>
</html>
```

### CSS
- 下記のソースをテキストエディタにコピー＆ペーストし、*.css形式で保存してください。

```css:qiita.css
html { 
    height: 100%;
}
body { 
    height: 100%; 
    margin: 0; 
    padding: 0;
    background-color:#eee;
    font-family:
	  "ヒラギノ角ゴ Pro W3",
	   "Hiragino Kaku Gothic Pro", 
	   "メイリオ", 
	   Meiryo, 
	   Osaka, 
	   "ＭＳ Ｐゴシック", 
	   "MS PGothic", 
	   sans-serif;
}
#container {
    margin: 5px;
    padding-bottom: 5px;
}
#gmap {
    margin: 5px;
    padding-bottom: 5px;
    display: flex;
}
#dpanel {
    margin: 5px;
    padding-bottom: 5px;
}
h1 {
    font-size:1.2em;
    background-color:#8AA5E5;
    color:#fff;
    padding:10px;
}
#map_canvas { 
    height: 100%;
}
#directionsPanel {
    background-color:#fff;
}
```

### JavaScript
- 下記のソースをテキストエディタにコピー＆ペーストし、*.js形式で保存してください。
- 13行目にGoogleマップの中心座標を記述しています（適宜、変更してください）。
- ルート検索は、交通手段が自動車かつ対象道路が一般道を前提としているため、設定条件は以下のとおりになります（目的に合わせて、設定を変更してください）。

・自動車でのルート（48行目）
・単位km表示（49行目）
・最適化された最短距離にする（50行目）
・高速道路を除外（51行目）
・有料道路を除外（52行目）

```javascript:qiita.js
var map;
var marker;
var markers = [];
var infowindow;
var directionsDisplay;
var directionsService;
var pinImage_red = new google.maps.MarkerImage("http://maps.google.com/mapfiles/ms/icons/red-dot.png");
var pinImage_blue = new google.maps.MarkerImage("http://maps.google.com/mapfiles/ms/icons/blue-dot.png");
var MarkerArray = new google.maps.MVCArray();

//initialize関数
function initialize() {
    var latlng = new google.maps.LatLng(35.681382,139.766084);//東京駅
    var myOptions = {
        zoom:14,
        center:latlng,
        mapTypeId:google.maps.MapTypeId.ROADMAP
    };
    map = new google.maps.Map(document.getElementById("map_canvas"),myOptions);
    
    directionsDisplay = new google.maps.DirectionsRenderer({
        polylineOptions: {
            //strokeColor: '#FF0000',
            strokeColor: '#1e90ff',
            strokeWeight: 6,
            strokeOpacity: 0.6
        }
    });
    directionsDisplay.setMap(map);

    // createMarker関数の呼び出し
    createMarker();
}
//searchRoute関数
function searchRoute() {
    var start = document.getElementById("start").value;
    var end = document.getElementById("end").value;
    
    var rendererOptions = {
        draggable: true,
        preserveViewport:false
    };
    
    directionsService = new google.maps.DirectionsService();
    var request = {
        origin: start,
        destination: end,
        travelMode: google.maps.DirectionsTravelMode.DRIVING, // 自動車でのルート
        unitSystem: google.maps.DirectionsUnitSystem.METRIC, // 単位km表示
        optimizeWaypoints: true, // 最適化された最短距離にする
        avoidHighways: true, // 高速道路を除外
        avoidTolls: true // 有料道路を除外
    };
    directionsService.route(request, function(response, status){
        if (status == google.maps.DirectionsStatus.OK){
            directionsDisplay.setDirections(response);
            directionsDisplay.setPanel(document.getElementById("directionsPanel"));
        }
        // overview_pathを表示する
        for (var i = 0; i < response.routes.length; i++) {
            var r = response.routes[i];
            for (var j = 0; j < r.overview_path.length; j++) {
                var latlng = r.overview_path[j];
                marker = new google.maps.Marker({
                    position: latlng,
                    icon: pinImage_red,
                    map: map
                });
                markers.push(marker);
            }
        }
    });
    map = new google.maps.Map(document.getElementById("map_canvas"),response);
    directionsDisplay.setMap(map);
}
//createMarker関数
function createMarker() {
    // mapをクリックしたときのイベントを設定
    google.maps.event.addListener(map, 'click', maplistener);

    // mapをクリックしたときの処理
    function maplistener(event) {
        // marker作成
        marker = new google.maps.Marker({
            map: map,
            draggable: true, // ドラッグを可能にする
            icon: pinImage_blue
        });
        // markerの位置を設定
        // event.latLng.lat()でクリックしたところの緯度を取得
        marker.setPosition(new google.maps.LatLng(event.latLng.lat(), event.latLng.lng()));
        MarkerArray.push(marker);
    }
}
//deleteMarkers関数
function deleteMarkers() {
    MarkerArray.forEach(function (marker, idx) { marker.setMap(null); });
    MarkerArray = []; // marker配列の初期化
}
//csvOutput関数
function csvOutput() {
    MarkerArray.forEach(function (marker, idx) {
        var pos = marker.getPosition();
        var lat = pos.lat();
        var lng = pos.lng();
        $("#csv").append(lat + "," + lng  + "<br />");
    });
}
//csvDelete関数
function csvDelete() {
    $("#csv").empty();
}
//invisibleMarkers関数
function invisibleMarkers() {
    markers.forEach(function (marker, idx) { marker.setVisible(false); });
}
//visibleMarkers関数
function visibleMarkers() {
    markers.forEach(function (marker, idx) { marker.setVisible(true); });
}
```
# 実行方法および実行結果

- 起点と終点の座標を入力してください。
- 検索ボタンをクリックしてください。
- 検索ルートとルートのパスのそれぞれのポイントがGoogleマップ上に表示されます。
- Googleマップの検索ルート上の任意の地点（交差点など）をクリックし、マーカーを設置してください。
- マーカーが設置されたら、座標出力ボタンをクリックしてください。
- 右のマーカーの座標が出力されます。

<img width="431" alt="キャプチャ.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/205045/ebabf06e-afb7-81b9-9fc3-46e601ce204e.png">
<img width="208" alt="キャプチャ2.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/205045/83583392-9772-1e07-209f-60333e29a458.png">

# 留意事項
- [Maps JavaScript APIは、1か月$200分の無料クレジットの範囲では最大 28,000 読み込み]((https://cloud.google.com/maps-platform/pricing/sheet/?authuser=1)**)
- **※無料枠を超えてAPIを使用すると課金されますのでご注意ください(>_<)**

# 参考書
[詳解! GoogleAppsScript完全入門 ~GoogleApps & G Suiteの最新プログラミングガイド~](https://www.amazon.co.jp/%E8%A9%B3%E8%A7%A3-GoogleAppsScript%E5%AE%8C%E5%85%A8%E5%85%A5%E9%96%80-GoogleApps-G-Suite%E3%81%AE%E6%9C%80%E6%96%B0%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E3%82%AC%E3%82%A4%E3%83%89/dp/4798053767)
[Developer Guide（Maps JavaScript API）](https://developers.google.com/maps/documentation/javascript/tutorial?authuser=1)