GoogleMapsAPIのルート検索をゼンリンの地図APIの大型車ルート検索でやってみた

## はじめに

<strong>本記事の目的</strong>
GoogleMapsPlatformとZENRINMapsAPIは、地図やルート検索における代表的なサービスです。本記事では、それぞれのルート検索機能にフォーカスし、特徴や違いを解説します。
<strong>対象読者</strong>
どちらのサービスを選ぶべきか迷っている方や、それぞれの技術を活用したい方に最適です。

## 両サービスの概要
<strong>Google Maps Platform</strong>
　提供元: Google
　特徴: 豊富なデータベース、正確なルート情報、大規模なエコシステム
　利用用途: 旅行、物流、エンタープライズ向けのアプリケーション
<strong>ZENRIN Maps API</strong>
　提供元: ZENRINDatacom
　特徴: 日本特化の高精度データ、豊富なカスタマイズ機能、施設内情報のサポート、高頻度なデータ更新
　利用用途: 物流、不動産、観光、小売り、防災、公共サービスのアプリケーション

## APIキーの取得
### Google Maps Platform
１．下記のページに接続します。
[Google Cloud Platform](https://console.cloud.google.com/welcome?hl=ja&inv=1&invt=Abkf1g&project=encoded-site-444608-b6)
２．「APIとサービス」メニューをクリックする。
![GMP_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965389/25cf963d-85c1-01e5-8b65-14aeb22ee9e7.png)
３．「認証情報」リンクをクリックして、「認証情報を作成」メニューから「APIキー」を選択します。
![GMP_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965389/5cd975aa-b298-2f99-18af-c1a2d81905c0.png)
４．この時点でAPIキーは作成されますが、セキュリティの強化のため「キーを制限」設定を行ってください。
![GMP_３.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965389/c29cb18a-7dd6-1c79-aab2-4f83173c9c62.png)
５．キーを利用するAPIに適したキーの制限を行います。
キーの制限を行わずにAPIキーを利用した場合、外部にキー情報が洩れると悪意のあるユーザに利用されてしまう恐れがあるのでご注意下さい。
下記から適切な制限を選択して設定を保存して下さい。
![GMP_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965389/aad23251-fc6d-b90f-3ddf-31e0cf1efc2e.png)

|項目 |値 |
|:-:|:-:|
|HTTPリファラー（ウェブサイト） |HTTPリファラーによる制限|
|IPアドレス（ウェブサーバー、crronジョブなど）|IPアドレスによる制限|
|Androidアプリ|パッケージ名およびフィンガープリントによる制限|
|iOSアプリ|iOSハンドル識別子による制限|


<strong>ZENRIN Maps API</strong>
１．検証用IDとパスワード（PW）取得
ZENRIN Maps APIを使用するためには、検証用IDとPWを取得しなければなりません。

１－１．検証用IDとPWの発行を依頼
以下の、URLから必要事項を入力して送信します。
[ZENRIN Maps API　無料お試しID　お申込みフォーム（２か月無料でお試しできます。）](https://www.zenrin-datacom.net/solution/zenrin-maps-api/trial?fm_cp=6757baf3203e3a00bb118509&fm_mu=67638b901fd14c02c8cdcdd7&utm_campaign=6757baf3203e3a00bb118509&utm_medium=else&utm_source=Qiita1)
![ZMA_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965389/bd6879b2-4ccd-e78b-7cf5-e48d47b7ac73.png)

１－２．検証用IDとPWの確認
フォーム送信から3営業日以内にメールにて検証用IDとPWが発行されます。

２．コンソールにログイン
以下のURLでメールにて送られてきたIDとPWを入力し、[ログイン](https://test-console.zmaps-api.com/)をします。
![ZMA_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965389/3e184372-64c3-1a2d-774c-b049f73f3a4c.png)

３．ユーザーの設定
ユーザー設定にて、「コンソール管理者メールアドレス」と「情報配信用メールアドレス」を設定します。
特にコンソール管理者メールアドレスはZENRIN Maps API コンソールのパスワードを忘れて再設定が必要となった場合に必要になります。
![ZMA_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965389/a9948b72-b0c8-e751-2531-5d1153c1446b.png)

４．チャネルの認証方式の設定、チャネル名変更
チャネルの設定変更は左メニューの「チャネル設定」から行うことができます。
「チャネル設定」押下後表示される画面にはチャネルの一覧が表示されています。
設定を行うチャネルの「編集」ボタンを押下してください。
![ZMA_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965389/6e53e2f0-61f3-e67b-48f4-4454411c2fd4.png)

認証方式は3種類あり、設定したいタブを押下するとそれぞれの認証方式の設定項目が表示されます。
認証方式を有効にするには、「無効」を「有効」に変更してください。
有効にしたい認証方式が複数ある場合は、それぞれの認証方式のタブで「有効」状態に変更してください。
それぞれの設定項目を入力後「変更」ボタンを押下すると有効状態と設定項目が反映されます。
![ZMA_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965389/19bfbee6-b973-3e4b-adde-7e6319ea2b05.png)

５．APIキーの取得・API利用開始
「チャネル一覧」のグレーアウトされている箇所にマウスオーバーすると表示される「APIキー」を使い、[APIリファレンス](https://developers.zmaps-api.com/v20/reference/)に則りAPIの利用を開始してください。

これで準備は完了です。
![ZMA_6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965389/0ff5f1b3-46dd-efe6-55ce-6bdab4a2005f.png)

 ## サンプルコード
<strong>Google Maps Platform</strong>
```GMP_route.html
<!DOCTYPE html>
<html>
<html lang="ja">
  <head>
    <meta charset="UTF-8">
    <title>Google Maps APIでルート検索</title>
    <script src="https://maps.googleapis.com/maps/api/js?key=[APIキー]&libraries=places"></script>
    <style>
      #map {
        height: 500px;
        width: 100%;
      }
    </style>
  </head>
  <body>
    <h1>ルート検索デモ</h1>
    <div id="map"></div>
    <script>
      function initMap() {
        const map = new google.maps.Map(document.getElementById("map"), {
          center: { lat: 35.637085839175484, lng: 139.6707547792902 }, //日本大学認定こども園
          zoom: 15,
        });

        const directionsService = new google.maps.DirectionsService();
        const directionsRenderer = new google.maps.DirectionsRenderer();
        directionsRenderer.setMap(map);

        var start = new google.maps.LatLng(35.637085839175484, 139.6707547792902); //日本大学認定こども園
        var end = new google.maps.LatLng(35.63448502797939, 139.66985552394794); //青葉学園野沢こども園      


        const request = {
          origin: start,
          destination: end,

          travelMode: google.maps.TravelMode.DRIVING,
        };

        directionsService.route(request, (result, status) => {
          if (status === google.maps.DirectionsStatus.OK) {
            directionsRenderer.setDirections(result);
          } else {
            console.error("ルート検索に失敗しました: " + status);
          }
        });
      }

      window.onload = initMap;
    </script>
  </body>
</html>
```

<strong>ZENRIN Maps API</strong>
```zma_route.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ZENRIN Maps API Route Display</title>
<style>
    body { margin: 0; padding: 0; }
    #ZMap { position: absolute; top: 0; bottom: 0; width: 100%; }
</style>
<script src="https://test-js.zmaps-api.com/zma_loader.js?key=[APIキー]&auth=referer"></script>
<script src="js/zma_route.js"></script>
</head>
<body>
<div id="ZMap"></div>
</body>
</html>
```
```zma_route.js
ZMALoader.setOnLoad(function (mapOptions, error) {
    if (error) {
        console.error(error);
        return;
    }

    const mapElement = document.getElementById('ZMap');

    // 地図の初期設定
    mapOptions.center = new ZDC.LatLng(35.637085839175484, 139.6707547792902); //日本大学認定こども園
    mapOptions.zoom = 17;
    mapOptions.mouseWheelReverseZoom = true; // ★マウスホイールのズーム方向の反転を指定


    const map = new ZDC.Map(mapElement, mapOptions, function() {
        // 地図生成成功時の処理

        // コントロールを追加
        map.addControl(new ZDC.ZoomButton('top-left'));
        map.addControl(new ZDC.Compass('top-right'));
        map.addControl(new ZDC.ScaleBar('bottom-left'));
        
        const start = new ZDC.LatLng(35.637085839175484, 139.6707547792902); //日本大学認定こども園
        const end = new ZDC.LatLng(35.63448502797939, 139.66985552394794); //青葉学園野沢こども園

        const startMarker = new ZDC.Marker(start);
        const endMarker = new ZDC.Marker(end);
        map.addWidget(startMarker);
        map.addWidget(endMarker);

        const routeUrl = `https://test-web.zmaps-api.com/route/route_mbn/drive_ptp?search_type=1&from=${start.lng},${start.lat}&to=${end.lng},${end.lat}&regulation_type=121200&toll_type=large`;

        fetch(routeUrl, {
            method: 'GET',
            headers: {
                'x-api-key': '[APIキー]',
                'Authorization': 'referer'
            },
        })
        .then(response => response.json())
        .then(data => {
            if (data.status === 'OK' && data.result?.item?.length > 0) {
                const routeItem = data.result.item[0];
                const links = routeItem.route?.link;

                if (Array.isArray(links) && links.length > 0) {
                    const decodedPath = [];
                    links.forEach(link => {
                        if (Array.isArray(link.line.coordinates)) {
                            link.line.coordinates.forEach(coord => {
                                if (Array.isArray(coord) && coord.length === 2) {
                                    decodedPath.push(new ZDC.LatLng(coord[1], coord[0]));
                                }
                            });
                        }
                    });

                    console.log("デコードされた座標:", decodedPath);

                    if (decodedPath.length > 0) {
                        const routeLine = new ZDC.Polyline(
                            decodedPath,
                            {
                                color: '#008dcb',
                                width: 5,
                                opacity: 0.7
                            }
                        );
                        map.addWidget(routeLine);
                    } else {
                        console.error("デコードされたルートデータが空です。");
                    }
                } else {
                    console.error("リンクデータが存在しません。");
                }
            } else {
                console.error("ルート検索に失敗しました。レスポンス:", data);
            }
        })
        .catch(error => console.error('ルート検索エラー:', error));
    }, function() {
        console.error('地図の生成に失敗しました');
    });
});

