Я предлагаю следующее решение, которое также работает в случае многоугольника, расположенного вокруг северного полюса, где расчет долготы и разности широт не имеет смысла.
Решение преобразует долготу и широту точек на Землю в трехмерные координаты, используя World Geodetic System 84. С этими трехмерными точками вы можете рассчитать проекцию одной точки на линию, определяемую двумя другими точками в трехмерном пространстве.
Вот код, выполняющий расчеты. Он использует класс javafx.geometry.Point3D
, доступный в Java 8.
/** Semi-major axis of earth in meter */
public static final double WGS84_A = 6378137.0;
/** Semi-minor axis of earth in meter */
public static final double WGS84_B = 6356752.314245;
/** Eccentricity of earth */
public static final double WGS84_E =
Math.sqrt((WGS84_A * WGS84_A)/(WGS84_B * WGS84_B) - 1);
public static final double DEGREES_TO_RADIANS = Math.PI/180;
/**
* Calculates a three-dimensional point in the
* World Geodetic System (WGS84) from latitude and longitude.
*/
public static Point3D latLonToPoint3D(double lat, double lon) {
double clat = Math.cos(lat * DEGREES_TO_RADIANS);
double slat = Math.sin(lat * DEGREES_TO_RADIANS);
double clon = Math.cos(lon * DEGREES_TO_RADIANS);
double slon = Math.sin(lon * DEGREES_TO_RADIANS);
double N = WGS84_A/Math.sqrt(1.0 - WGS84_E * WGS84_E * slat * slat);
double x = N * clat * clon;
double y = N * clat * slon;
double z = N * (1.0 - WGS84_E * WGS84_E) * slat;
return new Point3D(x, y, z);
}
/**
* Calculates distance of projection p of vector a on vector b.
*
* Use formula for projection, with p being the projection point:
* <p>
* p = a X b/|b|^2 * b
* </p>
* X being the dot product, * being multiplication of vector and constant
*/
public static Point3D calculateProjection(Point3D a, Point3D b) {
return b.multiply(a.dotProduct(b)/(b.dotProduct(b)));
}
/**
* Calculates shortest distance of vector x and the line defined by
* the vectors a and b.
*/
public static double calculateDistanceToLine(Point3D x, Point3D a, Point3D b) {
Point3D projectionOntoLine =
calculateProjection(x.subtract(a), b.subtract(a)).add(a);
return projectionOntoLine.distance(x);
}
Позвонив calculateDistanceToLine
с точкой и точками Сегменты многоугольника, вы можете найти ближайшую линию, определяемую краевые точки и расширенную до бесконечности. В случае вогнутого многоугольника это может быть не то, что вы хотите, как вы видите на картинке.
Принимая во внимание, что расстояние до многоугольника должно быть, по крайней мере до тех пор, как расстояние до ближайшей точки края, вы можете получить расстояние до края, как:
Math.max(calculateDistanceToLine(x, edgePoint1, edgePoint2),
Math.min(x.distance(edgePoint1), x.distance(edgePoint2)));
Обратите внимание, что этот расчет также дает не расстояние на поверхности земли, а прямое расстояние, проходящее через землю. Во всяком случае, этого достаточно, чтобы выбрать самое короткое расстояние.
Функция latLonToPoint3D
является модифицированной версией функции, которую я нашел here.
Это находит ближайший угол, а не ближайший край.Это также потенциально неправильно, если вопроситель заботится о расстоянии в фиксированной единице (например, км/мили), а не в градусах лат/долго. – Sbodd
Простите, что это заняло так много времени, но я не смог сделать это раньше. Перед моим первым ответом я не мог прочитать;) Теперь это должно сработать для вас! –