要約
本記事では、GoogleMaps APIとZENRINMaps APIを使用してランニングコースを検索し、その結果を比較しました。両APIにはランニング専用モードは存在しませんが、歩行者ルートをカスタマイズすることで実用的なランニングルートを作成できることがわかりました。GoogleMaps APIではwaypointsの活用が重要であり、ZENRINMaps APIでは各種パラメータのカスタマイズが可能です。実装方法、結果の違い、それぞれのAPIの長所と短所を解説し、開発者がプロジェクトに最適なAPIを選択する際の参考となる情報を提供しています。
はじめに
ランニングコース検索は、健康志向のランナーや効率的なトレーニングを求めるアスリートなど、様々なニーズを持つ人々にとって非常に重要です。
本記事では、ZENRIN Maps APIとGoogle Maps APIを用いて、このランニングコース検索を実装し、それぞれのAPIの特性と実装方法を比較検討します。
対象読者
- ランニングアプリの開発に興味がある方
- 経路探索APIの選定に悩んでいる方
- ZENRIN Maps APIとGoogle Maps APIの違いを詳しく知りたい方
ZENRINMaps APIでのランニングコース実装
実装概要
ZENRINMaps APIを利用して、特定の出発地点から目的地点までのランニングコースを検索し、地図上に表示する機能を実装しました。APIのパラメータをカスタマイズすることで、ランニングに適したルートを生成しています。 ZENRIN Maps APIを使用するためには検証用IDとPW取得が必要です。
お試しIDは下記から簡単に発行できました。
ZENRIN Maps API 無料お試しID お申込みフォーム(2か月無料でお試しできます)
詳細手順はこちらを参照しました。
実装コード
var map;
// ルート情報(所要時間、距離、消費カロリー)を表示する関数
function showRouteInfo(rawDuration, rawDistance, rawCalorie) {
convertTime(rawDuration);
convertDtn(rawDistance);
convertCalorie(rawCalorie);
}
// 所要時間を適切な形式で表示する関数
function convertTime(rawDuration) {
const timeInfoArea = document.getElementById('time');
if (timeInfoArea) {
const minutes = Math.round(rawDuration / 60); // 秒 → 分に変換
timeInfoArea.textContent = `${minutes} 分`;
} else {
console.error("所要時間の表示エリアが見つかりません。");
}
}
// 距離をキロメートル単位で表示する関数
function convertDtn(rawDistance) {
const distInfoArea = document.getElementById('dist');
if (rawDistance && distInfoArea) {
const distanceInKm = (rawDistance / 1000).toFixed(1); // メートル → キロメートルに変換
distInfoArea.textContent = `${distanceInKm} km`;
} else {
console.error("距離の表示エリアが見つかりません。");
}
}
// 消費カロリーを表示する関数
function convertCalorie(rawCalorie) {
const calorieInfoArea = document.getElementById('calorie');
if (rawCalorie && calorieInfoArea) {
calorieInfoArea.textContent = `${rawCalorie.toFixed(1)} kcal`;
} else {
console.error("消費カロリーの表示エリアが見つかりません。");
}
}
// ルート検索を実行する関数
function performRouteSearch(origin, destination) {
const startPoint = `${origin.lng},${origin.lat}`;
const goalPoint = `${destination.lng},${destination.lat}`;
const api = "/route/route_mbn/walk";
const params = {
search_type: 5,
from: startPoint,
to: goalPoint,
llunit: 'dec',
datum: 'JGD',
eta: true,
speed: 200,
weight: 70,
stride: 100,
step_count: true,
calorie: true,
avoid: '1,2'
};
try {
map.requestAPI(api, params, function (response) {
if (response.ret && response.ret.status === 'OK') {
const route = response.ret.message.result.item[0].route;
// 座標データを取得
const coordinates = route.section.flatMap(section =>
section.link.flatMap(link =>
link.line.coordinates.map(coord => new ZDC.LatLng(coord[1], coord[0]))
)
);
// 座標データが存在するか確認
if (coordinates.length === 0) {
console.warn("座標データが存在しません。");
return;
}
// 地図の範囲を調整
if (coordinates.length > 0) {
// 最初の座標を中心にする
const firstCoord = coordinates[0];
map.setCenter(new ZDC.LatLng(firstCoord.lat, firstCoord.lng));
map.setZoom(15); // 適切なズームレベルを設定
}
// ルート情報を取得して表示
const rawDuration = route.time; // 所要時間
const rawDistance = route.distance; // 距離
const rawCalorie = route.calorie; // 消費カロリー
showRouteInfo(rawDuration, rawDistance, rawCalorie);
// ポリラインでルートを地図上に描画
try {
const polyline = new ZDC.Polyline(coordinates, {
color: '#008dcb',
width: 5,
opacity: 0.7,
});
map.addWidget(polyline);
} catch (error) {
console.error("ZDC.Polylineの作成に失敗しました:", error);
}
} else {
console.error("ルート検索に失敗しました。");
}
});
} catch (error) {
console.error("ルート検索中にエラーが発生しました:", error);
}
}
// ポリラインの範囲を計算する関数
function calculatePolylineBounds(polylineCoordinates) {
if (!polylineCoordinates || polylineCoordinates.length === 0) {
console.warn('ポリラインの座標が無効です。');
return null;
}
let minLat = Number.POSITIVE_INFINITY;
let maxLat = Number.NEGATIVE_INFINITY;
let minLng = Number.POSITIVE_INFINITY;
let maxLng = Number.NEGATIVE_INFINITY;
try {
polylineCoordinates.forEach(point => {
if (point.lat < minLat) minLat = point.lat;
if (point.lat > maxLat) maxLat = point.lat;
if (point.lng < minLng) minLng = point.lng;
if (point.lng > maxLng) maxLng = point.lng;
});
} catch (error) {
console.error("calculatePolylineBounds関数でエラーが発生しました:", error);
return null;
}
const southWest = new ZDC.LatLng(minLat, minLng);
const northEast = new ZDC.LatLng(maxLat, maxLng);
return new ZDC.LatLngBounds(southWest, northEast);
}
// ZMALoaderの初期化
ZMALoader.setOnLoad(function (mapOptions, error) {
if (error) {
console.error(error);
return;
}
mapOptions.zoom = 17;
mapOptions.centerZoom = false;
map = new ZDC.Map(
document.getElementById('ZMap'),
mapOptions,
function () {
// スタート地点と終点を設定
const origin = new ZDC.LatLng(35.669, 139.700);
const destination = new ZDC.LatLng(35.671, 139.714);
console.log("map:", map);
performRouteSearch(origin, destination);
// 地図コントロールを追加
map.addControl(new ZDC.ZoomButton('bottom-left'));
map.addControl(new ZDC.Compass('top-right'));
map.addControl(new ZDC.ScaleBar('bottom-left'));
},
function () {
console.error("地図の生成に失敗しました");
}
);
});
実装結果と課題
-
結果: 結果: ZENRINMaps APIを使用することで、ランニングに適したルートを地図上に表示できました。速度、体重、歩幅などのパラメータを設定することで、消費カロリーの計算や階段・急な坂を避けるルートの生成が可能となりました。
ZENRINMaps APIで、以下のパラメータ指定によって、ランニングの条件を追加しています
const params = {
// 既存のパラメータに加えて
eta: true, // 到着予定時刻の計算を有効にする
speed: 200, // 移動速度を200m/分(12km/h)に設定(ジョギング程度の速度)
weight: 70, // ランナーの体重を70kgと仮定
stride: 100, // 歩幅を100cmと設定
step_count: true, // 歩数(ステップ数)の計算を有効にする
calorie: true, // 消費カロリーの計算を有効にする
avoid: '1,2' // 1: 階段、2: 急な坂を避ける
};
上記のようにパラメータを追加することでランニングコース検索用にカスタマイズしています。
-
ランニングに適した速度でのルート計算が可能になります
-
ランナーの体重と歩幅を考慮した、より正確な消費カロリーと歩数の計算ができます
-
階段や急な坂を避けることで、ランニングに適したルートを優先的に選択できます
-
到着予定時刻の計算により、ランニングの所要時間を正確に把握できます
-
課題: ランニング専用モードがないため、歩行者モードをベースにしたカスタマイズが必要です。また、パラメータの最適な設定値には個人差があることが考えられます。
GoogleMaps APIでのランニングコース実装
実装概要
GoogleMaps APIを使用して、出発地点から目的地点までのランニングコースを検索し、地図上に表示する機能を実装しました。WALKINGモードを使用し、waypointsを活用して公園内を通るルートを緯度経度で指定しています。
実装コード
<script>
function initMap() {
const map = new google.maps.Map(document.getElementById("map"), {
center: { lat: 35.465833, lng: 139.622778 },
zoom: 15,
});
const directionsService = new google.maps.DirectionsService();
const directionsRenderer = new google.maps.DirectionsRenderer();
directionsRenderer.setMap(map);
const request = {
origin: new google.maps.LatLng(35.669, 139.700), // 渋谷駅付近
destination: new google.maps.LatLng(35.671, 139.714), // 代々木公園付近
travelMode: google.maps.TravelMode.WALKING,
waypoints: [
{ location: new google.maps.LatLng(35.6705, 139.7055), stopover: false }, // 公園入口
{ location: new google.maps.LatLng(35.6712, 139.7100), stopover: false }, // 公園中央
{ location: new google.maps.LatLng(35.6700, 139.7120), stopover: false } // ランニングコース途中
],
optimizeWaypoints: true, // 最適なルートを計算
};
directionsService.route(request, (result, status) => {
if (status === 'OK') {
directionsRenderer.setDirections(result);
const route = result.routes[0].legs[0];
document.getElementById('distance').textContent = '総距離: ' + route.distance.text;
document.getElementById('duration').textContent = '所要時間: ' + route.duration.text;
const steps = route.steps;
const routeSteps = document.getElementById('route-steps');
routeSteps.innerHTML = "";
steps.forEach(step => {
const li = document.createElement('li');
li.innerHTML = step.instructions + ' (' + step.distance.text + ')';
routeSteps.appendChild(li);
});
} else {
console.error("ルート検索に失敗しました: " + status);
}
});
}
</script>
実装結果と課題
-
結果:GoogleMaps APIを使用して、基本的な歩行者ルートを地図上に表示し、距離や所要時間などの情報を提供できました。waypointsを活用することで、公園内を通るランニングコースを作成することができました。
-
課題:GoogleMaps APIはランニングに特化したオプションを提供していないため、ランナーが求める細かい条件を完全に満たすルート作成は難しい場合があります。
地図表示
API比較
-
ランニング対応: ZENRINMaps APIは、パラメータのカスタマイズによりランニングに適したルート生成が可能です。GoogleMaps APIは、waypointsを活用することで公園内のルートなどを指定できますが、ランニング特有の条件設定は限定的です。
-
情報提供: ZENRINMaps APIは消費カロリーなどのランナー向け情報を直接提供できます。GoogleMaps APIは基本的な距離と時間情報を提供しますが、ランナー特有の情報は別途計算が必要です。
-
カスタマイズ性: GoogleMaps APIは豊富なドキュメントと広範なコミュニティサポートがあり、カスタマイズの幅が広いです。ZENRINMaps APIは日本国内に特化した詳細な地図情報を提供しており、日本のランナー向けに最適化しやすい可能性があります。
-
使いやすさ: GoogleMaps APIは世界中で広く使われており、導入しやすいです。ZENRINMaps APIは日本国内での利用に特化しており、日本語のサポートが充実しています。
まとめ
本記事では、Google Maps APIとZENRIN Maps APIを使用してランニングコースを検索し、その結果を比較しました。両APIともランニング専用モードは存在しませんが、それぞれ異なるアプローチでランニングに適したルートを作成できることがわかりました。Google Maps APIではwaypointsによるルートのカスタマイズが、ZENRIN Maps APIでは詳細なパラメータ設定によるカスタマイズが可能です。それぞれのAPIの特徴を理解し、プロジェクトの要件や利用シーンに応じて適切なAPIを選択することで、より魅力的なランニングアプリを開発できるでしょう。