```


 ## 地図表示
<strong>Google Maps Platform</strong>
![GMPルート_野沢.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965389/b8a0432e-c0ca-7f9b-6aa9-dd2f29a83c9f.png)
<strong>ZENRIN Maps API</strong>
[ZENRINMapsAPI 大型車ルート表示](https://goldiek24.github.io/qiita-zma_largeroute_noge/zma_route.html)
![ZMA大型車_野沢.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965389/5ba1cad0-b7de-e9da-ce4f-dd13c1600777.png)

## 解説
### Google Maps Platform
#### １．HTML構造とスタイル
HTMLの基本構造を定義し、Google Maps APIのスクリプトを読み込んでいます。
また、地図を表示するための要素とそのスタイルを設定しています。
```GMP_route.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8">
    <title>Google Maps APIでルート検索</title>
    <script src="https://maps.googleapis.com/maps/api/js?key=[APIキー]&libraries=places"></script>
    <style>
      #map {
        height: 500px;
        width: 100%;
      }
    </style>
  </head>
  <body>
    <h1>ルート検索デモ</h1>
    <div id="map"></div>
    <!-- ... -->
  </body>
</html>
```

#### ２．地図の初期化
initMap関数では、Google Mapsオブジェクトを作成し、初期の中心座標とズームレベルを設定しています。
```GMP_route.js
function initMap() {
  const map = new google.maps.Map(document.getElementById("map"), {
    center: { lat: 35.637085839175484, lng: 139.6707547792902 }, //日本大学認定こども園
    zoom: 15,
  });
  // ...
}
```

#### ３．ルート検索の設定
ルート検索に必要なDirectionsServiceとDirectionsRendererオブジェクトを作成し、出発地点と目的地を設定しています。また、移動手段を車（DRIVING）に設定しています。
```GMP_route.js
const directionsService = new google.maps.DirectionsService();
const directionsRenderer = new google.maps.DirectionsRenderer();
directionsRenderer.setMap(map);

