如何在不使用MKMapView的情况下检查MKCoordinateRegion是否包含CLLocationCoordinate2D?

时间:2012-05-11 14:13:13

标签: iphone ios cllocationmanager cllocation mkcoordinateregion

我需要检查用户位置是否属于MKCoordinateRegion。 我很惊讶没有为此找到简单的功能,类似于:CGRectContainsCGPoint(rect,point)

我找到了以下代码:

CLLocationCoordinate2D topLeftCoordinate = 
    CLLocationCoordinate2DMake(region.center.latitude 
                               + (region.span.latitudeDelta/2.0), 
                               region.center.longitude 
                               - (region.span.longitudeDelta/2.0));


    CLLocationCoordinate2D bottomRightCoordinate = 
    CLLocationCoordinate2DMake(region.center.latitude 
                               - (region.span.latitudeDelta/2.0), 
                               region.center.longitude 
                               + (region.span.longitudeDelta/2.0));

        if (location.latitude < topLeftCoordinate.latitude || location.latitude > bottomRightCoordinate.latitude || location.longitude < bottomRightCoordinate.longitude || location.longitude > bottomRightCoordinate.longitude) {

    // Coordinate fits into the region

    }

但是,我不确定它是否准确,因为文档没有详细说明区域矩形的计算方式。

必须有更简单的方法。我是否忽略了MapKit框架文档中的某些功能?

9 个答案:

答案 0 :(得分:16)

如果有其他人与纬度和经度混淆,这里有经过测试的工作解决方案:

MKCoordinateRegion region = self.mapView.region;

CLLocationCoordinate2D location = user.gpsposition.coordinate;
CLLocationCoordinate2D center   = region.center;
CLLocationCoordinate2D northWestCorner, southEastCorner;

northWestCorner.latitude  = center.latitude  - (region.span.latitudeDelta  / 2.0);
northWestCorner.longitude = center.longitude - (region.span.longitudeDelta / 2.0);
southEastCorner.latitude  = center.latitude  + (region.span.latitudeDelta  / 2.0);
southEastCorner.longitude = center.longitude + (region.span.longitudeDelta / 2.0);

if (
    location.latitude  >= northWestCorner.latitude && 
    location.latitude  <= southEastCorner.latitude &&

    location.longitude >= northWestCorner.longitude && 
    location.longitude <= southEastCorner.longitude
    )
{
    // User location (location) in the region - OK :-)
    NSLog(@"Center (%f, %f) span (%f, %f) user: (%f, %f)| IN!", region.center.latitude, region.center.longitude, region.span.latitudeDelta, region.span.longitudeDelta, location.latitude, location.longitude);

}else {

    // User location (location) out of the region - NOT ok :-(
    NSLog(@"Center (%f, %f) span (%f, %f) user: (%f, %f)| OUT!", region.center.latitude, region.center.longitude, region.span.latitudeDelta, region.span.longitudeDelta, location.latitude, location.longitude);
}

答案 1 :(得分:15)

您可以使用MKMapPointForCoordinate将您的位置转换为某个点,然后在地图视图的MKMapRectContainsPoint上使用visibleMapRect。这完全脱离了我的头脑。如果有效,请告诉我。

答案 2 :(得分:15)

我发布此答案,因为我认为接受的解决方案无效。这个答案也不完美,但它处理的情况是坐标环绕360度边界,这足以适合我的情况。

+ (BOOL)coordinate:(CLLocationCoordinate2D)coord inRegion:(MKCoordinateRegion)region
{
    CLLocationCoordinate2D center = region.center;
    MKCoordinateSpan span = region.span;

    BOOL result = YES;
    result &= cos((center.latitude - coord.latitude)*M_PI/180.0) > cos(span.latitudeDelta/2.0*M_PI/180.0);
    result &= cos((center.longitude - coord.longitude)*M_PI/180.0) > cos(span.longitudeDelta/2.0*M_PI/180.0);
    return result;
}

答案 3 :(得分:7)

其他答案都有错误。接受的答案有点冗长,并且在国际日期附近失败了。余弦的答案是可行的,但对于非常小的区域是失败的(因为delta余弦是正弦趋向零接近零,意味着较小的角度差异,我们期望零变化)这个答案应该适用于所有情况,并且更简单。

夫特:

/* Standardises and angle to [-180 to 180] degrees */
class func standardAngle(var angle: CLLocationDegrees) -> CLLocationDegrees {
    angle %= 360
    return angle < -180 ? -360 - angle : angle > 180 ? 360 - 180 : angle
}

/* confirms that a region contains a location */
class func regionContains(region: MKCoordinateRegion, location: CLLocation) -> Bool {
    let deltaLat = abs(standardAngle(region.center.latitude - location.coordinate.latitude))
    let deltalong = abs(standardAngle(region.center.longitude - location.coordinate.longitude))
    return region.span.latitudeDelta >= deltaLat && region.span.longitudeDelta >= deltalong
}

