从给定坐标(不是当前位置,但手动提供)反向地理编码

时间:2014-02-09 20:26:48

标签: ios core-location reverse-geocoding clgeocoder

我正在获取所有图像的GPS信息,并将它们存储在字典中。我会传递lat&此词典中的长值到reverseGeocodeLocation函数调用&将结果存储在我的数据库中。

这里的问题是这个函数是异步调用&我需要将我的记录的整个过程同步到表中。

例如:我的数组读取以下坐标:32.77003,96.87532。它现在调用reverseGeocodeLocation函数,将这些坐标作为CLLocation对象传递。现在,在此异步函数返回地理编码的位置名称之前,具有新坐标集的下一个请求被发送到reverseGeocodeLocation函数。这会导致将记录插入数据库时​​出现不一致。

有没有办法让整个任务变得同步? 即让我的for循环等待,直到reverseGeocodeLocation返回一个值,然后转到下一个要进行地理编码的记录?

我的一些代码在这里:

for (int imgIdx=0; imgIdx<[imageMetaMutArray count]; imgIdx++)
{
    NSDictionary *localGpsDict = [[NSDictionary alloc]initWithDictionary: [imageMetaMutArray objectAtIndex:imgIdx]];
    imgLatLoc=[localGpsDict valueForKey:@"Latitude"];
    imgLongLoc=[localGpsDict valueForKey:@"Longitude"];
    dateStamp=[localGpsDict valueForKey:@"DateStamp"];
    timeStamp=[localGpsDict valueForKey:@"TimeStamp"];

    if(imgLatLoc && imgLongLoc && dateStamp && timeStamp)
    {
        CLGeocoder *geoCoder=[[CLGeocoder alloc]init];
        CLLocation *currentLocation=[[CLLocation alloc]initWithLatitude:[imgLatLoc doubleValue] longitude:[imgLongLoc doubleValue]];

        [geoCoder reverseGeocodeLocation:currentLocation completionHandler:^(NSArray *placeMarks, NSError *err){
        if(err)
        {
            NSLog(@"Reverse geo-coding failed because: %@",err);
            return;
        }


        if(placeMarks && placeMarks.count>0)
        {
            CLPlacemark *placeMarkers=placeMarks[0];
            NSDictionary *locationDictionary=placeMarkers.addressDictionary;

            NSString *country=[locationDictionary objectForKey:(NSString *)kABPersonAddressCountryKey];
            NSString *city=[locationDictionary objectForKey:(NSString *)kABPersonAddressCityKey];
            NSString *state=[locationDictionary objectForKey:(NSString *)kABPersonAddressStateKey];

            NSLog(@"logged in from:");

                if(city)
                {
                    NSLog(@"city: %@",city);
                    locName = [[NSString alloc]initWithString:city];
                    if(state)
                    {
                        NSLog(@"state: %@",state);
                        locName=[locName stringByAppendingString:@","];
                        locName=[locName stringByAppendingString:state];
                    }
                    if(country)
                    {
                        NSLog(@"country: %@",country);
                        locName=[locName stringByAppendingString:@","];
                        locName=[locName stringByAppendingString:country];
                    }
                }
                else
                {
                        if(state)
                        {
                            NSLog(@"state: %@",state);
                            locName = [[NSString alloc]initWithString:state];
                            if(country)
                            {
                                NSLog(@"country: %@",country);
                                locName=[locName stringByAppendingString:@","];
                                locName=[locName stringByAppendingString:country];
                            }
                        }
                        else
                        {
                            NSLog(@"country: %@",country);
                            locName = [[NSString alloc]initWithString:country];
                        }
                    }
                }
                else
                {
                    NSLog(@"Placemark Error code:: %lu\n%@",(unsigned long)placeMarks.count,placeMarks);
                }

                [locName retain];
                NSLog(@"location decoded is: %@",locName);
                /*Call for Insert into Images table*/
               [self insertDataImgTbl:locName];
          });
      }
   }
}

2 个答案:

答案 0 :(得分:2)

简短的回答是你不能让它同步。

您要做的是将继续执行下一个对象的代码移动到reverseGeocodeLocation的完成块中,因为这是最快的,您可以提交另一个reverseGeocodeLocation请求。让我看看我是否可以在这里制作一些伪代码...(也就是说,我没有编译它,所以它可能不完全正确......)

    // in place of the original for loop:
    [self reverseGeocodeForIndex:0];


