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

トラフィック・エンジン

TrafficEngineを使用すると、事故、工事、通行止めなどの現在の交通事案に関する詳細かつローカライズ可能な情報をクエリできます。

1行のコードでマップレイヤーの状態TRAFFIC_INCIDENTSを有効にすることで、地図上で交通事案を視覚化できます。HERE SDKは別のレイヤーもサポートしており、TRAFFIC_FLOWレイヤーを追加することで現在の交通状況を確認できます。

また、「ルート上の交通状況を視覚化する」セクションに示すように、Routeインスタンスに沿った交通状況を表示します。

TrafficEngineでクエリしたデータは、HERE Traffic API v7に基づいています。

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

トラフィック・エンジンを初期化する

TrafficEngine を使用すると、進行中の交通事案に関する詳細情報を取得できます。GeoBox または GeoCircle で、GeoCorridor を経由するルートに沿ってインシデントを検索できます。

たとえば、特定の種類のインシデントのみを検索するなど、複数のオプションを設定することもできます。その結果、影響を受ける車のタイプ、日付情報、位置情報、ローカライズ可能な説明、概要などに関する結果が得られます。

まず、TrafficEngine の新しいインスタンスを作成します。

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

トラフィック リクエストを開始する

以下では、GeoCircle 内で可能性のある TrafficIncidents を検索します。

private void queryForIncidents(GeoCoordinates centerCoords) {
    int radiusInMeters = 1000;
    GeoCircle geoCircle = new GeoCircle(centerCoords, radiusInMeters);
    TrafficIncidentsQueryOptions trafficIncidentsQueryOptions = new TrafficIncidentsQueryOptions();
    // Optionally, specify a language:
    // the language of the country where the incident occurs is used.
    // trafficIncidentsQueryOptions.languageCode = LanguageCode.EN_US;
    trafficEngine.queryForIncidents(geoCircle, trafficIncidentsQueryOptions, new TrafficIncidentsQueryCallback() {
        @Override
        public void onTrafficIncidentsFetched(@Nullable TrafficQueryError trafficQueryError,
                                              @Nullable List<TrafficIncident> trafficIncidentsList) {
            if (trafficQueryError == null) {
                // If error is null, it is guaranteed that the list will not be null.
                String trafficMessage = "Found " + trafficIncidentsList.size() + " result(s). See log for details.";
                TrafficIncident nearestIncident =
                        getNearestTrafficIncident(centerCoords, trafficIncidentsList);
                if (nearestIncident != null) {
                    trafficMessage += " Nearest incident: " + nearestIncident.getDescription().text;
                }
                showDialog("Nearby traffic incidents", trafficMessage);

                for (TrafficIncident trafficIncident : trafficIncidentsList) {
                    Log.d(TAG, "" + trafficIncident.getDescription().text);
                }
            } else {
                showDialog("TrafficQueryError:", trafficQueryError.toString());
            }
        }
    });
}
private fun queryForIncidents(centerCoords: GeoCoordinates) {
    val radiusInMeters = 1000
    val geoCircle = GeoCircle(centerCoords, radiusInMeters.toDouble())
    val trafficIncidentsQueryOptions = TrafficIncidentsQueryOptions()
    // Optionally, specify a language:
    // the language of the country where the incident occurs is used.
    // trafficIncidentsQueryOptions.languageCode = LanguageCode.EN_US;
    trafficEngine.queryForIncidents(geoCircle, trafficIncidentsQueryOptions,
        TrafficIncidentsQueryCallback { trafficQueryError, trafficIncidentsList ->
            if (trafficQueryError == null) {
                // If error is null, it is guaranteed that the list will not be null.
                var trafficMessage = "Found " + trafficIncidentsList!!.size + " result(s). See log for details."
                val nearestIncident: TrafficIncident =
                    getNearestTrafficIncident(centerCoords, trafficIncidentsList)
                if (nearestIncident != null) {
                    trafficMessage += " Nearest incident: " + nearestIncident.description.text
                }
                showDialog("Nearby traffic incidents", trafficMessage!!)

                for (trafficIncident in trafficIncidentsList) {
                    Log.d(TAG, "" + trafficIncident.description.text)
                }
            } else {
                showDialog("TrafficQueryError:", trafficQueryError.toString())
            }
        })
}

トラフィック レスポンスを処理する

アプリは検出された各 TrafficIncident の説明をログに記録します。ドイツで検索すると、デフォルトでは結果はドイツ語で表示されます。

たとえば、次のように設定することで、別の言語を指定することもできます。

trafficIncidentsQueryOptions.languageCode = LanguageCode.EN_US;
trafficIncidentsQueryOptions.languageCode = LanguageCode.EN_US;

TRAFFIC_INCIDENTS は、インシデントの場所を示す交通アイコンと線をレンダリングするレイヤーであることに注意してください。

TrafficIncident には、その位置を示す GeoPolyline が含まれています。地図上のタップされた位置とポリラインに含まれる地理座標を比較することで、地図上のタップされた位置に最も近いインシデントを見つけることができます。

