Core Location實現系統內建定位問題,corelocation

來源:互聯網
上載者:User

Core Location實現系統內建定位問題,corelocation

      Core Location是iOS SDK中一個提供裝置位置的架構。可以使用三種技術來擷取位置:GPS、蜂窩或WiFi。在這些技術中,GPS最為精準,如果有GPS硬體,Core Location將優先使用它。如果裝置沒有GPS硬體(如WiFi iPad)或使用GPS擷取當前位置時失敗,Core Location將退而求其次,選擇使用蜂窩或WiFi。

      Core Location的大多數功能是由位置管理器(CLLocationManager)提供的,可以使用位置管理器來指定位置更新的頻率和精度,以及開始和停止接收這些更新。

要使用位置管理器,必須首先將架構Core Location加入到項目中,再匯入其介面檔案:

#import <CoreLocation/CoreLocation.h>

並初始化位置管理器,指定更新代理,以及一些更新設定,然後更新

CLLocationManager *locManager = [[CLLocationManager alloc] init];locManager.delegate = self;[locManager startUpdatingLocation];

 位置管理器委託(CLLocationManagerDelegate)有兩個與位置相關的方法:

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
 CLLocation *curLocation = [locations lastObject];        if(curLocation.horizontalAccuracy > 0)    {        NSLog(@"當前位置:%.0f,%.0f +/- %.0f meters",curLocation.coordinate.longitude,              curLocation.coordinate.latitude,              curLocation.horizontalAccuracy);    }        if(curLocation.verticalAccuracy > 0)    {        NSLog(@"當前海拔高度:%.0f +/- %.0f meters",curLocation.altitude,curLocation.verticalAccuracy);    }
} - (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error { //此方法為定位失敗的時候調用。並且由於會在失敗以後重新置放,所以必須在末尾停止更新
  if(error.code == kCLErrorLocationUnknown)    {        NSLog(@"Currently unable to retrieve location.");    }    else if(error.code == kCLErrorNetwork)    {        NSLog(@"Network used to retrieve location is unavailable.");    }    else if(error.code == kCLErrorDenied)    {        NSLog(@"Permission to retrieve location is denied.");        [manager stopUpdatingLocation];    }
}

 

第一個方法處理定位成功,manager參數表示位置管理器執行個體;locations為一個數組,是位置變化的集合,它按照時間變化的順序存放。如果想獲得裝置的當前位置,只需要訪問數組的最後一個元素即可。集合中每個物件類型是CLLocation,它包含以下屬性:

coordinate — 座標。一個封裝了經度和緯度的結構體。

altitude — 海拔高度。正數表示在海平面之上,而負數表示在海平面之下。

horizontalAccuracy — 位置的精度(半徑)。位置精度通過一個圓表示,實際位置可能位於這個圓內的任何地方。這個圓是由coordinate(座標)和horizontalAccuracy(半徑)共同決定的,horizontalAccuracy的值越大,那麼定義的圓就越大,因此位置精度就越低。如果horizontalAccuracy的值為負,則表明coordinate的值無效。

verticalAccuracy — 海拔高度的精度。為正值表示海拔高度的誤差為對應的米數;為負表示altitude(海拔高度)的值無效。

speed — 速度。該屬性是通過比較當前位置和前一個位置,並比較它們之間的時間差異和距離計算得到的。鑒於Core Location更新的頻率,speed屬性的值不是非常精確,除非移動速度變化很小。

 

應用程式開始跟蹤使用者的位置時,將在螢幕上顯示一個是否允許定位的提示框。如果使用者禁用定位服務,iOS不會禁止應用程式運行,但位置管理器將建置錯誤。

第二個方法處理這種定位失敗,該方法的參數指出了失敗的原因。如果使用者禁止應用程式定位,error參數將為kCLErrorDenied;如果Core Location經過努力後無法確認位置,error參數將為kCLErrorLocationUnknown;如果沒有可供擷取位置的源,error參數將為kCLErrorNetwork。

通常,Core Location將在發生錯誤後繼續嘗試確定位置,但如果是使用者禁止定位,它就不會這樣做;在這種情況下,應使用方法stopUpdatingLocation停止位置管理器。

 

可根據實際情況來指定位置精度。例如,對於只需確定使用者在哪個國家的應用程式,沒有必要要求Core Location的精度為10米。要指定精度,可在啟動位置更新前設定位置管理器的desiredAccuracy。有6個表示不同精度的枚舉值:

extern const CLLocationAccuracy kCLLocationAccuracyBestForNavigation;extern const CLLocationAccuracy kCLLocationAccuracyBest;extern const CLLocationAccuracy kCLLocationAccuracyNearestTenMeters;extern const CLLocationAccuracy kCLLocationAccuracyHundredMeters;extern const CLLocationAccuracy kCLLocationAccuracyKilometer;extern const CLLocationAccuracy kCLLocationAccuracyThreeKilometers;

 對位置管理器啟動更新後,更新將不斷傳遞給位置管理器委託,直到停止更新。您無法直接控制這些更新的頻率,但可使用位置管理器的屬性distanceFilter進行間接控制。在啟動更新前設定屬性distanceFilter,它指定裝置(水平或垂直)移動多少米後才將另一個更新發送給委託。下面的代碼使用適合跟蹤長途跋涉者的設定啟動位置管理器:

CLLocationManager *locManager = [[CLLocationManager alloc] init];locManager.delegate = self;locManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;//定位精度百米以內locManager.distanceFilter = 200;//水平或者垂直移動200米調用代理程式更新位置[locManager startUpdatingLocation];//啟動位置更新

P.s. 定位要求的精度越高、屬性distanceFilter的值越小,應用程式的耗電量就越大。

位置管理器有一個headingAvailable屬性,它指出裝置是否裝備了磁性指南針。如果該屬性為YES,就可以使用Core Location來擷取航向(heading)資訊。接收航向更新與接收位置更新極其相似,要開始接收航向更新,可指定位置管理器委託,設定屬性headingFilter以指定要以什麼樣的頻率(以航向變化的度數度量)接收更新,並對位置管理器調用方法startUpdatingHeading:

位置管理器委託協議定義了用於接收航向更新的方法。該協議有兩個與航向相關的方法:

- (BOOL)locationManagerShouldDisplayHeadingCalibration:(CLLocationManager *)manager{    return YES;}- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading{    }

第一個方法指定位置管理器是否向使用者顯示校準提示。該提示將自動旋轉裝置360°。由於指南針總是自我校準,因此這種提示僅在指南針讀數劇烈波動時才有協助。當設定為YES後,提示可能會分散使用者的注意力,或影響使用者的當前操作。

第二個方法的參數newHeading是一個CLHeading對象。CLHeading通過一組屬性來提供航向讀數:magneticHeading和trueHeading。這些值的單位為度,類型為CLLocationDirection,即雙精確度浮點數。這意味著:

如果航向為0.0,則前進方向為北;

如果航向為90.0,則前進方向為東;

如果航向為180.0,則前進方向為南;

如果航向為270.0,則前進方向為西。

CLHeading對象還包含屬性headingAccuracy(精度)、timestamp(讀數的測量時間)和description(這種描述更適合寫入日誌而不是顯示給使用者)。下面示範了利用這個方法處理航向更新:

- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading{    if(newHeading.headingAccuracy >=0)    {        NSString *headingDesc = [NSString stringWithFormat:@"%.0f degrees (true), %.0f degrees (magnetic)",newHeading.trueHeading,newHeading.magneticHeading];                NSLog(@"%@",headingDesc);    }}

 

trueHeading和magneticHeading分別表示真實航向和磁性航向。如果位置服務被關閉了,GPS和wifi就只能擷取magneticHeading(磁場航向)。只有開啟位置服務,才能擷取trueHeading(真實航向)。

下面的代碼示範了,當存在一個確定了經緯度的地點,當前位置離這個地點的距離及正確航向:

#import "ViewController.h"#define kDestLongitude 113.12 //精度#define kDestLatitude 22.23 //緯度#define kRad2Deg 57.2957795 // 180/π#define kDeg2Rad 0.0174532925 // π/180@interface ViewController ()@property (strong, nonatomic) IBOutlet UILabel *lblMessage;@property (strong, nonatomic) IBOutlet UIImageView *imgView;@property (strong, nonatomic) CLLocationManager *locationManager;@property (strong, nonatomic) CLLocation *recentLocation;-(double)headingToLocation:(CLLocationCoordinate2D)desired current:(CLLocationCoordinate2D)current;@end@implementation ViewController- (void)viewDidLoad{    [super viewDidLoad];    self.locationManager = [[CLLocationManager alloc] init];    self.locationManager.delegate = self;    self.locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;    self.locationManager.distanceFilter = 1609; //1英裡≈1609米    [self.locationManager startUpdatingLocation];        if([CLLocationManager headingAvailable])    {        self.locationManager.headingFilter = 10; //10°        [self.locationManager startUpdatingHeading];    }}/* * According to Movable Type Scripts * http://mathforum.org/library/drmath/view/55417.html * *  Javascript: * * var y = Math.sin(dLon) * Math.cos(lat2); * var x = Math.cos(lat1)*Math.sin(lat2) - * Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon); * var brng = Math.atan2(y, x).toDeg(); */-(double)headingToLocation:(CLLocationCoordinate2D)desired current:(CLLocationCoordinate2D)current{    double lat1 = current.latitude*kDeg2Rad;    double lat2 = desired.latitude*kDeg2Rad;    double lon1 = current.longitude;    double lon2 = desired.longitude;    double dlon = (lon2-lon1)*kDeg2Rad;        double y = sin(dlon)*cos(lat2);    double x = cos(lat1)*sin(lat2) - sin(lat1)*cos(lat2)*cos(dlon);        double heading=atan2(y,x);    heading=heading*kRad2Deg;    heading=heading+360.0;    heading=fmod(heading,360.0);    return heading;}//處理航向 - (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading{    if(self.recentLocation!=nil && newHeading.headingAccuracy>=0)    {        CLLocation *destLocation = [[CLLocation alloc] initWithLatitude:kDestLatitude longitude:kDestLongitude];                double course = [self headingToLocation:destLocation.coordinate current:self.recentLocation.coordinate];                double delta = newHeading.trueHeading - course;                if (abs(delta) <= 10)        {            self.imgView.image = [UIImage imageNamed:@"up_arrow.png"];        }        else        {            if (delta > 180)            {                self.imgView.image = [UIImage imageNamed:@"right_arrow.png"];            }            else if (delta > 0)            {                self.imgView.image = [UIImage imageNamed:@"left_arrow.png"];            }            else if (delta > -180)            {                self.imgView.image = [UIImage imageNamed:@"right_arrow.png"];            }            else            {                self.imgView.image = [UIImage imageNamed:@"left_arrow.png"];            }        }        self.imgView.hidden = NO;    }    else    {        self.imgView.hidden = YES;    }}//處理定位成功- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{    CLLocation *curLocation = [locations lastObject];        if(curLocation.horizontalAccuracy >= 0)    {        self.recentLocation = curLocation;                CLLocation *destLocation = [[CLLocation alloc] initWithLatitude:kDestLatitude longitude:kDestLongitude];                CLLocationDistance distance = [destLocation distanceFromLocation:curLocation];                if(distance<500)        {            [self.locationManager stopUpdatingLocation];            [self.locationManager stopUpdatingHeading];            self.lblMessage.text = @"您已經到達目的地!";        }        else        {            self.lblMessage.text = [NSString stringWithFormat:@"距離目的地還有%f米",distance];        }    }}//處理定位失敗- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error{    if(error.code == kCLErrorLocationUnknown)    {        NSLog(@"Currently unable to retrieve location.");    }    else if(error.code == kCLErrorNetwork)    {        NSLog(@"Network used to retrieve location is unavailable.");    }    else if(error.code == kCLErrorDenied)    {        NSLog(@"Permission to retrieve location is denied.");        [self.locationManager stopUpdatingLocation];        self.locationManager = nil;    }}- (void)didReceiveMemoryWarning{    [super didReceiveMemoryWarning];    // Dispose of any resources that can be recreated.}@end

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.