Измерение расстояния между двумя координатами в PHP

Привет, мне нужно рассчитать расстояние между двумя точками, имеющими lat и long.

Я хотел бы избежать любого вызова внешнего API.

Я попытался реализовать Формулу Хаверсина в 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; } } 

Испытывая это с некоторыми заданными точками, которые имеют общественные расстояния, я не получаю надежный результат.

Я не понимаю, есть ли ошибка в исходной формуле или в моей реализации

Solutions Collecting From Web of "Измерение расстояния между двумя координатами в PHP"

Недавно я написал пример формулы 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 метров, поэтому результат будет в [m]. Чтобы получить результат в милях, вы могли бы, например, пройти 3959 миль в качестве $earthRadius и результат будет в [mi]. По-моему, хорошая привычка придерживаться единиц СИ, если нет особых причин для этого.

Редактировать:

Как правильно указал TreyA, формула Хаверсина имеет слабости с антиподальными точками из-за ошибок округления (хотя она устойчива на малых расстояниях). Чтобы обойти их, вы могли бы использовать формулу Винченти.

 /** * 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 } 

Для точных значений сделайте так:

 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; } 

Хм, я думаю, это должно сделать это …

Редактировать:

Для формул и, по крайней мере, JS-реализаций попробуйте: http://www.movable-type.co.uk/scripts/latlong.html

Осмелюсь … Я забыл деградировать все значения в круг-функциях …

Попробуйте это дает потрясающие результаты

 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); } 

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

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' 

Привет здесь Код для получения расстояния и времени с использованием двух разных латов и длинных

 $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); 

Вы можете проверить, что Example Below Link получает время между двумя разными местоположениями, используя широту и долготу в php