【kotlin / osmdroid】GPS測位位置を地図の中心にする

前回の記事では国土地理院地図を表示しましたが、最初に表示される地図のセンター位置は固定(東京タワー)としていました。

blog.misatowater.com

今回は地図の中心をGPSで測位した位置にするよう改修を加えます。

FusedLocationProviderClientを利用した現在位置の取得

FusedLocationProviderClientでGPS測位位置を取得します。GPS測位位置の取得にあたりユーザからロケーション利用のパーミッションを得る必要があります。

build.gradle

dependencies {
    implementation 'com.google.android.gms:play-services-location:17.0.0'   //追加
}

AndroidManifest.xml

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

MainActivity.kt

GPS測位のたびに中心を変えていっても構わないのですが、地図で移動先を確認している際に中心が動いてしまうとわずらわしいので、位置取得初回のみセンターを設定するようにします。
今後の応用を考えてGPS測位位置は常時取得し、チェックフラグでセンターを設定するかどうかを決めています。初回に1回だけ取得するならば、lastLocationの取得ではなくrequestLocationUpdatesを実装したほうが無難です。

import android.Manifest
import android.content.pm.PackageManager
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.preference.PreferenceManager
import androidx.appcompat.app.AlertDialog
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationCallback
import com.google.android.gms.location.LocationRequest
import com.google.android.gms.location.LocationResult
import org.osmdroid.config.Configuration
import org.osmdroid.tileprovider.tilesource.XYTileSource
import org.osmdroid.util.GeoPoint
import org.osmdroid.views.MapView
import org.osmdroid.views.overlay.ScaleBarOverlay

class MainActivity : AppCompatActivity() {

    private val PERMISSION_REQUEST_CODE = 1000

    private var centerTrigger = true

    private lateinit var mapView: MapView
    private lateinit var fusedLocationClient : FusedLocationProviderClient
    private lateinit var locationCallback: LocationCallback

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Configuration.getInstance().load(applicationContext, PreferenceManager.getDefaultSharedPreferences(applicationContext))
        fusedLocationClient = FusedLocationProviderClient(this)
        setContentView(R.layout.activity_main)

        mapView = findViewById(R.id.mapview)
        val tileSource = XYTileSource("GSI", 5, 18, 256,
            ".png", arrayOf("https://cyberjapandata.gsi.go.jp/xyz/std/"))
        mapView.setTileSource(tileSource)
        mapView.setMultiTouchControls(true)

        // パーミッションチェック
        if(ContextCompat.checkSelfPermission(this,
                Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // パーミッション未許可
            if(ActivityCompat.shouldShowRequestPermissionRationale(this,
                    Manifest.permission.ACCESS_FINE_LOCATION)) {
                AlertDialog.Builder(this)
                    .setMessage("need location permission")
                    .setPositiveButton("OK", ({_, _ ->
                        ActivityCompat.requestPermissions(this,
                            arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), PERMISSION_REQUEST_CODE)
                        })).show()
            } else {
                ActivityCompat.requestPermissions(this,
                    arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), PERMISSION_REQUEST_CODE)
            }
        } else {
            // パーミッション許可済
            trackLocation()
        }
    }

    // パーミッション可否に対するアクション
    override fun onRequestPermissionsResult(requestCode: Int,
            permissions: Array<out String>,  grantResults: IntArray) {
        when(requestCode) {
            PERMISSION_REQUEST_CODE -> {
                if((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
                    trackLocation()
                } else {
                    finish()
                }
            }
            else -> {
                super.onRequestPermissionsResult(requestCode, permissions, grantResults)
            }
        }
    }

    // GPS位置測位結果の取得
    private fun trackLocation() {
        val locationRequest = LocationRequest().apply {
            interval = 10000
            fastestInterval = 5000
            priority = LocationRequest.PRIORITY_HIGH_ACCURACY
        }

        locationCallback = object:LocationCallback() {
            override fun onLocationResult(locationResult: LocationResult?) {
                super.onLocationResult(locationResult)
                val location = locationResult?.lastLocation
                if(location != null && centerTrigger) {
                    showMapCenter(location.latitude, location.longitude)
                    centerTrigger = false
                }
            }
        }

        fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, mainLooper)
    }

    // 地図センター設定
    private fun showMapCenter(latitude: Double, longitude: Double) {
        val mapController = mapView.controller
        mapController.setZoom(15.0)
        mapController.setCenter(GeoPoint(latitude,  longitude))
    }

    override fun onPause() {
        super.onPause()
        mapView.onPause()
    }

    override fun onResume() {
        super.onResume()
        mapView.onResume()
    }

    override fun onStop() {
        super.onStop()
        fusedLocationClient.removeLocationUpdates(locationCallback)
    }
}

blog.misatowater.com