【问题标题】:iPhone iOS5 CLGeocoder how to geocode a large (200) set of addresses?iPhone iOS5 CLGeocoder 如何对大量(200)组地址进行地理编码?
【发布时间】:2012-05-24 02:16:31
【问题描述】:

我有一大组大约 200 个地址,我需要知道它们的纬度和经度。我创建了一个解析地址的方法,现在我正在尝试使用CLGeocoder 获取这些地址的坐标。

我目前的方法是并行创建地理编码器并让它们发挥作用。我注意到他们每个人似乎都有一个单独的线程。 (所以我一次看到了多达 100 个线程)。

我遇到的问题是,在某个时间点(大约 50 个地址之后),地理编码停止返回任何地点标记,并且

NSLog(@"Address not recognized: *%@*",[htc objectForKey:kAddressKey]);

被调用。这是对线程数量的限制还是内置的 CLGeocoder 限制?可能是我没有正确清理地理编码器并需要某种自动释放语句 (ARC)?

-(void)geocodeArray:(NSMutableArray*)array {

    NSMutableDictionary* htc = nil;
    objectsToGeocode = array.count;

    NSDictionary *htcDictionary =nil;
     for (int i = 0; i<array.count;i++) {
         htcDictionary = [array objectAtIndex:i];

        //create an updated dictionary that would hold the reverse geocoding location
        htc = [[NSMutableDictionary alloc] initWithDictionary:htcDictionary];
        NSLog(@"geocoding: %@",[htc objectForKey:kAddressKey]);

        CLGeocoder* geoCoder = [[CLGeocoder alloc] init];
        [geoCoder geocodeAddressString:[htc objectForKey:kAddressKey] completionHandler:^(NSArray *placemarks, NSError *error) {

            if(placemarks.count>0)
            {
                NSLog(@"Found placemarks for %@",[htc objectForKey:kAddressKey]);
                CLPlacemark* placemark =  [placemarks objectAtIndex:0];
                MyLocation *annotation = [[MyLocation alloc]
                                          initWithName:[htcDictionary objectForKey:kNameKey]
                                          address:[htcDictionary objectForKey:kAddressKey]
                                          coordinate:placemark.location.coordinate] ;
                annotation.faxNumber = [htc objectForKey:kFaxKey];
                annotation.phoneNumber = [htc objectForKey:kPhoneKey];
                annotation.website = [htc objectForKey:kWebsiteKey];
                annotation.type = [htc objectForKey:kFacilityTypeKey];
                [_mapView addAnnotation:annotation];  


                double placemarkToUserDistance = [self._mapView.userLocation.location distanceFromLocation:placemark.location] ;
                //convert distance to miles
                placemarkToUserDistance =placemarkToUserDistance/ 1000/ kKilometersPerMile;

                [htc setObject:[NSNumber numberWithDouble:placemarkToUserDistance] forKey:kDistanceToUserKey];
                [htc setObject:[NSNumber numberWithDouble:placemark.location.coordinate.latitude] forKey:kLatitudeKey];
                 [htc setObject:[NSNumber numberWithDouble:placemark.location.coordinate.longitude] forKey:kLongitudeKey];
                NSAssert([htc objectForKey:kLatitudeKey]!=nil,@"kLatitudeKey is not saved!");
                NSAssert([htc objectForKey:kLongitudeKey]!=nil,@"kLongitudeKey is not saved!");

            }else {
                NSLog(@"Address not recognized: *%@*",[htc objectForKey:kAddressKey]);
            }


            [self.dataSource addObject:htc];

            if(++geocodingCount >=objectsToGeocode){
                NSLog(@"%@",self.dataSource);
                    [self saveGeocoding];

            }

        } ];


        //        [temp addObject:htcDictionary];
    }

}

为了测试这是否是线程问题,我创建了这个方法,它将我的大型数据集拆分为 5 个数组,并尝试对它们进行地理编码。我注意到第一个请求以及第二个请求的一部分都通过了。但是一旦达到 (~50) 的幻数,地理编码就会停止。

对可能发生的事情有任何想法吗? 这是 Apple 对地理编码操作数量施加的限制吗?我应该增加请求之间的延迟还是尝试单独运行应用 5 次并手动拼凑结果?

