简体   繁体   中英

How can I check if a CLLocationCoordinate2D is inside four CLLocationCoordinate2D Square? in Objective C with Google Maps

I want to test if a CLLocationCoordinate2D is inside a Square created from other four CLLocationCoordinate2D

I have a struct like this:

typedef struct {
    CLLocationCoordinate2D southWest;
    CLLocationCoordinate2D southEast;
    CLLocationCoordinate2D northEast;
    CLLocationCoordinate2D northWest;
} Bounds;

And a CLLocationCoordinate2D coordinate passed as param. And I want to test if coordinate is inside the Bounds. How can I test that?

- (BOOL) isCoordinate:(CLLocationCoordinate2D)coordinate insideBounds:(Bounds)bounds { ... }

You just create a CLregion or CLCircularRegion and check if the coordinate is contained within the region...

Like this

CLCircularRegion *myRegion = [[CLCircularRegion alloc] initWithCenter:CLLocationCoordinate2DMake(22, -111) radius:500 identifier:@"myRegion"];
if ([myRegion containsCoordinate:CLLocationCoordinate2DMake(22.45, -111.1)]) {
    //Do what you want here
}

如果你有四个点你可以制作一个CGRect并使用CGRectContainsPoint()

If the 4 points do not represent rectangle or a circle but any 4 points on the map you will need to create the system yourself. For a point to be inside a shape represented by 4 oriented points it is easiest to check the cross product between the center and each sequential pair of bounds points. All results must be positive for clock wise order for the point to be inside the bounds. The second thing is you need to convert the coordinates to cartesian system and orient them... Let the code speak for itself:

- (double)crossProductZCoordinateForCenter:(CLLocationCoordinate2D)center left:(CLLocationCoordinate2D)left right:(CLLocationCoordinate2D)right {
    CLLocationCoordinate2D A = CLLocationCoordinate2DMake(left.latitude-center.latitude, left.longitude-center.longitude);
    CLLocationCoordinate2D B = CLLocationCoordinate2DMake(right.latitude-center.latitude, right.longitude-center.longitude);

    return B.latitude*A.longitude - A.latitude*B.longitude;
}

- (BOOL)isCartesianCoordinate:(CLLocationCoordinate2D)coordinate insideBounds:(Bounds)bounds {
    // Now we will break the system into 4 triangles and check their orientation by using a z component of the cross product. If one of them if negative the coordinate is not inside the region
    if([self crossProductZCoordinateForCenter:coordinate left:bounds.southWest right:bounds.northWest] < .0) return NO;
    if([self crossProductZCoordinateForCenter:coordinate left:bounds.northWest right:bounds.northEast] < .0) return NO;
    if([self crossProductZCoordinateForCenter:coordinate left:bounds.northEast right:bounds.southEast] < .0) return NO;
    if([self crossProductZCoordinateForCenter:coordinate left:bounds.southEast right:bounds.southWest] < .0) return NO;
    return YES;
}

- (BOOL)isCoordinate:(CLLocationCoordinate2D)coordinate insideBounds:(Bounds)bounds {
    // We may treat the coordinates as cartesian but east should always be larger then west and north should be larger then south
    // Those smaller must be increased by 360 degrees
    if(bounds.southEast.latitude < bounds.southWest.latitude) bounds.southEast.latitude += 360.0;
    if(bounds.northEast.latitude < bounds.northWest.latitude) bounds.northEast.latitude += 360.0;
    if(bounds.northEast.longitude < bounds.southEast.longitude) bounds.northEast.longitude += 360.0;
    if(bounds.northWest.longitude < bounds.southWest.longitude) bounds.northWest.longitude += 360.0;
    // Check if any of the combination coordinates are cartesicly inside the bounds
    // We need to increase the longitude and the latitude by 360 and check all 4 combinations
    if([self isCartesianCoordinate:coordinate insideBounds:bounds]) return YES;
    if([self isCartesianCoordinate:CLLocationCoordinate2DMake(coordinate.latitude+360.0, coordinate.longitude) insideBounds:bounds]) return YES;
    if([self isCartesianCoordinate:CLLocationCoordinate2DMake(coordinate.latitude, coordinate.longitude+360.0) insideBounds:bounds]) return YES;
    if([self isCartesianCoordinate:CLLocationCoordinate2DMake(coordinate.latitude+360.0, coordinate.longitude+360.0) insideBounds:bounds]) return YES;
    return NO;
}

