Any side effect of CLLocationManager with NSTimer using such approach? - iphone

I am working on an iPhone app which needs Location Updates with intervals specified by the user.
Here is the code sample, which I am using to do this:
#implementation TestLocation
- (void)viewDidLoad{
if ([Utils getDataWithKey:TIMER_INTERVAL] == nil) {
[Utils saveDataWithKey:TIMER_INTERVAL withValue:#"60.0"];
}
locationManager = [[[CLLocationManager alloc] init] autorelease];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
locationManager.distanceFilter = kCLDistanceFilterNone;
[locationManager startUpdatingLocation];
}
- (void)startLocationManager:(NSTimer *)timer{
[locationManager startUpdatingLocation];
[timer invalidate];
timer = nil;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
// Here is code to handle location updates...
[manager stopUpdatingLocation];
// Timer will start getting updated location.
NSTimeInterval timeInterval = [[Utils getDataWithKey:TIMER_INTERVAL] doubleValue];
[NSTimer scheduledTimerWithTimeInterval:timeInterval
target:self
selector:#selector(startLocationManager:)
userInfo:nil
repeats:NO];
}
// other implementations ...
#end
The code is working like a charm.
The question is:
I'm using CLLocationManager along with NSTimer, does this effect memory or battery consumption? I mean any negative effect on User Experience?
If so, any suggestion, helping links to do such task with optimization will be much appreciated.
Note: Utils is my class to store or retrieve data.

Yes this will have some side effects, you will not get the desired accuracy. Since it will call the locationManager:didUpdateToLocation:fromLocation: every time the GPS signal will
be come more accurate.

That isn't a good strategy because you can receive multiple asynchronous location events before the first call to [manager stopUpdatingLocation]. That will lead to exponential number of timers getting created.
Instead, just start the repeating timer after creating your location manager and still stop the location manager after each received event.

Related

Calculate Accurate Speed Using GPS

I am using following code to update location after every 0.1 second -
- (void)viewDidLoad
{
[super viewDidLoad];
CLController = [[CoreLocationController alloc] init];
CLController.delegate = self;
// set auto update timer
currentTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self
selector:#selector(updatestart) userInfo:nil repeats:YES];
}
-(void)updatestart
{
[CLController.locMgr startUpdatingLocation];
}
And then I am trying to calculate speed using
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
[locMgr stopUpdatingLocation];
NSLog(#"%f",newLocation.speed);
}
I am getting speed in meter/sec but after some time the process slows down and even when I stop traveling the speed is being calculated.
What I want is to get accurate speed and show alert if speed is above 12km/hr.
Is there any other approach which I can use to find the same?
Thanks..
Starting and stopping the location manager like that isn't the way to do it. You can't force it to give more location updates. Leave it running and average the location updates over time. To get the best accuracy, use kCLLocationAccuracyBestForNavigation for the location manager's desiredAccuracy.

MKMapView route update issue

I would like to create a route in mkmapview whenever user moves to some new coordinates, Eg i i am in position A, now i am moving towards position B then the route should also be update.
Thanks
I think i didnt provide sufficient information for my issue, Here it is now, I have started doing cycling from point a and now i am going to point b then c, d etc, But i didnt reached to point b yet, i am in middle of the way, i like to achieve that my route also updates as i move to point b or c, Do i get the updated lat long value from didupdatetolocation delegate method, store them in an array and then draw, Because if i do so, then i am drawing the path everytime from the array? or is there any other approach which only refresh the updated route with the previous path?
By using this you can update the route..
CLLocationManager *locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
// update the route.
}
following code is use for the draw path on map.
-(void) showRouteFrom: (Place*) f to:(Place*) t {
if(routes) {
[mapView removeAnnotations:[mapView annotations]];
[routes release];
}
PlaceMark* from = [[[PlaceMark alloc] initWithPlace:f] autorelease];
PlaceMark* to = [[[PlaceMark alloc] initWithPlace:t] autorelease];
[mapView addAnnotation:from];
[mapView addAnnotation:to];
routes = [[self calculateRoutesFrom:from.coordinate to:to.coordinate] retain];
[self updateRouteView];
[self centerMap];
}
For more detail and sample code grab from here.
hope this help lot..

