Вычисление расстояния между двумя координатами X, Y в PHP

Я пишу инструмент для игры, которая включает в себя вычисление расстояния между двумя координатами на тороидальной плоскости 500 единиц в поперечнике. То есть от [0,0] до [499,499] являются допустимыми координатами, а [0,0] и [499,499] также находятся рядом друг с другом.

В настоящее время в моем приложении я сравниваю расстояние между городом с местоположением [X, Y] относительно собственного местоположения пользователя [X, Y], которое они настроили заранее.

Для этого я нашел такой алгоритм, который вроде работает:

Math.sqrt ( dx * dx + dy * dy );

Поскольку сортировка списка страниц по расстоянию полезна, я реализовал этот алгоритм в запросе MySQL и сделал его доступным для своего приложения, используя следующую часть моего оператора SELECT:

SQRT( POW( ( ".strval($sourceX)." - cityX ) , 2 ) + POW( ( ".strval($sourceY)." - cityY ) , 2 ) ) AS distance

Это прекрасно работает для многих вычислений, но не принимает во внимание тот факт, что [0,0] и [499,499] находятся друг напротив друга.

Можно ли каким-либо образом настроить этот алгоритм для получения точного расстояния, учитывая, что 0 и 499 являются соседними?


person Umopepisdn    schedule 02.01.2011    source источник
comment
Возможно, это может помочь: en.wikipedia.org/wiki/Great-circle_distance Изменить: stackoverflow.com/questions/27928/   -  person seriousdev    schedule 02.01.2011


Ответы (7)


Обновление (тор):

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

На это ответ довольно прост — приведенная вами декартова формула более или менее правильна. Однако вам нужно обернуть расстояния так, чтобы все, что больше или равно 250 = 500/2, было переведено в диапазон от 0 до 250.

Итак, ответ примерно такой (я вообще не знаю PHP, поэтому, возможно, его нужно изменить для синтаксиса)

dx1 = min(dx, 500-dx)
dy1 = min(dy, 500-dy);
distance = Math.sqrt(dx1*dx1 + dy1*dy1);

(Это предполагает, что вы определили dx и dy как абсолютное значение разностей.)

Только что нашел эту процедуру, которая реализует тот же расчет в красиво упакованная функция.

Исходный ответ (сфера):

Вы не объяснили, как ваши координаты (x, y) сопоставляются с точками на сфере!

Существует (буквально) бесконечное количество вариантов, каждый из которых соответствует своей картографической проекции, и формула для каждого из них разная. Обратите внимание, что независимо от того, какой выбор вы сделаете, значение двух координат сильно различается.

Если ваши (x, y) действительно являются долготой и широтой, например, существует множество стандартных формул (например, гаверсинус), но вам придется сначала перевести 0-> 499 в 0-> 360 градусов для долготы и -90 ->90 градусов для широты. (Обратите внимание, что долгота и широта ведут себя на сфере совершенно по-разному!)

Но я подчеркиваю, что любой выбор, который вы сделаете, будет искажать плоскую геометрию, которую вы получите, если нанесете график в (x,y), по сравнению с тем, как он действительно выглядит на сфере.

(Наконец, если вы действительно имеете в виду, что верхний край такой же, как нижний, а правый такой же, как левый, то у вас, вероятно, тор, а не сфера!)

person Andrew Jaffe    schedule 02.01.2011
comment
Это был ПОЧТИ правильный ответ. Да, я работаю с тором. - person Umopepisdn; 08.01.2011

Я предполагаю, что вы имеете в виду перенос координат и ничего сферического. Как плоский лист бумаги, концы которого волшебным образом соединены друг с другом.

Это означает, что для карты размером 500x500 расстояние в направлении x (или y) не превышает 250. (Если бы это было более 250 шагов, мы могли бы пройти 500-x шагов назад.)

Простым способом исправить это было бы

dx = Math.abs(dx);
dy = Math.abs(dy);
if (dx > 250)
  dx = 500 - dx;
if (dy > 250)
  dy = 500 - dy;
