I keep getting this randomly when I run my gps app I'm building. It doesn't happen everytime, and the coordinates passed in are always valid (i nslog them). Is there documentation for these somewhere?
EDIT:
CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(locManager.location.coordinate.latitude, locManager.location.coordinate.longitude);
geocoder1 = [[MKReverseGeocoder alloc] initWithCoordinate:coord];
geocoder1.delegate = self;
[geocoder1 start];
and then about half the time it returns an error. I tried releasing and re-assigning the geocoder if there was an error, but that didn't help. Only thing that did was restarting the app.
In "MKTypes.h" in the MapKit framework, the following is defined:
Error constants for the Map Kit framework.
enum MKErrorCode {
MKErrorUnknown = 1,
MKErrorServerFailure,
MKErrorLoadingThrottled,
MKErrorPlacemarkNotFound,
};
...
MKErrorPlacemarkNotFound
The specified placemark could not be found.
This sounds like you are referencing some unknown placemark in your code? Or it could be that Google doesn't have a name for the position you are passing - however valid the coordinates may be.
I've met and solved this issue recently. In my case, when Apple Map cannot find any result for a query, it sometimes will just throw this this "MKErrorDomain = 4" error. So I ended up just treat this as "result not found".
It was painstaking to find this out, MapKit needs a better Error handling system.
I've been hitting this error repeatedly, and was unable to figure out how to make it stop; but I finally found an end-run around the whole issue that works quite well, and only takes a little more work: Don't use Apple's MKReverseGeocoder at all -- instead, directly call Google's reverse-geocoding API (this is apparently the same service that MKReverseGeocoder does behind the scenes). You can get back either JSON or XML (your preference), which you will then have to parse, but that isn't too hard.
For example, since my app is using ASIHTTPRequest, this is what it looks like (although this would also be easy to do with do with Apple's native APIs such as NSURLConnection):
#pragma mark -
#pragma mark CLLocationManagerDelegate
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
// Be careful: My code is passing sensor=true, because I got the lat/long
// from the iPhone's location services, but if you are passing in a lat/long
// that was obtained by some other means, you must pass sensor=false.
NSString* urlStr = [NSString stringWithFormat:
#"http://maps.googleapis.com/maps/api/geocode/xml?latlng=%f,%f&sensor=true",
newLocation.coordinate.latitude, newLocation.coordinate.longitude];
NSURL* url = [NSURL URLWithString:urlStr];
self.reverseGeocoderRequest = [ASIHTTPRequest requestWithURL:url];
self.reverseGeocoderRequest.delegate = self;
[self.reverseGeocoderRequest startAsynchronous];
}
By the way, Google's API has rules, just like Apple's does. Make sure you read the docs, especially regarding quotas.
I'm running into the same thing (the exact same code randomly fails sometimes) and I think I've found the answer. From Apple's developer docs: "Each Map Kit application has a limited amount of reverse geocoding capacity, so it is to your advantage to use reverse geocode requests sparingly."
So my theory is, we're getting rate-limited... since no other variables are changing (i.e. my code isn't changing, I'm running it on the simulator so the location of the device isn't changing, etc.) I think this must be the only remaining reason.
I just got done with a lot of research on this problem and it seems to be outside of our hands. I checked the developer forums as well as all around Stack and elsewhere and no one has a solution other than using a different service. There is a pretty good thread at https://devforums.apple.com/message/154126 on the subject.
Some people find the error after a certain time, I just find it to be out for a while and then comes back. I looked at the "Current Address" sample code and I couldn't see how I might have messed up. I ran the sample code and sure enough, it was NSLogging errors instead of returning a location.
This link has some code using Google's reverse geocoder: http://www.iphonedevsdk.com/forum/iphone-sdk-development/31883-pbrequestererrordomain-errors-reverse-geocoding.html#post155793
Actually, I am running into this problem as well. Code is extremely compact
//1. ask map for current coords
CLLocationCoordinate2D userLocation;
userLocation.latitude = [[_theMapView.userLocation location] coordinate].latitude;
userLocation.longitude = [[_theMapView.userLocation location] coordinate].longitude;
NSLog(#"%f, %f",userLocation.latitude,userLocation.longitude);
//2. reverse geocode coords
MKReverseGeocoder *reverseGeocoder = [[MKReverseGeocoder alloc]
initWithCoordinate:userLocation];
[reverseGeocoder setDelegate:self];
[reverseGeocoder start];
and later simply NSLog the error in the fail delegate method. It works the first time or two, then stops working
Related
Hello Friend i have seen many post regarding accuracy problem with gps but its not working all the time
-(void) locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
NSString *latstr = [NSString stringWithFormat:#"%f", newLocation.coordinate.latitude];
NSString *longstring=[NSStringstringWithFormat:#"%f",newLocation.coordinate.longitude];
if (abs(howRecent)>5.0)
{
[self.locationManager startUpdatingLocation];
return;
}
if(abs(newLocation.horizontalAccuracy)<0.0f)
{
[self.locationManager startUpdatingLocation];
return;
}
if(newLocation.horizontalAccuracy>65.0f)
{
[self.locationManager startUpdatingLocation];
return;
}
self.latstring = [latstr copy];
self.longstr = [longstring copy];
if((updateLocationFirst||loadFirstView))
{
[[NSUserDefaults standardUserDefaults]setObject:latstring forKey:#"Latitude"];
[[NSUserDefaults standardUserDefaults]setObject:longstr forKey:#"Longitude"];
[self displayParticularDaySpecial];
loadFirstView=FALSE;
updateLocationFirst=FALSE;
[self.locationManager stopUpdatingLocation];
}
}
Here the problem is I am sending the latitude and longitude to the google api with respect to some addresses if i am decreasing the accuracy value its taking lot of time to load and this value is having problem when you reach at destination with respect to destination with 0.6 miles difference.
You should refer to the Location Awareness Programming Guide which provides excellent programming examples for determining your location.
As you'll see in the demo in that document, when you start the standard location manager, you generally tell it what accuracy you require. And once you start the location manager, you don't have to start it again (like you appear to be doing in your code sample). The didUpdateLocations method will be called again and again, with increasing accuracy, all on its own. You don't need to start the location manager again ... it keeps going until you turn it off. By definition, if you're in didUpdateLocations, it means it's already on.
I also notice that you're using the old didUpdateToLocation. If you're programming for iOS 6, you really want to use didUpdateLocations, which is the current version of that CLLocationManagerDelegate method.
Finally, you mention it takes a long time to get your location. That's very possible. In fact, in certain areas, you will never get very close (or any location at all). Your app should assume that accurate locations will take a while, and you should gracefully handle if you never get a really accurate location, which is very possible.
Have you set desiredLocationAccuracy in your CLLocationManager? By default the range is somewhat wide.
It will take some time to acquire a very exact value.
I am trying to geocode address into coordinates using following code:
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressString:#"6138 Bollinger Road, San Jose, United States" completionHandler:^(NSArray* placemarks, NSError* error){
for (CLPlacemark* aPlacemark in placemarks)
{
// Process the placemark.
NSString *latDest1 = [NSString stringWithFormat:#"%.4f",aPlacemark.location.coordinate.latitude];
NSString *lngDest1 = [NSString stringWithFormat:#"%.4f",aPlacemark.location.coordinate.longitude];
lblDestinationLat.text = latDest1;
lblDestinationLng.text = lngDest1;
}
}];
I have tried it many times but the debugger never enters the block and I am not able to get the location. What can I try next?
All right I found my mistake.
The code is correct and works perfect. All the while I was working on it was through debugger and was trying to figure out why the debugger did not enter the block. But now I have found out debugger does not enter the block at that moment. It takes little in getting the location values. It is done asynchronously so I was not able to find it and I was getting crash because of no values just after the crash. I have moved my code post block to inside the block and everything works fine for me now.
I just ran that exact code and it worked as expected. Make sure that you have an active internet connection.
Try adding a NSLog on the strings and see if it gets called.
NSLog(#"lat: %#, lng: %#", latDest1, lngDest1);
Are you running it in the simulator or the device?
Blocks are new features to Objective C from iOS4.0 onwards. A block you can imagine as a delegate method working in same functional block. As for any delegate method it takes time to invoke, depending upon the condition, same way block executes the code inside it, when it completes its work of geocoding. You can read more about Block in apples documentation or read http://www.raywenderlich.com/9438/how-to-use-blocks-in-ios-5-tutorial-part-2.
You can also have look into my repository on GITHUB https://github.com/Mangesh20/geocoding
I'm attempting to overcome a sometimes-failure of didUpdateToLocation when checking local data based on current position, closing the app, traveling a bit, and opening the app again. What happens is that the user visits one place, checks the list, goes to another place, and the list is updated but using the old location data.
I would like to make sure I have a fresh newLocation.
What's wrong with this code?
double aa,bb,cc;
-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
[manager stopUpdatingLocation]; //only stop if new reliable pos fetched.
//old data? retry.
aa=[newLocation.timestamp timeIntervalSinceReferenceDate];
bb=[NSDate timeIntervalSinceReferenceDate];
cc=bb-aa; //must be <60 - minute-fresh
if (cc>60) {
[manager stopUpdatingLocation]; //only stop if new reliable pos fetched.
[manager startUpdatingLocation]; //only stop if new reliable pos fetched.
return;
}
...handle position
}
The symptoms are that cc is about 1200 seconds on app start, then on each retry it increases a few seconds.
I've tried -[newLocation.timestamp timeIntervalSinceNow], and similarly it increases a few seconds at each retry. At one point the interval was ~2400.
I'm using FilterNone and AccuracyBest, if that could influence it.
I'm open to alternative code solutions to make sure you have a fresh position.
The answer was that you need to write the handling the way you want it to behave.
I've read up on this common problem. I should have clarified that "force" means to start unambiguous behavior that gives a reliable position under optimal conditions for the phone. Not to busy-wait until satisfied or to expect an immediate result/error code.
From a common sense viewpoint, what would be desirable by all developers would be at least an extra method to simply ask for a position within parameters, and callback only when it's valid and within accuracy, or canceled, or timed out. If the conditions prevent it you then don't need to test out esoteric behavior to be able to handle it with custom code.
Why I needed to ask about this at all was:
no existence of a method functioning as described above
using code from a book (More Iphone 3 Development)
when writing it from scratch (looking at LocateMe.xproj), discovering that:
simulator behavior differs from phone behavior (not talking about the position itself, which is obviously as good as ip lookup can make it, but the behavior of didUpdateToLocation). Recognizing the limitations of a simulator, the method should at least behave like they do on a device. But currently, correctly written location handling/checking code just times out in the simulator (as in the LocateMe example), while incorrect code (asking once and using newLocation on callback) works.
and a bug causing didUpdateToLocation being called twice after [locationManager stopUpdatingLocation];
If on top of this you (like I) load data based on your position, and the data is required by multiple views in your application, the behavior of Apple's location-fetching methods doesn't make it easy to handle the chain of update-load-calculate-present consistently and problem-free throughout the app. Especially if your stuck between user/boss perception or decision of how it should work (a rock) and how the system works (a hard place).
Here's my current code which works on a device and in a simulator:
//ask for a position, as fresh as possible.
-(void)updateMeMap {
lastloc.coordinate=homeloc.coordinate;
lm.delegate=self;
ctr=0;
[self performSelector:#selector(stopUpd) withObject:nil afterDelay:gpstimeout];
[lm startUpdatingLocation];
}
//called on each position update.
-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
ctr++;
if (ctr<15) {
NSTimeInterval locationAge = -[newLocation.timestamp timeIntervalSinceNow];
NSLog(#"%d",ctr);
if (locationAge<60) {
NSLog(#"Found");
ctr=40;
homeloc.coordinate=newLocation.coordinate;
[self stopUpd];
[self reload];
} else {
NSLog(#"Lastupd");
lastloc.coordinate=newLocation.coordinate;
}
} else {
//enough tries, if not canceled choose lastloc.
if (ctr<40) {
NSLog(#"Last");
ctr=40;
homeloc.coordinate=lastloc.coordinate;
[self stopUpd];
[self reload];
}
}
}
//force stop updates. ctr prevents extra calls after stopUpdatingLocation.
//called after the timeout delay, if position found, cancel the timeout.
-(void)stopUpd {
[lm stopUpdatingLocation];
lm.delegate=nil;
if (ctr<15) {
ctr=40; //2 extra calls after stopupda... otherwise, now do nothing.
NSLog(#"Timeout");
homeloc.coordinate=lastloc.coordinate; //#need "copy"?
[self reload];
} else {
ctr=40; //2 extra calls after stopupda... otherwise, now do nothing.
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(stopUpd) object:nil];
}
}
// "couldn't get userlocation" handler. I also cancel like this on connectionDidFailWithError.
-(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
NSLog(#"Location failed: %#",[error localizedDescription]);
ctr=0;
[self stopUpd];
}
Where ctr is there to prevent the 2 extra calls, lastloc is last known reliable position, homeloc is the phone position used for (threaded) loading of location-specific data with [self reload].
The constants 15 and 60 are safe values from real-world testing; gpstimeout is set to 30. If you work a lot in a simulator you may want to set it to something short like 3 seconds, as all you will get is a stale but relatively usable position and no more until the timeout.
Edit: If you can best my code or point out an omission, I'll mark that as answer, I hate marking my own as answer.
I've come across a similar problem in one of my previous apps.
There's no way to force a location update as far as I'm concerned - (I searched for a long time and didn't find anything)
One thing that I found was using the UIBackgroundModes key in my info.plist.
Support for some types of background execution must be declared in advance by the application that uses them. An application declares this support by including the UIBackgroundModes key in its Info.plist file. Its value is an array that contains one or more strings with the following values:
audio. The application plays audible content to the user while in the
background. (This includes streaming audio or video content using
AirPlay.)
location. The application keeps users informed of their
location, even while running in the background.
voip. The application
provides the ability for the user to make phone calls using an
Internet connection.
Which is what you're looking for. See the docs for more info.
What if you put more of the code within the age check, like:
-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
NSTimeInterval locationAge = -[newLocation.timestamp timeIntervalSinceNow];
if (locationAge<60) {
[manager stopUpdatingLocation]; //only stop if new reliable pos fetched.
...handle position
}
}
I have been working on a maps application (iphone) originally I had my annotations set up to pull XML from google using their Places API. I'm having 3 issues.
For my annotation info, I was going
off of an example from Zen
(http://www.zen-sign.com/finding-business-listings-and-displaying-with-mapkit-part-1/
) and he has it set up to do it by
keyword, which wasn't really
necessary for me ( but I used it
anyway just to get a feel for
getting the annotations) in the
parser header he has:
-(void) getBusinessListingsByKeyword:(NSString*)keyword atLat:(float)lat atLng:(float)lng;
and in the the viewdidload of his
view controller
[locationsMap findLocationsByKeyword:#"Apple" ];
I'm not sure how to move from the
keyword parse version used in zen to
something that just does it
automatically (in the parser object- without the viewdidload in a different view controller if possible).
Any advice on what to read/watch or
sample code much appreciated
For places information
Google isn't the only kid on the
block and XML I hear comes in second
to JSON. So I wanted to know what
the best practice was for map
annotations made from business
information: JSON or XML?
The other issue I was having was
only getting 10 annotations (I want
to get 50 or more). So on top of
your advice on using XML or JSON.
How to I increase the amount of
annotations I'm getting.
Sorry for making this 3 parts but again any tutorials (text of video) would be very helpful. (So far I've watched hegarty from Stanford, Larson from MATC)
First
Don't know what you mean by automaticaly. But if you want to launch the map on users current location here you have two methods you can use:
-(IBAction)goToCurrentLocation{
CLLocation *location = [[CLLocation alloc]
initWithLatitude:myMap.userLocation.coordinate.latitude
longitude:myMap.userLocation.coordinate.longitude];
[self setCurrentLocation:location];
}
- (void)setCurrentLocation:(CLLocation *)location {
MKCoordinateRegion region = {{0.0f, 0.0f}, {0.0f, 0.0f}};
region.center = location.coordinate;
region.span.longitudeDelta = 0.15f;
region.span.latitudeDelta = 0.15f;
[self.myMap setRegion:region animated:YES];
}
Second. I don't know what are the best practicies but I used json with this jeson parser for my app ijustmadelove
Three. There is no problem in getting more then 10 annotations on the map. You have to have an error or a limitation in your code.
Currently i am developing an iPhone application where i have to show user's current city and location for which i am using Mapkit Framework.When i build the application it works fine and show me the exact city details .But right now when i try to build the application again application shows following error message in my log
/SourceCache/ProtocolBuffer_Sim/ProtocolBuffer-26/Runtime/PBRequester.m:523 server returned error: 503
reverseGeocoder: didFailWithError:Error Domain=PBRequesterErrorDomain Code=6001 "Operation could not be completed. (PBRequesterErrorDomain error 6001.)"
Is any body facing the same issue and how can i resolve it?
I am pretty sure this is happening because you are in testing, and using the reverse geocoder too much and Google's servers know that. So, it has basically blocked you for a while. Try again later and see if it works.
You need to make sure that you are not calling the geocoder more than once every 60 seconds.
I find out!
Well, now we'll have to use CLGeocoder, but if you want still to use MKReverseGeocoder, you MUST not call [geocoder start] twice, even if the geocode object is a fresh new one!
For example, DON'T do this:
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
MKReverseGeocoder *geoCoder = [[[MKReverseGeocoder alloc] initWithCoordinate:newLocation.coordinate] autorelease];
geoCoder.delegate = self;
[geoCoder start];
}
Or you'll get an arror: the location manager update position function is being called as soon as a new position is detected. As the accuracy is getting better and better, a new fresh new posistion is being sent to "didUpdateToLocation".
But creating a new object MKReverseGeocoder each time a position is found is a bad idea!
So, you've got a few solutions:
- Put a boolean that is true when geocoder is started. Then in didFindPlacemark (geocoder's), you should stop the geocoder with [geocoder cancel] and set your boolean to false.
- Make geocoder variable not local and manange a [geocoder cancel] + realease and create a new one and[geocoder start] each time didUpdateToLocation is beeing called.
- Any other way to avoid [geocoder start] to be called twice
I used the first solution and never get the 503 error anymore.
For me, MKReverseGeocoder was failing with 503 far too many times. It would also fail the first time it was requested, well after 60 seconds had passed. As a result, I decided to give up using it and instead decided to access Google's API directly using NSURLRequest via a service I created that also parses the result as a JSON String using the SBJSON Framework (very useful).
From my service, I call the following URL as follows:
NSString *urlStr = [NSString stringWithFormat:#"http://maps.googleapis.com/maps/api/geocode/json?latlng=%f,%f&sensor=true", coordinate.latitude, coordinate.longitude];
where coordinate is a CLLocationCoordinate2D.
I realise this is a long way around the problem, but since converting to this system I have had no issues!
It can happen at random, depending on the server's status.