Can't get location from Wifi connection on iPhone

I am trying to write an app that will get a users location for a mass transit app which works great when I am above ground. When I am underground the location doesn't update even if I have a wifi and/or a cell signal. Below is the code that I am using. From what I understood the iPhone is able to get a location from wifi signal only, is this incorrect? Any help would be greatly appreciated, thank you in advance!
- (void)viewDidLoad
{
[super viewDidLoad];
//********************** Add map ******************************************
//setup location manager
locationManager = [[CLLocationManager alloc] init];
[locationManager setDelegate:self];
[locationManager setDistanceFilter:kCLDistanceFilterNone];
[locationManager setDesiredAccuracy:kCLLocationAccuracyHundredMeters];
//setup map view
mapView = [[MKMapView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 160.0f)];
mapView.showsUserLocation = YES;
mapView.userTrackingMode = MKUserTrackingModeFollow;
//run loop in background
loopTimer = [[NSTimer scheduledTimerWithTimeInterval:timeInterval target:self selector:#selector(tick:) userInfo:nil repeats:YES]retain];
}
// Search for n seconds to get the best location during that time
- (void) tick: (NSTimer *) timer
{
// Check for valid coordinate
CLLocationCoordinate2D coord = mapView.userLocation.location.coordinate;
if (!coord.latitude && !coord.longitude) return;
//get coordinates to update map
[mapView setRegion:MKCoordinateRegionMake(coord, MKCoordinateSpanMake(0.005f, 0.005f)) animated:NO];
//update current location in view
currentLatView.text = [NSString stringWithFormat:#"%.2f", coord.latitude];
currentLonView.text = [NSString stringWithFormat:#"%.1f", coord.longitude];
}
The locationManager you setup in viewDidLoad is your instance of the CL Location Manager whereas MapKit uses its own instance when you set showsUserLocation to true.
So your settings for the distance filter and desired accuracy are not being used by MapKit, and in any case, you haven't started your location manager with startUpdatingLocation.
So, try starting your location manager instance and then use the delegate method
locationManager:(CLLocationManager *)manager didUpdateToLocation:
to get what your location manager says.
Here is what I came up with, seems to work well needs more testing.
- (void)viewDidLoad
{
[super viewDidLoad];
//********************** Add map ******************************************
// Create the manager object
self.locationManager = [[[CLLocationManager alloc] init] autorelease];
locationManager.delegate = self;
// This is the most important property to set for the manager. It ultimately determines how the manager will
// attempt to acquire location and thus, the amount of power that will be consumed.
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
// When "tracking" the user, the distance filter can be used to control the frequency with which location measurements
// are delivered by the manager. If the change in distance is less than the filter, a location will not be delivered.
locationManager.distanceFilter = kCLLocationAccuracyBest;
// Once configured, the location manager must be "started".
[locationManager startUpdatingLocation];
//initialize newCoord
currentCoord = [[CLLocation alloc] initWithLatitude:0 longitude:0];
//setup map view
mapView = [[MKMapView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 160.0f)];
mapView.showsUserLocation = YES;
mapView.userTrackingMode = MKUserTrackingModeFollow;
//create map view
[self.view addSubview:mapView];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
// test that the horizontal accuracy does not indicate an invalid measurement
if (newLocation.horizontalAccuracy < 0) return;
// test the age of the location measurement to determine if the measurement is cached
// in most cases you will not want to rely on cached measurements
NSTimeInterval locationAge = -[newLocation.timestamp timeIntervalSinceNow];
if (locationAge > 5.0) return;
// store all of the measurements, just so we can see what kind of data we might receive
currentCoord = newLocation;
[self tick];
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
// The location "unknown" error simply means the manager is currently unable to get the location.
if ([error code] != kCLErrorLocationUnknown) {
[self stopUpdatingLocation:NSLocalizedString(#"Error", #"Error")];
}
}
- (void)stopUpdatingLocation:(NSString *)state
{
[locationManager stopUpdatingLocation];
locationManager.delegate = nil;
}
- (void) tick
{
//do stuff here
}

Show an UIAlert when delegate in LocationManager takes too long

I'm trying to create an alert when my LocationManager takes too long. In its delegate method, I check the newLocation's timestamp to make sure it's something recent:
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
NSDate *eventDate = newLocation.timestamp;
NSTimeInterval howRecent = [eventDate timeIntervalSinceNow];
if (abs(howRecent) < 1.0) { // process time if time is less than 1.0 seconds old.
timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateCount:) userInfo:nil repeats:YES];
return;
}
After some debugging, I realize that putting timer in that if block isn't right. What ends up happening is, the timer never invalidates, or rather my updateCount: method keeps running. I think what's happening is the delegate keeps getting called back with a bad time, then it keeps running more timers, so I get UIAlert after UIAlert.
I've used the same updateCount: method in a test project to create a 30 second timer and invalidate it and it works fine. Now however, I am stuck since I basically want to find the person's location, but if it takes too long (> 30 seconds), throw an alert. I'm not really sure where I should put this kind of code. It seemed to me to put it in the delegate where I'm making the check of the timestamp since that's the error condition I'm looking to keep track of. But that doesn't seem to be working well for me.
Is there a better place to put this kind of check? Or is there a better way to do this kind of task altogether? I'm pretty new to programming, so my knowledge is limited. TIA.
I think what you should be doing is- set the 30 second timer when you call [locationManager startUpdatingLocation]; Now, you can update your method to look something like-
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
NSDate *eventDate = newLocation.timestamp;
NSTimeInterval howRecent = [eventDate timeIntervalSinceNow];
if (abs(howRecent) < 1.0) { //good time found
//use this location
//either remove the 30 second timer or set a boolean
self.goodLocationFound = YES;
[locationManager stopUpdatingLocation];
}
In your timer method, you could do-
{
[locationManager stopUpdatingLocation];
if(self.goodLocationFound == NO)
{
//display alert
}
}

CLLocationManager weird issues

I have a MKMapView whereby I drop an annotation everytime the view loads, or when showLocation custom class method is called.
I need the accuracy to be the best
-(void)viewDidLoad {
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
[locationManager startUpdatingLocation];
}
-(IBAction) showLocation:(id) sender{
[locationManager startUpdatingLocation];
}
- (void) locationManager:(CLLocationManager *) manager
didUpdateToLocation:(CLLocation *) newLocation
fromLocation:(CLLocation *) oldLocation {
// start geocoding with newLocation coordinate which will automatically set annotation.
SVGeocoder *geocodeRequest = [[SVGeocoder alloc]
initWithCoordinate:CLLocationCoordinate2DMake(newLocation.coordinate.latitude, newLocation.coordinate.longitude)];
[geocodeRequest setDelegate:self];
[geocodeRequest startAsynchronous];
[geocodeRequest release];
[locationManager stopUpdatingLocation];
}
My question is when will the didUpdateToLocation method be called? Only after a new location is found when I perform [locationManager startUpdatingLocation]?
I am facing some weird issue when the user is travelling and on stationary.
Say the user is travelling from point A->B->C->D with 1 min intervals between points. When I call my method at point C, sometime it returns the coordinates of point A, sometimes point B and sometimes C. It's just random.
It's even more weird when I am on stationary. I get different coordinates when I calls showLocation method even though I am hooked up on my house WiFi.
I was thinking of implementing the didUpdateToLocation to get the best result it can within 5secs. If within the 5secs, it finds a particular location of an accuracy I had defined, then use the coordinate. If not, use the best it has found within the 5sec time frame. But as I am new I am not sure how to code something like that. I read up NSTimer and it seems like it might work.
Any advices guys?
Thanks a lot in advance!
One of the reasons you are receiving the location from point A is that CoreLocation is returning the last valid location it had first until it can obtain a more accurate location. When you call [locationManager startUpdatingLocation]; it will return the -didUpdateToLocation over and over until you are statisfied and finally call -stopUpdatingLocation.
I think you just need to allow a bit of time for it to get a better location fix before you stop updating your location. I would consider moving the stop updating location from your -didUpdateToLocation to a different method.
Remove [locationManager stopUpdatingLocation]; from your code and try .