У меня есть функция, которая рассчитывает расстояние между двумя GPS-координатами. Затем я получаю все координаты из базы данных и перебираю их все, чтобы получить расстояние между текущим и предыдущим, а затем добавляю это в массив для конкретного устройства GPS. Почему-то это возврат NaN. Я попытался привести его к двойному, целому и округлению числа.
Вот мой PHP-код:
function distance($lat1, $lon1, $lat2, $lon2) {
$lat1 = round($lat1, 3);
$lon1 = round($lon1, 3);
$lat2 = round($lat2, 3);
$lon2 = round($lon2, 3);
$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;
if($miles < 0) $miles = $miles * -1;
return ($miles * 1.609344);
}
$this->db->query("SELECT * FROM `gps_loc` WHERE `imeiN`='" . $sql . "' AND `updatetime`>=$timeLimit ORDER BY `_id` DESC");
$dist = array();
$dist2 = array();
while($row = $this->db->getResults()) {
$dist2[$row['imeiN']] = 0;
$dist[$row['imeiN']][]["lat"] = $row['lat'];
$dist[$row['imeiN']][count($dist[$row['imeiN']]) - 1]["lng"] = $row['lon'];
}
foreach($dist as $key=>$d) {
$a = 0;
$b = 0;
foreach($dist[$key] as $n) {
if($a > 0) {
$dist2[$key] += $this->distance($n['lat'], $n['lng'], $dist[$key][$a - 1]['lat'], $dist[$key][$a - 1]['lng']);
}
$a++;
}
}
echo json_encode($dist2);
Значения, которые вы извлекаете из базы данных, могут быть строками, которые могут вызвать эту проблему.
Вы также можете проверить вопросы, которые Колинк поднял в своем посте.
Диапазон sin()
а также cos()
между -1 и 1. Поэтому в вашем первом расчете $dist
диапазон результатов от -2 до 2. Затем вы передаете это acos()
, чей аргумент должен быть между -1 и 1. Таким образом acos(2)
например дает NaN. Все остальное оттуда тоже дает NaN.
Я не уверен, какая именно формула должна быть, но именно отсюда исходит ваш NaN. Дважды проверьте свою тригонометрию.
Алгоритм будет выдавать NaN, если точки расположены слишком близко друг к другу. В этом случае $ dist получает значение 1. acos (1) равно NaN. Все последующие вычисления также дают NaN.
Вы округляете координаты в качестве первого шага, поэтому становится более вероятным, что значения станут равными после округления, и получится NaN
Это сферический закон косинусов, который вы используете? Я бы переключился на формулу Haversine:
function distance($lat1, $lon1, $lat2, $lon2)
{
$radius = 3959; //approximate mean radius of the earth in miles, can change to any unit of measurement, will get results back in that unit
$delta_Rad_Lat = deg2rad($lat2 - $lat1); //Latitude delta in radians
$delta_Rad_Lon = deg2rad($lon2 - $lon1); //Longitude delta in radians
$rad_Lat1 = deg2rad($lat1); //Latitude 1 in radians
$rad_Lat2 = deg2rad($lat2); //Latitude 2 in radians
$sq_Half_Chord = sin($delta_Rad_Lat / 2) * sin($delta_Rad_Lat / 2) + cos($rad_Lat1) * cos($rad_Lat2) * sin($delta_Rad_Lon / 2) * sin($delta_Rad_Lon / 2); //Square of half the chord length
$ang_Dist_Rad = 2 * asin(sqrt($sq_Half_Chord)); //Angular distance in radians
$distance = $radius * $ang_Dist_Rad;
return $distance;
}
Вы должны быть в состоянии изменить радиус Земли на любую форму измерения от радиуса в световых годах до радиуса в нанометрах и получить правильное число обратно для используемой единицы.
Спасибо за все ответы здесь — в результате я сделал функцию, которая объединяет вычисления и тесты для NaN в каждом, если оба не являются NaN — он усредняет вычисления, если один является NaN, а другой нет — он использует тот это верно и выдает отчет об ошибках для координат, которые не смогли выполнить одно из вычислений:
function distance_slc($lat1, $lon1, $lat2, $lon2) {
$earth_radius = 3960.00; # in miles
$distance = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($lon2-$lon1)) ;
$distance = acos($distance);
$distance = rad2deg($distance);
$distance = $distance * 60 * 1.1515;
$distance1 = round($distance, 4);
// use a second method as well and average
$radius = 3959; //approximate mean radius of the earth in miles, can change to any unit of measurement, will get results back in that unit
$delta_Rad_Lat = deg2rad($lat2 - $lat1); //Latitude delta in radians
$delta_Rad_Lon = deg2rad($lon2 - $lon1); //Longitude delta in radians
$rad_Lat1 = deg2rad($lat1); //Latitude 1 in radians
$rad_Lat2 = deg2rad($lat2); //Latitude 2 in radians
$sq_Half_Chord = sin($delta_Rad_Lat / 2) * sin($delta_Rad_Lat / 2) + cos($rad_Lat1) * cos($rad_Lat2) * sin($delta_Rad_Lon / 2) * sin($delta_Rad_Lon / 2); //Square of half the chord length
$ang_Dist_Rad = 2 * asin(sqrt($sq_Half_Chord)); //Angular distance in radians
$distance2 = $radius * $ang_Dist_Rad;
//echo "distance=$distance and distance2=$distance2\n";
$avg_distance=-1;
$distance1=acos(2);
if((!is_nan($distance1)) && (!is_nan($distance2))){
$avg_distance=($distance1+$distance2)/2;
} else {
if(!is_nan($distance1)){
$avg_distance=$distance1;
try{
throw new Exception("distance1=NAN with lat1=$lat1 lat2=$lat2 lon1=$lon1 lon2=$lon2");
} catch(Exception $e){
trigger_error($e->getMessage());
trigger_error($e->getTraceAsString());
}
}
if(!is_nan($distance2)){
$avg_distance=$distance2;
try{
throw new Exception("distance1=NAN with lat1=$lat1 lat2=$lat2 lon1=$lon1 lon2=$lon2");
} catch(Exception $e){
trigger_error($e->getMessage());
trigger_error($e->getTraceAsString());
}
}
}
return $avg_distance;
}
HTH кто-то в будущем, а также.