iOS开发-定位服务和地理围栏

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS开发-定位服务和地理围栏相关的知识,希望对你有一定的参考价值。

一、获得用户的位置
使用CoreLocation框架。
1、如果你的应用基于位置来正确运行,你应该在你的Info.plist中包括UIRequiredDeviceCapabilities键.App Store使用这个信息来阻止没有定位的设备下载该应用。
UIRequiredDeviceCapabilities键对应一个字符串数组,包括:
1)location-services字符串--如果你请求一般的定位服务
2)gps--如果你请求GPS级的精度
如果你的应用需要定位,但是没有定位也能正常运行,就不要包含这个键。

2、获得用户的定位
有2种方法:
1)标准的定位服务
2)显著的位置变化定位服务:只在4.0以后有效。


3、确定定位服务是否可用:
1)用户可以在设置程序中禁用定位服务
2)用户可以对某个应用拒绝定位服务
3)设备可能在飞行模式,并且不能启用需要的硬件。
因为这些原因,推荐你调用[CLLocationManager locationServicesEnabled]来确定定位服务是否可用。
使用[CLLocationManager authorizationStatus]==kCLAuthorizationStatusDenied 检查定位服务是否对本app禁止。 
    

4,启动标准的定位服务:
    CLLocationManager *locationManager = [[CLLocationManager alloc] init];
    locationManager.delegate = self;
    locationManager.desiredAccuracy = kCLLocationAccuracyKilometer;
    // Set a movement threshold for new events.
    locationManager.distanceFilter = 500;
    [locationManager startUpdatingLocation];


5、启动Significant-Change Location Service:
 [locationManager startMonitoringSignificantLocationChanges];
启动此服务后,即使应用没有被启动也可以接收通知,并且进入到后台状态来处理事件。但是不能让程序的后台任务超过10分钟

6, 从服务中接收位置数据
locationManager:didUpdateToLocation:fromLocation
locationManager:didFailWithError:


7、监视Shape-Based Regions:
在iOS4.0以后,当用户穿过地理边界时应用可以使用region monitoring来被通知。这个通知即使应用没有被启动也可以接收,并且进入到后台状态来处理事件。(推断:应该和startMonitoringSignificantLocationChanges一样,不能让程序的后台任务超过10分钟)

8、确定Region Monitoring的可用性
有几种原因可能导致Region Monitoring可能不可用:
1)设备可能没有硬件来支持region Monitoring
2)用户可能禁用了定位服务
3)设备可能出于飞行模式。
因此,你需要调用[CLLocationManager regionMonitoringAvailable]和[CLLocationManager regionMonitoringEnabled]来确定其是否可用。

9、定义一个要监视的Region:
要监视一个区域,你需要定义个region并且注册其到系统。Regions使用CLRegion类来定义,支持创建一个圆形区域。你创建的Region必须包括地理区域的数据和唯一的标志字符串。要注册一个Region,你需要调用CLLocationManager对象的startMonitoringForRegion:desiredAccuracy:方法。
例子:
  // 如果不支持RegionMonitoring,就不要创建Region了
   if ( ![CLLocationManager regionMonitoringAvailable] ||
        ![CLLocationManager regionMonitoringEnabled] )
      return NO;

   // 如果半径太大,注册会自动失败,因此当半径太大时,将其设置为最大值
   CLLocationDegrees radius = overlay.radius;
   if (radius > self.locManager.maximumRegionMonitoringDistance)
      radius = self.locManager.maximumRegionMonitoringDistance;
 
   // 创建一个Region并开始监视它
   CLRegion* region = [[CLRegion alloc] initCircularRegionWithCenter:overlay.coordinate radius:radius identifier:identifier];
   [self.locManager startMonitoringForRegion:region desiredAccuracy:kCLLocationAccuracyHundredMeters];
 
   [region release];

...
在注册之后立即就开始了监视Region,但是不要奢望马上就收到一个事件。只有穿过边界时才会产生事件。因此,如果在注册时,用户的位置已经在区域内,locationManager不会产生事件的。
你必须明智的使用Regions Monitoring。Regions是一个系统分享资源,并且其数量是有上限的。因此,Core Location限制一个应用同时监听的Regions数量。你应该考虑只监听用户临近位置的Region,当用户的位置变化时,你可以移除远处的Regions并添加近处的Regions来监听。如果你尝试注册一个Region并且没有空间了,LocationManager会调用locationManager:monitoringDidFailForRegion:withError:方法,并传递一个kCLErrorRegionMonitoringFailure错误码。

10、处理Region的穿过边界事件
locationManager:didEnterRegion:
locationManager:didExitRegion:


