Reverse geocoding from given coordinates (not current location, but manually provided)

坚强是说给别人听的谎言 提交于 2019-12-23 02:58:09

问题


I am fetching GPS information of all my images and they are stored in a Dictionary. I would pass on the lat & long values from this dictionary to the reverseGeocodeLocation function call & store the results in my database.

The problem here is that this function is an asynchronous call & I need to synchronize the whole process for my record to get inserted into the table.

Eg: My array read following coordinates: 32.77003,96.87532. It now calls the reverseGeocodeLocation function, passing on these coordinates as CLLocation object. Now before this async function returns me back the geo-coded location name, next request with new set of coordinates is sent to reverseGeocodeLocation function. This causes inconsistency to insert the record into database.

Is there any way to have this whole task turn synchronous?
i.e Make my for-loop wait until reverseGeocodeLocation returns a value and then move on to next record to be geo-coded?

A bit of my code is here:

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];
          });
      }
   }
}

回答1:


The short answer is that you can't make it synchronous.

What you want to do is move the code that goes on to the next object into the completion block of the reverseGeocodeLocation because that is the soonest that you can submit another reverseGeocodeLocation request. Let me see if I can make some pseudocode here... (that is, I haven't compiled this so it might not be exactly right...)

    // 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
        }
    }
}

And, of course, you can't depend on this being done to do anything else except that you might kick something off when you have reverseGeocoded the last object.

This asynchronous programming can drive you nuts.




回答2:


An alternative approach could be to place a synchronous request to the following URL, which returns reverse geo-coded results in XML format. You can later parse it, convert to JSON or whatever. The best part: 1) You're force synchronizing the whole process of reverse-geo coding 2) There's no restriction in terms of max requests you can make(I think its 50/min in case of calls to reverseGeocodeLocation handler). If exceeded, you get kCLErrorDomain code 2 error. So we avoid all that by the following approach below. Some sample code that works for me:

-(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);
}


来源:https://stackoverflow.com/questions/21664586/reverse-geocoding-from-given-coordinates-not-current-location-but-manually-pro

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!