我有一个函数可以计算两个 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。
您从数据库中提取的值可能是字符串,这会导致此问题。
您可能还想检查 Kolink 在他的帖子中提出的问题。
这是您使用的余弦球面定律吗? 我会改用半正矢公式:
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 而另一个不是 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 也是未来的某个人。
如果两点相同,则 acos 函数的自变量为
sin^(bg+cos^2(bg)*cos(lg-lg =
sin^(bg+cos^2(bg)*cos(0) =
sin^(bg+cos^2(bg)*1 =
sin^(bg+cos^2(bg) =
1
显示中间结果以及所有小数位,并检查结果是否恰好为 1,而不是类似的内容 1,00000000000000003。 发生这种情况是因为最后一位数字总是四舍五入。如果最后两位数字偶然四舍五入,则总和可能稍大。但是,它不能超过 1,因为这样 acos 就会失败并返回 NaN(不是数字)。