【问题标题】:Geo Search Without Distance无距离地理搜索
【发布时间】:2012-07-18 20:43:14
【问题描述】:

这是我第一次看到地理位置。请原谅我的长问题。我希望它是清楚的。

我有一个包含 lat & lng 记录的数据库。我正在尝试编写一个 PHP 类,它定义了四个形成边界框的十进制坐标(然后检索框中的所有记录)。

我只需要知道在用户位置 n 公里范围内的位置。我不需要从用户到位置的距离。

我正在构建answer 的第一部分。

以上答案的核心是:

直线10公里是:

on the latitude is equal to ~1'(minute)
on the longitude is equal to ~6'(minutes)

以此为基础,做一些快速的数学运算,然后在您的查询中添加 WHERE 子句删除“框”之外的任何位置 通过添加缓冲区创建,假设为 1' lat & 6' 长。

嗯,Patrick 说:“做一些快速的数学运算”。几天后我没能正确地做数学题。

我的班级在那里挂着,似乎工作了 10 公里。但是,我无法使其适用于任何其他距离。

首先我移动到秒,所以 10 公里是:

on the latitude is equal to 60 seconds
on the longitude is equal to 360 seconds

这很好用。

不过,我后来试了5km是:

on the latitude is equal to 30 seconds
on the longitude is equal to 180 seconds

这不起作用。

您能解释一下为什么这种方法在 5 公里(以及我尝试过的其他距离)内不起作用吗?

以下是我的课程代码。显然开关是垃圾。任何帮助表示赞赏。

class coords 
{
/*
    Calculate a bounding box from a decimal coordinate and distance.
    The public vars are populated with minimum and maximum longitudes and latitudes which define the boundary.
*/
    public $dec_lat_min;
    public $dec_lat_max;
    public $dec_lng_min;
    public $dec_lng_max;

    function init($dec_lat, $dec_lng, $dist) 
    { 
        // This switch is a terrible way to allow multiple distances.

        // 10km = 1 min lat = 60 sec lat
        // 10km = 6 min lng = 360 sec lng

        // 5km = 30 sec lat
        // 5km = 180 sec lng

        // 1km = 6 sec lat
        // 1km = 36 sec lat

        // 500m = 3 sec lat
        // 500m = 18 sec lat

        switch($dist)
        {
            case 10: // 10km
                $sec_diff_lat = 60;
                $sec_diff_lng = 360;
                break;
            case 5: // 5km
                $sec_diff_lat = 30;
                $sec_diff_lng = 180;
                break;
            case 1: // 1km
                $sec_diff_lat = 6;
                $sec_diff_lng = 36;
                break;
            default: // 500m
                $sec_diff_lat = 3;
                $sec_diff_lng = 18;
                break;  
        }

        // Convert lat to DMS
        $dms_lat = $this->dec2dms($dec_lat);

        // Allow for western hemisphere (ie negative)
        $dms_lat['hem'] == '-' ? $h = -1 : $h = 1;

        // Populate min and max latitudes
        $this->dec_lat_min = $this->dms2dec($dms_lat['deg'],$dms_lat['min'],$dms_lat['sec']+(-1 * $sec_diff_lat * $h),$dms_lat['hem']);
        $this->dec_lat_max = $this->dms2dec($dms_lat['deg'],$dms_lat['min'],$dms_lat['sec']+($sec_diff_lat * $h),$dms_lat['hem']);

        $dms_lng = $this->dec2dms($dec_lng);

        $dms_lng['hem'] == '-' ? $h = -1 : $h = 1;

        $this->dec_lng_min = $this->dms2dec($dms_lng['deg'],$dms_lng['min'],$dms_lng['sec']+(-1 * $sec_diff_lng * $h),$dms_lng['hem']);
        $this->dec_lng_max = $this->dms2dec($dms_lng['deg'],$dms_lng['min'],$dms_lng['sec']+($sec_diff_lng * $h),$dms_lng['hem']);

    }

    function dec2dms($d) 
    {
        $d = (string)$d;

        // got dashes?
        if ($d[0] == "-") {
            $hem = '-';
            $dVal = substr($d,1);
        } else {
            $hem = '';
            $dVal = $d;
        }

        // degrees = degrees
        $dVals = explode('.', $dVal);
        $dmsDeg = $dVals[0];

        // * 60 = mins
        $dRemainder = ('0.'.$dVals[1]) * 60;
        $dmsMinVals = explode('.', $dRemainder);
        $dmsMin = $dmsMinVals[0];

        // * 60 again = secs
        $dMinRemainder = ('0.'.$dmsMinVals[1]) * 60;
        $dmsSec = round($dMinRemainder);

        return array("deg"=>$dmsDeg,"min"=>$dmsMin,"sec"=>$dmsSec, "hem"=>$hem);
    }

    function dms2dec($deg,$min,$sec,$hem) 
    {
        // find decimal latitude
        $d = $deg+((($min*60)+($sec))/3600);
        $d = $hem.$d;

        return $this->round100000($d);
    }

    function round100000($v) 
    {
        return round($v * 100000) / 100000;
    } 
}

【问题讨论】:

  • +1 很好解释的问题/问题
  • 你能举一个你的表结构的例子吗?
  • 当我在试验时,表在 mySQL 上:id (INT Autoincrement), description (text), lng (FLOAT(10,6)), lat (FLOAT(10,6))。
  • 我没有采用这种方法。相反,我决定硬着头皮使用 Postgres+Postgis,效果非常好。

标签: php geolocation gis


【解决方案1】:

直线10公里是:经度等于~6分

距离经度比不是一个常数 - 它取决于用户的纬度。经度差可以用这个函数:

function distanceToLng($distance,$latDeg) {
    $latRad = $latDeg / 180 * M_PI;
    $R = 6371;
    return atan2(1*sin($distance/$R)*cos($latRad), cos($distance/$R) - sin($latRad)*sin($latRad)) * (180 / M_PI);
}

公式取自this问题。

您可以使用以下方法计算给定距离的纬度差:

$lngDeg = $distanceInKm/6371 * 180 / M_PI

【讨论】:

  • 谢谢。我曾希望让事情保持简单(和近似)。但是,我现在将拥抱数学并正确地做到这一点。
【解决方案2】:

正如另一个答案指出的那样,您确实想使用考虑到用户纬度的 Haversine 公式。

Haversine 公式:http://en.wikipedia.org/wiki/Haversine_formula

这个其他答案有大量资源可以在 PHP 或 SQL 中完成:

MySQL Great Circle Distance (Haversine formula)

【讨论】:

  • 非常感谢。您链接到的页面包含大量信息。
猜你喜欢
  • 2011-08-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-11
  • 2022-01-24
  • 2012-03-06
  • 1970-01-01
相关资源
最近更新 更多