Put MKPointAnnotation on MKDirections at a specific distance - iphone

I'm using MKDirections to draw a route from point A to point B, and I'm looking to put a MKPointAnnotation at specific distance from the starting point.
For example, I have a MKMap with MKDirections drawn from LA to NY. I then would like to place a pin at the 10 mile marker for the route the user has decided to take.
Any thoughts on how to find the lat/long, using the chosen route at a specific distance?
Thanks.
- (void)showDirections:(MKDirectionsResponse *)response
{
NSInteger iDistance = 0;
BOOL bPointinStep = NO;
self.response = response;
for (MKRoute *route in _response.routes)
{
NSLog(#"route.distance: %.0f", route.distance);
responseNumber = responseNumber + 1;
[_mapView addOverlay:route.polyline level:MKOverlayLevelAboveRoads];
for (MKRouteStep *step in route.steps)
{
iDistance = iDistance + step.distance;
NSLog(#"iDistance: %.0ld", (long)iDistance);
NSLog(#"step.distance: %.0f", step.distance);
NSLog(#"%#", step.instructions);
if (iProgress < iDistance && !bPointinStep) {
NSLog(#"pin point is on this step");
bPointinStep = YES;
}
}
}
}
This works, as I'm able to determine which step the pin point should be placed, but my question is on how to determine where within the step.

I'll explain my approach to solve a similar problem.
You can use the property step.polyline.coordinate. It gives you the latitude and longitude of the end point of each step of your route. When your variable iDistance exceeds the 10 miles, the end point of this step (or end point of the previous step) will give an approximate coordinate that you are looking for. In any case you will have the segment containing "10 miles" distance.
To obtain a more accurate coordinate you can perform some simple trigonometric operations between the end point of this segment and the endpoint of the previous segment, to calculate the distance and put the MKPointAnnotation .
I hope it helps you.

Related

Finding closest match of Latitude & Longitude in a list of Lat/Longs

I'm creating an iPhone app with Weather lookup for particular locations and I have the following problem that i'm not sure the best way to tackle.
I have the latitude and longitude of a location and want to find the closest lat/long match from a list of 5000+ locations
The 5000+ locations come from a JSON feed from Met Office Datapoint API and are in the form of a NSArray of NSDictionaries, the NSDictionary includes id, lat, long and name.
I want to match my location to the nearest location from the list from the Met Office and grab the id key value.
Many Thanks in advance
I'm assuming you're using CLLocation objects in this...
- (CLLocation*)closestLocationToLocation:(CLLocation*)currLocation
{
CLLocationDistance minDistance;
CLLocation *closestLocation = nil;
for (CLLocation *location in arrayOfLocations) {
CLLocationDistance distance = [location distanceFromLocation:currLocation];
if (distance <= minDistance
|| closestLocation == nil) {
minDistance = distance;
closestLocation = location;
}
}
//closestLocation is now the location from your array which is closest to the current location or nil if there are no locations in your array.
return closestLocation;
}
There may be a quicker way of doing this but this will get it done.
EDITED to use CLLocation functions
I did a similar thing once (finding all lat/lon Objects surrounding a point with a max. radius) and used the formula given here:
http://www.movable-type.co.uk/scripts/latlong.html
However, that was quite time consuming. So I sort of "boxed" the objects fist. Based on the calculation above (reverted of course) I calculated the latitude and longitude of those coordinates north, west, south and east that had exactly the maximum distance. With hose max and min values (for lat and lon) I queried all objects in question. And only for those I calculated the exact distance and included them in the list of results or excluded them.
However, so far that does not exactly match your problem. But I tried to fasten the calculations even further. For that I said to myself, that I do not need the exact distance from the searched object to mine but it is enogh to know wether it is closer than one of the boxes coordinates. And that part is the one that corresponds well to your question:
Your case could be much easier. Assuming that the locations in question (the shortest once) are close to the one location which you try to assign, all this complex math may not play a role. You do not need the exact distance. What you need is the cosest one. For that I would assume that the earth is flat and that the distances between longitudes (or latitude) are linear. That is not true of course but should be good enough to figure out, which of those is the closest.
Going from there you could use pythagoras.
Distance = sqrt(sqr(difference-in-lat) + sqr(difference-in-lon));
For the mere purpose of comparing the distances and finding the shortest, you could even replace the time consuming square route with a much faster sqare operation.
Square-Of-Distance = sqr(difference-in-lat) + sqr(difference-in-lon).
And then compare the various Square-Of-Distance rather than the Distance. The result will be the same but much faster.
BTW, that was a PHP projects. That's why I cannot provide sample code but just explain the algorihm.
I'd suggest something like this:
NSMutableArray *tempArray = [NSMutableArray new];
for (NSMutableDictionary *location in yourArrayOfLocations){
CLLocation coord;
coord.latitude = [location objectForKey:#"latitude"];
coord.longitude = [location objectForKey:#"longitude"];
[location setValue:[usersLocation distanceFromLocation:coord] forKey:#"distance"];
[tempArray addObject:location];
}
// Now sort the array
NSArray *sortedArray = [tempArray sortedArrayUsingComparator:^(id o1, id o2) {
NSDictionary *location1 = (NSDictionary *)o1;
NSDictionary *location2 = (NSDictionary *)o2;
return [[location1 objectForKey:#"distance"] compare:[location2 objectForKey:#"distance"]];
}];
[tempArray release];
Now you have an array ordered by distance. You can use the object at index 0, as it is the closest to the user's position.
Good Luck!

Optimization for MKAnnotations and Core Data

I have some locations ( in this case >3000 ) stored with Core Data. Once I open the map, I fetch the locations and store them in an array. Each time the mapview region is changed I call a function which will calculate which annotations are visible in the current visibleMaprect and filter them by pixel-distance. ( I know there would be more complex optimizations, like quadtrees, but I would not really implement it right now, if it's not extremely necessary ).
This is my code :
//locations is an array of NSManagedObjects
for (int i =0 ; i < [locations count]; i++)
{
// managed object class for faster access, valueforkey takes ages ...
LocationEntity * thisLocation = [locations objectAtIndex:i];
CLLocationCoordinate2D coord = CLLocationCoordinate2DMake( [thisLocation.latitude doubleValue], [thisLocation.longitude doubleValue]) ;
// mapRect is mapView.visibleMapRect
BOOL isOnScreen = MKMapRectContainsPoint(mapRect, MKMapPointForCoordinate(coord));
if (isOnScreen)
{
CGPoint cgp = [mapView convertCoordinate:coord toPointToView:mapView];
// compare the distance to already existing annotations
for (int idx = 0; idx < [annotations count] && hasEnoughDistance; idx++)
{
CGPoint cgp_prev = [mapView convertCoordinate:[[annotations objectAtIndex:idx] coordinate] toPointToView:mapView];
if ( getDist(cgp, cgp_prev) < dist ) hasEnoughDistance = FALSE;
}
}
if (hasEnoughDistance)
// if it's ok, create the annotation, add to an array and after the for add all to the map
}
The map is freezing for a few seconds after each zoom/movement.
I checked with time profiler and the simple obtainment of coordinates is taking sometimes 1 whole second, sometimes just 0.1, even though the coordinates are indexed attributes in my model... Also these type of lines seem to take ages :
CGPoint cgp = [mapView convertCoordinate:coord toPointToView:mapView];
Any suggestions how could I calculate the pixel/point distance between two annotations/coordinates without going through this function ? Or any optimization suggestions for Core Data?
Thanks :)
Ok, I sort of missed the not having them too close bit from your explanation. The conversion between the coordinates is very slow. The way you can alleviate it is to precompute the coordinates into map points with MKMapPointForCoordinate and store them persistently - they only depend on the coordinates. Then you can quickly calculate the distance between the map points of two annotations, scale it depending on your current zoom level of the map and this will quite closely relate to the actual distance on the screen. It should be accurate enough and will be much faster.
I would recommend calculating the squared distance and comparing it to squared dist. You would be saving a lot on the sqrt().
If you still get boggled down on getDist() (or getSqDist()) you could either go for a kd tree or use the Accelerate Framework to do the calculations. I've done the latter when I needed to calculate distances between many points and the speedup was very good. But the details of this is an another cup of tea. Let me know if you need any help with that.
The fact that your coordinates are indexed would only help if you actually searched for annotations by the coordinates, so it won't help if you just look through all of them.
A way of dealing with long loading times from CoreData would be to try making your annotations as lightweight as possible, so only storing the coordinates and map points. Then you could have a way of getting the rest of the annotation data as needed. This could be done with the proxy pattern.
One more thing. Fast enumeration might be faster and is better practice as well, so
for(LocationEntity* thisLocation in locations)
instead of
for (int i =0 ; i < [locations count]; i++)

How to get the nearest area from a list of locations?

I have a API which returns a list of different areas within a city with the weather at that area. I want to get the nearest area based on my current location.
API returns
Area
Latitude
Longitude
Weather
How to find the nearest area based on this data?
You would have to create CLLocation objects for all the areas, plus one for the current location of the user. Then use a loop similar to the one below to get the closest location:
NSArray *allLocations; // this array contains all CLLocation objects for the locations from the API you use
CLLocation *currentUserLocation;
CLLocation *closestLocation;
CLLocationDistance closestLocationDistance = -1;
for (CLLocation *location in allLocations) {
if (!closestLocation) {
closestLocation = location;
closestLocationDistance = [currentUserLocation distanceFromLocation:location];
continue;
}
CLLocationDistance currentDistance = [currentUserLocation distanceFromLocation:location];
if (currentDistance < closestLocationDistance) {
closestLocation = location;
closestLocationDistance = currentDistance;
}
}
One thing to note is that this method of calculating a distance uses a direct line between point A and point B. No roads or other geographical objects are taken into account.

Objective C - iPhone comparing 2 CLLocations /GPS coordinates

Ok , so thanks to Claus Broch I made some progress with comparing two GPS locations. I need to be able to say "IF currentlocation IS EQUAL TO (any GPS position from a list ) THEN do something
My code at the moment is :
CLLocationCoordinate2D bonusOne;
bonusOne.latitude = 37.331689;
bonusOne.longitude = -122.030731;
Which is the simulators GPS location at Infinite Loop
CLLocation *loc1 = [[CLLocation alloc] initWithLatitude:bonusOne.latitude longitude:bonusOne.longitude];
double distance = [loc1 getDistanceFrom:newLocation];
if(distance <= 10000000) {
Then do something
}
Any number under 10000000 and it assumes that there is no match.
Yes, you can use getDistanceFrom to get the distance between two points. The distance is in meters. You can use that comparison, compared with the current horizontal accuracy from the location manager, to determine if you are roughly at the "bonus one" position.
CLLocation *bonusLocation = [[CLLocation alloc] initWithLatitude:bonusOne.latitude longitude:bonusOne.longitude];
float distance = [bonusLocation getDistanceFrom:newLocation];
float threshold = 2 * [newLocation horizontalAccuracy]; // Or whatever you like
if(distance <= threshold) {
// You are at the bonus location.
}

how to calculate two coordinates distance in objective c?

as title how to? i have tried the code from google earth, but seem like the result is different with the google map calculation result. below provided the code i did
-(double)GetDistance:(double)lat1 long1:(double)lng1 la2:(double)lat2 long2:(double)lng2 {
//NSLog(#"latitude 1:%.7f,longitude1:%.7f,latitude2:%.7f,longtitude2:%.7f",lat1,lng1,lat2,lng2);
double radLat1 = [self rad:lat1];
double radLat2 = [self rad:lat2];
double a = radLat1 - radLat2;
double b = [self rad:lng1] -[self rad:lng2];
double s = 2 * asin(sqrt(pow(sin(a/2),2) + cos(radLat1)*cos(radLat2)*pow(sin(b/2),2)));
s = s * EARTH_RADIUS;
s = round(s * 10000) / 10000;
return s;
}
-(double)rad:(double)d
{
return d *3.14159265 / 180.0;
}
the EARTH_RADIUS value is 6378.138
by using this function by provided two coordinates the result come out is 4.5kM
but when i use google map get direction between two same coordinates, it show me the distance is about 8km
can anyone help to point out the problem of my code?
Since this is tagged iPhone, why not use the built-in distance function rather than rolling your own? location1 and location2 are CLLocation objects.
CLLocationDistance distance = [location1 getDistanceFrom:location2];
Here is a simple code (supposing you just have latitude and longitude of the two points)
CLLocation *startLocation = [[CLLocation alloc] initWithLatitude:startLatitude longitude:startLongitude];
CLLocation *endLocation = [[CLLocation alloc] initWithLatitude:endLatitude longitude:endLongitude];
CLLocationDistance distance = [startLocation distanceFromLocation:endLocation]; // aka double
Don't forget to add MapKit Framework to your project, and import MapKit in your file :
#import <MapKit/MapKit.h>
Google Maps is likely to be giving you the driving distance, whereas the great circle equation you have listed is going to be the straight line surface distance. If there was a straight line surface road directly from point A to point B, Google Maps would likely give you the same distance as the equation you have there.
Since
getDistanceFrom:
isDeprecated
Try use the
[newLocation distanceFromLocation:oldLocation
You should be able to use the google API directly to calculate either great circle distance or driving distance depending on your application needs.
See GLatLong::distanceFrom and GDirections::getDistance.