Share
Embed
[DroidKaigi2024] 電池寿命を考えた位置情報の監視方法を考える(Geofence)
# 背景
皆さんは、位置情報の監視をされたことはありますでしょうか?
監視をしたい用途としては、自分が家の付近にいるかどうかを判定してイベントを発行したり、イベント会場などに入ったときに通知を送りたいなど、様々な用途で使用をされていると思います。
位置情報の監視をすると一番最初に思いつくアルゴリズムとしては、数分に一回GPSを受信して、その位置情報が自分の決めたエリアに入っているかを判定するという動作になると思います。
ですが、GPSの取得には大きな電力を使用されます。
そこで今回GoogleMobileService(以下、GMS)の一つの機能であるGeofenceを用いて位置情報の監視を行い、電池寿命の改善を行いたいと考えます。
GMSのGeofenceは、GPS情報の他にモバイルネットワークの情報などのフィンガープリントを利用することで電池寿命を延ばしている
# このセッションで行うこと
このセッションではGeofenceの概要から説明を始め、Geofenceの簡単なアルゴリズムの組み立て方を説明し、その後GMSのGeofenceを実装して、動作検証を行う。
harutiro
September 24, 2024
Transcript
125 slides
-
⾃⼰紹介 • 牧野遥⽃ (makino haruto) ◦ GitHub: harutiro ◦ X(Twitter)
: minesu1224 ◦ Qiita: harutiro • 愛知⼯業⼤学 梶研究室 (Aichi Institute of Technology KajiLab) ◦ ⾏動をセンシングし、⾏動をデザインする ◦ センサを⽤いた屋内位置推定、 ⾏動認識技術の追求 3 -
⽬次 1. 位置情報サービスの仕組みについて 2. ジオフェンシングとは? 3. ⾃分でジオフェンスを実装をしてみる 4. もう少し効率的にジオフェンスを実装してみる 5.
GMSのジオフェンスの実装⽅法 6. 簡単な実演 7. まとめ 16 -
⽬次 1. 位置情報サービスの仕組みについて 2. ジオフェンシングとは? 3. ⾃分でジオフェンスを実装をしてみる 4. もう少し効率的にジオフェンスを実装してみる 5.
GMSのジオフェンスの実装⽅法 6. 簡単な実演 7. まとめ 17 -
位置情報の利⽤例 現実世界とリンクした、ユーザーのニーズに合わせたプロダクト作れる 19 • 地図アプリやカーナビゲーション ◦ GoogleMaps、Yahoo!地図など • ⼈流把握 ◦
⼈流アナリティクスなど • タクシー配⾞アプリ ◦ GO、DiDiなど • 位置情報を活⽤したゲーム ◦ ポケモンGO、駅メモ、Monster Hunter Nowなど • 家族や友⼈との位置情報共有アプリ ◦ Whoo、GoogleMapsなど... 総務省「位置情報(空間情報)を活用したサービス 」https://www.soumu.go.jp/johotsusintokei/whitepaper/ja/r05/html/nd247510.html -
GPSでの測位 23 • GPS衛星から発せられた電波を受信し、現在地を特定する • 電波を利⽤するため、 電波が正しく届かないところでは位置精度が下がる ◦ 建物内、電波が乱反射するところなど ◦
GPSは到達時間をみて位置を把握するため、 反射してしまうと到達時間が狂ってしまう -
GPS以外の位置推定 ~コラム~ 26 東京駅の Wi-Fi • 電波の強度を利⽤して位置推定を⾏う お、東京駅のWi-Fiと東京駅のスタバのWi-Fiが 近くにあるから、⼤体この辺かな? 東京駅の
スタバのWi-Fi あらかじめWi-Fiの電波情報を持っていれば、測位が可能 -
⽬次 1. 位置情報サービスの仕組みについて 2. ジオフェンシングとは? 3. ⾃分でジオフェンスを実装をしてみる 4. もう少し効率的にジオフェンスを実装してみる 5.
GMSのジオフェンスの実装⽅法 6. 簡単な実演 7. まとめ 29 -
Geofenceのアルゴリズムのやり⽅ 33 家の鍵が開く領域を決める 01 定義 ⾃分の位置情報を取得する 02 取得 範囲に⼊っていたら鍵を開ける 04
実⾏ ⾃分がその領域に⼊っているか計算をする 03 計算 -
Geofenceのエリアを決める 34 家の鍵が開く領域を定義する 01 STEP 定義 単純に家と⾃分の「距離」を利⽤する 家の緯度経度を調べる 範囲を決める(今回は円) 半径
300m 緯度 : 35.68124 経度 : 139.76769 -
⾃分の位置を取得してGeofenceの監視をする 35 02 STEP 取得 ⾃分の位置情報を取得する スマートフォンなどを⽤い位置を取得する 緯度 : 35.68124
経度 : 139.76769 半径: 0.3km 緯度 : 35.70000 経度 : 139.50000 -
エリアの中に⼊っているか計算を⾏う 36 ⾃分と家の距離を「三平⽅の定理」で簡単に出せるが「誤差」は⼤きい 03 STEP 計算 ⾃分がその領域に⼊っているか計算をする (ガバガバ計算例) 緯度 経
度 緯度 : 35.70 経度 : 139.50 緯度 : 35.68 経度 : 139.76 座標の点を置く 三平⽅の定理で距離を出す 緯度 経 度 緯度 : 35.70 経度 : 139.50 緯度 : 35.68 経度 : 139.76 ベクトルの距離を 実際の距離に変換する 0.26 ※1度=111km 0.26 * 111 = 29km -
実際の計算例 37 実際に計算をしてみると 0.26 * 111 = 28.86km … ③
緯度経度からベクトル的な距離を出す 40000 / 360 = 111km … ② 地球の⼀周は約40000kmなので 360で割って1度は111kmとなる 求めた距離から111kmをかけることで 距離は出せる … ① -
距離を⽤いエリアに⼊っているか判断を⾏う 38 04 STEP 実⾏ 範囲に⼊っていたら鍵を開ける 緯度 : 35.68124 経度
: 139.76769 半径: 0.3km 緯度 : 35.70000 経度 : 139.50000 0.3km 29km 今回は範囲に⼊っていなかったので、実⾏されない if (0.3 > 29) → 条件式が合わない -
距離を⽤いエリアに⼊っているか判断を⾏う 39 04 STEP 実⾏ 範囲に⼊っていたら鍵を開ける 緯度 : 35.68120 経度
: 139.76760 0.3km 0.2km もしエリアの中に⼊っていたら実⾏される if (0.3 > 0.2) → 条件式があった 緯度 : 35.68124 経度 : 139.76769 半径: 0.3km -
Geofenceのアルゴリズムの考え⽅ ~まとめ~ 40 家の鍵が開く領域を決める 01 定義 ⾃分の位置情報を取得する 02 取得 範囲に⼊っていたら鍵を開ける
04 実⾏ ⾃分がその領域に⼊っているか計算をする 03 計算 -
各アプローチでどれくらい誤差があるのか 〜コラム1〜 45 国⼟地理院の計算ツール: 23.63 km Haversine公式 : 23.58 km
(+0.07 km) 三平⽅の定理 : 28.86 km (+5.28 km) 誤差をかなり減らすことができる -
ジオフェンスを⾃由な形で実装する⽅法 〜コラム2〜 47 多⾓形の図形の内外判定⽅法 ※1 株式会社NTTPC コミュニケーションズ . 【第2 回】点の多角形に対する内外判定【技業
log】技術者が紹介する NTTPC のテクノロジー . https://www.nttpc.co.jp/technology/number_algorithm.html 交差数判定を⽤いる 1. 判別したい点からx 軸に⽔平な半直線をとる。 2. その半直線と多⾓形の辺が交差する点を数える。 3. その交点の数が奇数なら内部の点, 偶数なら外部の点とみなす。 ※1 -
⽬次 1. 位置情報サービスの仕組みについて 2. ジオフェンシングとは? 3. ⾃分でジオフェンスを実装をしてみる 4. もう少し効率的にジオフェンスを実装してみる 5.
GMSのジオフェンスの実装⽅法 6. 簡単な実演 7. まとめ 49 -
今回のスライドを作るにあたり参考にした記事 50 梶研究室のメンバーが作成した記事の引⽤と ⾃分の知識を混ぜて作成をしました 詳しい内容はこちらの記事もみていただけると幸いです ※1 log_sucaki Androidで一回位置情報を取得するボタンを作る https://zenn.dev/log_suzaki/articles/de6784e86590be ※2
log_sucaki FusedLocationで位置情報を継続的に取得する! [ver21.0.1] https://zenn.dev/log_suzaki/articles/e63c1446c61464 ※3 年普@ Androidで位置情報を取得する https://zenn.dev/nenfa/articles/d9258d1f92573a ※1 ※2 ※3 -
Geofenceの実装⽅法 〜⼿順〜 53 通知をする範囲を決める 01 定義 ⾃分の位置情報を取得する 02 取得 範囲に⼊っていたら動作を⾏う 04
実⾏ ⾃分がその領域に⼊っているか計算をする 03 計算 -
データ型の定義を⾏う 54 data class LatLon( val latitude:Double, val longitude:Double )
緯度経度をまとめた型 01 STEP 定義 -
通知を出したい範囲を定義する 55 01 STEP 定義 愛知⼯業⼤学 1号館 緯度:35.1849266 経度:137.1092261 半径:500m //
監視する中⼼位置 var monitorLocation: LatLon = LatLon( latitude = 35.1849266, longitude = 137.1092261 ) var monitorRadius: Double = 500.0 // 監視半径(メートル) -
位置情報の取得をするためのライブラリ導⼊ 57 implementation(libs.play.services.location) play-services-locationを導⼊する 02 STEP 取得 play-services-location = "21.3.0"
play-services-location = { module = "com.google.android.gms:play-services-location", version.ref = "play-services-location" } -
位置情報の取得をするためのパーミッション設定 58 <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"
/> 02 STEP 取得 • ACCESS_FINE_LOCATION ◦ 端末の正確な位置情報の取得の許可 • ACCESS_COARSE_LOCATION ◦ 端末の⼤体の位置情報の取得の許可 • ACCESS_BACKGROUND_LOCATION ◦ バックグラウンドで位置情報の取得の許可 -
位置情報を⼀度取得してみる 59 val gpsLocationManager = GPSLocationManager(this) gpsLocationManager.getLastLocation(object : GPSLocationManager.MyLocationCallback {
override fun onLocationResult(location: Location?) { if (location != null) { // 緯度経度の取得 val latitude = location.latitude val longitude = location.longitude } } override fun onLocationError(error: String) { // エラー処理 } }) GPSLocationManagerを⽤意して、取得を⾏う 02 STEP 取得 位置情報の取得は、たった10⾏程度でできました! -
エリアと⾃分の距離を計算する 61 03 STEP 計算 Haversineのアルゴリズムを実装する import kotlin.math.* fun distanceInMeters(lat1:
Double, lon1: Double, lat2: Double, lon2: Double): Double { val R = 6371000.0 // 地球の半径(メートル) val dLat = Math.toRadians(lat2 - lat1) val dLon = Math.toRadians(lon2 - lon1) val a = sin(dLat / 2) * sin(dLat / 2) + cos(Math.toRadians(lat1)) * cos(Math.toRadians(lat2)) * sin(dLon / 2) * sin(dLon / 2) val c = 2 * atan2(sqrt(a), sqrt(1 - a)) return R * c } -
エリアの中に⼊っているか判定をする 62 ⾃分の位置と、監視する位置の 距離を確認をする部分 fun checkMonitorLocation(latlon1: LatLon, latlon2: LatLon ,
radius: Double): Boolean { val distance = distanceInMeters(latlon1.latitude, latlon1.longitude, latlon2.latitude, latlon2.longitude) return distance <= radius } 04 STEP 実⾏ -
Geofenceの実装例 〜やり⽅〜 63 計算の部分を位置情報を取得するごとに⾏う gpsLocationManager.getLastLocation(object : GPSLocationManager.MyLocationCallback { override fun onLocationResult(location:
Location?) { if (location != null) { val checkResult = checkMonitorLocation(monitorLocation, LatLon(location.latitude, location.longitude), monitorRadius) // ⾃分の位置と、監視する位置を計算した結果を表⽰ // ここでやりたい動作をcheckResultを利⽤して切り替える Log.d(TAG, ${checkResult}) } } override fun onLocationError(error: String) { // エラー処理 } }) 04 STEP 実⾏ あとは、checkResultの値を利⽤して動作を切り替えるだけ -
Geofenceの実装⽅法 〜まとめ〜 65 通知をする範囲を決める 01 定義 ⾃分の位置情報を取得する 02 取得 範囲に⼊っていたら動作を⾏う 04
実⾏ ⾃分がその領域に⼊っているか計算をする 03 計算 -
⽬次 1. 位置情報サービスの仕組みについて 2. ジオフェンシングとは? 3. ⾃分でジオフェンスを実装をしてみる 4. もう少し効率的にジオフェンスを実装してみる 5.
GMSのジオフェンスの実装⽅法 6. 簡単な実演 7. まとめ 66 -
Geofenceの実装⽅法 69 通知をする範囲を決める 01 定義 ⾃分の位置情報を取得する 02 取得 範囲に⼊っていたら動作を⾏う 04
実⾏ ⾃分がその領域に⼊っているか計算をする 03 計算 取得⽅法を ⼯夫してみる -
効率的なGPSの取得⽅法 ~実装⽅法~ 73 private var getRate: Long = 10000//取得頻度(ms) private var
minRate: Long = 5000//更新頻度(ms) private var fusedLocationClient: FusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(context) private var locationRequest: LocationRequest? = null private var locationCallback: LocationCallback? = null 変数と定数を⽤意する 02 STEP 取得 -
LocationRequestを⽤意して定期実⾏をする 74 // LocationRequestの設定 locationRequest = LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, getRate) //
10秒ごとに取得 .setMinUpdateIntervalMillis(minRate) // 最⼩間隔5秒 .build() 02 STEP 取得 Priority.PRIORITY_HIGH_ACCURACYは精度の指定 getRateは取得間隔をミリ秒単位で指定 詳しい設定内容は次のスライドで⾏います -
⼀般的な電池消耗について 76 • 精度: 位置情報データの精度 ◦ 精度が⾼いほど電池の消耗が激しくなる • 頻度: 位置情報が計算される頻度
◦ 位置情報の計算頻度が⾼いほど電池の消費量が増加する • レイテンシ: 位置情報データの配信が遅れる度合 ◦ レイテンシが低いほど、電池の消費量が増加します LocationRequestを⾃分の欲しい情報に合わせて設定する -
LocationRequestで精度を変更する⽅法 77 • PRIORITY_HIGH_ACCURACY ◦ 可能な限り最も⾼い精度の位置情報が提供される。 ◦ GPS、Wi-Fi、セルラーが有効化され、様々なセンサーが使われる ◦ 電池が著しく消耗する可能性がある
• PRIORITY_BALANCED_POWER_ACCURACY ◦ 正確な位置情報が提供される⼀⽅で、電⼒消費が最適化されます ◦ GPS はほとんど使⽤されないで、Wi-Fi 情報とセルラー情報を利⽤する • PRIORITY_LOW_POWER ◦ 低い精度(都市レベル)の位置情報を提供する ◦ 主として基地局の情報に依存し、GPS と Wi-Fi からの⼊⼒の使⽤を避ける ◦ 電池の消耗を最⼩限に抑える • PRIORITY_NO_POWER ◦ 他のアプリから位置情報を受け取る 位置情報の精度を指定するには、setPriority() メソッドを使⽤する [ 精度 ] -
LocationRequestで頻度を変更する⽅法 78 setIntervalMillis() とsetMinUpdateIntervalMillis() を使⽤する [ 頻度 ] • setIntervalMillis()
◦ アプリのために位置情報を計算する時間間隔を指定する ◦ 単位はミリ秒 • setMinUpdateIntervalMillis() ◦ 最⼩の更新時間 ◦ setIntervalMillis() で設定した時間より早くきても、 データを渡さないようにする バッテリー消費を抑えるためできる限り⻑い間隔にしたほうがいい -
LocationRequestでレイテンシを変更する⽅法 79 setMaxUpdateDelayMillis() を使⽤する [ レイテンシ ] • 位置情報の更新を遅らせることができる最⻑時間を設定する このパラメータは、位置情報のバッチ処理動作を制御する
• 2 秒間隔で最⼤ 10 秒の更新遅延を指定してリクエストが⾏われた場合、 デバイスは 10 秒ごとに 5 つの場所のバッチを配信する • バッチ処理とは ◦ センサー⾃体は動かしてバッファに⼊れておき、 データの処理はまとめて⾏うこと。 GooglePlayService LocationRequest https://developers.google.com/android/reference/com/google/android/gms/location/LocationRequest.Builder#public-locationrequest.builder-setmaxupdatedelaymillis-long-maxupdatedelaymillis -
LocationRequestを⽤意して定期実⾏をする 80 // LocationRequestの設定 locationRequest = LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, getRate) //
10秒ごとに取得 .setMinUpdateIntervalMillis(minRate) // 最⼩間隔5秒 .build() 02 STEP 取得 精度:可能な限り⾼くする 頻度:5000ミリ秒(5秒) レイテンシ:頻度と同じ 今回は素早く動作を判定したいため、以下のような設定にする -
再取得をするコールバックを⽤意する 81 locationCallback = object : LocationCallback() { // 再取得をするコールバックの作成
override fun onLocationResult(locationResult: LocationResult) { for (location in locationResult.locations) { Log.d(TAG, “${location}”) // ロケーションの取得 } } override fun onLocationAvailability(availability: LocationAvailability) { if (!availability.isLocationAvailable) { Log.e(TAG,"Location unavailable") // エラー処理 } } } fusedLocationClient.requestLocationUpdates(locationRequest!!, locationCallback!!, null) // ⼀定期間後にコールバックを実⾏するようにコールバックを代⼊する 02 STEP 取得 位置情報を取得した時の動作を記述する -
エリアと⾃分の距離を計算する 82 03 STEP 計算 計算のアルゴリズムは、前回と全く同じ import kotlin.math.* fun distanceInMeters(lat1:
Double, lon1: Double, lat2: Double, lon2: Double): Double { val R = 6371000.0 // 地球の半径(メートル) val dLat = Math.toRadians(lat2 - lat1) val dLon = Math.toRadians(lon2 - lon1) val a = sin(dLat / 2) * sin(dLat / 2) + cos(Math.toRadians(lat1)) * cos(Math.toRadians(lat2)) * sin(dLon / 2) * sin(dLon / 2) val c = 2 * atan2(sqrt(a), sqrt(1 - a)) return R * c } -
距離を元に動作を切り分ける 83 計算の部分を位置情報を取得するごとに⾏う fun startLocationUpdates(callback: MyLocationCallback) { locationCallback = object
: LocationCallback() { override fun onLocationResult(locationResult: LocationResult) { for (location in locationResult.locations) { val checkResult = checkMonitorLocation(monitorLocation, LatLon(location.latitude, location.longitude), monitorRadius) // ⾃分の位置と、監視する位置を計算した結果を表⽰ // ここでやりたい動作をcheckResultを利⽤して切り替える Log.d(TAG, ${checkResult}) } // 省略されています } } } 04 STEP 実⾏ あとは、checkResultの値を利⽤して動作を切り替えるだけ -
⽬次 1. 位置情報サービスの仕組みについて 2. ジオフェンシングとは? 3. ⾃分でジオフェンスを実装をしてみる 4. もう少し効率的にジオフェンスを実装してみる 5.
GMSのジオフェンスの実装⽅法 6. 簡単な実演 7. まとめ 85 -
GMSとは 87 Google のアプリや API をまとめたもの。 Google アプリが連携して端末の機能を補完する。 Google Mobile
Service(GMS) ※1 Google GoogleMobileService https://www.android.com/intl/ja_jp/gms/ ※1 -
GMSのGeoFenceの制限 90 • 円形のジオフェンスしか作れない • ジオフェンスが作れる個数は100個まで • ジオフェンスの⼤きさが⼩さすぎると うまく発⽕しないことも ◦
100m以上の⼤きさにすると良い ※1 AndroidDevelopers ジオフェンスの最適な半径を選択する https://developer.android.com/develop/sensors-and-location/location/geofencing?hl=ja#choose-the-optimal-radius-for-your-geofence ※1 -
パーミッションの設定とライブラリの設定 93 01 STEP 設定 <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"
/> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> implementation(libs.play.services.location) play-services-location = "21.3.0" play-services-location = { module = "com.google.android.gms:play-services-location", version.ref = "play-services-location" } 前回のライブラリとほとんど変わらない -
あらかじめデータ型を定義する 95 data class LatLon( val latitude:Double, val longitude:Double )
02 STEP 作成 緯度経度をまとめた型 data class EntryData( val key:String, val value:LatLon ) GeoFenceの情報を⼊れるための型 -
位置情報 API にアクセスする⽅法 96 var geofencingClient: GeofencingClient = LocationServices.getGeofencingClient(_activity) 02
STEP 作成 位置情報 API にアクセスするに、 ジオフェンス クライアントのインスタンスを作成する private val geofenceList = mutableListOf<Geofence>() 登録したジオフェンスのリストを残す変数 -
GeoFenceの設定 97 fun createGeofence(entry: EntryData, radius: Float) { val item
= Geofence.Builder() // ジオフェンスを識別するためのID .setRequestId(entry.key) // ジオフェンスの範囲と場所を設定 .setCircularRegion( entry.value.latitude, entry.value.longitude, radius ) // ジオフェンスの有効期限を設定 今回は無期限 .setExpirationDuration(Geofence.NEVER_EXPIRE) // ⼊室してから60秒間滞在したらアラートを発⽣させる .setLoiteringDelay(60000) // 対象となる遷移タイプを設定します。 アラートはこれらに対してのみ⽣成されます .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER or Geofence.GEOFENCE_TRANSITION_EXIT or Geofence.GEOFENCE_TRANSITION_DWELL) // ジオフェンスのベストエフォート通知応答性を設定します。 .setNotificationResponsiveness(5000) .build() geofenceList.add(item) } 02 STEP 作成 細かい説明は次のスライドから⾏います GEOFENCE_TRANSITION_DWELLを利⽤する時は、 setLoiteringDelayをsetTransitionTypeの前に設定をする -
GeoFenceの位置と半径の設定 98 // ジオフェンスの範囲と場所を設定 .setCircularRegion( entry.value.latitude, entry.value.longitude, radius ) 02
STEP 作成 • 半径の単位はメートル、緯度経度の単位は度数法で渡す • ジオフェンスの最⼩半径を 100〜150 メートルに設定する必要がある • Wi-Fi が利⽤可能な場合、位置情報の精度は20〜50 メートル 屋内の位置情報を利⽤できる場合は、精度範囲を 5 メートルほどに狭めれる • Wi-Fi の位置情報を利⽤できない場合は、精度範囲が数百メートルから 数キロメートルに広がる場合がある -
GeoFenceの有効期限の設定 99 // ジオフェンスの有効期限を設定 今回は無期限 .setExpirationDuration(Geofence.NEVER_EXPIRE) 02 STEP 作成 • ジオフェンスの有効期限を設定する。
この期間が過ぎると、ジオフェンスは⾃動的に削除される • 単位はミリ秒で設定する。 • 無制限にしたい場合は、Geofence.NEVER_EXPIREを代⼊する -
滞在扱いにする期間を設定する 100 // ⼊室してから60秒間滞在したらアラートを発⽣させる .setLoiteringDelay(60000) 02 STEP 作成 • エリアの侵入してから滞在しているか判定するまでの時間を
ミリ秒単位で設定する • たとえば、setLoiteringDelayが 30,000 ミリ秒 (30秒) に設定されている場合、 ユーザーが この期間中にジオフェンス内に留まると、約 30,000 ミリ秒後 (30秒) にアラートを送信する • ユーザーがこの時間内にジオフェンスから出ると、アラートは送信され ない • 遷移タイプに Geofence.GEOFENCE_TRANSITION_DWELLフィルターが含まれている 場合は、設 定は必須 -
GeoFenceの対象となる遷移タイプの設定 101 // 対象となる遷移タイプを設定します。 .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER or Geofence.GEOFENCE_TRANSITION_EXIT) 02 STEP 作成
ユーザーがジオフェンスに⼊ったことを⽰す遷移タイプ Geofence.GEOFENCE_TRANSITION_ENTER ユーザーがジオフェンスを出たことを⽰す遷移タイプ Geofence.GEOFENCE_TRANSITION_EXIT ユーザーがジオフェンスに⼊り、⼀定時間そこに留まってるか⽰す遷移タイプ Geofence.GEOFENCE_TRANSITION_DWELL -
GeoFenceの応答時間 102 // ジオフェンスのベストエフォート通知応答性を設定します。 .setNotificationResponsiveness(5000) 02 STEP 作成 • 単位はミリ秒
• 応答性の値を⼤きく設定すると電⼒を⼤幅に節約できる • 応答性の値に⾮常に⼩さい値を設定しても、ジオフェンスに 出⼊りした直後に通知が届くとは限らない • 内部的に、応答性の値を調整して電⼒を節約する場合がある -
GeoFenceの設定 まとめ 103 02 STEP 作成 これで最⼩設定の設定が終わりました fun createGeofence(entry: EntryData, radius:
Float) { val item = Geofence.Builder() // ジオフェンスを識別するためのID .setRequestId(entry.key) // ジオフェンスの範囲と場所を設定 .setCircularRegion( entry.value.latitude, entry.value.longitude, radius ) // ジオフェンスの有効期限を設定 今回は無期限 .setExpirationDuration(Geofence.NEVER_EXPIRE) // ⼊室してから60秒間滞在したらアラートを発⽣させる .setLoiteringDelay(60000) // 対象となる遷移タイプを設定します。 アラートはこれらに対してのみ⽣成されます .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER or Geofence.GEOFENCE_TRANSITION_EXIT) // ジオフェンスのベストエフォート通知応答性を設定します。 .setNotificationResponsiveness(5000) .build() geofenceList.add(item) } -
ジオフェンスと最初のトリガーを指定する 104 fun getGeofencingRequest(): GeofencingRequest { return GeofencingRequest.Builder().apply { //
トリガーを設定します。 setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER) addGeofences(geofenceList) }.build() } ジオフェンスが追加された瞬間、およびデバイスが⼀定時間そのジオフェンス内にいる場合にトリガーを実⾏する GeofencingRequest.INITIAL_TRIGGER_ENTER ジオフェンスが追加された瞬間、およびデバイスがすでにそのジオフェンス内にいる場合に、トリガーを実⾏する GeofencingRequest.INITIAL_TRIGGER_EXIT ジオフェンスが追加された瞬間、およびデバイスがすでにそのジオフェンスの外側にいる場合に、トリガーを実⾏する GeofencingRequest.INITIAL_TRIGGER_DWELL 02 STEP 作成 -
GeoFenceの出⼊りを処理するレシーバを定義 105 private val geofencePendingIntent: PendingIntent by lazy { val
intent = Intent(activity, GeofenceBroadcastReceiver/:class.java) PendingIntent.getBroadcast(activity, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT) } GeoFenceのトリガーが発動した時にどのような動作をして欲しいか、 ブロードキャストレシーバーを定義しておく PendingIntentを⽤いる 02 STEP 作成 -
ジオフェンスの追加 106 // もし追加をする時に、位置情報の取得を常にとやっていない場合はエラーになる geofencingClient.addGeofences(getGeofencingRequest(), geofencePendingIntent).run { addOnSuccessListener { Log.d(TAG,
"addGeoFences: Success") } addOnFailureListener { Log.e(TAG, "addGeoFences: Failure") Log.e(TAG, it.toString()) } } GeofencingRequest オブジェクトと PendingIntent を指定して、 GeoFenceを作成する。 成功かどうかで、リスナーが呼び出される 02 STEP 作成 -
ジオフェンスへの出⼊りを処理する 108 class GeofenceBroadcastReceiver : BroadcastReceiver() { override fun onReceive(context:
Context?, intent: Intent?) { // この中に動作を書いていく } } 03 STEP 実⾏ ブロードキャストレシーバーを⽤意して、 イベントを検知した時にバックグラウンドで動作をする サービスなどでも動作はするらしいが、 公式ではブロードキャストレシーバーがおすすめと書かれていました -
レシーバーの設定をManifestにする。 109 <application // …省略 tools:targetApi="31"> <activity> // …省略 </activity>
<receiver android:name=".feature.geofence.GeofenceBroadcastReceiver" android:enabled="true" android:exported="true" /> </application> 03 STEP 実⾏ -
ジオフェンスへの出⼊りを処理する 110 // GeoFencingイベントを取得 val geofencingEvent = GeofencingEvent.fromIntent(intent!!) // エラーハンドリング
if (geofencingEvent?.hasError() == true) { val errorMessage = GeofenceStatusCodes .getStatusCodeString(geofencingEvent.errorCode) Log.e(TAG, errorMessage) return } 03 STEP 実⾏ GeoFencingのイベントをIntentから取得して、エラーハンドリングを⾏う -
ジオフェンスへの出⼊りを処理する 111 // GeoFencingイベントのタイプを取得 val geofenceTransition = geofencingEvent?.geofenceTransition // ⼊室‧退室‧滞在しているかの判定
if ( geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER || geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT || geofenceTransition == Geofence.GEOFENCE_TRANSITION_DWELL ) { // この中に、どのような挙動をさせたいか書く } 03 STEP 実⾏ GeoFenceがどの状態で動作をしたのかを識別する。 ここで⼊室と退室で動作を切り替えることもできる -
ジオフェンスへの出⼊りを処理する 112 // ジオフェンスのトリガーイベントが発⽣した場合の処理 val triggeringGeofence: List<Geofence>? = geofencingEvent.triggeringGeofences //
トリガーされたジオフェンスの情報を利⽤して必要な処理を⾏います if (triggeringGeofence != null) { for (geofence in triggeringGeofence) { val requestId: String = geofence.requestId sendNotification(context, "ジオフェンスの「${transitionTypes(geofence.transitionTypes)}」ました") } } 03 STEP 実⾏ どのジオフェンスがトリガーとなったのかを取得する。 今回は通知を発⾏するコードを想定して作成している。 sendNotification() は通知を発⾏するメソッドを別で⽤意している -
⽬次 1. 位置情報サービスの仕組みについて 2. ジオフェンシングとは? 3. ⾃分でジオフェンスを実装をしてみる 4. もう少し効率的にジオフェンスを実装してみる 5.
GMSのジオフェンスの実装⽅法 6. 簡単な実演 7. まとめ 115 -
評価実験に⽤いるシチュエーション [実験内容] ジオフェンシングの精度と電池消費量が アプローチによってどのような差が ⽣まれるのかを確認する 116 [実験⽅法] 渋⾕の街を「何もしていない状態」 「⾃作ジオフェンスで監視をしている状態」 「GMSのジオフェンスで監視をしている状態」
の3つの状態のスマートフォンを持ち⼀周歩き、 電流の消費量と通知のタイミングを記録する [条件] • 半径は500m • 取得頻度は5秒程度に合わせる • セルラー情報は取れていない • スリープモードにしない -
実験結果 電⼒消費量 119 154.37 平均 141.00 中央値 40.90 標準偏差 268.72 平均
244.00 中央値 89.06 標準偏差 152.28 平均 131.00 中央値 62.14 標準偏差 ⾃作で作成したジオフェンスはかなりの電⼒を消費した ライブラリのジオフェンスは何もしていない時とほぼ変わらない -
⽬次 1. 位置情報サービスの仕組みについて 2. ジオフェンシングとは? 3. ⾃分でジオフェンスを実装をしてみる 4. もう少し効率的にジオフェンスを実装してみる 5.
GMSのジオフェンスの実装⽅法 6. 簡単な実演 7. まとめ 122 -
GMSと⾃作のGeoFenceメリデメ⼀覧 123 項⽬ GMSのGeoFence ⾃作のGeoFence 電池持ち 通常の状態とそこまで 変わらない 通常の2倍ほど 形
円形のみ ⾃由な多⾓形 精度 多少発⽕するまでに時間 がかかる 取得間隔を狭めれば⾼い GeoFenceの個数 100個 無制限 実装⾯ 特に位置情報サービスの 知識がなく作れる ⾊々考えた上でチューニング をする必要がある -
電池寿命を向上させる⽅法 124 • 位置情報の取得では ◦ 精度‧頻度‧レイテンシを必要な情報に合わせて設定する • Geofenceでは ◦ ⾃分のいる位置から明らかに離れている
ジオフェンスしかない場合は、取得間隔を減らす