detect if a point is inside a MKPolygon overlay

后端 未结 6 1914
余生分开走
余生分开走 2020-12-07 23:44

I want to be able to tell if tap is within a MKPolygon.

I have a MKPolygon:

CLLocationCoordinate2D  points[4];

points[0] = CLLocationCoordinate2DMak         


        
相关标签:
6条回答
  • 2020-12-07 23:50

    I created this MKPolygon category in case anyone wants to use it. Seems to work well. You have to account for the interior polygons (i.e. holes in the polygon):

    @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
    
    0 讨论(0)
  • 2020-12-07 23:52

    I am getting MKPolygon Data points from xml file in string. I parse data string to Array of points and use approach give in http://alienryderflex.com/polygon/

    It works for me....

    -(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;
    
    }
    

    my polygon(NSArray) objects are in string for e.g. @"-89.860021,44.944266,0"

    0 讨论(0)
  • 2020-12-07 23:56

    Your foundTap method:

    -(IBAction)foundTap:(UITapGestureRecognizer *)recognizer
    {
        CGPoint point = [recognizer locationInView:self.mapView];
    
        CLLocationCoordinate2D tapPoint = [self.mapView convertPoint:point toCoordinateFromView:self.view];
    
        [self pointInsideOverlay:tapPoint];
    
        if (isInside) 
         {
           ....
         }
    }
    

    Here is a method to call from the previous to check if the point is inside the overlay:

    -(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;
            }
    }
    
    0 讨论(0)
  • 2020-12-07 23:56

    Determining whether a point is in an arbitrary polygon is non-trivial and it is unsurprising that Apple doesn't supply it as part of MKPolygon. You can access the points, which allows you to iterate over the edges.

    To determine whether a point p is inside a polygon s, consider each edge as a directed line segment in s. If a ray from p in any fixed direction (typically parallel to either the X or Y axis) intersects the segment, take the sign of the Z component of the cross product of the ray with that directed line segment. If the Z component is > 0, add 1 to a counter. If it is < 0, subtract 1. The trick in implementing this is to avoid issues when the edge is nearly parallel to the ray, or when the ray passes through a vertex (it has to count only once, not once for each edge).

    When you have done this for all edges in s, you will have counted the number of times your ray intersects the outline of the polygon, where if the edge was going from left to right you added, and if it was going from right to left, you subtracted. If the resulting sum is zero, you are outside the polygon. Otherwise you are inside it.

    There are numerous optimizations possible. One such is to do a quick bounding box test before the more complete test. Another is to have a data structure with bounds on all the edges to trivially discard edges that do not intersect the ray.

    Edit: the Z component of A X B ( the cross product of A with B ) is given by:

    a.x * b.y - a.y * b.x
    

    since all you care about is the sign, you can check

    a.x * b.y > a.y * b.x
    
    0 讨论(0)
  • 2020-12-08 00:00

    Here is Swift 4.2 updated version thanks to @Steve Stomp

    extension MKPolygon {
        func contain(coor: CLLocationCoordinate2D) -> Bool {
            let polygonRenderer = MKPolygonRenderer(polygon: self)
            let currentMapPoint: MKMapPoint = MKMapPoint(coor)
            let polygonViewPoint: CGPoint = polygonRenderer.point(for: currentMapPoint)
            if polygonRenderer.path == nil {
                return false
            }else{
                return polygonRenderer.path.contains(polygonViewPoint)
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-08 00:06

    This worked for me in #Swift 4.2:

    extension MKPolygon {
        func isCoordinateInsidePolyon(coordinate: CLLocationCoordinate2D) -> Bool {
            let polygonRenderer = MKPolygonRenderer(polygon: self)
            let currentMapPoint: MKMapPoint = MKMapPoint(coor)
            let polygonViewPoint: CGPoint = polygonRenderer.point(for: currentMapPoint)
            if polygonRenderer.path == nil {
                return false
            }else{
                return polygonRenderer.path.contains(polygonViewPoint)
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题