@Nullable
private TrafficIncident getNearestTrafficIncident(GeoCoordinates currentGeoCoords,
                                                  List<TrafficIncident> trafficIncidentsList) {
    if (trafficIncidentsList.size() == 0) {
        return null;
    }

    // By default, traffic incidents results are not sorted by distance.
    double nearestDistance = Double.MAX_VALUE;
    TrafficIncident nearestTrafficIncident = null;
    for (TrafficIncident trafficIncident : trafficIncidentsList) {
        List<GeoPolyline> trafficIncidentPolyLines = new ArrayList<>();

        // It is guaranteed that each incident has a valid polyline.
        trafficIncidentPolyLines.add(trafficIncident.getLocation().polyline);

        // If the polyline contains any gaps, they are available as additionalPolylines in TrafficLocation.
        trafficIncidentPolyLines.addAll(trafficIncident.getLocation().additionalPolylines);

        for (GeoPolyline trafficIncidentPolyline : trafficIncidentPolyLines) {
            for (GeoCoordinates geoCoords : trafficIncidentPolyline.vertices) {
                double currentDistance = currentGeoCoords.distanceTo(geoCoords);
                if (currentDistance < nearestDistance) {
                    nearestDistance = currentDistance;
                    nearestTrafficIncident = trafficIncident;
                }
            }
        }
    }

    return nearestTrafficIncident;
}
private fun getNearestTrafficIncident(
    currentGeoCoords: GeoCoordinates,
    trafficIncidentsList: List<TrafficIncident>?
): TrafficIncident? {
    if (trafficIncidentsList!!.size == 0) {
        return null
    }

    // By default, traffic incidents results are not sorted by distance.
    var nearestDistance = Double.MAX_VALUE
    var nearestTrafficIncident: TrafficIncident? = null
    for (trafficIncident in trafficIncidentsList) {
        val trafficIncidentPolyLines: MutableList<GeoPolyline> = java.util.ArrayList()

        // It is guaranteed that each incident has a valid polyline.
        trafficIncidentPolyLines.add(trafficIncident.location.polyline)

        // If the polyline contains any gaps, they are available as additionalPolylines in TrafficLocation.
        trafficIncidentPolyLines.addAll(trafficIncident.location.additionalPolylines)

        for (trafficIncidentPolyline in trafficIncidentPolyLines) {
            for (geoCoords in trafficIncidentPolyline.vertices) {
                val currentDistance = currentGeoCoords.distanceTo(geoCoords)
                if (currentDistance < nearestDistance) {
                    nearestDistance = currentDistance
                    nearestTrafficIncident = trafficIncident
                }
            }
        }
    }

    return nearestTrafficIncident
}

もちろん、MapPolyline を追加して、地図上にインシデントのポリラインを自分自身でレンダリングすることもできます。独自のアイコンを MapMarker として追加することもできます。インシデントのタイプに応じて、別の画像を選択することもできます。

交通量情報をクエリする

TrafficEngineを使用すると、trafficEngine.queryForFlow(...)を呼び出して非同期リクエストを実行することで、リアルタイムの交通渋滞に関する情報をクエリすることもできます。なお、リクエストが発生した時刻が異なる可能性があり、アプリ側での表示方法も異なる場合があるため、データがTRAFFIC_FLOWレイヤーに表示されているものとは異なる可能性があります。

private void requestRealtimeTrafficOnRoute(Route route) {
    // We are interested to see traffic also for side paths.
    int halfWidthInMeters = 500;

    GeoCorridor geoCorridor = new GeoCorridor(route.getGeometry().vertices, halfWidthInMeters);
    TrafficFlowQueryOptions trafficFlowQueryOptions = new TrafficFlowQueryOptions();
    trafficEngine.queryForFlow(geoCorridor, trafficFlowQueryOptions, new TrafficFlowQueryCallback() {
        @Override
        public void onTrafficFlowFetched(@Nullable TrafficQueryError trafficQueryError,
                                         @Nullable List<TrafficFlow> list) {
            if (trafficQueryError == null) {
                for (TrafficFlow trafficFlow : list) {
                    Double confidence = trafficFlow.getConfidence();
                    if (confidence != null && confidence <= 0.5) {
                        // Exclude speed-limit data and include only real-time and historical
                        // flow information.
                        continue;
                    }

                    // Visualize all polylines unfiltered as we get them from the TrafficEngine.
                    GeoPolyline trafficGeoPolyline = trafficFlow.getLocation().polyline;
                    addTrafficPolylines(trafficFlow.getJamFactor(), trafficGeoPolyline);
                }
            } else {
                showDialog("Error while fetching traffic flow:", trafficQueryError.toString());
            }
        }
    });
}
private fun requestRealtimeTrafficOnRoute(route: Route) {
    // We are interested to see traffic also for side paths.
    val halfWidthInMeters = 500

    val geoCorridor = GeoCorridor(route.geometry.vertices, halfWidthInMeters)
    val trafficFlowQueryOptions = TrafficFlowQueryOptions()
    trafficEngine.queryForFlow(geoCorridor, trafficFlowQueryOptions,
        TrafficFlowQueryCallback { trafficQueryError, list ->
            if (trafficQueryError == null) {
                for (trafficFlow in list!!) {
                    val confidence = trafficFlow.confidence
                    if (confidence != null && confidence <= 0.5) {
                        // Exclude speed-limit data and include only real-time and historical
                        // flow information.
                        continue
                    }

                    // Visualize all polylines unfiltered as we get them from the TrafficEngine.
                    val trafficGeoPolyline = trafficFlow.location.polyline
                    addTrafficPolylines(trafficFlow.jamFactor, trafficGeoPolyline)
                }
            } else {
                showDialog("Error while fetching traffic flow:", trafficQueryError.toString())
            }
        })
}

