Минимальное расстояние между точкой и линией по широте, долготе

У меня есть линия с двумя точками широты и долготы
A: 3.222895, 101.719751
B: 3.227511, 101.724318

и 1 очко
C: 3.224972, 101.722932

Как рассчитать минимальное расстояние между точкой C и линией, состоящей из точек A и B? Это будет удобно, если вы также можете предоставить расчет и код objective-C. Расстояние составляет около 89 метров (с помощью линейки в Google Earth).

5 ответов


спасибо Мими и этой замечательной статье http://www.movable-type.co.uk/scripts/latlong.html но они не дают всей картины. Вот одна деталь. Все эти точки собираются с помощью Google Earth, используя метку, чтобы отметить места. Убедитесь, что lat/long установлены в десятичных градусах в настройках.

lat A = 3.222895  
lon A = 101.719751  
lat B = 3.222895  
lon B = 101.719751  
lat C = 3.224972  
lon C = 101.722932  
Earth radius, R = 6371

1. Сначала вам нужно найти подшипник от A до C и от A до B.
Формула подшипника

bearingAC = atan2( sin(Δλ)*cos(φ₂), cos(φ₁)*sin(φ₂) − sin(φ₁)*cos(φ₂)*cos(Δλ) )  
bearingAB = atan2( sin(Δλ)*cos(φ₂), cos(φ₁)*sin(φ₂) − sin(φ₁)*cos(φ₂)*cos(Δλ) ) 

φ является широта, λ-долгота, R-радиус Земли

2. Найти расстояние с помощью косинусов

distanceAC = acos( sin(φ₁)*sin(φ₂) + cos(φ₁)*cos(φ₂)*cos(Δλ) )*R

3. Найти cross-track расстояние

distance = asin(sin(distanceAC/ R) * sin(bearingAC − bearing AB)) * R

Objective-C code

double lat1 = 3.227511;
double lon1 = 101.724318;
double lat2 = 3.222895;
double lon2 = 101.719751;
double lat3 = 3.224972;
double lon3 = 101.722932;

double y = sin(lon3 - lon1) * cos(lat3);
double x = cos(lat1) * sin(lat3) - sin(lat1) * cos(lat3) * cos(lat3 - lat1);
double bearing1 = radiansToDegrees(atan2(y, x));
bearing1 = 360 - ((bearing1 + 360) % 360);

double y2 = sin(lon2 - lon1) * cos(lat2);
double x2 = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(lat2 - lat1);
double bearing2 = radiansToDegrees(atan2(y2, x2));
bearing2 = 360 - ((bearing2 + 360) % 360);

double lat1Rads = degreesToRadians(lat1);
double lat3Rads = degreesToRadians(lat3);
double dLon = degreesToRadians(lon3 - lon1);

double distanceAC = acos(sin(lat1Rads) * sin(lat3Rads)+cos(lat1Rads)*cos(lat3Rads)*cos(dLon)) * 6371;  
double min_distance = fabs(asin(sin(distanceAC/6371)*sin(degreesToRadians(bearing1)-degreesToRadians(bearing2))) * 6371);

NSLog(@"bearing 1: %g", bearing1);  
NSLog(@"bearing 2: %g", bearing2);  
NSLog(@"distance AC: %g", distanceAC);  
NSLog(@"min distance: %g", min_distance);

на самом деле для этого есть библиотека. Вы можете найти его здесь https://github.com/100grams/CoreLocationUtils


вычислить подшипник для каждого: C до A и C до B:

var y = Math.sin(dLon) * Math.cos(lat2);
var x = Math.cos(lat1)*Math.sin(lat2) -
        Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon);
var brng = Math.atan2(y, x).toDeg();

dLon= lon2-lon1;

рассчитать расстояние между дорожками:

var dXt = Math.asin(Math.sin(distance_CB/R)*Math.sin(bearing_CA-bearing_CB)) * R;

R-радиус Земли, dXt-минимальное расстояние, которое вы хотели рассчитать.


код для выполнения этого расчета размещен по адресу здесь. Это реализует точное решение в терминах эллипсоидальных геодезических. Для основных геодезических расчетов можно использовать GeographicLib или порт этих алгоритмов на C, которые включены в версию 4.9.0 PROJ.4. Этот интерфейс C документирован здесь.

вот результат компиляции и выполнения перехвата.cpp:

$ echo 3.222895 101.719751 3.227511 101.724318 3.224972 101.722932 | ./intercept 
Initial guess 3.225203 101.7220345
Increment 0.0003349040566247297 0.0003313413822354505
Increment -4.440892098500626e-16 0
Increment 0 0
...
Final result 3.225537904056624 101.7223658413822
Azimuth to A1 -135.1593040635131
Azimuth to A2 44.84069593652217
Azimuth to B1 134.8406959363608

расстояние до линии 88,743 м:

$ echo 3.224972 101.722932 3.225537904056624 101.7223658413822 | GeodSolve -i
-45.15927221 -45.15930407 88.743

см. пост здесь: https://stackoverflow.com/a/33343505/4083623

для расстояния до нескольких тысяч метров я бы упростил вопрос от сферы до плоскости. Тогда проблема довольно проста, так как можно использовать простой расчет треугольника:

у нас есть точки A и B и ищем расстояние X до линии AB. Затем:

Location a;
Location b;
Location x;

double ax = a.distanceTo(x);
double alfa = (Math.abs(a.bearingTo(b) - a.bearingTo(x))) / 180
            * Math.PI;
double distance = Math.sin(alfa) * ax;

Если вы знаете, как рассчитать расстояние между двумя точками, получите расстояния между каждой из двух точек, вы получите AB, AC и BC. Вы хотите знать расстояние между точкой С и прямой АВ.

сначала получите значение P

P=(AB+BC+AC)/2

используя P, вам нужно получить S

S=SQRT((P(P-AC)(P-AB)(P-AC)) 

SQRT означает квадратный корень. Тогда вы получите то, что хотите

2*S/AB