ガイダンスを開始

HERE SDK を使用すると、包括的なターン・バイ・ターンナビ (矢印ナビ)エクスペリエンスを構築できます。 このフィーチャーを使用すると、アプリは現在のデバイスの位置を計算されたルートと照合して確認し、ナビゲーションの指示をジャストインタイムで取得できます。

ナビゲーションは、PUBLIC_TRANSITを除くすべての利用可能なトランスポートモードでサポートされています。 公共交通機関の利用は、ナビゲーションで不確かな、予期しない結果が生じる可能性があります。

BUS ルートのナビゲーションサポートは現在制限されています : 今後リリースされる HERE SDK では、バス固有のレーンアシスタンスの改善や、バスナビゲーションのより正確なターンバイターンナビが期待されています。

トランスポートモードは、 Routeをまたいで変更される場合があります。たとえば、公園を歩いて観光地に到着した場合、車を降りる必要がある場合があります。 ルートが計算されると、トランスポート モードが Route オブジェクトの各 Section に添付されます。

車、トラック、タクシー、バス、スクーターのルートについては、道路とマップで車両の位置を合わせます。歩行者ルートなどの他のモードについては、舗装されていない舗装されていない舗装路やドライバーがアクセスできないその他の経路に加えて、位置を合わせることができます。 一方、高速道路などの特定の道路は歩行者用のナビゲートはできません。 自転車ルートでは、高速道路を除く利用可能なすべての経路を利用できます。

HERE SDK では、トラッキングルートがなくても トラッキングモードをサポートしています。トラッキングモードでは、現在の道路、マップマッチングした場所、および制限速度などのその他のサポート情報が提供されます。

HERE SDK に は、 視覚的なフィードバックを示すための操作矢印のための UI アセットがありません。 代わりに、ルート上で利用できるすべての情報が単純なデータ型として提供され、必要に応じて独自のアセットを選択できます。

ヒント : ご自身の Android アプリケーションで使用できる再利用可能なアセットは、 HERE の MSDKUI オープンソースプロジェクトにあり ます。このプロジェクトは、 GitHub のこのリンクから利用できます。 再利用可能なアイコンの数は、 HERE の公式アイコンライブラリ に記載されています。

カスタマイズされたナビゲーションマップ ビューは、オプションでVisualNavigatorとともにレンダリングされます。 startRendering() が呼び出されると、現在の方向を示す矢印の形式で事前設定されたインスタンス MapMarker3D が追加され、受信位置の更新がスムーズに補間されます。 さらに、マップの向きが最適なデフォルト値に変更されます。

事前設定されたインスタンスMapMarker3D は、独自のモデルを設定してカスタマイズすることも、無効にすることもできます。 内部的に、VisualNavigatorLocationIndicatorインスタンスを使用するため、VisualNavigatorLocationIndicatorカスタムを設定することもできます。 この操作が完了したら、インスタンスを手動で追加、削除、および更新する必要があります。 マップ ビューですでにLocationIndicatorインスタンスを使用している場合と同様 に、関連するマップアイテムのセクションを参照してください。

デフォルトでは、LocationIndicatorのスタイルは、VisualNavigatorに設定できるトランスポートモードによって決まります。 ルートが設定されている場合は、代わりにルートインスタンスから取得されます。 カスタムアセットを使用する場合は、クラスLocationIndicatorを介してスタイルを直接切り替える必要があります。

NavigationCustom Example アプリで は、ナビゲーションが停止したときにカスタムLocationIndicatorおよび別のタイプに切り替える方法を示します。 また、ナビゲーションパースペクティブのカスタマイズ方法も示します。

音声ガイダンスは、 任意 のプラットフォーム TTS (音声合成)ソリューションにStringとして送信できる操作通知を提供します。

完全なオフラインサポート : オフライン マップ データがキャッシュ、インストール、またはプリロードされている場合、すべてのナビゲーションフィーチャーはインターネットに接続されていなくても動作します。 たとえば、DynamicRouteEngine を使用して交通最適化されたルートをオンラインで検索する場合、オンライン接続が必要なフィーチャーは一部だけです。 特に記載がない限り、以下のすべてのフィーチャーはオフラインで動作します。

他のエンジンとは異なり、ガイダンスがキャッシュ、インストール、または事前にプリロードされていないリージョンに到達すると、 HERE SDK は自動的にオンラインデータのダウンロードを試みます。 その逆も同様ですが、このオフライン マップデータは、オンライン接続が利用可能であっても、デバイスで利用可能な場合に利用されます。

ターン・バイ・ターンナビ (矢印ナビ)

ターン・バイ・ターンナビ (矢印ナビ)の基本原則は、速度や方位値などの位置情報を頻繁に受け取ることです。 これらの値は、道路と照合され、目的のルートと比較されます。 現在地と 次の目的地の方向を指定できる操作指示が表示されます。

ルートを出るときに、メートル単位で逸脱が通知されます。 この通知を使用すると、新しいルートを計算するかどうかを決定できます。 最後に、位置シミュレータを使用すると、開発フェーズでルートナビゲーションをテストできます。

注 : 重要

ターン・バイ・ターンナビ (矢印ナビ)を使用するアプリケーション開発者は、想定されるすべての使用シナリオでアプリケーションを徹底的にテストして、安全で正しい動作を保証する必要があります。 アプリケーション開発者は、アプリユーザーに以下を含む ( ただし、これらに限定されない ) 義務について警告する責任があります。

  • 安全でない、または違法な状況につながる可能性のある指示に従わないでください。
  • すべての地域の法律に従ってください。
  • 運転中に携帯電話またはそのフィーチャーの一部を使用することは禁止されている場合がありますので、ご了承ください。
  • 運転中は、常にハンドルから手を離さないでください。
  • 走行中は、道路の安全性を最優先にしてください。

以下のセクションのすべてのコードスニペットは 、ナビゲーションサンプルアプリの一部として GitHub でも利用できます。このアプリは、関連するコードを示し、指導中に画面をアクティブに保つなど、安定したドライビング体験とベストプラクティスを提供します。 ただし、本格的な量産対応アプリケーションのすべての側面をカバーするわけではありません。 たとえば、アプリ では、アプリ がバックグラウンドで動作している間に位置情報の更新を取得する方法は表示されません。

位置情報のバックグラウンド更新をする場合は、『位置情報の取得』ガイドの関連セクションを参照してください。 位置情報の更新を提供する限り、デバイスの画面がロックされていたり、マップ ビューが一時停止されている場合でも、すべてのナビゲーションイベントがシームレスに配信され続けます。

さらに 、 GitHubNavigationQuickStart アプリを利用し て、数行のコードでガイダンスを開始する方法を確認することもできます。 次のセクションも参照してください。

利用開始

HERE SDK のナビゲーションフィーチャーについてより詳しく調べる前に、まず短いコードの例を参照して、音声で操作できる指示とガイダンスビューを使用してガイダンスを開始する方法を示します。

private void startGuidance(Route route) {
    try {
        // Without a route set, this starts tracking mode.
        visualNavigator = new VisualNavigator();
    } catch (InstantiationErrorException e) {
        throw new RuntimeException("Initialization of VisualNavigator failed: " + e.error.name());
    }

    // This enables a navigation view including a rendered navigation arrow.
    visualNavigator.startRendering(mapView);

    // Hook in one of the many listeners. Here we set up a listener to get instructions on the maneuvers to take while driving.
    // For more details, please check the "Navigation" example app and the Developer Guide.
    visualNavigator.setManeuverNotificationListener(maneuverText -> {
        Log.d("ManeuverNotifications", maneuverText);
    });

    // Set a route to follow. This leaves tracking mode.
    visualNavigator.setRoute(route);

    // VisualNavigator acts as LocationListener to receive location updates directly from a location provider.
    // Any progress along the route is a result of getting a new location fed into the VisualNavigator.
    setupLocationSource(visualNavigator, route);
}

private void setupLocationSource(LocationListener locationListener, Route route) {
    try {
        // Provides fake GPS signals based on the route geometry.
        locationSimulator = new LocationSimulator(route, new LocationSimulatorOptions());
    } catch (InstantiationErrorException e) {
        throw new RuntimeException("Initialization of LocationSimulator failed: " + e.error.name());
    }

    locationSimulator.setListener(locationListener);
    locationSimulator.start();
}

このコードの抜粋では、ガイダンスビューが開始 され、提供されたrouteで定義されている宛先に到達するまで、操作手順がコンソールに出力されます ( 宣言を含むすべてのコードについては、「 NavigationQuickStart Example アプリ 」を参照してください ) 。 操作の指示はドライバーに伝えられることを意図しており、「左折して 5,000 m 先の Invalidenstra ß e に入る」などの文字列が含まれている場合があります。 操作方法の詳細な説明も利用できます。詳細については、以下のセクションを参照してください。

上記では、 HERE SDK のシミュレーションフィーチャーを使用して位置情報の更新を取得しています。 もちろん、VisualNavigatorに実際の位置情報の更新をフィードすることもできます。

ナビゲーションアプリの基本原則は次のとおりです。

  1. Routeを作成します。 走行するルートがないと、ガイダンスを開始できません。
  2. インスタンスVisualNavigator を作成してレンダリングを開始します ( または、独自のガイダンスビューをレンダリングする場合はインスタンスNavigator を作成します ) 。
  3. RouteVisualNavigatorに設定します。
  4. 位置情報の更新がVisualNavigatorに送られました。 位置情報がないと、ルート沿いのルートの進行状況を検出できません。 これにより、上に示したようにシミュレートが可能です。また、実際の位置情報の更新ができます。

利用開始時は、 GitHubNavigationQuickStart サンプルアプリ を参照して、この仕組みを確認してください。 そこでは、 HERE SDK が提供する多くのナビゲーションフィーチャーの詳細を確認できます。

Waypointを設定する場合 、ドライバーが道路のどちら側に到着するかを、sideOfStreetHintに設定して調整できます。 ドライバーが動いている場合、ベアリング値を headingInDegrees に設定 Waypointすることで、初期の方向を決定できます。 これにより、次の目的地がドライバーの後ろにある場合に、不要な U ターンを避けることができます。 これは、不要な道路横断を避けるためなど、歩行者のルートを最適化するのにも役立ちます。

ナビゲーターを使用してガイダンスイベントの確認

前述のように、目的地へのナビゲーションを開始する前に、次の 2 つのことを行う必要があります。

  • 走行するRoute 。 ナビゲーションを開始するには、NavigatorまたはVisualNavigatorインスタンスにRouteを設定する必要があります。
  • Navigator または VisualNavigator インスタンスにユーザーがいる場所を定期的に通知する場所ソース。

ルートがすでに計算されていない場合は、次のように作成します。 Route インスタンスを取得中で あることがここに示されます。 アプリをトラッキングモードでのみ開始する場合は、この手順をスキップできます。

ターン・バイ・ターンナビ (矢印ナビ)中に、NavigatorまたはVisualNavigatorインスタンスから現在のLocationと同期されたすべてのManeuver情報を取得します。 移動中は 、Routeオブジェクトから直接Manueverデータを取得しないでください

ガイダンスを開始するには、 2 つの選択肢があります。 ヘッドレス Navigator を使用するか、またはVisualNavigatorのサポートを受け てください。 NavigatorVisualNavigator のサブセットを提供するため、どちらも同じインターフェイスを提供しますが、VisualNavigator は、個別の Location更新間のスムーズな補間などのフィーチャーを備えた視覚的なレンダリング支援を最上位に提供します。

別の要件は、Locationインスタンスを提供することです。現在の場所で頻繁に更新を行わないとナビゲーションを実行できないためです。 このため には、 GitHub にあるプロバイダの実装を使用できます。

プラットフォームポジショニングソリューションを実装するか、 HERE SDK ポジショニングフィーチャーを使用するか または ロケーションシミュレータを設定することで、新しい場所にフィードできます。

基本的な情報フローは次のとおりです。

ロケーションプロバイダ => ロケーション => (Visual ) ナビゲータ => イベント

Location 任意のソースを「ロケーションプロバイダ」として設定できます。 onLocationUpdated()Navigator またはVisualNavigatorでのみ呼び出す必要 があります。

開発者は、有効な場所でVisualNavigatorにフィードする責任があります。 VisualNavigatorは、受信した各場所について、ルート沿いの進行状況を示す適切なイベント(操作、予想されるルートからの逸脱など)を使用して応答します。 結果のイベントは、提供された位置信号の精度と周波数に依存します。

まず、参照実装の新しいインスタンスを作成して場所を取得します。

herePositioningProvider = new HEREPositioningProvider();

次に、新しい VisualNavigator インスタンスを作成し、上記のからへのリスナーHEREPositioningProvider として設定します。 このVisualNavigatorクラスは、位置を受け取るonLocationUpdated()メソッドを定義するLocationListenerインターフェイスに準拠しています。

try {
    visualNavigator = new VisualNavigator();
} catch (InstantiationErrorException e) {
    throw new RuntimeException("Initialization of VisualNavigator failed: " + e.error.name());
}

// Now visualNavigator will receive locations from the HEREPositioningProvider.
herePositioningProvider.startLocating(visualNavigator, LocationAccuracy.NAVIGATION);

さらに、トラッキングするルートを設定してください ( トラッキングモードにする予定がある場合のみ ) 。

visualNavigator.setRoute(route);

VisualNavigatorのレンダリング機能を使用しない場合は 、代わりにクラスNavigatorを使用することもできます。 このクラスは、フードの下の同じコードを使用 し、VisualNavigatorとまったく同じように動作しますが、特殊なナビゲーションビューのレンダリングはサポートしていません。

次のステップでは、ルートの進行状況、現在の場所、ルートの逸脱に関する次の操作について通知を受け取るように、いくつかのリスナーをアタッチできます。

// Notifies on the progress along the route including maneuver instructions.
visualNavigator.setRouteProgressListener(new RouteProgressListener() {
    @Override
    public void onRouteProgressUpdated(@NonNull RouteProgress routeProgress) {
        List<SectionProgress> sectionProgressList = routeProgress.sectionProgress;
        // sectionProgressList is guaranteed to be non-empty.
        SectionProgress lastSectionProgress = sectionProgressList.get(sectionProgressList.size() - 1);
        Log.d(TAG, "Distance to destination in meters: " + lastSectionProgress.remainingDistanceInMeters);
        Log.d(TAG, "Traffic delay ahead in seconds: " + lastSectionProgress.trafficDelay.getSeconds());

        // Contains the progress for the next maneuver ahead and the next-next maneuvers, if any.
        List<ManeuverProgress> nextManeuverList = routeProgress.maneuverProgress;

        ManeuverProgress nextManeuverProgress = nextManeuverList.get(0);
        if (nextManeuverProgress == null) {
            Log.d(TAG, "No next maneuver available.");
            return;
        }

        int nextManeuverIndex = nextManeuverProgress.maneuverIndex;
        Maneuver nextManeuver = visualNavigator.getManeuver(nextManeuverIndex);
        if (nextManeuver == null) {
            // Should never happen as we retrieved the next maneuver progress above.
            return;
        }

        ManeuverAction action = nextManeuver.getAction();
        String roadName = getRoadName(nextManeuver);
        String logMessage = action.name() + " on " + roadName +
                " in " + nextManeuverProgress.remainingDistanceInMeters + " meters.";

        // Angle is null for some maneuvers like Depart, Arrive and Roundabout.
        Double turnAngle = nextManeuver.getTurnAngleInDegrees();
        if (turnAngle != null) {
            if (turnAngle > 10) {
                Log.d(TAG, "At the next maneuver: Make a right turn of " + turnAngle + " degrees.");
            } else if (turnAngle < -10) {
                Log.d(TAG, "At the next maneuver: Make a left turn of " + turnAngle + " degrees.");
            } else {
                Log.d(TAG, "At the next maneuver: Go straight.");
            }
        }

        // Angle is null when the roundabout maneuver is not an enter, exit or keep maneuver.
        Double roundaboutAngle = nextManeuver.getRoundaboutAngleInDegrees();
        if (roundaboutAngle != null) {
            // Note that the value is negative only for left-driving countries such as UK.
            Log.d(TAG, "At the next maneuver: Follow the roundabout for " +
                    roundaboutAngle + " degrees to reach the exit.");
        }

        if (previousManeuverIndex != nextManeuverIndex) {
            messageView.setText("New maneuver: " + logMessage);
        } else {
            // A maneuver update contains a different distance to reach the next maneuver.
            messageView.setText("Maneuver update: " + logMessage);
        }

        previousManeuverIndex = nextManeuverIndex;
    }
});

// Notifies on the current map-matched location and other useful information while driving or walking.
visualNavigator.setNavigableLocationListener(new NavigableLocationListener() {
    @Override
    public void onNavigableLocationUpdated(@NonNull NavigableLocation currentNavigableLocation) {
        MapMatchedLocation mapMatchedLocation = currentNavigableLocation.mapMatchedLocation;
        if (mapMatchedLocation == null) {
            Log.d(TAG, "The currentNavigableLocation could not be map-matched. Are you off-road?");
            return;
        }

        Double speed = currentNavigableLocation.originalLocation.speedInMetersPerSecond;
        Double accuracy = currentNavigableLocation.originalLocation.speedAccuracyInMetersPerSecond;
        Log.d(TAG, "Driving speed (m/s): " + speed + "plus/minus an accuracy of: " +accuracy);
    }
});

// Notifies on a possible deviation from the route.
visualNavigator.setRouteDeviationListener(new RouteDeviationListener() {
    @Override
    public void onRouteDeviation(@NonNull RouteDeviation routeDeviation) {
        Route route = visualNavigator.getRoute();
        if (route == null) {
            // May happen in rare cases when route was set to null inbetween.
            return;
        }

        // Get current geographic coordinates.
        MapMatchedLocation currentMapMatchedLocation = routeDeviation.currentLocation.mapMatchedLocation;
        GeoCoordinates currentGeoCoordinates = currentMapMatchedLocation == null ?
                routeDeviation.currentLocation.originalLocation.coordinates : currentMapMatchedLocation.coordinates;

        // Get last geographic coordinates on route.
        GeoCoordinates lastGeoCoordinatesOnRoute;
        if (routeDeviation.lastLocationOnRoute != null) {
            MapMatchedLocation lastMapMatchedLocationOnRoute = routeDeviation.lastLocationOnRoute.mapMatchedLocation;
            lastGeoCoordinatesOnRoute = lastMapMatchedLocationOnRoute == null ?
                    routeDeviation.lastLocationOnRoute.originalLocation.coordinates : lastMapMatchedLocationOnRoute.coordinates;
        } else {
            Log.d(TAG, "User was never following the route. So, we take the start of the route instead.");
            lastGeoCoordinatesOnRoute = route.getSections().get(0).getDeparturePlace().originalCoordinates;
        }

        int distanceInMeters = (int) currentGeoCoordinates.distanceTo(lastGeoCoordinatesOnRoute);
        Log.d(TAG, "RouteDeviation in meters is " + distanceInMeters);

        // Now, an application needs to decide if the user has deviated far enough and
        // what should happen next: For example, you can notify the user or simply try to
        // calculate a new route. When you calculate a new route, you can, for example,
        // take the current location as new start and keep the destination - another
        // option could be to calculate a new route back to the lastMapMatchedLocationOnRoute.
        // At least, make sure to not calculate a new route every time you get a RouteDeviation
        // event as the route calculation happens asynchronously and takes also some time to
        // complete.
        // The deviation event is sent any time an off-route location is detected: It may make
        // sense to await around 3 events before deciding on possible actions.        
    }
});

HERE では、RouteProgressListenerNavigableLocationListener、およびRouteDeviationListenerを設定しまし た。

RouteProgressListenerの内部では、渡されたRouteインスタンスのSectionごとの進行状況に関する詳細情報にアクセスできます。 ルートは、ウェイポイントおよびトランスポートモードの数に基づいて複数のセクションに分割できます。 remainingDistanceInMeters および trafficDelay.getSeconds() はすでにセクションごとに累積されています。 SectionProgress 一覧表の最後の項目を確認して、目的地までの全体的な残り距離と全体的な推定交通遅延を取得します。

