goo blog サービス終了のお知らせ 

marunomaruno-memo

marunomaruno-memo

[Android] 位置情報 (1) 無線ネットワークを使った位置情報

2011年12月18日 | Android
位置情報 (1) 無線ネットワークを使った位置情報
================================================================================

■ 無線ネットワークを使った位置情報

無線ネットワークを使った位置情報を取得する。
これも、センサーを使ったものと同じようにプログラミングできる。

基本は、以下の手順による。
1. LocationManager のオブジェクトを取得する
2. LocationManager オブジェクトに位置情報取得のリスナーを登録する
3. 位置情報が変化したら、その値を取得する


◆ アクティビティのクラス。

□ Gps01Activity.java
---
package jp.marunomaruno.android.sample;

import android.app.Activity;
import android.location.Criteria;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

/**
 * 位置情報を取得して表示する。
 * @author marunomaruno
 * @version 1.0, 2011-12-14
 * @since 1.0
 */
public class Gps01Activity extends Activity {
    // TextView
    private TextView sensorValueTextView; // GPS値

    private LocationManager locationManager;                            // (1)
    private LocationListener sensorEventListener; // onPause()が呼ばれたときに、すべてのリスナーを解除するため

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        sensorValueTextView = (TextView) findViewById(R.id.sensor_value);
        locationManager = (LocationManager) getSystemService(LOCATION_SERVICE); // (2)
        Log.d(getString(R.string.logTag), "locationManager = "
                + locationManager);
    }

    @Override
    protected void onResume() {
        super.onResume();

        // プロバイダーを取得する条件を作成する
        Criteria fineAndLowPower = new Criteria();                      // (3)
        fineAndLowPower.setAccuracy(Criteria.ACCURACY_FINE);            // (4)
        fineAndLowPower.setPowerRequirement(Criteria.POWER_LOW);        // (5)
        fineAndLowPower.setAltitudeRequired(true);                      // (6)
        fineAndLowPower.setBearingRequired(true);                       // (7)
        fineAndLowPower.setSpeedRequired(true);                         // (8)

        // プロバイダーを取得する
        String provider = locationManager
                .getBestProvider(fineAndLowPower, true);                // (9)
        Log.d(getString(R.string.logTag), "provider = "
                + provider);

        TextView providerNameTextView = (TextView) findViewById(R.id.provider_name);
        providerNameTextView.setText(provider);
        
        // プロバイダーがみつからなければ、NETWORK_PROVIDER を設定しておく
        if (provider == null) {                                         // (10)
            provider = LocationManager.NETWORK_PROVIDER;
            providerNameTextView.setText(R.string.no_provider);
        }
        
        // 位置情報のリスナーを取得して登録する
        sensorEventListener = new GpsLocationListener(sensorValueTextView); // (11)
        locationManager.requestLocationUpdates(
                provider, 
                0, 
                0, 
                sensorEventListener);                                   // (12)
    }

    @Override
    public void onPause() {
        super.onPause();

        // すべてのリスナーを解除する
        locationManager.removeUpdates(sensorEventListener);             // (13)
    }
}
---

(1)(2) LocationManager とその取得

    private LocationManager locationManager;        // (1)


□ LocationManager

LocationManager は、位置情報を管理するクラス。ここに、位置情報が変更すると起こる
イベントに対するリスナーを登録する。

java.lang.Object
   +     android.location.LocationManager


このクラスのオブジェクトは、
Context.getSystemService(Context.LOCATION_SERVICE)
をとおして取得する。実際は(2)を参照。


Context のサブクラスである Activity クラスのサブクラス中なので、つぎのように取得
できる。

    locationManager = (LocationManager) getSystemService(LOCATION_SERVICE); // (2)


(3)-(8) プロバイダーを取得する基準を作成する

位置情報を取得するためのプロバイダーを選ぶための基準を作成する。この基準に基づい
て、システムは最適と思われるプロバイダーを選ぶ。

    Criteria fineAndLowPower = new Criteria();                      // (3)

