CLLocation distanceFromLocation result is differ when I calculate - ios

I have used CLLocation's distanceFromLocation: method to calculate distance from some location.
But it's result is slightly different when I calculate by using "Haversine formula".
- (CLLocationDistance)distanceFromCoordinate:(CLLocationCoordinate2D)fromCoord {
double earthRadius = 6371.0; // Earth's radius in Kilometers
// Get the difference between our two points then convert the difference into radians
double nDLat = RADIANS((fromCoord.latitude - self.coordinate.latitude));
double nDLon = RADIANS((fromCoord.longitude - self.coordinate.longitude));
double fromLat = RADIANS(self.coordinate.latitude);
double toLat = RADIANS(fromCoord.latitude);
double nA = pow ( sin(nDLat/2.0), 2 ) + cos(fromLat) * cos(toLat) * pow ( sin(nDLon/2.0), 2 );
double nC = 2.0 * atan2( sqrt(nA), sqrt( 1 - nA ));
double nD = earthRadius * nC;
return nD * 1000.0;
}
CLLocation * loc = [[CLLocation alloc] initWithLatitude:[location.latitude doubleValue]
longitude:[location.longitude doubleValue]];
CLLocationDistance dist = [userLocation distanceFromLocation:loc];
CLLocationDistance dist2 = [userLocation distanceFromCoordinate:loc.coordinate];
Why two values are different?
Should I init location object with horizontalAccuracy and verticalAccuracy?

Your results are different because you are using different code.
You don't say how different.
Totally different? There's a bug in your code.
Big differences for places close together? Maybe your formula has problems with rounding errors.
Differences that grow as places are further apart? Maybe your definition of distance is different. Should be the closest distance on a path along earth surface.
In general, Earth is not a sphere but a spheroid. Taking that into account is more difficult but gives more precise results.

It is slightly different because Earth's Radius is not exact 6371 km. Use the correct Earth's radius, may be you can get better results.

Use the official WGS84 earth radius:
6 378 137 meter
I remember that ios delivers exactly the same result.
Should I init location object with horizontalAccuracy and
verticalAccuracy?
No, for sure not. That attributes are hints how acurate the position might be.
Distance is calculated by latitude and longitude only.
There are not much formulas:
- haversine formula (a bit slower than law of cosines, otherwise fine)
- law of cosines (problematic on small distances if not using 64 bit precision)
- vicenties which i smore acurate, it uses an elipsoidal earth model.

