本記事では、AndroidアプリにGoogle Mapsと位置情報取得機能を組み込み、リアルタイムで現在位置を表示しつつ、移動軌跡を地図上に表示する方法を紹介します。
Androidの位置情報パーミッション解説
- ACCESS_FINE_LOCATION
概要: 高精度の位置情報(GPSやWi-Fi、携帯基地局など)を取得するための権限。
用途: ナビアプリ、ランニングトラッカー、精密な地図サービスなど。
特徴: 数メートル単位の精度。
2.ACCESS_COARSE_LOCATION
概要: 粗い位置情報(Wi-Fiや携帯基地局)を取得するための権限。
用途: おおよその現在地で十分なサービス(天気、周辺情報など)。
特徴: 数百メートル〜数キロの精度。
3.ACCESS_BACKGROUND_LOCATION
概要: アプリがバックグラウンドでも位置情報を取得するための権限。
用途: 運動ログ、デリバリー追跡、防犯サービスなど。
注意点:
Android 10(API 29)以降で導入。
ACCESS_FINE_LOCATIONまたはACCESS_COARSE_LOCATIONとセットで必要。
ユーザーに強い理由を提示しないと許可されにくい。
注意事項
Android 6.0(API 23)以降は実行時パーミッションの取得が必須。
ACCESS_BACKGROUND_LOCATIONはユーザー体験やプライバシーに大きく関わるため、Google Playの審査が厳しいです。
アプリ概要
- 現在地の緯度・経度、高度、精度、速度、進行方向、時刻を表示
- Google Maps上に現在地マーカーと円(半径30m)を表示
- 過去の位置履歴を
Polyline
として地図に描画 -
ACCESS_FINE_LOCATION
を使用して高精度な位置情報を取得
AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<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"/>
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/drone"
android:label="@string/app_name"
android:roundIcon="@mipmap/drone_round"
android:supportsRtl="true"
android:theme="@style/Theme.Demo0430"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="YOUR_API_KEY_HERE" />
</application>
</manifest>
MainActivity.kt
の実装ポイント
-
FusedLocationProviderClient
を使用して位置情報を取得 -
LocationCallback
で位置更新を受け取り、UI・地図を更新 -
Polyline
で移動の軌跡を地図に描画
override fun onLocationResult(result: LocationResult) {
val location = result.lastLocation ?: return
// 情報表示用のテキスト作成
val timeStr = SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.getDefault()).format(Date(location.time))
val info = """
緯度: ${location.latitude}
経度: ${location.longitude}
高度: ${location.altitude} m
精度: ${location.accuracy} m
速度: ${location.speed} m/s
進行方向: ${location.bearing} °
時刻: $timeStr
""".trimIndent()
locationText.text = info
val latLng = LatLng(location.latitude, location.longitude)
// マーカー・サークルの更新
currentMarker?.remove()
currentMarker = googleMap.addMarker(
MarkerOptions().position(latLng).title("現在地")
.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_AZURE))
)
currentCircle?.remove()
currentCircle = googleMap.addCircle(
CircleOptions()
.center(latLng)
.radius(30.0)
.strokeColor(Color.RED)
.fillColor(0x30FF0000)
.strokeWidth(2f)
)
// 軌跡描画
pathPoints.add(latLng)
googleMap.addPolyline(
PolylineOptions().addAll(pathPoints).color(Color.BLUE).width(8f)
)
googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 17f))
}
パーミッション確認とリクエスト
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED
) {
startLocationUpdates()
} else {
permissionLauncher.launch(Manifest.permission.ACCESS_FINE_LOCATION)
}
activity_main.xml
- UIレイアウト
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="8dp">
<TextView
android:id="@+id/textLocation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="位置情報取得中..."
android:textSize="16sp"
android:padding="8dp" />
<fragment
android:id="@+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
strings.xml
<resources>
<string name="app_name">demo0430</string>
<string name="location_loading">位置情報を取得中...</string>
<string name="location_error">位置情報を取得できませんでした</string>
</resources>
build.gradle.kts
(アプリモジュール)
android {
namespace = "com.example.demo0430"
compileSdk = 35
defaultConfig {
applicationId = "com.example.demo0430"
minSdk = 34
targetSdk = 35
versionCode = 1
versionName = "1.0"
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "11"
}
}
dependencies {
implementation("com.google.android.gms:play-services-location:21.0.1")
implementation("com.google.android.gms:play-services-maps:18.1.0")
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat)
implementation(libs.material)
implementation(libs.androidx.activity)
implementation(libs.androidx.constraintlayout)
}
補足と改善ポイント
-
fetchLocation()
やonLocationResult()
内でもSecurityException
をtry-catch
で明示的に扱うとより堅牢になります。 -
accuracy
は環境により誤差が出るため、ユーザーに注意書きを加えると親切です。 - 軌跡データ(
pathPoints
)をRoomやSharedViewModelで保存・共有することで履歴タブなどの実装にも応用可能です。
おわりに
このアプリをベースに、今後は以下の機能拡張も可能です:
- 履歴表示(リスト/地図)機能
- バックグラウンドでの位置取得
- 位置情報のAWS等へのアップロード