CLLocation方法distanceFromLocation的问题:结果不准确

9

我正在尝试使用distanceFromLocation:方法来计算我手持iPhone行走的总距离。目前为止,我一直在寻找帮助来解决我混乱、不准确和似乎是任意的结果。在这些代码片段中,theLabel只是我应用程序界面中存在的一个标签对象,distanceMoved是我试图存储我行走的总距离的变量,而locMan是在我的@interface文件中声明的位置管理器。

- (void)viewDidLoad
{
   locMan = [[CLLocationManager alloc] init];
   locMan.delegate = self;
   [locMan startUpdatingLocation];
   isInitial = true;
   distanceMoved = 0.0;
   [super viewDidLoad];
}

-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
   distanceMoved += [newLocation distanceFromLocation: oldLocation];
   theLabel.text = [NSString stringWithFormat: @"%f meters", distanceMoved];
}

任何帮助我修正此处错误的建议都将不胜感激。谢谢!
4个回答

14
  1. 过滤掉缓存的 (旧的) 位置数据,
  2. 过滤掉无效的位置结果 (精度 < 0),
  3. 将精度要求设置为kCLLocationAccuracyBest
  4. 设置最小距离过滤器,最好至少为10米以过滤掉位置噪声。
  5. 编辑:当oldLocation为空时,不要计算距离。

你还可以做更多的工作,但如果你能得到足够好的水平精度(<30m),这应该就可以了。你可以过滤掉低精度的结果,但这样你就必须自己跟踪oldLocation,这有点复杂。

添加一个NSLog (见下文) ,这样你就可以找出发生了什么。 如果你仍然不能得到好的结果,请发布NSLog的输出,这样我们就可以看到发生了什么并提供更多的帮助。

-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
    NSTimeInterval age = -[newLocation.timestamp timeIntervalSinceNow]; 

    if (age > 120) return;    // ignore old (cached) updates

    if (newLocation.horizontalAccuracy < 0) return;   // ignore invalid udpates

    // EDIT: need a valid oldLocation to be able to compute distance
    if (oldLocation == nil || oldLocation.horizontalAccuracy < 0) return; 

    CLLocationDistance distance = [newLocation distanceFromLocation: oldLocation];

    NSLog(@"%6.6f/%6.6f to %6.6f/%6.6f for %2.0fm, accuracy +/-%2.0fm",
        oldLocation.coordinate.latitude,
        oldLocation.coordinate.longitude,
        newLocation.coordinate.latitude,
        newLocation.coordinate.longitude, 
        distance,
        newLocation.horizontalAccuracy);

    distanceMoved += distance;
    theLabel.text = [NSString stringWithFormat: @"%f meters", distanceMoved];
}

- (void)viewDidLoad
{
   locMan = [[CLLocationManager alloc] init];
   locMan.delegate = self;
   locMan.desiredAccuracy = kCLLocationAccuracyBest;
   locMan.distanceFilter = 10;

   [locMan startUpdatingLocation];
   isInitial = true;
   distanceMoved = 0.0;
   [super viewDidLoad];
}

iOS6更新如果您希望使用didUpdateLocations:来完成相同的操作(因为上述方法已经过时),最简单的解决方案是在类中声明一个属性来保存每个位置,以便在下一次更新时可以得到前一个位置。 声明一个属性来保存oldLocation:

@property (nonatomic, retain) CLLocation* oldLocation;

然后在委托方法中,您可以保存每个newLocation,以便在下次调用委托方法时将其用作oldLocation

-(void)locationManager:(CLLocationManager *)manager didUpdateToLocations:(NSArray *)locations
{
    CLLocation* newLocation = [locations lastObject];

    NSTimeInterval age = -[newLocation.timestamp timeIntervalSinceNow]; 

    if (age > 120) return;    // ignore old (cached) updates

    if (newLocation.horizontalAccuracy < 0) return;   // ignore invalid udpates

    // EDIT: need a valid oldLocation to be able to compute distance
    if (self.oldLocation == nil || self.oldLocation.horizontalAccuracy < 0) {
        self.oldLocation = newLocation;
        return; 
    }

    CLLocationDistance distance = [newLocation distanceFromLocation: self.oldLocation];

    NSLog(@"%6.6f/%6.6f to %6.6f/%6.6f for %2.0fm, accuracy +/-%2.0fm",
        self.oldLocation.coordinate.latitude,
        self.oldLocation.coordinate.longitude,
        newLocation.coordinate.latitude,
        newLocation.coordinate.longitude, 
        distance,
        newLocation.horizontalAccuracy);

    distanceMoved += distance;
    theLabel.text = [NSString stringWithFormat: @"%f meters", distanceMoved];

    self.oldLocation = newLocation;    // save newLocation for next time
}

@progrmr:在iOS 6中,didUpdateToLocation方法已经改变。由于它只有一个NSArray位置参数,我们该如何实现它?我正在尝试实现相同的功能。 - rohan-patel
@progrmr:非常感谢。谢谢。我会尝试并留下评论,如果有任何疑问。 - rohan-patel
顺便问一下,在这个方法中我们如何获取 self.oldLocation?我昨天就卡在那了。locations 数组只包含最新的位置.. :/ - rohan-patel
再看一遍代码,sets方法在两个地方设置了self.oldLocation。 - progrmr
@progrmr:非常感谢。我明白了。不幸的是,您的答案未被注意到。值得得到许多赞扬。 :-) - rohan-patel
显示剩余6条评论

0

你应该做的第一件事是通过检查位置管理器接收到的第一个更新的时间戳来释放第一个更新,它通常会缓存最后已知的位置。其次要记住准确性,你将得到的初始值总是在一个广泛的范围内。


0

您没有设置位置管理器的准确性。请查看苹果文档,大概是accuracy = bestfornavigation之类的东西。这会像地狱一样耗电,但是适用于这种情况。

编辑:我刚想起来我手头有它。

// Create the Location Manager
locationManager = [[CLLocationManager alloc] init];

// Configure the Location Manager
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;

我尝试设置所有不同的常数以达到所需的准确度,但还是没有成功。现在,我的应用程序会从distanceMoved在-1.00000处开始,而不是0.000000。问题可能出在我的Core Location框架或我的iPhone本身吗? - bgottlob
NSLog输出旧位置和新位置,以查看它们如何变化。然后告诉我,这样我就可以了解正在发生什么。我已经与位置管理器一起工作了很多,我记得如果您没有正确配置它,它会出现奇怪的问题。 - Pochi
纬度和经度坐标似乎是正确的,因为每次更新位置时,新位置被存储为旧位置。由于某种原因,两个位置之间的距离被计算为一个极高的数字。 - bgottlob

0
CLLocationDistance distance = [secondLocation distanceFromLocation:firstLocation];  // distance is expressed in meters

CLLocationDistance kilometers = distance / 1000.0;
// or you can also use this..
CLLocationDistance meters = distance;

NSString *distanceString = [[NSString alloc] initWithFormat: @"%f", kilometers];

flot totaldistancecovered = [distanceString floatValue];

//Now,you can use this float value for addition...
// distanceMoved  should be float type variable which is declare in .h file...

 distanceMoved = distanceMoved + totaldistancecovered ;
 theLabel.text = [NSString stringWithFormat: @"%f meters", distanceMoved];

希望这能对你有所帮助。


CLLocationDistance 只是 double 的一个别名,因此没有必要将其转换为字符串,然后再进行数学计算。 - pr1001

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