基準オブジェクトを作成して、つぎのメソッドを使って基準を設定する。

    fineAndLowPower.setAccuracy(Criteria.ACCURACY_FINE);            // (4)
    fineAndLowPower.setPowerRequirement(Criteria.POWER_LOW);        // (5)
    fineAndLowPower.setAltitudeRequired(true);                      // (6)
    fineAndLowPower.setBearingRequired(true);                       // (7)
    fineAndLowPower.setSpeedRequired(true);                         // (8)


□ Criteria クラス

プロバイダーを選択する上での基準になる。ここに、選択する基準を設定する。

java.lang.Object
   +     android.location.Criteria


□ 定数
---
int     ACCURACY_COARSE    おおよその精度
int     ACCURACY_FINE      細かい精度
int     ACCURACY_HIGH      高い精度
int     ACCURACY_LOW       低い精度
int     ACCURACY_MEDIUM    中間の精度
int     NO_REQUIREMENT     要求しない
int     POWER_HIGH         電池を多く使う
int     POWER_LOW          電池をそれほど使わない
int     POWER_MEDIUM       電池を普通に使う
---

□ コンストラクター
---
    Criteria()
    Criteria(Criteria criteria)
---


□ おもな基準を設定するメソッド
---
void  setAccuracy(int accuracy)                     緯度・経度の精度を設定
void  setBearingAccuracy(int accuracy)              ベアリングの精度を設定
void  setHorizontalAccuracy(int accuracy)           緯度・経度の精度を設定
void  setPowerRequirement(int level)                最大電力レベルを設定
void  setSpeedAccuracy(int accuracy)                速度の精度を設定
void  setVerticalAccuracy(int accuracy)             高度の精度を設定
void  setCostAllowed(boolean costAllowed)   プロバイダがコストを負担するかどうか
void  setAltitudeRequired(boolean altitudeRequired) 標高を出すかどうか
void  setBearingRequired(boolean bearingRequired)   ベアリングを出すかどうか
void  setSpeedRequired(boolean speedRequired)       速度を出すかどうか
---

なお、それぞれの基準を設定するメソッドによって、指定できる基準の定数が決まってい
る。この定数以外だと、IllegalArgumentException 例外がスロー。

--------------------- ------  ------  ------  ------  ------  --------------
                      COARSE   FINE    HIGH     LOW   MEDIUM  NO_REQUIREMENT
--------------------- ------  ------  ------  ------  ------  --------------
setAccuracy             ○      ○
setBearingAccuracy                      ○      ○                  ○
setHorizontalAccuracy                   ○      ○      ○          ○
setSpeedAccuracy                        ○      ○                  ○
setVerticalAccuracy                     ○      ○      ○          ○
--------------------- ------  ------  ------  ------  ------  --------------

★ 今回のサンプルの(6)-(8)のように、標高、ベアリング、速度を要求しても、ネット
ワーク・プロバイダーの場合、できなければ 0 が返るようだ。
GPS プロバイダーの場合、動きが止まるようだ(?)。


(9)(10) プロバイダーを取得する

作成した基準に基づいて、プロバイダーを取得する。

    String provider = locationManager
            .getBestProvider(fineAndLowPower, true);                    // (9)
    if (provider == null) {                                             // (10)
        provider = LocationManager.NETWORK_PROVIDER;
        providerNameTextView.setText(R.string.no_provider);
    }

プロバイダーを取得するのは、基準を指定して、その基準にしたがって、よりよいプロバ
イダーをシステムが選択する。
getBestProvider() メソッドの 2 番目の引数 enabledOnly が true の場合は、使用でき
るものだけが取得されるの。使用できるプロバイダーがなければ、null が返る。


□ メソッド
---
String     getBestProvider(Criteria criteria, boolean enabledOnly)
    基準を満たすプロバイダー名を取得する
---

上記以外にも、プロバイダーを取得したり、確認するメソッドがある。
---
List<String>     getAllProviders()
    すべてのプロバイダー名を取得する

LocationProvider     getProvider(String name)
    指定されたプロバイダー名のプロバイダーのオブジェクトを取得する

