GPSから精度の高いデータを取得する方法/コード

iPhoneGPSを使い位置情報を取得する方法/コードはネット上にたくさん出ています。またAppleiPhone Dev Center にある LocateMe サンプルはとても有用です。しかし、これらの情報/コードを元にGPSで位置情報を取得してみると全然違う位置情報が取得されてしまったりします。







私も色々と試しながら、なんとか満足な位置情報を取得できる方法/コードをみつける事が出来ました。

取得方法ですが、以下のようにしました。

  • GPS取得を開始し一定時間計測を行い、その中で一番 精度(newLocation.horizontalAccuracy)の高い値を使う
  • 以下のようなデータは無視する
    • 取得時間(newLocation.timestamp)が古いもの。これはLocateMeサンプルに書かれています。
    • 精度の悪い(newLocation.horizontalAccuracy が一定以上大きい)データ

iPhoneGPSは衛星から情報以外にWiFi基地局の位置情報、携帯電話基地局の位置情報などを使い、高い精度の値を短い時間で取得しようとします。したがって、かなり精度の低い情報が最初に取得されてしまう事もあります。都内を電車で移動しながら測定してみたところ 誤差 8km などという精度の低い情報が取得されてしまう事もありました。

コード

完全なコードではありませんが、主要な部分を書きました。

SampleViewController.h

#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
@interface SampleViewController : UIViewController <CLLocationManagerDelegate> {
        IBOutlet UILabel *statusLable;            // ステータス表示用 label
        IBOutlet UIActivityIndicatorView *activityIndicator;    // GPS取得中にクルクルするindicator
        CLLocationManager *locationManager;
        CLLocation *bestEffortAtLocation;
        ....
}
@end

SampleViewController.m

#import "SampleViewController.h"

#define GPS_MEASURING_TIME 3.0
#define GPS_DESIRED_ACCURACY 100.0
#define GPS_UNADOPTABLE_ACCURACY 2000.0


// GPS測定開始処理。ここではView表示時に測定開始
- (void)viewWillAppear:(BOOL)animated {
    locationManager = [[CLLocationManager alloc] init];
    locationManager.delegate = self;
    bestEffortAtLocation = nil;
    locationManager.desiredAccuracy = GPS_DESIRED_ACCURACY;
    // GPS測定開始
    [locationManager startUpdatingLocation];
    // GPS_MEASURING_TIME 秒後に 測定終了(stopUpdatingLocation:)メソッドを実行
    [self performSelector:@selector(stopUpdatingLocation:)
                     withObject:nil
                     afterDelay:GPS_MEASURING_TIME];
     NSLog(@"locationManager startUpdatingLocation");

    statusLable.text = @"GPS 取得中...";
    [activityIndicator startAnimating];
    [super viewWillAppear:animated];
}

// GPS情報の処理。OSからGPS情報取得時に呼び出される
- (void)locationManager:(CLLocationManager *)manager 
                 didUpdateToLocation:(CLLocation *)newLocation  
                 fromLocation:(CLLocation *)oldLocation
{
    // newLocation.descriptionで位置情報の全てが文字列で取得できる
    NSLog(@"newLoc: %@", newLocation.description);

    // 不要なデータは無視
    if (-[newLocation.timestamp timeIntervalSinceNow] > 5.0) return;
    if (newLocation.horizontalAccuracy > GPS_UNADOPTABLE_ACCURACY) return;

    // 一番精度の高い情報を記憶
    if (bestEffortAtLocation == nil ||
         newLocation.horizontalAccuracy < bestEffortAtLocation.horizontalAccuracy) {
         [bestEffortAtLocation autorelease];
         bestEffortAtLocation = [newLocation retain];
     }
}

// GPSエラー処理
- (void)locationManager:(CLLocationManager *)manager  didFailWithError:(NSError *)error
{
    [activityIndicator stopAnimating];
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"GPS Error"
                                message:@"Error" 
                                delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
    [alert show];
    [alert release];
}

// GPS測定終了処理
- (void)stopUpdatingLocation:(NSObject *)args {
    [locationManager stopUpdatingLocation];
    [activityIndicator stopAnimating];

    if (bestEffortAtLocation == nil) {
         statusLabel.text = @"GPS 取得失敗";
         return;
    }
    statusLabel.text = @"GPS 取得完了";


    float longitude = bestEffortAtLocation.coordinate.longitude;
    float latitude = bestEffortAtLocation.coordinate.latitude;

    // 位置情報を使った処理
        ....
        ....
}