// Doing the reverse geocode is in a method so you can easily call it from within the completion block.
// Maybe your parameter is not the imgIdx value but is instead some object -- I'm just hacking your posted code
// The point is that your completion block has to be able to tell when
// it is done and how to go on to the next object when it is not done.
(void) reverseGeocodeForIndex:(int) imgIdx {
    NSDictionary *localGpsDict = [[NSDictionary alloc]initWithDictionary: [imageMetaMutArray objectAtIndex:imgIdx]];
    imgLatLoc=[localGpsDict valueForKey:@"Latitude"];
    imgLongLoc=[localGpsDict valueForKey:@"Longitude"];
    dateStamp=[localGpsDict valueForKey:@"DateStamp"];
    timeStamp=[localGpsDict valueForKey:@"TimeStamp"];

    if(imgLatLoc && imgLongLoc && dateStamp && timeStamp)
    {
        CLGeocoder *geoCoder=[[CLGeocoder alloc]init];
        CLLocation *currentLocation=[[CLLocation alloc]initWithLatitude:[imgLatLoc doubleValue] longitude:[imgLongLoc doubleValue]];

        [geoCoder reverseGeocodeLocation:currentLocation completionHandler:^(NSArray *placeMarks, NSError *err){
        // completion block
            if(err)
            {
                 // error stuff
            }

            if(placeMarks && placeMarks.count>0)
            {
                 // what happens when you get some data
            }

            // now see if we are done and if not do the next object
            if (imgIdx<[imageMetaMutArray count])
            {
                [self reverseGeocodeForIndex:imgIdx+1];
            } else {
                // Everything is done maybe you want to start something else
            }
        }];
    } else {
        // Since you might not reverseGeocode an object you need an else case
        // here to move on to the next object.
        // Maybe you could be more clever and not duplicate this code.
        if (imgIdx<[imageMetaMutArray count])
        {
            [self reverseGeocodeForIndex:imgIdx+1];
        } else {
            // Everything is done maybe you want to start something else
        }
    }
}

当然,你不能依赖于这样做来做任何其他事情,除非你在反向编码最后一个对象时可能会踢掉一些东西。

这种异步编程可以让你疯狂。

答案 1 :(得分:1)

另一种方法是将同步请求发送到以下URL,该URL以XML格式返回反向地理编码结果。您可以稍后解析它,转换为JSON或其他。最好的部分:1)你强制同步反向地理编码的整个过程 2)对于你可以做出的最大请求没有限制(我认为在调用reverseGeocodeLocation处理程序时它是50 / min)。如果超出,则会出现kCLErrorDomain代码2错误。因此,我们通过以下方法避免所有这些。一些适合我的示例代码:

-(NSString *)giveMeLocName: (double)gpsLat :(double)gpsLong
{
NSString *finalLocation=[[NSString alloc]init];
//Below is URL we need to send request
NSString *reverseGeoCodeLoc = [NSString
                                 stringWithFormat:@"http://nominatim.openstreetmap.org/reverse?format=xml&zoom=18&addressdetails=1&accept-language=en&lat=%lg&lon=%lg",gpsLat,gpsLong];

NSURL *myLocUrl = [NSURL URLWithString:reverseGeoCodeLoc];
ASIHTTPRequest *myLocRequest = [ASIHTTPRequest requestWithURL:myLocUrl];
[myLocRequest setDidFinishSelector:@selector(reverseGeoCodeImg:)];

[myLocRequest setDelegate:self];
[myLocRequest startSynchronous];
NSLog(@"waiting for location info..");
//Do stuff after receiving results here
}

//This block receives HTTP response as XML(containing reverse geo-coded info. I parse this to JSON using XMLReader class(downloadable)
-(void)reverseGeoCodeImg:(ASIHTTPRequest *)request
{
/*Allocations*/
locDict=[[NSDictionary alloc]init];
revGeoCodePart=[[NSDictionary alloc]init];
addressParts=[[NSDictionary alloc]init];
cityName=[[NSString alloc]init];
stateName=[[NSString alloc]init];
countryName=[[NSString alloc]init];

NSLog(@"starting reverse geo-code!!");
NSString *responseString = [request responseString];

NSError *parseError = nil;
locDict=[XMLReader dictionaryForXMLString:responseString error:&parseError];
[locDict retain];
revGeoCodePart=[locDict objectForKey:@"reversegeocode"];
[revGeoCodePart retain];
addressParts=[revGeoCodePart objectForKey:@"addressparts"];
[addressParts retain];

cityName=[[addressParts objectForKey:@"city"] objectForKey:@"text"];
[cityName retain];
stateName=[[addressParts objectForKey:@"state"]objectForKey:@"text"];
[stateName retain];
countryName=[[addressParts objectForKey:@"country"]objectForKey:@"text"];
[countryName retain];

NSLog(@"city: %@\nstate: %@\ncountry: %@",cityName,stateName,countryName);
}