List<String>     getProviders(boolean enabledOnly)
    基準を満たす、すべてのプロバイダー名を取得する

List<String>     getProviders(Criteria criteria, boolean enabledOnly)
    基準を満たす、すべてのプロバイダー名を取得する

boolean     isProviderEnabled(String provider)
    指定されたプロバイダー名のプロバイダーが使用できるかどうかを返す
---


(11)(12) 位置情報のリスナーを取得して登録

センサーのときと同じように、リスナーのオブジェクトを取得する。

    sensorEventListener = new GpsLocationListener(sensorValueTextView);  // (11)

位置情報のリスナーは、LocationListener インターフェースを実装しているオブジェク
トで、つぎのメソッドを実装しなければならない。
    abstract void onLocationChanged(Location location)
    abstract void onProviderDisabled(String provider)
    abstract void onProviderEnabled(String provider)
    abstract void onStatusChanged(String provider, int status, Bundle extras)


位置情報リスナーの登録には、LocationManager.requestLocationUpdates() メソッドを
使う。

    locationManager.requestLocationUpdates(
            provider, 
            0, 
            0, 
            sensorEventListener);                                    // (12)

このメソッドは、いくつかオーバーロードされている。

□ requestLocationUpdates のオーバーロードされたメソッド
---
void  requestLocationUpdates(long minTime, float minDistance, 
                    Criteria criteria, PendingIntent intent)
    基準 に基づいた インテント を登録する

void  requestLocationUpdates(long minTime, float minDistance, 
                    Criteria criteria, LocationListener listener, Looper looper)
    基準 に基づいた リスナー を登録する

void  requestLocationUpdates(String provider, long minTime, float minDistance,
                                    LocationListener listener)
    プロバイダーに基づいた リスナー を登録する

void  requestLocationUpdates(String provider, long minTime, float minDistance,
                                    LocationListener listener, Looper looper)
    プロバイダーに基づいた リスナー を登録する

void  requestLocationUpdates(String provider, long minTime, float minDistance,
                                    PendingIntent intent)
    プロバイダーに基づいた インテント を登録する
---

それぞれの引数
---
long             minTime      位置情報を更新する最小の時間間隔(ミリ秒)
float            minDistance  位置情報を更新する最小の距離間隔(メートル)
Criteria         criteria     基準
PendingIntent    intent       位置を更新するためのインテント
LocationListener listener     位置が更新されたときに呼ばれるリスナー
Looper           looper       メッセージループのオブジェクト
String           provider     プロバイダー
---

なお、この引数 minTime や minDistance は、指定したとおりになるかどうかはわからな
い。プロバイダー次第か。


□ プロバイダー

LocationManager クラスのプロバイダー関係の定数
---
String  GPS_PROVIDER         GPS ロケーションを利用したプロバイダー
String  NETWORK_PROVIDER     ネットワークを利用したプロバイダー
String  PASSIVE_PROVIDER     位置情報の修正を行わない特殊なプロバイダー
---


(13) すべてのリスナーを解除する

    locationManager.removeUpdates(sensorEventListener);                // (13)


◆ 位置情報のリスナーのクラス。

位置情報が変わったことに対するリスナーのクラス。LocationListener インターフェー
スを実装する。

位置情報が変わったら、onLocationChanged() が呼ばれる。


□ GpsLocationListener.java
---
package jp.marunomaruno.android.sample;

import android.location.Location;
import android.location.LocationListener;
import android.location.LocationProvider;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

/**
 * GPSによるロケーションのリスナー
 * 
 * @author maruno
 * @version 1.0, 2011-12-14
 * @since 1.0
 */
public class GpsLocationListener implements LocationListener {          // (1)

    protected TextView textView;

    public GpsLocationListener(TextView textView) {
        this.textView = textView;
    }

