Androidで位置情報を扱うときの3つのTips

位置情報を取得しようとしていろいろ困ったので、まとめておこうと思います。


# LocationManager vs Google Play Service location API, どちらを使う？

Androidには位置情報を取得する窓口として、 [LocationManager](https://developer.android.com/reference/android/location/LocationManager.html) と Google Play Service の [位置情報API](https://developers.google.com/android/reference/com/google/android/gms/location/package-summary) があります。

どちらを使うべきか迷うのですが、 [Androidのドキュメント](https://developer.android.com/training/location/index.html) によると、 Google Play Service の方を使うことが推奨されているようです。

> If you are currently using the Android framework location APIs, you are strongly encouraged to switch to the Google Play services location APIs as soon as possible.

> （抄訳）
> もし今Android fromeworkの位置情報APIを使っているなら、なるべく早くGoogle Play Serviceの位置情報APIに乗り換えることを強く自信を持っておすすめします。

というわけで、使うなら Google Play Service の位置情報APIを使った方が良さそうです。



# 住所とか場所の名前を知りたい

位置情報APIを使えば、端末の緯度経度を知ることができます。
が、緯度経度ではなく、住所を知りたいことだってあるのです。
（住所入力フォームのオートコンプリートとかそういう用途で）

Androidでは [Geocoderクラス](https://developer.android.com/reference/android/location/Geocoder.html) を使うことで意外と簡単に住所を調べる事ができます。
Play ServiceではなくAndroid Frameworkに入っているのが驚き。

ちなみに、施設名や住所から緯度経度を調べることを **ジオコーディング**、逆に緯度経度から住所などを調べることを **リバースジオコーディング** というらしいです。

```java
// ジオコーディングサービスが存在していないと使えないらしい
if (!Geocoder.isPresent()) return;

Geocoder coder = new Geocoder(getContext());

// ネットワークアクセスが発生するので、UIスレッド以外で動かすこと
List<Address> addresses = coder.getFromLocation(
                                    location.getLatitude(), location.getLongitude(), 1);

// リバースジオコーディングに失敗したときは、 addresses がnullもしくは空リストになる

Log.d("location", addresses.get(0).toString()); //Address[addressLines=[0:"日本",1:"〒100-8111 東京都千代田区千代田１ー１"...
```

`Geocoder` の[コンストラクタ第二引数](https://developer.android.com/reference/android/location/Geocoder.html#Geocoder(android.content.Context, java.util.Locale))に好きな Locale を渡すことで、特定の言語や地域に合わせた表記で住所を得ることもできます。

また、この Geocoder クラスで、住所から緯度経度を引くこともできるようです。


## Address クラスの中身（＠日本）

都道府県名だけ欲しい、とかそういうこともあるので、要素を取り出して使うことがほとんどだと思います。

メソッドの全容は[ドキュメント](https://developer.android.com/reference/android/location/Address.html)を見ていただいた方が良いかと思いますが、よく使いそうなメソッドだけまとめてみました。

|メソッド | 説明 |
|:--|:--|
| `getCountryName` | 国名（`日本`とか） |
| `getCountryCode` | 国コード（`JP`とか） |
| `getPostalCode` | 郵便番号に相当（`100-8111`とか） |
| `getAdminArea` | 都道府県に相当（`東京都`や`埼玉県`など） |
| `getSubAdminArea` | 郡に相当するらしい？（渋谷区内の座標で試したらnullだった） |
| `getLocality` | 市区町村に相当（`渋谷区`とか） |
| `getThoroughfare` | 町名に相当（`１丁目`とか。「丁目」がついていないこともある様子。数字は全角だった） |
| `getSubThoroughfare` | 街区符号に相当（`１`とか。「番」「番地」はついていなかった） |
| `getFeatureName` | 住居番号、もしくは建物の名前？（住居番号のときは `１` など。「号」はついていなかった） |
| `getPhone` | 電話番号らしい（多分ランドマークなどだと代表番号が取れる？） |
| `getAddressLine` | そのまま郵便物の宛名に書ける感じの住所表記を取得できる。引数で行数を指定 |
| `getMaxAddressLineIndex` | ↑の表記が何行あるのか返してくれる |


# 端末の位置情報サービスが有効化されているかどうか知りたい

通知バーの辺りからサクッと位置情報の有効/無効を切り替えられます。
これが有効かどうかを知りたいときは、 [LocationManager#isProviderEnabled](https://developer.android.com/reference/android/location/LocationManager.html#isProviderEnabled(java.lang.String)) を調べます。

```java
LocationManager manager = (LocationManager) getContext().getSystemService(Context.LOCATION_SERVICE);

if (manager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
    // Wifiや携帯電話網を使った位置情報推定が使用可能
    // 「設定」から有効にされているなら、とりあえずこの条件が真になる

    // GPSが有効か調べたければ LocationManager.PASSIVE_PROVIDER を引数に入れる
}
```

Play Service の [LocationServices.SettingApi](https://developers.google.com/android/reference/com/google/android/gms/location/LocationServices#SettingsApi) でも調べられるらしいですが、結果は非同期で返ってくるようなので、この点においてはLocationManagerの方が使い勝手が良いかも知れません。


# あわせてよみたい

* [Androidで独自Viewを作るときの4つのTips +2](http://qiita.com/kikuchy/items/0fa6bd232930bb12e187)
* [iOSで独自Viewを作る時の5つのTips](http://qiita.com/kikuchy/items/f1d6731d804b63cf7a29)