ガイド変更履歴HERE SDK API references
ガイド

高度なルート検索機能

到達圏検索やリアルタイム交通情報などの機能は、すでに計算されたルートで更新でき、ルート計画において重要な役割を果たします。このセクションでは、ナビゲーション中に残されたルートに戻る機能や、他のサービスからルートをインポートする機能についても詳しく説明します。

到達圏検索機能はHERE Isoline Routing API v8を使用します。ルートインポート機能はHERE Routing API v8を使用します。 HERE Isoline Routing API v8に基づくトランザクションの例:RoutingEngineIsolineOptionsを使用してルートを計算します。 HERE Routing API v8に基づいてオンラインでルートをインポートする例:RoutingEngine.importRoutes(...)

これらの機能の価格については、「HERE基本プランの価格表」を参照してください。Navigateライセンスを使用している場合、または価格についてご質問がある場合は、お問い合わせください。

到達圏検索

Isoline Routing (等値線ルート検索) を使用すると、時間、距離、エネルギー消費量に基づいて、特定の地点からの到達範囲を表すポリゴンを生成できます。ポリゴンには、特定の時間内、最大移動距離、さらには電気自動車で利用可能な充電レベルで到達できるすべての目的地が含まれます。詳細については、Isoline RoutingのAPIリファレンスを参照してください。

HERE SDK は、中心点から到達可能な領域を計算するために使用できる IsolineRoutingEngine を提供します。

do {
    try isolineRoutingEngine = IsolineRoutingEngine()
} catch let engineInstantiationError {
    fatalError("Failed to initialize isoline routing engine. Cause: \(engineInstantiationError)")
}

等値線ルート検索では、計算時にリアルタイムと過去の交通量のデータが考慮されます。

これがどのように役立つかの例をいくつか示します。

  • ユーザーは現在地から徒歩 2 km 以内のレストランを検索できます。
  • ユーザーは観光スポットまでの距離に基づいてホテルを検索できます。たとえば、米国オーランドにあるディズニー ワールドやユニバーサル スタジオなどの主要観光スポットまで車で 20 分以内のホテルを検索できます。

以下に、電気自動車シナリオの電力消費量ベースの Isoline の例を示します。この例ではドライバーが、指定された 400 Wh の制限内でどの地点に到達できるかを知りたいと考えています。したがって、目標は消費電力量を 400 Wh 以下にすることですが、問題はこのエネルギー制限内でドライバーはどこまで到達できるのかということです。

電気自動車は、現在のバッテリー充電量と、道路の勾配や補助電源の使用量などのエネルギー消費率に影響する要因に基づいて、到達可能な距離が制限されています。したがって、充電ポイントに到達する前にエネルギー切れになるのを避けるために、適切な範囲を視覚化することが役立ちます。車には固有の消費量パラメーターがあるため、正確な航続距離を計算するにはリクエストでそれらを指定する必要があります。詳細については、以下の電気自動車のセクションを参照してください。

結果は、ドライバーに視覚的な方向を提供するために地図上に表示できる GeoPolygon 形状になります。

let calculationOptions = IsolineOptions.Calculation(rangeType: .consumptionInWattHours, rangeValues: [400])
let isolineOptions = IsolineOptions(calculationOptions: calculationOptions,
                                    routingOptions: getEVRoutingOptions())

isolineRoutingEngine.calculateIsoline(center: Waypoint(coordinates: startGeoCoordinates),
                               isolineOptions: isolineOptions) { (routingError, isolines) in

    if let error = routingError {
        self.showDialog(title: "Error while calculating reachable area:", message: "\(error)")
        return
    }

    // When routingError is nil, the isolines list is guaranteed to contain at least one isoline.
    // The number of isolines matches the number of requested range values. Here we have used one range value,
    // so only one isoline object is expected.
    let isoline = isolines!.first!

    // If there is more than one polygon, the other polygons indicate separate areas, for example, islands, that
    // can only be reached by a ferry.
    for geoPolygon in isoline.polygons {
        // Show polygon on map.
        let fillColor = UIColor(red: 0, green: 0.56, blue: 0.54, alpha: 0.5)
        let mapPolygon = MapPolygon(geometry: geoPolygon, color: fillColor)
        self.mapView.mapScene.addMapPolygon(mapPolygon)
        self.mapPolygonList.append(mapPolygon)
    }
}

これによって、電気自動車が出発地から取ることができる直線方向の最速ルートをたどろうとしながら、400 Wh 以下の電力消費量で到達できるエリアがわかります。