trafficDelay.getSeconds()Route データが計算された時刻に基づいているため 、ガイダンス中の交通障害による遅れは更新されません。 値は、初期データに基づいて進行中のセクションに沿ってのみ更新されます。 現在 のトラフィック状況に基づいて、最適化されたルートを定期的に要求するには、DynamicRoutingEngine を使用します。

RouteProgressListener の内部 では、前方にある次の操作手順にもアクセスできます。 このために、次のものmaneuverIndexを使用します。

// Contains the progress for the next maneuver ahead and the next-next maneuvers, if any.
List<ManeuverProgress> nextManeuverList = routeProgress.maneuverProgress;

ManeuverProgress nextManeuverProgress = nextManeuverList.get(0);
if (nextManeuverProgress == null) {
    Log.d(TAG, "No next maneuver available.");
    return;
}

int nextManeuverIndex = nextManeuverProgress.maneuverIndex;
Maneuver nextManeuver = visualNavigator.getManeuver(nextManeuverIndex);

maneuver 取得した情報 visualNavigator を使用して、ドライバーが次のアクションや、このアクションが実行されるまでの距離などのその他の役立つ情報を表示するためのディスプレイを作成できます。 上記の例に示されているデバッグ目的で使用されている場合を除き、テキスト表現には使用しないことをお勧めします。 代わりに音声ガイダンスを使用してください ( 以下を参照 ) 。

ただし、ローカライズされた道路名または番地 ( 高速道路番号など ) を表示すると便利です。これらの住所は、次のように取得できます。

private String getRoadName(Maneuver maneuver) {
    RoadTexts currentRoadTexts = maneuver.getRoadTexts();
    RoadTexts nextRoadTexts = maneuver.getNextRoadTexts();

    String currentRoadName = currentRoadTexts.names.getDefaultValue();
    String currentRoadNumber = currentRoadTexts.numbersWithDirection.getDefaultValue();
    String nextRoadName = nextRoadTexts.names.getDefaultValue();
    String nextRoadNumber = nextRoadTexts.numbersWithDirection.getDefaultValue();

    String roadName = nextRoadName == null ? nextRoadNumber : nextRoadName;

    // On highways, we want to show the highway number instead of a possible road name,
    // while for inner city and urban areas road names are preferred over road numbers.
    if (maneuver.getNextRoadType() == RoadType.HIGHWAY) {
        roadName = nextRoadNumber == null ? nextRoadName : nextRoadNumber;
    }

    if (maneuver.getAction() == ManeuverAction.ARRIVE) {
        // We are approaching the destination, so there's no next road.
        roadName = currentRoadName == null ? currentRoadNumber : currentRoadName;
    }

    if (roadName == null) {
        // Happens only in rare cases, when also the fallback is null.
        roadName = "unnamed road";
    }

    return roadName;
}

上に示すように、currentRoadTexts.names.getDefaultValue() 経由でデフォルトの道路テキストを直接取得できます。 ほとんどの場合、この道路名はローカルの標識に示されている通りになります。

または、currentRoadTexts.names.getPreferredValueForLocales(locales)経由で、優先言語のリストに基づいて道路名のローカライズされたテキストを取得 できます。 使用できる言語がない場合は、既定の言語が返されます。

RoadTextsListenerを使用 して、現在運転中の状況RoadTexts(トラッキングモード中など)について通知を受け取ることができます。

デバイスの GPS センサーによって提供された位置が正確でない可能性があるため、VisualNavigatorは内部的に、NavigableLocation物体の一部として当社に提供されたマップマッチングした場所を計算します。 たとえば、道路の場所はナビゲーション可能なパスにあることが想定されています。 ただし、ユーザーが車を降りた場合や、 GPS 信号が弱すぎてマップマッチングした場所を見つけることができない場合にも、軌道から外れてしまうことがあります。

マップマッチングした場所を使用して、ユーザーに視覚的なフィードバックを提供することをお勧めします。 たとえば、マップマッチングした場所に基づいて現在のマップ ビューを更新します。 ユーザーがオフロードにいるときなど、位置情報をマップ上で照合できなかった場合にのみ、一致しないoriginalLocationにフォールバックすることが役立つ場合があります。 以下では、 VisualNavigator のレンダリングフィーチャを使用して、マップ ビューを自動的に更新することを選択します。

ナビゲーション中は、 ManeuverAction列挙型 (enum) によって示されている操作アイコンを視覚的インジケータとして表示することをお勧め します。また、走行を開始する前に、操作をプレビューするために、Maneuver指示テキスト (nextManeuver.getText()) がより多くのリストに収まるようにします。 これらのローカライズされた指示は記述的であり、進行中のガイダンスのコンテキスト外で理解できます。 ただし、一般 的には、オープンソースの HERE Icon ライブラリ にある対応するManeuverActionアイコンと一緒に表示できます。 詳細については 、「ルーティング 」セクションを参照してください。

反対に nextManeuver.getRoadTexts()nextManeuver.getNextRoadTexts()およびnextManeuver.getExitSignTexts() は 、ナビゲーション中のターンバイターン操作の一部として表示されます。 これらはManeuverNavigatorまたは VisualNavigatorから取得された場合にのみ空では ありません。 Route インスタンスから取得した場合、これらの属性は常に空になります。

高速道路などの一部の道路には道路名がありません。 代わりに、道路番号を取得してください。 また、世界のどこかに名前のない道路がある可能性もあることにも注意してください。

次の表に、操作手順のプロパティの使用方法を示します。

操作手順のプロパティ
RoutingEngine Navigator / VisualNavigator
manage.getText() 空でない文字列を提供します。 空でない文字列を提供します。 出力例 getText(): "Detmolder Stra ß e を A100 方面に右折します。 "
manoulation.getRoadTexts() 空の文字列を提供します。 空でない文字列を提供します。 出力例 getRoadTexts().names.getDefaultValue(): "Stadtring" 。
manoulation.getNextRoadTexts() 空の文字列を提供します。 空でない文字列を提供します。 出力例 getNextRoadTexts().names.getDefaultValue(): "Halenseestra ß e"
manoulation.getExitSignTexts() 空の文字列を提供します。 空でない文字列を提供します。 出力例 getExitSignTexts().getDefaultValue(): " ハンブルク " 。

上記のイベントを自分でトリガーする必要はありません。 代わりに、 VisualNavigator は、ロケーションプロバイダの実装から取得されたものとして、提供された場所で反応します。

ルートの逸脱が検出された場合は、ユーザーを目的地にリルーティングするかどうかを distanceInMeters に基づいて決定できます。 フルルートの再計算では、同じルートパラメータを使用することがあります。 ルートに戻る方法の詳細については、次のセクションを参照してください。

上の例では、に含まれている座標に基づいて距離を計算します RouteDeviationdistanceInMeters。 ルート上の予想される場所と実際の場所の間の直線距離を示します。 その距離が遠すぎると判断 された場合は、インスタンスVisualNavigator への新たに計算されたルートを設定できます。それ以降のすべてのイベントは、新しいルートに基づいて作成されます。

ドライブガイダンスのシナリオでは、 lastLocationOnRoutemapMatchedLocationnullの場合があることに注意してください。 routeDeviation.lastLocationOnRoutenullの場合 、ユーザーはルートを走行していませんでした。これは、開始位置が道路ネットワーク から離れている場合に発生することがあります。 通常、 Navigator/VisualNavigatorLocation の更新内容を道路と一致させようとします。 ドライバーが遠すぎると、その場所を見つけることができません。

イベントが非同期で配信されるため、キュー内の以前のイベントは、古いルートについて少なくとも 1 回は配信されることがあります。 この問題を回避するには、必要に応じて、新しいルートを設定した後で新しいリスナーを添付します。

ナビゲーションサンプルアプリ に、偏差を検出する方法が示されています。

道路標識のイベントの確認

道に沿って多くの標識を見つけることができます。 運転中に、RoadSignWarningListenerを設定することで、これらの標識に関する詳細な通知を受信できます。

結果の RoadSignWarning イベントには、RoadSignTypeRoadSignCategory などの情報を含むシールドに関する情報が含まれます。

デフォルトでは、イベントは他の警告と同じ距離のしきい値で開始されます。

  • 高速道路を利用する場合、約 2000 メートル先にイベントが発生します。
  • 田舎道では、約 1500 メートル先にイベントが発生します。
  • 都市部では、約 1000 メートル先にイベントが発生します。

RoadSignWarningOptions では、通知を受け取る標識のフィルタを設定できます。

優先道路標識の例をいくつか示します。

すべての道路標識が含まれているわけではありません。 RoadSignType はサポートされているすべてのタイプを一覧表示 たとえば 、制限速度 を示す道路標識は、専用のSpeedLimitListenerで検知できるため、除外されます。

次のコード スニペット は、使用例を示しています。

RoadSignWarningOptions roadSignWarningOptions = new RoadSignWarningOptions();
// Set a filter to get only shields relevant for TRUCKS and HEAVY_TRUCKS.
roadSignWarningOptions.vehicleTypesFilter = Arrays.asList(RoadSignVehicleType.TRUCKS, RoadSignVehicleType.HEAVY_TRUCKS);
visualNavigator.setRoadSignWarningOptions(roadSignWarningOptions);

// Notifies on road shields as they appear along the road.
visualNavigator.setRoadSignWarningListener(new RoadSignWarningListener() {
    @Override
    public void onRoadSignWarningUpdated(@NonNull RoadSignWarning roadSignWarning) {
        Log.d(TAG, "Road sign distance (m): " + roadSignWarning.distanceToRoadSignInMeters);
        Log.d(TAG, "Road sign type: " + roadSignWarning.type.name());

        if (roadSignWarning.signValue != null) {
            // Optional text as it is printed on the local road sign.
            Log.d(TAG, "Road sign text: " + roadSignWarning.signValue.text);
        }

        // For more road sign attributes, please check the API Reference.
    }
});

RoadSignWarning イベントは次の 2 回正確に発行されます。

  • DistanceTypeAHEAD で、distanceToRoadSignInMeters が 0 より大きい場合。
  • DistanceTypePASSED 0 の場合 distanceToRoadSignInMeters

スピードカメラ、道路標識、現実的なビューなど、道路沿いの単一のオブジェクトについて通知する位置警告の場合、一度に発生するアクティブな警告は常に1つだけです。 これは、各 AHEAD イベントの後に常に PASSED イベントが続くことを意味し、1つのオブジェクトに対する2つの先行警告が同時にアクティブになることを回避します。

料金所の確認

もう 1 つの警告のタイプは次の料金所でのでき事を確認するTollStopWarningListenerである。

このイベントは、他の警告と同じ距離のしきい値で開始されます。

  • 高速道路を利用する場合、約 2000 メートル先にイベントが発生します。
  • 田舎道では、約 1500 メートル先にイベントが発生します。
  • 都市部では、約 1000 メートル先にイベントが発生します。

すべての Warners と同様に、イベントはトラッキングモードおよびターン・バイ・ターンナビ (矢印ナビ)中に発行されます。

TollBoothLane のクラス内では、料金所のどの車線が車両タイプに適しているか、および受け入れられている支払い方法などの他の情報を見つけることができます。

// Notifies on upcoming toll stops. Uses the same notification
// thresholds as other warners and provides events with or without a route to follow.
visualNavigator.setTollStopWarningListener(new TollStopWarningListener() {
    @Override
    public void onTollStopWarning(@NonNull TollStop tollStop) {
        List<TollBoothLane> lanes = tollStop.lanes;

        // The lane at index 0 is the leftmost lane adjacent to the middle of the road.
        // The lane at the last index is the rightmost lane.
        int laneNumber = 0;
        for (TollBoothLane tollBoothLane : lanes) {
            // Log which vehicles types are allowed on this lane that leads to the toll booth.
            logLaneAccess(laneNumber, tollBoothLane.access);
            TollBooth tollBooth = tollBoothLane.booth;
            List<TollCollectionMethod> tollCollectionMethods = tollBooth.tollCollectionMethods;
            List<PaymentMethod> paymentMethods = tollBooth.paymentMethods;
            // The supported collection methods like ticket or automatic / electronic.
            for (TollCollectionMethod collectionMethod : tollCollectionMethods) {
                Log.d(TAG,"This toll stop supports collection via: " + collectionMethod.name());
            }
            // The supported payment methods like cash or credit card.
            for (PaymentMethod paymentMethod : paymentMethods) {
                Log.d(TAG,"This toll stop supports payment via: " + paymentMethod.name());
            }
        }
    }
});

private void logLaneAccess(int laneNumber, LaneAccess laneAccess) {
    Log.d(TAG,"Lane access for lane " + laneNumber);
    Log.d(TAG,"Automobiles are allowed on this lane: " + laneAccess.automobiles);
    Log.d(TAG,"Buses are allowed on this lane: " + laneAccess.buses);
    Log.d(TAG,"Taxis are allowed on this lane: " + laneAccess.taxis);
    Log.d(TAG,"Carpools are allowed on this lane: " + laneAccess.carpools);
    Log.d(TAG,"Pedestrians are allowed on this lane: " + laneAccess.pedestrians);
    Log.d(TAG,"Trucks are allowed on this lane: " + laneAccess.trucks);
    Log.d(TAG,"ThroughTraffic is allowed on this lane: " + laneAccess.throughTraffic);
    Log.d(TAG,"DeliveryVehicles are allowed on this lane: " + laneAccess.deliveryVehicles);
    Log.d(TAG,"EmergencyVehicles are allowed on this lane: " + laneAccess.emergencyVehicles);
    Log.d(TAG,"Motorcycles are allowed on this lane: " + laneAccess.motorcycles);
}

支払いの正確な価格や料金所の場所など、詳細な情報を Route オブジェクトの一部として利用できます。 このような情報は、旅程を開始する前にルートから抽出する場合に役立ちます。 たとえば、タップ可能なアイテム MapMarker を使用して、ルートに沿って料金所を示すことができます。 ガイダンス中に、このような詳細情報が原因でドライバーの注意が散漫になる可能性があります。 そのため、事前に情報を提供することをお勧めします。

スクールゾーンの警告イベントの処理

他の警告と同様に、専用の SchoolZoneWarningListener を設定することもできます。

// Notifies on school zones ahead.
visualNavigator.setSchoolZoneWarningListener(new SchoolZoneWarningListener() {
    @Override
    public void onSchoolZoneWarningUpdated(@NonNull List<SchoolZoneWarning> list) {
        // The list is guaranteed to be non-empty.
        for (SchoolZoneWarning schoolZoneWarning : list) {
            if (schoolZoneWarning.distanceType == DistanceType.AHEAD) {
                Log.d(TAG, "A school zone ahead in: " + schoolZoneWarning.distanceToSchoolZoneInMeters + " meters.");
                // Note that this will be the same speed limit as indicated by SpeedLimitListener, unless
                // already a lower speed limit applies, for example, because of a heavy truck load.
                Log.d(TAG, "Speed limit restriction for this school zone: " + schoolZoneWarning.speedLimitInMetersPerSecond + " m/s.");
                if (schoolZoneWarning.timeRule != null && !schoolZoneWarning.timeRule.appliesTo(new Date())) {
                    // For example, during night sometimes a school zone warning does not apply.
                    // If schoolZoneWarning.timeRule is null, the warning applies at anytime.
                    Log.d(TAG, "Note that this school zone warning currently does not apply.");
                }
            } else if (schoolZoneWarning.distanceType == DistanceType.REACHED) {
                Log.d(TAG, "A school zone has been reached.");
            } else if (schoolZoneWarning.distanceType == DistanceType.PASSED) {
                Log.d(TAG, "A school zone has been passed.");
            }
        }
    }
});

通知のスレッシュホールドはすべてのリージョンに適用され、次の方法 SchoolZoneWarningOptionsで設定できます。

SchoolZoneWarningOptions schoolZoneWarningOptions = new SchoolZoneWarningOptions();
schoolZoneWarningOptions.filterOutInactiveTimeDependentWarnings = true;
schoolZoneWarningOptions.warningDistanceInMeters = 150;
visualNavigator.setSchoolZoneWarningOptions(schoolZoneWarningOptions);

デフォルトの距離しきい値は、 API リファレンス にあります。

ルート逸脱の処理

上記のセクションで見たように、このイベント RouteDeviation を使用して、ドライバーが元ルートを離れたときに検出できます。 これは、たとえばドライバーが運転中に代替ルートやルート オプションについて以前に選択した内容を無視して、目的地まで別のルートを選択した場合など、偶然または意図的に発生する可能性があることに注意してください。

上記のように 、ドライバーの現在の位置からルート上の最後の既知の位置までの距離を検出できます。 その距離に基づいて、アプリケーションは、新しいルート全体を計算する時か、ルート代替およびルートオプションの選択を維持するためにユーザーを元のルートに誘導する時かを決定できます。

HERE SDKは ルートを自動的に再計算することはなく、偏差距離のみを記録するため、ルートに戻る方法に関するロジックはアプリ側で実装する必要があります。

ヒント: このイベントRouteDeviation は、新しいロケーションが更新されるたびに起動されます。 イベントの不必要な処理を避けるために、ドライバーがまだ逸脱しているかどうかを確認するために数秒待つことをお勧めします。 イベントが発生しなくなった場合は、ドライバーがルートに戻っていることを意味します。 ルート計算は非同期的に行われ、新しいルート計算を開始するタイミングと方法がアプリケーションによって決定されることに注意してください。 ただし、 Navigator または VisualNavigator インスタンスへのナビゲーション中にいつでも新しいルートを設定でき、今後のイベントは新しく設定された Route インスタンスに基づいて更新されます。

ユーザーがオフロードである場合もあることを言及する必要があります。 新しいルートを設定した後も、ユーザはまだオフロードにいる可能性があります。そのため、ユーザはまだルートをたどることができません。 このような場合でも、新しく設定されたルートの偏差イベントを受信し、 routeDeviation.lastLocationOnRoute は、nullになります。 ユーザーの現在の位置が変更されていない場合は、新しいルート計算を再開しないことをお勧めします。

HERE SDK には、ルートの逸脱を処理するための複数の API があります。

  1. 新しい、または更新した RouteOptions を使用して新しい代替ルートを提供するために、RoutingEngine を使用してルート全体を再計算します。 ユーザーの現在の位置を新しい開始点として使用する場合は、最初のWaypointの進行方向も指定してください 。
  2. このメソッドreturnToRoute() を使用して、最初に選択した代替ルートに到達するための新しいルートを計算します。 オンライン RoutingEngine およびOfflineRouteEngineで利用できます。 OfflineRouteEngine を使用して計算されたルート には、交通情報が含まれなくなっていることに注意してください。
  3. 元のルート上にある新しい出発地を使用して routingEngine.refreshRoute()で古いルートを更新し、必要に応じてルート オプションを更新します。RouteHandle は元のルートを識別する必要があります。 このオプションでは、逸脱した場所からルートに戻るパスは提供されないため、それ自体では、逸脱のユースケースには適していません。
  4. さらに、HERE SDKは、現在のトラフィック状況に基づいて最適化されたルートを定期的に要求できるDynamicRoutingEngineを提供します。 RouteHandleを必要とするため、オンラインで計算されたルートが必要です。 これは、ユーザーがまだルートをたどっている間に、より良いルートを見つけることを目的としています。 したがって、入力として現在の位置を必要としますが、偏差のユースケースには最適な選択ではない場合があります。

1 番めと 3 番めのオプションについては 、「ルーティング 」セクションで説明します。 元のルートを更新する 3 番目のオプションでは、逸脱した場所からルートに戻るパスは提供されません。 このため、以下では説明しません。 ただし、アプリケーションは、ルートから移動された部分を削除して、ユーザーが自分で新しい出発地点に到達できるようにするために使用できます。

逸脱した場所の距離や位置などのパラメータに基づいて、アプリケーションはドライバーに提供するオプションを決定する必要があります。

ただし、一般的な推奨事項は 、逸脱が検出された 場合にreturnToRoute()を使用することです。これは、アプリがユーザーが選択する複数の代替ルートを提供している場合に、ユーザーを元の選択した代替ルートに戻す最適なオプションであるためです。

GPS ソースから提供された場所がルートに十分近くなると、リソースを節約するためのマップマッチングが行われません。 その場所がルートから離れている場合にのみ、 HERE SDK はその場所を道路にマップしようとします。