目标C:

/* Standardises and angle to [-180 to 180] degrees */
+ (CLLocationDegrees)standardAngle:(CLLocationDegrees)angle {
    angle %= 360
    return angle < -180 ? -360 - angle : angle > 180 ? 360 - 180 : angle
}

/* confirms that a region contains a location */
+ (BOOL)region:(MKCoordinateRegion*)region containsLocation:(CLLocation*)location {
    CLLocationDegrees deltaLat = fabs(standardAngle(region.center.latitude - location.coordinate.latitude))
    CLLocationDegrees deltalong = fabs(standardAngle(region.center.longitude - location.coordinate.longitude))
    return region.span.latitudeDelta >= deltaLat && region.span.longitudeDelta >= deltalong
}

对于包含任一极点的区域,此方法失败,但随后坐标系本身在极点处失败。对于大多数应用,此解决方案应该足够了。 (注意,未在目标C上测试)

答案 4 :(得分:6)

我已经使用此代码来确定坐标是否在圆形区域(围绕它的半径的坐标)内。

- (BOOL)location:(CLLocation *)location isNearCoordinate:(CLLocationCoordinate2D)coordinate withRadius:(CLLocationDistance)radius
{
    CLCircularRegion *circularRegion = [[CLCircularRegion alloc] initWithCenter:location.coordinate radius:radius identifier:@"radiusCheck"];

    return [circularRegion containsCoordinate:coordinate];
}

答案 5 :(得分:1)

Owen Godfrey,目标C代码不起作用,这是很好的代码: 在Objective-C上失败,这是很好的代码:

/* Standardises and angle to [-180 to 180] degrees */
- (CLLocationDegrees)standardAngle:(CLLocationDegrees)angle {
    angle=fmod(angle,360);
    return angle < -180 ? -360 - angle : angle > 180 ? 360 - 180 : angle;
}

-(BOOL)thisRegion:(MKCoordinateRegion)region containsLocation:(CLLocation *)location{
    CLLocationDegrees deltaLat =fabs([self standardAngle:(region.center.latitude-location.coordinate.latitude)]);
    CLLocationDegrees deltaLong =fabs([self standardAngle:(region.center.longitude-location.coordinate.longitude)]);
    return region.span.latitudeDelta >= deltaLat && region.span.longitudeDelta >=deltaLong;
}
    CLLocationDegrees deltalong = fabs(standardAngle(region.center.longitude - location.coordinate.longitude));
    return region.span.latitudeDelta >= deltaLat && region.span.longitudeDelta >= deltalong;
}

谢谢!

答案 6 :(得分:1)

基于Lukasz解决方案,但在Swift中,万一有人可以使用Swift:

func isInRegion (region : MKCoordinateRegion, coordinate : CLLocationCoordinate2D) -> Bool {

    let center   = region.center;
    let northWestCorner = CLLocationCoordinate2D(latitude: center.latitude  - (region.span.latitudeDelta  / 2.0), longitude: center.longitude - (region.span.longitudeDelta / 2.0))
    let southEastCorner = CLLocationCoordinate2D(latitude: center.latitude  + (region.span.latitudeDelta  / 2.0), longitude: center.longitude + (region.span.longitudeDelta / 2.0))

    return (
        coordinate.latitude  >= northWestCorner.latitude &&
        coordinate.latitude  <= southEastCorner.latitude &&

        coordinate.longitude >= northWestCorner.longitude &&
        coordinate.longitude <= southEastCorner.longitude
    )
}

答案 7 :(得分:1)

像魅力一样为我工作(Swift 5)

func check(
    location: CLLocationCoordinate2D,
    contains childLocation: CLLocationCoordinate2D,
    with radius: Double)
    -> Bool
{
    let region = CLCircularRegion(center: location, radius: radius, identifier: "SearchId")
    return region.contains(childLocation)
}

答案 8 :(得分:0)

我有同样的计算问题。我喜欢Owen Godfrey提出的概念here,即使是Fernando here也错过了纬度不同于经度并且具有不同范围的事实。为了澄清我的建议,我将其与测试一起发布,以便您可以自己查看。

import XCTest
import MapKit

// MARK - The Solution

extension CLLocationDegrees {

    enum WrapingDimension: Double {
        case latitude = 180
        case longitude = 360
    }

    /// Standardises and angle to [-180 to 180] or [-90 to 90] degrees
    func wrapped(diemension: WrapingDimension) -> CLLocationDegrees {
        let length = diemension.rawValue
        let halfLenght = length/2.0
        let angle = self.truncatingRemainder(dividingBy: length)
        switch diemension {
        case .longitude:
            //        return angle < -180.0 ? 360.0 + angle : angle > 180.0 ? -360.0 + angle : angle
            return angle < -halfLenght ? length + angle : angle > halfLenght ? -length + angle : angle
        case .latitude:
            //        return angle < -90.0 ? -180.0 - angle : angle > 90.0 ? 180.0 - angle : angle
            return angle < -halfLenght ? -length - angle : angle > halfLenght ? length - angle : angle
        }
    }
}

