尋找附近網點geohash演算法及實現 (PHP版本)

來源:互聯網
上載者:User
參考文檔:

http://blog.csdn.net/wangxiafghj/article/details/9014363geohash 演算法原理及實現方式
http://blog.charlee.li/geohash-intro/ geohash:用字串實現附近地點搜尋
http://blog.sina.com.cn/s/blog_7c05385f0101eofb.html 尋找附近點--Geohash方案討論
http://www.wubiao.info/372 尋找附近的xxx 球面距離以及Geohash方案探討
http://en.wikipedia.org/wiki/Haversine_formula Haversine formula球面距離公式
http://www.codecodex.com/wiki/Calculate_Distance_Between_Two_Points_on_a_Globe 球面距離公式代碼實現
http://developer.baidu.com/map/jsdemo.htm#a6_1 球面距離公式驗證
http://www.wubiao.info/470 Mysql or Mongodb LBS快速實現方案


geohash有以下幾個特點:

首先,geohash用一個字串表示經度和緯度兩個座標。某些情況下無法在兩列上同時應用索引 (例如MySQL 4之前的版本,Google App Engine的資料層等),利用geohash,只需在一列上應用索引即可。

其次,geohash表示的並不是一個點,而是一個矩形地區。比如編碼wx4g0ec19,它表示的是一個矩形地區。 使用者發行就緒地址編碼,既能表明自己位於北海公園附近,又不至於暴露自己的精確座標,有助於隱私保護。

第三,編碼的首碼可以表示更大的地區。例如wx4g0ec1,它的首碼wx4g0e表示包含編碼wx4g0ec1在內的更大範圍。 這個特性可以用於附近地點搜尋。首先根據使用者當前座標計算geohash(例如wx4g0ec1)然後取其首碼進行查詢 (SELECT * FROM place WHERE geohash LIKE 'wx4g0e%'),即可查詢附近的所有地點。

Geohash比直接用經緯度的高效很多。


Geohash演算法實現(PHP版本)

codingMap[substr($this->coding,$i,1)]=str_pad(decbin($i), 5, "0", STR_PAD_LEFT);        }      }      public function decode($hash)    {        $binary="";        $hl=strlen($hash);        for($i=0; $i<$hl; $i++)        {            $binary.=$this->codingMap[substr($hash,$i,1)];        }          $bl=strlen($binary);        $blat="";        $blong="";        for ($i=0; $i<$bl; $i++)        {            if ($i%2)                $blat=$blat.substr($binary,$i,1);            else                $blong=$blong.substr($binary,$i,1);          }           $lat=$this->binDecode($blat,-90,90);         $long=$this->binDecode($blong,-180,180);        // $lat=$this->binDecode($blat,2,54);        // $long=$this->binDecode($blong,72,136);          $latErr=$this->calcError(strlen($blat),-90,90);        $longErr=$this->calcError(strlen($blong),-180,180);          $latPlaces=max(1, -round(log10($latErr))) - 1;        $longPlaces=max(1, -round(log10($longErr))) - 1;          $lat=round($lat, $latPlaces);        $long=round($long, $longPlaces);          return array($lat,$long);    }      public function encode($lat,$long)    {        $plat=$this->precision($lat);        $latbits=1;        $err=45;        while($err>$plat)        {            $latbits++;            $err/=2;        }          $plong=$this->precision($long);        $longbits=1;        $err=90;        while($err>$plong)        {            $longbits++;            $err/=2;        }          $bits=max($latbits,$longbits);          $longbits=$bits;        $latbits=$bits;        $addlong=1;        while (($longbits+$latbits)%5 != 0)        {            $longbits+=$addlong;            $latbits+=!$addlong;            $addlong=!$addlong;        }          $blat=$this->binEncode($lat,-90,90, $latbits);          $blong=$this->binEncode($long,-180,180,$longbits);          $binary="";        $uselong=1;        while (strlen($blat)+strlen($blong))        {            if ($uselong)            {                $binary=$binary.substr($blong,0,1);                $blong=substr($blong,1);            }            else            {                $binary=$binary.substr($blat,0,1);                $blat=substr($blat,1);            }            $uselong=!$uselong;        }          $hash="";        for ($i=0; $icoding[$n];        }          return $hash;    }      private function calcError($bits,$min,$max)    {        $err=($max-$min)/2;        while ($bits--)            $err/=2;        return $err;    }      private function precision($number)    {        $precision=0;        $pt=strpos($number,'.');        if ($pt!==false)        {            $precision=-(strlen($number)-$pt-1);        }          return pow(10,$precision)/2;    }      private function binEncode($number, $min, $max, $bitcount)    {        if ($bitcount==0)            return "";        $mid=($min+$max)/2;        if ($number>$mid)            return "1".$this->binEncode($number, $mid, $max,$bitcount-1);        else            return "0".$this->binEncode($number, $min, $mid,$bitcount-1);    }      private function binDecode($binary, $min, $max)    {        $mid=($min+$max)/2;          if (strlen($binary)==0)            return $mid;          $bit=substr($binary,0,1);        $binary=substr($binary,1);          if ($bit==1)            return $this->binDecode($binary, $mid, $max);        else            return $this->binDecode($binary, $min, $mid);    }}  ?>

測試執行個體


以下網點離我最近:"; //開始$b_time = microtime(true); //方案A,直接利用資料庫儲存函數,遍曆排序 //方案B geohash求出附近,然後排序 //當前 geohash值 $n_geohash = $geohash->encode($n_latitude,$n_longitude); //附近$n = 3;$like_geohash = substr($n_geohash, 0, $n); $sql = 'select * from retailersinfotable where geohash like "'.$like_geohash.'%"'; $query = mysql_query($sql);if(mysql_num_rows($query)){while($row=mysql_fetch_array($query)){$data[] = array ("latitude" =>$row["Latitude"],"longitude"=>$row["Longitude"],"name" =>$row["RetailersName"],);}} //算出實際距離 foreach($data as $key=>$val){ $distance = getDistance($n_latitude,$n_longitude,$val['latitude'],$val['longitude']); $data[$key]['distance'] = $distance; //排序列 $sortdistance[$key] = $distance;} //距離排序array_multisort($sortdistance,SORT_ASC,$data); //結束$e_time = microtime(true); echo "(計算耗時:" ;echo $e_time - $b_time; echo "秒)
"; //var_dump($data); foreach($data as $key=>$val){echo "
";echo $val['distance']. " 米-------".$val['name'];} /*** @desc 根據兩點間的經緯度計算距離* @param float $latitude 緯度值* @param float $longitude 經度值*/function getDistance($latitude1, $longitude1, $latitude2, $longitude2) {$earth_radius = 6371000; //approximate radius of earth in meters$dLat = deg2rad($latitude2 - $latitude1);$dLon = deg2rad($longitude2 - $longitude1); /* Using the Haversine formula http://en.wikipedia.org/wiki/Haversine_formula http://www.codecodex.com/wiki/Calculate_Distance_Between_Two_Points_on_a_Globe 驗證:百度地圖 http://developer.baidu.com/map/jsdemo.htm#a6_1 calculate the distance */$a = sin($dLat/2) * sin($dLat/2) + cos(deg2rad($latitude1)) * cos(deg2rad($latitude2)) * sin($dLon/2) * sin($dLon/2);$c = 2 * asin(sqrt($a));$d = $earth_radius * $c;return round($d); //四捨五入} ?>
  • 相關文章

    聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.