逸脱後にルートに戻る

RoutingEngine またはOfflineRoutingEngineを使用して、元のルートに戻るルートをオンラインまたはオフラインで計算 します。 この方法returnToRoute() は、最初に選択したルートを維持しながら、ドライバーができるだけ早くルートに戻るのを支援する場合に使用します。

returnToRoute() は、ルート逸脱を処理するための選択肢の1つにすぎません。 代替オプションについては、上記を参照してください。 たとえば、場合によっては、ユーザーの目的地までの新しいルート全体を計算することをお勧めします。

現在 、このフィーチャーreturnToRoute()はエンジンと同じトランスポートモードをサポート しています。、OfflineRoutingEngineおよびRoutingEngineの両方を使用できます。 RoutingEngineでメソッドを実行する場合 、パブリックトランジットルートのみがサポートされません。RoutingEngine の他のすべての利用可能なトランスポートモードがサポートされます。

このOfflineRoutingEngineメソッドのreturnToRoute()には、キャッシュ、またはすでにダウンロードされたマップ データが必要です。 ほとんどの場合、ドライバーがルートから逸脱している間に、元のルートに戻るパスがすでにキャッシュされている可能性があります。 ただし、逸脱が大きすぎる場合は、代わりに新しいルートを計算することを検討してください。

オンラインRoutingEngineを使用する場合、下の RouteRouteHandle が含まれている必要があります。またはートの計算結果が NO_ROUTE_HANDLE エラーとなります。 OfflineRoutingEngine の場合、この操作は不要です。

ルートの計算には、次のパラメータが必要です。

  • オリジナルのRouteNavigator / VisualNavigatorから入手できます。
  • また、すでに走行したルートの一部を設定する必要があります。 この情報は RouteDeviation イベントによって提供されます。
  • 新しい開始地点の Waypoint。ドライバーの現在のマップと一致する場所である可能性があります。

新しい出発地点は 、次のイベントRouteDeviation から取得できます。

// Get current geographic coordinates.
MapMatchedLocation currentMapMatchedLocation = routeDeviation.currentLocation.mapMatchedLocation;
GeoCoordinates currentGeoCoordinates = currentMapMatchedLocation == null ?
        routeDeviation.currentLocation.originalLocation.coordinates : currentMapMatchedLocation.coordinates;

// If too far away, consider to calculate a new route instead.
Waypoint newStartingPoint = new Waypoint(currentGeoCoordinates); // See RouteDeviation.

オンラインRoutingEngine では 、完全に新しいルートが計算されることがあります。たとえば、ユーザーが以前に選択したルートの代替ルートよりも早く目的地に到達できる場合です。 OfflineRoutingEngine は 、ルートの非移動部分を誤って再利用します。

一般に、アルゴリズムは元のルートに戻る最速の方法を探しますが、宛先までの距離も考慮します。 新しいルートは、可能であれば元のルートの形状を保持しようとします。

まだ走行していないストップオーバー はスキップされません。 パススルーウェイポイントの場合、新しいルートでそれらのルートがまったく考慮される保証はありません。

任意で、ドライバーの見出し方向を設定することで、ルート計算を改善できます。

if (currentMapMatchedLocation.bearingInDegrees != null) {
    newStartingPoint.headingInDegrees = currentMapMatchedLocation.bearingInDegrees;
}

最後に、新しいルートを計算します。

routingEngine.returnToRoute(
        originalRoute,
        newStartingPoint,
        routeDeviation.lastTraveledSectionIndex,
        routeDeviation.traveledDistanceOnLastSectionInMeters, new CalculateRouteCallback() {
    @Override
    public void onRouteCalculated(@Nullable RoutingError routingError, @Nullable List<Route> list) {
        if (routingError == null) {
            Route newRoute = list.get(0);
            // ...
        } else {
            // Handle error.
        }
    }
});

CalculateRouteCallback が再利用されるため、ルートのリストが提供されます。 ただし、リストに含まれるルートは 1 つだけです。 エラー処理は、  RoutingEngineと同じロジックに従います。

オンラインおよびオフラインでの使用に関する一般的なガイドラインとして 、このフィーチャーreturnToRoute()は、その先にある originalRoute のすでに計算された部分を再利用しようとします。 トラフィックデータは、RoutingEngineが、オンラインで使用された場合にのみ更新され、反映されます。

作成された新しいルートも 、originalRouteで検出されたものと同じものOptimizationModeを使用します。

ただし、最良の結果を得る RoutingEngine には、オンラインを使用してトラフィックを最適化したルートを取得することをお勧めします。

以下に、上記の推奨事項を使用した実装例を示します。

private void handleRerouting(RouteDeviation routeDeviation,
                             int distanceInMeters,
                             GeoCoordinates currentGeoCoordinates,
                             MapMatchedLocation currentMapMatchedLocation) {
    // Counts the number of received deviation events. When the user is following a route, no deviation
    // event will occur.
    // It is recommended to await at least 3 deviation events before deciding on an action.
    deviationCounter ++;

    if (isReturningToRoute) {
        // Rerouting is ongoing.
        Log.d(TAG, "Rerouting is ongoing ...");
        return;
    }

    // When user has deviated more than distanceThresholdInMeters. Now we try to return to the original route.
    int distanceThresholdInMeters = 50;
    if (distanceInMeters > distanceThresholdInMeters && deviationCounter >= 3) {
        isReturningToRoute = true;

        // Use current location as new starting point for the route.
        Waypoint newStartingPoint = new Waypoint(currentGeoCoordinates);

        // Improve the route calculation by setting the heading direction.
        if (currentMapMatchedLocation.bearingInDegrees != null) {
            newStartingPoint.headingInDegrees = currentMapMatchedLocation.bearingInDegrees;
        }

        // In general, the return.to-route algorithm will try to find the fastest way back to the original route,
        // but it will also respect the distance to the destination. The new route will try to preserve the shape
        // of the original route if possible and it will use the same route options.
        // When the user can now reach the destination faster than with the previously chosen route, a completely new
        // route is calculated.
        Log.d(TAG, "Rerouting: Calculating a new route.");
        routingEngine.returnToRoute(lastCalculatedRoute,
                                    newStartingPoint,
                                    routeDeviation.lastTraveledSectionIndex,
                                    routeDeviation.traveledDistanceOnLastSectionInMeters,
                                    (routingError, list) -> {
            // For simplicity, we use the same route handling.
            // The previous route will be still visible on the map for reference.
            handleRouteResults(routingError, list);
            // Instruct the navigator to follow the calculated route (which will be the new one if no error occurred).
            visualNavigator.setRoute(lastCalculatedRoute);
            // Reset flag and counter.
            isReturningToRoute = false;
            deviationCounter = 0;
            Log.d(TAG, "Rerouting: New route set.");
        });
    }
}

このコードでは 、 deviationCounterを使用して、再ルーティングをあまり早く開始しないように注意してください。 さらに、フラグisReturningToRoute を使用して 、前の再ルーティングリクエストが完了する前に新しい再ルーティングリクエストが開始されないようにします。

距離のしきい値は 50 m に設定されています ユーザーがまだルートから逸脱していて、距離がこのしきい値を超える場合、再ルーティングが考慮されます。

これらのチェックを使用すると、 新しい偏差イベントを受信するたびにRouteDeviationコールバックでの再経路化の可能性を処理するためにメソッドを呼び出すことができます。 ユーザーがルートを走行している場合、偏差イベントは送信されません。 一方、再ルーティング後に新しいルートが設定された場合、新しい出発地点として現在の場所が基準になります。この場合、ユーザーがすでにもう一度逸脱していない限り、追加の偏差イベントは発生しません。 この場合、再ルーティングプロセスが再度開始されます。

より適切なルートを動的に検索

現在のトラフィック状況に基づいて、最適化されたルートを定期的に要求するには、DynamicRoutingEngine を使用します。 このエンジンは、走行中の現在のルートよりも速い( ETA に基づいて)新しいルートを検索します。

DynamicRoutingEngine には、オンライン接続と RouteHandleが必要です。 オフラインでより適切なルートを検索しようとした場合、または RouteHandleが有効になっていない 場合、ルーティングエラーが伝播されます。

// Enable route handle.
CarOptions routingOptions = new CarOptions();
routingOptions.routeOptions.enableRouteHandle = true;

DynamicRoutingEngineOptionsを設定すると、より適切なルートで通知を受け取る前にminTimeDifferenceを定義できます。 minTimeDifference は 、現在設定されているルートの残りの ETA と比較されます。 DynamicRoutingEngineOptions では、エンジンがより適切なルートを検索する頻度を決定するようにpollIntervalを設定することもできます。

private void createDynamicRoutingEngine() {
    DynamicRoutingEngineOptions dynamicRoutingOptions = new DynamicRoutingEngineOptions();
    // Both, minTimeDifference and minTimeDifferencePercentage, will be checked:
    // When the poll interval is reached, the smaller difference will win.
    dynamicRoutingOptions.minTimeDifference = Duration.ofSeconds(1);
    dynamicRoutingOptions.minTimeDifferencePercentage = 0.1;
    dynamicRoutingOptions.pollInterval = Duration.ofMinutes(5);

    try {
        // With the dynamic routing engine you can poll the HERE backend services to search for routes with less traffic.
        // This can happen during guidance - or you can periodically update a route that is shown in a route planner.
        //
        // Make sure to call dynamicRoutingEngine.updateCurrentLocation(...) to trigger execution. If this is not called,
        // no events will be delivered even if the next poll interval has been reached.
        dynamicRoutingEngine = new DynamicRoutingEngine(dynamicRoutingOptions);
    } catch (InstantiationErrorException e) {
        throw new RuntimeException("Initialization of DynamicRoutingEngine failed: " + e.error.name());
    }
}

minTimeDifference を0 に設定すると、イベントは発生しません。 同じことがminTimeDifferencePercentageにも当てはまり ます。 イベントを取得するには、値 >=0 を設定してください。

より適切なルートを受信すると、元のルートとの差 route がメートルと秒で表示されます。

private void startDynamicSearchForBetterRoutes(Route route) {
    try {
        // Note that the engine will be internally stopped, if it was started before.
        // Therefore, it is not necessary to stop the engine before starting it again.
        dynamicRoutingEngine.start(route, new DynamicRoutingListener() {
            // Notifies on traffic-optimized routes that are considered better than the current route.
            @Override
            public void onBetterRouteFound(@NonNull Route newRoute, int etaDifferenceInSeconds, int distanceDifferenceInMeters) {
                Log.d(TAG, "DynamicRoutingEngine: Calculated a new route.");
                Log.d(TAG, "DynamicRoutingEngine: etaDifferenceInSeconds: " + etaDifferenceInSeconds + ".");
                Log.d(TAG, "DynamicRoutingEngine: distanceDifferenceInMeters: " + distanceDifferenceInMeters + ".");

                String logMessage = "Calculated a new route. etaDifferenceInSeconds: " + etaDifferenceInSeconds +
                        " distanceDifferenceInMeters: " + distanceDifferenceInMeters;
                messageView.setText("DynamicRoutingEngine update: " + logMessage);

                // An implementation needs to decide when to switch to the new route based
                // on above criteria.
            }

            @Override
            public void onRoutingError(@NonNull RoutingError routingError) {
                Log.d(TAG,"Error while dynamically searching for a better route: " + routingError.name());
            }
        });
    } catch (DynamicRoutingEngine.StartException e) {
        throw new RuntimeException("Start of DynamicRoutingEngine failed. Is the RouteHandle missing?");
    }
}

提供され たetaDifferenceInSecondsdistanceDifferenceInMetersに基づい て、現在のルートと比較して、newRouteを使用するかどうかをアプリケーションが決定できます。 その場合は、いつでもVisualNavigator またはNavigatorに設定できます。

DynamicRoutingEngineは 、新たに設定されたルートを認識しません。 つまり、ルートの逸脱を検出し、たとえばroutingEngine.returnToRoute(...)を呼び出して新しいルートを並行して計算しようとする場合 、navigatorインスタンスに新しいルートが設定された後でDynamicRoutingEngineに通知する必要があります。 そのため には、stop()を呼び出してからstart(...)dynamicRoutingEngineインスタンスを呼び出し、新しいルートで再度開始します。 ルートがonBetterRouteFound() コールバックの外側に設定された navigator直後に行うことができます。 このRouteDeviationイベントを使用 すると、ユーザーがルートから逸脱した距離を計算できます ( 逸脱後のルートに戻るを参照 ) 。

簡単にするために、新しいルートを設定するための推奨フローは次のようになります。

  1. 新しいルートを設定するかどうかを決定します。
  2. 「はい」の場合は、DynamicRoutingEngineを停止します。
  3. 新しいルートを設定します:navigator.setRoute(newRoute)
  4. 新しい ルートでDynamicRoutingEngine を開始します。

イベントonBetterRouteFound() の外部で、次の手順を呼び出してください。 新しい位置情報の更新を受信する場合は、ローカルフラグを使用して上記の手順に従います。たとえば、 dynamicRoutingEngine.updateCurrentLocation(..)をコールする前に、以下を参照してください。

ガイダンス中に新しいルートを渡すと、ユーザーエクスペリエンスに影響が出る可能性があります。変更についてユーザーに通知することをお勧めします。 また、ユーザーが条件を事前に定義できるようにすることもお勧めします。 たとえば、 etaDifferenceInSeconds において新しいルートを走行する正当性はありません。

DynamicRoutingEngineは 交通情報および ETA を定期的に更新するために使用できますが 、新しいルートが異なるとは限りません。 さらに、 DynamicRoutingEnginedistanceDifferenceInMeters に情報を与えますが、ルートの長さが変更されていない場合でも、ルートの形状が同じであるとは限りません。 ただし、 ETA のみが変更され、長さが同じ場合、交通状況の更新により ETA のみが更新された可能性があります。 元のルートにとどまることが重要な場合 routingEngine.refreshRoute()は、ルートシェイプの座標を比較するか、または独自のルートを計算することを検討する必要があります。 refreshRoute()を呼び出しても ルートの形状は変更され ません。 詳細については、「ルーティング」セクションを参照してください。 反対 に、DynamicRoutingEngine の用途はより適切なルートを見つけることであり、そのためには新しいルート形状をたどって交通の障害物を回避することが最も望ましいことに注意してください。 また、より適切なルートは、前方のルートに交通障害物が存在する(または存在しなくなった)場合にのみ発見できます。

ドライバーの最後のマップマッチングした場所 を更新し、 取得後すぐにDynamicRoutingEngine ( RouteProgress またはNavigableLocation更新の一部として ) に設定してください。 これは、より適切なルートが常にドライバーの現在の位置に近い位置から開始されるようにするために重要です。

dynamicRoutingEngine.updateCurrentLocation(lastMapMatchedLocation, routeProgress.sectionIndex);

DynamicRoutingEngine は、ルート上にあるマップと一致する位置が必要です。 ユーザーがルートから逸脱している場合、RoutingError.COULD_NOT_MATCH_ORIGINが届きます。

lastMapMatchedLocationNavigableLocationListener および sectionIndexRouteProgressListener から取得できます 。 updateCurrentLocation() からイベントを受信する場合は、RouteProgressListenerを呼び出すことをお勧めします。

目的地に到着すると、エンジンは自動的に停止しません。 そのため 、エンジンが不要になったときにstop() をコールすることをお勧めします。

これの実装例について は、対応するナビゲーションの例アプリを参照してください。

ルートの交通情報を更新

交通遅延時間を含む最新の到着時間( ETA )を提供し、現在のルートを超える交通の障害物について通知することが重要です。 旅程の交通情報を更新する方法を教えてください。

次の 2 つのシナリオがあります。

  • 既存のルートにとどまる : この場合、RoutingEngineを使用して、 refreshRoute()を定期的に呼び出します。
  • 交通の障害物を迂回するための適切な代替ルートを探します。 DynamicRouteEngineを使用します。 ユーザーが新しいRouteを走行する必要がある場合は、NavigatorまたはVisualNavigator のインスタンスに設定をする必要があります。

更新された ETA 、交通遅延、交通渋滞情報を Route オブジェクトから直接取得できます。

これらのオプションの詳細については、上記 のセクションを参照してください。

ルートポリラインを独自にレンダリングする場合、ルート自体でのトラフィックの表示がサポートされます。 この例については 、「ルーティング」セクションを参照してください。 このセクションでは Route 、オブジェクトからトラフィック情報を抽出する方法についても説明します。

または 、マップでトラフィックフローレイヤをイネーブルにすることもできます。 のデフォルト設定で VisualNavigatorは、任意のズーム レベルのルートポリラインのほかに、トラフィックフロー回線が引き続き表示されます。 たとえば 、 HERE Wego アプリケーションはこのアプローチを使用して、現在の交通状況を視覚化します。

ビジュアルナビゲーターを使用してマップ ビューを更新

位置の更新に自分で対応するか、またはこの目的で VisualNavigator を使用できます。

通常、ナビゲーション中に次の操作を行います。

  • マップ上の現在の位置をトラッキングします。
  • 現在の方向を示す位置矢印を表示します。
  • マップを現在の方向に回転させます。
  • 操作矢印など、他の視覚的なアセットを追加します。

新しい位置情報イベントが発生するたびに、VisualNavigatorに送られた元の GPS 信号に基づいて計算されたマップマッチングした場所を保持する新しいNavigableLocationが作成されます。 このマップマッチング した場所を使用してマップ ビューを更新できます。

ほとんどの場合、位置情報の更新は頻繁に行われますが、個別の手順で取得することに注意してください。 つまり、各場所の間に数百メートルの距離がある可能性があります。 カメラを新しい場所に更新すると、少しジャンプすることがあります。

一方、VisualNavigatorのレンダリングフィーチャーを使用すると 、スムーズに補間された移動を利用できます。 ドライバーの速度に応じて、 2 つの位置更新の間の不足している座標が補間され、ターゲットマップの位置が自動的に更新されます。

さらに、VisualNavigatorは、マップを傾斜させ、マップを見出し方向に回転させて、 3D 位置の矢印とLocationIndicatorを表示 します。 これらはすべて、次の 1 行のコードでアクティブ化できます。

// This enables a navigation view including a rendered navigation arrow.
visualNavigator.startRendering(mapView);

スクリーンショット: デバイスで実行されているターン・バイ・ターンナビ (矢印ナビ)の例。

さらに、次のものを使用して、現在の場所のトラッキングを停止できます。

visualNavigator.setCameraBehavior(null);

次のコマンドを使用して再度有効にします。

// Alternatively, use DynamicCameraBehavior to auto-zoom the camera during guidance.
visualNavigator.setCameraBehavior(new FixedCameraBehavior());

デフォルトでは、カメラトラッキングが有効になっています。 したがって、マップは常に現在の位置を中心に配置されます。 これを一時的に無効にすると、ユーザーが手動でパニングしたり、ナビゲーションまたは追従中にマップを操作したりできます。 3D の位置を示す矢印が移動し続けますが、マップは移動しません。 カメラトラッキングモードが再び有効になると、マップが現在の位置にジャンプし、位置情報の更新内容をスムーズにトラッキングできます。

進行中のナビゲーションを停止するには、 visualNavigator.setRoute(null)を呼び出します。 上記のリスナを null にリセットするか 、ロケーションプロバイダで、stop()を呼び出してください。 詳細については 、以下のストップナビゲーションセクションを参照してください。

ソースコードの全文について は、対応するナビゲーションのサンプルアプリを確認してください。

ナビゲーション操作をカスタマイズ

NavigationCustom Example アプリで は、ナビゲーションが停止したときにカスタムLocationIndicatorおよび別のタイプに切り替える方法を示します。 また、ナビゲーションパースペクティブのカスタマイズ方法も示します。 GitHubでサンプルアプリを探します。

  • CameraBehavior では、ガイダンス中のマップ ビューの外観をカスタマイズできます。 DynamicCameraBehavior を使用して自動ズームの動作を設定 できます。または、FixedCameraBehaviorを使用して静的なチルトおよびズームの方向を設定できます。この方向は、プログラムで更新できます。 また、主点の変更などの他のオプションも使用できます。 SpeedBasedCameraBehavior には カスタマイズオプションもあり、トラッキングモードでの使用が最適です。
  • ManeuverNotificationOptions では、 TTS 音声コマンドをいつ転送するかを指定できます。

