欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

IOS CoreLocation实现系统自带定位的方法

程序员文章站 2023-12-20 21:06:40
core location是ios sdk中一个提供设备位置的框架。可以使用三种技术来获取位置:gps、蜂窝或wifi。在这些技术中,gps最为精准,如果有gps硬件,co...

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

上一篇:

下一篇: