PHP 返回 NaN

发布于 2024-12-23 14:38:42 字数 1442 浏览 3 评论 0原文

我有一个函数可以计算两个 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);

I have a function that calculates the distance between two GPS coordinates. I then get all the coordinates from the database and loop through them all to get the distance between the current one and the previous one, then add that to an array for the specific GPS device. For some reason it is return NaN. I have tried casting it as a double, an int, and rounding the number.

Here is my PHP code:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

甜`诱少女 2024-12-30 14:38:42

sin()cos() 的范围介于 -1 和 1 之间。因此,在第一次计算 $dist 时,结果范围为-2 到 2。然后将其传递给 acos(),其参数必须介于 -1 和 1 之间。例如,acos(2) 给出 NaN。那里的其他所有内容也给出 NaN 。

我不确定公式应该是什么,但这就是你的 NaN 的来源。仔细检查你的三角学。

The range of sin() and cos() is between -1 and 1. Therefore in your first calculation of $dist the result range is -2 to 2. You then pass this to acos(), whose argument must be between -1 and 1. Thus acos(2) for example gives NaN. Everything else from there gives NaN as well.

I'm not sure what the formula should be exactly, but that's where your NaN is coming from. Double-check your trigonometry.

魔法少女 2024-12-30 14:38:42

如果点彼此太接近,该算法将产生 NaN。在这种情况下,$dist 的值为 1。acos(1) 为 NaN。所有后续计算也会产生 NaN。
您首先对坐标进行舍入,因此舍入后值更有可能变得相等,并产生 NaN。

The algo will produce NaN if points are too close to each other. In that case $dist gets value 1. acos(1) is NaN. All subsequent calculations produce NaN too.
You round coordinates as the first step, so it makes more probable that the values become equal after rounding, and produce NaN.

梦初启 2024-12-30 14:38:42

您从数据库中提取的值可能是字符串,这会导致此问题。

您可能还想检查 Kolink 在他的帖子中提出的问题。

The values you are pulling from the database may be strings, which would cause this issue.

You may also want to check the issues that Kolink raised in his post.

护你周全 2024-12-30 14:38:42

这是您使用的余弦球面定律吗?我会改用半正弦公式:

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

您应该能够将地球半径更改为任何形式的测量形式,从光年半径到纳米半径,并根据所使用的单位得到正确的数字。

Is that the spherical law of cosines you're using? I'd switch to the Haversine formula:

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

You should be able to change the earth's radius to any form of measurement from radius in light years to radius in nanometers and get the proper number back out for the unit used.

半枫 2024-12-30 14:38:42

感谢这里的所有回复 - 结果我做了一个函数,它结合了每个 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 将来也有人。

Thanks for all the responses here - as a result I made a function which combines to computations and tests for NaN in each, if both are not NaN - it averages the calculation, if one is NaN and the other is not - it uses the one that's valid and gives error report for the coordinates that failed one of the calculation:

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 someone in the future as well.

薆情海 2024-12-30 14:38:42

如果两个点相同,则 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(非数字)。

If both points are identical, the argument of the acos function is

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

Display the intermediate results with all decimal places and check whether the result is exactly 1 and not something like
1,00000000000000003.
This happens because the last digit is always rounded. If both last digits are rounded up by chance, the sum may be slightly too large. However, it must not exceed 1 because then acos fails and returns NaN (Not a Number).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文