マップ ビュー のカスタマイズオプションがさらに必要な場合 は、VisualNavigator の代わりに Navigator を使用することを検討してください。 ヘッドレスNavigatorであれば、同じ機能を利用できます。 ただし、デフォルトまたはカスタマイズ可能なレンダリングオプションはありません。その代わりに、マップ ビュー 全体を独自にレンダリングできます。たとえば、より大きなルートラインやその他の視覚的なカスタマイズを使用する場合は、 HERE SDK の一般的なレンダリング機能を使用できます。

Navigatorを使用している場合、スムーズなマップエクスペリエンスをレンダリングするには、マップビューの現在のターゲット位置を自分で更新する必要があります。 ロケーションプロバイダは、新しい位置情報の更新を個別の手順でのみ送信します。これは、高頻度で配達された場合でも、「ジャンプ」マップ ビュー につながります。 そのため、 InterpolatedLocationListener を使用して、VisualNavigatorと同じスムーズな位置更新を取得することをお勧めします。

通過済みのルート

デフォルト では、VisualNavigatorは異なる色でRouteをレンダリングし、現在の位置の背後にある移動パーツをユーザーののパーツから視覚的に分離します。 これは無効化またはカスタマイズできます。 既定では、 HERE Wego モバイルアプリケーションと同じ色が使用されます。

通過済みのルートの視覚化を無効にする場合は、次を呼び出してください。

visualNavigator.setRouteProgressVisible​(false);

デフォルト VisualNavigatorColors では、昼間モードと夜間モードが使用できます。 たとえば、日中に応じて色を切り替えることができます。 デフォルトの色は、次のようにカスタマイズできます。

private void customizeVisualNavigatorColors() {
    Color routeAheadColor =  Color.valueOf(android.graphics.Color.BLUE);
    Color routeBehindColor = Color.valueOf(android.graphics.Color.RED);
    Color routeAheadOutlineColor = Color.valueOf(android.graphics.Color.YELLOW);
    Color routeBehindOutlineColor = Color.valueOf(android.graphics.Color.DKGRAY);
    Color maneuverArrowColor = Color.valueOf(android.graphics.Color.GREEN);

    VisualNavigatorColors visualNavigatorColors = VisualNavigatorColors.dayColors();
    RouteProgressColors routeProgressColors = new RouteProgressColors(
            routeAheadColor,
            routeBehindColor,
            routeAheadOutlineColor,
            routeBehindOutlineColor);

    // Sets the color used to draw maneuver arrows.
    visualNavigatorColors.setManeuverArrowColor(maneuverArrowColor);
    // Sets route color for a single transport mode. Other modes are kept using defaults.
    visualNavigatorColors.setRouteProgressColors(SectionTransportMode.CAR, routeProgressColors);
    // Sets the adjusted colors for route progress and maneuver arrows based on the day color scheme.
    visualNavigator.setColors(visualNavigatorColors);
}

これ により、パスに沿ってレンダリングされる操作矢印の色を変更して、次のターンを示すこともできます。

ウェイポイントイベントを受信

VisualNavigator / Navigator classes は、より便利な通知を提供します。 次に、渡されたウェイポイントで通知を受け取る方法の例を示します。 目的地のウェイポイントでは、次の 2 つの方法で通知を受け取ることができます。

  • 以下の最初のリスナが目的地に到達したことを通知します。そのため、ナビゲーションを停止できます。
  • 一方、 2 番目のリスナーは 、目的地のウェイポイントを含むすべてのタイプのウェイポイントについて通知を受け取る方法を示しますが 、パススルー ウェイポイントは除きます
// Notifies when the destination of the route is reached.
visualNavigator.setDestinationReachedListener(new DestinationReachedListener() {
    @Override
    public void onDestinationReached() {
        String message = "Destination reached.";
        messageView.setText(message);
        // Guidance has stopped. Now consider to, for example,
        // switch to tracking mode or stop rendering or locating or do anything else that may
        // be useful to support your app flow.
        // If the DynamicRoutingEngine was started before, consider to stop it now.
    }
});

// Notifies when a waypoint on the route is reached or missed
visualNavigator.setMilestoneStatusListener(new MilestoneStatusListener() {
    @Override
    public void onMilestoneStatusUpdated(@NonNull Milestone milestone, @NonNull MilestoneStatus milestoneStatus) {
      if (milestone.waypointIndex != null && milestoneStatus == MilestoneStatus.REACHED) {
          Log.d(TAG, "A user-defined waypoint was reached, index of waypoint: " + milestone.waypointIndex);
          Log.d(TAG,"Original coordinates: " + milestone.originalCoordinates);
      }
      else if (milestone.waypointIndex != null && milestoneStatus == MilestoneStatus.MISSED) {
          Log.d(TAG, "A user-defined waypoint was missed, index of waypoint: " + milestone.waypointIndex);
          Log.d(TAG,"Original coordinates: " + milestone.originalCoordinates);
      }
      else if (milestone.waypointIndex == null && milestoneStatus == MilestoneStatus.REACHED) {
          // For example, when transport mode changes due to a ferry a system-defined waypoint may have been added.
          Log.d(TAG, "A system-defined waypoint was reached, index of waypoint: " + milestone.mapMatchedCoordinates);
      }
     else if (milestone.waypointIndex == null && milestoneStatus == MilestoneStatus.MISSED) {
          // For example, when transport mode changes due to a ferry a system-defined waypoint may have been added.
          Log.d(TAG, "A system-defined waypoint was missed, index of waypoint: " + milestone.mapMatchedCoordinates);
      }
}
});

このメソッド onMilestoneStatusUpdated()は 、ルートに沿って渡された、または失われたウェイポイントに関する情報を含むインスタンスMilestoneを提供します。 ストップオーバー のウェイポイントのみが含まれています。 また、目的地のウェイポイントと、ユーザーが追加したその他のすべてのストップオーバーウェイポイントが含まれます。 さらに、 HERE SDK が追加したウェイポイントも含まれています。たとえば、フェリーを利用する必要がある場合などです。 ただし、旅程の出発地点である最初のウェイポイントは除外されます。 パス スルータイプのウェイポイント も除外されます。

Milestone には、ルートの計算時にユーザーが設定したウェイポイントリストを参照するインデックスが含まれています。 利用できない場合、 Milestoneは ルートの計算中に設定されたウェイポイントを参照します。たとえば、フェリーを利用する必要があることを示すために、ルーティングアルゴリズムによって追加のストップオーバーが含まれていた場合などです。

MilestoneStatus 列挙型 (enum) は、対応する Milestoneに達したか、または不在であったかを示します。

速度制限イベントの受信

SpeedLimitListener を実装すると 、道路で利用できる制限速度でイベントを受信できます。 これらは、ローカルの標識に示されている制限速度、および特定の気象条件にのみ有効な制限速度などの特殊な速度状況に関する警告になります。

条件付きとしてマークされている制限速度は、時間によって異なる場合があります。 たとえば、学校区の制限速度は、特定の時間帯にのみ有効です。 この場合、 HERE SDK はデバイスの時間を制限速度の時間範囲と比較します。 制限速度が現在有効な場合、イベントとして伝播されます。有効でない場合は、イベントとして伝播されません。

実装例については 、 GitHub にあるナビゲーションの例アプリ を参照してください。

// Notifies on the current speed limit valid on the current road.
visualNavigator.setSpeedLimitListener(new SpeedLimitListener() {
    @Override
    public void onSpeedLimitUpdated(@NonNull SpeedLimit speedLimit) {
        Double currentSpeedLimit = getCurrentSpeedLimit(speedLimit);

        if (currentSpeedLimit == null) {
            Log.d(TAG, "Warning: Speed limits unknown, data could not be retrieved.");
        } else if (currentSpeedLimit == 0) {
            Log.d(TAG, "No speed limits on this road! Drive as fast as you feel safe ...");
        } else {
            Log.d(TAG, "Current speed limit (m/s):" + currentSpeedLimit);
        }
    }
});

private Double getCurrentSpeedLimit(SpeedLimit speedLimit) {

    // Note that all values can be null if no data is available.

    // The regular speed limit if available. In case of unbounded speed limit, the value is zero.
    Log.d(TAG,"speedLimitInMetersPerSecond: " + speedLimit.speedLimitInMetersPerSecond);

    // A conditional school zone speed limit as indicated on the local road signs.
    Log.d(TAG,"schoolZoneSpeedLimitInMetersPerSecond: " + speedLimit.schoolZoneSpeedLimitInMetersPerSecond);

    // A conditional time-dependent speed limit as indicated on the local road signs.
    // It is in effect considering the current local time provided by the device's clock.
    Log.d(TAG,"timeDependentSpeedLimitInMetersPerSecond: " + speedLimit.timeDependentSpeedLimitInMetersPerSecond);

    // A conditional non-legal speed limit that recommends a lower speed,
    // for example, due to bad road conditions.
    Log.d(TAG,"advisorySpeedLimitInMetersPerSecond: " + speedLimit.advisorySpeedLimitInMetersPerSecond);

    // A weather-dependent speed limit as indicated on the local road signs.
    // The HERE SDK cannot detect the current weather condition, so a driver must decide
    // based on the situation if this speed limit applies.
    Log.d(TAG,"fogSpeedLimitInMetersPerSecond: " + speedLimit.fogSpeedLimitInMetersPerSecond);
    Log.d(TAG,"rainSpeedLimitInMetersPerSecond: " + speedLimit.rainSpeedLimitInMetersPerSecond);
    Log.d(TAG,"snowSpeedLimitInMetersPerSecond: " + speedLimit.snowSpeedLimitInMetersPerSecond);

    // For convenience, this returns the effective (lowest) speed limit between
    // - speedLimitInMetersPerSecond
    // - schoolZoneSpeedLimitInMetersPerSecond
    // - timeDependentSpeedLimitInMetersPerSecond
    return speedLimit.effectiveSpeedLimitInMetersPerSecond();
}

制限速度は、指定したトランスポートモードによって異なります。 現在、 HERE SDK は 、各国の商用車に関する法的規制( CVR )に基づいて、乗用車トラックの差別化を図っています。 つまり、上記 SpeedLimit のイベントはトラックの制限速度の低下を示している可能性があります。 たとえば、高速道路では 、制限速度はドイツ で最大 80 km/時 になります。一方、車両では、制限速度が 130 km/時 以上であることが示されている場合があります。 CVR の制限速度を取得するには、マップバージョン 32 以降を使用してください。 それより低いマップバージョンでは、トラックは車と同じ制限速度を受け取ります。 マップのバージョンは、ダウンロードされた地域がない場合でもMapUpdater で更新できます。ナビゲーションは、現在地図キャッシュに保存されているのと同じバージョンのマップ データ のみを要求するためです。 したがって、これはオンライン オフラインの両方の使用に適用されることに注意してください。

情報

トラックの場合は、 RouteOptionsの内側もTruckSpecifications を指定することをお勧めします。 トラック grossWeightInKilograms の制限速度に影響が出る可能性があります。 ほとんどの国では、法的に許可されている制限速度に影響があります。 重量が設定されていない場合、法的に許容されているトラックの最高速度制限のみが転送されます。 HERE SDK は、トラックの重量が非常に少ないと想定します。 トラックの制限速度は、地域の商用車規制( CVR )に基づいて決定されます。 日本 のような国々では、この規制が異なります。 ただし、トラック用に計算されたルートでは、車両に適した制限速度は提供されません。たとえば、トラックの重量が 3.5 T 未満の場合は、代わりに車のルートを計算することを検討してください。

注意 : トラック 運転者の場合、トラッキングモードの場合は、navigator.setTrackingTransportProfile(vehicleProfile)を呼び出しVehicleProfileてトランスポートモードTRUCKなどを使用して設定します。 デフォルト CAR では、が想定され、車両に有効な制限速度のみを受信します。車両に応じて、重量などの他の車両プロパティも指定してください。

日本 のルートの場合、TruckSpecifications経由で特殊フラグisLightTruckを設定できます。 このフラグは、トラックが車両として分類できるほど軽量であるかどうかを示します。 このフラグ は、日本 以外の国では使用できません。 これはこのフィーチャーのベータ版です。

速度警告イベントを受信

新しい制限速度イベント ( 上記を参照 ) を受信したときに、自分で制限速度を超過したことを検出できますが、アプリに速度警告フィーチャーを実装するのに役立つ、より便利なソリューションがあります。

これは、天候に応じた制限速度などの一時的な制限速度を超過した場合には警告しません。

onSpeedWarningStatusChanged() は、ドライバーが現在許可されている制限速度を超過すると、ただちに通知します。 また、制限速度を超過した後、ドライバーが再び低速で走行しているときにも通知されます。

// Notifies when the current speed limit is exceeded.
visualNavigator.setSpeedWarningListener(new SpeedWarningListener() {
    @Override
    public void onSpeedWarningStatusChanged(SpeedWarningStatus speedWarningStatus) {
        if (speedWarningStatus == SpeedWarningStatus.SPEED_LIMIT_EXCEEDED) {
            // Driver is faster than current speed limit (plus an optional offset).
            // Play a notification sound to alert the driver.
            // Note that this may not include temporary special speed limits, see SpeedLimitListener.
            Uri ringtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
            Ringtone ringtone = RingtoneManager.getRingtone(context, ringtoneUri);
            ringtone.play();
        }

        if (speedWarningStatus == SpeedWarningStatus.SPEED_LIMIT_RESTORED) {
            Log.d(TAG, "Driver is again slower than current speed limit (plus an optional offset).");
        }
    }
});

onSpeedWarningStatusChanged() では、制限速度データが利用できない場合は通知されません。 この情報は NavigableLocation 、インスタンスの一部としてのみ使用できます。

SpeedWarningStatus が 通知されるのは、現在の速度を超過した場合、または再び復元された場合のみです。たとえば、ドライバーが頻繁に運転する速度が速すぎた場合、 1 つのイベントのみが通知されます。

onSpeedWarningStatusChanged() 通知は、現在の道路の制限速度およびドライバーの速度に応じて異なります。 つまり、ルートから独立して、速度警告イベントをトラッキングモードでも取得できます。 その結果 SPEED_LIMIT_RESTORED 、ルートが変更されたときに、ドライバーの減速後にイベントを受け取ることができます。

オプションで、制限速度の値に追加されるオフセットを定義できます。 オフセットを含む制限速度を超過した場合にのみ、通知が送られます。 以下では、 2 つのオフセットを定義します。 1 つは下限のオフセットで、もう 1 つは高速の制限のオフセットです。 境界は 次の要素highSpeedBoundaryInMetersPerSecondで定義されます。

private void setupSpeedWarnings() {
    SpeedLimitOffset speedLimitOffset = new SpeedLimitOffset();
    speedLimitOffset.lowSpeedOffsetInMetersPerSecond = 2;
    speedLimitOffset.highSpeedOffsetInMetersPerSecond = 4;
    speedLimitOffset.highSpeedBoundaryInMetersPerSecond = 25;

    visualNavigator.setSpeedWarningOptions(new SpeedWarningOptions(speedLimitOffset));
}

ここでは、highSpeedBoundaryInMetersPerSecond を 25 m/s に設定しています。 制限速度の標識に 25 m/s を超える値が表示 されている場合、使用されているオフセットはhighSpeedOffsetInMetersPerSecondです。 25 m/s 未満の場合、使用されているオフセットは lowSpeedOffsetInMetersPerSecondです。

上で使用した値の例では、

  • 道路の制限速度が 27 m/s の場合、使用される(高速)オフセットは 4 m/s です これは、 31 m/s = 27 m/s + 4 m/s を超える速度で走行している場合にのみ警告通知を受け取ることを意味します highSpeedOffsetInMetersPerSecond 現在の制限速度がより大きいため、 highSpeedBoundaryInMetersPerSecondが使用されます。

  • 道路の制限速度が 20 m/s の場合、使用されている(低速)オフセットは 2 m/s です つまり、 22 m/s = 20 m/s + 2 m/s を超える速度で走行している場合にのみ、警告通知が送られます lowSpeedOffsetInMetersPerSecond 現在の制限速度がよりも小さいため、 highSpeedBoundaryInMetersPerSecondが使用されます。

負のオフセット値を設定することもできます。 これは、制限に達する前にバッファを用意して制限速度を超過しないようにする場合に役立ちます。 走行 速度が遅すぎる場合、例えば定義されているオフセットよりも遅い場合などは、前の速度警告が復元されない限り、通知を受け取ることはありません。

車両の仕様については、上記の制限速度についての規則と同じ規則が適用されます。

スピードカメラのイベントを受信

NavigatorまたはVisualNavigatorSafetyCameraWarningListenerを接続すると、ドライバーの速度を検知するカメラに通知するSafetyCameraWarningイベントについて通知を受け取ることができます。

ほとんどの国では、カメラは固定的にインストールされています。 HERE SDK は、カメラが現在アクティブかどうかを通知しません。

「スピードカメラ」とも呼ばれるセイフティカメラに関する通知を受け取ることは、国によっては利用できない場合があります。地域の法律および規制によります。 フランスなどの国では、スピードカメラの正確な位置情報は法律で禁止されています。 代わりに、政府のガイドラインを満たすためには、 ここでの通知の精度を下げることのみができます。 ただし、ほとんどの国では、正確な位置情報が許可されています。

現在、以下の国々がサポートされています。

スピードカメラのカバー範囲

  • アメリカ合衆国
  • イギリスのイギリスと北アイルランド
  • アラブ首長国連邦
  • トルコ
  • タイ
  • 台湾
  • スウェーデン
  • スペイン
  • 南アフリカ
  • スロベニア
  • スロバキア
  • シンガポール
  • セルビア
  • サウジアラビア
  • ロシア連邦
  • ルーマニア
  • カタール
  • ポルトガル
  • ポーランド
  • オマーン
  • ノルウェー
  • オランダ
  • メキシコ
  • マレーシア
  • マカオ
  • ルクセンブルグ
  • リトアニア
  • ラトビア
  • クウェート
  • カザフスタン
  • イタリア
  • イスラエル
  • マン島
  • アイスランド
  • ハンガリー
  • 香港
  • ギリシャ
  • フランス
  • フィンランド
  • エストニア
  • デンマーク
  • チェコ
  • キプロス
  • クロアチア
  • チリ
  • カナダ
  • ブルガリア
  • ブラジル
  • ボスニア・ヘルツェゴビナ
  • ベルギー
  • ベラルーシ
  • バーレーン
  • アゼルバイジャン
  • オーストリア
  • アルゼンチン
  • アンドラ

道路の属性を取得

RoadAttributesListenerを実装すると、道路属性でイベントを受信できます。 道路を走行中に属性が変更されると、イベントが開始されます。

