使用 Google 地理编码的 For 循环 - 高错误率 - iPhone

发布于 2024-12-07 22:09:25 字数 2169 浏览 1 评论 0原文

我使用下面的代码通过地理编码获取位置信息,然后将地图图钉添加到 Google 地图视图。该代码使用 For 循环来循环访问数据库中的每个位置。问题是代码运行时无法返回大约 50% 地点的位置信息。这些失败的项目将按照以下代码保存在 failedLoad 数组中。

谁能建议这是为什么?另外,由于这些失败的项目保存在“failedLoad”数组中,有没有办法我可以使用这个数组来加载任何丢失的引脚?

编辑

失败的项目是由于 620 错​​误造成的,这意味着我提交项目的速度太快。如何在代码中添加延迟?

谢谢 !

-(void)displayPlaces {


for (PlaceObject *info in mapLocations) {

        // GET ANNOTATION INFOS
        NSString * addressOne = info.addressOne;
        NSString * name = info.name;
        NSString * postCode = info.postCode;

        NSString * addressTwo = [addressOne stringByAppendingString:@",London,"];
        NSString * address = [addressTwo stringByAppendingString:postCode];

       NSString* urlString = [NSString stringWithFormat:@"http://maps.google.com/maps/geo?q=%@&output=csv", [address stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
       NSURL* url = [NSURL URLWithString:urlString];
       NSURLRequest* req = [NSURLRequest requestWithURL:url];

       OHURLLoader* loader = [OHURLLoader URLLoaderWithRequest:req];
       [loader startRequestWithCompletion:^(NSData* receivedData, NSInteger httpStatusCode) {
           NSString* locationString = loader.receivedString;
           NSArray *listItems = [locationString componentsSeparatedByString:@","];

        double latitude = 0.0;
        double longitude = 0.0;

        if([listItems count] >= 4 && [[listItems objectAtIndex:0] isEqualToString:@"200"]) {
            latitude = [[listItems objectAtIndex:2] doubleValue];
            longitude = [[listItems objectAtIndex:3] doubleValue];

        }

        else {

            NSLog(@"Error %@",name);
            [failedLoad addObject : info];

        }  


        CLLocationCoordinate2D coordinate;
        coordinate.latitude = latitude;
        coordinate.longitude = longitude;
        MyLocation *annotation = [[[MyLocation alloc] initWithName:name address:address coordinate:coordinate] autorelease];

        [mapViewLink addAnnotation:annotation];

       } errorHandler:^(NSError *error) {
           NSLog(@"Error while downloading %@: %@",url,error);
       }];

 }


}

I am using the below code to fetch location information via Geocoding and then add a map pin to a Google Map View. The code uses a For loop to cycle through each place in my database. The problem is that the code fails to return location info for about 50% of the places when run. These failed items are saved in the failedLoad array as per the code below.

Can anyone suggest why this is ? Also since these failed items are saved in the "failedLoad" array, is there a way I could use this array to load any missing pins ?

EDIT

The failed items are due to a 620 error which means that I am submitting items too quickly. How can I add a delay into the code ?

Thanks !

-(void)displayPlaces {


for (PlaceObject *info in mapLocations) {

        // GET ANNOTATION INFOS
        NSString * addressOne = info.addressOne;
        NSString * name = info.name;
        NSString * postCode = info.postCode;

        NSString * addressTwo = [addressOne stringByAppendingString:@",London,"];
        NSString * address = [addressTwo stringByAppendingString:postCode];

       NSString* urlString = [NSString stringWithFormat:@"http://maps.google.com/maps/geo?q=%@&output=csv", [address stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
       NSURL* url = [NSURL URLWithString:urlString];
       NSURLRequest* req = [NSURLRequest requestWithURL:url];

       OHURLLoader* loader = [OHURLLoader URLLoaderWithRequest:req];
       [loader startRequestWithCompletion:^(NSData* receivedData, NSInteger httpStatusCode) {
           NSString* locationString = loader.receivedString;
           NSArray *listItems = [locationString componentsSeparatedByString:@","];

        double latitude = 0.0;
        double longitude = 0.0;

        if([listItems count] >= 4 && [[listItems objectAtIndex:0] isEqualToString:@"200"]) {
            latitude = [[listItems objectAtIndex:2] doubleValue];
            longitude = [[listItems objectAtIndex:3] doubleValue];

        }

        else {

            NSLog(@"Error %@",name);
            [failedLoad addObject : info];

        }  


        CLLocationCoordinate2D coordinate;
        coordinate.latitude = latitude;
        coordinate.longitude = longitude;
        MyLocation *annotation = [[[MyLocation alloc] initWithName:name address:address coordinate:coordinate] autorelease];

        [mapViewLink addAnnotation:annotation];

       } errorHandler:^(NSError *error) {
           NSLog(@"Error while downloading %@: %@",url,error);
       }];

 }


}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

稀香 2024-12-14 22:09:25

您可能应该一个接一个地发送它们(或者 5 × 5?),而不是使用 for 循环并同时发送所有请求,

这是一种方法(未在实际代码中进行测试,只是我一边打字,所以可能有一些拼写错误):

// In the instance variables, have:
@property(retain) NSMutableSet* mapLocationsToGeocode;

// When you want to decode, use:
self.mapLocationsToGeocode = [NSMutableSet setWitharray:mapLocations];
// (Or add to the existing NSSet if you have one and add Places using multple passes)
[self popLocationAndGeocode];

-(void)popLocationAndGeocode
{
  // Pop any location from the set
  PlaceObject* onePlace = [mapLocationsToGeocode anyObject];

  // Build the URL given the PlaceObject
  NSString* address = [NSString stringWithFormat:@"%@,London,%@",info.addressOne,info.postCode];
  NSString* name = info.name;

  NSString* urlString = [NSString stringWithFormat:@"http://maps.google.com/maps/geo?q=%@&output=csv", [address stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
  NSURLRequest* req = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString]];

  // Remove it so it won't be poped again
  [mapLocationsToGeocode removeObject:onePlace];

  // Send the request here to decode the PlaceObject
  OHURLLoader* loader = [OHURLLoader URLLoaderWithRequest:req];
  [loader startRequestWithCompletion:^(NSData* receivedData, NSInteger httpStatusCode) {
    NSString* locationString = loader.receivedString;
    NSArray* listItems = [locationString componentsSeparatedByString:@","];
    ...

    if([listItems count] >= 4 && [[listItems objectAtIndex:0] isEqualToString:@"200"]) {
        // Process with latitude and longitude, add your MKAnnotation, etc
    } else {
        NSLog(@"Error %@",name);
        [failedPlaces addObject:onePlace];
    }  
    ...

     // Schedule the next decoding request (1)
     if ([mapLocationsToGeocode count]) [self performSelector:@selector(popLocationAndGeocode) withObject:nil afterDelay:0.1];
   } errorHandler:^(NSError *error) {
       NSLog(@"Error while downloading %@: %@",url,error);
       [failedPlaces addObject:onePlace];

       // Schedule the next decoding request anyway (1)
       if ([mapLocationsToGeocode count]) [self performSelector:@selector(popLocationAndGeocode) withObject:nil afterDelay:0.1];
   }];

   // Schedule the next decoding request (2) -- solution 2
   // if ([mapLocationsToGeocode count]) [self performSelector:@selector(popLocationAndGeocode) withObject:nil afterDelay:1.0]; // wait one sec before sending next request
}

当然,不要忘记在完成后(或在 dealloc 中)将属性设置回 nil 以释放内存。

在情况 (1) 中,我在完成块和错误块中调用 performSelector:withObject:afterDelay ,以便只有在第一个请求/解码过程完成后才会调用下一个请求/解码过程。这样你的请求就有点序列化了。

在情况(2)(注释/禁用)中,我在 startRequestWithCompletion:... 方法之后调用 performSelector:withObject:afterDelay ,因此它不会等待第一个请求结束后弹出下一个请求。但你会(希望)等待足够长的时间,这样你就不会达到 GoogleAPI 速率限制


请注意,这不是唯一的解决方案,还有很多其他可能性。一种是使用 NSOperationQueue 将请求一个接一个地放入队列中,并向其添加依赖项,或者在 GCD 串行队列上安排请求的发送过程(是的,我知道我告诉过你不使用 GCD 来实际发送您的请求,但这仍然适用,仍然不使用 GCD+同步请求,但您可以使用 GCD 对将调用 [OHURLLoader 的块进行排队startRequestWithCompletion:...] 在主线程上一个接一个地执行请求本身仍然由 RunLoop 在主线程上执行)

Instead of using a for loop and send all your requests at the same time, you probably should send them one after the other (or 5 by 5?)

Here is one way to do it (not tested in real code, just typed as I go so may have some typo):

// In the instance variables, have:
@property(retain) NSMutableSet* mapLocationsToGeocode;

// When you want to decode, use:
self.mapLocationsToGeocode = [NSMutableSet setWitharray:mapLocations];
// (Or add to the existing NSSet if you have one and add Places using multple passes)
[self popLocationAndGeocode];

-(void)popLocationAndGeocode
{
  // Pop any location from the set
  PlaceObject* onePlace = [mapLocationsToGeocode anyObject];

  // Build the URL given the PlaceObject
  NSString* address = [NSString stringWithFormat:@"%@,London,%@",info.addressOne,info.postCode];
  NSString* name = info.name;

  NSString* urlString = [NSString stringWithFormat:@"http://maps.google.com/maps/geo?q=%@&output=csv", [address stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
  NSURLRequest* req = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString]];

  // Remove it so it won't be poped again
  [mapLocationsToGeocode removeObject:onePlace];

  // Send the request here to decode the PlaceObject
  OHURLLoader* loader = [OHURLLoader URLLoaderWithRequest:req];
  [loader startRequestWithCompletion:^(NSData* receivedData, NSInteger httpStatusCode) {
    NSString* locationString = loader.receivedString;
    NSArray* listItems = [locationString componentsSeparatedByString:@","];
    ...

    if([listItems count] >= 4 && [[listItems objectAtIndex:0] isEqualToString:@"200"]) {
        // Process with latitude and longitude, add your MKAnnotation, etc
    } else {
        NSLog(@"Error %@",name);
        [failedPlaces addObject:onePlace];
    }  
    ...

     // Schedule the next decoding request (1)
     if ([mapLocationsToGeocode count]) [self performSelector:@selector(popLocationAndGeocode) withObject:nil afterDelay:0.1];
   } errorHandler:^(NSError *error) {
       NSLog(@"Error while downloading %@: %@",url,error);
       [failedPlaces addObject:onePlace];

       // Schedule the next decoding request anyway (1)
       if ([mapLocationsToGeocode count]) [self performSelector:@selector(popLocationAndGeocode) withObject:nil afterDelay:0.1];
   }];

   // Schedule the next decoding request (2) -- solution 2
   // if ([mapLocationsToGeocode count]) [self performSelector:@selector(popLocationAndGeocode) withObject:nil afterDelay:1.0]; // wait one sec before sending next request
}

Of course, dont forget to set the property back to nil when done (or in the dealloc) to release the memory.

In case (1), I call performSelector:withObject:afterDelay in both the completion and error blocks, so that the next request/decoding process is called only once the first has finished. This way your requests are somewhat serialized.

In case (2) (commented/disabled), I call performSelector:withObject:afterDelay right after the startRequestWithCompletion:... method, so it won't wait for the end of the first request to pop the next one. But you will wait (hopefully) long enough so that you won't reach the GoogleAPI rate limit


Note that this is not the only solution, there are plenty other possibilities. One being to use NSOperationQueue to queue the requests one after the other on a queue and add dependencies to it, or schedule the sending process of the requests on a GCD serial queue (yeah, I know I told you not to use GCD to actually send your requests, but that still apply, still dont use GCD+synchronous requests, but you can use GCD to queue blocks that will call [OHURLLoader startRequestWithCompletion:...] on the main thread one after the other; the request itself still being executed on the main thread by the RunLoop)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文