-(void)geocodeDatasource
{

        //I'm trying to build a file with coordinates of addresses and include it with the app
        geocodingCount = 0;
        self.dataSource = [[NSMutableArray alloc] initWithCapacity:self.arrayForGeocodingInitialJSON.count+5];
        haveToEmailInitialResults = YES;


    //attempt to geocode in batches

     float numberOfArrays = 5.0;
    NSMutableArray* array1 = [[NSMutableArray alloc] initWithCapacity:arrayForGeocodingInitialJSON.count/numberOfArrays];
    NSMutableArray* array2 = [[NSMutableArray alloc] initWithCapacity:arrayForGeocodingInitialJSON.count/numberOfArrays];
    NSMutableArray* array3 = [[NSMutableArray alloc] initWithCapacity:arrayForGeocodingInitialJSON.count/numberOfArrays];
    NSMutableArray* array4 = [[NSMutableArray alloc] initWithCapacity:arrayForGeocodingInitialJSON.count/numberOfArrays];
    NSMutableArray* array5 = [[NSMutableArray alloc] initWithCapacity:arrayForGeocodingInitialJSON.count/numberOfArrays];

    for(int i = 0 ;i<arrayForGeocodingInitialJSON.count;i++)
    {
        id object = [arrayForGeocodingInitialJSON objectAtIndex:i];
        if(i<arrayForGeocodingInitialJSON.count*(1/numberOfArrays))
        {
            [array1 addObject:object];
        }else if(i>=arrayForGeocodingInitialJSON.count/numberOfArrays && i<arrayForGeocodingInitialJSON.count*(2/numberOfArrays))
        {
            [array2 addObject:object];
        }else if(i>=arrayForGeocodingInitialJSON.count*(2/numberOfArrays) && i<arrayForGeocodingInitialJSON.count*(3/numberOfArrays))
        {
            [array3 addObject:object];
        }else if(i>=arrayForGeocodingInitialJSON.count*(3/numberOfArrays) && i<arrayForGeocodingInitialJSON.count*(4/numberOfArrays))
        {
            [array4 addObject:object];
        }else if(i>=arrayForGeocodingInitialJSON.count*(4/numberOfArrays) && i<arrayForGeocodingInitialJSON.count)
        {
            [array5 addObject:object];
        }



    }

    //simple delays eliminate the need for extra variables and notifications
        [self geocodeArray:array2];

        [self performSelector:@selector(geocodeArray:) withObject:array1 afterDelay:15];
        [self performSelector:@selector(geocodeArray:) withObject:array3 afterDelay:30];
        [self performSelector:@selector(geocodeArray:) withObject:array4 afterDelay:45];
        [self performSelector:@selector(geocodeArray:) withObject:array5 afterDelay:45];
}

谢谢!

【问题讨论】:

    标签: iphone objective-c multithreading ios5 clgeocoder


    【解决方案1】:

    您无法立即对大型集进行地理编码。 iOS 会限制你。我已经看到 iOS 一次将您限制为 50 个地理编码,“时间”因素是未知的。

    我也遇到过类似的问题,但由于我只需要将地理编码数据按顺序呈现给用户,这需要时间,因此我将所有地理编码都排入队列。

    实际上,我对 25 个数据块进行地理编码 - 一次向用户 1 显示结果,每个结果之间的间隔大约为半秒。当我要显示的内容少于 4 个时,我将对接下来的 25 个进行地理编码。这种情况一直持续到所有内容都被地理编码(或者在我的情况下,无限期)。

    如果您需要一次对所有内容进行地理编码,则需要将地理编码与每个块之间的延迟链接在一起,并在完成之前显示某种忙碌指示符。这可能需要一些时间来处理大量的地理编码。

    【讨论】:

    • 这很好,如果我理解正确的话,如果您在后续请求之间提供合理的延迟,您可以对超过 50 个请求进行地理编码?
    • 是的,据我所知。我已经能够运行几个小时的地理编码,每个地理编码之间都有延迟(我目前有 3 秒的延迟)。
    • 我认为限制是每分钟 50 个。每两个请求之间有 60 秒/50 = 1.2 秒的延迟,我没​​有收到任何错误(只有稍微少一点,我做到了!)。
    • 知道每分钟 50 个请求是基于设备还是基于应用?
    【解决方案2】:

    答案是您违反了 Apple 地理编码器的 TOS。

    来自CLGeocoder 文档。

    应用程序应该意识到它们如何使用地理编码。以下是有效使用此类的一些经验法则:

    最多为任何一项用户操作发送一个地理编码请求。 如果用户执行涉及对同一位置进行地理编码的多个操作,请重用初始地理编码请求的结果,而不是为每个操作启动单独的请求。 当您想要自动更新用户的当前位置时(例如当用户移动时),只有在用户移动了相当长的距离并且经过了合理的时间后才发出新的地理编码请求。例如,在典型情况下,您每分钟不应发送超过一个地理编码请求。 不要在用户不会立即看到结果时启动地理编码请求。例如,如果您的应用程序处于非活动状态或在后台,请不要启动请求。

    Apple 完全有权停止提供回复、完全切断您的应用,甚至撤销您的开发者帐户。

    如果您需要快速处理如此大量的地址,那么您应该考虑获得像 Factual for Points of Interest 或 Data Science Toolkit 这样的服务,用于邮政地址和地理区域。


    更新

    这是Places API - Resolve 的 API 文档,这是 Factual 的 places 地理编码器。


    更新 2

    这是 Street Address to Coordinates 的 API,它是 Data Science Toolkit 的 location 地理编码器。

    【讨论】:

    • Factual没有地理编码服务,只有反向地理编码。
    • @CodyC 我更新了我的答案以包含 Factual 地理编码服务的 API。
    • 这是用于定位地点(例如麦当劳)而不是地点(例如西雅图,华盛顿州)。根据developer.factual.com/display/docs/Core+API+-+Geo+Filters“事实不提供地理编码器(地址到坐标转换器)”
    • @CodyC 很好,我完全错过了。我的地理编码需求一直是 POI。我已经更新了我的答案以包含一个用于位置地理编码器的 API。
    猜你喜欢
    • 1970-01-01
    • 2011-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-15
    • 1970-01-01
    • 2017-06-17
    • 1970-01-01
    相关资源
    最近更新 更多