And some simple tests might come in handy:

- (void)resampleCoordinate:(CLLocationCoordinate2D *)coordinate {
    if(coordinate->latitude < -180.0) coordinate->latitude += 360.0;
    if(coordinate->latitude > 180.0) coordinate->latitude -= 360.0;
    if(coordinate->longitude < -180.0) coordinate->longitude += 360.0;
    if(coordinate->longitude > 180.0) coordinate->longitude -= 360.0;
}

- (void)testLocationSystem {
    NSInteger numberOfTests = 0;
    NSInteger numberOfTestsCheckedOut = 0;

    for(double latitude = -180.0; latitude <= 180.0; latitude++) {
        for(double longitude = -180.0; longitude <= 180.0; longitude++) {
            Bounds bounds;
            bounds.southWest = CLLocationCoordinate2DMake(latitude-15.0, longitude-15.0);
            bounds.northEast = CLLocationCoordinate2DMake(latitude+15.0, longitude+15.0);
            bounds.northWest = CLLocationCoordinate2DMake(latitude-15.0, longitude+15.0);
            bounds.southEast = CLLocationCoordinate2DMake(latitude+15.0, longitude-15.0);
            [self resampleCoordinate:&(bounds.northWest)];
            [self resampleCoordinate:&(bounds.northEast)];
            [self resampleCoordinate:&(bounds.southEast)];
            [self resampleCoordinate:&(bounds.southWest)];

            numberOfTests++;
            BOOL success = [self isCoordinate:CLLocationCoordinate2DMake(latitude, longitude) insideBounds:bounds];
            if(success) {
                numberOfTestsCheckedOut++;
            }
            else {
                NSLog(@"Failed");
            }
        }
    }
    NSLog(@"%d/%d succeeded", (int)numberOfTestsCheckedOut, (int)numberOfTests);
}
- (void)testLocationFailSystem {
    NSInteger numberOfTests = 0;
    NSInteger numberOfTestsCheckedOut = 0;

    for(double latitude = -180.0; latitude <= 180.0; latitude++) {
        for(double longitude = -180.0; longitude <= 180.0; longitude++) {
            Bounds bounds;
            bounds.southWest = CLLocationCoordinate2DMake(latitude-15.0, longitude-15.0);
            bounds.northEast = CLLocationCoordinate2DMake(latitude+15.0, longitude+15.0);
            bounds.northWest = CLLocationCoordinate2DMake(latitude-15.0, longitude+15.0);
            bounds.southEast = CLLocationCoordinate2DMake(latitude+15.0, longitude-15.0);
            [self resampleCoordinate:&(bounds.northWest)];
            [self resampleCoordinate:&(bounds.northEast)];
            [self resampleCoordinate:&(bounds.southEast)];
            [self resampleCoordinate:&(bounds.southWest)];

            for(double angle = .0f; angle < M_PI; angle+=M_PI/10.0) {
                CLLocationCoordinate2D coordiunate = CLLocationCoordinate2DMake(latitude+cos(angle)*20.0, longitude+sin(angle)*20.0);
                [self resampleCoordinate:&coordiunate];

                numberOfTests++;

                BOOL success = [self isCoordinate:coordiunate insideBounds:bounds]; // must fail
                if(success == NO) {
                    numberOfTestsCheckedOut++;
                }
                else {
                    NSLog(@"Failed");
                }
            }

        }
    }
    NSLog(@"%d/%d succeeded", (int)numberOfTestsCheckedOut, (int)numberOfTests);
}

These all pass for me. If you find a situation not working but should or other way around please contact me.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM