Рисование линий наложения большого круга в MKMapView

#ios #mkmapview #great-circle

#iOS #mkmapview #большой круг

Вопрос:

Я пытаюсь нарисовать линию большого круга между двумя точками широты / lon в MKMapView. Это линия, которая выглядела бы закругленной («прямая» линия на глобусе) и лучше всего визуализируется здесь. На самом деле этот очень странный сайт WordPress, кажется, начинает описывать, как именно это сделать, но он резко заканчивается после первых нескольких шагов.

Читая документацию Apple, я вижу

В iOS 4.0 и более поздних версиях вы также можете использовать координаты проецируемой карты вместо регионов для указания некоторых значений. Когда вы проецируете изогнутую поверхность земного шара на плоскую поверхность, вы получаете двумерную версию карты, где линии долготы кажутся параллельными. Местоположения и расстояния на этой карте задаются с использованием типов данных MKMapPoint, MKMapSize и MKMapRect. Вы можете использовать эти типы данных для указания видимой области карты и при указании местоположения наложений.

Как бы я применил это к наложению большого круга, я не уверен. Кто-нибудь может помочь?

Комментарии:

1. В качестве продолжения я еще не нашел решения и отошел от MKMapView. Я использовал некоторый JavaScript для интерфейса AJAX Bing Maps (версия 7) и намерен отображать карту в WebView.

Ответ №1:

Я реализовал это для рисования маршрута большого круга для самолетов, следующих между двумя аэропортами, используя 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 amp;amp; prevCoord.longitude > -180  amp;amp; prevCoord.longitude < 0 
       amp;amp; coord.longitude > 170 amp;amp; coord.longitude < 180 amp;amp; 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];
}

}
  

Теперь вы создали наложения, теперь вам просто нужно предоставить MKOverlayView в 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;
}
  

Надеюсь, это поможет.

Скриншот http://s1-03.twitpicproxy.com/photos/large/489178500.png

Ответ №2:

Это поздняя версия игры, но о ней стоит упомянуть MKGeodesicPolyline , новая с iOS 7.0, которая «прокладывает кратчайший путь по поверхности Земли».

Благодаря этому становится просто создавать и добавлять MKPolylineOverlay геодезическую полилинию типа.

 points = [CLLocationCoordinate2DMake(27.123, 85.765),
                  CLLocationCoordinate2DMake(41.444, 106.987)]
geodesic = MKGeodesicPolyline(coordinates: points, count: 2)
mapView.add(geodesic)
  

не забудьте включить средство визуализации и предоставить MapView делегат:

 //MARK: - MKMapView Delegate Method
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
    if overlay is MKGeodesicPolyline {
        let polylineRenderer = MKPolylineRenderer(overlay: overlay)
        polylineRenderer.strokeColor = UIColor.white
        polylineRenderer.lineWidth = 2.5
        return polylineRenderer
    }
}
  

Ответ №3:

Этого можно достичь, создав подкласс класса MKOverlayPathView. Вам нужно переопределить метод createPath (void), и в принципе вы могли бы использовать UIBezierPath для создания дуги или напрямую создать дугу как path, что возможно, но я этого еще не сделал.

Как только вы определите путь в методе, вам нужно установить свойство path класса с вновь созданным путем. Таким образом, рендеринг пути будет выполнен автоматически.

Надеюсь, это поможет.

Ответ №4:

Параметр numsegs должен изменяться в зависимости от уровня масштабирования карты и расстояния между двумя точками. Координаты широты / долготы двух точек могут быть преобразованы в пиксельную координату. Таким образом, параметр numsegs можно рассматривать как функцию различий в пикселях.