Определить, находится ли точка внутри оверлея MKPolygon
Я хочу быть в состоянии сказать, находится ли нажатие в пределах MKPolygon.
У меня есть МК Полигон:
CLLocationCoordinate2D points[4];
points[0] = CLLocationCoordinate2DMake(41.000512, -109.050116);
points[1] = CLLocationCoordinate2DMake(41.002371, -102.052066);
points[2] = CLLocationCoordinate2DMake(36.993076, -102.041981);
points[3] = CLLocationCoordinate2DMake(36.99892, -109.045267);
MKPolygon* poly = [MKPolygon polygonWithCoordinates:points count:4];
[self.mapView addOverlay:poly];
//create UIGestureRecognizer to detect a tap
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(foundTap:)];
tapRecognizer.numberOfTapsRequired = 1;
tapRecognizer.numberOfTouchesRequired = 1;
[self.mapView addGestureRecognizer:tapRecognizer];
это просто базовая схема штата Колорадо.
Я установил преобразование в долготу / долготу:
-(IBAction)foundTap:(UITapGestureRecognizer *)recognizer
{
CGPoint point = [recognizer locationInView:self.mapView];
CLLocationCoordinate2D tapPoint = [self.mapView convertPoint:point toCoordinateFromView:self.view];
}
но я не уверен, как технология, если моя точка касания находится в пределах MKPolygon. Кажется, что нет способа сделать эту проверку, поэтому я предполагаю, что мне нужно конвертировать MKPolygon в CGRect и использовать CGRectContainsPoint.
У MKPolygon есть свойство.points, но я не могу их вернуть.
какие-либо предложения?
РЕДАКТИРОВАТЬ:
Оба решения ниже работают в iOS 6 или ниже, но ломаются в iOS 7. В iOS 7 polygon.path
собственность всегда возвращается NULL
, Г-жа Анна была достаточно любезна, чтобы найти решение в другом вопросе здесь. Это включает в себя создание собственного пути из точек многоугольника, чтобы перейти в CGPathContainsPoint()
,
изображение моего многоугольника:
6 ответов
Я создал эту категорию MKPolygon на случай, если кто-то захочет ее использовать. Кажется, работает хорошо. Вы должны учесть внутренние многоугольники (то есть отверстия в многоугольнике):
@interface MKPolygon (PointInPolygon)
-(BOOL) pointInPolygon:(CLLocationCoordinate2D) point mapView: (MKMapView*) mapView;
@end
@implementation MKPolygon (PointInPolygon)
-(BOOL) pointInPolygon:(CLLocationCoordinate2D) point mapView: (MKMapView*) mapView {
MKMapPoint mapPoint = MKMapPointForCoordinate(point);
MKPolygonView * polygonView = (MKPolygonView*)[mapView viewForOverlay:self];
CGPoint polygonViewPoint = [polygonView pointForMapPoint:mapPoint];
return CGPathContainsPoint(polygonView.path, NULL, polygonViewPoint, NO) &&
![self pointInInteriorPolygons:point mapView:mapView];
}
-(BOOL) pointInInteriorPolygons:(CLLocationCoordinate2D) point mapView: (MKMapView*) mapView {
return [self pointInInteriorPolygonIndex:0 point:point mapView:mapView];
}
-(BOOL) pointInInteriorPolygonIndex:(int) index point:(CLLocationCoordinate2D) point mapView: (MKMapView*) mapView {
if(index >= [self.interiorPolygons count])
return NO;
return [[self.interiorPolygons objectAtIndex:index] pointInPolygon:point mapView:mapView] || [self pointInInteriorPolygonIndex:(index+1) point:point mapView:mapView];
}
@end
Ваш foundTap
метод:
-(IBAction)foundTap:(UITapGestureRecognizer *)recognizer
{
CGPoint point = [recognizer locationInView:self.mapView];
CLLocationCoordinate2D tapPoint = [self.mapView convertPoint:point toCoordinateFromView:self.view];
[self pointInsideOverlay:tapPoint];
if (isInside)
{
....
}
}
Вот метод для вызова из предыдущего, чтобы проверить, находится ли точка внутри оверлея:
-(void)pointInsideOverlay:(CLLocationCoordinate2D )tapPoint
{
isInside = FALSE;
MKPolygonView *polygonView = (MKPolygonView *)[mapView viewForOverlay:polygonOverlay];
MKMapPoint mapPoint = MKMapPointForCoordinate(tapPoint);
CGPoint polygonViewPoint = [polygonView pointForMapPoint:mapPoint];
BOOL mapCoordinateIsInPolygon = CGPathContainsPoint(polygonView.path, NULL, polygonViewPoint, NO);
if ( !mapCoordinateIsInPolygon )
//we are finding points that are inside the overlay
{
isInside = TRUE;
}
}
Вот обновленная версия swift 3 благодаря @Steve Stomp
extension MKPolygon {
func contains(coordinate: CLLocationCoordinate2D) -> Bool {
let polygonRenderer = MKPolygonRenderer(polygon: self)
let currentMapPoint: MKMapPoint = MKMapPointForCoordinate(coordinate)
let polygonViewPoint: CGPoint = polygonRenderer.point(for: currentMapPoint)
return polygonRenderer.path.contains(polygonViewPoint)
}
}
Это сработало для меня в Swift:
extension MKPolygon {
func isCoordinateInsidePolyon(coordinate: CLLocationCoordinate2D) -> Bool {
var inside = false
let polygonRenderer = MKPolygonRenderer(polygon: self)
let currentMapPoint: MKMapPoint = MKMapPointForCoordinate(coordinate)
let polygonViewPoint: CGPoint = polygonRenderer.pointForMapPoint(currentMapPoint)
if CGPathContainsPoint(polygonRenderer.path, nil, polygonViewPoint, true) {
inside = true
}
return inside
}
}
Я получаю очки данных MKPolygon из XML-файла в строке. Я анализирую строку данных в массиве точек и использую подход, предоставленный в http://alienryderflex.com/polygon/
Меня устраивает....
-(BOOL)isPoint:(CLLocationCoordinate2D)findLocation inPloygon:(NSArray*)polygon{
NSMutableArray *tempPolygon=[NSMutableArray arrayWithArray:polygon];
int i, j=(int)tempPolygon.count-1 ;
bool oddNodes=NO;
double x=findLocation.latitude;
double y=findLocation.longitude;
for (i=0; i<tempPolygon.count; i++) {
NSString*coordString=[tempPolygon objectAtIndex:i];
NSArray*pointsOfCoordString=[coordString componentsSeparatedByString:@","];
CLLocationCoordinate2D point=CLLocationCoordinate2DMake([[pointsOfCoordString objectAtIndex:1] doubleValue], [[pointsOfCoordString objectAtIndex:0] doubleValue]);
NSString*nextCoordString=[tempPolygon objectAtIndex:j];
NSArray*nextPointsOfCoordString=[nextCoordString componentsSeparatedByString:@","];
CLLocationCoordinate2D nextPoint=CLLocationCoordinate2DMake([[nextPointsOfCoordString objectAtIndex:1] doubleValue], [[nextPointsOfCoordString objectAtIndex:0] doubleValue]);
if ((point.longitude<y && nextPoint.longitude>=y)
|| (nextPoint.longitude<y && point.longitude>=y)) {
if (point.latitude+(y-point.longitude)/(nextPoint.longitude-point.longitude)*(nextPoint.latitude-point.latitude)<x) {
oddNodes=!oddNodes; }}
j=i; }
return oddNodes;
}
мои объекты многоугольника (NSArray) находятся в строке, например, @"-89.860021,44.944266,0"
Определение того, находится ли точка в произвольном многоугольнике, нетривиально, и неудивительно, что Apple не поставляет ее как часть MKPolygon. Вы можете получить доступ к точкам, что позволяет итерировать по краям.
Чтобы определить, находится ли точка p внутри многоугольника s, рассмотрим каждое ребро как направленный отрезок в s. Если луч из p в любом фиксированном направлении (обычно параллельном оси X или Y) пересекает сегмент, возьмите знак компонента Z перекрестного произведения луча с этим направленным сегментом линии. Если компонент Z> 0, добавьте 1 к счетчику. Если это < 0, вычтите 1. Хитрость в реализации этого состоит в том, чтобы избежать проблем, когда край почти параллелен лучу, или когда луч проходит через вершину (он должен считаться только один раз, а не один раз для каждого края),
Когда вы сделаете это для всех ребер в s, вы посчитаете, сколько раз ваш луч пересекает контур многоугольника, где, если ребро проходило слева направо, вы добавили, и если оно двигалось справа налево Вы вычитали. Если полученная сумма равна нулю, вы находитесь за пределами многоугольника. В противном случае вы внутри.
Возможны многочисленные оптимизации. Один из таких способов - выполнить быстрый ограничивающий тест перед более полным тестом. Другой - иметь структуру данных с границами по всем ребрам, чтобы тривиально отбрасывать ребра, которые не пересекают луч.
Изменить: компонент Z A X B (перекрестное произведение A с B) определяется как:
a.x * b.y - a.y * b.x
так как все, что вас волнует, это знак, вы можете проверить
a.x * b.y > a.y * b.x