Forward Geocoding with CLGeocoder Issues - iphone

I'm having an issue using forward geocoding on iOS5 with the geocodeAddressString function.
I have a MKMapView open up as a modal view to display different locations with data grabbed from the internet. My issue is when I dismiss the modal view, and then open it up again. On second try, only about half of my annotations are actually placed on the map. On a third try, none show up.
I have a feeling my issue here has to do with memory management and the scope of the CLGeocoder, but I just can't figure it out.
I create all of my annotations in the ViewDidLoad function of the view controller containing the MapView. Here is the code I use to get the coordinates of the addresses:
int locationCount = 0;
for(NSDictionary *date in locations)
{
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressString:[NSString stringWithFormat:#"%#, %#", [date objectForKey:#"venue"], [date objectForKey:#"location"]] completionHandler:^(NSArray *placemarks, NSError *error)
{
// if we find the exact location (including the venue string), create an annotation for the map
if(placemarks && placemarks.count > 0)
{
CLPlacemark *topResult = [placemarks objectAtIndex:0];
TourAnnotation *placemarkAnnotation = [[TourAnnotation alloc] initWithLocation:topResult.location andDetails:date];
placemarkAnnotation.tag = locationCount;
[tourMap addAnnotation:placemarkAnnotation];
}
// if not place an annotation at the center of the city
else
{
[geocoder geocodeAddressString:[date objectForKey:#"location"] completionHandler:^(NSArray *placemarks, NSError *error)
{
if(placemarks && placemarks.count > 0)
{
CLPlacemark *topResult = [placemarks objectAtIndex:0];
TourAnnotation *placemarkAnnotation = [[TourAnnotation alloc] initWithLocation:topResult.location andDetails:date];
placemarkAnnotation.tag = locationCount;
[tourMap addAnnotation:placemarkAnnotation];
}
}];
}
}];
++locationCount;
}
Any suggestions would be appreciated.

If it's memory issues have you considered how the annotation are dequeued and reused, the map view has a delegate method to do such a thing.
This tutorial may be of some assistance.
Ray Wenderlich map tutorial

Related

get nearest locations from my current location

I am currently developing a mobile application for iOS which consists a part that requires to determine the users current location and from current location, i want to find nearest locations.
Is there any way to implement this ??
I have done some coding for finding current location of user.
here is my code snippet,
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:locationManager.location
completionHandler:^(NSArray *placemarks, NSError *error) {
NSLog(#"reverseGeocodeLocation:completionHandler: Completion Handler called!");
if (error){
NSLog(#"Geocode failed with error: %#", error);
return;
}
CLPlacemark *placemark = [placemarks objectAtIndex:0];
NSLog(#"placemarks=%#",[placemarks objectAtIndex:0]);
But my problem is, how will i get nearest other locations ?? from my current location which i already fetched.
Thanks in advance.
Try this...
MKPointAnnotation *ann=[MKPointAnnotaion alloc] init];
CLLocationCoordinate2D point = [ann coordinate];
myLocation = [[CLLocation alloc] initWithLatitude:point.latitude longitude:point.longitude];
CLLocationDistance nearByLocation=[yourCurrentLocation distanceFromLocation:myLocation];

Geocode + MKPointAnnotation not playing nicely

I'm having the weirdest issue, and it's doing my head in. A global variable which I've set up within a Singleton is reporting correctly from within the function it's set in, then as NULL from within the very next function (which is where I need to access it), but as correct from another View! So the variable is correctly set, but it's not behaving within a certain function. There is also a weird error warning being generated by the offending line (which I've marked between *).
The warning is:
Property access result unused - getters should not be used for side effects.
Apologies for the very spotty code. I'm prototyping and learning as I go, so it's a mishmash of things I've cobbled from the net. What the code does is recognise a long tap on a mapview, and then places a pin at the location (while recording the location), and I'm trying to use Geocode to show the address at the pin position.
The first function is as follows:
- (void)handleLongPress:(UIGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer.state != UIGestureRecognizerStateBegan)
return;
CGPoint touchPoint = [gestureRecognizer locationInView:self.fullMapView];
CLLocationCoordinate2D touchMapCoordinate =
[self.fullMapView convertPoint:touchPoint toCoordinateFromView:self.fullMapView];
//save new birthplace in global variable
globalsSingle.gblBirthplace = touchMapCoordinate;
//place user location and record it
MKUserLocation *location = fullMapView.userLocation;
globalsSingle.gblCurrentLocation = location.coordinate;
//first remove any previous birthplace pin
[self removeAllPinsButUserLocation];
[self reverseGeocode];
//place new birthplace pin
MKPointAnnotation *birthPlacePin = [[MKPointAnnotation alloc] init];
birthPlacePin.coordinate = touchMapCoordinate;
birthPlacePin.title = #"My Birthplace";
**** birthPlacePin.subtitle = #"%#", globalsSingle.gblAddress; ****
[self.fullMapView addAnnotation:birthPlacePin];
NSLog(#"gblAddress = %#", globalsSingle.gblAddress);
}
The above function calls the next:
-(void)reverseGeocode {
CLGeocoder *ceo = [[CLGeocoder alloc]init];
CLLocation *loc = [[CLLocation alloc]initWithLatitude:globalsSingle.gblBirthplace.latitude longitude:globalsSingle.gblBirthplace.longitude]; //insert your coordinates
[ceo reverseGeocodeLocation: loc completionHandler:
^(NSArray *placemarks, NSError *error) {
CLPlacemark *placemark = [placemarks objectAtIndex:0];
//String to hold address
NSString *locatedAt = [[placemark.addressDictionary valueForKey:#"FormattedAddressLines"] componentsJoinedByString:#", "];
// save the address text
globalsSingle.gblAddress = locatedAt;
NSLog(#"addressDictionary %#", placemark.addressDictionary);
NSLog(#"placemark %#",placemark.region);
NSLog(#"placemark %#",placemark.country); // Give Country Name
NSLog(#"placemark %#",placemark.locality); // Extract the city name
NSLog(#"location %#",placemark.name);
NSLog(#"location %#",placemark.ocean);
NSLog(#"location %#",placemark.postalCode);
NSLog(#"location %#",placemark.subLocality);
NSLog(#"location %#",placemark.location);
//Print the location to console
NSLog(#"I am currently at %#",locatedAt);
NSLog(#"gblAddress from reverse Geocode = %#", globalsSingle.gblAddress);
}
];
}
What's even weirder (to me) is that the NSLog's from within reverseGeocode are all printing correctly, but the NSLog from the first function is reporting NULL, and is printing before the one from reverseGeocode even though it's (I assume) being executed second! For example, a debug output is:
2013-05-21 23:41:04.662 Project Name[5659:c07] gblAddress = (null)
2013-05-21 23:41:04.808 Project Name[5659:c07] gblAddress from reverse Geocode = Januária - MG, Brazil
Any help anyone could be bothered to offer I'd appreciate, as I'm bamboozled :)
The method reverseGeocodeLocation:completionHandler: is executed asynchronously, which means that it will move on to the next lines before it finishes.
Asynchronous vs synchronous execution, what does it really mean?
It is called asynchronously because the method reverseGeocodeLocation:completionHandler: might need some time to do it, and when it is finished, the completion block is called.
You should place the new birthplace pin only after the completion block of the reverseGeocodeLocation is called, for example inside the completion block, to ensure you have got the placemark data first before placing the pin. Or you can just update the subtitle of the newly added pin inside the completion block.
[ceo reverseGeocodeLocation: loc completionHandler:
^(NSArray *placemarks, NSError *error) {
CLPlacemark *placemark = [placemarks objectAtIndex:0];
//String to hold address
NSString *locatedAt = [[placemark.addressDictionary valueForKey:#"FormattedAddressLines"] componentsJoinedByString:#", "];
// save the address text
globalsSingle.gblAddress = locatedAt;
//place new birthplace pin
MKPointAnnotation *birthPlacePin = [[MKPointAnnotation alloc] init];
birthPlacePin.coordinate = globalsSingle.gblBirthplace;
birthPlacePin.title = #"My Birthplace";
birthPlacePin.subtitle = globalsSingle.gblAddress;
[self.fullMapView addAnnotation:birthPlacePin];
}
];
}
When you are calling [self reverseGeocode]; the rest of handleLongPress will continue to run without waiting for reverseGeocode to finish. This is why you are seeing the print functions being called in an order you weren't expecting.
[self performSelectorOnMainThread:#selector(reverseGeocode) withObject:nil waitUntilDone:YES];
If handleLongPress is running on the main thread, the above line can replace [self reverseGeocode] and should produce the expected results.

CLGeocoder reverseGeocodeLocation "kCLErrorDomain error 2"

I'm developing an iOS app with reverse geocoding features. When I call the function the first time everything is fine. After the second call (with a new instance of the controller where the call is done) the "Domain=kCLErrorDomain Code=2" Error appears. This happens on the Simulator and on the device. The Coordinates are valid.
My Code:
CLGeocoder *geoCoder = [[CLGeocoder alloc] init];
CLLocation *loc = [[CLLocation alloc] initWithLatitude:cityCoords.latitude longitude:cityCoords.longitude];
self.displayedCity = [[Stadt alloc] init];
[geoCoder reverseGeocodeLocation:loc completionHandler:^(NSArray *placemarks, NSError *error) {
if(!error){
for (CLPlacemark * placemark in placemarks) {
self.displayedCity.name = [placemark locality];
self.displayedCity.stadtCoord = placemark.region.center;
}
[self loadCity:self.displayedCity.name];
}
else{
NSLog(#"failed getting city: %#", [error description]);
}
}];
Thanks in advance!!
Error 2 usually means that you have called the geolocation server too often. Typically this happens when you send a reverse-Geocoding Request to the server each time the delegate method didUpdateLocations was fired. In the docs Apple says that this should typically only be done once a minute.
More info on the this error can be found in Apple's docs on kCLErrorDomain: Core Location Constants Reference and in CLError.h:
kCLErrorNetwork The network was unavailable or a network error occurred.

How to draw routes on maps in ios 6?

I want to show maps & draw routes on maps. My application supports for ios 4 plus. So how should i use maps to work on ios 6 as well as before. Also i want to know sholud i use custom mapview in my app to display maps & routes or should i use
[[UIApplication sharedApplication] openURL:]
I have never user MapKits. So please provide if any tutorial. Also let me know if there are any rd party libraries that can be used.
If you don't want an in-app map. Use the following:
NSString *destinationAddress = #"Amsterdam";
Class itemClass = [MKMapItem class];
if (itemClass && [itemClass respondsToSelector:#selector(openMapsWithItems:launchOptions:)]) {
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressString:destinationAddress completionHandler:^(NSArray *placemarks, NSError *error) {
if([placemarks count] > 0) {
MKPlacemark *placeMark = [[MKPlacemark alloc] initWithPlacemark:[placemarks objectAtIndex:0]];
MKMapItem *mapItem = [[MKMapItem alloc]initWithPlacemark:placeMark];
MKMapItem *mapItem2 = [MKMapItem mapItemForCurrentLocation];
NSArray *mapItems = #[mapItem, mapItem2];
NSDictionary *options = #{
MKLaunchOptionsDirectionsModeKey:MKLaunchOptionsDirectionsModeDriving,
MKLaunchOptionsMapTypeKey:
[NSNumber numberWithInteger:MKMapTypeStandard],
MKLaunchOptionsShowsTrafficKey:#YES
};
[MKMapItem openMapsWithItems:mapItems launchOptions:options];
} else {
//error nothing found
}
}];
return;
} else {
NSString *sourceAddress = [LocalizedCurrentLocation currentLocationStringForCurrentLanguage];
NSString *urlToOpen = [NSString stringWithFormat:#"http://maps.google.com/maps?saddr=%#&daddr=%#",
[sourceAddress stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding],
[destinationAddress stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlToOpen]];
}
This opens the map application and checks if it is ios5 or ios6.
For ios5 I use the LocalizedCurrentLocation from this post http://www.martip.net/blog/localized-current-location-string-for-iphone-apps
For ios6 I use the CLGeocoder to get the placemark and then open the map with it and the current location.
Remember to add CoreLocation.framework and MapKit.framework
I think this'll help you:
http://developer.decarta.com/Apis/IOS/Tutorial/Lesson6
http://developer.decarta.com/Apis/IOS/Tutorial/Lesson6Example
Or this?
http://spitzkoff.com/craig/?p=136
Maybe this is fun to do if you want data in your map:
http://www.raywenderlich.com/21365/introduction-to-mapkit-in-ios-6-tutorial
Or some basic information about Mapkit
http://www.techotopia.com/index.php/Working_with_Maps_on_the_iPhone_with_MapKit_and_the_MKMapView_Class

CLGeocoder and backward compatibility

I have a simple question.
MKReverseCoder is deprecated and doesn't work since iOS 5. We have to use CLGeocoder. But, there are a lot of people under iOS4. How the new apps can work with iOS4 and iOS5 for geocoding ?
Thanks!
If anyone is trying to move onto CLGeocoder from MKReverseGeocoder then I have written a blog post that might be of help http://jonathanfield.me/jons-blog/clgeocoder-example.html
Basically an example would be, after you have created locationManager and CLGeocoder objects just add this code to your viewDidLoad() and then make some labels or text areas to show the data.
[super viewDidLoad]; locationManager.delegate = self; [locationManager startUpdatingLocation];
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
[self.CLGeocoder reverseGeocodeLocation: locationManager.location completionHandler:
^(NSArray *placemarks, NSError *error) {
CLPlacemark *placemark = [placemarks objectAtIndex:0];
isoCountryCode.text = placemark.ISOcountryCode;
country.text = placemark.country;
postalCode.text= placemark.postalCode;
adminArea.text=placemark.administrativeArea;
subAdminArea.text=placemark.subAdministrativeArea;
locality.text=placemark.locality;
subLocality.text=placemark.subLocality;
thoroughfare.text=placemark.thoroughfare;
subThoroughfare.text=placemark.subThoroughfare;
//region.text=placemark.region;
}];
MKReverseGeocoder still works with iOS5. It's just deprecated, which means it'll be removed at some later point (like at the release of something like iOS6). So you can continue to use it now without any issues
- (IBAction)geoCodeLocation:(id)sender{
[self.geoCoder reverseGeocodeLocation: locationManager.location completionHandler:
^(NSArray *placemarks, NSError *error) {
CLPlacemark *placemark = [placemarks objectAtIndex:0];
NSLog(#"placemark %#",placemark);
//String to hold address
NSString *locatedAt = [[placemark.addressDictionary valueForKey:#"FormattedAddressLines"] componentsJoinedByString:#", "];
NSLog(#"addressDictionary %#", placemark.addressDictionary);
NSLog(#"placemark %#",placemark.region);
NSLog(#"placemark %#",placemark.country); // Give Country Name
NSLog(#"placemark %#",placemark.locality); // Extract the city name
NSLog(#"location %#",placemark.name);
NSLog(#"location %#",placemark.ocean);
NSLog(#"location %#",placemark.postalCode);
NSLog(#"location %#",placemark.subLocality);
NSLog(#"location %#",placemark.location);
//Print the location to console
NSLog(#"I am currently at %#",locatedAt);
//Set the label text to current location
[locationLabel setText:locatedAt];
}];
For more see property of CLPlacemark