Mapbox Isochrone APIのdepart_atを試して、地図上に特定の曜日や時間帯に応じた到達圏を表示してみた

## はじめに
こちらは前回Mapbox Isochrone API記事の続きになります。

https://qiita.com/yan_x/items/4e0a71c54764ac19cdf9

### depart_atとは
Isochrone APIの機能で、正確で最新の交通データを利用して、特定の曜日や時間帯の到達圏を高い精度で予測できます。
depart_atを使用するためにはパブリック・レビューへの登録が必要です。

https://www.mapbox.com/access/isochrones-depart-at

### 完成イメージ
東京駅を中心に、未来の（本文では2024年7月1日）特定時間帯を選択して、車で20分で到達できる範囲を地図上に表示してみました。選択できる時間帯は0時から20時を4時間刻みに設定しました。
作成したサンプルは以下になります。
![qiita14.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2916958/cb53caa9-6648-957f-577c-96b2887fe230.gif)

8時や16時は道路が混んでいるので到達圏が小さくなっていることがわかります。

コードはトークンを入れれば使えます。
<details><summary>サンプルコード</summary>

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Demo: Get started with the Isochrone API</title>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <!-- Mapbox GL JS -->
    <script src="https://api.tiles.mapbox.com/mapbox-gl-js/v3.3.0/mapbox-gl.js"></script>
    <link
      href="https://api.tiles.mapbox.com/mapbox-gl-js/v3.3.0/mapbox-gl.css"
      rel="stylesheet"
    />
    <!-- Mapbox Assembly -->
    <link
      href="https://api.mapbox.com/mapbox-assembly/v1.3.0/assembly.min.css"
      rel="stylesheet"
    />
    <script src="https://api.mapbox.com/mapbox-assembly/v1.3.0/assembly.js"></script>
    <style>
      body {
        margin: 0;
        padding: 0;
      }

      #map {
        position: absolute;
        top: 0;
        bottom: 0;
        width: 100%;
      }
    </style>
  </head>

  <body>
    <div id="map"></div>

    <div class="absolute fl my24 mx24 py24 px24 bg-gray-faint round">
      <form id="params">
        <h4 class="txt-m txt-bold mb6">Choose a travel mode:</h4>
        <div class="mb12 mr12 toggle-group align-center">
          <label class="toggle-container">
            <input name="profile" type="radio" value="walking" />
            <div class="toggle toggle--active-null toggle--null">Walking</div>
          </label>
          <label class="toggle-container">
            <input name="profile" type="radio" value="cycling" />
            <div class="toggle toggle--active-null toggle--null">Cycling</div>
          </label>
          <label class="toggle-container">
            <input name="profile" type="radio" value="driving" checked />
            <div class="toggle toggle--active-null toggle--null">Driving</div>
          </label>
        </div>
        <h4 class="txt-m txt-bold mb6">Choose a maximum duration:</h4>
        <div class="mb12 mr12 toggle-group align-center">
          <label class="toggle-container">
            <input name="duration" type="radio" value="10" checked />
            <div class="toggle toggle--active-null toggle--null">10 min</div>
          </label>
          <label class="toggle-container">
            <input name="duration" type="radio" value="20" />
            <div class="toggle toggle--active-null toggle--null">20 min</div>
          </label>
          <label class="toggle-container">
            <input name="duration" type="radio" value="30" />
            <div class="toggle toggle--active-null toggle--null">30 min</div>
          </label>
        </div>
        <h4 class="txt-m txt-bold mb6">Choose a depart time:</h4>
        <p>2024-07-01</p>
        <div class="mb12 mr12 toggle-group align-center">
          <label class="toggle-container">
            <input name="departure" type="radio" value="00:00" checked />
            <div class="toggle toggle--active-null toggle--null">00:00</div>
          </label>
          <label class="toggle-container">
            <input name="departure" type="radio" value="04:00" />
            <div class="toggle toggle--active-null toggle--null">04:00</div>
          </label>
          <label class="toggle-container">
            <input name="departure" type="radio" value="08:00" />
            <div class="toggle toggle--active-null toggle--null">08:00</div>
          </label>
          <label class="toggle-container">
            <input name="departure" type="radio" value="12:00" />
            <div class="toggle toggle--active-null toggle--null">12:00</div>
          </label>
          <label class="toggle-container">
            <input name="departure" type="radio" value="16:00" />
            <div class="toggle toggle--active-null toggle--null">16:00</div>
          </label>
          <label class="toggle-container">
            <input name="departure" type="radio" value="20:00" />
            <div class="toggle toggle--active-null toggle--null">20:00</div>
          </label>
        </div>
      </form>
    </div>

    <script>
      mapboxgl.accessToken = 'UserAccessToken';

      const map = new mapboxgl.Map({
        container: 'map', // container id
        style: 'mapbox://styles/mapbox/streets-v12', // stylesheet
        center: [139.767124, 35.681236], // starting position [lng, lat]
        zoom: 11.5 // starting zoom
      });

      // Target the params form in the HTML
      const params = document.getElementById('params');

      // Create variables to use in getIso()
      const urlBase = 'https://api.mapbox.com/isochrone/v1/mapbox/';
      const lon = 139.767124;
      const lat = 35.681236;
      let profile = 'cycling';
      let minutes = 10;
      let departure = '2024-07-01T00:00';

      // Set up a marker that you can use to show the query's coordinates
      const marker = new mapboxgl.Marker({
        'color': '#314ccd'
      });

      // Create a LngLat object to use in the marker initialization
      // https://docs.mapbox.com/mapbox-gl-js/api/#lnglat
      const lngLat = {
        lon: lon,
        lat: lat
      };

      // Create a function that sets up the Isochrone API query then makes a fetch call
      async function getIso() {
        const query = await fetch(
          `${urlBase}${profile}/${lon},${lat}?contours_minutes=${minutes}&polygons=true&depart_at=${departure}&access_token=${mapboxgl.accessToken}`,
          { method: 'GET' }
        );
        const data = await query.json();
        // Set the 'iso' source's data to what's returned by the API query
        map.getSource('iso').setData(data);
      }

      // When a user changes the value of profile or duration by clicking a button, change the parameter's value and make the API query again
      params.addEventListener('change', (event) => {
        if (event.target.name === 'profile') {
          profile = event.target.value;
        } else if (event.target.name === 'duration') {
          minutes = event.target.value;
        } else if (event.target.name === 'departure') {
          departure = '2024-07-01T' + event.target.value;
        }
        getIso();
      });

      map.on('load', () => {
        // When the map loads, add the source and layer
        map.addSource('iso', {
          type: 'geojson',
          data: {
            'type': 'FeatureCollection',
            'features': []
          }
        });

        map.addLayer(
          {
            'id': 'isoLayer',
            'type': 'fill',
            'source': 'iso',
            'layout': {},
            'paint': {
              'fill-color': '#5a3fc0',
              'fill-opacity': 0.3
            }
          },
          'poi-label'
        );

        // Initialize the marker at the query coordinates
        marker.setLngLat(lngLat).addTo(map);

        // Make the API call
        getIso();
      });
    </script>
  </body>