11、在后台获得定位事件
有几种方法:
1)使用Signicant Location Change服务: [locationManager startMonitoringSignificantLocationChanges];
2)使用标准的定位服务: 在Info.plist中的UIBackgroundMode键中指定location


12、保护电池电量的技巧
1)在你不需要定位的时候关闭定位服务:
2)使用significant-change 定位服务
3)使用低精度的desired accuracy
4)如果是周期性的轮询服务,那在周期中间关闭定位服务

二、获得方向相关的事件
Core Location支持两种方法来获得方向相关的信息:
1)有磁力计(magnetometer)的设备可以报告设备指向的方向,也称为heading。
2)有GPS的设备可以报告设备移动的方向,也称为course。

记住,heading和course不表示相同的信息。

1、如果你的应用需要方向相关的信息才能正确运行,需要在Info.plist中指定UIRequiredDeviceCapabilities键,其值为一个字符串的数组,包括:
1)magnetometer---对应heading
2)gps--对应course

2、获得Heading-Related事件:步骤:
1)实例化CLLocationManager对象
2)确定[CLLocationManager headingAvailable]
3)指定代理
4)如果你想获得真实的north values,需要启用标准服务
5)调用startUpdatingHeading开始投递heading事件

如下例:
    CLLocationManager* locManager = [[[CLLocationManager alloc] init] autorelease];
    locManager.delegate = self;

   // Start location services to get the true heading.
   locManager.distanceFilter = 1000;
   locManager.desiredAccuracy = kCLLocationAccuracyKilometer;
   [locManager startUpdatingLocation];
 
   // Start heading updates.
   if ([CLLocationManager headingAvailable]) {
      locManager.headingFilter = 5;
      [locManager startUpdatingHeading];
   }


代理函数:locationManager:didUpdateHeading:方法。
一旦你收到一个新的事件,你需要检查headingAccuracy属性来确保数据有效。另外,如果你正在使用true heading value,你应该在使用它之前,检查其是否包含一个有效的值。
- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading {
   if (newHeading.headingAccuracy < 0)
      return;

   // Use the true heading if it is valid.
   CLLocationDirection  theHeading = ((newHeading.trueHeading > 0) ? newHeading.trueHeading : newHeading.magneticHeading);
   self.currentHeading = theHeading;
   [self updateHeadingDisplays];

}

3、在用户移动时获得course信息
拥有GPS硬件的设备可以生成设备的当前course和速度的信息。crouse信息用来指示设备正在移动的方向,并且不需要反应设备自身的方向。因此,主要用来导航。
实际的course和speed消息在CLLocation对象中返回. 
newLocation.speed
newLocation.course


三、地理编码位置数据 Geocoding Location Data:
1、关于Geocoder对象:
一个geocoder对象使用网络服务来在(经度、纬度)和地标之间转换,地标为一组数据的集合,例如街道、城市、州和国家信息。Reverse geocoding(逆地理编码)是将(经度、纬度)转换为地标。Forward geocoding(正地理编码)是将位置名称信息转换为(经度、纬度)值。逆地理编码在所有的ios版本中都可用,但正地理编码仅在iOS5.0以后支持。
因为地理编码服务需要网络,因此,如果设备出于飞行模式或者设备当前没有网络,地理编码对象不能连接它需要的服务,因此肯定返回一个错误。

2、转换坐标到Place Name信息:
在iOS中,你可以使用CLGeocoder类来处理逆地理编码。MKReverseGecoder在iOS5.0以后被弃用。

3、使用CLGeocoder获得地标信息
创建一个CLGeocoder类的实例,并调用
[gecoder reverseGeocodeLocation:completionHandler:]方法
例子:

CLGeocoder *gecoder=[[CLGeocoder alloc]init];
[geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks,NSError *error){
   if ([placemarks count]>0){
    annotation.placemark=[placemark objectAtIndex:0];
    ...
  }
}];

...

4、从Reverse Geocoder中获得地标信息:
MKReverseGeocoder *theGeocoder=[[MKReverseGeocoder alloc]initWithCoordinate:location.coordinate];
theGeocoder.delegate=self;
[theGeocoder start];