最速かどうかはルートの最適化モードによって異なります (上記のセクションで説明しました)。もちろん、任意のモードを指定できます。各等値線には、移動の開始地点となる中心点が1つだけ必要であることに注意してください。

エネルギー消費量に関心があるため、RoutingOptionsElectricVehicleOptionsも指定しました。これらのオプションには、以下の電気自動車ルート検索セクションに示されているバッテリー仕様が含まれている必要があります。範囲タイプとして時間または距離の制限を指定する場合は、前に示した通常のルート オプションを指定できます。

IsolineRangeType では指定する範囲値のタイプを定義します。ここでは 400 を使用しています。複数の値を指定できます。その結果、指定した範囲値ごとに 1 つずつ、複数の Isoline オブジェクトが取得されます。

一方、到達範囲に島が含まれる場合など、特殊な場合には、各 Isoline オブジェクトに複数のポリゴンを含めることができます。このようなエリアは別個のポリゴンとして提供されます。

最終的なポリゴンの形状は maxPoints パラメーターで定義できます。これにより、GeoPolygon の頂点の数が決まります。このパラメーターはポリゴンの実際の計算には影響せず、その外観にのみ影響することに注意してください。

Screenshot: Showing the area of reach for the given limit.

スクリーンショットでは、ポリゴンの中心位置が大きな緑色の円で示されています。また、このエリアを超えて、ルート上にさらに 2 つの充電スタンドが見つかった候補ルートも表示されています。次のセクションでは、電気自動車のルートを計算する方法について説明します。

上記のコード スニペットは、GitHub の「EVRouting」サンプル アプリの一部として見つけることができます。

ルートを更新する

Route オブジェクトに含まれる交通流情報は、ルートが計算された時点で有効です。上記を参照してください。この情報を後でアップデートする場合は、ルートをいつでも更新できます。

ルートのオプションを更新することもできます。

// Update the route options and set a new start point on the route.
// The new starting point must be on or very close to the original route, preferrably, use a map-matched waypoint if possible.
// Note: A routeHandle is only available, when RouteOptions.enableRouteHandle was set to true when the original route was calculated.
guard let routeHandle = route.routeHandle else {
    print("Error: No routeHandle. Was RouteOptions.enableRouteHandle set to true?")
    return
}
let refreshRouteParameters = RefreshRouteParameters(routeHandle: routeHandle,
                                                    startingPoint: Waypoint(coordinates: GeoCoordinates(latitude: 13.45, longitude: 53.56)))
routingEngine.refreshRoute(refreshRouteParameters: refreshRouteParameters,
                           routingOptions: RoutingOptions()) { (routingError, routes) in
    if (routingError == nil) {
        let newRoute = routes?.first
        // ...
    } else {
        // Handle error.
    }
}

このためには、RouteHandle を知る必要があります。これはルートが計算される前に、RouteOptions.enableRouteHandle 経由でリクエストする必要があります。

さらに、ルートの更新は、ルート オプションをある移動タイプから別の移動タイプに変換したり、特定のオプションを更新したりするのに便利です。変換が不可能な場合、たとえば、歩行者用ルートがトラック用ルートに変換される場合には routingError が表示されます。

新しい出発地を指定してルートを短縮することもできます。新しいルートは計算されないため、新しい出発地は元のルートに非常に近い必要があります。新しい出発地が遠すぎる場合は routingError が発生します。

進行中のターン・バイ・ターンナビ中にドライバーがルートから逸脱した場合は、迂回部分に新しいルート計算が必要になるため、ルートを更新するだけでは十分ではありません。このような場合、新しい出発地を設定してルート全体を再計算するか、最初に選択した代替ルートを維持できるreturnToRoute()メソッドを使用する方が便利な場合があります。

ターン・バイ・ターンナビの実行中にルートを更新する (Navigateでのみ使用可能)

ターン・バイ・ターンナビ中に、最新の交通遅延情報を事前に取得するためにルートを更新できます。更新されたルートを Navigator に設定する前に、ルート上の最後の場所を新しい出発地として設定する必要があります。ただし、リフレッシュ操作の進行中にドライバーがすでに進んでいる場合、予期しない結果が生じる可能性があります。いずれにせよ、到達予測時刻を更新することのみが目的で、新しい出発地が既存のルート上にある場合は、必ずしも新しいルートを設定する必要はありません。この先のルートの残りの部分は変更されていないため、代わりに交通遅延時間を含む更新された到達予測時刻をそのまま使用できます。案内中に新しいルートを設定すると、新しいナビゲーション セッションが効率的に開始されます。

