Core Locationで位置を取得する際にCLActivityTypeとしてCLActivityType.otherNavigation以外を指定すると意図しないマップマッチが行われる

iOSやiPadOSで位置を記録するアプリを作成し、移動経路を記録して動作を検証すると、時折、実際には通過していない場所に誤差の少ない経路が記録されることがありました。

例えば、下の図1は、池上本門寺付近を自転車で通過した際の位置を記録したものです。図の中央の池上小学校の南側の川沿いに、誤差の少なそうな直線が東西に記録されていますが、実際はこの道路は通過しておらず、その南側の道路を通過したところが誤って記録されているようでした。

図1: iPhoneでCLActivityType.otherを指定して移動経路を記録している様子

GPSロガーのアプリとしては、実際の経路と大きく異るものが記録されるようでは使い物になりません。

そこで、作成したGPSロガーアプリとWahoo ELEMENTを同時に使用し、何が起こっているのか調べました。

GPSロガーアプリで位置を取得しているコードの要点は次のようになっています。

import SwiftUI
import CoreLocation

class LocationService: NSObject {
    public static let shared = LocationService()
    
    private let locationManager = CLLocationManager()
    
    override init() {
        super.init()
        self.locationManager.delegate = self
        self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
        // CLActivityType.otherNavigationを使用した場合のみ、マップマッチが行われません(図3の状態)。
        self.locationManager.activityType = .otherNavigation
        // CLActivityType.other等、.otherNavigation以外を使用すると意図しないマップマッチが行われました(図2の状態)。
        // self.locationManager.activityType = .other
        // 以下省略
    }
    
    // 以下省略
}

種々の条件を変更して繰り返し検証した結果、CLActivityTypeの値として.otherNavigation以外を指定した場合、移動速度が約20 km/hを超えた場合に、現在地の最寄りの尤もらしい道路に吸着される、いわゆる「マップマッチ」が行われることが判りました。

例えば、下の図2のとおりです。

図2の緑色の線はWahoo ELEMENTで記録した結果のGPXをプロットしたもので、実際に移動した経路とよく合っています。

赤色の線は、CLActivityType.otherを指定したGPSロガーアプリで記録した結果をプロットしたものです。緑色の実際の経路と比較すると、意図せずして、実際とは異なる道路にマップマッチされている箇所があることが見てとれます。

図2: iPhoneでCLActivityType.otherを指定して記録した経路(赤色線)とWahoo Elementで記録した経路(緑色線)

意図しないマップマッチングが行われてしまうと、取得した現在位置をジオフェンスサーバーに送信して何らかの処理を行う場合などに、期待する結果を得ることができません。

そこで、CLActivityTypeのenumの値を入れ替えて検証したところ、CLActivityType.otherNavigationの場合のみ、マップマッチングが行われないことが判りました。

図3は、図2と同じ経路をCLActivityType.otherNavigationを指定したGPSロガーアプリを使って記録した結果です。

CLActivityType.otherを指定した場合と異なり、CLActivityType.otherNavigationを指定した場合には意図しないマップマッチがなされていないことが見てとれます。

図3: iPhoneでCLActivityType.otherNavigationを指定して記録した経路(赤色線)とWahoo Elementで記録した経路(緑色線)

Core Locationのドキュメントを読みましたが、マップマッチの有無を指定するための方法は記載されていないようでした。

また、マップマッチ自体は実施されているとしても、どのような条件でマップマッチが始まるかも書かれていないようです。

CLActivityType.fitness等、公園の中でのジョギングや河川敷でのサイクリングの経路など、マップマッチが行われると実際と大きく異る結果となるため、CLActivityType.otherNavigationをうっかり指定しないよう注意する必要がありそうです。