查找附近网点geohash算法及实现 (PHP版本)

参考文档:

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版本)

<?php
class Geohash
{
    private $coding="0123456789bcdefghjkmnpqrstuvwxyz";
    private $codingMap=array();

    public function Geohash()
    {
        for($i=0; $i<32; $i++)
        {
            $this->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; $i<strlen($binary); $i+=5)
        {
            $n=bindec(substr($binary,$i,5));
            $hash=$hash.$this->coding[$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);
    }
}

?>

测试实例

<?php

require_once("db_config.php");
require_once('geohash.class.php');

$geohash=new Geohash;

//经纬度转换成Geohash

//获取附近的信息

$n_latitude  =  34.236080797698;
$n_longitude = 109.0145193757;

echo "当前位置为:经度108.7455,纬度34.3608<br/><br/>
以下网点离我最近:";

//开始
$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 "秒)<br/>";

//var_dump($data);

  foreach($data as $key=>$val)
{
	echo "</br>";
	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);   //四舍五入
}

?>
时间: 2024-11-29 11:57:02

查找附近网点geohash算法及实现 (PHP版本)的相关文章

查找附近网点geohash算法及实现 (Java版本)

参考文档: 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        

查找最近距离geohash算法(增加周边邻近编号)

接着上一篇文章:查找附近网点geohash算法及实现 (Java版本) http://blog.csdn.net/sunrise_2013/article/details/42024813 参考文档: http://www.slideshare.net/sandeepbhaskar2/geohash  介绍geohash原理及例子 http://geohash.gofreerange.com/    演示实例 http://geohash.gofreerange.com/    周边8格子实例

故障定位之查找附近点GeoHash研讨

随着移动终端的普及,很多应用都基于LBS功能,附近的某某(餐馆.银行.妹纸等等). 基础数据中,一般保存了目标位置的经纬度:利用用户提供的经纬度,进行对比,从而获得是否在附近. 目标:查找附近的XXX,由近到远返回结果,且结果中有与目标点的距离. 针对查找附近的XXX,提出两个方案,如下: 一.方案A:=================================================================================================

查找附近点--Geohash方案讨论

转载自:http://blog.csdn.net/wangliqiang1014/article/details/9143825 随着移动终端的普及,很多应用都基于LBS功能,附近的某某(餐馆.银行.妹纸等等). 基础数据中,一般保存了目标位置的经纬度:利用用户提供的经纬度,进行对比,从而获得是否在附近. 目标:查找附近的XXX,由近到远返回结果,且结果中有与目标点的距离. 针对查找附近的XXX,提出两个方案,如下: 一.方案A:=================================

空间索引 - GeoHash算法及其实现优化

h1,h2,h3,h4,h5,h6,p,blockquote { margin: 0; padding: 0 } body { font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", Arial, sans-serif; font-size: 13px; line-height: 18px; color: #737373; background-color: white; margin: 10px

Geohash 算法学习

Geohash 算法: 这是一套纬度/经度地理编码算法,把纬度/经度编码成base32位的字符串.这种编码和纬度/经度不是唯一对应,其实是一个纬度/经度区间.算法有一个精度概念,精度越高,字符串越长,所表示的区间越小.可以编码后的字符串想象成一个格子,里面存放一些纬度/经度值.格子趋近很小的时候,只能存放一纬度/经度值,那么编码和纬度/经度就是唯一对应的关系.但是这个不是重点,这套算法目的就是把纬度/经度编码成近似值,通过近似值搜索,就能很高效的缩小范围,然后再从小范围里查找精确值. 例如,坐标

geohash算法原理及实现方式

1.geohash特点 2.geohash原理 3.geohash的php .python.java.C#实现代码 4.观点讨论 w微博:http://weibo.com/dxl0321 geohash有以下几个特点: 首先,geohash用一个字符串表示经度和纬度两个坐标.某些情况下无法在两列上同时应用索引 (例如MySQL 4之前的版本,Google App Engine的数据层等),利用geohash,只需在一列上应用索引即可. 其次,geohash表示的并不是一个点,而是一个矩形区域.比

GeoHash算法附近寻址

原文出处: zhanlijun    引子 机机是个好动又好学的孩子,平日里就喜欢拿着手机地图点点按按来查询一些好玩的东西.某一天机机到北海公园游玩,肚肚饿了,于是乎打开手机地图,搜索北海公园附近的餐馆,并选了其中一家用餐. 饭饱之后机机开始反思了,地图后台如何根据自己所在位置查询来查询附近餐馆的呢?苦思冥想了半天,机机想出了个方法:计算所在位置P与北京所有餐馆的距离,然后返回距离<=1000米的餐馆.小得意了一会儿,机机发现北京的餐馆何其多啊,这样计算不得了,于是想了,既然知道经纬度了,那它应

Geohash算法

1.算法背景 Geohash的初衷是如何用尽量短的URL来标志地图上的某个位置,而地图上的位置一般是用经纬度来表示,问题就转化为如何把经纬度转化为一个尽量短的URL. Geohash的算法描述请参考:http://en.wikipedia.org/wiki/Geohash ,本文的主要目的是更加细致地解释该算法的原理及实用场景. 2.算法 算法的主要思想是对某一数字通过二分法进行无限逼近,比如纬度的区间是[-90,90],假如给定一个纬度值:46.5,可以通过下面算法对46.5进行无限逼近: (