    @Override
    public void onLocationChanged(Location location) {                  // (2)
        Log.d(textView.getResources().getString(R.string.logTag),
                "onLocationChanged()");
        textView.setText("");

        textView.append(String.format(textView.getResources().getString(
                R.string.sensor_value_format_for_timestamp), 
                textView.getResources().getString(R.string.time), 
                location.getTime()));                                   // (3)
        textView.append("\n");

        textView.append(String.format(textView.getResources().getString(
                R.string.sensor_value_format_for_real), 
                textView.getResources().getString(R.string.latitude), 
                location.getLatitude()));                               // (4)
        textView.append("\n");

        textView.append(String.format(textView.getResources().getString(
                R.string.sensor_value_format_for_real), 
                textView.getResources().getString(R.string.longitude), 
                location.getLongitude()));                              // (5)
        textView.append("\n");

        textView.append(String.format(textView.getResources().getString(
                R.string.sensor_value_format_for_real), 
                textView.getResources().getString(R.string.accuracy), 
                location.getAccuracy()));                               // (6)
        textView.append("\n");

        textView.append(String.format(textView.getResources().getString(
                R.string.sensor_value_format_for_real), 
                textView.getResources().getString(R.string.altitude), 
                location.getAltitude()));                               // (7)
        textView.append("\n");

        textView.append(String.format(textView.getResources().getString(
                R.string.sensor_value_format_for_real), 
                textView.getResources().getString(R.string.speed), 
                location.getSpeed()));                                  // (8)
        textView.append("\n");

        textView.append(String.format(textView.getResources().getString(
                R.string.sensor_value_format_for_real), 
                textView.getResources().getString(R.string.bearing), 
                location.getBearing()));                                // (9)
        textView.append("\n");

    }

    @Override
    public void onProviderDisabled(String provider) {                   // (10)
        Log.d(textView.getResources().getString(R.string.logTag),
                "onProviderDisabled()");
    }

    @Override
    public void onProviderEnabled(String provider) {                    // (11)
        Log.d(textView.getResources().getString(R.string.logTag),
                "onProviderEnabled()");
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {    // (12)
        Log.d("TEST", "onStatusChanged()");
        switch (status) {
        case LocationProvider.AVAILABLE:                                // (13)
            Log.d(textView.getResources().getString(R.string.logTag),
                    "onStatusChanged() - AVAILABLE");
            break;
        case LocationProvider.OUT_OF_SERVICE:                           // (14)
            Log.d(textView.getResources().getString(R.string.logTag),
                    "onStatusChanged() - OUT_OF_SERVICE");
            break;
        case LocationProvider.TEMPORARILY_UNAVAILABLE:                  // (15)
            Log.d(textView.getResources().getString(R.string.logTag),
                    "onStatusChanged() - TEMPORARILY_UNAVAILABLE");
            break;
        default:
            Log.d(textView.getResources().getString(R.string.logTag),
                    "onStatusChanged() - Other");
            break;
        }
    }
}
---

(1) LocationListener インターフェースを実装

位置情報を取得するリスナーは、LocationListener インターフェースを実装する。

    public class GpsLocationListener implements LocationListener {       // (1)


□ LocationListener インターフェース

実装すべきメソッド
---
abstract void     onLocationChanged(Location location)
    場所が変更されたときに呼び出される

abstract void     onProviderDisabled(String provider)
    プロバイダーが無効になったときに呼び出される

abstract void     onProviderEnabled(String provider)
    プロバイダーが有効になったときに呼び出される

abstract void     onStatusChanged(String provider, int status, Bundle extras)
    プロバイダーの状態が変更されたときに呼び出される
---


(2) 場所が変更されたときに呼び出されるハンドラー・メソッド

つぎのメソッドを実装する。

