Измерение расстояния между двумя координатами в PHP
Привет у меня есть необходимость рассчитать расстояние между двумя точками, имеющими lat и long.
Я хотел бы избежать любого вызова внешнего API.
Я попытался реализовать формулу Haversine в PHP:
вот код:
class CoordDistance
{
public $lat_a = 0;
public $lon_a = 0;
public $lat_b = 0;
public $lon_b = 0;
public $measure_unit = 'kilometers';
public $measure_state = false;
public $measure = 0;
public $error = '';
public function DistAB()
{
$delta_lat = $this->lat_b - $this->lat_a ;
$delta_lon = $this->lon_b - $this->lon_a ;
$earth_radius = 6372.795477598;
$alpha = $delta_lat/2;
$beta = $delta_lon/2;
$a = sin(deg2rad($alpha)) * sin(deg2rad($alpha)) + cos(deg2rad($this->lat_a)) * cos(deg2rad($this->lat_b)) * sin(deg2rad($beta)) * sin(deg2rad($beta)) ;
$c = asin(min(1, sqrt($a)));
$distance = 2*$earth_radius * $c;
$distance = round($distance, 4);
$this->measure = $distance;
}
}
тестирование его с некоторыми заданными точками, которые имеют публичные расстояния, я не получаю надежного результата.
Я не понимаю, есть ли ошибка в исходной формуле или в моей реализации
9 ответов:
не так давно я написал пример формулы haversine, и опубликовал его на своем сайте:
/** * Calculates the great-circle distance between two points, with * the Haversine formula. * @param float $latitudeFrom Latitude of start point in [deg decimal] * @param float $longitudeFrom Longitude of start point in [deg decimal] * @param float $latitudeTo Latitude of target point in [deg decimal] * @param float $longitudeTo Longitude of target point in [deg decimal] * @param float $earthRadius Mean earth radius in [m] * @return float Distance between points in [m] (same as earthRadius) */ function haversineGreatCircleDistance( $latitudeFrom, $longitudeFrom, $latitudeTo, $longitudeTo, $earthRadius = 6371000) { // convert from degrees to radians $latFrom = deg2rad($latitudeFrom); $lonFrom = deg2rad($longitudeFrom); $latTo = deg2rad($latitudeTo); $lonTo = deg2rad($longitudeTo); $latDelta = $latTo - $latFrom; $lonDelta = $lonTo - $lonFrom; $angle = 2 * asin(sqrt(pow(sin($latDelta / 2), 2) + cos($latFrom) * cos($latTo) * pow(sin($lonDelta / 2), 2))); return $angle * $earthRadius; }➽ обратите внимание, что вы получаете расстояние обратно в том же блоке, как вы передаете с параметром
$earthRadius. Значение по умолчанию-6371000 метров, так что результат будет в [М] тоже. Чтобы получить результат в милях, вы можете, например, пройти 3959 мили как$earthRadiusи результат будет в [mi]. На мой взгляд это хорошая привычка придерживаться единиц СИ, если нет особых оснований поступить иначе.Edit:
Как правильно указала Трейя, формула Haversine имеет слабые стороны с антиподальной точки из-за ошибок округления (хотя это и конюшня для небольших расстояний). Чтобы обойти их, вы можете использовать формула Vincenty вместо.
/** * Calculates the great-circle distance between two points, with * the Vincenty formula. * @param float $latitudeFrom Latitude of start point in [deg decimal] * @param float $longitudeFrom Longitude of start point in [deg decimal] * @param float $latitudeTo Latitude of target point in [deg decimal] * @param float $longitudeTo Longitude of target point in [deg decimal] * @param float $earthRadius Mean earth radius in [m] * @return float Distance between points in [m] (same as earthRadius) */ public static function vincentyGreatCircleDistance( $latitudeFrom, $longitudeFrom, $latitudeTo, $longitudeTo, $earthRadius = 6371000) { // convert from degrees to radians $latFrom = deg2rad($latitudeFrom); $lonFrom = deg2rad($longitudeFrom); $latTo = deg2rad($latitudeTo); $lonTo = deg2rad($longitudeTo); $lonDelta = $lonTo - $lonFrom; $a = pow(cos($latTo) * sin($lonDelta), 2) + pow(cos($latFrom) * sin($latTo) - sin($latFrom) * cos($latTo) * cos($lonDelta), 2); $b = sin($latFrom) * sin($latTo) + cos($latFrom) * cos($latTo) * cos($lonDelta); $angle = atan2(sqrt($a), $b); return $angle * $earthRadius; }
нашел код что дает мне надежные результаты.
function distance($lat1, $lon1, $lat2, $lon2, $unit) { $theta = $lon1 - $lon2; $dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta)); $dist = acos($dist); $dist = rad2deg($dist); $miles = $dist * 60 * 1.1515; $unit = strtoupper($unit); if ($unit == "K") { return ($miles * 1.609344); } else if ($unit == "N") { return ($miles * 0.8684); } else { return $miles; } }результаты :
echo distance(32.9697, -96.80322, 29.46786, -98.53506, "M") . " Miles<br>"; echo distance(32.9697, -96.80322, 29.46786, -98.53506, "K") . " Kilometers<br>"; echo distance(32.9697, -96.80322, 29.46786, -98.53506, "N") . " Nautical Miles<br>";
Это просто дополнение к @martinstoeckli и @Janith Chinthana ответы. Для тех, кому интересно, какой алгоритм самый быстрый я написал тест производительности. Лучший результат производительности показывает оптимизированную функцию от codexworld.com:
/** * Optimized algorithm from http://www.codexworld.com * * @param float $latitudeFrom * @param float $longitudeFrom * @param float $latitudeTo * @param float $longitudeTo * * @return float [km] */ function codexworldGetDistanceOpt($latitudeFrom, $longitudeFrom, $latitudeTo, $longitudeTo) { $rad = M_PI / 180; //Calculate distance from latitude and longitude $theta = $longitudeFrom - $longitudeTo; $dist = sin($latitudeFrom * $rad) * sin($latitudeTo * $rad) + cos($latitudeFrom * $rad) * cos($latitudeTo * $rad) * cos($theta * $rad); return acos($dist) / $rad * 60 * 1.853; }вот результаты теста:
Test name Repeats Result Performance codexworld-opt 10000 0.084952 sec +0.00% codexworld 10000 0.104127 sec -22.57% custom 10000 0.107419 sec -26.45% custom2 10000 0.111576 sec -31.34% custom1 10000 0.136691 sec -60.90% vincenty 10000 0.165881 sec -95.26%
вот простой и Совершенный код для вычисления расстояния между двумя широте и долготе. Здесь был найден следующий код:http://www.codexworld.com/distance-between-two-addresses-google-maps-api-php/
$latitudeFrom = '22.574864'; $longitudeFrom = '88.437915'; $latitudeTo = '22.568662'; $longitudeTo = '88.431918'; //Calculate distance from latitude and longitude $theta = $longitudeFrom - $longitudeTo; $dist = sin(deg2rad($latitudeFrom)) * sin(deg2rad($latitudeTo)) + cos(deg2rad($latitudeFrom)) * cos(deg2rad($latitudeTo)) * cos(deg2rad($theta)); $dist = acos($dist); $dist = rad2deg($dist); $miles = $dist * 60 * 1.1515; $distance = ($miles * 1.609344).' km';
для тех, кто любит короче и быстрее(не вызывая deg2rad()).
function circle_distance($lat1, $lon1, $lat2, $lon2) { $rad = M_PI / 180; return acos(sin($lat2*$rad) * sin($lat1*$rad) + cos($lat2*$rad) * cos($lat1*$rad) * cos($lon2*$rad - $lon1*$rad)) * 6371;// Kilometers }
попробуйте это дает потрясающие результаты
function getDistance($point1_lat, $point1_long, $point2_lat, $point2_long, $unit = 'km', $decimals = 2) { // Calculate the distance in degrees $degrees = rad2deg(acos((sin(deg2rad($point1_lat))*sin(deg2rad($point2_lat))) + (cos(deg2rad($point1_lat))*cos(deg2rad($point2_lat))*cos(deg2rad($point1_long-$point2_long))))); // Convert the distance in degrees to the chosen unit (kilometres, miles or nautical miles) switch($unit) { case 'km': $distance = $degrees * 111.13384; // 1 degree = 111.13384 km, based on the average diameter of the Earth (12,735 km) break; case 'mi': $distance = $degrees * 69.05482; // 1 degree = 69.05482 miles, based on the average diameter of the Earth (7,913.1 miles) break; case 'nmi': $distance = $degrees * 59.97662; // 1 degree = 59.97662 nautic miles, based on the average diameter of the Earth (6,876.3 nautical miles) } return round($distance, $decimals); }
для точных значений сделать это так:
public function DistAB() { $delta_lat = $this->lat_b - $this->lat_a ; $delta_lon = $this->lon_b - $this->lon_a ; $a = pow(sin($delta_lat/2), 2); $a += cos(deg2rad($this->lat_a9)) * cos(deg2rad($this->lat_b9)) * pow(sin(deg2rad($delta_lon/29)), 2); $c = 2 * atan2(sqrt($a), sqrt(1-$a)); $distance = 2 * $earth_radius * $c; $distance = round($distance, 4); $this->measure = $distance; }Хм, я думаю,что это должно сделать...
Edit:
для формул и по крайней мере JS-реализаций попробуйте:http://www.movable-type.co.uk/scripts/latlong.html
смеешь меня... Я забыл deg2rad все значения в круге функций...
Привет здесь код для получения расстояния и времени, используя два разных Lat и Long
$url ="https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial&origins=16.538048,80.613266&destinations=23.0225,72.5714"; $ch = curl_init(); // Disable SSL verification curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // Will return the response, if false it print the response curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // Set the url curl_setopt($ch, CURLOPT_URL,$url); // Execute $result=curl_exec($ch); // Closing curl_close($ch); $result_array=json_decode($result); print_r($result_array);вы можете проверить пример ниже Ссылка получить время между двумя различными местами, используя широту и долготу в php
множитель изменяется в каждой координате из-за теории расстояния большого круга, как написано здесь :
http://en.wikipedia.org/wiki/Great-circle_distance
и вы можете вычислить ближайшее значение, используя эту формулу, описанную здесь:
http://en.wikipedia.org/wiki/Great-circle_distance#Worked_example
ключ преобразует каждое значение градус-минута-секунда во все градусы значение:
N 36°7.2', W 86°40.2' N = (+) , W = (-), S = (-), E = (+) referencing the Greenwich meridian and Equator parallel (phi) 36.12° = 36° + 7.2'/60' (lambda) -86.67° = 86° + 40.2'/60'
Comments