現在地の検索
マッピングアプリケーションを使用する主な理由の 1 つは、自分がどこにいるかを確認することです。 LocationEngine
HERE SDK が提供するは、 iOS プラットフォーム の位置を使用する包括的な位置特定ソリューションを実装し、 GPS やその他のグローバル衛星ナビゲーションシステム( GNSS )受信機などの複数の位置情報ソースと連携します。
iOSデバイスがGNSS精度を達成する能力は、そのハードウェアに依存します。 すべてのiOSデバイスがこのフィーチャーをサポートするハードウェアを備えているわけではありません。 サブメーターの精度は現在iOSではサポートされていないため、別のレシーバーが必要です。
注
簡単に確認できます
HERE SDK の位置情報フィーチャーを統合するには、少なくとも次の手順を実行する必要があります。
- 必要 な iOS 権限を
.plist
ファイルに追加し、ユーザーに権限を要求します。 LocationEngine
を作成し、少なくとも 1 つのLocationDelegate
を設定します。 -
LocationEngine
を1回開始し、必要な精度レベルを設定します。 Location
の更新を受信し、アプリで処理します。
必要な権限を追加
アプリでの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>
注
バックグラウンド で位置情報の更新を要求する場合にのみ、権限のNSLocationAlwaysAndWhenInUseUsageDescription
が必要になります。 詳細については、「バックグラウンド更新の有効化」を参照してください。
GPS などのネイティブの位置情報サービスを使用するアプリは、ユーザーの許可を要求します。 すべてのデバイスが同じ機能を提供しているわけではありません。また、特定のハードウェアの制約があるため、結果が異なる可能性があります。 を使用する前 LocationEngine
に、ネイティブの位置情報サービスが有効になっているかどうかを確認することをお勧めします。 ほとんど の iOS デバイスでは、ユーザーは [ 設定 ] > [ プライバシー ] > [ 位置情報サービス ] の順に移動して位置情報サービスがオンになっていることを確認できます。
注
最近の iPad デバイスでも 、UIRequiredDeviceCapabilities
に必要なgps
の機能が不足 している可能性があります。 Info.plist
ファイルに存在する場合、アプリ はこのようなデバイスにインストールされません。 この機能を削除すると、アプリ がインストールされますが 、デバイスから受け取る更新 Location
の精度は半径が大きくなり、結果として非常に不正確になります。 GPS センサーがない場合、デバイスはネットワークの配置などにフォールバックすることがあります。
以下のコード スニペットを使用して、アプリケーションを確認 CLAuthorizationStatus
し、ユーザーの承認を要求できます。 Apple の 詳細については、 iOS のドキュメントを参照 CLAuthorizationStatus
してください。
import CoreLocation
private func startLocating() {
if locationEngine.start(locationAccuracy: .bestAvailable) == .missingPermissions {
requestLocationAuthorization()
}
}
public func requestLocationAuthorization() {
let locationAuthorizationStatus = CLLocationManager.authorizationStatus()
switch locationAuthorizationStatus {
case .restricted:
let alert = UIAlertController(title: "Location Services are restricted", message: "Please remove Location Services restriction in your device Settings", preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alert.addAction(okAction)
present(alert, animated: true, completion: nil)
return
case .denied:
let alert = UIAlertController(title: "Location access is denied", message: "Please allow location access for the application in your device Settings", preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alert.addAction(okAction)
present(alert, animated: true, completion: nil)
return
case .authorizedWhenInUse, .authorizedAlways:
break
case .notDetermined:
locationManager.requestWhenInUseAuthorization()
break
default:
break
}
}
LocationEngine を作成します
LocationEngine
の新規作成は簡単です。
do {
try locationEngine = LocationEngine()
} catch let engineInstantiationError {
fatalError("Failed to initialize LocationEngine. Cause: \(engineInstantiationError)")
}
最後に確認した場所を取得
エンジンが初期化されると、少なくとも 1 回前にエンジンが始動し、少なくとも 1 つの位置を受信している限り、最後に確認された位置を取得できます。そうでない場合、nil
が返されます。 この情報は残ります。したがって、最後に確認された場所もアプリケーションセッション間で利用できます。
if let myLastLocation = locationEngine.lastKnownLocation {
print("Last known location: '%f', '%f'", myLastLocation.coordinates.latitude, myLastLocation.coordinates.longitude)
}
注
LocationEngine
最後の既知の場所を取得するために、を開始する必要はありません。また、リスナーを設定する必要もありません。 が LocationEngine
前のセッションで 1 回正常に開始され、有効な位置イベントが少なくとも 1 回受信されただけで十分です。 Location
オブジェクトには timestamp
、その場所がいつ受信されたかを示すが含まれています。
位置情報イベントの通知を受信
次に、 LocationEngine
を開始する前に、 LocationStatusDelegate
プロトコルに準拠してエンジンのステータスの変更が通知されるようにし、それをロケーションエンジンの addLocationStatusDelegate()
メソッドに登録することをお勧めします。 さまざまなステータスの詳細については、 API リファレンス を確認してください。
class PositioningExample: LocationStatusDelegate {
func onStatusChanged(locationEngineStatus: LocationEngineStatus) {
print("LocationEngineStatus: : \(locationEngineStatus)")
}
func onFeaturesNotAvailable(features: [LocationFeature]) {
for feature in features {
print("Feature not available: '%s'", String(describing: feature))
}
}
locationEngine.addLocationStatusDelegate(locationStatusDelegate: self)
}
注
開始が成功すると、LocationStatusDelegate
は常にステータスLocationEngineStatus.engineStarted
を受信し、停止が成功すると、常にステータスLocationEngineStatus.engineStopped
を受信します。
また、代理人 onFeaturesNotAvailable()
のコールバック を通じて LocationFeature
、利用できないものがある場合は通知されます。 必要なフィーチャーが利用できない場合は、HERE の担当者にご連絡ください。 注 : LocationFeature
列挙型 (enum) は現在保留中のフィーチャーです。
エンジンを始動する前に最後に考慮すべきことは、 LocationDelegate
プロトコルに準拠していることです。このプロトコルは、新しい Location
が検出されたときに通知を送信する onLocationUpdated()
コールバックを提供します。 これは、前述のLocationStatusDelegate
と同様の方法で行うことができ ます。
class PositioningExample: LocationDelegate {
func onLocationUpdated(location: Location) {
print("Location updated: \(location.coordinates)")
}
locationEngine.addLocationDelegate(locationDelegate: self)
}
注
コールバック onLocationUpdated()
はメインスレッドで受信されます。他のすべてのコールバックと同じです。
現在の地理座標およびタイムスタンプを除き、その他 Location
のすべてのフィールドはオプションです。 たとえば、受信 したオブジェクトLocation
には、ベアリング角度および現在の速度に関する情報が含まれている場合がありますが、この情報は利用できることは保証されていません。 使用できない値は、として返さ nil
れます。 ポジショニングに使用されるソースの種類( エンジンの始動に使用されるLocationAccuracy
によって定義されます。以下の「 Start and Stop Receiving Locations 」セクションを参照してください)と、デバイスの機能が利用可能なフィールドに影響を与えます。
それぞれのaddLocationStatusDelegate()
メソッドおよびaddLocationDelegate
メソッドを呼び出して、必要なだけLocationStatusDelegate
およびLocationDelegate
を追加できます。
位置情報受信の開始および停止
次のコード スニペットのように、事前定義された LocationAccuracy
モードのいずれかを渡して、 LocationEngine
の start()
メソッドを呼び出す準備ができました。
class PositioningExample: LocationStatusDelegate, LocationDelegate {
private func startLocating() {
locationEngine.addLocationStatusDelegate(locationStatusDelegate: self)
locationEngine.addLocationDelegate(locationDelegate: self)
if locationEngine.start(locationAccuracy: .bestAvailable) == .missingPermissions {
requestLocationAuthorization()
}
}
}
注
start()
このメソッドはすでに次の値を返しています LocationStatus
: LocationStatusDelegate
上の行でを設定しましたが、ステータスをただちに使用して、アプリケーションに位置情報の権限が付与されているかどうかを確認します。 権限が付与されていない場合、 OS はユーザーに権限を要求します。
LocationEngine
iOS プラットフォーム の位置情報を使用して位置情報の更新を生成します。 LocationAccuracy
が iOS 自体の CLLocationAccuracy
にマップする方法については、以下の表を参照してください。利用可能なすべてのモードの詳細については、API リファレンスを参照してください。
LocationAccuracy
次の表に、CLLocationAccuracy
からへのマッピングを示します。
位置精度 | CLLocationAccuracy |
最高の可用性 | kCLLocationAccuracyBest |
ナビゲーション | kCLLocationAccuracyBestForNavigation |
TensOfMeters | kCLLocationAccuracyNearestTenMeters |
100 分の 1 メートル法 | kCLLocationAccuracyHundredMeters |
キロメートル | kCLLocationAccuracyThreeKilometers |
LocationEngine
が開始された後 stop()
は、ユーザーがコールするまで開始状態のままになります。 LocationEngineStatus.alreadyStarted
最初にstop()
をコールせずにもう一度コールしようとすると、メッセージが表示されます。 このメソッドisStarted()
を使用して、エンジンが始動しているかどうかを確認できます。 同様に、 LocationEngine
を開始し、最初のものを停止せずに別のものを開始しようとすると、 LocationEngineStatus.alreadyStarted
エラーが発生します。 一度に始動できるエンジンは 1 つだけです。
位置情報の更新をこれ以上受信したくない場合 stop()
は、メソッドを呼び出してエンジンを停止できます。 不要になった受講者は、必ず削除してください。
class PositioningExample: LocationStatusDelegate, LocationDelegate {
public func stopLocating() {
locationEngine.removeLocationDelegate(locationDelegate: self)
locationEngine.removeLocationStatusDelegate(locationStatusDelegate: self)
locationEngine.stop()
}
}
一般 に、アプリが廃棄された場合は、LocationEngine
を停止することをお勧めします。
停止中の更新を一時停止
デフォルト LocationEngine
では、ロケーションデータが変更されない場合、は自動的にロケーションの更新を一時停止します。 これを使用すると、デバイスが静止している場合など、バッテリーの寿命を延ばすことができます。 このフィーチャーは、 LocationEngine.setPauseLocationUpdatesAutomatically()
を呼び出して制御できます。
バックグラウンド更新の有効化
アプリケーションがバックグラウンドで実行されているときに位置情報の更新を引き続き受信する場合は、アプリのファイルInfo.plist
に次のキーを追加して、この機能を有効にする必要があります。
<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 のドキュメントを参照してください。
さらに、ユーザーに自動化のリクエストが必要です。 上記のセクションで共有したコード スニペットでは、必要な権限を追加 することもできます。
自動化がクリアされると、すべての設定が完了します。 LocationEngine.setBackgroundLocationAllowed()
メソッドを使用すると、バックグラウンドで位置情報の更新を有効または無効にできます。 LocationEngine.setBackgroundLocationIndicatorVisible()
メソッドを使用して、アプリケーションのバックグラウンド位置情報インジケータの表示範囲を設定することもできます。
最後に、 LocationEngine.setPauseLocationUpdatesAutomatically()
メソッドに false
渡すことで、デバイスが停止しているときに位置情報の更新が一時停止しないようにできます。
注
setBackgroundLocationAllowed()
と setBackgroundLocationIndicatorVisible()
は、アプリケーションでバックグラウンド位置情報フィーチャーが有効になっていない場合には、LocationEngineStatus.notAllowed
が戻ります。 それ以外の場合は 、LocationEngineStatus.ok
が返されます。
付属の Positioning Example アプリでは、既定ではバックグラウンド更新が有効になっていません。
Location
オブジェクト内に存在する horizontalAccuracyInMeters
フィールドは、「不確実性の半径」とも呼ばれ、68% の確率で真の地理座標が存在する可能性が高いエリアの推定値を提供します。これは、現在の位置の周囲にハローインジケータを描画するために使用されます。 下の図は、内側の緑色の円location.coordinates
に 、周囲の円を半径の精度の円horizontalAccuracyInMeters
として示し ています。 正確な地理座標は、精度円の内側(68%)または外側(32%)にあります。
図: 水平方向の不確実性と垂直方向の不確実性の半径。 同様に、高度の場合、 verticalAccuracyInMeters
値が10メートルの場合、実際の高度は 、 68%の確率で、高度-10mから高度+10mの範囲内にあると予想されます。 bearingAccuracyInDegrees
およびspeedAccuracyInMetersPerSecond
などの他の精度値も同じ規則に従います。不確実性が小さいほど精度が向上します。
注
この値coordinates.altitude
は平均海面レベル(MSL)に関連して与えられます。
68% を超える確率で達成 (CEP68)
68% の確率 (CEP68) では不十分な場合、 99% の精度を達成できますか ? はい、次のとおりです。 与えられた円形誤差確率 (CEP) は自由度が 2 つのカイ (χ)2乗分布に続くため、次の式に基づいて望ましい確率を簡単に計算できます。
確率 | 不確実性の半径 |
50% | CEP50 = 0.78 x CEP68 |
60% | CEP60 = 0.90 x CEP68 |
70% | CEP70 = 1.03 x CEP68 |
80% | CEP80 = 1.19 x CEP68 |
90% | CEP90 = 1.42 x CEP68 |
95% | CEP95 = 1.62 x CEP68 |
99% | CEP99 = 2.01 x CEP68 |
上の表を使用して、マップ上のハローインジケータのさまざまな確率レベルを表示できます。 たとえば、水平方向の精度が 20 メートルの場合、半径のほぼ 2 倍の確率で 99 %を達成できます。 精度値は常に CEP68 として指定されます。つまり、次のことを意味します。
CEP99 = 2.01 x CEP68 = 2.01 x 20m = 40.2m
発見された場所の周囲に 40.2 メートルの半径を描画できるようになり ました。確率は 99% で、実際の場所はその円の内側にあります。 一方、半径 0 メートルの確率は 0% です。
チュートリアル : 現在地をマップに表示
LocationIndicator
は 、マップ上のデバイスの現在位置を表すために使用されます。 インジケータが現在の位置の値で更新される前に、デフォルト Location
が設定されます。デフォルトは、最後に確認された場所、または最初の位置更新が到着する前にユーザが表示する必要がある場所です。 デフォルトでは、水平精度は半径horizontalAccuracyInMeters
のMapCircle
で表示されます。
private static let defaultGeoCoordinates = GeoCoordinates(latitude: 52.520798, longitude: 13.409408)
private var locationIndicator: LocationIndicator!
private func addMyLocationToMap(myLocation: Location) {
locationIndicator = LocationIndicator()
locationIndicator.isAccuracyVisualized = true
locationIndicator.locationIndicatorStyle = .pedestrian;
locationIndicator.updateLocation(myLocation)
mapView.addLifecycleDelegate(locationIndicator)
let distanceInMeters = MapMeasure(kind: .distance,
value: PositioningExample.defaultCameraDistance)
mapCamera.lookAt(point: myLocation.coordinates,
zoom: distanceInMeters)
}
private func updateMyLocationOnMap(myLocation: Location) {
locationIndicator.updateLocation(myLocation)
mapCamera.lookAt(point: myLocation.coordinates)
}
if let lastLocation = locationEngine.lastKnownLocation {
addMyLocationToMap(myLocation: lastLocation)
} else {
var defaultLocation = Location(coordinates: PositioningExample.defaultGeoCoordinates)
defaultLocation.time = Date()
addMyLocationToMap(myLocation: defaultLocation)
}
func onLocationUpdated(_ location: Location) {
updateMyLocationOnMap(myLocation: location)
}
スクリーンショット: マップ上の現在の位置を示す位置情報インジケータ。 上記の実装に示されているように、updateLocation()
を呼び出して、Location
オブジェクトを位置情報インジケータに渡すことができます。 この例では、ユーザーの現在の位置を追跡することを目的としています。そのため、マップビューポートの中心位置も更新されます。