🚙

Isochrone APIの新機能、depart_atを試してみる

2023/11/20に公開

はじめに

Mapboxはナビゲーションに関するAPIを提供しています。その中のIsochrone APIdepart_at機能が追加されました。この記事ではこの新機能を試してみます。なお、depart_atと使用するためにはパブリック・レビューへの登録が必要です。

depart_at機能の詳細については以下のブログをご参照ください。

https://www.mapbox.jp/blog/isochrones-depart-at

Isochrone APIとは

Isochrone APIはある地点から◯◯分で到達できる領域を得ることができるAPIです。例えば、東京駅を中心に自動車で5分で到達できる場所といった情報が得られます。

作成するもの

東京駅を中心に、自動車で5分、10分、15分で到達できる範囲をポリゴンで描画します。また、ある一日の午前0時から3時間刻みの変化を見たいのでスライダーを使って表示を切り替えます。

完成品は以下のとおりです。

コード

ちょうどタイムスライダーのサンプルがあったのでこれを流用します。

https://docs.mapbox.com/mapbox-gl-js/example/timeline-animation/

コードの全体像は完成品のJSボタンを押して表示されるコードをご参照ください。

定数

最初にいくつか定数を定義します。centerは地図およびIsochroneの中心座標です。departuresdepart_atに使用する日時です。minutescontours_minutesで使用する時間です。

const center = [139.7668267, 35.6807286];

const departures = [
  "2023-12-03T00:00",
  "2023-12-03T03:00",
  "2023-12-03T06:00",
  "2023-12-03T09:00",
  "2023-12-03T12:00",
  "2023-12-03T15:00",
  "2023-12-03T18:00",
  "2023-12-03T21:00"
];

const minutes = [5, 10, 15];

ヘルパー関数

getLayerIdは日時と時間から一意なレイヤーIDを生成する関数です。

function getLayerId(departure, minute) {
  return departure + "-" + minute;
}

filterByはスライダーで選択された日時に該当するレイヤーのみを表示するようにする関数です。Isochroneで取得したデータからポリゴンを作成しますが、レイヤーIDがスライダーで選択された日時のものだけを表示します。

function filterBy(index) {
  departures.forEach((departure) => {
    minutes.forEach((minute) => {
      const visible = getLayerId(departures[index], minute);
      const id = getLayerId(departure, minute);
      map.setFilter(id, visible === id);
    });
  });

  document.getElementById("departure").textContent = departures[index];
}

地図の表示

いつも通り、Mapオブジェクトを作成ます。containerで地図を表示するHTMLエレメントのidを指定します。

const map = new mapboxgl.Map({
  container: "map",
  style: "mapbox://styles/mapbox/light-v11",
  center,
  zoom: 10
});

ソース・レイヤーの作成

map.on("load", () => {/*ここ*/});の中でソース及びレイヤーを作成します。

Isochone APIアクセス

3時間毎のデータを取得するのでPromise.allで同時にアクセスします。APIアクセスのためのURLの組み立て方はAPIドキュメントをご参照ください。今回追加されたのはdepart_atの部分です。

const tasks = departures.map(async (departure) => {
  return fetch(
    `https://api.mapbox.com/isochrone/v1/mapbox/driving/${center}?contours_minutes=${minutes.join(
      ","
    )}&contours_colors=6706ce,04e813,4286f4&polygons=true&depart_at=${departure}&access_token=${
      mapboxgl.accessToken
    }`
  );
});

const responses = await Promise.all(tasks);

取得したデータを用いてソース・レイヤーの作成

responses.map(async (response) => {})で各responseに関してソース、レイヤーの作成を行います。

リクエストURLの中からdepart_atで指定した日時を抽出します。

const departure = response.url.match(
  /depart_at=(\d{4}-\d{2}-\d{2}T\d{2}:\d{2})/
)[1];

fetch APIを使用してデータを取得したのでresponse.json()でIsochone APIのレスポンスのJSONを取得できます。中身はGeoJSONなのでそのままaddSourceでソースを作成します。

const features = await response.json();
map.addSource(departure, {
  type: "geojson",
  data: features
});

次にレイヤーを作成します。レスポンスのGeoJSONはFeaturesで中には5分、10分、15分のデータに対応したPolygonデータが配列として格納されています。色分けするために、一つずつ取り出してそれぞれレイヤーを作成します。filter["==", ["get", "contour"], minute]contourプロパティ(時間が格納されている)をチェックします。例えば、5分のレイヤーではcontour5であるデータを使用する、となります。

features.features.forEach((feature) => {
  const minute = feature.properties.contour;
  const id = getLayerId(departure, minute);

  map.addLayer({
    id,
    type: "fill",
    source: departure,
    filter: ["==", ["get", "contour"], minute],
    paint: {
      "fill-color": feature.properties.fillColor,
      "fill-opacity": feature.properties["fill-opacity"]
    }
  });
});

処理全体をawait Promise.all();でくるんでいるので、空のPromiseを返します。全resonpseの処理が完了するのを待ちたいのでこの処理を入れています。

return new Promise((resolve) => {
  resolve();
});

スライダーの実装

スライダーが動かされたときにfilterByを呼び出し、該当する日時のレイヤーだけを表示するようにします。また、読み込み完了後に自動的に0番目を表示したいのでfilterBy(0)を実行します。このタイミングですべてのレイヤーの作成が完了している必要があったので先程空のPromiseを作成していました。

document.getElementById("slider").addEventListener("input", (e) => {
  filterBy(parseInt(e.target.value, departures.length));
});

filterBy(0);

まとめ

日中は道路が混んでいるので到達できる範囲が小さくなっている様子がわかります。

GitHubで編集を提案
マップボックス・ジャパン合同会社

Discussion