チュートリアル : HERE Positioning を使用してハイキングアプリを作成。 アドベンチャー旅行のデジタル日記

ガルワール ヒマラヤ山脈のチャンドラシラ トレッキングへの道、AMSL 5000 m

アドベンチャー好きの方には、ハイキングやトレッキングの旅を記録して友人や家族に見せる興奮を覚えています。 カメラなどの従来の方法では、これらの体験を視覚的に記録できますが、撮影した正確な経路は記録されません。 このセクションで は、 HERE SDK for iOS (Navigate Edition を使用して、大胆な旅程を記録し、視覚化します。

このアプリを出発点として使用すると、 HERE SDK の多くの機能を利用する、カスタマイズされた独自のアプリケーションを作成できます。 さらに、 HERE SDK には、 GitHub にある既製のコードが付属しています。

ハイキングルートを HERE マップに表示したいので、歩数をカウントするのではなく、 GPS 信号の記録に重点を置いています。

次に、 HERE SDK を使用して HikingDiary アプリを作成してみましょう。

位置情報を取得して表示

この GitHub レポジトリにある HERE SDK のコードスニペットを使用 すると、 HEREPositioningProvider をアプリに統合することで、コードを簡単にコピー&ペーストして位置を特定できます。HEREPositioningProviderは 、 HERE SDK が提供する位置情報エンジンを利用して iOS プラットフォーム の位置情報を使用し、 GPS 、ネットワーク位置情報プロバイダ、その他のグローバル衛星ナビゲーションシステム( GNSS )受信機などのさまざまなソースから高精度の位置情報データを提供します。

次の手順で は、メインクラスの LocationDelegate を継承してコピーしたHEREPositioningProviderを統合し、HikingApp.swift名前を付け て 、そのメインクラスのインスタンスをはじめにHEREPositioningProviderに作成します。

// A class to receive location events from the device.
private let herePositioningProvider = HEREPositioningProvider()

// Sets delegate to receive locations from HERE Positioning.
herePositioningProvider.startLocating(locationDelegate: self, accuracy: .navigation)

...

// Conform to LocationDelegate protocol.
func onLocationUpdated(_ location: heresdk.Location) {
  ...
}

LocationDelegate プロトコルに準拠したonLocationUpdated(location: Location)メソッドを実装することを忘れないでください。 このメソッドはメインスレッドで呼び出さ れ、新しいロケーションが利用可能になるたびに呼び出されます。

注 : 精度パラメーターを使用して、位置更新の精度を設定できます。 navigation この設定は、可能な限り高い精度です。

GitHubHikingAppクラスの最終実装を確認 できます。

必要な権限を追加

位置追跡では、ユーザーの居場所やアクティビティに関する機密情報が明らかになる可能性があるため、プライバシーに関する懸念が生じます。 このような問題に対処するために、多くのモバイルプラットフォームでは、位置情報データにアクセスする前にアプリが明示的にユーザーの同意を取得すること、および位置追跡の頻度と精度を制御するオプションを提供することが必要です。 アプリの開発者は、プライバシーに関する規制およびベストプラクティスに従って、位置情報データを責任を持って処理することが重要です。 したがって 、アプリでのLocationEngine の使用を開始する前に、必要な権限をアプリ Info.plist のファイルに追加する必要があります。

<key>UIRequiredDeviceCapabilities</key>
<array>
  <string>location-services</string>
  <string>gps</string>
</array>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
  <string>This app needs to access your current location to display it on the map.</string>
<key>NSLocationWhenInUseUsageDescription</key>
  <string>This app needs to access your current location to display it on the map.</string>
<key>NSMotionUsageDescription</key>
  <string>Motion detection is needed to determine more accurate locations, when no GPS signal is found or used.</string>

作成された Info.plist ファイルは GitHub にあります。

バックグラウンド更新の有効化

ヒマラヤに行くときは、いつも自分のデバイスをポケットに入れてそこに置いておきます。 そのため、ハイキングの追跡を続行するには、アプリでバックグラウンド更新を有効にする必要があります。 このためには locationEngine.setBackgroundLocationAllowed 、アプリケーション Info.plist のファイルに次のキーを追加して、 true に設定し、このような機能を有効にする必要があります。

<key>UIBackgroundModes</key>
  <array>
        <string>location</string>
    <string>processing</string>
    </array>

iOS バージョン 13.0 以降では、「処理」モードが必要です。 追加する場合は、次のものも必要です。

<key>BGTaskSchedulerPermittedIdentifiers</key>
    <array>
      <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
    </array>

詳細については、 Apple の iOS のドキュメントを参照してください。

また、コピーされた HEREPositioningProvider に次の行を追加します。既定ではバックグラウンドの更新が有効になっていません。

// Allow background location updates.
_ = locationEngine.setBackgroundLocationAllowed(allowed: true)

あるいは、 Xcode のインターフェイスの最上位のプロジェクトレベルで、位置情報のアクセス権を有効にすることもできます。 [ 署名と機能 ] 、 [ ターゲット ] 、 [ バックグラウンドモード ] の順に移動し、 [ 位置情報の更新 ] と [ バックグラウンド処理 ] を確認します。

位置の精度の処理

位置情報を受信できるようになったので、位置情報の正確さを確認する必要があります。

onLocationUpdatedから受け取るオブジェクト Location は、特定の時間における世界中のユーザーの位置を記述したもので、真の地理座標の概算にすぎません。 静止していても、 GNSS 衛星は静止していませんが、宇宙を非常に高速に移動します。そのため、位置の推定値が移動する可能性があります。 たとえば、トレッキング中に瞑想したり食事をしたりする場合、推定位置は実際のロケーションから変化します。 このような状況では、自宅にいるときでも長距離を移動できるため、アプリは位置を更新しないでください。

次の図は、受信した GPS 信号の変動の問題を示しています。

図 : GPS 信号の視覚化。

ハイキング担当者は、ユーザーの位置を示します(移動中または移動していない)。 男性を囲む円は 、任意の時点T1 で horizontalAccuracyInMetersにより、半径が定義されている精度円です。 青い円は、いつでも T1 で水平 精度円の内側にある可能性が 68% 、 精度円の外側にある確率が 32% の場合の位置の例です。 たとえば、 horizontalAccuracyInMeters が10 メートルの場合、真の地理座標が半径 10 メートル以内にある可能性が 68% になります。 ハイキングの担当者が時間 T1 から T2 に移行すると 、値horizontalAccuracyInMeters が小さくなり、精度の低い円になります。 ただし、精度円内で真の地理座標が見つかる確率が高くなります。 このため、精度の低い円は大きい円よりも精度が高いと結論付けることができます。

単純な実装では、特定の精度しきい値未満のロケーションのみを受け入れることができます。

これにより、次に示すように、距離の計算が不正確になる可能性があります。

図 : 距離の計算に問題。

黒い線は GPS 信号に基づいて計算された距離で、青い線は実際の距離ですが、後ろにロープを伸ばして実際の経路を追跡しない限り、決定することは不可能です。

この問題の解決方法は?

ロケーションフィルタリングのアルゴリズムを作成

上記の問題を解決するには、移動時にのみロケーションを受け入れるロケーションフィルタリングのアルゴリズムを作成する必要があります。 このために、新しい DistanceAccuracyLocationFilter クラスを作成 し、予定されている位置の水平精度および前回の位置見積もりまでの距離に基づいてフィルタリング戦略を提供します。

このためには、次の 2 種類のフィルターが必要です。

  1. DistanceFilter: 距離のしきい値に基づいて位置をフィルタリングします。
  2. AccuracyFilter: 推定位置の水平精度に基づいて位置をフィルタリングします。また、一定の精度の読み取り値のみが含まれます。 このフィルタを作成するに は、 HERE SDK によって提供されたLocationオブジェクトにあるhorizontalAccuracyInMetersプロパティを使用します。 推定水平精度がメートル単位で示され、真の地理座標がこの不確実性の半径内にあり、その確率は 68% であることが示されます ( 上の最初の図を参照 ) 。

このような距離フィルタをどのように実装できるかを見てみましょう。 まず 、プロパティdistanceThresholdInMeters を定義する必要があります。たとえば、 15 メートルとします。 推定された位置が、最後に受け入れられた位置からdistanceThresholdInMetersを超えている場合にのみ、 GPS 信号を受け入れます。 次の図は、計画したメカニズムの概念を示しています。

図 : 距離のしきい値の表示。

各円は、さまざまな精度の円を使用して、 1 つの位置の推定値を表します。 交差した円が距離のしきい値を下回ったため、廃棄されました。

DistanceFilterの上で、各 GPS 信号の精度も考慮する必要があります ( 上記を参照 ) 。 このため accuracyRadiusThresholdInMeters に、プロパティを定義します。 10 メートルとしましょう。 そのため、より大きい値の各 GPS 信号 accuracyRadiusThresholdInMeters がフィルタリングされます。 上の最初の図では、すべての青い GPS 信号が受け入れられ、赤い GPS 信号が廃棄されています。

これで、次の 2 つのフィルタメカニズムを使用して、アルゴリズムの実装に進む準備ができました。

protocol LocationFilterStrategy {
    func checkIfLocationCanBeUsed(_ location: Location) -> Bool
}

class DistanceAccuracyLocationFilter: LocationFilterStrategy {
    // These two parameters define if incoming location updates are considered to be good enough.
    // In the field, the GPS signal can be very unreliable, so we need to filter out inaccurate signals.
    static let accuracyRadiusThresholdInMeters = 10.0
    static let distanceThresholdInMeters = 15.0
    private var lastAcceptedGeoCoordinates: GeoCoordinates?

    func checkIfLocationCanBeUsed(_ location: Location) -> Bool {
        if isAccuracyGoodEnough(location) && isDistanceFarEnough(location) {
            lastAcceptedGeoCoordinates = location.coordinates
            return true
        }
        return false
    }

    // Checks if the accuracy of the received GPS signal is good enough.
    private func isAccuracyGoodEnough(_ location: Location) -> Bool {
        guard let horizontalAccuracyInMeters = location.horizontalAccuracyInMeters else {
            return false
        }

        // If the location lies within the radius of accuracyCircleRadiusInMetersThreshold then we accept it.
        if horizontalAccuracyInMeters <= DistanceAccuracyLocationFilter.accuracyRadiusThresholdInMeters {
            return true
        }
        return false
    }

    // Checks if last accepted location is farther away than xx meters.
    // If it is, the new location will be accepted.
    // This way we can filter out signals that are caused by a non-moving user.
    private func isDistanceFarEnough(_ location: Location) -> Bool {
        guard let lastAcceptedGeoCoordinates = lastAcceptedGeoCoordinates else {
            // We always accept the first location.
            lastAcceptedGeoCoordinates = location.coordinates
            return true
        }

        let distance = location.coordinates.distance(to: lastAcceptedGeoCoordinates)
        if distance >= DistanceAccuracyLocationFilter.distanceThresholdInMeters {
            return true
        }
        return false
    }
}

DistanceAccuracyLocationFilter アルゴリズムは、 HERE SDK GitHub レポジトリにもあります。

ここで、受信位置信号のインスタンスのDistanceAccuracyLocationFilterを作成してフィルタリングし、マップ上の位置を更新できます。

private var locationFilter: LocationFilterStrategy

...

// Filter out undesired location signals.
locationFilter = DistanceAccuracyLocationFilter()

...

func onLocationUpdated(_ location: heresdk.Location) {
    if locationFilter.checkIfLocationCanBeUsed(location) {
      // Use the location.
    }
}

LocationFilterStrategy プロトコルを採用することで、独自のロケーションフィルタリングのアルゴリズムを作成することもできます。 たとえば、次のようなバイパスフィルタを実装できます。

// The DefaultLocationFilter class implements the LocationFilterStrategy protocol and
// allows every location signal to pass inorder to visualize the raw GPS signals on the map.
class DefaultLocationFilter: LocationFilterStrategy {
    func checkIfLocationCanBeUsed(_ location: Location) -> Bool {
        return true
    }
}

LocationFilterStrategyでは、アプリのコア機能を変更せずに、ロケーションフィルタリングに異なるアルゴリズムを使用するようにアプリをカスタマイズできます。上記のフィルタリングアルゴリズムはできるだけシンプルに保たれており、ニーズに合わせて改善できます。

位置情報の更新の記録

位置情報の更新が完了したら、次の手順で位置情報を記録します。 このため には、 HERE SDK GitHub の GPXTrackWriter コード スニペットを使用 して、 GPXTrack を作成します。 これ GPXTrackGPXDocument クラスで保存および読み込みできます。 GPXDocument にはすべての GPX トラックが含まれ、 GPX ファイル形式で保存されます。 このため、保存したファイルは、 GPX ファイル形式を理解している他のアプリケーションと簡単に共有できます。

GPX は、経由地、トラック、ルートなどの GPS データ交換の事実上の標準データ形式です。 GPX ファイルには、地理情報が XML 形式で含まれており、さまざまなソフトウェアプログラムや GPS デバイスで簡単に処理できます。

HERE SDK は、 GPX データの読み取り、表示、および操作をさまざまな方法で行うためのツールを提供することで、 GPX データの処理を支援します。

位置情報の更新を記録するには 、 GPX 操作を管理する GPXManager のインスタンスを作成します。

private var gpxTrackWriter = GPXTrackWriter()
private let gpxManager: GPXManager

// Create a GPXDocument file with named as myGPXDocument.
gpxManager = GPXManager(gpxDocumentFileName: "myGPXDocument")

次に 、内部onLocationUpdated()メソッドにGPXTrack位置情報の更新を書き込みます。

// Add location updates to a GPX track.
gpxTrackWriter.onLocationUpdated(location)

最後に、ハイキングルートを保存するために、 GPX トラックを次のロケーションに保存する 1 行のコードを使用します GPXDocument

// Permanently store the trip on the device.
gpxManager.saveGPXTrack(gpxTrackWriter.track)

旅程をロードするには、次を呼び出してください。

guard let gpxTrack = gpxManager.getGPXTrack(index: index) else {
    return
}

注 : 複数のハイキングルート を1つのGPXDocumentに複数のGPXTrackとして記録して保存できます。

アプリを完了

アプリを完了するため に、マップ上に MapPolyline を使用して移動したパスを表示します。 の実装で MapPolylineは、 『開発者ガイド for the HERE SDK 』にあるマップアイテムガイドを使用します。 HikingApp.swift クラスでも参照できます。

旅程中にポリラインを延長するため に、インスタンスgpxManager から最新の座標リストを取得します。これは、すでに最新の位置情報が含まれているためです。 このリストを使用して、 geoPolyline 既存 mapPolyline のインスタンスに設定した新しいインスタンスを作成できます。

// Update the polyline shape that shows the travelled path of the user.
mapPolyline.geometry = geoPolyline

これにより、マップ ビューにすでに表示されているポリラインがただちに拡張されます。

このブログの投稿では、アプリの開発プロセスのすべてのステップについて説明したわけではありません。 より完全な描写を取得するには、この利用開始ガイドを参照してください。

完全な Xcode プロジェクトは 、 GitHub で見つけることができます。

完成したアプリを以下に示します。

スクリーンショット : 衛星マップ スキーム搭載の HikingDiary アプリ。
スクリーンショット : HikingDiary アプリ屋外ラスタレイヤー付き

HERE SDK には、さまざまなマップスキームがあります。 デフォルトでは、アプリに satellite マップ スキームが表示されます。 ただし、MapScheme を変更 して、別のマップ スキームに切り替えることができます。 使用可能な MapScheme のオプションの詳細については、HERE SDKの『 API リファレンス 』を参照してください。

また、アプリでは、メインビューの右上隅にあるスイッチをスライドさせて、マップビューの上に屋外ラスタレイヤーを作成できます。 これは、ハイトラインやより詳細な経路を示すため、ハイキングに適したサードパーティ製のラスターレイヤーです。 ここでは、 thunderforest.com の屋外マップを使用しています。 カスタムのラスタレイヤの詳細については、 『HERE SDK 開発者ガイド 』を参照してください。

次のステップ

この小さな HikingDiary アプリには、さまざまな改善方法があります。以下にいくつかのアイデアを示します。

  • 旅程に高さプロファイルを含めるには、GeoCoordinatesオブジェクトのaltitudeプロパティを使用します。
  • たとえば、 HERE SDK で terrain は、より多くのマップスキームを統合して、丘の斜面を色分けするスキームを提供しています。
  • 旅程の合計期間と、休憩を取ったタイムスタンプを使用して、旅程を保存します。
  • 保存されている GPX ファイルのエクスポートオプションを含めて、他のアプリケーションと共有します。

GitHub のオープンソースプロジェクトとしてアプリをリリースしたので 、ご参加をお待ちしております。 バグ修正、新機能、全体的な改善を含むプルリクエストを自由に送信できます。

Happy Hiking!

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

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