I also got the same problem... so i used google webservice to calculate distance. use this method, you will get accurate distance
-(void)calculateDistance()
{
//http://maps.googleapis.com/maps/api/directions/json?origin=41.742964,-87.995971& destination=41.811511,-87.967923&mode=driving&sensor=false
NSString *LocationUrl = [NSString stringWithFormat:#"http://maps.googleapis.com/maps/api/directions/json?origin=%#,%#&destination=%#,%#&mode=driving&sensor=false",origin.latitude,origin.longitude,destination.latitude,destination.latitude];
NSLog(#"Location URL:%#",LocationUrl);
NSURL *finalurl = [NSURL URLWithString: LocationUrl];
NSLog(#"Final URL = %#",finalurl);
NSData *data = [NSData dataWithContentsOfURL:finalurl];
NSLog(#"Data:-%#",data);
NSError *error;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data
options:kNilOptions error:&error];
// NSLog(#"josn data of location:%#",[json description]);
NSMutableDictionary *routes = [json objectForKey:#"routes"];
NSMutableArray *legs = [routes valueForKey:#"legs"];
NSMutableDictionary *newDistance =[legs valueForKey:#"distance"];
NSMutableArray distanceList =[[newDistance valueForKey:#"text"]objectAtIndex:0];
distance = [[distanceList objectAtIndex:0]floatValue];
NSLog(#"%.1f",distance);
}
Hope it will help you

Related

Calculate distance while walking between a fixed point and my current location in iOS

I have to calculate the distance between two locations in iOS and objective c. My one location is fixed at a point and when I walk I have to calculate a distance between my current position and the fixed point. I have used distanceFromLocation method but I am not getting the closer distance value. I have gone through a few articles and a few StackOverflow solutions but none gave me proper result.
Below is the code I used and in the code currentLocation and destinationLocation are properties to hold latitude and longitude of the locations. The destinationLocation is always a fixed location and currentLocation keep on changing when I walk. There are few UILabels to print current and fixed latitude and longitude values. I have to calculate the distance every time between these two points. When I move half or less meter it shows me a large distance which is inconsistent as well. Could you please help me what mistake am I doing here? Is there any other way to achieve this? Thanks!
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations{
CLLocation *newLocation = locations.lastObject;
self.currentLocation = [[CLLocation alloc] initWithLatitude:newLocation.coordinate.latitude longitude:newLocation.coordinate.longitude];
if(self.destinationLocation == nil){
self.destinationLocation = [[CLLocation alloc] initWithLatitude:newLocation.coordinate.latitude longitude:newLocation.coordinate.longitude];
self.destinationLatitude.text = [NSString stringWithFormat:#"%.8f", self.destinationLocation.coordinate.latitude];
self.destinationLongitude.text = [NSString stringWithFormat:#"%.8f", self.destinationLocation.coordinate.longitude];
}
self.currentLatitude.text = [NSString stringWithFormat:#"%.8f", self.currentLocation.coordinate.latitude];
self.currentLongitude.text = [NSString stringWithFormat:#"%.8f", self.currentLocation.coordinate.longitude];
CLLocationDistance distance = [self.currentLocation distanceFromLocation:self.destinationLocation];
self.gpsDistanceMeasurement.text = [NSString stringWithFormat:#"%.2f m", distance];
}
you can use haversine formula to calculate the distance between two points
swift 3.x
let Source = CLLocationCoordinate2D.init(latitude: lat, longitude: long)
let Destination = CLLocationCoordinate2D.init(latitude: lat, longitude: long)
func DistanceCalculator(Source:CLLocationCoordinate2D,Destination:CLLocationCoordinate2D) -> Double
{
// HaverSine Formula to calculate Diastance On Sphere Refrences//https://www.movable-type.co.uk/scripts/latlong.html
// Angle = sin2(∆Ø/2) + cosØ1 * cosØ2 * sin2(∆ø/2)
// constant = 2 * atan2(√angle,√1-angle)
// distance = R * c
let Earth_Radius:Double = 6371 * 1000
let LatDelta = self.DegreeToRad(Degree: (Destination.latitude - Source.latitude))
let LongDelta = self.DegreeToRad(Degree: (Destination.longitude - Source.longitude))
let latRad = self.DegreeToRad(Degree: Source.latitude)
let longRad = self.DegreeToRad(Degree: Destination.latitude)
let Angle = (sin(LatDelta/2) * sin(LatDelta/2)) + (cos(latRad) * cos(longRad) * sin(LongDelta/2) * sin(LongDelta/2))
let constant = 2 * atan2(sqrt(Angle),sqrt(1-Angle))
let Distance = Earth_Radius * constant
return Distance
}
func DegreeToRad(Degree:Double) -> Double
{
return Degree * (Double.pi / 180)
}

iOS: How to get the distance of multiple location from one location

i am working on a project in which i have to show the distance of multiple locations from user's locations. locations are based on latitude and longitude.
i am using the following code to get the distance between two locations is shows nearly same distance
CLLocation *locA = [[CLLocation alloc] initWithLatitude:28.6379 longitude: 77.2432];
CLLocation *locB = [[CLLocation alloc] initWithLatitude:28.6562 longitude:77.2410];
CLLocationDistance distance = [locA distanceFromLocation:locB];
NSLog(#"Distance is %f",distance);
float i = distance/1000;
NSLog(#"distance between two places is %f KM", i);
but now i am struct to get the distance of multiple locations from my location: locaA.
for example i take NSarray for latitude and longitude as
NSArray * latArray = [[NSArray alloc]initWithObjects:#"28.6129",#"28.6020",#"28.5244", nil];
NSArray * longArray = [[NSArray alloc]initWithObjects:#"77.2295",#"77.2478",#"77.1855", nil];
Please help me to resolve it
Take locaA as user's location
You can use below method to find distance
#define DEG2RAD(degrees) (degrees * 0.01745327)
double currentLatitudeRad = DEG2RAD(currentLatitude);
double currentLongitudeRad = DEG2RAD(currentLongitude);
double destinationLatitudeRad = DEG2RAD(destinationLatitude);
double destinationLongitudeRad = DEG2RAD(destinationLongitude);
double distance = acos(sin(currentLatitudeRad) * sin(destinationLatitudeRad) + cos(currentLatitudeRad) * cos(destinationLatitudeRad) * cos(currentLongitudeRad - destinationLongitudeRad)) * 6880.1295896;
Here, currentLatitude and currentLongitude is user's location. destinationLatitude and destinationLongitude is each object from your array "latArray" and "longArray" which you can iterate via for loop. distance is the distance between user's location and locations in array. Obtained distance will be in kilometres.
CLLocation *currentLocation = ... // This is a reference to your current location as a CLLocation
NSArray *arrayOfOtherCLLocationObjects = ... // This is an array that contains all of the other points you want to calculate the distance to as CLLocations
NSMutableArray *distancesFromCurrentLocation = [[NSMutableArray alloc] initWithCapacity:arrayOfOtherCLLocationObjects.count]; // We will add all of the calculated distances to this array
for (CLLocation *location in arrayOfOtherCLLocationObjects) // Iterate through each location object
{
CLLocationDistance distance = [location distanceFromLocation:currentLocation]; // Calculate distance
[distancesFromCurrentLocation addObject:#(distance)]; // Append distance to array. You need to wrap the distance object as an NSNumber so you can append it to the array.
}
// At this point, you have the distance for each location point in the array distancesFromCurrentLocation
Swift version:
let currentLocation: CLLocation = //current location
let otherLocations: [CLLocation] = //the locations you want to know their distance to currentLocation
let distances = otherLocations.map { $0.distanceFromLocation(currentLocation) }

subtracting latitudes with NSNumber type to find distance

I want to subtract two latitudes from each other to find the shortest distance, but I get this error, "Arithmetic on pointer to interface 'NSNumber', which is not a constant size in non-fragile ABI" If I change the - to a + I get a different error "Invalid operands to binary expression ('NSNumber *' and 'NSNumber *')" I've tried using doubles and many combinations of things, but it just doesn't work.
NSNumber *userLatitude = [NSNumber numberWithDouble:43.55];//sample
NSArray *listOfCities = [managedObjectContext executeFetchRequest:request error:&error];
for (CityList *item in listOfCities){
NSLog(#"latitude is %#",item.latitude);
NSNumber *distanceLat = userLatitude - item.latitude;
I will then insert them into a mutable array along with the longitudes and compare the distance. One possible solution using CLLocation would be
double distance = [usersCurrentLoc distanceFromLocation:otherLoc];
where usersCurrentLoc and otherLoc are both CLLocation variables.
I also want use the latitude and longitudes individually so I can do some custom plotting, and they are also stored separately, so I'd like to figure out the correct data types and most efficient solution.
item.latitude comes from core-data with the data model type of double and X-code auto generated the CityList class with a property of NSNumber * latitude;
If you want subtract two NSNumbers, then use this
NSNumber *distanceLat = [NSNumber numberWithFloat:([userLatitude floatValue] - [item.latitude floatValue])];
This:
NSNumber *distanceLat = userLatitude - item.latitude;
needs to be:
NSNumber *distanceLat = #([userLatitude doubleValue] - item.latitude);
If item.latitude is also an NSNumber then you need to call doubleValue on it too.
NSNumber is an object. You can't do math on the object. You need to use doubleValue to get its value and then you need to wrap the result in a new NSNumber instance.
BTW - why bother with NSNumber here? Why not do:
double userLatitude = 43.55;
double distanceLat = userLatitude - item.latitude;
CLLocationCoordinate2D newCoordinate = [newLocation coordinate];
CLLocationCoordinate2D oldCoordinate = [oldLocation coordinate];
CLLocationDistance meters = [newLocation distanceFromLocation:oldLocation];
The above one can be used to find distance between two locations

How to implement %0.7f in annotation.coordinate?

I have two strings which hold values say for ex:35.5044752 97.3955550
Let me convert it :
double f1=[la doubleValue];
double f2=[lo doubleValue];
(value of f1 and f2 is dynamic say for example f1= "35.5044752" f2="97.3955550" )
if i want to print it in NSLog i will do as follows :
NSLog(#" %f %f ",f1,f2);
And it returns 35.504475 97.395555
hence i change it as
NSLog(#" %0.7f %0.7f ",f1,f2);
And gets the full values like 35.5044752 97.3955550
Now i need it to use in the Coordinate like below:
annotation.coordinate=CLLocationCoordinate2DMake(coord.longitude, coord.longitude);
My Question is how can i implement %0.7f here like which i made in NSlog ?
so that i should take input fully instead of reducing or altering the value.
make a try like this. Directly pass values to obj center
CLLocationCoordinate2D center;
...
else if ([elementName isEqualToString:#"Lat"]) {
center.latitude = [[attributeDict objectForKey:#"degrees"] doubleValue];
}
else if ([elementName isEqualToString:#"Lon"]) {
center.longitude = [[attributeDict objectForKey:#"degrees"] doubleValue];
}
...
OR
Archived the coordinate in foundLocation:
NSNumber *latitudeObject = [NSNumber numberWithDouble:coord.latitude];
NSNumber *longitudeObject = [NSNumber numberWithDouble:coord.longitude];
NSArray *coordinateArray = [NSArray arrayWithObjects:latitudeObject, longitudeObject, nil];
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:coordinateArray]
forKey:WhereamiCoordinatePrefKey];
Unarchived the coordinate in viewDidLoad:
NSArray *coordinateArray = [NSKeyedUnarchiver unarchiveObjectWithData:[[NSUserDefaults standardUserDefaults]
objectForKey:WhereamiCoordinatePrefKey]];
CLLocationCoordinate2D savedCoordinate = CLLocationCoordinate2DMake([[coordinateArray objectAtIndex:0] doubleValue],
[[coordinateArray objectAtIndex:1] doubleValue]);
MKCoordinateRegion savedRegion = MKCoordinateRegionMakeWithDistance(savedCoordinate, 250, 250);
[worldView setRegion:savedRegion animated:YES];
The %0.7f format specifier deals with how your value is displayed, not with how it is stored. A double is always double and has its inherent precision and nothing you can do, short of casting it to another data type, will change that.
As far as I know, the double data type offers the highest floating point precision of the standard data types. If you need greater precision than that, you're going to have to use something other than a double.
In other words, when you perform an operation on a double, it is always calculated to the full precision allowed by the double data type.
For more information on the subject, see the Wikipedia entry on floating point data types.

Order results of CLGeoCoder geocodeAddressString by nearest location

I'm sure that I can figure this out based on code that I have implemented in PHP in the past, but I was hoping that someone has either done this and can provide an example or knows of a way to use the iOS SDK to accomplish it.
Here's my relevant code:
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
CLLocationDistance dist = _searchDistance;
CLLocationCoordinate2D point = self.locationManager.location.coordinate;
CLRegion *region = [[CLRegion alloc] initCircularRegionWithCenter:point radius:dist identifier:#"Hint Region"];
[geocoder geocodeAddressString:address inRegion:region completionHandler:^(NSArray *placemarks, NSError *error)
{
if( error )
{
// Push errors to the user
}
else
{
NSLog( [NSString stringWithFormat:#"Got results: %#", placemarks] );
}
}];
This pushes to the log something like the following:
(
"300 Oak St, Phoenix, OR 97535-5722, United States # <+42.26953820,-122.81554259> +/- 100.00m, region (identifier <+42.26953820,-122.81554106> radius 27.00) <+42.26953820,-122.81554106> radius 27.00m",
"300 Oak St, Ashland, OR 97520, United States # <+42.19955633,-122.71289484> +/- 100.00m, region (identifier <+42.19955633,-122.71289484> radius 138.42) <+42.19955633,-122.71289484> radius 138.42m",
"300 Oak St, Jacksonville, OR 97530, United States # <+42.31236366,-122.97179130> +/- 100.00m, region (identifier <+42.31236366,-122.97179130> radius 138.33) <+42.31236366,-122.97179130> radius 138.33m",
"300 Oak St, Central Point, OR 97502, United States # <+42.37422514,-122.91427182> +/- 100.00m, region (identifier <+42.37422514,-122.91427182> radius 138.29) <+42.37422514,-122.91427182> radius 138.29m",
"300 Oak St, Rogue River, OR 97537, United States # <+42.43621216,-123.16864522> +/- 100.00m, region (identifier <+42.43621216,-123.16864522> radius 138.24) <+42.43621216,-123.16864522> radius 138.24m"
)
In this case, the second result is the closest to my current location. I would like to have this list ordered by matches that are closest to my current location. I'm reasonably confident that I know how to do the math to figure out which location is closest, but I was hoping for a shortcut. Iterating through this array and sorting the results is going to be somewhat heavy, so I'd like to avoid that if possible.
EDIT (complete answer):
#Akash gave me the bit of information I was seeking, so I marked that answer as the correct one. If anyone is interested in sorting the results (rather than just getting the closest location), I put the unsorted results into an NSDictionary using the distance as a key, then used compare to sort the keys and then put the original placemark array elements into an output array ordered by the key to which they were associated in the NSDictionary. Here's the complete code (and the complete answer to my question):
-(NSMutableArray *)distanceSortPlacemarks:(NSArray *)inputArray fromLocation:(CLLocation *)originLocation
{
NSMutableArray *returnValue = [[NSMutableArray alloc] initWithCapacity:[inputArray count]];
NSMutableDictionary *sortDictionary = [[NSMutableDictionary alloc] initWithCapacity:[inputArray count]];
for( int i = 0; i < [inputArray count]; i++ )
{
CLPlacemark *currentPlacemark = [inputArray objectAtIndex:i];
[sortDictionary
setObject:currentPlacemark
forKey:[NSNumber numberWithDouble:[currentPlacemark.location distanceFromLocation:originLocation]]
];
}
NSArray *sortedKeys = [[sortDictionary allKeys] sortedArrayUsingSelector:#selector(compare:)];
for( int i = 0; i < [sortedKeys count]; i++ )
{
CLPlacemark *currentPlacemark = [sortDictionary objectForKey:[sortedKeys objectAtIndex:i]];
[returnValue insertObject:currentPlacemark atIndex:i];
}
return returnValue;
}
I know this could be a little tighter (some of the variables aren't absolutely necessary), but I tend to err on the side of making code easier to understand at quick glance.
Try getting distance of your current location from CLLocation in each CLPlacemark as follows :
for(int i = 0; i < placemarks.count; i++)
{
double distance = [placemarks[i].location distanceFromLocation:yourCurrentLocation];
if(i == 0)
{
indexOfClosestLocation = i;
distanceFromClosestLocation = distance;
}
else
{
if(distance < distanceFromClosestLocation)
{
distanceFromClosestLocation = distance;
indexOfClosestLocation = i;
}
}
}
After executing this code you have index of closest CLLocation in placemarks array and distance to it in meters.

Resources