代替策として、最後の場所と現在のセクション インデックスを設定する必要がある DynamicRoutingEngine の使用を検討してください。この情報は、次の dynamicRoutingEngineOptions.pollInterval に到達したときに使用されます。トレードオフとして、これはすぐには更新されず、現在の交通障害を回避するためのよりよいルートが見つかった場合、ルートの形状が変更される可能性があります。ここでも、refreshRoute()と同様に、新しいルートを設定するかどうかを決定する必要があります。

OfflineRoutingEngine ではルートの更新はサポートされていません。現在の交通状況をクエリするにはオンライン接続が必要です。 同様に、DynamicRoutingEngine にはオンライン接続が必要です。

ルートに戻る

RoutingEngine を使用すると、現在の交通状況に基づいて既存のルート (上記を参照) を更新できます。その結果、以前のルートよりも早い新しいルートが作成される可能性があります。ただし、新しい startingPoint は元のルートに非常に近い必要があります。

startingPoint がルートからさらに離れているときに対処する必要がある場合は、RoutingEnginereturnToRoute() フィーチャーの使用を検討してください。たとえば、現地の交通状況により、ドライバーが迂回することを決定する場合があります。returnToRoute() の呼び出しでも新しいルートが作成されますが、できるだけコストのかかるルートの再計算を行わずに、可能な限り元のルートに似せようとします。

  • ストップオーバー経由地は必ず通過します。
  • パススルー経由地はルートを形成することのみを目的としているため、破棄される可能性があり新しいルートでそれらが使用されるかどうかは決まっていません。
  • 現在の交通状況が考慮され、ルートの形状が変更される場合があります。

ターン・バイ・ターンナビはNavigateでのみ利用できることに注意してください。

注 (Navigateにのみ適用)

returnToRoute()メソッドは、キャッシュまたはダウンロードされたマップデータに対してオフラインで動作するOfflineRoutingEngineでも使用できます。ただし、現在の交通状況は考慮されていません。

returnToRoute()を使用したコーディング例については、ナビゲーションの章をご覧ください。

ルートを永続化する

セッション間でルートを永続化して再利用できます。Routeクラスで提供されるserialize(...)およびdeserialize(...)メソッドを使用すると、routeオブジェクトを保存し復元できます。代わりに、routingEngine.importRoute(...)によるインポート機能も使用できます。

ルートインポート

さまざまなオーバーロードされたroutingEngine.importRoute()メソッドのいずれかを介して、他のAPIや/orベンダーからルートをインポートできます。これは 1 対 1 のインポート機能ではなく、再構成される機能です。

新しい Route オブジェクトは以下から作成できます。

  • オプション1:GeoCoordinatesRouteOptions のリスト。これは、ルートを別のベンダーからインポートする場合や、ルートを長期間保持する場合に役立ちます。ルートの形状は指定されたものにできる限り近づけられます。オンライン接続が必要です。
  • オプション2:RouteHandle。これは、他の HERE サービスからルートをインポートする場合に便利です。考えられるユースケースとしては、HERE WeGo Web サイトまたは HERE REST API を使用する別の Web ページでルートを作成し、それをモバイル デバイスに転送して移動を開始する場合などです。バックエンド側のマップ更新や現実世界のその他の変更により、ハンドルが無効になる可能性があることに注意してください。したがって、ハンドルは数時間のみ使用することをお勧めします。RouteHandle は特定の情報をエンコードしますが、バックエンドから完全なルート データを取得するにはオンライン接続が必要です。

適用された AvoidanceOptions は破棄され、ルートの Section.sectionNotices を介して違反として報告される場合があります。たとえば、高速道路の回避をリクエストしたものの、指定した座標が高速道路と重なる場合、最終的なルートは依然として高速道路と重なりますが、目的の回避オプションに違反していることを示す通知が追加されます。

一般に、ルートをインポートすると、たとえまったく同じ RouteOptions を使用したとしても、必ずしも元のルートとまったく同じルートが再現されるとは限りません。新しい建設現場や関連する交通イベントなどにより、マップは常に変化する可能性があります。たとえば、道路の制限速度の変更、新しい道路の建設または既存の道路の閉鎖などの場合に、マップ データ自体も変化する可能性があります。また、出発時刻は、特定の道路を利用できるかどうかに影響を与える可能性があります。

以下でさらに詳しく見ていきます。

