Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Drawing Great Circle overlay lines on an MKMapView

I'm trying to draw a Great Circle line between two lat/lon points on an MKMapView. This is a line that would appear rounded (a 'straight' line on a globe) and is best visualized here. In fact this very odd WordPress site seems to begin to describe exactly how to do this, but it ends abruptly after the first few steps.

Reading in Apple's documentation I see

In iOS 4.0 and later, you can also use projected map coordinates instead of regions to specify some values. When you project the curved surface of the globe onto a flat surface, you get a two-dimensional version of a map where longitude lines appear to be parallel. Locations and distances on this map are specified using the MKMapPoint, MKMapSize, and MKMapRect data types. You can use these data types to specify the map’s visible region and when specifying the location of overlays.

How I would apply this to a Great Circle overlay I'm not sure. Can anyone help?

like image 929
Ryan Avatar asked Dec 07 '25 12:12

Ryan


1 Answers

I've implemented this for drawing a great circle route for aircraft going between two airports using MKPolyline.

+ (void)createGreatCircleMKPolylineFromPoint:(CLLocationCoordinate2D)point1 
                                     toPoint:(CLLocationCoordinate2D)point2
                                  forMapView:(MKMapView*)mapView
{
double lat1 = point1.latitude;
double lon1 = point1.longitude;
double lat2 = point2.latitude;
double lon2 = point2.longitude;
lat1 = lat1 * (PI/180);
lon1 = lon1 * (PI/180);
lat2 = lat2 * (PI/180);
lon2 = lon2 * (PI/180);
double d = 2 * asin( sqrt(pow(( sin( (lat1-lat2)/2) ), 2) + cos(lat1) * cos(lat2) * pow(( sin( (lon1-lon2)/2) ), 2)));
int numsegs = 100;
CLLocationCoordinate2D *coords = malloc(sizeof(CLLocationCoordinate2D) * numsegs);
double f = 0.0;
for(int i=1; i<=numsegs; i++)
{
    f += 1.0 / (float)numsegs;
    double A=sin((1-f)*d)/sin(d);
    double B=sin(f*d)/sin(d);
    double x = A*cos(lat1) * cos(lon1) +  B * cos(lat2) * cos(lon2);
    double y = A*cos(lat1) * sin(lon1) +  B * cos(lat2) * sin(lon2);
    double z = A*sin(lat1)           +  B*sin(lat2);
    double latr=atan2(z, sqrt(pow(x, 2) + pow(y, 2) ));
    double lonr=atan2(y, x);
    double lat = latr * (180/PI);
    double lon = lonr * (180/PI);
    //        NSLog(@"lat: %f lon: %f", lat, lon);
    CLLocationCoordinate2D loc = CLLocationCoordinate2DMake(lat, lon);
    coords[i - 1] = loc;
}

//check for circling west to east. If the plane is crossing 180, we need
//to draw two lines or else the polyline connects the dots and draws a straight
//line all the way across the map.
CLLocationCoordinate2D prevCoord;
BOOL twoLines = NO;
int numsegs2 = 0;
CLLocationCoordinate2D *coords2;

for(int i=0; i<numsegs; i++)
{
    CLLocationCoordinate2D coord = coords[i];
    if(prevCoord.longitude < -170 && prevCoord.longitude > -180  && prevCoord.longitude < 0 
       && coord.longitude > 170 && coord.longitude < 180 && coord.longitude > 0)
    {
        twoLines = YES;
        coords2 = malloc(sizeof(CLLocationCoordinate2D) * (numsegs - i));
        numsegs2 = numsegs - i;
        for(int j=0; j<numsegs2; j++)
        {
            coords2[j] = coords[i + j];
        }
        break;
    }
    prevCoord = coord;
}

//remove any previously added overlays
[mapView removeOverlays:mapView.overlays];

if(twoLines)
{
    MKPolyline *polyline = [MKPolyline polylineWithCoordinates:coords count:numsegs - numsegs2];
    free(coords);
    [mapView addOverlay:polyline];

    MKPolyline *polyline2 = [MKPolyline polylineWithCoordinates:coords2 count:numsegs2];
    free(coords2);
    [mapView addOverlay:polyline2];
}
else
{
    MKPolyline *polyline = [MKPolyline polylineWithCoordinates:coords count:numsegs];
    free(coords);
    [mapView addOverlay:polyline];
}

}

You've now created the overlay(s), now you just need to provide an MKOverlayView in mapView:viewForOverlay.

- (MKOverlayView*)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay
{
    MKPolyline *polyline = (MKPolyline*)overlay;
    MKPolylineView *view = [[[MKPolylineView alloc] initWithPolyline:polyline] autorelease];
    //choose your line params here
    view.lineWidth = 2;
    view.fillColor = [UIColor blueColor];
    return view;
}

Hope this helps.

Screenshot http://s1-03.twitpicproxy.com/photos/large/489178500.png

like image 87
cberkley Avatar answered Dec 10 '25 00:12

cberkley