Why does MKMetersBetweenMapPoints give me different results when I swap the parameters? - iphone

I'm actually trying to calculate the distance between the max and min point in the x and y coordinates for the MKMapPoints.
For that, I'm doing this (max distance in y axis):
MKMapPoint test1, test2;
double dist;
test1.x = 0.0;
test1.y = 0.0;
test2.x = 0.0;
test2.y = MKMapSizeWorld.height;
dist = MKMetersBetweenMapPoints(test2, test1);
NSLog(#"Distance %f",dist);
I get 18997878.291251 in the console. But when I change the distance calculation to:
dist = MKMetersBetweenMapPoints(test1, test2);
I get 18873651.664238, so I don't understand what's the difference. I don't even know if I'm doing the right thing to get the max values of distance in the x and y axes.
Any help will be appreciated.

I guess it's an algorithm problem. Some kind of approximation that stops when a certain precision is achieved. That's why no commutation there. You can try sample code to get distance between two points on map without using MKMapPoints:
- (float) distanceToLPU {
useDelegate
CLLocationCoordinate2D pointACoordinate = appDelegate.usrLoc;
CLLocation *pointALocation = [[CLLocation alloc] initWithLatitude:pointACoordinate.latitude longitude:pointACoordinate.longitude];
CLLocationCoordinate2D pointBCoordinate;
pointBCoordinate.latitude = [self.latitude doubleValue];
pointBCoordinate.longitude = [self.longitude doubleValue];
CLLocation *pointBLocation = [[CLLocation alloc] initWithLatitude:pointBCoordinate.latitude longitude:pointBCoordinate.longitude];
float distanceMeters = [pointALocation distanceFromLocation:pointBLocation];
[pointALocation release];
[pointBLocation release];
NSString *dist = [NSString stringWithFormat:#" (%.0f м)", distanceMeters];
NSLog(#"Distance to this LPU:%#", dist);
return distanceMeters;
}

Related

How to optimize code for drawing map overlay

I draw path on MKMapView based on coordinates stored in SQLite on iPhone.
But now I stored 14000 coordinates (just lat/lng) in database and now when I want to display overlay path I get application crash.
My question is is there any way to optimize this code to be faster?
This is in view did load:
// ar is NSMutableArray and it is populate from database for a few seconds but code bellow cost me app crash
for(Path* p in ar)
{
self.routeLine = nil;
self.routeLineView = nil;
// while we create the route points, we will also be calculating the bounding box of our route
// so we can easily zoom in on it.
MKMapPoint northEastPoint;
MKMapPoint southWestPoint;
// create a c array of points.
MKMapPoint* pointArr = malloc(sizeof(CLLocationCoordinate2D) * ar.count);
for(int idx = 0; idx < ar.count; idx++)
{
Path *m_p = [ar objectAtIndex:idx];
CLLocationDegrees latitude = m_p.Latitude;
CLLocationDegrees longitude = m_p.Longitude;
// create our coordinate and add it to the correct spot in the array
CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(latitude, longitude);
MKMapPoint point = MKMapPointForCoordinate(coordinate);
// adjust the bounding box
// if it is the first point, just use them, since we have nothing to compare to yet.
if (idx == 0) {
northEastPoint = point;
southWestPoint = point;
}
else
{
if (point.x > northEastPoint.x)
northEastPoint.x = point.x;
if(point.y > northEastPoint.y)
northEastPoint.y = point.y;
if (point.x < southWestPoint.x)
southWestPoint.x = point.x;
if (point.y < southWestPoint.y)
southWestPoint.y = point.y;
}
pointArr[idx] = point;
}
// create the polyline based on the array of points.
self.routeLine = [MKPolyline polylineWithPoints:pointArr count:ar.count];
_routeRect = MKMapRectMake(southWestPoint.x, southWestPoint.y, northEastPoint.x - southWestPoint.x, northEastPoint.y - southWestPoint.y);
// clear the memory allocated earlier for the points
free(pointArr);
[self.mapView removeOverlays: self.mapView.overlays];
// add the overlay to the map
if (nil != self.routeLine) {
[self.mapView addOverlay:self.routeLine];
}
UPDATE
ViewDidLoad:
...
[self performSelectorInBackground:#selector(drawPathInBackground) withObject:nil];
...
-(void)drawPathInBackground{
for(int idx = 0; idx < ar.count; idx++)
{ ... }
[self.mapView performSelector:#selector(addOverlay:) onThread:[NSThread mainThread] withObject:self.routeLine waitUntilDone:YES];
}
I did like this and UI not freezes.
The only thing that left is how to draw MKPolyLine on every X points?
three approaches:
don't display every point but rather combine nearby points to just one. the saving depends on your data and the necessity to display all.
if possible load the data in a background thread and display in multiple batches on the main thread. the user will practically see how the data is loaded after time.
load and display data lazily. means: only display those points which are visible on screen
Do fetching from the database and processing in a background thread.
Then reduce the number of coordinates in the path using the Douglas–Peucker algorithm:
And cache the results.
If you have array of coordinates the use this code
here routes is array of coordinates.
NSLog(#"count %d",[routes count]);
MKMapPoint* pointArr = malloc(sizeof(CLLocationCoordinate2D) * [routes count]);
for(int idx = 0; idx < [routes count]; idx++)
{
CLLocation* location = [routes objectAtIndex:idx];
CLLocationCoordinate2D workingCoordinate;
workingCoordinate.latitude=location.coordinate.latitude;
workingCoordinate.longitude=location.coordinate.longitude;
NSLog(#"loop = %f,%f",workingCoordinate.latitude, workingCoordinate.longitude);
MKMapPoint point = MKMapPointForCoordinate(workingCoordinate);
pointArr[idx] = point;
}
// create the polyline based on the array of points.
self.routeLine = [MKPolyline polylineWithPoints:pointArr count:[routes count]];
[mapView addOverlay:self.routeLine];
free(pointArr);
Hope this helps.
google has a algorithm that can encode the locations to string.
for your situation , 14000 coordinates will be encoded to a String nearly 14000 length.
then put the String into sqlite.
it will accelerate the speed to get data from DB

MKMapView randomly panning to Antarctica

I have an app set in San Francisco, which plots points on a map. Once the points are plotted, I use this code to zoom and pan the map to fit all the points:
//BASE_RADIUS = 0.0144927536
- (MKCoordinateRegion)regionFromLocations:(NSArray*)locations {
if([locations count] <= 0) {
MKCoordinateRegion region = self.mapView.region;
return region;
}
CLLocationCoordinate2D upper = [[locations objectAtIndex:0] coordinate];
CLLocationCoordinate2D lower = [[locations objectAtIndex:0] coordinate];
// FIND LIMITS
for(MapPinModel *eachLocation in locations) {
if([eachLocation coordinate].latitude > upper.latitude) upper.latitude = [eachLocation coordinate].latitude;
if([eachLocation coordinate].latitude < lower.latitude) lower.latitude = [eachLocation coordinate].latitude;
if([eachLocation coordinate].longitude > upper.longitude) upper.longitude = [eachLocation coordinate].longitude;
if([eachLocation coordinate].longitude < lower.longitude) lower.longitude = [eachLocation coordinate].longitude;
}
// FIND REGION
MKCoordinateSpan locationSpan;
locationSpan.latitudeDelta = upper.latitude - lower.latitude;
locationSpan.longitudeDelta = upper.longitude - lower.longitude;
if(locationSpan.latitudeDelta < BASE_RADIUS) { locationSpan.latitudeDelta = BASE_RADIUS; }
if(locationSpan.longitudeDelta < BASE_RADIUS) { locationSpan.longitudeDelta = BASE_RADIUS; } //the smallest it gets is a mile.
CLLocationCoordinate2D locationCenter;
locationCenter.latitude = (upper.latitude + lower.latitude) / 2;
locationCenter.longitude = (upper.longitude + lower.longitude) / 2;
MKCoordinateRegion region = MKCoordinateRegionMake(locationCenter, locationSpan);
return region;
}
I then zoom the map to this region using:
MKCoordinateRegion region = [self regionFromLocations:_data];
[mapView setRegion:region animated:YES]; //shows all the pins
This works about 95% of the time. The other five percent, the map zooms to Antarctica. The strange thing is, I've caught the zoom in the debugger once, and the coordinates are inside San Francisco. Any ideas on what may be happening here?

MKMAPView not showing map

I am using follwing code to show image of the given lat lon value but it gives error
NSString*lat=latitude;
NSString*longi=longitude;
mapView = [[MKMapView alloc] initWithFrame:self.view.bounds];
mapView.mapType = MKMapTypeHybrid;mapView.mapType=MKMapTypeHybrid;
double _lat = [lat doubleValue];
double _lng = [longi doubleValue];
CLLocationCoordinate2D coord = (CLLocationCoordinate2D){_lat, _lng};
MKCoordinateSpan span = (MKCoordinateSpan){0.2, 0.2};
MKCoordinateRegion region = (MKCoordinateRegion){coord, span};
[mapView setRegion:region];
[self.view addSubview:mapView];
Few questions:
NSString*lat=latitude;, what is your latitute, could it be CGFloat or other type?
Could you print the error message that you see?
Have you use the debugger, at which line of code it crashes? I suggest place a breakpoint at the line where: NSString*lat=latitude;
You need to provide more information of the error (if there is anything from the log). "Give and you will receive", thats what my mentor always teach me. Give more information, and you will receive the answer you want.

How to calculate the distance between two places using Haversine Formula in iPhone?

I want to calculate the distance between two places using Haversine formula. Actually i am having the latitude and longitude values of two places. Now i want to calculate the distance between that places using Haversine formula.
For Eg:
First Place:
"lat" : 12.97159870,
"lng" : -77.59456270
Second Place:
"lat" : 9.915996999999999,
"lng" : -78.1218470
},
Now i want to calculate the distance using Haversine Formula.
Please help me out.
Thanks!
You can use CLLocation class (CoreLocation.framework) method's distanceFromLocation:(CLLocation*)loc;
CLLocation *locA = [[CLLocation alloc] initWithLatitude:lat1 longitude:long1];
CLLocation *locB = [[CLLocation alloc] initWithLatitude:lat2 longitude:long2];
CLLocationDistance distance = [locA distanceFromLocation:locB];
[locA release]; [locB release];
iOS provides standard means to calculate the distance between 2 geographic locations - you need to use CLLocation class for that:
#import <CoreLocation/CoreLocation.h>
CLLocation *loc1 = [[[CLLocation alloc] initWithLatitude:12.97159870 longitude:-77.59456270] autorelease];
CLLocation *loc2 = [[[CLLocation alloc] initWithLatitude: 9.915996 longitude:-78.1218470] autorelease];
double distance = [loc1 distanceFromLocation: loc2];
You'll also need to add CoreLocation.framework to link with your project.
// Copy library to your project https://github.com/heycarsten/haversine-objc. Then you get distance between two location by using
Haversine *hvs = [[Haversine alloc]initWithLat1:Lati1 lon1:Longi1 lat2:Lati2 lon2:Longi2];
// Getting Distance using Math Formula..
double dLat1InRad = DEGREES_TO_RADIANS(Lati1);
double dLong1InRad = DEGREES_TO_RADIANS(Longi1);
double dLat2InRad = DEGREES_TO_RADIANS(Lati2);
double dLong2InRad = DEGREES_TO_RADIANS(Longi2);
double dLongitude = dLong2InRad - dLong1InRad;
double dLatitude = dLat2InRad - dLat1InRad;
double a = pow(sin(dLatitude/2.0), 2)+ cos(dLat1InRad) * cos(dLat2InRad) * pow(sin(dLongitude / 2.0), 2);
double c = 2.0 * asin(sqrt(a));
const double kEarthRadiusKms = 6376.5;
double dDistance = kEarthRadiusKms * c;

getDistanceFrom Vs distanceFromLocation

I'm having a problem with getDistanceFrom and distanceFromLocation. getDistanceFrom is deprecated iOS 4.1 and distanceFromLocation is not available in 3.1.3, how do I get around this problem.
Has anyone else come across this problem ?
My code at the moment is:
CLLocationDistance kilometers;
if ([currentLocation respondsToSelector: #selector(distanceFromLocation:)])
{
kilometers = [currentLocation distanceFromLocation:previousLocation] / 1000;
}
else
{
//kilometers = [currentLocation getDistanceFrom:previousLocation] / 1000;
kilometers = [currentLocation performSelector: #selector(getDistanceFrom:) withObject: previousLocation] / 1000;
}
Depending on which iOS I compile with I'm getting 'invalid operands to binary' on the lines:
kilometers = [currentLocation distanceFromLocation:previousLocation] / 1000;
kilometers = [currentLocation performSelector: #selector(getDistanceFrom:) withObject: previousLocation] / 1000;
Regards,
Stephen
There's a blog post by Cédric Luthi which has a pretty good solution to this.
In short, you need to enter the following into your main.m and make sure to #import <objc/runtime.h> :
Method getDistanceFrom = class_getInstanceMethod([CLLocation class], #selector(getDistanceFrom:));
class_addMethod([CLLocation class], #selector(distanceFromLocation:), method_getImplementation(getDistanceFrom), method_getTypeEncoding(getDistanceFrom));
You can then use distanceFromLocation: on any OS.
I use my own method. It is MUCH faster and can be used on both iOS 3.x and iOS 4.x.
- (double)distanceFrom:(CLLocationCoordinate2D)locationA to:(CLLocationCoordinate2D)locationB
{
double R = 6368500.0; // in meters
double lat1 = locationA.latitude*M_PI/180.0;
double lon1 = locationA.longitude*M_PI/180.0;
double lat2 = locationB.latitude*M_PI/180.0;
double lon2 = locationB.longitude*M_PI/180.0;
return acos(sin(lat1) * sin(lat2) +
cos(lat1) * cos(lat2) *
cos(lon2 - lon1)) * R;
}