オプション 1:地理座標のリストからルートをインポートする

RouteOptions を使用して GeoCoordinates のリストからルートをインポートするには、ルートの形状を定義する GeoCoordinates のリストが必要です。このような座標は互いに非常に近い必要があり、そうでない場合は計算に失敗します。このような座標のリストは、別のベンダーによって計算されたルートから抽出することも、GPX トレースから抽出することもできます。

// Import a route from list of Location instances.
// Note that the geographic coordinates need to be close to each other,
// ideally taken from an existing route.
let routeLocations = [
    Location(coordinates: GeoCoordinates(latitude: 52.518032, longitude: 13.420632)),
    Location(coordinates: GeoCoordinates(latitude: 52.51772, longitude: 13.42038)),
    Location(coordinates: GeoCoordinates(latitude: 52.51764, longitude: 13.42062)),
    Location(coordinates: GeoCoordinates(latitude: 52.51754, longitude: 13.42093)),
    Location(coordinates: GeoCoordinates(latitude: 52.51735, longitude: 13.42155)),
    Location(coordinates: GeoCoordinates(latitude: 52.51719, longitude: 13.42209)),
    Location(coordinates: GeoCoordinates(latitude: 52.51707, longitude: 13.42248)),
    Location(coordinates: GeoCoordinates(latitude: 52.51695, longitude: 13.42285)),
    Location(coordinates: GeoCoordinates(latitude: 52.5168, longitude: 13.42331)),
    Location(coordinates: GeoCoordinates(latitude: 52.51661, longitude: 13.42387)),
    Location(coordinates: GeoCoordinates(latitude: 52.51648, longitude: 13.42429)),
    Location(coordinates: GeoCoordinates(latitude: 52.51618, longitude: 13.42513)),
    Location(coordinates: GeoCoordinates(latitude: 52.5161, longitude: 13.42537)),
    Location(coordinates: GeoCoordinates(latitude: 52.51543, longitude: 13.42475)),
    Location(coordinates: GeoCoordinates(latitude: 52.51514, longitude: 13.42449)),
    Location(coordinates: GeoCoordinates(latitude: 52.515001, longitude: 13.424374))]

routingEngine.importRoute(with: routeLocations, options: RoutingOptions()) { (routingError, routes) in
    if (routingError == nil) {
        let newRoute = routes?.first
        // ...
    } else {
        // Handle error.
    }
}

このオプションでは、目的の交通機関タイプに一致する importRoute(..) メソッドのオーバーロードを選択できます。

Location オブジェクトと RouteOptions のリストからルートをインポートする場合、RoutingEngine では指定された地理座標にできるだけ近いルート形状が作成されます。

最良の結果を得るには、1Hz GPS データ、または数メートルの間隔が空いたポイントを使用します。データが非常にまばらな場合、エラーが発生する可能性があります。

位置情報のリストは無制限ではありません。サポートされる項目の最大数については、「API リファレンス」を参照してください。同様に、calculateRoute(..) を呼び出すときの経由地のリストは無制限ではありません。

さらに、importRoute(..) メソッドのオーバーロードを使用して RouteStop 要素のリストを指定できます。座標のリストだけからルートをインポートすると、通常はストップオーバー経由地に関する情報が失われます。このような停止を指定するには RouteStop クラスを使用します。このクラスでは、停止する場所である locationIndexstopDuration を指定できます。これは全体の到達予測時刻に影響し、ナビゲーション中にRouteStopがストップオーバー経由地として扱われるため、通過時にMilestoneとして報告されます (ナビゲーションはNavigateでのみサポートされています)。

OfflineRoutingEngineを使用する場合、calculateRoute(..)による経由地の設定には制限がありません (Navigateでのみ使用可能)。

この機能を使用するには、すべての提供ポイントを道路にマップマッチングする必要があります。したがって、(長い) ルートを RouteHandle からインポートするよりも大幅に時間がかかる可能性があります。

オプション 2:RouteHandle からルートをインポートする

以下では、RouteHandle オプションについて見ていきます。RouteHandle​(String handle) コンストラクタを使用すると、指定された文字列ハンドルから RouteHandle を作成できます。このような文字列は、利用可能な HERE REST API の 1 つなど、他のバックエンド ソースから提供できます。この文字列は数時間のみ有効であることに注意してください。

以下に、ルート ハンドル文字列を使用してルートを作成する REST API 呼び出しの例を示します (YOUR_API_KEY を使用している実際のキーに置き換えます)。

