Android
 Computer >> コンピューター >  >> プログラミング >> Android

Androidで緯度経度から住所を取得する方法!Geocoderを使った逆ジオコーディングの実装手順

本記事では、Androidアプリにおいて緯度と経度の情報から住所(住所文字列)を取得する方法を、サンプルコード付きで段階的に解説します。位置情報から住所へ変換する処理は「逆ジオコーディング」と呼ばれ、Androidでは標準APIのGeocoderクラスを利用することで実装できます。

全体の流れ

今回作成するサンプルアプリは、ボタンをタップすると指定した緯度・経度に対応する住所を画面に表示するシンプルなものです。実装の流れは以下の通りです。

  1. Android Studioで新規プロジェクトを作成する
  2. レイアウトファイル(activity_main.xml)を編集する
  3. メインアクティビティ(MainActivity.java)を実装する
  4. 位置情報取得用サービスクラス(AppLocationService.java)を作成する
  5. 住所変換用クラス(LocationAddress.java)を作成する
  6. パーミッションをマニフェストに追加する

ステップ1:新規プロジェクトの作成

まず、Android Studioを起動し、メニューから「File」→「New Project」を選択して新しいプロジェクトを作成します。必要な項目をすべて入力してプロジェクトの雛形を生成しましょう。

ステップ2:レイアウトファイルの編集

次に、res/layout/activity_main.xmlに以下のコードを追加します。ボタンと住所表示用のTextViewを配置したシンプルなレイアウトです。

<RelativeLayout
    xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16sp"
    tools:context=".MainActivity">
    <TextView
        android:text=""
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/textView" />
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="40dp"
        android:text="Show Address"
        android:id="@+id/btnShowAddress"
        android:layout_centerHorizontal="true" />
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:id="@+id/tvAddress"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="134dp"
        android:layout_alignParentEnd="true" />
</RelativeLayout>

ステップ3:MainActivity.javaの実装

続いて、src/MainActivity.javaに以下のコードを記述します。ボタンがクリックされると、GPSプロバイダーから現在地を取得し、その座標をもとに住所変換処理を開始します。また、位置情報サービスが無効な場合には設定画面への遷移を促すダイアログも表示します。

import android.support.v7.app.AppCompatActivity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.location.Location;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.Settings;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
    Button btnShowAddress;
    TextView tvAddress;
    Location location;
    AppLocationService appLocationService;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tvAddress = findViewById(R.id.tvAddress);
        appLocationService = new
            AppLocationService(MainActivity.this);
        btnShowAddress = findViewById(R.id.btnShowAddress);
        btnShowAddress.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View arg0) {
                location = appLocationService.getLocation(LocationManager.GPS_PROVIDER);
                double latitude = 13.1000727;
                double longitude = 80.2126274;
                LocationAddress locationAddress = new LocationAddress();
                locationAddress.getAddressFromLocation(latitude, longitude,
                    getApplicationContext(), new GeoCodeHandler());
                showSettingsAlert();
            }
            public void showSettingsAlert() {
                AlertDialog.Builder alertDialog = new AlertDialog.Builder(MainActivity.this);
                alertDialog.setTitle("SETTINGS");
                alertDialog.setMessage("Enable Location
                    Provider! Go to settings menu?");
                alertDialog.setPositiveButton("Settings", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                        MainActivity.this.startActivity(intent);
                    }
                });
                alertDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                    }
                });
                alertDialog.show();
            }
            class GeoCodeHandler extends Handler {
                @Override
                public void handleMessage(Message message) {
                    String locationAddress;
                    switch (message.what) {
                        case 1:
                            Bundle bundle = message.getData();
                            locationAddress = bundle.getString("address");
                        break;
                        default:
                        locationAddress = null;
                    }
                    tvAddress.setText(locationAddress);
                }
            }
        });
    }
}

サンプルでは緯度・経度を固定値(latitude = 13.1000727longitude = 80.2126274)として指定していますが、実際のアプリではGPSで取得した現在地の座標を渡すようにしてください。

ステップ4:AppLocationService.javaの作成

次に、位置情報を取得するためのJavaクラスApplicationService.java(ここではAppLocationService)を作成し、以下のコードを追加します。LocationManagerを利用して、指定したプロバイダーからの最終既知位置情報を取得する仕組みです。

import android.annotation.SuppressLint;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.IBinder;
@SuppressLint("Registered")
public class AppLocationService extends Service implements LocationListener {
    protected LocationManager locationManager;
    Location location;
    private static final long MIN_DISTANCE_FOR_UPDATE = 10;
    private static final long MIN_TIME_FOR_UPDATE = 1000 * 60 * 2;
    public AppLocationService(Context context) {
        locationManager = (LocationManager) context.getSystemService(LOCATION_SERVICE);
    }
    @SuppressLint("MissingPermission")
    public Location getLocation(String provider) {
        if (locationManager.isProviderEnabled(provider)) {
            locationManager.requestLocationUpdates(provider,
                MIN_TIME_FOR_UPDATE,
                MIN_DISTANCE_FOR_UPDATE, this);
            if (locationManager != null) {
                location = locationManager.getLastKnownLocation(provider);
                return location;
            }
        }
        return null;
    }
    @Override
    public void onLocationChanged(Location location) {
    }
    @Override
    public void onProviderDisabled(String provider) {
    }
    @Override
    public void onProviderEnabled(String provider) {
    }
    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
    }
    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }
}

