Рисование линий наложения большого круга в MKMapView
Я пытаюсь нарисовать линию большого круга между двумя точками широты / долготы на MKMapView. Это линия, которая будет выглядеть закругленной ("прямая" линия на глобусе) и лучше всего визуализируется здесь. На самом деле этот очень странный сайт WordPress, похоже, начинает описывать, как именно это сделать, но он заканчивается внезапно после первых нескольких шагов.
Читая в документации Apple, я вижу
В iOS 4.0 и более поздних версиях вы также можете использовать проекционные координаты карты вместо областей, чтобы указать некоторые значения. Когда вы проецируете изогнутую поверхность земного шара на плоскую поверхность, вы получаете двумерную версию карты, где линии долготы кажутся параллельными. Местоположение и расстояния на этой карте указываются с использованием типов данных MKMapPoint, MKMapSize и MKMapRect. Вы можете использовать эти типы данных, чтобы указать видимую область карты и указать местоположение наложений.
Как я применил бы это к оверлею Большого Круга, я не уверен. Кто-нибудь может помочь?
4 ответа
Я реализовал это, чтобы нарисовать большой круговой маршрут для самолетов, летящих между двумя аэропортами с использованием 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];
}
}
Вы создали оверлей (ы), теперь вам просто нужно предоставить 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;
}
Надеюсь это поможет.
Это поздно в игре, но стоит упомянуть 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
}
}
Это может быть достигнуто путем создания подкласса класса MKOverlayPathView. Вам необходимо переопределить метод (void)createPath, и, в основном, вы можете использовать UIBezierPath для создания дуги или создать дугу в качестве пути напрямую, что возможно, но я этого еще не сделал.
После того, как вы определили путь к методу, вам нужно установить свойство пути класса с только что созданным путем. Таким образом, рендеринг пути будет выполняться автоматически.
Надеюсь это поможет.
Параметр numsegs должен изменяться в зависимости от уровня масштабирования карты и расстояния между двумя точками. Координаты широты / долготы двух точек можно преобразовать в координаты пикселей. Таким образом, параметр numsegs можно рассматривать как функцию разностей пикселей.