var start = new google.maps.LatLng(35.637085839175484, 139.6707547792902); //日本大学認定こども園
var end = new google.maps.LatLng(35.63448502797939, 139.66985552394794); //青葉学園野沢こども園      

const request = {
  origin: start,
  destination: end,
  travelMode: google.maps.TravelMode.DRIVING,
};

```

#### ４．ルート検索の実行
ここでは、設定したリクエストを使用してルート検索を実行し、結果を地図上に表示しています。エラーが発生した場合はコンソールにエラーメッセージを出力します。
```GMP_route.js
directionsService.route(request, (result, status) => {
  if (status === google.maps.DirectionsStatus.OK) {
    directionsRenderer.setDirections(result);
  } else {
    console.error("ルート検索に失敗しました: " + status);
  }
});
```

#### ５．地図の読み込み
最後に、ページの読み込みが完了したときにinitMap関数を実行するよう設定しています。これにより、地図の初期化とルート検索が自動的に行われます。
```GMP_route.js
window.onload = initMap;

```


### ZENRIN Maps API
#### １．地図の初期化
ZENRINMapsAPIの初期化と地図の生成を行っています。ZMALoader.setOnLoad関数を使用してAPIのロードを待ち、地図のオプションを設定し、ZDC.Mapオブジェクトを作成しています。
 ```zma_route.js
 ZMALoader.setOnLoad(function (mapOptions, error) {
    if (error) {
        console.error(error);
        return;
    }

    const mapElement = document.getElementById('ZMap');

    // 地図の初期設定
    mapOptions.center = new ZDC.LatLng(35.681406, 139.767132); // 東京駅
    mapOptions.zoom = 13;

    const map = new ZDC.Map(mapElement, mapOptions, function() {
        // 地図生成成功時の処理
    }, function() {
        console.error('地図の生成に失敗しました');
    });
});
```

#### ２．地図コントロールの追加
ここでは、地図上にズームボタン、コンパス、スケールバーなどのコントロールを追加しています。これらのコントロールにより、ユーザーは地図の操作や情報の確認が容易になります。
 ```zma_route.js
 // コントロールを追加