代理函数为:
-(void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFindPlaceMark:(MKPlaceMark *)place
-(void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFailWithError:(NSError *)error

5、转换Place Names为Coordinate
[geocoder gecodeAddressString:@"1 Infinite Loop"  completionHandler:^(NSArray *placeMarks,NSError *error){
   for (CLPlacemark *aPlaceMark in placemarks){
     //CLPlacemark对象包含地理位置信息,当然包括经纬度
  }
}];


四、显示地图
Map Kit框架
1、理解Map Geometry
一个map view扁平的表现一个球形对象--地球。你需要了解一些关于如何在map view中指定点,并且这些点如何转换为地球表面的坐标。如果你打算防止自定义的内容,如overlays,到地图上,理解地图坐标系统是非常重要的。

2、地图坐标系统
地球的三维坐标如何被映射到地图的二维坐标上的?
Map Kit使用Mercator地图映射。
你如何指定地图上的数据点取决于你打算如何使用它们。Map Kit支持3中基本的坐标系统来指定地图数据点:
1)A map coordinate:是地图的球形表示的(经度,纬度)。地图坐标系主要的方法来指定地球上的位置。使用CLLocationCoordinate2D结构。使用MKCoordinateSpan和MKCoordinateRegion结构来指定区域。
2)A map point:是Mercator地图映射之后的(x,y)点。使用地图点来计算地图相关的计算,而不是map坐标,因为他们简化了计算。在应用中,你主要使用map point来指定shape和自定义的overlay的position。使用MKMapPoint结构。使用MKMapSize和MKMapRect结构来指定区域。
3)A Point是UIView坐标系中的graphical单元。在绘制view的内容之前,Map Piont和Map Coordinate必须被映射为Point。使用CGPoint,CGSize和CGRect。
在大多数情况中,你应该使用的坐标系取决于你要使用的Map Kit界面。当你要存储实际的数据到文件中时,map coordinate是首选。
Core Location也是用map coordinate来指定位置值。

3、坐标系统间的转换:
Convert From        Convert To             Conversion routines
Map Coordinate    Points                     convertCoordinate:toPointToView:(MKMapView)
                                                             convertRegion:toRectToView:(MKMapView)
Map Coordinate    Map Points             MKMapPointForCoordinate
Map Points           Map Coordinates    MKCoordinateForMapPoint
                                                            MKCoordinateRegionForMapRect
Map Points           Points                     PointForMapPoint:(MKOverlayView)
                                                            rectForMapRect:(MKOverlayView)
Points                  Map Coordinates     convertPoint:toCoordinateFromView:(MKMapView)
                                                            convertRect:toRegionFromView:(MKMapView)
Points                  Map Points              mapPointForPoint:(MKOverlayView)
                                                            mapRectForRect:(MKOverlayView)


4、添加一个Map View到用户界面
MKMapView
5、配置Map的属性
1)设置Map的可见部分:region属性,是一个MKCoordinateRegion结构
typedef struct {
 CLLocationCoordinate2D center;
 MKCoordinateSpan span;
} MKCoordinateRegion;
有趣的是span。span类似于一个矩形的宽度和高度值,但是通过map coordinate指定,因此,通过degree(度)、minutes(分)、seconds(秒)来度量。latitude(纬度)的一度大约为111km,但是longitude(经度)的一度不同与纬度。在赤道,经度的一度大约为111km,但是在极点,这个值为0.如果你选择使用米来指定span,你可以使用MKCoordinateRegionMakeWithDistance来创建一个region data 结构,用米而不是用度。
你指定的region属性的值(或者使用setRegion:animated:方法)通常和实际存储在属性中的值不一样。设置region的span只是定义了你想要查看的矩形,但也隐式设置了map view的缩放级别。map view不能显示任意的缩放级别,并且必须调整你设置的region来匹配它支持的缩放级别。它选择能容纳你的整个region可见,同时又尽可能填满屏幕的缩放级别。相应地,它然后调整region属性。要查找region的结果而不实际更改region的属性,你可以使用map view的regionThatFits:方法。

2)缩放和Panning 地图内容:
a)Pan 地图:更改centerCoordinate属性的值 或者 setCenterCoordinate:animated:方法
b)更改缩放级别:更改region属性的值或setRegion:animated:方法

如果只是要Pan地图,需要使用第一种方法,如果你更改region属性的center属性,通常会导致缩放级别发生变化。

CLLocationCoordinate2D mapCenter = myMapView.centerCoordinate;
mapCenter = [myMapView convertPoint:CGPointMake(1, (myMapView.frame.size.height/2.0)) toCoordinateFromView:myMapView];
[myMapView setCenterCoordinate:mapCenter animated:YES];


要缩放地图,修改region的span,要zoom in,就设置一个更小的值给span,要zoom out,就设置一个更大的值给span。
MKCoordinateRegion theRegion = myMapView.region;
// Zoom out
theRegion.span.longitudeDelta *= 2.0;
theRegion.span.latitudeDelta *= 2.0;
[myMapView setRegion:theRegion animated:YES];


3)在地图上显示用户的当前位置
设置showsUserLocation属性为YES
。这样做导致map view使用Core Location来查找用户的位置并添加一个MKUserLocation类型的annotation到地图上。

6、响应用户与地图的交互
MKMapView报告显著的map-related事件给其代理。代理遵循MKMapViewDelegate协议。可以响应如下事件:
1)更改地图的可见region
2)从网络上加载map 碎片
3)更改用户的位置
4)更改相关的annotation和overlay

