使用iOS地理围栏跟踪多个(20+)位置

33

一款iOS应用程序使用地理围栏来通知用户有关预定义的附近位置。应用程序允许错过某些位置(用户不会收到有关附近位置的通知),但希望保持缺失率低。

一种实现方法是使用startMonitoringSignificantLocationChanges开始监视重大位置更改,并且每次触发“位置更改”事件时,在报告的位置周围500米半径内寻找位置。

让我担心的是在每次重要位置更改时执行附近区域查询的要求以及对电池的影响。

另一种方法是使用startMonitoringForRegion注册位置,但是苹果对同时跟踪的区域数量设置了(合理的)限制,为20个,而我们有比20个位置多得多的位置。因此需要某种形式的动态更新跟踪的区域,但我仍然不确定最佳方法是什么。

有没有任何想法可以使其保持低电池消耗,但对于位置缺失率也很低?


1
我也很想知道如何做到这一点。 - nhisyam
请参见此处的Swift答案:[https://dev59.com/bKPia4cB1Zd3GeqPujG5#45391851] - mfaani
5个回答

24

由于这个问题上的活动不多,我将描述我们目前如何解决这个问题。

我们将新地区的重新加载与重大位置更改(SLC)事件相关联。当发生SLC时,我们检查应该“地理围栏化”的20个相邻区域。为了找到最近的20个区域,我们只是根据以下公式近似1''的纬度和经度:

纬度: 1度 = 110.54千米

经度: 1度 = 111.320 * cos(纬度)千米

然后,只需检查设备当前位置的监视区域中心的边界正方形(请参见:使用纬/经度+千米距离进行简单计算?

例如,如果(10N,10E)是设备的当前位置,我们从顶点为(10-1',10-1'),(X-10',10+1'),(10 +1',10 +1'),(10 +1',10-1')的边界正方形开始(在纬度(10N,10E)处,每一纬度/经度分钟近似1.85千米)。

如果有20个(或几乎20个)-我们将它们注册为地理围栏,并等待下一个SCL。如果少/多,只需增加/减少边界矩形的大小并重复搜索即可。

您可以调整此搜索算法以获得更好的性能,但在此处描述的算法已经可以胜任。


1
我也在尝试做同样的事情,但是当应用程序关闭时它对我没有用,只有当应用程序在后台运行时才有效。如果您能提供一些示例代码,我将不胜感激。谢谢。 - HEH
2
既然您已经在使用重大位置更改事件,为什么不完全跳过iOS地理围栏呢?您可以自己检查是否在其中一个地理围栏内,而不是寻找最近的地理围栏。 - Dag Høidahl
2
SLC事件触发频率不足以完全替代地理围栏。我要补充的是我们曾经也遇到同样的问题,但采用了不同的解决方式。我们使用SLC中的位置对象,并使用内置位置管理器方法计算最接近这些坐标的前20个。丢弃旧的20个点并添加新的20个最近的点。 - Bill Burgess

9
你可以为“元地理围栏”预留一个位置,该围栏包含所有当前监控的位置。当用户离开此围栏时,应用程序将收到通知。然后应用程序可以更新自身并停止跟踪最远的区域,开始跟踪附近的新区域。

2
我已经按照上述方法编写了一个解决方案,链接在这里:http://stackoverflow.com/questions/22297995/add-more-than-20-regions-to-geofencing-ios/24080059#24080059 - uofc
1
@uofc 在那个页面中,你的解决方案在哪里? - JAHelia
如果手机将离开该元地理围栏处于某些糟糕的状态(例如关闭),您将不会收到通知,并将一直保持静默,直到下次应用启动。 - DanSkeel

6

我认为我可以提供另一种选项,用于在您的应用程序中使用超过20个地理围栏。这种方法已经在我们的应用程序中长期运行良好,并且使用了内置的CLLocation方法。

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
    if (locations.count > 0) {
        CLLocation *location = locations[0];

        NSMutableArray *sortedFences = [[NSMutableArray alloc] init];

        // add distance to each fence to be sorted
        for (GeofenceObject *geofence in enabledFences) {
            // create a CLLocation object from my custom object
            CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(geofence.latitude, geofence.longitude);
            CLLocation *fenceLocation = [[CLLocation alloc] initWithLatitude:coordinate.latitude longitude:coordinate.longitude];
            // calculate distance from current location
            CLLocationDistance distance = [location distanceFromLocation:fenceLocation];
            // save distance so we can filter array later
            geofence.distance = distance;
            [sortedFences addObject:geofence];
        }

        // sort our array of geofences by distance and add we can add the first 20

        NSSortDescriptor *sortByName = [NSSortDescriptor sortDescriptorWithKey:@"distance" ascending:YES];
        NSArray *sortDescriptors = [NSArray arrayWithObject:sortByName];
        NSArray *sortedArray = [sortedFences sortedArrayUsingDescriptors:sortDescriptors];

       // should only use array of 20, but I was using hardcoded count to exit

        for (GeofenceObject *geofence in sortedArray) {
            CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(geofence.latitude, geofence.longitude);
            CLLocationDistance radius = geofence.radius;
            NSString *ident = geofence.geofenceId;

            CLCircularRegion *fenceRegion = [[CLCircularRegion alloc] initWithCenter:coordinate radius:radius identifier:ident];
            fenceRegion.notifyOnEntry = geofence.entry;
            fenceRegion.notifyOnExit = geofence.exit;
            [locationController.locationManager startMonitoringForRegion:fenceRegion];
        }
    }
}

希望这能帮助某人或引导他们走上正确��道路。

2
嗨Bill,我刚刚看了你的解决方案。你只调用了"startMonitoringForRegion:"而从未调用"stopMonitoringForRegion:"。如果已经注册了20个区域,但在下一次调用中尝试注册一个新的区域,会发生什么?在调用"startMonitoringForRegion:"之前,将所有已注册的区域存储起来,并停止所有已注册的区域,这样做有意义吗? - Robert Weindl
1
只有当我的应用程序进入后台且超过20个时,我才会调用startMonitoring。当我的应用程序返回(-becomeActive)时,我会将其关闭。我不断地关闭和重新打开Geofences。当我添加Geofence时,我不会直接将其添加到可能失败的点上。我基本上将其设置为启用状态,然后告诉我的管理器添加最近的20个。你的情况可能会有所不同。 - Bill Burgess
请用 Swift 回答一下,谢谢。 - SaiPavanParanam
Obj-C 中使用的所有内容都应该在 Swift 中可用。转换不应该太困难。 - Bill Burgess
@RobertWeindl 使用 locationManager.monitoredRegions,您可以获取当前正在监视的区域并将它们全部删除。 - mfaani

3
如果您担心在每次重要的位置更改时执行接近性检查,那么您可以使用空间索引/搜索方法,例如R树R*树来减少每个位置更改所需的比较次数,因为这些搜索算法将过滤掉(可能很大的)空间无关区域。这应该会减少执行接近性检查所需的时间/电池功率。

2
我知道这篇文章已经有些年头了,但对于那些想要做类似事情的人来说,Skyhook 提供了无限数量场所的地理围栏功能。
从他们的营销角度来看: Skyhook 的 Context Accelerator 可以让应用程序开发者和广告商通过简单的 Web 接口即时部署无限数量的地理围栏到任何品牌连锁店(例如 CVS)或场所类别(例如便利店)。使用 Skyhook 的第一方位置网络的同一项专利技术,Context Accelerator SDK 在设备上管理这些活动的地理围栏,而不受操作系统限制,从而实现无限地理围栏。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接