// Notifies on the attributes of the current road including usage and physical characteristics.
visualNavigator.setRoadAttributesListener(new RoadAttributesListener() {
    @Override
    public void onRoadAttributesUpdated(@NonNull RoadAttributes roadAttributes) {
        // This is called whenever any road attribute has changed.
        // If all attributes are unchanged, no new event is fired.
        // Note that a road can have more than one attribute at the same time.

        Log.d(TAG, "Received road attributes update.");

        if (roadAttributes.isBridge) {
            // Identifies a structure that allows a road, railway, or walkway to pass over another road, railway,
            // waterway, or valley serving map display and route guidance functionalities.
            Log.d(TAG, "Road attributes: This is a bridge.");
        }
        if (roadAttributes.isControlledAccess) {
            // Controlled access roads are roads with limited entrances and exits that allow uninterrupted
            // high-speed traffic flow.
            Log.d(TAG, "Road attributes: This is a controlled access road.");
        }
        if (roadAttributes.isDirtRoad) {
            // Indicates whether the navigable segment is paved.
            Log.d(TAG, "Road attributes: This is a dirt road.");
        }
        if (roadAttributes.isDividedRoad) {
            // Indicates if there is a physical structure or painted road marking intended to legally prohibit
            // left turns in right-side driving countries, right turns in left-side driving countries,
            // and U-turns at divided intersections or in the middle of divided segments.
            Log.d(TAG, "Road attributes: This is a divided road.");
        }
        if (roadAttributes.isNoThrough) {
            // Identifies a no through road.
            Log.d(TAG, "Road attributes: This is a no through road.");
        }
        if (roadAttributes.isPrivate) {
            // Private identifies roads that are not maintained by an organization responsible for maintenance of
            // public roads.
            Log.d(TAG, "Road attributes: This is a private road.");
        }
        if (roadAttributes.isRamp) {
            // Range is a ramp: connects roads that do not intersect at grade.
            Log.d(TAG, "Road attributes: This is a ramp.");
        }
        if (roadAttributes.isRightDrivingSide) {
            // Indicates if vehicles have to drive on the right-hand side of the road or the left-hand side.
            // For example, in New York it is always true and in London always false as the United Kingdom is
            // a left-hand driving country.
            Log.d(TAG, "Road attributes: isRightDrivingSide = " + roadAttributes.isRightDrivingSide);
        }
        if (roadAttributes.isRoundabout) {
            // Indicates the presence of a roundabout.
            Log.d(TAG, "Road attributes: This is a roundabout.");
        }
        if (roadAttributes.isTollway) {
            // Identifies a road for which a fee must be paid to use the road.
            Log.d(TAG, "Road attributes change: This is a road with toll costs.");
        }
        if (roadAttributes.isTunnel) {
            // Identifies an enclosed (on all sides) passageway through or under an obstruction.
            Log.d(TAG, "Road attributes: This is a tunnel.");
        }
    }
});
}

実装例について は、 GitHub にあるナビゲーションの例アプリを参照してください。

HERE SDK 自体は、roadAttributes.isTunnelのようなイベントには反応しません。 アプリケーションは 、isTunnelが true である限りナイトマップ スキーム に切り替えを決定できます。 内部的には、 HERE SDK はこの検出アルゴリズムを提供するためにトンネル補間アルゴリズムを使用しています。これは、通常、トンネル内にいる間に GPS 信号が非常に弱いか、または失われる可能性があるためです。

レーンアシスタンスを利用

HERE SDK は、ドライバーがルートの走行を助けるために車線変更を提供します。 「いいえ Route 」に設定されている場合、車線支援は提供されません。

ジャンクションに到達する前に、 2 つの独立したリスナーを設定して、次のイベントを取得できます(交差およびラウンドバックを含む)。

  • ManeuverViewLaneAssistance: Lane ジャンクションが複雑と見なされているかどうかにかかわらず、ジャンクションで次のルート操作が行われた場合の推奨事項のリストを提供します。
  • JunctionViewLaneAssistance: Lane ジャンクションで操作が行われたかどうかにかかわらず、複雑なジャンクションでのみ推奨事項のリストを提供します。 このイベントは、非複合ジャンクションには配信されません

複合ジャンクションは次のように定義されます。

  • ジャンクションには少なくとも分岐があります。
  • ジャンクションには、現在のルートに沿っていない方向のレーンが少なくとも 2 つあります。

両方のイベントを同じジャンクションまたは異なるジャンクションに送信できます。 Lane インスタンスには、現在の道路で利用可能な車線、その方向のカテゴリ、車線が推奨されるかどうかなどの情報が含まれています。

どちらのイベントも、高速道路以外のジャンクションより 300 メートル先、高速道路のジャンクションより 1300 メートル先に発生します。 ただし、現在のところ、次の複合ジャンクションまでの距離は JunctionViewLaneAssistance イベントの一部として公開されていません。 ManeuverViewLaneAssistanceでは、 この距離は、RouteProgressイベント経由で利用できる次の操作までの距離の一部として利用できます。

各車線は、以下 LaneDirectionCategoryに保存されている複数の方向に進むことができます。

  • straight: 直進する車線。
  • slightlyLeft: 45 度前後にわずかに左に出る車線。
  • slightlyRight: 45 度前後にわずかに右に曲がる車線。
  • quiteLeft: 90 度前後にかなり左に出る車線。
  • quiteRight: 90 度前後の車線。
  • hardLeft: 135 度ほど左に曲がる車線。
  • hardRight: 135 度前後に急に曲がる車線。
  • uTurnLeft: 左に U ターンして 180 度曲がる車線。
  • uTurnRight: 右に U ターンして 180 度曲がる車線。

すべてのメンバーが 同時にtrue または false にできることに注意してください。 理論 的には、すべてのメンバーが車線がすべての複数の方向に導かれる場合にtrue になることがあります。 ただし、ほとんどの車線は 1 つまたは 2 つの方向につながっており、たとえば、車線が 2 つの別々の車線に分かれた場合、quiteLeftquiteRighttrue になります。

ドライバーに視覚的なフィードバックを提供するには、 9 つの方向ごとに 1 つの透明なイメージアセットを作成することをお勧めします。 各画像をオーバーレイとして使用し、複数の画像を 1 つの車線のピクトグラムに混合して、道路上の車線ごとに可能な道順を示すことができます。

最も重要なのは、車両がルートを走行している間に、どの車線を走行するかドライバーに知らせることができることです。 この情報はLane.recommendationStateに保存 され、推奨レーンの絵文字を強調表示することをお勧めします。

図 : 3 つのレーンがある道路で、左端の 2 つの道路が次の操作につながる可能性のある視覚化の例。

レーンアシスタンス情報 には、逆方向のレーンは含まれていません。代わりに、現在の走行方向のレーンのみが記述されています。 レーンのリストは、常に左端のレーン(インデックス 0 )から道路の右端のレーン(最後のインデックス)まで並べ替えられます。

このように、レーンアシスタンスは、左側および右側の両方の運転国で同じように機能します。

左側の運転国にいるかをroadAttributes.isRightDrivingSide で確認してください。 操縦指示およびその他の通知は、自動的に国に合わせて調整されます。 レーンアシスタンスの場合、国に関係なくコードは同じように機能します。レーンのリストは常に左からインデックス 0 から右に並べ替えられます。

ManeuverViewLaneAssistance イベントを受信した直後にイベントを表示することをお勧めします。 音声 ManeuverNotificationListener ガイダンスイベントを受信するために、イベントがと同期されます。

JunctionViewLaneAssistance イベントによって提供された車線情報は、別の UI エリアに表示することをお勧めします。これは、今後注意が必要な複雑な交差点があることを示しています。

ManeuverViewLaneAssistance との交差点で、車線変更のための推奨を受けられます

このイベントManeuverViewLaneAssistance では、操作が行われるジャンクションで推奨レーンが提供されます。 この操作は、 VisualNavigatorMapViewをレンダリングしているときに、マップ上で操作矢印によって表示されます。ジャンクションの場所は、RouteProgressイベントの一部として利用できる次のManeuverジャンクションから取得できます。

ManeuverViewLaneAssistance イベントは、ManeuverNotificationListenerによって送信された対応する操作音声通知と同期されます。 これは、ほとんどの道路で、交差点までの距離で次の操作を説明する操作音声通知テキストと同じ頻度で、イベントが同時に到着することを意味します。 以下で説明するように、このイベントは、 TTS エンジンが操作メッセージをドライバーに伝えるために使用できます。

上記の他のイベントと同様に、NavigatorまたはVisualNavigatorManeuverViewLaneAssistanceListenerを添付できます。 結果 ManeuverViewLaneAssistance のオブジェクトには、現在の道路で利用可能な車線に関する情報と、その道順などの情報が含まれます。

次のコード スニペットは、どのレーンを取得するかについての情報を取得する方法を示しています。

// Notifies which lane(s) lead to the next (next) maneuvers.
visualNavigator.setManeuverViewLaneAssistanceListener(new ManeuverViewLaneAssistanceListener() {
    @Override
    public void onLaneAssistanceUpdated(@NonNull ManeuverViewLaneAssistance maneuverViewLaneAssistance) {
        // This lane list is guaranteed to be non-empty.
        List<Lane> lanes = maneuverViewLaneAssistance.lanesForNextManeuver;
        logLaneRecommendations(lanes);

        List<Lane> nextLanes = maneuverViewLaneAssistance.lanesForNextNextManeuver;
        if (!nextLanes.isEmpty()) {
            Log.d(TAG, "Attention, the next next maneuver is very close.");
            Log.d(TAG, "Please take the following lane(s) after the next maneuver: ");
            logLaneRecommendations(nextLanes);
        }
    }
});

...

private void logLaneRecommendations(List<Lane> lanes) {
    // The lane at index 0 is the leftmost lane adjacent to the middle of the road.
    // The lane at the last index is the rightmost lane.
    int laneNumber = 0;
    for (Lane lane : lanes) {
        // This state is only possible if maneuverViewLaneAssistance.lanesForNextNextManeuver is not empty.
        // For example, when two lanes go left, this lanes leads only to the next maneuver,
        // but not to the maneuver after the next maneuver, while the highly recommended lane also leads
        // to this next next maneuver.
        if (lane.recommendationState == LaneRecommendationState.RECOMMENDED) {
            Log.d(TAG,"Lane " + laneNumber + " leads to next maneuver, but not to the next next maneuver.");
        }

        // If laneAssistance.lanesForNextNextManeuver is not empty, this lane leads also to the
        // maneuver after the next maneuver.
        if (lane.recommendationState == LaneRecommendationState.HIGHLY_RECOMMENDED) {
            Log.d(TAG,"Lane " + laneNumber + " leads to next maneuver and eventually to the next next maneuver.");
        }

        if (lane.recommendationState == LaneRecommendationState.NOT_RECOMMENDED) {
            Log.d(TAG,"Do not take lane " + laneNumber + " to follow the route.");
        }

        logLaneDetails(laneNumber, lane);

        laneNumber++;
    }
}

private void logLaneDetails(int laneNumber, Lane lane) {
    // All directions can be true or false at the same time.
    // The possible lane directions are valid independent of a route.
    // If a lane leads to multiple directions and is recommended, then all directions lead to
    // the next maneuver.
    // You can use this information like in a bitmask to visualize the possible directions
    // with a set of image overlays.
    LaneDirectionCategory laneDirectionCategory = lane.directionCategory;
    Log.d(TAG,"Directions for lane " + laneNumber);
    Log.d(TAG,"laneDirectionCategory.straight: " + laneDirectionCategory.straight);
    Log.d(TAG,"laneDirectionCategory.slightlyLeft: " + laneDirectionCategory.slightlyLeft);
    Log.d(TAG,"laneDirectionCategory.quiteLeft: " + laneDirectionCategory.quiteLeft);
    Log.d(TAG,"laneDirectionCategory.hardLeft: " + laneDirectionCategory.hardLeft);
    Log.d(TAG,"laneDirectionCategory.uTurnLeft: " + laneDirectionCategory.uTurnLeft);
    Log.d(TAG,"laneDirectionCategory.slightlyRight: " + laneDirectionCategory.slightlyRight);
    Log.d(TAG,"laneDirectionCategory.quiteRight: " + laneDirectionCategory.quiteRight);
    Log.d(TAG,"laneDirectionCategory.hardRight: " + laneDirectionCategory.hardRight);
    Log.d(TAG,"laneDirectionCategory.uTurnRight: " + laneDirectionCategory.uTurnRight);

    // More information on each lane is available in these bitmasks (boolean):
    // LaneType provides lane properties such as if parking is allowed.
    LaneType laneType = lane.type;
    // LaneAccess provides which vehicle type(s) are allowed to access this lane.
    LaneAccess laneAccess = lane.access;
}

maneuverViewLaneAssistance.lanesForNextNextManeuverは、通常空のリストですが、 2 つの操作が非常に近い場合があります。 このような場合、このリストには、現在の操作状況に達した直後に車線が取得する情報が保持されます。

次の操作に達するまで、車線に関する情報が有効になります。 次の操作に到達するか、または新しい ManeuverViewLaneAssistance イベントに含まれている情報に置き換えられたら、非表示にする必要があります。

// See above code snippet for the RouteProgressListener.
if previousManeuverIndex != nextManeuverIndex {
    // A new maneuver: Remove stale lane assistance info.
}

上記のコードRouteProgressListener を参照 すると、nextManeuverIndexの入手方法を確認できます。この方法では、新しい操作を行う必要があることが示されます。

JunctionViewLaneAssistance で、複雑なジャンクションに適したレーンを選択できます

HERE SDK は、ManeuverViewLaneAssistance(上記を参照)に加えて、ジャンクションで実際に操作が行われていない場合でも、複雑なジャンクションで利用可能なレーンについて通知するイベント JunctionViewLaneAssistance を提供します。 これらの通知は ManeuverViewLaneAssistanceと並行して動作しますが 複雑 なジャンクションに達する前にのみ発生します ( 上記を参照 ) 。

ManeuverViewLaneAssistanceと比較して、このJunctionViewLaneAssistanceイベントでは、複雑なジャンクションを安全に通過するためにより多くのレーンを推奨できますが、ジャンクションを通過した後、これらのレーンのすべてが次の操作につながるわけではありません。

ManeuverViewLaneAssistanceとは異なり 、ジャンクションが渡されたタイミングを検出するには、リストが空かどうかを確認します。

// Notfies which lane(s) allow to follow the route.
visualNavigator.setJunctionViewLaneAssistanceListener(new JunctionViewLaneAssistanceListener() {
    @Override
    public void onLaneAssistanceUpdated(@NonNull JunctionViewLaneAssistance junctionViewLaneAssistance) {
        List<Lane> lanes = junctionViewLaneAssistance.lanesForNextJunction;
        if (lanes.isEmpty()) {
            Log.d(TAG, "You have passed the complex junction.");
        } else {
            Log.d(TAG, "Attention, a complex junction is ahead.");
            logLaneRecommendations(lanes);
        }
    }
});

複合ジャンクションを通過したら、アプリ の UI を更新して車線情報を削除することをお勧めします。 JunctionViewLaneAssistance イベントは、複雑な交差点でどの車線を走行するかを示す追加のヒントと見なすことができます。特に、このような交差点で操縦が行われない場合は、この情報は ManeuverViewLaneAssistanceイベントに含まれていないためです。

ルートがない場合、車線変更に関連するイベントは発生しませんので、ご注意ください。

標識や交差点のリアルなビューを取得

RealisticViewWarningListener では、道路標識および複合交差点ビューの SVG 文字列データを 3D で受信できます。このイベントRealisticViewWarning には、標識と交差点のビュー、両方の SVG データが含まれています。 この警告は 、複雑 なジャンクションにのみ表示されます(上記を参照)。

RealisticViewWarningOptions realisticViewWarningOptions = new RealisticViewWarningOptions();
realisticViewWarningOptions.aspectRatio = AspectRatio.ASPECT_RATIO_3_X_4;
realisticViewWarningOptions.darkTheme = false;
visualNavigator.setRealisticViewWarningOptions(realisticViewWarningOptions);

// Notifies on signposts together with complex junction views.
// Signposts are shown as they appear along a road on a shield to indicate the upcoming directions and
// destinations, such as cities or road names.
// Junction views appear as a 3D visualization (as a static image) to help the driver to orientate.
//
// Optionally, you can use a feature-configuration to preload the assets as part of a Region.
//
// The event matches the notification for complex junctions, see JunctionViewLaneAssistance.
// Note that the SVG data for junction view is composed out of several 3D elements,
// a horizon and the actual junction geometry.
visualNavigator.setRealisticViewWarningListener(new RealisticViewWarningListener() {
    @Override
    public void onRealisticViewWarningUpdated(@NonNull RealisticViewWarning realisticViewWarning) {
        double distance = realisticViewWarning.distanceToRealisticViewInMeters;
        DistanceType distanceType = realisticViewWarning.distanceType;

        // Note that DistanceType.REACHED is not used for Signposts and junction views
        // as a junction is identified through a location instead of an area.
        if (distanceType == DistanceType.AHEAD) {
            Log.d(TAG, "A RealisticView ahead in: "+ distance + " meters.");
        } else if (distanceType == DistanceType.PASSED) {
            Log.d(TAG, "A RealisticView just passed.");
        }

        RealisticView realisticView = realisticViewWarning.realisticView;
        if (realisticView == null) {
            Log.d(TAG, "A RealisticView just passed. No SVG data delivered.");
            return;
        }

        String signpostSvgImageContent = realisticView.signpostSvgImageContent;
        String junctionViewSvgImageContent = realisticView.junctionViewSvgImageContent;
        // The resolution-independent SVG data can now be used in an application to visualize the image.
        // Use a SVG library of your choice to create an SVG image out of the SVG string.
        // Both SVGs contain the same dimension and the signpostSvgImageContent should be shown on top of
        // the junctionViewSvgImageContent.
        // The images can be quite detailed, therefore it is recommended to show them on a secondary display
        // in full size.
        Log.d("signpostSvgImage", signpostSvgImageContent);
        Log.d("junctionViewSvgImage", junctionViewSvgImageContent);
    }
});

realisticView.signpostSvgImageContentは 、realisticView.junctionViewSvgImageContentの上にオーバーレイすることを目的 としています。 両方の画像を同じ縦横比でリクエストできます。 このようにすると、両方の画像のサイズが同じになり、左上の同じ位置にレンダリングできます。

スクリーンショット : 標識の画像でオーバーレイされた交差点表示。

HERE SDK は SVG を文字列としてのみ提供 するため、サードパーティのライブラリ を使用して、 AndroidSVG などの SVG 文字列のコンテンツをレンダリングする必要があります。 正しいフォントを使用するために、 HERE SDK には無料のフォントパッケージが用意されています。以下を参照してください。

ジャンクションビューのデータは約 2 MB のみを占有するように最適化され、署名後のデータはわずか数 KB しか占有しません。 ただし、利用可能 なフィーチャー設定 を使用して、事前にイメージデータをプリロードすることをお勧め します。詳細については、最適化ガイドを参照してください。

16:9の解像度は横向きの形式で使用できますが、縦向きモードでも使用して、全画面表示ができないようにすることができます。 ただし、SVGアセットは非常に詳細なので、セカンダリディスプレイでフルスクリーンで表示することをお勧めします。

スピードカメラ、道路標識、現実的なビューなど、道路沿いの単一のオブジェクトについて通知する位置警告の場合、一度に発生するアクティブな警告は常に1つだけです。 これは、各 AHEAD イベントの後に常に PASSED イベントが続くことを意味し、1つのオブジェクトに対する2つの先行警告が同時にアクティブになることを回避します。

ナビゲーションの例GitHub のアプリ を参照して、使用例を確認してください。

このフィーチャーRealisticView はベータリリースとしてリリースされているため、いくつかのバグや予期しない動作が発生する可能性があります。 非推奨プロセスなしで新しいリリースでは、関連するAPIが変更される可能性があります。

SVG レンダラーおよび HERE フォントを統合

署名者の SVG をレンダリングするに は(上記を参照)、 AndroidSVG プラグインを使用することをお勧めします。 さらに、 SVG コンテンツでフォントファミリとして定義されている必要な TTF フォントが必要です。 これらのフォントは、 HERE SDK 配布パッケージに含まれています。

  1. AndroidSVG プラグインベンダーの指示に従って統合します。 ベンダーのライセンスに問題がないことを確認してください。 アプリ build.gradle のファイルを変更します。
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.aar', '*.jar'], exclude : ['*mock*.jar'])
    implementation 'androidx.appcompat:appcompat:1.3.1'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.1'

    // Needed for realistic views rendering that are provided as SVG strings.
    implementation 'com.caverock:androidsvg-aar:1.4'
}
  1. import com.caverock.androidsvg.SVG; コードにステートメントを追加し、プラグインの統合が成功したかどうかを確認します。

  2. SignpostFonts.zip HERE SDK 配布パッケージ( HERE SDK のバイナリも含むパッケージ)にあるアーカイブを展開します。 コンテンツをプロジェクト asset のフォルダーYourApp/app/src/main/assetsにコピーします。 まだ存在しない場合は、フォルダを作成します

  3. Android のAssetManagerで必要なフォントを読み込むためのフォントリゾルバークラスを作成します。 次のステップでは、プラグインによって登録されます。 プラグインがフォントを使用する必要がある場合、リゾルバを介してフォントを検索します。

import android.content.res.AssetManager;
import android.graphics.Typeface;
import android.util.Log;

import com.caverock.androidsvg.SVGExternalFileResolver;