五、Annotating Maps
1、添加Annotations到地图上:
要实现这个必须提供两个对象:
1)遵守MKAnnotation协议的对象(即annotation对象)
2)一个继承自MKAnnotationView类的视图,用来绘制annoatation的表现(及annotation view)

Map Kit提供了一些标准的annotation视图,并且你可以定义自己的annotation views。

2、添加Annotation到地图上的清单:
1)定义一个合适的annotation对象,使用下面的选项:
  a)使用MKPointAnnotation类来实现一个简单的annotation。
  b)定义一个自定义的遵循MKAnnotation协议的对象
2)定义一个annotation view来表示屏幕上的数据。
  a)如果annotation可以被一个静态图像代表,那就创建一个MKAnnotationView类的实例,并指定image属性
  b)如果你想使用一个标准的annotation,那就创建一个MKPinAnnotationView类的实例
  c)如果静态图像不合适,那就子类化MKAnnotationView,并实现自定义的draw代码。
3)实现mapView:viewForAnnotation:方法---在你的mapView代理中。并返回一个annotationView。
4)使用addAnnotation:或addAnnotations:方法来添加addnotation对象。


3、定义自定义的Annotation对象
主要是遵守MKAnnotation协议:
必须有coordinate只读属性

4、使用标准的Annotation Views:
MKAnnotationView 和 MKPinAnnotationView(MKAnnotationView的子类)
MKAnnotationView适用于只展示一幅图像。通过image属性,还可以指定centerOffset属性来移动中心点。

5、定义自己的Annotation View
子类化MKAnnotationView,你可以继续使用image属性,并重载drawRect:方法。

6、在代理中创建Annotation Views
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id)annotation{
}

7、管理地图的Annotation对象:
可以在mapView:regionWillChangeAnimated:和mapView:regionDidChangeAnimated:方法中添加和移除annotations
在iOS4.0以后,你可以使用MKMetersBetweenMapPoints方法来获得两个点之间的绝对距离。你还可以使用MKMapRectIntersetsRect函数来查找任何的交集。

8、标记你的AnnotationView为可拖动:
1)在你的annotation对象中,实现setCoordinate:方法来允许map view更新annotation的coordinate点。
2)设置其draggable属性为YES。

当用户拖动annotation时,委托收到mapView:annotationView:didChangeDragState:fromOldState:方法


9、在地图上显示OverLay:
和Annotation类似:
1)需要实现了MKOverLay协议的对象(即overlay对象)
2)一个视图(继承自MKOverlayView类)(即overlay视图)

10、添加overlay到地图的清单:
1)定义一个overlay对象:
  a)使用MKCircle、MKPolygon、MKPolyline类 等
  b)子类化MKShape或MKMultiPoint来创建自定义的overlay对象。
  c)使用一个已存在的类并遵守MKOverlay协议。
2)定义一个overlay视图:
  a)对于标准图形,使用MKCircleView、MKPolygonView、MKPolylineView
  b)对于继承自MKShape的自定义形状,定义合适的MKOverlayPathView的子类来渲染形状。
  c)对于其它自定义的形状和overlay,子类化MKOverlayView并实现drawRect:方法
3)实现代理函数: mapView:viewForOverlay: 并返回MKOverlayView
4)使用addOverlay:方法添加overlay


11、使用标准的Overlay对象和视图:
MKCircle、MKPolygon、MKPolyline为overlay对象。
MKCircleView、MKPolygonView、MKPolylineVIew为overlay视图。

12、定义自定义的Overlay对象:
子类化MKShape或MKMultiPoint或者采用MKOverlay协议。
1)必须有coordinate可读属性
2)一个bounding rectangle,完全包含了overlay的内容。

13、定义自定义的Overlay视图:
子类化MKOverlayView,并重载
drawMapRect:zoomScale:inContext:
canDrawMapRect:zoomScale:

14、从代理中创建Overlay视图
mapView:viewForOverlay:方法

15、管理地图的Overlay对象:
overlays属性
代理函数mapView:didAddOverlayViews:方法,然后使用MKMapRectIntersectsRect函数来看看已经添加的overlay与其它的overlay是否有交集。

16、使用overlay当作annotations
MKOverlay协议遵循MKAnnotation协议。因此,所有的overlay对象都可以当作annotation对象来使用

以上是关于iOS开发-定位服务和地理围栏的主要内容,如果未能解决你的问题,请参考以下文章

带有地理围栏的 html5 地理定位 API

读书笔记iOS-微定位技术

带有模拟位置提供程序的 Android 地理围栏

在后台服务中创建地理围栏

地理围栏最佳策略

什么是 “地理围栏”?