I have an application working on iOS devices getting constantly the current latitude and longitude from the device. I would like to show an alert when the user reaches a certain lon/lat.
Here is the long to be reached : 2.39364456
Here is the lat to be reached : 48.84185814
When the user reaches this point, he should get an alert.
Unfortunately it doesn't work since the user doesn't always get to this exact point.
What I would like to do is : if you are NEAR to this point, in a certain range defined, then we pop up the alert.
I have no idea how to do this. I tried to implement long/lat to UTM converter and then try to work with UTM format but it was a failure.
Is it possible to work with the lon/lat's numbers which are after the comas to make a "range" to be in so the pop up shows ?
Thanks for reading and thanks in advance for your help ;)
Sounds like you are looking for the region monitoring functionality that comes in Core Location in iOS.
Here is a related question that talks about that.
And here is some Apple sample code.
And the latest Apple documentation that talks about Region Monitoring.
When your app gets into the region you're watching, your app gets called with the CLLocationManager delegate "locationManager:didEnterRegion:" method. And that is where you can fire your UIAlert from.
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
NSLog(#"didUpdateToLocation: %#", newLocation);
CLLocation *currentLocation = newLocation;
if (currentLocation != nil) {
CLLocation *locA = [[CLLocation alloc] initWithLatitude:currentLocation.coordinate.latitude longitude:currentLocation.coordinate.longitude];
}
CLLocation *locB = [[CLLocation alloc] initWithLatitude:lat2 longitude:long2];
distance = [locA distanceFromLocation:locB];
if(distance<yourDesiredDiustance)
{
show Alert
}
}
You have 2 locations, aCLLocationA which is position defined by you, and aCLLocationB user location which get's updated. Now what you do:
Step 1
Find the distance by using this code between points on Map:
CLLocationDistance distance = [aCLLocationA distanceFromLocation:aCLLocationB];
Step 2
if(distance<=yourRange)
{
//You have to show popup, because your location is in range with that point
}
Related
In my application I want do geofencing. I have list of locations, for which I am setting geofencing regions. All location have radius 100m. Following is the code to set Region:
CLLocationCoordinate2D cord;
cord.latitude = [location.latitude doubleValue];
cord.longitude = [location.longtitude doubleValue];
CLLocationDistance regionRadius = location.radius;
CLRegion *region = [[CLRegion alloc] initCircularRegionWithCenter:cord radius:regionRadius identifier:[NSString stringWithFormat:#"%d",location.locId]];
[appDelegate.locationManager startMonitoringForRegion:region];
My problem is that, its not working properly. For debugging, in didEnterRegion delegate added distance calculation in between current location and region location some times this distance is more then 1500m.
Following is the code to calculate distance between current location and region location:
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region {
CLLocation *loc1 = locationManager.location;
CLLocation *loc2 = [[CLLocation alloc] initWithLatitude:region.center.latitude longitude:region.center.longitude];
double dist = [loc1 distanceFromLocation:loc2];
}
Any idea, why -locationManager:didEnterRegion: get triggered such a wrong way?
This is a glitch in iOS Region Monitoring and I have faced the similar issue with Geofencing and I have found a way to solve that problem.
I have posted the question here and I have answered myself, I have a blog post on that as well:-
Region Monitoring Glitch on iOS 7 - Multiple Notifications at the same time
iOS Region Monitoring and Location Manager
I'm using the delegate method:
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
To get the current and old location, I used:
CLLocation *newLocation = [locations lastObject];
self.currentUserLocation = newLocation;
if(self.oldLocation == nil)
{
self.oldLocation = newLocation;
return;
}
EDIT 1: I can get the old location now.
But I always have a negative speed when I use [newLocation speed]; = -1
The device used is an iPhone 4s. Do you have an idea ?
Also, for the locationManager, I used kCLLocationAccuracyBestForNavigation and kCLDistanceFilterNone. I can see on the map my current location moving.
EDIT 2:
I finally achieve the issue with the speed using this method:
- (CLLocationSpeed)speedTravelledFromLocation:(CLLocation*)fromLocation;
{
NSTimeInterval tInterval = [self.timestamp timeIntervalSinceDate:fromLocation.timestamp];
double distance = [self distanceFromLocation:fromLocation];
double speed = (distance / tInterval);
return speed;
}
This method returns the speed calculated from the distance and time deltas between self and fromLocation.
I found this method in this repository: https://github.com/100grams/CoreLocationUtils
Hope it will help someone ;)
EDIT 3
Got it! It was because I'm testing on simulator! I tested on a device and the speed using [newLocation speed] is correct!
Thank you for your help.
Regards,
Lapinou.
Negative speed usually means you don't have a GPS signal.
Why are you expecting the location callback to have 2 or more locations?
This method:
(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
Usually delivers you just one location, except if for some reason multiple locations arrived before that method was called, but you usually will get just one location.
The problem is that you are assuming that that method gives you your previous location along with the new one, and it's not like that.
I am using CoreLocation to grab the users location. I need to grab the users location then send that to the server. I have it all working, but! I am implementing locationManager:(CCLLocationManager *)manager...
My url request is hitting before the location is found. Whats the best way to:
Request location
Store in string
Send string to request
I want to ensure the location is found before its sent. Do I check against the instance of the class with a conditional and inside the block hit the server. I dont need to keep updating, just grab once and stopUpdatingLocation
Was going through this post previously: Getting Longitude/Latitude of the User When the viewDidLoad
What I have so far:
- (void)viewDidLoad {
[super viewDidLoad];
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
[locationManager startUpdatingLocation];
...more below... server request below here
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
NSLog(#"didUpdateToLocation: %#", newLocation);
CLLocation *currentLocation = newLocation;
if (currentLocation != nil) {
NSLog(#"found!");
[locationManager stopUpdatingLocation];
}
}
Thoughts on how to do that? I think the conditional may work, but not sure if there was a proper way to handle that using the methods provided from CoreLocation.
Create a new method that will perform your location-dependent code. Then once you have the location (I.e. After the NSLog(#"found!"); line), call this method.
Note that the method may get called multiple times as the location accuracy improves - you might want to handle this.
For example:
- (void)viewDidLoad {
[super viewDidLoad];
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
[locationManager startUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
NSLog(#"didUpdateToLocation: %#", newLocation);
CLLocation *currentLocation = newLocation;
if (currentLocation != nil)
{
NSLog(#"found!");
[self handleLocation:newLocation];
[locationManager stopUpdatingLocation];
}
}
- (void)handleLocation:(CLLocation*)location
{
// handling code here
}
The location manager is asynchronous. You have to ask for location updates, then wait for it to call you back.
Worse, the first several updates are usually garbage that needs to be thrown away.
The first location update frequently has a timestamp that is hours, days, or even weeks old. You first have to check to make sure the timestamp is in the last few seconds.
Once you've done that, you need to check the horizontal accuracy, and make sure the reading is accurate enough. Often when you first start the location manager the first few readings have an accuracy value of more than a kilometer, which is awful. (Accuracy is really a radius value. You can only be sure that the location you get is somewhere inside a circle with the specified radius.) You need to come up with an app-specific accuracy reading that is "good enough", and throw away accuracy readings until you get one that is good enough. You also need to check for negative accuracy readings, which mean that the GPS is returning invalid values.
Next, you have to allow for the case where the GPS doesn't settle down in a reasonable time. (Sometimes it can take multiple minutes, or simply fail to get a good reading.) In that case you need to handle it as a failure.
So, to handle all that you need to write your locationManager:didUpdateLocation: method to check the time stamp and the horizontal accuracy to make sure the reading is actually good. You also need to make sure you time out and report a failure if you can't get a decent location reading after a reasonable wait. One way to do this is to start a "give up" timer when you first begin location updates, and after you get a good reading, kill it. If the timer fires, stop location updates and report a "can't get a good location reading" error to your user. Understand that it's not uncommon to take 10 or 15 seconds to get a decent accuracy reading.
{
...
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
}
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
NSLog(#"myLocation1: %#",[locations lastObject]);
myLocation = [locations lastObject];
NSLog(#"myLocation2: %#",[locations lastObject]);
[manager stopUpdatingLocation];
[self doSomethingWithLocation];
}
Currently I'm in the location 40.000,40.000.
I'm closing my app and change location to 10.000,10.000
When entering the app again and running [locationManager startUpdatingLocation]; my log will show:
myLocation1: <+40.00000000,+40.00000000>
myLocation2: <+40.00000000,+40.00000000>
If I'll trigger [locationManager startUpdatingLocation]; again my log will show:
myLocation1: <+10.00000000,+10.00000000>
myLocation2: <+10.00000000,+10.00000000>
How can I call didUpdateLocations once and still get the current location?
Should I use another delegate?
I guess I could place stopUpdatingLocation inside doSomethingWithLocation and run doSomethingWithLocation after some sort of delay in order for the right location to be updated but I'm sure that's not the way it's meant to be.
Thanks
Leave the location manager running for a while (e.g. 30 seconds), setting a timer to tell it to stop. The location manager updates are like pancakes, the first one you get isn't always the best.
The first update you are seeing is likely a "stale" location, which was determined many minutes ago when location services were last powered up. Or it may be a very inaccurate location determined using cell-tower positioning, for example. If you just need to get the device's current location, using Core Location directly requires a good deal of code because you must handle these cases. (The CLLocationManager API appears to be built for apps that need continuous location updates, like turn-by-turn GPS navigation apps.)
Instead of using CLLocationManager directly, I suggest you take a look at using an open source component such as INTULocationManager which will handle all of this work for you and make it trivially simple to request one or more discrete requests for the device's current location.
In this case you should check timestamp of location. User does not move on such distances so quickly.
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
CLLocation *location = [locations lastObject];
if(fabs([location.timestamp timeIntervalSinceNow]) < 5)//seconds
{
myLocation = location;
manager.delegate = nil;
[manager stopUpdatingLocation];
[self doSomethingWithLocation];
}
}
I'm in the process of writing an application that shows the user's distance from a fixed point as the user walks around (i.e. the label showing the distance from the user to the point is updated every time the user moves). I use a CLLocationManager with the code shown below:
- (void)viewDidLoad
{
locationManager=[[CLLocationManager alloc]init];
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
}
-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
CLLocationDistance meters = [newLocation distanceFromLocation:fixedPoint];
self.distanceLabel.text = [[NSString alloc] initWithFormat:#"Distance: %.1f feet", meters*3.2808399];
}
The label that is supposed to show the distance from the user to the point isn't updated constantly and when it is updated, it doesn't usually show the correct distance from the user to the fixed point. I was wondering if there is a better way for me to try and do this, or do the fundamental limitations of the core location framework make this impossible. Any help will be greatly appreciated.
Are you filtering out old (cached) positions? You should also filter based on accuracy, you probably don't want low accuracy locations.
You won't get continous or periodic update, the callback only occurs when the location has changed.
Assuming the device has GPS and can see enough GPS satellites to get a good position, this works fine.
-(void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation {
NSTimeInterval age = -[newLocation.timestamp timeIntervalSinceNow];
if (age > 120) return; // ignore old (cached) updates
if (newLocation.horizontalAccuracy < 0) return; // ignore invalid udpates
...
}