</html>

```

</details>



## 作成手順
前回記事のコードから追加した部分を紹介します。
### サイドバーに表示したい特定の時間帯を追加する
```html
        <h4 class="txt-m txt-bold mb6">Choose a depart time:</h4>
        <p>2024-07-01</p>
        <div class="mb12 mr12 toggle-group align-center">
          <label class="toggle-container">
            <input name="departure" type="radio" value="00:00" checked />
            <div class="toggle toggle--active-null toggle--null">00:00</div>
          </label>
          <label class="toggle-container">
            <input name="departure" type="radio" value="04:00" />
            <div class="toggle toggle--active-null toggle--null">04:00</div>
          </label>
          <label class="toggle-container">
            <input name="departure" type="radio" value="08:00" />
            <div class="toggle toggle--active-null toggle--null">08:00</div>
          </label>
          <label class="toggle-container">
            <input name="departure" type="radio" value="12:00" />
            <div class="toggle toggle--active-null toggle--null">12:00</div>
          </label>
          <label class="toggle-container">
            <input name="departure" type="radio" value="16:00" />
            <div class="toggle toggle--active-null toggle--null">16:00</div>
          </label>
          <label class="toggle-container">
            <input name="departure" type="radio" value="20:00" />
            <div class="toggle toggle--active-null toggle--null">20:00</div>
          </label>
        </div>
```

### Isochrone APIへのリクエスト
fetchする通常のIsochrone APIにdepart_atを追加するだけです。
```js
`${urlBase}${profile}/${lon},${lat}?contours_minutes=${minutes}&polygons=true&depart_at=${departure}&access_token=${mapboxgl.accessToken}`
```
### depart_atの時間帯に動的変化を適用する
depart_atに使える日時のフォーマットは```YYYY-MM-DDThh:mm:ssZ```、```YYYY-MM-DDThh:mmss±hh:mm```、```YYYY-MM-DDThh:mm```、こちらは```YYYY-MM-DDThh:mm```を使用しています。
```js
let departure = '2024-07-01T00:00';
```
```js
else if (event.target.name === 'departure') {
  departure = '2024-07-01T' + event.target.value;
}
```