雨の日の屋根がある歩行者ルート検索を MapBox と ZENRIN Maps API で比較してみた


## 要約

本記事では、Mapbox と ZENRIN Maps API という 2 つの地図サービスを使用して、地図表示と雨の日に屋根付きの歩行者ルート計算の方法を解説しています。Mapbox 自体には『雨の日に屋根付きの歩行者ルートを表示する』ための直接的な機能はありません。そのため、通常の歩行者ルート（pedestrian routes）のみを表示しています。 ZENRIN Maps API は、屋根がある道を優先して徒歩ルートを検索する機能をサポートしています。これにより、雨の日でも快適に歩けるよう、屋根のある道が多いルートを選択して表示することができます。

## はじめに

地図を使って目的地までの経路を表示することは、多くのウェブアプリケーションで重要な機能です。Mapbox と ZENRIN Maps API は、それぞれ異なる API とツールを提供しており、地図の表示やルート計算を行うために非常に便利です。本記事では、これらのツールを利用して地図を描画し、歩行者の経路を計算する方法について解説します。

## 対象読者

この記事は、以下の読者を対象としています：
地図 API を活用したアプリケーション開発に興味がある開発者。
Mapbox や ZENRIN Maps API を使った地図表示やルート計算の実装を試してみたい開発者。
地図上で経路を可視化する機能を提供するウェブアプリケーションを作りたい開発者。

## 両サービスの概要

## Mapbox**

Mapbox は、地図の表示、カスタマイズ、経路検索などを行うための強力なツールセットを提供する地図サービスです。特に、Mapbox Directions API は、様々な交通手段（歩行、自転車、車など）のルート計算をサポートしており、開発者は簡単に地図にルートを描画できます。

## ZENRIN Maps API

ZENRIN Maps API は、日本の地図サービスで、地図の表示、位置情報の取得、ルート検索などができます。特に ZENRIN Maps API は、日本国内で特化した詳細な地図データを提供しており、商業施設や公共交通機関情報などが豊富です。ZENRIN Maps API API を使用することで、経路検索を行い、地図上にその経路を描画することが可能です。

## API キーの取得

**Mapbox**

**アクセストークンの取得方法**
利用する前に規約確認が必要なので規約の詳細については、公式サイトで確認してください.

Mapbox API を利用するには、アクセストークンを取得する必要があります。以下のリンクからログインし、API キーを取得してください。

