Isochrone APIの新機能、depart_atを試してみる
はじめに
Mapboxはナビゲーションに関するAPIを提供しています。その中のIsochrone APIでdepart_at
機能が追加されました。この記事ではこの新機能を試してみます。なお、depart_at
と使用するためにはパブリック・レビューへの登録が必要です。
depart_at
機能の詳細については以下のブログをご参照ください。
Isochrone APIとは
Isochrone APIはある地点から◯◯分で到達できる領域を得ることができるAPIです。例えば、東京駅を中心に自動車で5分で到達できる場所といった情報が得られます。
作成するもの
東京駅を中心に、自動車で5分、10分、15分で到達できる範囲をポリゴンで描画します。また、ある一日の午前0時から3時間刻みの変化を見たいのでスライダーを使って表示を切り替えます。
完成品は以下のとおりです。
コード
ちょうどタイムスライダーのサンプルがあったのでこれを流用します。
コードの全体像は完成品のJSボタンを押して表示されるコードをご参照ください。
定数
最初にいくつか定数を定義します。center
は地図およびIsochroneの中心座標です。departures
はdepart_at
に使用する日時です。minutes
はcontours_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分のレイヤーではcontour
が5
であるデータを使用する、となります。
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);
まとめ
日中は道路が混んでいるので到達できる範囲が小さくなっている様子がわかります。
Discussion