Как проверить, содержит ли MKCoordinateRegion CLLocationCoordinate2D без использования MKMapView?

Мне нужно проверить, принадлежит ли местоположение пользователя к MKCoordinateRegion. Я был удивлен, не найти простую функцию для этого, что-то вроде: CGRectContainsCGPoint (прямоугольник, точка).

Я нашел следующий фрагмент кода:

<code>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

    }
</code>

Но я не уверен, что он точный, поскольку в документации не указано, как именно рассчитывается прямоугольник региона.

Должен быть более простой способ сделать это. Я пропустил какую-то функцию в документации инфраструктуры MapKit?

Ответы на вопрос(8)

MKMapPointForCoordinate, затем используйтеMKMapRectContainsPoint на карте зренияvisibleMapRect. Это совершенно не в моей голове. Дайте мне знать, если это работает.

 nevan king11 мая 2012 г., 19:50
То, как ты это делаешь, прекрасно. Регион указывает диапазон в градусах, равный широте и долготе. Они конвертируют напрямую. Я не совсем уверен в логике вашего заявления if. Разве они не должны быть&& вместо того||?
 Lukasz11 мая 2012 г., 17:26
Он чувствует себя совершенно подавляющим, чтобы инициализировать весь MKMapView и настроить его только для такой простой проверки. Мне нужно рассчитать это вне любого контроллера представления.
 Lukasz11 мая 2012 г., 18:13
Регион в порядке. Я просто не уверен, правильно ли я проверяю. В документации MKCoordinateRegion не указано, как именно широта и долгота образуют прямоугольник области.
 nevan king11 мая 2012 г., 17:44
Извините, я думал, что вы работаете с уже созданным видом карты. Если у вас есть только этот регион, вам придется положиться на него, чтобы быть точным. Почему вы думаете, что регион не годится? Откуда вы взяли регион?
 james Burns09 февр. 2014 г., 22:41
Это отличное, простое решение. Я должен проверить больше, но, возможно, лучшее решение этой хитрой проблемы.

предложенная Оуэном ГодфрВо Бун даже ФернандоВо пропустил тот факт, что широта обернута иначе, чем долгота и имеет другой диапазон. Чтобы прояснить мое предложение, я публикую его с тестами, чтобы вы могли проверить его самостоятельно.

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

соответствует международной дате. Ответ по косинусу работоспособен, но не подходит для очень маленьких областей (поскольку дельта-косинус - это синус, который стремится к нулю около нуля, то есть для меньших угловых разностей мы ожидаем нулевого изменения) Этот ответ должен работать правильно для всех ситуаций и является более простым.

Swift:

/* 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
}

Этот метод не подходит для областей, которые включают в себя любой полюс, но тогда сама система координат выходит из строя на полюсах. Для большинства приложений этого решения должно быть достаточно. (Примечание: не тестировалось на Objective C)

 Verticon13 сент. 2016 г., 14:17
В методе standardAngle для случая, когда нормализованный угол> 180: возвращаемое значение не должно быть углом 360 - вместо 360 - 180?
 Cherpak Evgeny19 окт. 2015 г., 11:05
да, код objc не компилируется
 Nikita Ivaniushchenko13 июн. 2018 г., 10:58
При сравнении с широтой или долготой следует использовать span.delta/2.

чтобы определить, находится ли координата внутри круговой области (координата с радиусом вокруг нее).

- (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];
}
Решение Вопроса

если кто-то еще перепутал с широтами и долготами, вот проверенное, рабочее решение:

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);
}
 lichen1985308 мая 2013 г., 21:02
Я сомневаюсь, что это сработает: 1. почему location.latitude> = northWestCorner.latitude? Разве это не должно быть sounthEastCorner.latitude? 2. Что если рассчитанная минимальная долгота равна -2,0, максимальная долгота равна 2,0, а ваше местоположение. Долгота - 359,0?
 MarekR08 мая 2014 г., 17:38
@ lichen19853 правильно, что он потерпит неудачу при тестировании на 360 градусов. Смотрите мой ответ ниже для более правильного решения.

так как, по моему мнению, принятое решение недействительно. Этот ответ также не идеален, но он обрабатывает случай, когда координаты охватывают границы 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;
}
 Brainware11 янв. 2016 г., 19:14
Это должно быть принятое решение. Функция cos () решает проблему от 0 до 360 градусов. Несмотря на то, что он выполняет нелинейный масштаб на расстоянии, он сравнивается с одинаково масштабированной дельтой, поэтому он работает как шарм.
 Verticon19 сент. 2017 г., 22:08
Это помогло мне создать небольшую четко очерченную прямоугольную область в моем городе. Я не могу засвидетельствовать общее дело.

Сбой 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;
}

Благодарность

 Nikita Ivaniushchenko13 июн. 2018 г., 10:58
Используйте region.span.latitudeDelta / 2 и region.span.longitudeDelta в последней строке

но в 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
    )
}

Ваш ответ на вопрос