extension MKCoordinateRegion {
    /// confirms that a region contains a location
    func contains(_ coordinate: CLLocationCoordinate2D) -> Bool {
        let deltaLat = abs((self.center.latitude - coordinate.latitude).wrapped(diemension: .latitude))
        let deltalong = abs((self.center.longitude - coordinate.longitude).wrapped(diemension: .longitude))
        return self.span.latitudeDelta/2.0 >= deltaLat && self.span.longitudeDelta/2.0 >= deltalong
    }
}

// MARK - Unit tests

class MKCoordinateRegionContaingTests: XCTestCase {

    func testRegionContains() {
        var region = MKCoordinateRegionMake(CLLocationCoordinate2DMake(0, 0), MKCoordinateSpan(latitudeDelta: 1, longitudeDelta: 1))
        var coords = CLLocationCoordinate2DMake(0, 0)
        XCTAssert(region.contains(coords))

        coords = CLLocationCoordinate2DMake(0.5, 0.5)
        XCTAssert(region.contains(coords))

        coords = CLLocationCoordinate2DMake(-0.5, 0.5)
        XCTAssert(region.contains(coords))
        coords = CLLocationCoordinate2DMake(0.5, 0.5000001)
        XCTAssert(!region.contains(coords)) // NOT Contains
        coords = CLLocationCoordinate2DMake(0.5, -0.5000001)
        XCTAssert(!region.contains(coords)) // NOT Contains
        coords = CLLocationCoordinate2DMake(1, 1)
        XCTAssert(!region.contains(coords)) // NOT Contains

        region = MKCoordinateRegionMake(CLLocationCoordinate2DMake(0, 180), MKCoordinateSpan(latitudeDelta: 1, longitudeDelta: 1))
        coords = CLLocationCoordinate2DMake(0, 180.5)
        XCTAssert(region.contains(coords))
        coords.longitude = 179.5
        XCTAssert(region.contains(coords))
        coords.longitude = 180.5000001
        XCTAssert(!region.contains(coords)) // NOT Contains
        coords.longitude = 179.5000001
        XCTAssert(region.contains(coords))
        coords.longitude = 179.4999999
        XCTAssert(!region.contains(coords)) // NOT Contains

        region = MKCoordinateRegionMake(CLLocationCoordinate2DMake(90, -180), MKCoordinateSpan(latitudeDelta: 1, longitudeDelta: 1))
        coords = CLLocationCoordinate2DMake(90.5, -180.5)
        XCTAssert(region.contains(coords))

        coords = CLLocationCoordinate2DMake(89.5, -180.5)
        XCTAssert(region.contains(coords))

        coords = CLLocationCoordinate2DMake(90.50000001, -180.5)
        XCTAssert(!region.contains(coords)) // NOT Contains

        coords = CLLocationCoordinate2DMake(89.50000001, -180.5)
        XCTAssert(region.contains(coords))

        coords = CLLocationCoordinate2DMake(89.49999999, -180.5)
        XCTAssert(!region.contains(coords)) // NOT Contains
    }

    func testStandardAngle() {
        var angle = 180.5.wrapped(diemension: .longitude)
        var required = -179.5
        XCTAssert(self.areAngleEqual(angle, required))

        angle = 360.5.wrapped(diemension: .longitude)
        required = 0.5
        XCTAssert(self.areAngleEqual(angle, required))

        angle = 359.5.wrapped(diemension: .longitude)
        required = -0.5
        XCTAssert(self.areAngleEqual(angle, required))

        angle = 179.5.wrapped(diemension: .longitude)
        required = 179.5
        XCTAssert(self.areAngleEqual(angle, required))

        angle = 90.5.wrapped(diemension: .latitude)
        required = 89.5
        XCTAssert(self.areAngleEqual(angle, required))

        angle = 90.5000001.wrapped(diemension: .latitude)
        required = 89.4999999
        XCTAssert(self.areAngleEqual(angle, required))

        angle = -90.5.wrapped(diemension: .latitude)
        required = -89.5
        XCTAssert(self.areAngleEqual(angle, required))

        angle = -90.5000001.wrapped(diemension: .latitude)
        required = -89.4999999
        XCTAssert(self.areAngleEqual(angle, required))
    }

    /// compare doubles with presition to 8 digits after the decimal point
    func areAngleEqual(_ a:Double, _ b:Double) -> Bool {
        let presition = 0.00000001
        let equal = Int(a / presition) == Int(b / presition)
        print(String(format:"%14.9f %@ %14.9f", a, equal ? "==" : "!=", b) )
        return equal
    }
}