// Needed by AndroidSVG plugin to render the provided fonts.
// Note: It has to be registered for use with the plugin.
public class FontResolver extends SVGExternalFileResolver {

    final private AssetManager assetManager;

    public FontResolver(AssetManager assetManager) {
        super();
        this.assetManager = assetManager;
    }

    @Override
    public Typeface resolveFont(String fontFamily, int fontWeight, String fontStyle) {
        try {
            return Typeface.createFromAsset(assetManager, getFontPath(fontFamily));
        } catch (RuntimeException exception) {
            Log.e("FontResolver", exception.getMessage());
        }

        return null;
    }

    private String getFontPath(String fontFamily) {
        switch (fontFamily) {
            case "FiraGO-Map":
                return "FiraGO_Map/FiraGO-Map.ttf";
            case "SignText-Bold":
                return "SignText/SignText-Bold.ttf";
            case "SignTextNarrow-Bold":
                return "SignTextNarrow/SignTextNarrow-Bold.ttf";
            case "SourceHanSansSC-Normal":
                return "SourceHanSansSC/TTF/SourceHanSansSC-Normal.ttf";

            default:
                Log.e("FontResolver", "Font not found: " + fontFamily);
                return "";
        }
    }
}
  1. SVG コンテンツをレンダリングします。
private void showRealisticViews(String signpostSvgImageContent, String junctionViewSvgImageContent) {
    ImageView signpostSvgImage = createImageView(signpostSvgImageContent);
    ImageView junctionViewSvgImage = createImageView(junctionViewSvgImageContent);

    if (signpostSvgImage == null || junctionViewSvgImage == null) {
        throw new RuntimeException("Unexpectedly, the SVG string could not be parsed.");
    }

    // Draw the signpost image on top of the junction view image.
    FrameLayout frameLayout = new FrameLayout(MainActivity.this);
    frameLayout.addView(junctionViewSvgImage);
    frameLayout.addView(signpostSvgImage);

    // Attention: In a production-app, be careful to not distract a driver.
    // It is recommended to show this on a secondary display that resets
    // automatically after some time or when the junction was passed.

    // ... now show the view.
}

private boolean registerFontResolver = true;

// Create a rendered bitmap from a raw SVG string.
// Requires com.caverock.androidsvg.SVG plugin.
@Nullable
private ImageView createImageView(String svgString) {
    // Register our font resolver with AndroidSVG.
    if (registerFontResolver) {
        registerFontResolver = false;
        SVG.registerExternalFileResolver(new FontResolver(getAssets()));
    }

    SVG svg;
    try {
        svg = SVG.getFromString(svgString);
    } catch (SVGParseException e) {
        e.printStackTrace();
        return null;
    }
    PictureDrawable pictureDrawable = new PictureDrawable(svg.renderToPicture());
    ImageView imageView = new ImageView(MainActivity.this);
    imageView.setImageDrawable(pictureDrawable);
    return imageView;
}

現在、 HERE SDK では次の TTF フォントが提供されています。 これらのコードは、商用および非商用のプロジェクトで自由に使用できます。 アーカイブSignpostFonts.zipに含まれている各フォントのライセンスファイルを確認してください。

  • SourceHanSansSC-Normal.ttf : このフォントは、主にマカオ、台湾、 Hongkong で使用されます。
  • FiraGO-Map.ttf : このフォントは主にイスラエルで使用されます。
  • SignText - Bold.ttf : このフォントは主にベトナムで使用されます。
  • SignTextNarrow - Bold .ttf : このフォントは、上記の国を除くすべての国で使用されます。

SVG コンテンツで指定されているフォントファミリが見つからない場合、通常は任意の SVG プラグインによってデフォルトのフォントがレンダリングされますが、期待どおりに表示されない場合があります。

環境ゾーンの警告を取得

環境ゾーンは、 LEZ ( Low Emission Zones )または CAZ ( Clean Air Zones )とも呼ばれ、大気の質を改善し、汚染を低減するために特定の制限または規制が実施されている都市または地域内の指定地域です。 これらのゾーンは、二酸化窒素( NO2 )や粒子状物質( PM )など、高レベルの汚染物質を排出する車両の侵入を阻止または制限することを目的としています。

環境ゾーンの特定の規則および規制は、都市や国によって異なる場合があります。 通常、特定の排出ガス基準を満たしていない車両は、その地域への侵入を禁止されているか、または料金を支払う必要があります。

環境ゾーンの指定および対応する規則は、輸送機関および環境機関と協力して、地域または地方の当局によって決定されます。

HERE SDK は、次のような今後の環境ゾーンで知られています。

visualNavigator.setEnvironmentalZoneWarningListener(new EnvironmentalZoneWarningListener() {
    @Override
    public void onEnvironmentalZoneWarningsUpdated(@NonNull List<EnvironmentalZoneWarning> list) {
        // The list is guaranteed to be non-empty.
        for (EnvironmentalZoneWarning environmentalZoneWarning : list) {
            DistanceType distanceType = environmentalZoneWarning.distanceType;
            if (distanceType == DistanceType.AHEAD) {
                Log.d(TAG, "A EnvironmentalZone ahead in: "+ environmentalZoneWarning.distanceInMeters + " meters.");
            } else if (distanceType == DistanceType.REACHED) {
                Log.d(TAG, "A EnvironmentalZone has been reached.");
            } else if (distanceType == DistanceType.PASSED) {
                Log.d(TAG, "A EnvironmentalZone just passed.");
            }

            // The official name of the environmental zone (example: "Zone basse émission Bruxelles").
            String name = environmentalZoneWarning.name;
            // The description of the environmental zone for the default language.
            String description = environmentalZoneWarning.description.getDefaultValue();
            // The environmental zone ID - uniquely identifies the zone in the HERE map data.
            String zoneID = environmentalZoneWarning.zoneId;
            // The website of the environmental zone, if available - null otherwise.
            String websiteUrl = environmentalZoneWarning.websiteUrl;
            Log.d(TAG, "environmentalZoneWarning: description: " + description);
            Log.d(TAG, "environmentalZoneWarning: name: " + name);
            Log.d(TAG, "environmentalZoneWarning: zoneID: " + zoneID);
            Log.d(TAG, "environmentalZoneWarning: websiteUrl: " + websiteUrl);
        }
    }
});

EnvironmentalZoneWarningOptions を都市部、高速道路、および地方道路タイプのデフォルトの距離通知のしきい値を変更する場合に使用します。 以下のも参照してください。

通知頻度

RouteProgressなどのすべてのイベントは、位置情報の更新に応じて送信されます。 LocationEngineを使用する場合、 LocationAccuracy.navigationは、少なくとも 1 秒近くの更新頻度を使用してください。

このルールの例外は、 RouteDeviationRoadAttributesRoadTextsMilestoneManeuverViewLaneAssistance、イベントなどのイベント駆動型の通知です。 各場所を更新した後、そのようなイベントを配信する必要があるかどうかを確認します。 通常、これは現在の道路で属性が変更されたときに発生します。

一部のイベントでは、経由の操作通知テキストなどのしきい値を設定でき ManeuverNotificationTimingOptionsます。

SpeedLimitSafetyCameraWarningTollStopRoadSignWarningRealisticViewWarning 、、などの Warner の場合、通知のしきい値 ( TruckRestrictionWarning イベントを除く、以下を参照 ) は次のとおりです。

  • 都市で AHEAD は、 1000 メートル先にイベントが送信されます。
  • 田舎道では、イベントは 1500 m 先に送られます。
  • 高速道路を利用する場合は、 2000 m 先にイベントが送られます。

    TruckRestrictionWarning イベントの通知しきい値 :

  • 都市では、このイベントAHEAD は 500 メートル先に送られます。

  • 田舎道では、 750 メートル先にイベントが送られます。
  • 高速道路を利用する場合は、 1500 m 先にイベントが送られます。

トラッキングモードでは、すべての警告がサポートされます。 これは、特定のルートに沿って、または目的地に到着した後で、自由にドライブを行っている場合にも、各警告タイプのイベントを取得することを意味します。

さらに、すべての警告は 、明示的に記述されている場合を除き、ルートまたはRouteOptionsセットに固有の ものではありません ( トラックの制限速度など ) 。 たとえば、 徒歩ルートを追従している間にイベントTruckRestrictionWarningを受け取ることができます ( 必要な場合 ) 。 一般に、警告イベントは、ナビゲーターに供給されたマップマッチングした場所に基づいて生成されます。 たとえば、歩行者ルートは、 GPS 信号の精度のために横方向を無視する他の交通モードと同様に、道路の同じ側にマップマッチングされることがよくあります。

情報

アプリケーションが特定の危険な場所について警告を発したい場合があります。 たとえば、車両の前方にある障害物までの距離を表示します。 このような場合は、 navigator.calculateRemainingDistanceInMeters​(GeoCoordinates coordinates) ルート上のユーザーの現在の位置と、そのユーザーが coordinates 前方のルートにいる場合に指定された位置との間の距離を提供する便利な方法を使用することを検討してください。 が coordinates すでにルートの背後にある場合、またはルートにまったくない場合は null 、が返されます。 この方法では、アプリが coordinates 問題の発生先を把握している必要があります。

トラック案内

HERE SDK は、さまざまなフィーチャーを備えたプレミアムトラックの取り回しおよびガイダンスをサポートしています。 たとえば、ナビゲーション中に、狭いトンネルなどのトラックの前方の制限について通知を受け取るようにリスナーを添付できます。 他にも、大型トラックや、トラックの重量が道路の許容重量を超える道路を通過するのに十分な高さではない橋が制限されることがあります。

次のコード スニペットを参照してください。

// Notifies truck drivers on road restrictions ahead. Called whenever there is a change.
visualNavigator.setTruckRestrictionsWarningListener(new TruckRestrictionsWarningListener() {
    @Override
    public void onTruckRestrictionsWarningUpdated(@NonNull List<TruckRestrictionWarning> list) {
        // The list is guaranteed to be non-empty.
        for (TruckRestrictionWarning truckRestrictionWarning : list) {
            if (truckRestrictionWarning.distanceType == DistanceType.AHEAD) {
                Log.d(TAG, "TruckRestrictionWarning ahead in: "+ truckRestrictionWarning.distanceInMeters + " meters.");
            } else if (truckRestrictionWarning.distanceType == DistanceType.REACHED) {
                Log.d(TAG, "A restriction has been reached.");
            } else if (truckRestrictionWarning.distanceType == DistanceType.PASSED) {
                // If not preceded by a "REACHED"-notification, this restriction was valid only for the passed location.
                Log.d(TAG, "A restriction just passed.");
            }

            // One of the following restrictions applies ahead, if more restrictions apply at the same time,
            // they are part of another TruckRestrictionWarning element contained in the list.
            if (truckRestrictionWarning.weightRestriction != null) {
                WeightRestrictionType type = truckRestrictionWarning.weightRestriction.type;
                int value = truckRestrictionWarning.weightRestriction.valueInKilograms;
                Log.d(TAG, "TruckRestriction for weight (kg): " + type.name() + ": " + value);
            } else if (truckRestrictionWarning.dimensionRestriction != null) {
                // Can be either a length, width or height restriction of the truck. For example, a height
                // restriction can apply for a tunnel. Other possible restrictions are delivered in
                // separate TruckRestrictionWarning objects contained in the list, if any.
                DimensionRestrictionType type = truckRestrictionWarning.dimensionRestriction.type;
                int value = truckRestrictionWarning.dimensionRestriction.valueInCentimeters;
                Log.d(TAG, "TruckRestriction for dimension: " + type.name() + ": " + value);
            } else {
                Log.d(TAG, "TruckRestriction: General restriction - no trucks allowed.");
            }
        }
    }
});

DistanceType.REACHEDは 、トラックの制限に達したことを通知します。 制限が渡されると、イベントの後にPASSEDが渡されます。 制限に長さがない場合、 REACHED はスキップ され、イベントPASSED のみが送信されます。 AHEAD イベントは常に最初に送信されます。

各距離タイプの制限は、 1 回だけ正確に付与されます。 更新された距離情報をドライバーに継続的に通知する場合 RouteProgress は、目的地までの頻繁な距離更新を含むをトラッキングすることで通知できます。

すべての制限がない場合は、一般的なトラックの制限が適用されます。 制限のタイプは、TruckRestrictionWarningTypeからも参照 できます。

制限の警告を マップのMapFeatures.VEHICLE_RESTRICTIONSレイヤーと比較する場合、 道路の 1 つの方向にのみ有効な制限があることに注意してください。

nullルート または 新しいルートを設定してガイダンスを停止すると 、AHEAD通知で発行された制限は、保留中の制限警告をクリアするための PASSEDイベントが即座に発生します。ルートを追従中 - ルートにないすべての制限がフィルタリングされますが、ドライバーがルートから十分に離れた( 15 メートルを超える)とただちに、現在の道路で前方にサポートされている制限が適用され、再び制限警告が表示されます。

トラックの制限に関する通知しきい値は、他の警告とわずかに異なり ます。 ここ に記載されているしきい値を確認してください。

このイベントTruckRestrictionWarning は、前方の道路網のマップ データに基づいています。 現在設定されている内容にかかわらず、制限が適用 TransportModeされます。

ルートを計算するとき に、次TruckSpecificationsを含むTruckOptions を指定でき ます。 これは結果Routeに影響を与える可能性があります。 ただし 、TruckRestrictionWarningイベントには影響しません。 先方のマップ データで検出されたほとんどの制限事項が転送されます。 そのため、現在の車両に関連しない制限警告をアプリケーションがフィルタリングすることは理にかなっている場合があります。 このイベントでは、トラッキングするルートがない場合にもトラッキングモードでイベントが配信されます。

トラックルートの詳細については 、「ルーティング 」セクションを参照してください。 たとえば、トラック専用のルートの計算方法を見つけることができます。 一般に、ルートに Truck 輸送タイプが含まれている場合、トラック用に最適化されます。

さらに、いくつかの回避オプションを指定して、たとえば特定の都市部を除外できます。 これはすべて、ルートが計算され てNavigatorまたは、VisualNavigatorに渡される前に指定できます。

以下のフィーチャーについても説明します。

  • トラックの寸法などの車両の制限を指定したり、トラックが危険物を運搬している場合に、TruckSpecificationsおよびHazardousGoodリストを含むことができるように指定できます。 TruckOptionsこの情報を使用して、トラックルートを形成できます。 トラックの制限事項について通知を受け取るに TruckRestrictionWarning は、上記のようにイベントを聴いてください。
  • RoadAttributes前述のように特定の音声を聞くことができます。
  • トランスポートモードがTRUCKに設定 されている場合、SpeedLimitイベントは商用車の規制( CVR )制限速度を示します。これは、車両の制限速度よりも低い可能性があります。 ルートを計算するときは、RouteOptionsの内部TruckSpecificationsも指定することを検討してください。 トラッキングモードの場合 は、navigator.setTrackingTransportProfile(vehicleProfile)を呼び出して、TRUCKトランスポートモードでVehicleProfileを設定します。 デフォルトでは、トラッキングは、CAR が想定されます。 トラックに応じて重量などの他の車両プロパティを必ず指定してください。
  • 言及する価値があるのは、grossWeightInKilogramsweightInKilogramsは CVR の速度制限、ルート制限、到着予想時間に影響します。適切に TruckSpecificationsを設定しないと、ルートおよび通知が不適切になる可能性があります。
  • AvoidanceOptionsを使用して、排出ゾーンを除外して、良識ある都心部の空気を汚染しないようにすることができます。この機能を使用 すると、特定の類似RoadFeaturesのトンネルを回避することもできます。 これらはTruckOptions経由で設定でき、ルート計算から除外されます。
  • マップにスピードカメラのアイコンを表示するマップ レイヤースキームを有効にできます:MapScene.Layers.SAFETY_CAMERAS。注 : このレイヤーは車にも適しています。
  • トラック固有の情報をマップに表示するように最適化されたマップ レイヤー スキームを有効にできます:MapScene.Layers.VEHICLE_RESTRICTIONS。 たとえば、 影響を受ける道路上でアクティブおよび 非アクティブな制限を紫色の線で強調表示するための いくつかのMapFeatureModesが提供されます。灰色の線または灰色のアイコンは、制限が非アクティブであることを示します。 道路がこのような紫の線を横切っており、その道路自体が紫で示されていない場合、この制限は現在の道路には適用されません。 アイコンが正確な場所を示すとは限りません。 たとえば、制限された道路の場合、アイコンが制限された道路の中央に配置されます。または、制限がより長い場合は、 1 つ以上の道路に沿って同じ制限についてアイコンを複数回繰り返すことができます。 アイコン自体は国ごとにローカライズされ、制限のタイプを表します。 ほとんどの制限では、制限の場所と種類も TruckRestrictionWarning イベントによって示されます ( 上図を参照 ) 。
  • PlaceDetails にシャワーやトイレの情報が含まれている TruckAmenities を使用します。ルート沿いの廊下を探して、トラックのアメニティが入っている場所がないか確認してください。 この機能を有効にするには、"show"nameとして、 "truck"valueとして指定して searchEngine.setCustomOption()を呼び出します。

MapScene.Layers.Vehicle_restrictions

オフロードガイダンス

オフロードガイダンスを利用すると、オフロードの目的地に到着するのを支援できます。 通常は、マップに一致する目的地でガイダンスが停止します。 目的地が休息地の中央にある場合など、目的地が道路ネットワークにマップマッチングできない場合、目的地はオフロードと見なされます。

スクリーンショット : オフロードガイダンス

目的地がオフロードかどうかを検出し、マップに一致する目的地に到達したときにユーザーに通知できます。

// Notifies when the destination of the route is reached.
visualNavigator.setDestinationReachedListener(new DestinationReachedListener() {
    @Override
    public void onDestinationReached() {
        Section lastSection = lastCalculatedRoute.getSections().get(lastCalculatedRoute.getSections().size() - 1);
        if (lastSection.getArrivalPlace().isOffRoad()) {
            Log.d(TAG, "End of navigable route reached.");
            String message1 = "Your destination is off-road.";
            String message2 = "Follow the dashed line with caution.";
            // Note that for this example we inform the user via UI.
            uiCallback.onManeuverEvent(ManeuverAction.ARRIVE, message1, message2);
        } else {
            Log.d(TAG, "Destination reached.");
            String distanceText = "0 m";
            String message = "You have reached your destination.";
            uiCallback.onManeuverEvent(ManeuverAction.ARRIVE, distanceText, message);
        }
    }
});

この例では、ビューを更新するために、アプリ側の uiCallback メカニズムを使用します。 このコードは こことは関係がないため、省略されています。 このドキュメントは、 GitHub の「 Rerouting のExample アプリ」に記載されています。

マップと一致する目的地(たとえば、前方車両につながる道路) に達していない場合、ユーザーはオフロードガイダンスを受けられません。 オフロードガイダンスは、 onDestinationReached() イベントの受信後にのみ開始されます。

オフロードイベントが開始されると、 新しいルートが設定されていない限り、通常のガイダンスモードに戻ってイベントRouteProgress を受信する方法はありません。

オフロード走行先にガイダンスを提供するには、次のコードを追加します。

// Notifies on the progress when heading towards an off-road destination.
// Off-road progress events will be sent only after the user has reached
// the map-matched destination and the original destination is off-road.
// Note that when a location cannot be map-matched to a road, then it is considered
// to be off-road.
visualNavigator.setOffRoadProgressListener(new OffRoadProgressListener() {
    @Override
    public void onOffRoadProgressUpdated(@NonNull OffRoadProgress offRoadProgress) {
        String distanceText = convertDistance(offRoadProgress.remainingDistanceInMeters);
        // Bearing of the destination compared to the user's current position.
        // The bearing angle indicates the direction into which the user should walk in order
        // to reach the off-road destination - when the device is held up in north-up direction.
        // For example, when the top of the screen points to true north, then 180° means that
        // the destination lies in south direction. 315° would mean the user has to head north-west, and so on.
        String message = "Direction of your destination: " + Math.round(offRoadProgress.bearingInDegrees) + "°";
        uiCallback.onManeuverEvent(ManeuverAction.ARRIVE, distanceText, message);
    }
});