    public void onLocationChanged(Location location) {                    // (2)

引数は Location オブジェクト。

□ Location クラス

位置情報を持つクラス。

java.lang.Object
   +     android.location.Location


定数は、以下の 3 つがある。これは、緯度や経度の形式を決める。

定数
---
int FORMAT_DEGREES  [+-]DDD.DDDDD、 D: 度
int FORMAT_MINUTES  [+-]DDD:MM.MMMMM、D: 度、M: 分、1 度 = 60 分
int FORMAT_SECONDS  [+-]DDD:MM:SS.SSSSS、D: 度、M: 分、S: 秒、1 度 = 60 分、1 分 = 60 秒
---

おもなメソッド
---
static String convert (double coordinate, int outputType) 
    度(coordinate)を指定された緯度や経度の形式(outputType)の文字列にする
float    getAccuracy()   精度
double   getAltitude()   標高
float    getBearing()    ベアリング
Bundle   getExtras()     プロバイダー固有の情報
double   getLatitude()   緯度
double   getLongitude()  経度
String   getProvider()   プロバイダー
float    getSpeed()      速度
long     getTime()       時間
---

convert() メソッドの実行例

Location.convert(123.456789, Location.FORMAT_DEGREES) = "123.45679"
Location.convert(123.456789, Location.FORMAT_MINUTES) = "123:27.40734"
Location.convert(123.456789, Location.FORMAT_SECONDS) = "123.27:24.4404"


(3)-(9)

次のメソッドで、緯度や経度を取得している。

    location.getTime()));                                   // (3)
    location.getLatitude()));                               // (4)
    location.getLongitude()));                              // (5)
    location.getAccuracy()));                               // (6)
    location.getAltitude()));                               // (7)
    location.getSpeed()));                                  // (8)
    location.getBearing()));                                // (9)


(10) プロバイダーが無効になったときに呼び出されるハンドラー・メソッド

    public void onProviderDisabled(String provider) {                    // (10)


(11) プロバイダーが有効になったときに呼び出されるハンドラー・メソッド

    public void onProviderEnabled(String provider) {                    // (11)


(12) プロバイダーの状態が変更されたときに呼び出されるハンドラー・メソッド

    public void onStatusChanged(String provider, int status, Bundle extras) {    // (12)

今回のように、ひとつのプロバイダーを登録しているだけだと動かないようだ。


(13)-(15) 状態の変化

LocationProvider の定数によって、どいう状態かを判断する。

    case LocationProvider.AVAILABLE:                                // (13)
    case LocationProvider.OUT_OF_SERVICE:                           // (14)
    case LocationProvider.TEMPORARILY_UNAVAILABLE:                  // (15)

LocationProvider の定数
---
int     AVAILABLE     
int     OUT_OF_SERVICE     
int     TEMPORARILY_UNAVAILABLE 
---


◆ レイアウト

□ res/layout/main.xml
---
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <!-- プロバイダー名 -->
    <TextView
        android:id="@+id/provider_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />

    <!-- センサー値 -->
    <TextView
        android:id="@+id/sensor_value"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />

</LinearLayout>
---


◆ 文字列の定数

□ res/values/string.xml
---
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">Gps01</string>
    <string name="logTag">TEST</string>
    <string name="no_provider">プロバイダーは見つかりませんでした。</string>
    <string name="gpsUnit">m/s^2</string>
    <string name="sensor_value_format_for_real">%1$s = %2.2f</string>
    <string name="sensor_value_format_for_timestamp">%1$s = %2$tF %2$tT</string>
    <string name="latitude">緯度</string>
    <string name="longitude">経度</string>
    <string name="accuracy">精度</string>
    <string name="altitude">標高</string>
    <string name="time">時間</string>
    <string name="speed">速度</string>
    <string name="bearing">ベアリング</string>
</resources>
---


◆マニフェスト

ユーザーパーミッションをつける。

□AndroidManifest.xml
---
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="jp.marunomaruno.android.sample"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="8" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>    <!-- (1) -->
    <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>    <!-- (2) -->
    <uses-permission android:name="android.permission.INTERNET"/>    <!-- (3) -->

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:label="@string/app_name"
            android:name=".Gps01Activity" >
            <intent-filter >
                <action android:name="android.intent.action.MAIN" />

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

</manifest>
---

(1)-(3)

パーミッションは以下のものを使う

ACCESS_FINE_LOCATION    GPS を使う
ACCESS_MOCK_LOCATION    エミュレーターで GPS をエミュレートする
INTERNET                無線を使う

これを、AndroidManifest.xml に記述する。



最新の画像もっと見る

コメントを投稿