https://router.hereapi.com/v8/routes?apikey=-YOUR_API_KEY&origin=52.524465%2C13.382334&destination=52.525301%2C13.399844&return=polyline%2Csummary%2Cactions%2Cinstructions%2CrouteHandle&transportMode=car

JSON レスポンスからルート ハンドル文字列をコピーし、次のように HERE SDK で使用できます。

routingEngine.importRoute(routeHandle: RouteHandle(handle: "routeHandleStringFromBackend"), options: RoutingOptions()) { (routingError, routes) in
    if (routingError == nil) {
        let newRoute = routes?.first
        // ...
    } else {
        // Handle error.
    }
}

RouteHandle 文字列はすでに計算されたルートを識別します。インポートが正常に完了すると、新しいRouteオブジェクトが作成され、HERE SDKでさらに使用できるようにCalculateRouteCallbackの一部として提供されます。

注 (Navigateにのみ適用)

OfflineRoutingEngineでは、RouteHandleを使用してルートをオフラインでインポートすることもできます。ただし、GeoCoordinates のリストに渡すことはできません。これは、オンライン接続を必要とする RoutingEngine でのみ可能です。

ただし、無制限の数のWaypointアイテムをOfflineRoutingEngine.calculateRoute(..)に挿入することで、指定されたストップオーバー経由地を使用してルートをオフラインで計算し、元のルートにできるだけ近いルートを形成するという回避策が考えられます。200 個までしか経由地がサポートされていないオンライン RoutingEngine とは異なり、OfflineRoutingEngine にはそのような制限はありませんが、ルートが長くなるとパフォーマンスのトレードオフが発生する可能性があります。したがって、すべてのルート座標のサブセットのみを経由地として渡すことをお勧めします。たとえば、距離のしきい値でフィルタリングします。

追加の経由地は、オンライン RoutingEngine に関してすでに示したのと同じ方法で設定できます。


EN 日本語

HERE documentation

Find answers to your product and technical questions

Documentation

What's new

Videos

EN 日本語

HERE ドキュメント

製品や技術に関する質問の答えを見つけましょう。より多くの内容と最新の情報については、英語版をご覧ください。

ドキュメント

ダイナミックマップ

動的コンテンツ関連のAPIをアプリやサービスに活用して、ドライバーが安全・快適かつ予定どおりに目的地へ到着できるよう支援します。

地図とデータ

世界中を走行する多数のマッピング車両から得られる最新の位置情報データを活用し、精度の高い地図やカスタムレイヤーを構築できます。

最新情報

動画

(function () { const input = document.querySelector('input[data-typeahead]'); if (!input) return; // Prevent the form from submitting/navigating input.closest('form')?.addEventListener('submit', e => e.preventDefault()); input.addEventListener('input', function () { const q = this.value.trim().toLowerCase(); document.querySelectorAll('.nav-group-name').forEach(group => { let anyVisible = false; group.querySelectorAll('.nav-group-task').forEach(task => { const text = task.textContent.trim().toLowerCase(); const show = !q || text.includes(q); task.style.display = show ? '' : 'none'; if (show) anyVisible = true; }); // Hide the whole group header if nothing matches group.style.display = anyVisible || !q ? '' : 'none'; }); }); })(); (function () { function onTokenClick(event) { var link = event.target.closest('.sdk-for-ios .item .token'); if (!link) return; event.preventDefault(); console.log('token clicked', link.textContent.trim()); var item = link.closest('.item'); if (!item) return; var content = item.querySelector('.height-container'); if (!content) { console.log('no .height-container found for item', item); return; } var isHidden = window.getComputedStyle(content).display === 'none'; content.style.display = isHidden ? 'block' : 'none'; link.classList.toggle('token-open', isHidden); var href = link.getAttribute('href'); if (href) { if (history.pushState) history.pushState({}, '', href); else location.hash = href; } } function openHashTarget() { var hash = window.location.hash.slice(1); if (!hash) return; var anchor = document.querySelector('.sdk-for-ios a[name="' + hash + '"]'); if (!anchor) return; var item = anchor.closest('.item'); if (!item) return; var link = item.querySelector('.token'); var content = item.querySelector('.height-container'); if (!link || !content) return; content.style.display = 'block'; link.classList.add('token-open'); } function init() { console.log('HERE SDK accordion init'); openHashTarget(); } document.removeEventListener('click', onTokenClick); document.addEventListener('click', onTokenClick); if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } window.addEventListener('hashchange', openHashTarget); window.addEventListener('pageLoad', init); })();