// Notifies when the off-road destination of the route has been reached (if any).
visualNavigator.setOffRoadDestinationReachedListener(new OffRoadDestinationReachedListener() {
    @Override
    public void onOffRoadDestinationReached() {
        Log.d(TAG, "Off-road destination reached.");
        String distanceText = "0 m";
        String message = "You have reached your off-road destination.";
        uiCallback.onManeuverEvent(ManeuverAction.ARRIVE, distanceText, message);
    }
});

この例では、アプリサイドの convertDistance() のメソッドを使用して、距離をメートルおよびキロメートルに変換します。 このコードは HERE SDK での使用には適していません。したがって、このコードの実装は上記では省略されています。

オフロードガイダンスでは、ユーザーがマップで一致した目的地に到着する前に支援を提供するものではありません。 また、 ユーザーが道路から逸脱したり、不明な道路を走行したりした場合など、走行のオフロード走行はサポートされません。 ユーザーが既知の道路ネットワークで到達可能なルートの最後の地点に到達した場合にのみ、オフロードガイダンスが開始されます。

デフォルトでは、上記のコードがない場合でも、オフロード目的地につながる破線がマップに表示されます。 これは次のように制御できます。

// Enable off-road visualization (if any) with a dotted straight-line
// between the map-matched and the original destination (which is off-road).
// Note that the color of the dashed line can be customized, if desired.
// The line will not be rendered if the destination is not off-road.
// By default, this is enabled.
visualNavigator.setOffRoadDestinationVisible(true);

このフィーチャーは 、ユーザーを経路に沿って案内するものではなく、何らかの操作情報を提供するものでもありません。 提供された情報は慎重に取り扱うようにしてください。 ユーザーの現在位置からオフロード目的地まで直線のみが引かれます。 現実には、目的地が到達不能であるか、歩行者が移動できない危険な地域にある可能性があります。 ユーザーに適宜通知してください。

ロケーションプロバイダを実装

VisualNavigatorLocation インスタンスを提供するには、ロケーションプロバイダが必要 です。 任意のソースから位置データをフィードできます。 以下では、デバイスからのネイティブの位置データ テストドライブのシミュレートされた位置データを切り替えることができる実装を使用する予定です。

すでに説明したように、VisualNavigatorLocationListenerインターフェイスに準拠しているため、onLocationUpdated()を呼び出すクラスのリスナーとして使用できます。

位置情報データのソースとして、「 位置情報を検索 」セクションに示されているコードに基づいたHEREPositioningProviderを使用します。

ナビゲーションでは LocationAccuracy.NAVIGATIONLocationEngine ターン・バイ・ターンナビ (矢印ナビ)中に最良の結果が保証されるように、の開始時にを使用することをお勧めします。

イベントを配信するには、herePositioningProviderを開始する必要があります 。

// Call this to start getting locations from a device's sensor.
herePositioningProvider.startLocating(visualNavigator, LocationAccuracy.NAVIGATION);

必要な HERE SDKの Location タイプには、ベアリングおよび速度情報、現在の地理座標、 VisualNavigatorによって消費されるその他の情報が含まれます。 提供されたデータの正確性と完全性が高いほど、ナビゲーション全体の操作性がより正確になります。

Locationオブジェクトから取得されたbearing値によって 移動の方向が決まり 、 その方向に回転するLocationIndicatorアセットによって示されます。 ユーザーが移動していない場合、新しいベアリング値が設定されるまで最後のローテーション値が保持されます。 Location データのソースに応じて、この値の精度を増減できます。

内部的には 、Locationtimestamp が使用され、ユーザーがトンネルを通過しているか、または信号が単に失われたかなどを評価します。

GitHub で、ロケーションプロバイダのリファレンス実装を見つける ことができます。

位置情報シミュレータを設定

開発中に、テスト目的でルート上で予想される進行状況を再生すると便利な場合があります。 は LocationSimulator 、元のルート座標から取得された連続的な位置情報を提供します。

以下では、 LocationSimulator を代替プロバイダとして統合し、実際の位置情報の更新とシミュレートされた位置情報の切り替えを可能にしています。

import com.here.sdk.core.LocationListener;
import com.here.sdk.core.errors.InstantiationErrorException;
import com.here.sdk.navigation.LocationSimulator;
import com.here.sdk.navigation.LocationSimulatorOptions;
import com.here.sdk.routing.Route;

// A class that provides simulated location updates along a given route.
// The frequency of the provided updates can be set via LocationSimulatorOptions.
public class HEREPositioningSimulator {

    private LocationSimulator locationSimulator;

    // Starts route playback.
    public void startLocating(LocationListener locationListener, Route route) {
        if (locationSimulator != null) {
            locationSimulator.stop();
        }

        locationSimulator = createLocationSimulator(locationListener, route);
        locationSimulator.start();
    }

    public void stopLocating() {
        if (locationSimulator != null) {
            locationSimulator.stop();
            locationSimulator = null;
        }
    }

    // Provides fake GPS signals based on the route geometry.
    private LocationSimulator createLocationSimulator(LocationListener locationListener, Route route) {
        LocationSimulatorOptions locationSimulatorOptions = new LocationSimulatorOptions();
        locationSimulatorOptions.speedFactor = 2;
        locationSimulatorOptions.notificationInterval = Duration.ofMillis(500);

        LocationSimulator locationSimulator;

        try {
            locationSimulator = new LocationSimulator(route, locationSimulatorOptions);
        } catch (InstantiationErrorException e) {
            throw new RuntimeException("Initialization of LocationSimulator failed: " + e.error.name());
        }

        locationSimulator.setListener(locationListener);

        return locationSimulator;
    }
}

また、LocationSimulatorOptionsを設定することで 、現在のシミュレート位置の移動速度を指定することができます。 デフォルトでは 、notificationInterval は1で、speedFactorは 1.0です。これは、トラフィック関連の制約を考慮せずに、ユーザが通常運転または各ルートセグメントを歩く平均速度に等しくなります。 デフォルトの速度は、道路形状、道路状況、その他の統計データによって異なる場合がありますが、現在の制限速度を超えることはありません。 値が1.0を超えると、速度が比例して増加します。 ルートに指定した時間間隔で十分な座標が含まれていない場合は、によって追加のロケーションイベントが補間 VisualNavigatorされます。

LocationSimulatorによって放出される位置は 補間されず、ソースに基づいて提供されます。 の場合 Route、ルート形状の座標が使用されます(互いに非常に近い)。 の場合 GPXTrack、座標はGPXデータに基づいて出力されます。 たとえば、2つの座標の間に100メートルのメートルがある場合、それらの2つの座標だけが時間設定に基づいて放出されます。 ただし、に入力すると VisualNavigator、レンダリングされたマップアニメーションはによって補間 VisualNavigatorされます。

VisualNavigator 連続 Location する更新間の距離が 100 m を超える場合、はアニメーションをスキップします が speedFactor 増加すると、ロケーションの更新間隔も変化します。通知間隔が適切に調整されていない場合は、次のようになります。 たとえば、速度係数を 8 に変更する場合は、 Location 通知間隔を 125 ms (1000 ms/8) に変更して、更新間の距離を一貫したものにする必要があります。 notificationInterval および speedFactor は反比例します。 したがって、 A speedFactor が 3 の場合、 330 notificationInterval ms を推奨します。

次のコードは 、 enableRoutePlayback()enableDevicePositioning()を呼び出して、シミュレートされた場所実際の場所をシームレスに切り替える方法を示し ています。

// Provides simulated location updates based on the given route.
public void enableRoutePlayback(Route route) {
    herePositioningProvider.stopLocating();
    herePositioningSimulator.startLocating(visualNavigator, route);
}

// Provides location updates based on the device's GPS sensor.
public void enableDevicePositioning() {
    herePositioningSimulator.stopLocating();
    herePositioningProvider.startLocating(visualNavigator, LocationAccuracy.NAVIGATION);
}

新しいシミュレーションまたは実ロケーションソースを開始する前に、進行中のシミュレーションまたは実ロケーションソースを必ず停止する必要があります。

GitHub のナビゲーションサンプルアプリに含ま れている上記のコードを確認できます。

音声ガイダンス

運転中は、ユーザーの注意をルートに集中させる必要があります。 提供されている操作データから視覚的な表現を作成できますが ( 上記を参照 ) 、ターンバイターンガイダンス中に話すことを目的としたローカライズされたテキスト表現を取得することもできます。 これらの操作手順の通知Stringとして提供されるため 、任意の TTS ソリューションと一緒に使用できます。

操作の通知はドライバーを対象としています。 歩行者の案内には使用しないことをお勧めします。

HERE SDK には、事前に録音された音声スキンは含まれていません。 つまり、再生のために TTS エンジンを統合する必要があります。 オーディオ再生の詳細および例については、以下を参照してください。

通知の例 ( 文字列で提供 ) :

Voice message: After 1 kilometer turn left onto North Blaney Avenue.
Voice message: Now turn left.
Voice message: After 1 kilometer turn right onto Forest Avenue.
Voice message: Now turn right.
Voice message: After 400 meters turn right onto Park Avenue.
Voice message: Now turn right.

これらの通知を取得するには ManeuverNotificationListener、を設定します。

visualNavigator.setManeuverNotificationListener(new ManeuverNotificationListener() {
    @Override
    public void onManeuverNotification(@NonNull String voiceText) {
        voiceAssistant.speak(voiceText);
    }
});

ここでは、操作通知を読み上げるための Text-To-Speech エンジンをラップする VoiceAssistantというヘルパー クラスを使用します。このエンジンは Android TextToSpeech のクラスを使用しています。 必要に 応じて、このクラスを GitHubナビゲーションサンプルアプリの一部として見つけることができます。

オプションで 、ナチュラルガイダンスを有効にすることもできます。 ManeuverNotification テキストは、ルートに沿って重要なオブジェクト(信号機や停止標識など)を含めるように拡張して、操作をより理解しやすくすることができます。 例: "次 の信号で 左折してウォールストリートに入ります。" デフォルトでは、このフィーチャーは無効になっています。 これを有効にするには、 includedNaturalGuidanceTypesのリストを介して ManeuverNotificationOptionsTRAFFIC_LIGHTなどの NaturalGuidanceType を少なくとも 1 つ追加します。

LanguageCodeを設定して通知テキストをローカライズし、UnitSystem を設定できます。 メートル法またはインペリアル法の長さの単位を決定します。ルートを設定する前に、必ずこれを呼び出してください。そうでない場合、デフォルトの設定は (EN_US,METRIC) です。 詳細については ManeuverNotificationOptions、 API リファレンス を参照してください。

private void setupVoiceGuidance() {
    LanguageCode ttsLanguageCode = getLanguageCodeForDevice(VisualNavigator.getAvailableLanguagesForManeuverNotifications());
    visualNavigator.setManeuverNotificationOptions(new ManeuverNotificationOptions(ttsLanguageCode, UnitSystem.METRIC));

    // Set language to our TextToSpeech engine.
    Locale locale = LanguageCodeConverter.getLocale(ttsLanguageCode);
    if (voiceAssistant.setLanguage(locale)) {
        Log.d(TAG, "TextToSpeech engine uses this language: " + locale);
    } else {
        Log.e(TAG, "TextToSpeech engine does not support this language: " + locale);
    }
}

この例では、次に示すデバイスの優先言語設定を使用します。

private LanguageCode getLanguageCodeForDevice(List<LanguageCode> supportedVoiceSkins) {

    // 1. Determine if preferred device language is supported by our TextToSpeech engine.
    Locale localeForCurrenDevice = Locale.getDefault();
    if (!voiceAssistant.isLanguageAvailable(localeForCurrenDevice)) {
        Log.e(TAG, "TextToSpeech engine does not support: " + localeForCurrenDevice + ", falling back to EN_US.");
        localeForCurrenDevice = new Locale("en", "US");
    }

    // 2. Determine supported voice skins from HERE SDK.
    LanguageCode languageCodeForCurrenDevice = LanguageCodeConverter.getLanguageCode(localeForCurrenDevice);
    if (!supportedVoiceSkins.contains(languageCodeForCurrenDevice)) {
        Log.e(TAG, "No voice skins available for " + languageCodeForCurrenDevice + ", falling back to EN_US.");
        languageCodeForCurrenDevice = LanguageCode.EN_US;
    }

    return languageCodeForCurrenDevice;
}

内部的 には、RouteProgressイベントからアクセスできるManeuverデータから操作の通知テキストが生成されます。 RouteProgress イベントは、パスした位置の更新に基づいて頻繁に生成されます。 操作の通知では、テキストメッセージを生成する頻度とタイミングを指定できます。 これは、ManeuverNotificationTimingOptionsを使用して指定できます。

ManeuverNotificationTypeについて、各トランスポートモードおよび道路タイプをManeuverNotificationTimingOptionsに設定できます。 また、 距離をメートル単位 または 秒単位で指定することもできます。

  1. ManeuverNotificationType.RANGE: 1 番目の通知。 rangeNotificationDistanceInMeters またはrangeNotificationTimeInSecondsを使用して指定します。
  2. ManeuverNotificationType.REMINDER: 2 番目の通知。 reminderNotificationDistanceInMeters またはreminderNotificationTimeInSecondsを使用して指定します。
  3. ManeuverNotificationType.DISTANCE: 3 番目の通知。 distanceNotificationDistanceInMeters またはdistanceNotificationTimeInSecondsを使用して指定します。
  4. ManeuverNotificationType.ACTION: 4 番目の通知。 actionNotificationDistanceInMeters またはで指定 actionNotificationTimeInSecondsします。

タイプは距離順に並べられます。 操作の数メートル前にのみ TTS メッセージをカスタマイズする場合 は、distanceNotificationDistanceInMetersのみを設定します。 maneuverNotificationOptions.includedNotificationTypes経由でタイプを省略することもでき ます。 たとえば 、タイプDISTANCEのみを設定した場合 、操作が行われた場合でも、他の通知は受信されません (=ACTION) 。

以下では DISTANCE 、高速道路 ( 道路タイプ ) の車両のタイプ値 (= トランスポートモード ) のみを微調整し、その他のすべての値をそのままにします。

// Get currently set values - or default values, if no values have been set before.
// By default, notifications for cars on highways are sent 1300 meters before the maneuver
// takes place.
ManeuverNotificationTimingOptions carHighwayTimings =
        navigator.getManeuverNotificationTimingOptions(TransportMode.CAR, RoadType.HIGHWAY);

// Set ManeuverNotificationType.DISTANCE (3rd notification):
// Set a new value for cars on highways and keep all other values unchanged.
carHighwayTimings.distanceNotificationDistanceInMeters = 1500;

// Apply the changes.
navigator.setManeuverNotificationTimingOptions(TransportMode.CAR, RoadType.HIGHWAY, carHighwayTimings);

// By default, we keep all types. If you set an empty list you will disallow generating the texts.
// The names of the type indicate the use case: For example, RANGE is the farthest notification.
// ACTION is the notification when the maneuver takes place.
// And REMINDER and DISTANCE are two optional notifications when approaching the maneuver.
maneuverNotificationOptions.includedNotificationTypes = Arrays.asList(
        ManeuverNotificationType.RANGE, // 1st notification.
        ManeuverNotificationType.REMINDER, // 2nd notification.
        ManeuverNotificationType.DISTANCE, // 3rd notification.
        ManeuverNotificationType.ACTION); // 4th notification.

既定では、操作の通知テキストは正投影形式 ( 「ウォール街」 ) になっています。 一部の TTS エンジンは音素をサポートしており、プレーン テキスト「Wall Street」を SSML 表記法「"wɔːl"striːt」で追加します。これにより、発音がよくなります。 次のコードを呼び出して、音素を有効にします。

// Add phoneme support with SSML.
// Note that phoneme support may not be supported by all TTS engines.
maneuverNotificationOptions.enablePhoneme = true;
maneuverNotificationOptions.notificationFormatOption = NotificationFormatOption.SSML;

音素を使用したボイスメッセージの例 :

After 300 meters turn right onto <lang xml:lang="ENG"><phoneme alphabet="nts"  ph="&quot;wɔːl&quot;striːt" orthmode="ignorepunct">Wall Street</phoneme></lang>.

API リファレンス を参照して、 ManeuverNotificationOptionsで設定できるその他のオプションを探します。

HERE SDK は 37 言語をサポートしています。 VisualNavigator を使用して、から言語を照会でき VisualNavigator.getAvailableLanguagesForManeuverNotifications()ます。 HERE SDK 内のすべての言語が LanguageCode 列挙型 (enum) として指定されます。 これを Locale インスタンスに変換するには、LanguageCodeConverterを使用します。 これは、 GitHubナビゲーションサンプルアプリの一部として検索されるオープンソースのユーティリティクラスです。

操作の通知を生成するためにサポートされている各言語は HERE SDK フレームワーク内で音声スキンとして保存されます。 フレームワークを展開し 、 VOICE_ASSETS フォルダを探します。 関心のないアセットを削除して、 HERE SDK パッケージのサイズを縮小できます。 ファイルの削除方法については、「マップ」セクションを参照してください。

ただし、操作の通知を TTS エンジンに送信するには、選択した TTS エンジンで使用する言語がサポートされていることを確認する必要があります。 通常、各デバイスには一部の言語がプリインストールされていますが、最初はすべての言語が表示されているわけではありません。

SpatialAudioNavigation の例アプリでは、 Android のネイティブコードとVisualNavigator を併用して、オーディオパンを使用して TTS オーディオメッセージを再生し、ステレオパノラマを介して方向を指示する方法を示します。 この例は 、 GitHub で確認できます。

音声ガイダンスでサポートされている言語

以下に 、サポートされているすべての音声言語のリストと、 HERE SDK フレームワーク内に保存されている関連する音声スキンの名前を示します。

  • アラビア語(サウジアラビア): voice_package_ar-sa
  • チェコ語 : voice_package_cs-cz
  • デンマーク語 : voice_package_da-dk
  • ドイツ語 : voice_package_de-de
  • ギリシャ語 : VOICE_PACKAGE_EL-GR
  • 英語 ( イギリス ) : voice_package_en-gb
  • 英語(米国 ): voice_package_en-US
  • スペイン語(スペイン): VOICE_PACKAGE_ES-ES
  • スペイン語(メキシコ): VOICE_PACKAGE_ES-MX
  • ペルシャ語(イラン料理): voice_package_fa-ir
  • フィンランド語: VOICE_PACKAGE_FI
  • フランス語(カナダ): voice_package_fr-CA
  • フランス語 : voice_package_fr-FR
  • ヘブライ語 : VOICE_PACKAGE_HE-IL
  • ヒンディー語 : voice_package_hi-in
  • クロアチア語 : VOICE_PACKAGE_hr-HR
  • ハンガリー語 : VOICE_PACKAGE_HU-HU
  • インドネシア語 : バハサ語 voice_package_id - ID
  • イタリア語: voice_package_it-it
  • 日本語: voice_package_ja-JP
  • 韓国語 : voice_package_ko-KR
  • ノルウェー語 : ブークモール語 voice_package_nb -no
  • オランダ語 : voice_package_nl-nl-nl
  • ポルトガル語(ポルトガル) VOICE_PACKAGE_pt
  • ポルトガル語(ブラジル): VOICE_PACKAGE_pt -BR
  • ポーランド語 : voice_package_pt -pt
  • ルーマニア語 : voice_package_ro-R
  • ロシア語 : voice_package_ru-ru
  • スロバキア語 : voice_package_sk-sk
  • セルビア語 : voice_package_sr-CS
  • スウェーデン語 : voice_package_sv -se
  • タイ語 : voice_package_th-th
  • トルコ語 : VOICE_PACKAGE_tr - TR
  • ウクライナ語 : VOICE_PACKAGE_UK-UA
  • ベトナム語 : voice_package_vi -vn
  • 中国語 ( 簡体字 ) : voice_package_zh-CN
  • 中国語 ( 繁体字香港 ): voice_package_zh-HK
  • 中華語(台湾語): voice_package_zh-TW

HERE SDK フレームワークを開き voice_assets 、フォルダーを検索します。 フレームワークのサイズを縮小する場合は、不要な音声パッケージを削除できます。

空間オーディオ操作の通知