map.addControl(new ZDC.ZoomButton('top-left'));
map.addControl(new ZDC.Compass('top-right'));
map.addControl(new ZDC.ScaleBar('bottom-left'));
 ```

#### ３．マーカーの設定
ルートの始点（日本大学認定こども園）と終点（青葉学園野沢こども園）にマーカーを設置しています。ZDC.Markerオブジェクトを作成し、map.addWidgetメソッドで地図上に追加しています。
 ```zma_route.js
        const start = new ZDC.LatLng(35.637085839175484, 139.6707547792902); //日本大学認定こども園
        const end = new ZDC.LatLng(35.63448502797939, 139.66985552394794); //青葉学園野沢こども園
const startMarker = new ZDC.Marker(start);
const endMarker = new ZDC.Marker(end);
map.addWidget(startMarker);
map.addWidget(endMarker);
 ```

#### ４．ルート検索APIリクエスト
ZENRINMapsAPIのルート検索エンドポイントにリクエストを送信しています。fetch関数を使用してHTTPリクエストを行い、APIキーと認証情報をヘッダーに含めています。
リクエストパラメータに「ルート探索用車種(regulation_type=121200)」を設定しています。
 ```zma_route.js
const routeUrl = `https://test-web.zmaps-api.com/route/route_mbn/drive_ptp?search_type=1&from=${start.lng},${start.lat}&to=${end.lng},${end.lat}&regulation_type=121200`;

fetch(routeUrl, {
    method: 'GET',
    headers: {
        'x-api-key': '[APIキー]',
        'Authorization': 'referer'
    },
})
.then(response => response.json())
.then(data => {
    // ルートデータの処理
})
.catch(error => console.error('ルート検索エラー:', error));
```

#### ５．ルートデータの処理とポリライン描画
APIから返されたルートデータを処理し、ポリラインとして地図上に描画しています。ルートの座標データをZDC.LatLngオブジェクトに変換し、ZDC.Polylineを使用してルートを描画しています。
```zma_route.js
if (data.status === 'OK' && data.result?.item?.length > 0) {
    const routeItem = data.result.item[0];
    const links = routeItem.route?.link;

    if (Array.isArray(links) && links.length > 0) {
        const decodedPath = [];
        links.forEach(link => {
            if (Array.isArray(link.line.coordinates)) {
                link.line.coordinates.forEach(coord => {
                    if (Array.isArray(coord) && coord.length === 2) {
                        decodedPath.push(new ZDC.LatLng(coord[1], coord[0]));
                    }
                });
            }
        });

        if (decodedPath.length > 0) {
            const routeLine = new ZDC.Polyline(
                decodedPath,
                {
                    color: '#008dcb',
                    width: 5,
                    opacity: 0.7
                }
            );
            map.addWidget(routeLine);
        }
    }
}
```

## API比較
### 1. APIの初期化と地図の表示
**Google Maps Platform**
・google.maps.Mapオブジェクトを直接作成して地図を表示します
・デフォルトのコントロールが自動的に表示されます
**ZENRIN Maps API**
・ZMALoader.setOnLoad関数を使用してAPIを初期化します
・ZDC.Mapオブジェクトを作成して地図を表示します
・コントロール（ズームボタン、コンパス、スケールバー）を明示的に追加します

### 2. ルート検索の実装
**Google Maps Platform**
・DirectionsServiceとDirectionsRendererオブジェクトを使用します
・directionsService.routeメソッドでルート検索を実行し、結果を自動的に描画します
**ZENRIN Maps API**
・カスタムURLを構築し、fetchAPIを使用してルート検索リクエストを送信します
・結果を手動でパースし、ポリラインを作成してルートを描画します
・詳細な設定（regulation_typeなど）が可能です

### 3. パラメータの設定
**Google Maps Platform**
・origin、destination、travelModeなどのパラメータをオブジェクトとして指定します。
・基本的なルート検索パラメータのみ設定可能です。
**ZENRIN Maps API**
・URLパラメータとしてsearch_type、from、to、regulation_typeなどを指定します。
・ルート探索用車種（regulation_type=121200）など、詳細な設定が可能です。

### ４. カスタマイズ性
**Google Maps Platform**
・シンプルな実装で基本的なルート検索機能を提供します。
・グローバルな標準に基づいたルート検索を行います。
**ZENRIN Maps API**
・ルートの描画や地図の操作をより細かくカスタマイズできます。
・日本固有の道路事情や規制に対応した詳細な設定が可能です。

## まとめ
これらの違いから、ZENRIN Maps APIは日本国内での詳細なルート検索や特殊な要件に対応する場合に適しており、Google Maps Platformは簡単な実装で基本的なルート検索機能を提供する場合に適していると言えます。