distance = Math.sqrt ( dx * dx + dy * dy );
person Ishtar    schedule 02.01.2011
comment
Да, это более точная формулировка. - person Umopepisdn; 02.01.2011
comment
Посмотрим, смогу ли я перенести это на MySQL. - person Umopepisdn; 02.01.2011
comment
К сожалению, я не мог использовать это, так как мне нужно было, чтобы вычисления выполнялись в MySql, чтобы я мог в полной мере использовать столбец так же, как и другие на странице. - person Umopepisdn; 08.01.2011
comment
@Umopepisdn - я думаю, вам лучше рассчитывать расстояния в PHP. MySQL не был предназначен для таких вещей. Если вы просто хотите 20 ближайших городов; есть лучшие способы их получить, вы можете задать для этого новый вопрос. - person Ishtar; 08.01.2011

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

Но, как я понял, вам нужна формула, точная для почти противоположных точек. Формула Хаверсина здесь не работает. В таком случае вам нужна формула Винсенти, которая точна даже в противоположных случаях.

http://en.wikipedia.org/wiki/Great-circle_distance#Formulae

person Agnius Vasiliauskas    schedule 02.01.2011

Похоже, вы просто используете специальное конечное декартово пространство, которое «плиточное». В этом случае каждый объект не имеет уникальной позиции. Вместо (x, y) это (x + i*w, y + j*h) для всех возможных целых значений i и j, где w и h — ширина и высота вашего «окна» соответственно.

Очевидно, что тогда расстояние не уникально, а минимальное расстояние равно min(d(p1,p2))) по всем i, j.

Если ваши координаты завернуты, вам просто нужно вычислить их для i = -1,0,1 и j = -1,0,1, а затем взять наименьший.

person Stretto    schedule 02.01.2011

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

Я думаю, что лучший подход будет основан на широте и долготе, например:

http://jan.ucc.nau.edu/~cvm/latlongdist.html

В MySQL встроено геокодирование. Почему бы не использовать это?

http://www.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL

person duffymo    schedule 02.01.2011
comment
Хм, похоже, этот подход может сработать. Чтение через ppt; Благодарность! Я опубликую свое решение, если я его разработаю. - person Umopepisdn; 02.01.2011
comment
Я попытался взять формулу Хаверсина, потому что казалось, что она может иметь значение, и добавить ее в свой запрос, чтобы посмотреть, как она работает. Результаты были совершенно разными. Вот новый запрос: - person Umopepisdn; 02.01.2011
comment
И вот несколько до/после между старым алгоритмом и новым: - person Umopepisdn; 02.01.2011
comment
ближайший: [168 125] 113,371 к 824,860 Хаверсина [359 282] 136,561 к 1071,594 Хаверсина - person Umopepisdn; 02.01.2011
comment
самый дальний: [374 483] 319,788 до 10 635,081 Хаверсина [362 497] 329,303 до 10 581,878 Хаверсина - person Umopepisdn; 02.01.2011
comment
Самая большая проблема, с которой я не уверен в этих результатах, заключается в том, что масштаб далеко не тот. - person Umopepisdn; 02.01.2011
comment
Извините за вводящее в заблуждение описание сферы. Это сделало геокодирование неправильным подходом. - person Umopepisdn; 08.01.2011

Хотя некоторые из ответов здесь были очень близки, проблема была наконец решена с помощью этого сегмента SELECT:

SQRT( POW( LEAST( ABS($sourceXstr-cityX), ( 500 +LEAST($sourceXstr,cityX)-GREATEST($sourceXstr,cityX))) , 2 ) + POW( LEAST( ABS($sourceYstr-cityY), ( 500 +LEAST($sourceYstr,cityY)-GREATEST($sourceYstr,cityY))) , 2 ) ) AS distance

person Umopepisdn    schedule 08.01.2011

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

Если ваши точки находятся в двумерной плоскости, расстояние между точками (x1, y1) и (x2, y2) определяется теоремой Пифагора:

формула Пифагора

d = squareroot( square(x2 - x1) + square(y2 - y1) )

В PHP,

$x1 = $y1 = 2;
$x2 = $y2 = 5;
$distance = sqrt( pow(($x2-$x1),2) + pow(($y2-$y1),2) );
echo $distance;              // 4.2426406871193
person Somnath Muluk    schedule 27.07.2016