LoginSignup
0
1

More than 3 years have passed since last update.

Android10でGPSの2点から距離を求める

Last updated at Posted at 2020-07-29

環境

IDE : Android Studio 4.0.1
OS : Android10
言語 : kotlin
ライブラリ : okhttp, YOLP(Yahoo API)

ライブラリの登録

build.gradle
dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation 'androidx.core:core-ktx:1.3.0'
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

    implementation 'com.google.android.gms:play-services-location:17.0.0'
    implementation 'com.squareup.okhttp3:okhttp:4.4.0'
}

保存したら右クリックでsync nowを選択し、ライブラリをIDEに反映させる。

パーミッションの追加

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.takilab.aroundhokkaido">
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

    <application
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

インターネットへのアクセスを有効にする権限
android.permission.INTERNET

位置情報を取得する権限
android.permission.ACCESS_COARSE_LOCATION
android.permission.ACCESS_FINE_LOCATION
android.permission.ACCESS_BACKGROUND_LOCATION

大文字小文字に注意すること(ハマったのは内緒)

UI

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/locationText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="[0]"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/distanceText"
        android:layout_width="76dp"
        android:layout_height="17dp"
        android:layout_marginTop="24dp"
        android:text="[km]"
        android:textAlignment="center"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/locationText" />

</androidx.constraintlayout.widget.ConstraintLayout>

位置情報と距離情報をとりあえず画面に出す。

コード

package com.takilab.aroundhokkaido

import android.Manifest
import android.annotation.SuppressLint
import android.content.pm.PackageManager
import android.location.Location
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Log
import androidx.core.app.ActivityCompat
import com.google.android.gms.location.*
import kotlinx.android.synthetic.main.activity_main.*
import okhttp3.*
import org.json.JSONObject
import java.io.IOException

class MainActivity : AppCompatActivity() {
    companion object {
        private const val PERMISSION_REQUEST_CODE = 1234
    }

    private val appid = "ひみつー"

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

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        requestPermission()

        fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)

        var updatedCount = 0
        var prevLocation : Location? = null

        val client : OkHttpClient = OkHttpClient()
        val url : String = "https://map.yahooapis.jp/dist/V1/distance?output=json&coordinates=%f,%f %f,%f&appid=%s"
        // 位置情報取得時のコールバック定義オブジェクト
        locationCallback = object : LocationCallback() {
            /**
             * 位置情報取得時の処理
             */
            @SuppressLint("SetTextI18n")
            override fun onLocationResult(locationResult: LocationResult?) {
                locationResult ?: return
                for (location in locationResult.locations) {
                    updatedCount++
                    if (prevLocation == null) {
                        locationText.text = "[${updatedCount}] ${location.latitude} , ${location.longitude}"
                    } else {
                        locationText.text = "[${updatedCount}] ${location.latitude} , ${location.longitude}" +
                                " - ${prevLocation!!.latitude} , ${prevLocation!!.longitude}"
                        // HTTPリクエストを作成
                        val request = Request.Builder()
                            .url(url.format(location.longitude, location.latitude, prevLocation!!.longitude, prevLocation!!.latitude, appid))
                            .build()
                        // HTTPリクエストを送信
                        client.newCall(request).enqueue(object : Callback {
                            /**
                             * HTTPリクエスト失敗時の処理
                             */
                            override fun onFailure(call: Call, e: IOException) {
                                println("fail : $e")
                            }

                            /**
                             * HTTPリクエスト成功時の処理
                             */
                            override fun onResponse(call: Call, response: Response) {
                                var str = response!!.body!!.string()
                                val jsonObject = JSONObject(str)
                                val jsonArray = jsonObject.getJSONArray("Feature")
                                for (i in 0 until jsonArray.length()) {
                                    val jsonData = jsonArray.getJSONObject(i)
                                    val geometry = jsonData.getJSONObject("Geometry")
                                    val distance = geometry.getDouble("Distance")
                                    // ハンドラを経由してメインスレッドからUIにアクセスしないとエラーになる。
                                    val mainHandler : Handler = Handler(Looper.getMainLooper())
                                    mainHandler.post(Runnable {
                                        distanceText.text = "$distance"
                                    })
                                }
                            }
                        })
                    }
                    prevLocation = location
                }
            }
        }
    }

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

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

    /**
     * 権限の問い合わせを行う。
     */
    private fun requestPermission() {
        // 位置情報にアクセスする権限があるかどうかを確認する。
        if (!checkPermission()) {
            // 権限が無いため、許可を求める
            ActivityCompat.requestPermissions(this,
                arrayOf(
                    Manifest.permission.ACCESS_COARSE_LOCATION,
                    Manifest.permission.ACCESS_FINE_LOCATION,
                    Manifest.permission.ACCESS_BACKGROUND_LOCATION
                ),
                PERMISSION_REQUEST_CODE)
        }
    }

    /**
     * 権限の確認を行う
     */
    private fun checkPermission() : Boolean {
        return ActivityCompat.checkSelfPermission(this,
                Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED &&
                    ActivityCompat.checkSelfPermission(this,
                        Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED &&
                    ActivityCompat.checkSelfPermission(this,
                        Manifest.permission.ACCESS_BACKGROUND_LOCATION) == PackageManager.PERMISSION_GRANTED
    }

    /**
     * 位置情報の取得開始
     */
    private fun startLocationUpdates() {
        // 権限の再確認(無いとIDE(Android Studio)に怒られる)
        if (!checkPermission()) {
            requestPermission()
            return
        }
        // 位置情報の取得開始
        val locationRequest = createLocationRequest() ?: return
        fusedLocationClient.requestLocationUpdates(
            locationRequest,
            locationCallback,
            null)
    }

    /**
     * 位置情報の取得を停止する
     */
    private fun stopLocationUpdates() {
        // 位置情報の取得停止
        fusedLocationClient.removeLocationUpdates(locationCallback)
    }

    /**
     * 位置情報を行うインスタンスの作成
     */
    private fun createLocationRequest(): LocationRequest? {
        return LocationRequest.create()?.apply {
            interval = 10000
            fastestInterval = 5000
            priority = LocationRequest.PRIORITY_HIGH_ACCURACY
        }
    }
}

GPSの情報を取得する前に、アプリに権限が付与されているかを確認する。
権限が無ければユーザーに権限付与を求めるダイアログを表示する。
startLocationUpdates()でGPSの情報を取得開始。
GPSのデータを取得すると、onLocationResult()が実行される。
現位置と前位置の情報を元に、YahooのAPIを使用して2点間の距離を問い合わせる。
Request.Builder()でHTTPリクエストを作成し、client.newCall()で送信。
HTTPレスポンスを受信するとonResponse()が実行される。
レスポンスからJsonを取得し、解析して、距離データ(km)を取得する。
UIに反映させるには、メインスレッドでなければエラーになるので、
ハンドラを使用してメインスレッドからUIに反映させる。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1