[mapbox コンソール](https://console.mapbox.com/)

**ZENRIN Maps API**

１．検証用 ID とパスワード（PW）取得

ZENRIN Maps API を利用するには、検証用 ID とパスワード（PW）を取得し、API キーを取得する必要があります。以下のリンクから申請・取得を行ってください

[ZENRIN Maps API 　無料お試し ID 　お申込みフォーム](https://www.zenrin-datacom.net/solution/zenrin-maps-api/trial?fm_cp=6757baf3203e3a00bb118509&fm_mu=676395761fd14c04c9cd1f6e&utm_campaign=6757baf3203e3a00bb118509&utm_medium=else&utm_source=Qiita17)

[ コンソール](https://test-console.zmaps-api.com/)

[ZENRIN Maps API リファレンス](https://developers.zmaps-api.com/v20/reference/)

### サンプルコード

**Mapbox**

### route_MapboxWalk.html:

```javascript
mapboxgl.accessToken = "YOUR ACCESS TOKEN";
const map = new mapboxgl.Map({
  container: "map", // 地図を表示するHTML要素のID
  style: "mapbox://styles/mapbox/streets-v11", // 地図のスタイル
  center: [139.767132, 35.681406], // 東京駅の緯度経度
  zoom: 12, // 初期ズームレベル
});
```

mapboxgl.accessToken は、Mapbox の API を利用するために必要なアクセストークンです。


new mapboxgl.Map(): 地図を表示するための Mapbox のインスタンスを作成します。ここでは地図が表示される HTML 要素（container）、地図スタイル（style）、初期の中心位置（center）、およびズームレベル（zoom）を設定しています。

**ナビゲーションコントロールを追加**

```javascript
const nav = new mapboxgl.NavigationControl();
map.addControl(nav, "top-left");
```

new mapboxgl.NavigationControl(): ズームイン・アウトや回転を行うためのナビゲーションコントロールを作成します。

map.addControl(nav, 'top-left'): 作成したナビゲーションコントロールを地図に追加し、位置を画面の左上（top-left）に指定します。

**出発点と到着点のマーカーを追加**

```javascript
const startingPoint = [139.763, 35.675]; // 始点
const endingPoint = [139.7844, 35.6853]; // 終点

new mapboxgl.Marker({ color: "blue" })
  .setLngLat(startingPoint) // 出発点
  .addTo(map);

new mapboxgl.Marker({ color: "red" })
  .setLngLat(endingPoint) // 到着点
  .addTo(map);
```

new mapboxgl.Marker(): 新しいマーカーを地図に追加します。ここでは、出発点を青、到着点を赤で表示しています。

**歩行者ルート計算**

```javascript
function calculatePedestrianRoute() {
  const coordinates = [startingPoint, endingPoint]
    .map((coord) => coord.join(","))
    .join(";");

  const url = `https://api.mapbox.com/directions/v5/mapbox/walking/${coordinates}?geometries=geojson&access_token=${mapboxgl.accessToken}`;

  fetch(url)
    .then((response) => response.json())
    .then((data) => {
      if (data.code !== "Ok") {
        throw new Error(`ルート計算エラー:${data.message}`);
      }

      const route = data.routes[0].geometry;

      // 既存のルートを削除
      if (map.getLayer("route")) {
        map.removeLayer("route");
        map.removeSource("route");
      }

      // 歩行者専用ルートを地図に追加
      map.addSource("route", {
        type: "geojson",
        data: {
          type: "Feature",
          geometry: route,
        },
      });

      map.addLayer({
        id: "route",
        type: "line",
        source: "route",
        layout: {
          "line-join": "round",
          "line-cap": "round",
        },
        paint: {
          "line-color": "blue",
          "line-width": 4,
        },
      });
    })
    .catch((error) => {
      console.error("Route calculation error:", error);
    });
}
```

calculatePedestrianRoute 関数: Mapbox Directions API を使って歩行者のルートを計算し、地図に描画します。

coordinates.join(';'): 出発点と到着点を;で繋げて、Mapbox API が要求する形式にします。

fetch(url): Directions API を呼び出し、取得したルート情報を JSON として処理します。

if (map.getLayer('route')): 既にルートが描画されていれば、それを削除して新しいルートを描画します。

map.addSource('route'): ルートデータ（GeoJSON）を地図に追加します。
map.addLayer: ルートを青い線で地図に描画します。

**ZENRIN Maps API**

**1. 地図の初期化**

```javascript
ZMALoader.setOnLoad(function (mapOptions, error) {
  if (error) return console.error("ZMALoader error:", error);

  const mapElement = document.getElementById("ZMap");
  mapOptions.center = new ZDC.LatLng(35.681406, 139.767132); // 東京駅
  mapOptions.mouseWheelReverseZoom = true;
  mapOptions.zoom = 15;

  const map = new ZDC.Map(
    mapElement,
    mapOptions,
    function () {
      map.addControl(new ZDC.ZoomButton("top-left"));
      map.addControl(new ZDC.ScaleBar("bottom-left"));
    },
    function () {
      console.error("地図の生成に失敗しました");
    }
  );
});
```

ZMALoader.setOnLoad:ZENRIN Maps API の地図オブジェクトが読み込まれた後に実行される関数を設定します。地図のオプション（中心位置やズームレベル）を設定し、地図を描画します。

new ZDC.Map(): ZENRIN Maps API の地図インスタンスを作成し、HTML 要素に地図を表示します。ズームボタンやスケールバーなどのコントロールも追加しています。

**出発点と到着点のマーカーを追加**

```javascript
const start = new ZDC.LatLng(35.675, 139.763); // 有楽町駅
const end = new ZDC.LatLng(35.6853, 139.7844); // 人形町

map.addWidget(new ZDC.Marker(start, { styleId: ZDC.MARKER_COLOR_ID_BLUE_L }));
map.addWidget(new ZDC.Marker(end, { styleId: ZDC.MARKER_COLOR_ID_YELLOW_L }));
```

new ZDC.LatLng(): ZENRIN Maps API の座標オブジェクトを作成します。出発点と到着点を設定します。

map.addWidget(): 地図にマーカーを追加します。マーカーには青（出発点）と黄色（到着点）の色が設定されています。

**ルート検索と描画**

```javascript
// ルート検索用のURLを作成
const routeUrl = `https://test-web.zmaps-api.com/route/route_mbn/walk?search_type=4&from=${start.lng},${start.lat}&to=${end.lng},${end.lat}&llunit=dec&datum=JGD`;

// APIリクエストを送信
fetch(routeUrl, {
  method: "GET",
  headers: {
    "x-api-key": "YOUR ACCESS TOKEN", // APIキーを指定
    Authorization: "ip", // 認証情報を指定
  },
})
  .then((response) => response.json()) // レスポンスをJSON形式で取得
  .then((data) => {
    // ステータスがOKで、かつルート情報が存在する場合
    if (data.status === "OK" && data.result?.item?.length) {
      const decodedPath = []; // デコードされたルート座標を格納する配列

      // ルート情報を解析し、座標データを取得
      data.result.item[0]?.route?.section?.forEach((section) => {
        section?.link?.forEach((link) => {
          link?.line?.coordinates?.forEach((coord) => {
            decodedPath.push(new ZDC.LatLng(coord[1], coord[0])); // 緯度・経度を適切な形式で追加
          });
        });
      });

      // デコードされたルートが存在する場合、マップに表示
      if (decodedPath.length > 0) {
        const routeLine = new ZDC.Polyline(decodedPath, {
          color: "blue",
          width: 4,
          opacity: 1,
        });
        map.addWidget(routeLine); // ルートをマップ上に追加
      } else {
        console.error("Decoded path is empty."); // ルート座標が空の場合のエラーログ
      }
    } else {
      console.error("Route search failed:", data.status); // ルート検索が失敗した場合のエラーログ
    }
  })
  .catch((error) => {
    console.error("ルート検索エラー:", error);
  });
```

fetch(routeUrl): ZENRIN Maps API の API を呼び出して、指定した座標間の経路情報を取得します。

data.result.item[0]?.route?.section: 返ってきたデータの中から経路情報を抽出し、座標を ZDC.LatLng オブジェクトに変換してリストに追加します。

new ZDC.Polyline(): 座標を使ってポリライン（線）を描画します。

### 地図表示

**Mapbox**
![スクリーンショット 2025-02-13 101111.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3981270/1e105aca-1b67-4283-af78-6bc465a80a0f.png)
**ZENRIN Maps API**


[参考サイト](https://bikram-pandey.github.io/web_projects/route_ZMA_walk.html)
![スクリーンショット 2025-02-13 101044.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3981270/16a15619-ba60-45e3-8523-afea10e12dbd.png)

## まとめ

ZENRIN Maps API は、雨の日でも屋根付きの歩行者ルートを正確に検索できる機能がある一方で、Mapbox ではそのような機能が提供されていません。Mapbox を使用する場合、屋根がある道のデータ（例：GeoJSON）を手動で準備し、それを地図にオーバーレイする必要があります。このため、Mapbox での屋根付きルート検索は難しく、追加のデータ管理とカスタマイズが求められます。対して、ZENRIN Maps API は、これらのルート検索をより簡単に行えるため、実装がスムーズです。