【kotlin / osmdroid】GPS測位位置を地図上に表示する(ライブラリ使用)

前回の記事ではFusedLocationProviderClientを利用して、GPS測位位置を地図上に反映させました。しかしOsmdroidのサンプルをよく見ると、ライブラリの機能だけで現在位置を反映できるようです。

blog.misatowater.com

Osmdroidのライブラリの活用

Osmdroidのドキュメントを参考に

  • 自位置の反映
  • コンパス
  • スケールバー
  • 出典
  • センターボタン

を実装します。

自位置の反映とフォロー

val locationOverlay = MyLocationNewOverlay(GpsMyLocationProvider(applicationContext), mapView)
locationOverlay.enableMyLocation()
locationOverlay.enableFollowLocation()
mapView.overlays.add(locationOverlay)

ライブラリでGPS測位位置を取得しているため、呼び出す前に別途パーミッションを得る必要があります。
enableMyLocationは測位位置の表示、enableFollowLocationは自位置をセンターにして自位置が変わった(移動した)場合、センターを追従します。

コンパス

val compassOverlay = CompassOverlay(applicationContext, InternalCompassOrientationProvider(applicationContext), mapView)
compassOverlay.enableCompass()
mapView.overlays.add(compassOverlay)

スケールバー

val scaleBar = ScaleBarOverlay(mapView)
scaleBar.setAlignRight(true)
scaleBar.setScaleBarOffset(100, 100)
scaleBar.setTextSize(14 * this.resources.displayMetrics.density)
mapView.overlays.add(scaleBar)

出典

val copyrightOverlay = CopyrightOverlay(applicationContext)
copyrightOverlay.setAlignRight(true)
copyrightOverlay.setTextSize(14)
mapView.overlays.add(copyrightOverlay)

出典はXYTileSourceのcopyrightで指定した文字列が出力されます。

センターボタン

val centerMap = findViewById<ImageView>(R.id.center_map)
centerMap.setOnClickListener {
    mapController.animateTo(locationOverlay.myLocation)
    locationOverlay.enableFollowLocation()
}

地図を手動で動かすとフォローが解除されてしまうため、センターボタン押下時に再度フォローするようにしています。

activity_main.xml
<ImageView
    android:id="@+id/center_map"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginEnd="40dp"
    android:layout_marginBottom="40dp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:srcCompat="@drawable/osm_ic_center_map" />

レイアウトにセンターボタンのイメージを追加します。オーバーレイで追加するのが正しい形なのかもしれません。でもそこまでするメリットはないような気もします。

実装結果とMainActivity.kt全体

f:id:misatospring:20191017220024j:plain

自位置はデフォルトで人型です。
MyLocationNewOverlay#setPersonIcon(Bitmap icon)で変更できるようです。

MainActivity.kt

import android.Manifest
import android.content.pm.PackageManager
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.ImageView
import androidx.preference.PreferenceManager
import androidx.appcompat.app.AlertDialog
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import org.osmdroid.config.Configuration
import org.osmdroid.tileprovider.tilesource.XYTileSource
import org.osmdroid.views.MapView
import org.osmdroid.views.overlay.CopyrightOverlay
import org.osmdroid.views.overlay.ScaleBarOverlay
import org.osmdroid.views.overlay.compass.CompassOverlay
import org.osmdroid.views.overlay.compass.InternalCompassOrientationProvider
import org.osmdroid.views.overlay.mylocation.GpsMyLocationProvider
import org.osmdroid.views.overlay.mylocation.MyLocationNewOverlay

class MainActivity : AppCompatActivity() {

    private val PERMISSION_REQUEST_CODE = 1000
    private lateinit var mapView: MapView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Configuration.getInstance().load(applicationContext, PreferenceManager.getDefaultSharedPreferences(applicationContext))
        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("ロケーションの権限が必要です。")
                    .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 {
            // パーミッション許可済
            startActivity()
        }
    }

    override fun onRequestPermissionsResult(requestCode: Int,
            permissions: Array<out String>,  grantResults: IntArray) {
        when(requestCode) {
            PERMISSION_REQUEST_CODE -> {
                if((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
                    startActivity()
                } else {
                    finish()
                }
            }
            else -> {
                super.onRequestPermissionsResult(requestCode, permissions, grantResults)
            }
        }
    }

    private fun startActivity() {
        val mapController = mapView.controller
        mapController.setZoom(16.0)

        // スケールバー表示
        val scaleBar = ScaleBarOverlay(mapView)
        scaleBar.setAlignRight(true)
        scaleBar.setScaleBarOffset(100, 100)
        scaleBar.setTextSize(14 * this.resources.displayMetrics.density)
        mapView.overlays.add(scaleBar)

        // コンパス表示
        val compassOverlay = CompassOverlay(applicationContext,
            InternalCompassOrientationProvider(applicationContext), mapView)
        compassOverlay.enableCompass()
        mapView.overlays.add(compassOverlay)

        // 自位置表示
        val locationOverlay = MyLocationNewOverlay(GpsMyLocationProvider(applicationContext), mapView)
        locationOverlay.enableMyLocation()
        locationOverlay.enableFollowLocation()
        mapView.overlays.add(locationOverlay)

        // センターボタン
        val centerMap = findViewById<ImageView>(R.id.center_map)
        centerMap.setOnClickListener {
            mapController.animateTo(locationOverlay.myLocation)
            // 地図を動かすとfollowがとまるので、センター押下時は再度followさせる
            locationOverlay.enableFollowLocation()
        }

        // 出典
        val copyrightOverlay = CopyrightOverlay(applicationContext)
        copyrightOverlay.setAlignRight(true)
        copyrightOverlay.setTextSize(14)
        mapView.overlays.add(copyrightOverlay)
    }

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

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

blog.misatowater.com