Как рассчитать прямоугольник вокруг географической точки? - PullRequest
1 голос
/ 15 апреля 2011

CLLocationManager передаст моему делегату новый CLLocation при каждом изменении местоположения.Координаты этого местоположения выражаются в виде объекта CLLocationCoordinate2D, который просто содержит широту и долготу.Я хотел бы взять это местоположение и определить широту и долготу 1000 м к югу и 1000 к западу и широту и долготу 1000 м к северу и 1000 м к востоку.Таким образом, я получаю одну координату к юго-западу от местоположения и одну к северо-востоку от местоположения.

Я понятия не имею, как это сделать, и мой GoogleFoo сегодня выглядит довольно плохо.Какую информацию я обнаружил, предложил непробиваемую математику.Кто-нибудь может помочь хакеру?Я могу использовать iOS API, если он есть, но уравнение, которое просто оперирует значениями double для широты и долготы, будет еще лучше.Он не должен быть точным в пределах сантиметров, хотя в пределах нескольких метров было бы неплохо.В идеале это будет выглядеть примерно так:

NSArray *rect = CalculateRectangleFromLocation(
    clLocationCoordinate2D,
    1000.0
);

И тогда * rect будет иметь четыре значения: широту и длину юго-западного угла и широту и длину северо-восточного угла.

Ответы [ 2 ]

5 голосов
/ 15 апреля 2011

Вот код для получения верхних / правых / нижних / левых координат ограничительного прямоугольника.

LatLon.h

#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>

extern double radians(double degrees);
extern double degrees(double radians);

extern CLLocationCoordinate2D LatLonDestPoint(CLLocationCoordinate2D origin, double brearing, CLLocationDistance distance);

LatLon.m

const CLLocationDegrees kLatLonEarthRadius = 6371.0;

double radians(double degrees) {
    return degrees * M_PI / 180.0;
}

double degrees(double radians) {
    return radians * 180.0 / M_PI;
}

CLLocationCoordinate2D LatLonDestPoint(CLLocationCoordinate2D origin, double bearing, CLLocationDistance distance) {
    double brng = radians(bearing);
    double lat1 = radians(origin.latitude);
    double lon1 = radians(origin.longitude);

    CLLocationDegrees lat2 = asin(sin(lat1) * cos(distance / kLatLonEarthRadius) + 
                                  cos(lat1) * sin(distance / kLatLonEarthRadius) * cos(brng));
    CLLocationDegrees lon2 = lon1 + atan2(sin(brng) * sinf(distance / kLatLonEarthRadius) * cos(lat1),
                                                      cosf(distance / kLatLonEarthRadius) - sin(lat1) * sin(lat2));
    lon2 = fmod(lon2 + M_PI, 2.0 * M_PI) - M_PI;

    CLLocationCoordinate2D coordinate;
    if (! (isnan(lat2) || isnan(lon2))) {
        coordinate.latitude = degrees(lat2);
        coordinate.longitude = degrees(lon2);
    }

    return coordinate;
}

Использование

CLLocationCoordinate2D location = ...;
double distance = ...;

CLLocationCoordinate2D right = LatLonDestPoint(location, 90.0, distance);
CLLocationDegrees rectRight = right.longitude;

CLLocationCoordinate2D top = LatLonDestPoint(location, 0.0, distance);
CLLocationDegrees rectTop = top.latitude;

CLLocationCoordinate2D left = LatLonDestPoint(location, 270.0, distance);
CLLocationDegrees rectLeft = left.longitude;

CLLocationCoordinate2D bottom = LatLonDestPoint(location, 180.0, distance);
CLLocationDegrees rectBottom = bottom.latitude;

Swift

extension CLLocationCoordinate2D {
    fileprivate func radians(degrees: Double) -> Double { return degrees * .pi / 180.0 }
    fileprivate func degrees(radians: Double) -> Double { return radians * 180.0 / .pi }

    func coordinate(bearing: Double, distanceInMeter distance: CLLocationDistance) -> CLLocationCoordinate2D {
        let kLatLonEarthRadius: CLLocationDegrees = 6371.0
        let brng: Double = radians(degrees: bearing)
        let lat1: Double = radians(degrees: self.latitude)
        let lon1: Double = radians(degrees: self.longitude)

        let lat2: CLLocationDegrees = asin(
            sin(lat1) * cos(distance / kLatLonEarthRadius) +
            cos(lat1) * sin(distance / kLatLonEarthRadius) * cos(brng)
        )

        var lon2: CLLocationDegrees = lon1 + atan2(
            sin(brng) * sin(distance / kLatLonEarthRadius) * cos(lat1),
            cos(distance / kLatLonEarthRadius) - sin(lat1) * sin(lat2)
        )
        lon2 = fmod(lon2 + .pi, 2.0 * .pi) - .pi

        var coordinate = CLLocationCoordinate2D()
        if !lat2.isNaN && !lon2.isNaN {
            coordinate.latitude = degrees(radians: lat2)
            coordinate.longitude = degrees(radians: lon2)
        }
        return coordinate
    }

    func rect(distanceInMeter meter: CLLocationDistance) -> (north: Double, west: Double, south: Double, east: Double) {
        let north = coordinate(bearing: 0, distanceInMeter: meter).latitude
        let south = coordinate(bearing: 180, distanceInMeter: meter).latitude
        let east = coordinate(bearing: 90, distanceInMeter: meter).longitude
        let west = coordinate(bearing: 270, distanceInMeter: meter).longitude

        return (north: north, west: west, south: south, east: east)
    }
}
0 голосов
/ 15 апреля 2011

Я обычно делаю это, используя библиотеку PROJ4 для преобразования широты и долготы в проекцию в метрах, которая полезна для моего региона (UTM работает хорошо, если у вас нет дополнительной информации, я в Северной Калифорнии, поэтому геодезисты в моем регион все работает в EPSG: 2226), добавляя к нему соответствующее смещение в метрах, а затем используя PROJ4 для обратного преобразования.

Позднее редактирование: ответ, данный Джаянтом ниже, подойдет, в зависимости от того, насколько точным должен быть прямоугольник с вашими метрами. Земля - ​​это не сфера, это даже не сплюснутый сфероид, поэтому проекция, в которой добавляется ваше расстояние к вашей широте и долготе, может иметь значение. Даже с использованием PROJ4 эти метры находятся на уровне моря. География сложнее, чем вы думаете.

...