Can't get location from Wifi connection on iPhone - 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
}

Related

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..

CLLocationManager tracks wrong Location (Track Me)

I am implementing Track me option in my code.CLLocationManager is not working as expected.when I start app remain at the same position ,CLLocationManager changes around 20-30 meters with-in 1 min minutes..then I remain Constant.
And If i change my position to track same thing happen in starting 1 min CLLocationManager moves 20-30 min extra then moves with my speed..
Why this happening..
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
self.locationManager.distanceFilter = 0.0001;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
}
-(void)start {
[self.locationManager startUpdatingLocation];
}
- (void)locationManager:(CLLocationManager*)manager
didUpdateToLocation:(CLLocation*)newLocation
fromLocation:(CLLocation*)oldLocation {
[self processLocationChange:newLocation fromLocation:oldLocation];
}
-(void)processLocationChange:(CLLocation*)newLocation fromLocation:oldLocation {
if (newLocation != oldLocation) {
NSLog(#"Moved from %# to %#", oldLocation, newLocation);
CLLocation* lastKnownLocation = NULL;
if ([self.locationPoints count] > 0) {
lastKnownLocation = [self.locationPoints objectAtIndex:[self.locationPoints count] - 1];
}
else {
lastKnownLocation = newLocation;
self.bottomLeft = newLocation.coordinate;
self.topRight = newLocation.coordinate;
}
// Check for new boundaries
CLLocationCoordinate2D coords = newLocation.coordinate;
if (coords.latitude < bottomLeft.latitude || coords.longitude < bottomLeft.longitude) {
self.bottomLeft = coords;
NSLog(#"Changed bottom left corner");
}
if (coords.latitude > topRight.latitude || coords.longitude > topRight.longitude) {
self.topRight = coords;
NSLog(#"Changed top right corner");
}
double speed = fabs(newLocation.speed);
double deltaDist = fabs([newLocation distanceFromLocation:lastKnownLocation]);
double newAvgSpeed = (self.totalDistance + deltaDist) / ((double)[self getElapsedTimeInMilliseconds] / 1000.0);
double accuracy = newLocation.horizontalAccuracy;
double alt = newLocation.altitude;
NSLog(#"Change in position: %f", deltaDist);
NSLog(#"Accuracy: %f", accuracy);
NSLog(#"Speed: %f", speed);
NSLog(#"Avg speed: %f", newAvgSpeed);
self.totalDistance += deltaDist;
self.currentSpeed = speed;
self.avgSpeed = newAvgSpeed;
self.altitude = alt;
NSLog(#"Delta distance = %f", deltaDist);
NSLog(#"New distance = %f", self.totalDistance);
// Add new location to path
[self.locationPoints addObject:newLocation];
// Update stats display
[self.first.start1 updateRunDisplay];
// Update map view
[self updateMap:lastKnownLocation newLocation:newLocation];
}
}
I faced the same problem in my current Pedometer app. I stretched, banged my head for couple of days. Then I found out that CLLocationManager is not able to track <5 meter distance and location generate updates. I kept self.locationManager.distanceFilter =2.0; and it gave me location updates even device was stationary. So I just changed distancefilter to 5.0 meter and it started working great. Try taking 5 meters it should work, I tested and all my wrong notifications issues vanished:
self.locationManager.distanceFilter =5.0;
You are taking self.locationManager.distancefilter=0.0001 which is I suppose out of capacity for CLLocationManager to track such a minor movement. Also you need to filter out old locations i.e. cached location updates as mentioned in Location Awareness Guide by Apple. I have used this condition in my code to filter all events which are older than 5 seconds.
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations
{
CLLocation *currentLocation=[locations lastObject];
NSDate* eventDate = currentLocation.timestamp;
NSTimeInterval howRecent = [eventDate timeIntervalSinceNow];
if(abs(howRecent)<5.0 && self.currentLocation.horizontalAccuracy<=10 && self.currentLocation.horizontalAccuracy>0)
{
//you have got fresh location event here.
}
}
I think give the distance filter effective with this
self.locationManager.distanceFilter = kCLDistanceFilterNone;
and you can start updating location method but also try this method these both methods are required to get exact location
[locationManager startMonitoringSignificantLocationChanges];

iPhone annotations cause app to act slow

When I load 500 annotations on map, app becomes laggy when moving a map, is there a way to solve this problem?
It's good when the map is zoomed and show 50 annotations, but when zoomed out to show all annotations, app is slow and it laggs when moving within a map.
Current code:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.annotations = [[NSMutableArray alloc] init];
// Create location manager object
locationManager = [[CLLocationManager alloc] init];
// All results from the location manager
[locationManager setDistanceFilter:kCLDistanceFilterNone];
// Set accuracy
[locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
// Start updating current location
[mapView setShowsUserLocation:YES];
self.mapView.mapType = MKMapTypeStandard; // also MKMapTypeSatellite or MKMapTypeHybrid
// Start off by default in some city
MKCoordinateRegion newRegion;
newRegion.center.latitude = value1;
newRegion.center.longitude = value2;
newRegion.span.latitudeDelta = value3;
newRegion.span.longitudeDelta = value4;
[self.mapView setRegion:newRegion animated:YES];
[self.mapView addAnnotations:self.annotations]; // --> self.annotations is array with 500 annotations
}

Any side effect of CLLocationManager with NSTimer using such approach?

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.

iphone updateToLocation works differently over 3G versus wireless network?

I have a simple mapview that has the following viewdidload method and didupdate to location:
- (void)viewDidLoad {
NSLog(#"in view did load");
[super viewDidLoad];
self.mapView.showsUserLocation = YES;
self.put_url = nil;
locationManager = [[CLLocationManager alloc] init];
[locationManager setDesiredAccuracy:kCLLocationAccuracyNearestTenMeters];
[locationManager setDelegate:self];
noUpdates = 0;
[locationManager startUpdatingLocation];
self.availableParking = [[NSMutableArray alloc] init];
//self.availableParking = nil;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
NSLog(#"in didupdatetolocatoin");
if([newLocation horizontalAccuracy] < 100 && [newLocation horizontalAccuracy] > 0) {
// Add annotation to map
DDAnnotation *annotation = [[DDAnnotation alloc] initWithCoordinate:newLocation.coordinate title:#"Park Here?"];
MKCoordinateRegion region;
MKCoordinateSpan span;
span.latitudeDelta = 0.05;
span.longitudeDelta = 0.05;
region.span = span;
region.center = newLocation.coordinate;
self.mapView.region = region;
NSLog(#"in didupdatetolocation");
noUpdates++;
NSLog(#"IN UPDATELOCATION NOUPDATES = %d",noUpdates);
if (noUpdates == 1) {
[self.mapView addAnnotation:annotation];
NSLog(#"ADDED ANNOTATION IN didUpdateToLocation!!");
[locationManager stopUpdatingLocation];
[self.settingsViewController setState:self.mapView.userLocation.subtitle andCity:#"fuckface"];
NSLog(#"STOPPED UPDATING LOCATION");
UpdateLocation *updatedLocation = [[[UpdateLocation alloc] initWithUserid:#"fuckface" andCoordinate:newLocation.coordinate withMapViewController:self]
autorelease];
NSLog(#"Lat = %f, Long = %f",newLocation.coordinate.latitude,newLocation.coordinate.longitude);
//[NSThread detachNewThreadSelector:#selector(getLocations) toTarget:self withObject:nil];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(sendUpdate)
name:#"NoteFromOne"
object:updatedLocation];
}
// We only update location once, and let users to do the rest of the changes by dragging annotation to place they want
} else {
NSLog(#"Accuracy not good enough %lf", [newLocation horizontalAccuracy]);
}
}
When Im connected to a wireless network it works flawlessly zooming in and dropping an annotation pin in my current location. Over the 3G network it never zooms or drops the pin. Any ideas as to why? Thanks in advance.
Here's a couple of screen shots:
With 3G:
alt text http://www.freeimagehosting.net/uploads/fe7fcbb2ea.jpg
With wifi:
alt text http://www.freeimagehosting.net/uploads/6b653e60a7.jpg
The GPS unit has nothing to do with the network connection (unless the GPS signal is not available, in which case the GPS may try to use the wifi hotspot or the cell your device is connected to in order to infer your location). It works independently of it if the GPS signal is available, and the network is only used to actually show the map. From the code snipped you posted, you only show the map when you reach an horizontal accuracy less than 100 meters, otherwise you let the GPS unit updating the location. However, if you try your code in the exact SAME place, the GPS unit on your device should always return the same updates. Therefore, I really do not understand the behaviour you are describing: this would be possible only if the GPS unit returned different updates for the same place when a different network connection is available because the GPS signal is not available. Are you seeing the same latitude/longitude in the two cases or not? Are you seeing the same accuracy? Be careful to measure this in exactly the same place in both cases.
If you obtain the same updates, then it may be possible that your 3G cellular connection is simply not powerful enough or is only apparently available, so that you did not get the map. Try testing the speed of your 3G network from the same place.
A related consideration. You should allow the GPS unit to work until either a specified amount of time elapses (say 20 seconds, use NSTimer for this) or until you reach a specified level of accuracy, whichever happens first. Otherwise you may end up consuming too much the battery.