Androidアプリ開発: Google Mapsで位置情報と移動履歴を表示するアプリの作成

本記事では、AndroidアプリにGoogle Mapsと位置情報取得機能を組み込み、リアルタイムで現在位置を表示しつつ、移動軌跡を地図上に表示する方法を紹介します。

---

 Androidの位置情報パーミッション解説

1. ACCESS_FINE_LOCATION

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

概要: 高精度の位置情報（GPSやWi-Fi、携帯基地局など）を取得するための権限。

用途: ナビアプリ、ランニングトラッカー、精密な地図サービスなど。

特徴: 数メートル単位の精度。



---

2.ACCESS_COARSE_LOCATION

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

概要: 粗い位置情報（Wi-Fiや携帯基地局）を取得するための権限。

用途: おおよその現在地で十分なサービス（天気、周辺情報など）。

特徴: 数百メートル〜数キロの精度。



---

3.ACCESS_BACKGROUND_LOCATION

<uses-permission android:name="android.permission.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`

```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` で移動の軌跡を地図に描画

```kotlin
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))
}
```

###  パーミッション確認とリクエスト

```kotlin
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レイアウト

```xml
<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`

```xml
<resources>
    <string name="app_name">demo0430</string>
    <string name="location_loading">位置情報を取得中...</string>
    <string name="location_error">位置情報を取得できませんでした</string>
</resources>
```

---

##  `build.gradle.kts`（アプリモジュール）

```kotlin
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等へのアップロード**

---