ステップ5:LocationAddress.javaの作成

次に、緯度経度を住所に変換するJavaクラスLocationAddress.javaを作成し、以下のコードを追加します。内部ではGeocoder.getFromLocation()メソッドをバックグラウンドスレッドで呼び出し、取得結果をHandler経由でUIスレッドに返しています。

import android.content.Context;
import android.location.Address;
import android.location.Geocoder;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import java.io.IOException;
import java.util.List;
import java.util.Locale;
class LocationAddress {
    private static final String TAG = "LocationAddress";
    static void getAddressFromLocation(final double latitude,
        final double longitude, final Context context, final Handler
        handler) {
        Thread thread = new Thread() {
            @Override
            public void run() {
                Geocoder geocoder = new Geocoder(context,
                    Locale.getDefault());
                String result = null;
                try {
                    List<Address> addressList =
                        geocoder.getFromLocation(
                        latitude, longitude, 1);
                    if (addressList != null &&
                        addressList.size() > 0) {
                        Address address = addressList.get(0);
                        StringBuilder sb = new
                            StringBuilder();
                        for (int i = 0; i < address.getMaxAddressLineIndex(); i++) {
                            sb.append(address.getAddressLine(i)).append("
");
                        }
                        sb.append(address.getLocality()).append("
");
                        sb.append(address.getPostalCode()).append("
");
                        sb.append(address.getCountryName());
                        result = sb.toString();
                    }
                } catch (IOException e) {
                    Log.e(TAG, "Unable connect to Geocoder", e);
                } finally {
                    Message message = Message.obtain();
                    message.setTarget(handler);
                    if (result != null) {
                        message.what = 1;
                        Bundle bundle = new Bundle();
                        result = "Latitude: " + latitude + " Longitude: " + longitude +
                            "

Address:
" + result;
                        bundle.putString("address", result);
                        message.setData(bundle);
                    } else {
                        message.what = 1;
                        Bundle bundle = new Bundle();
                        result = "Latitude: " + latitude + " Longitude: " + longitude +
                            "
 Unable to get address for
                            this lat-long.";
                        bundle.putString("address" , result);
                        message.setData(bundle);
                    }
                    message.sendToTarget();
                }
            }
        };
        thread.start();
    }
}

ネットワーク処理を伴うため、必ずメインスレッド以外(ワーカースレッド)で実行している点に注目してください。これにより、ANR(アプリケーション応答なし)エラーを回避できます。

ステップ6:AndroidManifest.xmlへの権限追加

最後に、androidManifest.xmlに以下のコードを記述し、位置情報アクセスのためのパーミッションを宣言します。

<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="https://schemas.android.com/apk/res/android"
    package="app.com.sample">
    <uses-permission
        android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission
        android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <application
        android:allowBackup="true"
        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>

ACCESS_COARSE_LOCATIONはおおよその位置情報、ACCESS_FINE_LOCATIONは高精度な位置情報の取得に必要な権限です。両方を宣言しておくことで、GPSおよびネットワークベースの位置取得に対応できます。

アプリの実行と動作確認

それでは、実際にアプリを実行してみましょう。実機のAndroidスマートフォンをPCに接続していることを前提とします。Android Studioからアプリを起動するには、プロジェクト内のアクティビティファイルを開き、ツールバーの「Run」アイコンをクリックします。デバイス選択画面で接続したモバイル端末を選択すると、端末側に以下のような初期画面が表示されます。

Androidで緯度経度から住所を取得する方法!Geocoderを使った逆ジオコーディングの実装手順

「Show Address」ボタンをタップすると、指定した緯度・経度に対応する住所が画面下部のTextViewに表示されます。うまく動作しない場合は、端末の位置情報設定が有効になっているか、Geocoderが端末上で利用可能かどうかを確認してください。

  1. Androidで指定したURLから画像をダウンロードして表示する方法を徹底解説

    AndroidでURLから画像をダウンロードする方法 このチュートリアルでは、Androidアプリで指定したURLから画像をダウンロードし、画面に表示する方法を段階的に解説します。ネットワーク通信はメインスレッドで行うとアプリがフリーズするため、AsyncTaskを使ってバックグラウンドで処理するのがポイントです。 手順1:新しいプロジェクトを作成する Android Studioを起動し、メニューから「File」→「New Project」を選択して、必要な情報を入力して新しいプロジェクトを作成します。 手順2:レイアウトファイル(activity_main.xml)を編集する res/

  2. Androidでアクティビティからフラグメントへ変数を渡す方法を徹底解説

    はじめに この記事では、Androidアプリ開発においてアクティビティ(Activity)からフラグメント(Fragment)へ変数を渡す方法を、実際のコード例とともに段階的に解説します。 アクティビティからフラグメントへのデータ受け渡しには、Bundleを使うのが基本です。アクティビティ側でsetArguments()メソッドによりBundleをフラグメントにセットし、フラグメント側でgetArguments()メソッドを使って値を取り出します。 手順1:新規プロジェクトを作成する Android Studioで新しいプロジェクトを作成します。メニューから「File」→「New Projec