Geocoder returns null after some time - objective-c

I'm receiving a list of latitude and longitudes by the web service and geocoding then to get the full address.
The problem is that after some time, they become null, and the geocoder stop working!
Can somebody help me?
Here's the code that I'm using to get the address:
-(void)getAddressArray:(void (^)(id response))completion{
for (int i=0; i<[favoritos count]; i++) {
double latitude = [[[favoritos valueForKey:#"latitude"] objectAtIndex:i] doubleValue];
double longitude = [[[favoritos valueForKey:#"longitude"] objectAtIndex:i] doubleValue];
CLLocationCoordinate2D location = CLLocationCoordinate2DMake(latitude, longitude);
CLLocation * locationAtual = [[CLLocation alloc]initWithLatitude:location.latitude longitude:location.longitude];
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:locationAtual completionHandler:^(NSArray *placemarks, NSError *error) {
NSDictionary *dicAddress;
CLPlacemark *place = [placemarks firstObject];
if (placemarks) {
NSLog(#"%#",place.addressDictionary);
if ([place.addressDictionary valueForKey:#"Thoroughfare"] == [NSNull null] || ![place.addressDictionary valueForKey:#"Thoroughfare"]) {
dicAddress = #{#"id":[[favoritos objectAtIndex:i] valueForKey:#"id"],#"address":#"Endereço não encontrado!",#"complemento":[[favoritos objectAtIndex:i] valueForKey:#"references"],#"number":[[favoritos valueForKey:#"numero"] objectAtIndex:i],#"name":[[favoritos valueForKey:#"name"] objectAtIndex:i], #"latitude":[[favoritos valueForKey:#"latitude"] objectAtIndex:i], #"longitude":[[favoritos valueForKey:#"longitude"] objectAtIndex:i]};
}
[address addObject:dicAddress];
if ([address count] == [favoritos count])
completion(address);
}
}];
}
Thanks!

Check the error passed in to the block, probably you are sending to many request and the rate limit is reached.
There is rate limit for reverseGeocodeLocation:completionHandler:
Geocoding requests are rate-limited for each app, so making too many
requests in a short period of time may cause some of the requests to
fail. When the maximum rate is exceeded, the geocoder passes an error
object with the value kCLErrorNetwork to your completion handler.

Related

What is it that is causing the NSInvalidArgumentException?

I've searched a lot but I can't find what I'm doing wrong. I'm trying to get 2 numbers that are stored in Firebase (uses a json file) and then create google maps markers with one of these numbers being the latitude and the other the longitude.
__block int i =0;
while (i < 5) {
i++;
NSString *url = [NSString stringWithFormat:#"---myfirebaseurl---", i];
Firebase *ref = [[Firebase alloc] initWithUrl:url];
[ref observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {
if (snapshot.value == [NSNull null]){
i = 5;
}
if (snapshot.value != [NSNull null]){
NSString *latitudestring = snapshot.value[#"latitude"];
NSString *longitudestring = snapshot.value[#"longitude"];
long latitude = [latitudestring longLongValue];
long longitude = [longitudestring longLongValue];
NSLog(#"latitude: %li and longitude: %li", latitude, longitude);
CLLocationCoordinate2D position = CLLocationCoordinate2DMake(latitude, longitude);
GMSMarker *marker = [GMSMarker markerWithPosition:position];
marker.map = mapview;
marker.title = (#"%#", snapshot.value[#"nome"]);
marker.snippet = (#"%#", snapshot.value[#"endereco"]);
}
}
];
}
Sorry if the code it's kinda messy.
This error message appears: *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSTaggedPointerString objectForKeyedSubscript:]
And the google maps markers aren't created at all. Any insight on this?
I found the answer just by changing FEEventTypeChildAdded to FEEventTypeValue. I can't really explain the reasoning, but at least it works.

How to get the time zone name for a CLLocation

I have an application that uses a CLGeocoder to forwardGeocode a placemark from an address string. The CLPlacemark response contains a CLLocation which gives me GPS coordinates.
The only way to create an NSTimeZone seems to be by using the correct Time Zone Name. It is important to point out that I am not using the current location of the device, so [NSTimeZone localTimeZone] will not work for me.
Is there a way to get the timezone name for the CLLocation so that I can create an NSTimeZone correctly?
NOTE: I have been using timeZoneForSecondsFromGMT but that never contains correct DST data, so it is not helpful for me.
You should use https://github.com/Alterplay/APTimeZones to get NSTimeZone from CLLocation. It also works with CLGeocoder.
since iOS9 it should be possible direclty using CLGeocoder as specified here: https://developer.apple.com/library/prerelease/ios/releasenotes/General/WhatsNewIniOS/Articles/iOS9.html
Search results for MapKit and CLGeocoder can provide a time zone for the result.
I found an interesting approach using CLGeocoder, which I put into a category on CLLocation. The interesting part looks like this:
-(void)timeZoneWithBlock:(void (^)(NSTimeZone *timezone))block {
[[[CLGeocoder alloc] init] reverseGeocodeLocation:self completionHandler:^(NSArray *placemarks, NSError *error) {
NSTimeZone *timezone = nil;
if (error == nil && [placemarks count] > 0) {
CLPlacemark *placeMark = [placemarks firstObject];
NSString *desc = [placeMark description];
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"identifier = \"([a-z]*\\/[a-z]*_*[a-z]*)\"" options:NSRegularExpressionCaseInsensitive error:nil];
NSTextCheckingResult *result = [regex firstMatchInString:desc options:0 range:NSMakeRange(0, [desc length])];
NSString *timezoneString = [desc substringWithRange:[result rangeAtIndex:1]];
timezone = [NSTimeZone timeZoneWithName:timezoneString];
}
block(timezone);
}];
}
Usage is like this:
CLLocation *myLocation = ...
[myLocation timeZoneWithBlock:^(NSTimeZone *timezone) {
if (timezone != nil) {
// do something with timezone
} else {
// error determining timezone
}
}];
Despite requiring a network connection and working asynchronously, I have found this to be the most reliable way of getting the time zone for a location.
EDITED YEARS LATER
This answer hasn't aged well since the timeZone property was added to CLPlacemark in iOS9 (thank you Ortwin Genz). Here's an updated category method:
-(void)timeZoneWithBlock:(void (^)(NSTimeZone *timezone))block {
[[[CLGeocoder alloc] init] reverseGeocodeLocation:self completionHandler:^(NSArray *placemarks, NSError *error) {
NSTimeZone *timeZone = nil;
if (error == nil && [placemarks count] > 0) {
CLPlacemark *placeMark = [placemarks firstObject];
timeZone = placeMark.timeZone;
}
block(timeZone);
}];
}

Trouble to show route between points Google Directions API. iOS 6

I have a problem when displaying a route in MapView if route has waypoints. I use DIRECTIONS GOOGLE API:
https://developers.google.com/maps/documentation/directions/
The route is not drawn using the road.
Any help? Thanks!!
The trouble images:
The code:
reponse = [self getRouteFromGoogleApiWithPoints:arrayOfCoords andMode:#"driving" error:error];
if(reponse == nil)
{
UIAlertView *succes= [[UIAlertView alloc] initWithTitle:NSLocalizedString(#"txtError", nil) message:NSLocalizedString(#"ServerError", nil) delegate: self cancelButtonTitle: #"OK" otherButtonTitles: nil];
[succes show];
}
else
{
NSArray *routes = [reponse objectForKey:#"routes"];
NSDictionary *route = [routes objectAtIndex:0];
NSDictionary *polylineOverview = [route objectForKey:#"overview_polyline"];
NSString *polylinePoints = [polylineOverview objectForKey:#"points"];
NSArray *decodedPolyline = [self decodePolyLine:polylinePoints];
CLLocationCoordinate2D coords[[decodedPolyline count]];
int i=0;
for(CLLocation* loc in decodedPolyline)
{
CLLocationCoordinate2D c;
c.latitude = loc.coordinate.latitude;
c.longitude = loc.coordinate.longitude;
coords[i]=c;
i++;
}
MKPolyline *line = [MKPolyline polylineWithCoordinates:(CLLocationCoordinate2D*)coords count:decodedPolyline.count];
[mapview addOverlay:line];
[mapview setNeedsDisplay];
}
}
}
+ (NSMutableArray *)decodePolyLine: (NSString *)encoded {
NSInteger len = [encoded length];
NSInteger index = 0;
NSMutableArray *array = [[NSMutableArray alloc] init];
NSInteger lat=0;
NSInteger lng=0;
while (index < len) {
NSInteger b;
NSInteger shift = 0;
NSInteger result = 0;
do {
b = [encoded characterAtIndex:index++] - 63;
result |= (b & 0x1f) << shift;
shift += 5;
} while (b >= 0x20);
NSInteger dlat = ((result & 1) ? ~(result >> 1) : (result >> 1));
lat += dlat;
shift = 0;
result = 0;
do {
b = [encoded characterAtIndex:index++] - 63;
result |= (b & 0x1f) << shift;
shift += 5;
} while (b >= 0x20);
NSInteger dlng = ((result & 1) ? ~(result >> 1) : (result >> 1));
lng += dlng;
NSNumber *latitude = [[NSNumber alloc] initWithFloat:lat * 1e-5];
NSNumber *longitude = [[NSNumber alloc] initWithFloat:lng * 1e-5];
CLLocation *loc = [[CLLocation alloc] initWithLatitude:[latitude floatValue] longitude:[longitude floatValue]];
[array addObject:loc];
}
return array;
}
+(NSDictionary*)getRouteFromGoogleApiWithPoints:(NSArray*)arrayOfPoints andMode:(NSString*)mode error:(NSError **)error
{
NSDictionary *result = nil;
NSString *waypoints = #"";
CLLocation* origin = [arrayOfPoints objectAtIndex:0];
CLLocation* destination = [arrayOfPoints objectAtIndex:arrayOfPoints.count - 1];
// Create the waypoints
for(int i = 1; i < arrayOfPoints.count - 2; i++)
{
CLLocation* current = [arrayOfPoints objectAtIndex:i];
waypoints = [waypoints stringByAppendingString:[NSString stringWithFormat:#"%f,%f%%7C",current.coordinate.latitude,current.coordinate.longitude]];
}
CLLocation* lastWaypoint = [arrayOfPoints objectAtIndex:arrayOfPoints.count - 2];
waypoints = [waypoints stringByAppendingString:[NSString stringWithFormat:#"%f,%f",lastWaypoint.coordinate.latitude,lastWaypoint.coordinate.longitude]];
NSString *urlString =[NSString stringWithFormat:#"https://maps.googleapis.com/maps/api/directions/json?origin=%f,%f&destination=%f,%f&waypoints=%#&sensor=true&mode=%#",origin.coordinate.latitude,origin.coordinate.longitude,destination.coordinate.latitude,destination.coordinate.longitude,waypoints,mode];
NSDictionary* response = [self getDictionaryFromURLString:urlString withParameters:nil error:error];
if (response != nil)
{
if ([[response objectForKey:#"status"] isEqualToString:#"OK"])
{
result = response;
NSLog(#"%#",response);
}
else
{
if (error)
{
NSMutableDictionary* details = [NSMutableDictionary dictionary];
[details setValue:[response objectForKey:#"status"] forKey:NSLocalizedDescriptionKey];
*error = [[NSError alloc] initWithDomain:#"AppName" code:2 userInfo:details];
}
}
}
else
{
if(error)
{
NSMutableDictionary* details = [NSMutableDictionary dictionary];
[details setValue:NSLocalizedString(#"ErrorServidor", nil) forKey:NSLocalizedDescriptionKey];
*error = [NSError errorWithDomain:#"AppName" code:1 userInfo:details];
return nil;
}
}
return result;
}
First off, according to the Google Terms & Conditions you're not allowed to use their directions/data on someone else's maps.
Secondly, the first is partly imposed because people have different data, place roads in different spots, break the roads into different segments etc etc and thus instructions don't line up.
If you want to show directions in your map you'll have to find another source. Maybe you should consider the CloudMade API.
Now you could use Google Maps SDK for iOS. That's the only legal option if you need to display Google Directions API under iOS6.
You can use Mapkit direction request API.
Direction request api has some limitation for consecutive calls made otherwise it works similar as google direction api.
There are ways to fetch direction for driving, walking and transit.
Also there is a way to get alternate routes.
MKDirectionsRequest *request = [[MKDirectionsRequest alloc] init];
MKPlacemark *sourcePlacemark = [[MKPlacemark alloc] initWithCoordinate:SourceCLLocationCorodinate).coordinate addressDictionary:nil];
MKMapItem *sourceMapItem = [[MKMapItem alloc] initWithPlacemark:sourcePlacemark];
[request setSource:sourceMapItem];
MKPlacemark *destPlacemark = [[MKPlacemark alloc] initWithCoordinate:destCLLocationCoordinate addressDictionary:nil];
MKMapItem *destMapItem = [[MKMapItem alloc] initWithPlacemark:destPlacemark];
[request setDestination:destMapItem];
[request setTransportType:MKDirectionsTransportTypeAny];
request.requestsAlternateRoutes = NO;
MKDirections *directions = [[MKDirections alloc] initWithRequest:request];
if(![directions isCalculating])
{
[directions calculateDirectionsWithCompletionHandler:
^(MKDirectionsResponse *response, NSError *error) {
if (error)
{
// Handle Error
NSLog(#"Error for this particular call");
}
else
{
for (MKRoute * route in response.routes)
{
//Add the route.polyline to the mapkit overlay
}
}];
}

Issue forward-geocoding multiple addresses

I am connecting to a remote web service which basically returns an XML back. I am then parsing that XML into a Property object (think real state sort of thing)
But now, the web service returns a postal code for each property alone. It does not provide a coordinate which is what I need to place an annotation in the map. I am able to geocode an address provided a postal code. However, my problem is it is not allowing me to do multiple requests
Here's my code
- (void)processProperties:(Property *)property {
[geocoder geocodeAddressString:property.postalCode
completionHandler:^(NSArray* placemarks, NSError* error){
placemark = [placemarks lastObject];
for (CLPlacemark* aPlacemark in placemarks)
{
[sublet setLatitude:aPlacemark.location.coordinate.latitude];
[sublet setLongitude:aPlacemark.location.coordinate.longitude];
}
}];
}
- (void)addAnnotations:(NSArray *)objects {
CLLocationDegrees lat;
CLLocationDegrees longitude;
CLLocationCoordinate2D mCoords;
NSString *fullAddress;
// Add the annotations found nearby
for (Property *property in objects) {
[self processProperties:property];
lat = property.latitude;
longitude = property.longitude;
fullAddress = [NSString stringWithFormat:#"%# %# %#", property.houseNumber, #" ", property.streetName];
[self createAnnotationWithCoords:mCoords :fullAddress :[NSString stringWithFormat:#"$%.2f", property.rent]];
}
zoomLevel = 0.1;
mCoords = CLLocationCoordinate2DMake(lat,longitude);
MKCoordinateRegion region = MKCoordinateRegionMake(mCoords,MKCoordinateSpanMake(zoomLevel,zoomLevel));
[self.mapView setRegion:region animated:YES];
}
For some reason it's just geocoding 1 property. Is not going through the loop accordingly.
Any ideas folks?
Use this on your Forward Geo Function. geocoder needs to be release and initialized again to start a new address, hope this helps.
- (void)processProperties:(Property *)property {
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressString:property.postalCode
completionHandler:^(NSArray* placemarks, NSError* error){
placemark = [placemarks lastObject];
for (CLPlacemark* aPlacemark in placemarks)
{
[sublet setLatitude:aPlacemark.location.coordinate.latitude];
[sublet setLongitude:aPlacemark.location.coordinate.longitude];
}
[geocoder release];
}];
}

Objective C: Getting 'Invalid Initializer Error' when setting CLLocationCoordinate2D location

I am trying to find and set the location as shown below but I keep getting the error 'Invalid Initializer'
CLLocationCoordinate2D location =[self.mapView addressLocation];
Where addressLocation method is as show below
-(CLLocationCoordinate2D) addressLocation {
NSString *urlString = [NSString stringWithFormat:#"http://maps.google.com/maps/geo?q=%#&output=csv",
[searchBar.text stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
//NSString *locationString = [NSString stringWithContentsOfURL:[NSURL URLWithString:urlString]];
NSError* error;
NSString *locationString = [NSString stringWithContentsOfURL:[NSURL URLWithString:urlString] encoding:NSASCIIStringEncoding error:&error];
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 {
//Show error
}
CLLocationCoordinate2D location;
location.latitude = latitude;
location.longitude = longitude;
return location;
}
Can you advise why I am getting the 'Invalid initializer' error? I have already imported the corelocation location framework and also imported the header files. So not sure what is wrong.
You are invoking [self.mapView addressLocation]. Shouldn't it be [self addressLocation]? I'm presuming mapView is the MKMapView in your class and doesn't have any addressLocation method.