次の手順では、独自のカラーエンコーディングを使用して交通流ポリラインをレンダリングできます。

private void addTrafficPolylines(double jamFactor, GeoPolyline geoPolyline) {
    Color lineColor = getTrafficColor(jamFactor);
    if (lineColor == null) {
        // We skip rendering low traffic.
        return;
    }
    float widthInPixels = 10;
    MapPolyline trafficSpanMapPolyline = null;
    try {
        trafficSpanMapPolyline = new MapPolyline(geoPolyline, new MapPolyline.SolidRepresentation(
                new MapMeasureDependentRenderSize(RenderSize.Unit.PIXELS, widthInPixels),
                lineColor,
                LineCap.ROUND));
    }  catch (MapPolyline.Representation.InstantiationException e) {
        Log.e("MapPolyline Representation Exception:", e.error.name());
    } catch (MapMeasureDependentRenderSize.InstantiationException e) {
        Log.e("MapMeasureDependentRenderSize Exception:", e.error.name());
    }

    mapView.getMapScene().addMapPolyline(trafficSpanMapPolyline);
    mapPolylines.add(trafficSpanMapPolyline);
}

// Define a traffic color scheme based on the traffic jam factor.
// 0 <= jamFactor < 4: No or light traffic.
// 4 <= jamFactor < 8: Moderate or slow traffic.
// 8 <= jamFactor < 10: Severe traffic.
// jamFactor = 10: No traffic, ie. the road is blocked.
// Returns null in case of no or light traffic.
@Nullable
private Color getTrafficColor(Double jamFactor) {
    if (jamFactor == null || jamFactor < 4) {
        return null;
    } else if (jamFactor >= 4 && jamFactor < 8) {
        return Color.valueOf(1, 1, 0, 0.63f); // Yellow
    } else if (jamFactor >= 8 && jamFactor < 10) {
        return Color.valueOf(1, 0, 0, 0.63f); // Red
    }
    return Color.valueOf(0, 0, 0, 0.63f); // Black
}
private fun addTrafficPolylines(jamFactor: Double, geoPolyline: GeoPolyline) {
    val lineColor = getTrafficColor(jamFactor)
        ?: // We skip rendering low traffic.
        return
    val widthInPixels = 10f
    var trafficSpanMapPolyline: MapPolyline? = null
    try {
        trafficSpanMapPolyline = MapPolyline(
            geoPolyline, SolidRepresentation(
                MapMeasureDependentRenderSize(RenderSize.Unit.PIXELS, widthInPixels.toDouble()),
                lineColor,
                LineCap.ROUND
            )
        )
    } catch (e: MapPolyline.Representation.InstantiationException) {
        Log.e("MapPolyline Representation Exception:", e.error.name)
    } catch (e: MapMeasureDependentRenderSize.InstantiationException) {
        Log.e("MapMeasureDependentRenderSize Exception:", e.error.name)
    }

    mapView.mapScene.addMapPolyline(trafficSpanMapPolyline!!)
    mapPolylines.add(trafficSpanMapPolyline)
}

// Define a traffic color scheme based on the traffic jam factor.
// 0 <= jamFactor < 4: No or light traffic.
// 4 <= jamFactor < 8: Moderate or slow traffic.
// 8 <= jamFactor < 10: Severe traffic.
// jamFactor = 10: No traffic, ie. the road is blocked.
// Returns null in case of no or light traffic.
@Nullable
private fun getTrafficColor(jamFactor: Double?): Color? {
    if (jamFactor == null || jamFactor < 4) {
        return null
    } else if (jamFactor >= 4 && jamFactor < 8) {
        return Color.valueOf(1f, 1f, 0f, 0.63f) // Yellow
    } else if (jamFactor >= 8 && jamFactor < 10) {
        return Color.valueOf(1f, 0f, 0f, 0.63f) // Red
    }
    return Color.valueOf(0f, 0f, 0f, 0.63f) // Black
}

Trafficサンプルアプリを試す

使用例は、JavaKotlinの両方で提供されている「Traffic」サンプルアプリの一部としてGitHubで入手できます。


EN 日本語

HERE documentation

Find answers to your product and technical questions

Documentation

What's new

Videos

EN 日本語

HERE ドキュメント

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

ドキュメント

ダイナミックマップ

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

地図とデータ

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

最新情報

動画

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