-->

Determine whether a CLLocationCoordinate2D is with

2019-05-31 15:49发布

问题:

I am trying to find a simple method to determine whether a CLLocationCoordinate2D lies within the boundaries of an arbitrary shape defined by a series of other CLLocationCoordinate2D's. The shapes may be large enough that great-circle paths need to be considered.

CL used to have a circular region and the containsCoordinate: call to test against, but this has been deprecated in iOS7 and the dox do not contain a hint of what might replace it. I cannot find any other examples, notably one that works on polygons.

There are many similar questions here on SO, but they are not related to iOS specifically, and again, I can't seem to find one that works generally on great-circle polys.

回答1:

Here's an example (using Algonquin Provincial Park) of an approach that may work for you.

To use CGPathContainsPoint for this purpose, an MKMapView is not required.

Nor is it necessary to create an MKPolygon or even to use the CLLocationCoordinate2D or MKMapPoint structs. They just make the code easier to understand.

The screenshot below was created from the data only for illustration purposes.

int numberOfCoordinates = 10;

//This example draws a crude polygon with 10 coordinates
//around Algonquin Provincial Park.  Use as many coordinates
//as you like to achieve the accuracy you require.
CLLocationCoordinate2D algonquinParkCoordinates[numberOfCoordinates];
algonquinParkCoordinates[0] = CLLocationCoordinate2DMake(46.105,    -79.4);
algonquinParkCoordinates[1] = CLLocationCoordinate2DMake(46.15487,  -78.80759);
algonquinParkCoordinates[2] = CLLocationCoordinate2DMake(46.16629,  -78.12095);
algonquinParkCoordinates[3] = CLLocationCoordinate2DMake(46.11964,  -77.70896);
algonquinParkCoordinates[4] = CLLocationCoordinate2DMake(45.74140,  -77.45627);
algonquinParkCoordinates[5] = CLLocationCoordinate2DMake(45.52630,  -78.22532);
algonquinParkCoordinates[6] = CLLocationCoordinate2DMake(45.18662,  -78.06601);
algonquinParkCoordinates[7] = CLLocationCoordinate2DMake(45.11689,  -78.29123);
algonquinParkCoordinates[8] = CLLocationCoordinate2DMake(45.42230,  -78.69773);
algonquinParkCoordinates[9] = CLLocationCoordinate2DMake(45.35672,  -78.90647);

//Create CGPath from the above coordinates...
CGMutablePathRef mpr = CGPathCreateMutable();

for (int p=0; p < numberOfCoordinates; p++)
{
    CLLocationCoordinate2D c = algonquinParkCoordinates[p];

    if (p == 0)
        CGPathMoveToPoint(mpr, NULL, c.longitude, c.latitude);
    else
        CGPathAddLineToPoint(mpr, NULL, c.longitude, c.latitude);
}

//set up some test coordinates and test them...
int numberOfTests = 7;
CLLocationCoordinate2D testCoordinates[numberOfTests];
testCoordinates[0] = CLLocationCoordinate2DMake(45.5, -78.5);
testCoordinates[1] = CLLocationCoordinate2DMake(45.3, -79.1);
testCoordinates[2] = CLLocationCoordinate2DMake(45.1, -77.9);
testCoordinates[3] = CLLocationCoordinate2DMake(47.3, -79.6);
testCoordinates[4] = CLLocationCoordinate2DMake(45.5, -78.7);
testCoordinates[5] = CLLocationCoordinate2DMake(46.8, -78.4);
testCoordinates[6] = CLLocationCoordinate2DMake(46.1, -78.2);

for (int t=0; t < numberOfTests; t++)
{
    CGPoint testCGPoint = CGPointMake(testCoordinates[t].longitude, testCoordinates[t].latitude);

    BOOL tcInPolygon = CGPathContainsPoint(mpr, NULL, testCGPoint, FALSE);

    NSLog(@"tc[%d] (%f,%f) in polygon = %@",
          t,
          testCoordinates[t].latitude,
          testCoordinates[t].longitude,
          (tcInPolygon ? @"Yes" : @"No"));
}

CGPathRelease(mpr);

Here are the results of the above test:

tc[0] (45.500000,-78.500000) in polygon = Yes
tc[1] (45.300000,-79.100000) in polygon = No
tc[2] (45.100000,-77.900000) in polygon = No
tc[3] (47.300000,-79.600000) in polygon = No
tc[4] (45.500000,-78.700000) in polygon = Yes
tc[5] (46.800000,-78.400000) in polygon = No
tc[6] (46.100000,-78.200000) in polygon = Yes


This screenshot is to illustrate the data only (actual MKMapView is not required to run the code above):



回答2:

Anna's solution converted to Swift 3.0:

extension CLLocationCoordinate2D {

    func contained(by vertices: [CLLocationCoordinate2D]) -> Bool {
        let path = CGMutablePath()

        for vertex in vertices {
            if path.isEmpty {
                path.move(to: CGPoint(x: vertex.longitude, y: vertex.latitude))
            } else {
                path.addLine(to: CGPoint(x: vertex.longitude, y: vertex.latitude))
            }
        }

        let point = CGPoint(x: self.longitude, y: self.latitude)
        return path.contains(point)
    }
}