ManeuverNotificationListener で提供されているものvoiceTextと同様に、空間オーディオ情報を使用して拡張することもできます ( 上記を参照 ) 。

空間オーディオの操作の通知を使用すると、音声読み上げ文字列のステレオパノラマをリアルタイムで調整できます。 これは、車両に座っているドライバーに対する操作位置に基づいています。

このため には、ManeuverNotificationListenerの代わりに(または並行して)SpatialManeuverNotificationListenerを使用します。 空間的な操作が可能になると通知をトリガーします。 さらに、 SpatialManeuverAzimuthListener を追加して、 HERE SDK によって定義された空間オーディオ操作の通知軌跡の 1 つを構成する方位角エレメントをトリガーします。 結果 SpatialTrajectoryData には、使用する次の方位角が含まれ、空間オーディオ操作の通知の軌跡が終了したかどうかが示されます。

パンニングを開始し、 SpatialManeuverestimatedAudioCueDurationを更新するためにCustomPanningDataを渡し、 およびinitialAzimuthInDegrees プロパティと sweepAzimuthInDegrees プロパティをカスタマイズするためにSpatialManeuverAudioCuePanningを使用します。

道路標識アイコンの取得

iconProvider.createRoadShieldIcon(...)では「 A7 」や「 US-101 」などの道路番号がマップ ビューにすでに表示されているように、非同期で Bitmap を作成できます。

道路標識アイコンの作成はオフラインで行われ、インターネット接続は必要ありません。 アイコンの作成に必要なデータは、そのアイコン自体 Route からのみ取得されますが、手動で入力することもできます。

道路標識アイコンの例。

以下では、ルートを走行している間に、データをオンザフライで取得する方法を示します。 すべての道路、特に小さな道路が道路標識アイコンで表示されるわけではありません。 一般的に、ガイダンスを開始する前、または次の操作のガイダンス中に、アイコンをルートプレビューの一部として表示して、操作が行われた場所を視覚的に示すことができます。

次の操縦と一緒に道路標識を表示しています。

道路標識アイコンを生成するために必要なすべての情報が Route オブジェクトの一部です。

RouteType およびLocalizedRoadNumberなどのパラメーターを必要とするアイコン自体がRoadShieldIconPropertiesから生成され ます。 これらのパラメーターは、 Route オブジェクトのSpanから取得できます。

span.getShieldText(..) を使用して RoadShieldIconPropertiesで使用するためにshieldText 取得します。 span.getRoadNumbers()では、 RouteType ( レベル 1 ~ 6 の道路が主要道路かどうかを示す ) や CardinalDirection ( 「 101 West 」など ) などの追加情報を含むLocalizedRoadNumberアイテムのリストを取得できます。

IconProvider.IconCallback では、結果の画像またはエラーを受け取ることができます。

これはこのフィーチャーのベータリリースであるため、バグがいくつか発生したり、予期しない動作が発生する可能性があります。 関連する API は、非推奨プロセスなしで新しいリリースで変更されることがあります。

以下の例では 、イベントRouteProgress で道路標識アイコンをと組み合わせて表示 / 非表示にする方法について、考えられる解決策を見つけることができます。

visualNavigator.setRouteProgressListener(new RouteProgressListener() {
    @Override
    public void onRouteProgressUpdated(@NonNull RouteProgress routeProgress) {
        List<ManeuverProgress> maneuverProgressList = routeProgress.maneuverProgress;
        ManeuverProgress nextManeuverProgress = maneuverProgressList.get(0);
        if (nextManeuverProgress == null) {
            Log.d(TAG, "No next maneuver available.");
            return;
        }

        int nextManeuverIndex = nextManeuverProgress.maneuverIndex;
        Maneuver nextManeuver = visualNavigator.getManeuver(nextManeuverIndex);

        // ...
        // Here you can extract maneuver information from nextManeuverProgress.
        // ...

        if (previousManeuver == nextManeuver) {
            // We are still trying to reach the next maneuver.
            return;
        }
        previousManeuver = nextManeuver;

        // A new maneuver takes places. Hide the existing road shield icon, if any.  
        uiCallback.onHideRoadShieldIcon();

        Span maneuverSpan = getSpanForManeuver(visualNavigator.getRoute(), nextManeuver);
        if (maneuverSpan != null) {
            // Asynchronously create the icon and show it.
            createRoadShieldIconForSpan(maneuverSpan);
        }
    }
});

この例では、 ビューを更新するために、アプリ側のuiCallback メカニズムを使用します。 このコードは ここ とは関連がないため、省略されていますが、 ReroutingGitHub 付属のアプリの例に記載されています。

次のような操作のためのスパンを取得できます。

@Nullable
private Span getSpanForManeuver(Route route, Maneuver maneuver) {
    Section sectionOfManeuver = route.getSections().get(maneuver.getSectionIndex());
    List<Span> spansInSection = sectionOfManeuver.getSpans();

    // The last maneuver is located on the last span.
    // Note: Its offset points to the last GeoCoordinates of the route's polyline:
    // maneuver.getOffset() = sectionOfManeuver.getGeometry().vertices.size() - 1.
    if (maneuver.getAction() == ManeuverAction.ARRIVE) {
        return spansInSection.get(spansInSection.size() - 1);
    }

    int indexOfManeuverInSection = maneuver.getOffset();
    for (Span span : spansInSection) {
        // A maneuver always lies on the first point of a span. Except for the
        // the destination that is located somewhere on the last span (see above).
        int firstIndexOfSpanInSection = span.getSectionPolylineOffset();
        if (firstIndexOfSpanInSection >= indexOfManeuverInSection) {
            return span;
        }
    }

    // Should never happen.
    return null;
}

以下に、道路シールドアイコンを生成するための情報を抽出する方法を示すコードがあります。

private void createRoadShieldIconForSpan(Span span) {
    if (span.getRoadNumbers().items.isEmpty()) {
        // Road shields are only provided for roads that have route numbers such as US-101 or A100.
        // Many streets in a city like "Invalidenstr." have no route number.
        return;
    }

    // For simplicity, we use the 1st item as fallback. There can be more numbers you can pick per desired language.
    LocalizedRoadNumber localizedRoadNumber = span.getRoadNumbers().items.get(0);
    Locale desiredLocale = Locale.US;
    for (LocalizedRoadNumber roadNumber : span.getRoadNumbers().items) {
        if (localizedRoadNumber.localizedNumber.locale == desiredLocale) {
            localizedRoadNumber = roadNumber;
        }
    }

    // The route type indicates if this is a major road or not.
    RouteType routeType = localizedRoadNumber.routeType;
    // The text that should be shown on the road shield.
    String shieldText = span.getShieldText(localizedRoadNumber);
    // This text is used to additionally determine the road shield's visuals.
    String routeNumberName = localizedRoadNumber.localizedNumber.text;

    if (lastRoadShieldText.equals(shieldText)) {
        // It looks like this shield was already created before, so we opt out.
        return;
    }

    lastRoadShieldText = shieldText;

    // Most icons can be created even if some properties are empty.
    // If countryCode is empty, then this will result in a IconProviderError.ICON_NOT_FOUND. Practically,
    // the country code should never be null, unless when there is a very rare data issue.
    String countryCode = span.getCountryCode() == null ? "" : span.getCountryCode();
    String stateCode = span.getStateCode() == null ? "" : span.getStateCode();

    RoadShieldIconProperties roadShieldIconProperties =
            new RoadShieldIconProperties(routeType, countryCode, stateCode, routeNumberName, shieldText);

    // Set the desired constraints. The icon will fit in while preserving its aspect ratio.
    long widthConstraintInPixels = ManeuverView.ROAD_SHIELD_DIM_CONSTRAINTS_IN_PIXELS;
    long heightConstraintInPixels = ManeuverView.ROAD_SHIELD_DIM_CONSTRAINTS_IN_PIXELS;

    // Create the icon offline. Several icons could be created in parallel, but in reality, the road shield
    // will not change very quickly, so that a previous icon will not be overwritten by a parallel call.
    iconProvider.createRoadShieldIcon(
            roadShieldIconProperties,
            // A road shield icon can be created to match visually the currently selected map scheme.
            MapScheme.NORMAL_DAY,
            widthConstraintInPixels,
            heightConstraintInPixels, new IconProvider.IconCallback() {
                @Override
                public void onCreateIconReply(@Nullable Bitmap bitmap,
                                              @Nullable String description,
                                              @Nullable IconProviderError iconProviderError) {
                    if (iconProviderError != null) {
                        Log.d(TAG, "Cannot create road shield icon: " + iconProviderError.name());
                        return;
                    }

                    // If iconProviderError is null, it is guaranteed that bitmap and description are not null.
                    Bitmap roadShieldIcon = bitmap;

                    // A further description of the generated icon, such as "Federal" or "Autobahn".
                    String shieldDescription = description;
                    Log.d(TAG, "New road shield icon: " + shieldDescription);

                    // An implementation can now decide to show the icon, for example, together with the
                    // next maneuver actions.
                    uiCallback.onRoadShieldEvent(roadShieldIcon);
                }
            });
}

追加のフラグlastRoadShieldText を使用して 、アイコンがすでに作成されているかどうかを確認します。

ここでも、 ビューを更新するために、アプリ側のuiCallback メカニズムを使用します。 このコードは ここ とは関連がないため、省略されていますが、 ReroutingGitHub 付属のアプリの例に記載されています。

ナビゲーションを停止

route が 設定されかつLocationPrivider が開始されると、ターン・バイ・ターンナビ (矢印ナビ)が自動的に開始されますが、ナビゲーションの停止は可能なシナリオによって異なります。

ナビゲーションを停止してトラッキングモードに切り替え ( 以下を参照 ) 、パスをトラッキングしたままマップと一致する位置を受信するか、 またはトラッキングモードに戻らずにナビゲーションを停止します。 最初のケースでは、現在の routenullに設定するだけで済みます。 これにより、ターン・バイ・ターンナビ (矢印ナビ)関連のすべてのイベントの伝播のみが停止されますが、マップマッチングした場所アップデートおよび速度警告情報などを受信できるように、イベントは存続します。 ターン・バイ・ターンナビ (矢印ナビ)イベントの伝播は、目的の宛先に到達すると自動的に停止されます。 route を再度設定すると、ターン・バイ・ターンナビ (矢印ナビ)関連のすべてのイベントが再度伝播されます。

トラッキングモードに戻らずにナビゲーションを停止 する場合( たとえば、マップに一致しない位置の更新のみをロケーションプロバイダから直接取得する場合) は、VisualNavigatorからすべてのイベントを取得しないことをお勧めします。 このためには、すべてのリスナーを個別にnullに設定する必要があります。

アプリで位置情報の更新を利用するために、ロケーションプロバイダの実装を再利用できます。 HERE Positioning では、複数のインスタンスLocationListenerを 設定できます。

VisualNavigatorを使用する場合は、stopRendering()を呼び出します。 MapViewが呼び出されると、VisualNavigatorによって制御されなくなります。

  • マップの向き、カメラの距離、傾きなどの設定は、レンダリング中に変更された可能性がありますが、更新されなくなりました。 コール前の最後の状態stopRendering() が保持 されます。 たとえば、ガイダンス中にマップが傾いていた場合、マップは傾斜したままになります。 このため、stopRendering() を呼び出した後で、目的のカメラ設定を適用することをお勧めします。
  • 新しい場所をVisualNavigatorに送り続けても、マップは現在の場所に移動されなくなり ます。
  • VisualNavigator によって所有されている既定またはカスタムの位置情報インジケータが再度非表示になります。
  • null のリスナーを設定してサブスクライブ解除しない限り、RouteProgress などのすべてのロケーションベースのイベントが配信されます ( 上記を参照 ) 。

VisualNavigatorMapView インスタンス上で動作するため、 MapViewを破棄する前にstopRendering() を呼び出すことをお勧め します。 また、また、LocationSimulatorDynamicRoutingEngineを以前に開始していた場合は停止することをお勧めします。ただし、MapView が一時停止している場合は、VisualNavigatorも停止する必要はありません。 VisualNavigatorMapView 一時停止すると、は自動的にレンダリングを停止し、 MapView が再開されるとレンダリングを開始します(VisualNavigator が以前にレンダリングされていたとき)。

トラッキング

VisualNavigator クラスを使用してターンバイターン ナビゲーションを開始および停止することができますが、ルートをたどる必要のないトラッキングモードに切り替えることもできます。 このモードは、しばしば 運転者アシスタンスモードとも呼ばれます。 公共交通機関を除く、すべての交通手段で利用できます。 公共交通機関の利用は、トラッキングにおいてに不確かな、予期しない結果が生じる可能性があります。 他のすべてのトランスポートモードがサポートされていますが、トラッキングは車両のトランスポートモードに最も適しています。

トラッキングを有効にするには、次をコールするだけです。

visualNavigator.setRoute(null);
herePositioningProvider.startLocating(visualNavigator, LocationAccuracy.NAVIGATION)

ここでは、実際の GPS 位置情報を取得できますが 、LocationSimulatorを使用して任意のルートから位置情報を再生することもできます ( 上図を参照 ) 。

もちろん、routeインスタンスを設定せずにVisualNavigatorを初期化できます。トラッキングモードのみに関心がある場合は、ルートを明示的にヌルに設定する必要はありません。

トラッキングモードでは、ルートをトラッキングすることなく起動できる、NavigableLocationListenerまたはSpeedWarningListenerなどのリスナーのイベントのみを取得できます。 一般に、すべての警告がサポートされています。 ルートが設定されていない場合、RouteProgressListenerなどの他のリスナーはイベントを配信しません。

これにより、リスナーをアクティブな状態に保ち、無料のトラッキングとターンバイターンのナビゲーションをその場で切り替えることができます。

トラッキングモードで動作するリスナーを確認するには、 API リファレンス で概要を確認してください。

ドライバーが道順をすでに知っているが、現在の道路名や旅程中の制限速度などの追加情報を取得したい場合は、トラッキングが便利です。

トラッキングを有効にする場合は 、次のフィーチャーSpeedBasedCameraBehaviorも有効にすることをお勧めします。

visualNavigator.setCameraBehavior(new SpeedBasedCameraBehavior());

このカメラモードは、現在の走行速度に基づいてマップ ビューを最適化するようにカメラの位置を自動的に調整します。

カメラのフォローを停止するには、次をコールしてください。

visualNavigator.setCameraBehavior(null);

これは、ジェスチャの処理を一時的に有効にする場合に、ガイダンス中にも役立ちます。 ターン・バイ・ターンナビ (矢印ナビ)が実行中の場合、ドライバーの注意をそらさないように自動的にトラッキングに戻すことをお勧めします。

旅程を準備

HERE SDK は、マップ データ のルートプリフェッチをサポートしています。 これにより、ターン・バイ・ターンナビ (矢印ナビ) 中などのユーザー体験を改善して、一時的なネットワークの損失を適切に処理できます。

旅程が発生した地域にオフラインのマップがすでにダウンロードされている場合は、この操作は不要です。 この場合、すべてのマップ データ がすでに存在し、ネットワーク接続は必要ありません。 たとえば、専用の OfflineRoutingEngine とは異なり 、Navigatorまたは VisualNavigator は、キャッシュされたデータまたはオフライン マップ データにフォールバックする必要があるタイミングを自動的に決定します。 一般に、ナビゲーションにはマップ データ が必要です。マップ ビュー を表示せずにヘッドレスで実行された場合でも同様です。 この理由は、マップマッチングのためのナビゲーション中にマップ データ にアクセスし、たとえば制限速度などの特定の道路属性について通知する必要があるためです。 このデータは、デバイスで利用可能なデータから取得されます。または、利用できない場合は、ナビゲーション中にダウンロードする必要があります。 そのため、今後の道路を見越してより多くのデータをプリフェッチすることが有益です。 プリフェッチを使用しない場合、一時的な接続の損失はあまり適切に処理されません。

これはこのフィーチャーのベータリリースであるため、バグがいくつか発生したり、予期しない動作が発生する可能性があります。 関連する API は、非推奨プロセスなしで新しいリリースで変更されることがあります。

コンストラクタRoutePrefetcherには、パラメータとしてインスタンスSDKNativeEngineのみが必要です。 HERE SDK の初期化後、SDKNativeEngine.getSharedInstance() 経由で取得できます。

RoutePrefetcher では、マップ データ を事前にダウンロードできます。 マップ データ が マップキャッシュにロードされます。 マップキャッシュには独自のサイズ制約があり、すでにデータが含まれている可能性があります。 RoutePrefetcher は、新しいマップ データ を保存するために、古いキャッシュデータを削除する必要がある場合があります。

  • 旅程を開始する前にroutePrefetcher.prefetchAroundLocation(currentGeoCoordinates)を1 回コールすることをお勧めします。 このコールは、半径が 2 km の指定された場所をマップ データ が事前にマップキャッシュにプリフェッチし、ユーザーがルートのトラッキングを開始したときに十分なマップ データ が存在することを保証します。ただし、ルートがユーザーの現在の場所から開始されていることが前提です。

  • ナビゲーションが開始したら、次 routePrefetcher.prefetchAroundRouteOnIntervals​(navigator)を 1 回コールしてください。 現在指定されているインスタンスNavigator に設定されているルートに沿って、経路内のマップ データ をプリフェッチします。 ルートが設定されていない場合、データはプリフェッチされません。 ルートコリドーのデフォルトの長さは 10 km 、幅は 5 km です マップ データ は、不連続の間隔でのみプリフェッチされます。 現在の経路の終点に達する 1 km 前にプリフェッチが開始されます。 プリフェッチは、 RouteProgress イベントによって示される現在のマップマッチングした場所 に基づいて行われます。 最初のプリフェッチは、ルート沿いに 9 km の距離を走行した後に開始されます。 新しいルートがnavigatorに設定されている場合、このメソッドを再度呼び出す必要はありませんが、このメソッドが 2 回以上呼び出されても、悪影響はありません。

ナビゲーション 例のアプリ に、RoutePrefetcherの使用例を示します。

RoutePrefetcher がルートの開始時に正常に使用され、その後接続が失われた場合、キャッシュされたデータは、マップキャッシュが削除されるまで、今後のパワーサイクルであっても保持されます。 マップキャッシュの削除ポリシーの詳細について は、 ここ を参照してください。

  • ナビゲーション を開始するに、両方のメソッドを同時に呼び出すこともできます。 ただし、トレードオフのために、トリップ開始直後に必要なすべてのデータをプリフェッチするのに十分な時間がない場合があります。
  • prefetchAroundRouteOnIntervals() ガイダンス中にネットワークトラフィックが継続的に増加することに注意してください。

もちろん、プリフェッチされたデータがない場合でもガイダンスを利用できますが、エクスペリエンスの最適化はあまり行われない場合があります。

どちらの呼び出しも、キャッシュされたマップ データ を利用する一時的なオフラインの使用例を最適化するのに役立ちます。 prefetchAroundLocation() はナビゲーションユースケース の外部でも使用できますが 、prefetchAroundRouteOnIntervals() には進行中のナビゲーションシナリオが必要です。

または 、ルート全体のマップ データを事前にプリフェッチすることもできます。 ルートのシェイプから作成されたGeoCorridorのタイルデータRoutePrefetcher.prefetchGeoCorridor()をプリフェッチするために使用します。 この処理には、ルートの長さ、経路の幅、およびネットワークに応じて、少し時間がかかることがあるため、PrefetchStatusListener.onProgress()経由で進行状況が報告されます。 操作が完了すると、イベント PrefetchStatusListener.onComplete() が送信されます。

ルートがすでにダウンロード Region されたデータを通過する場合、経路のこれらの部分は再利用され、再度ダウンロードされることはありません。

マップ データをプリフェッチするもう 1 つの方法は、PolygonPrefetcherで実装できます。 これにより、コンクリートから独立してデータを取得でき Routeます。 代わりに、 データのダウンロード先を指定するには、GeoPolygon のみが必要です。 このデータは、 RoutePrefetcherの場合と同様に、一時的にマップキャッシュにのみ保存されます。 PolygonPrefetcherでは、 データをダウンロードする前に、予想されるダウンロード済みのMapDataSize見積もりを行うこともできます。

」に一致する結果は 件です

    」に一致する結果はありません