geohash实现附近的工作室,php函数实现-tp5
geohash简介:
geohash是一种地址编码,它能把二维的经纬度编码成一维的字符串。
geohash有以下几个特点:
首先,geohash用一个字符串表示经度和纬度两个坐标。某些情况下无法在两列上同时应用索引 (例如MySQL 4之前的版本,Google App Engine的数据层等),利用geohash,只需在一列上应用索引即可。
其次,geohash表示的并不是一个点,而是一个矩形区域。比如编码wx4g0ec19,它表示的是一个矩形区域。 使用者可以发布地址编码,既能表明自己位于北海公园附近,又不至于暴露自己的精确坐标,有助于隐私保护。
第三,编码的前缀可以表示更大的区域。例如wx4g0ec1,它的前缀wx4g0e表示包含编码wx4g0ec1在内的更大范围。
这个特性可以用于附近地点搜索。首先根据用户当前坐标计算geohash(例如wx4g0ec1)然后取其前缀进行查询 (SELECT * FROM
place WHERE geohash LIKE ‘wx4g0e%’),即可查询附近的所有地点。Geohash比直接用经纬度的高效很多。
用途:
移动互联网,lbs可以说是一个基础应用,geohash对于解决附近地点搜索提供了一个有效的解决方案。
相关函数
/********** geohash是一种地址编码,它能把二维的经纬度编码成一维的字符串。 * 得到编码 * $latitude //纬度 * $longitude //经度 * $precision //精密度, 默认是12 * @return string */ function encode_geohash($latitude, $longitude, $deep) { $BASE32 = '0123456789bcdefghjkmnpqrstuvwxyz'; $bits = array(16,8,4,2,1); $lat = array(-90.0, 90.0); $lon = array(-180.0, 180.0); $bit = $ch = $i = 0; $is_even = 1; $i = 0; $geohash = ''; while($i < $deep) { if ($is_even) { $mid = ($lon[0] + $lon[1]) / 2; if($longitude > $mid) { $ch |= $bits[$bit]; $lon[0] = $mid; }else{ $lon[1] = $mid; } } else{ $mid = ($lat[0] + $lat[1]) / 2; if($latitude > $mid) { $ch |= $bits[$bit]; $lat[0] = $mid; }else{ $lat[1] = $mid; } } $is_even = !$is_even; if ($bit < 4) $bit++; else { $i++; $geohash .= $BASE32[$ch]; $bit = 0; $ch = 0; } } return $geohash; } /******geohash解码 * @param $geohash * @return array */ function decode_geohash($geohash) { $geohash = strtolower($geohash); $BASE32 = '0123456789bcdefghjkmnpqrstuvwxyz'; $bits = array(16,8,4,2,1); $lat = array(-90.0, 90.0); $lon = array(-180.0, 180.0); $hashlen = strlen($geohash); $is_even = 1; for($i = 0; $i < $hashlen; $i++ ) { $of = strpos($BASE32,$geohash[$i]); for ($j=0; $j<5; $j++) { $mask = $bits[$j]; if ($is_even) { $lon[!($of&$mask)] = ($lon[0] + $lon[1])/2; } else { $lat[!($of&$mask)] = ($lat[0] + $lat[1])/2; } $is_even = !$is_even; } } $point = array( 0 => ($lat[0] + $lat[1]) / 2, 1 => ($lon[0] + $lon[1]) / 2); return $point; } /** * 计算两个经纬度距离 * @param $lat1 纬度 * @param $lng1 经度 * @param $lat2 纬度 * @param $lng2 经度 */ function get_distance($lat1, $lng1, $lat2, $lng2){ $earthRadius = 6367000; //approximate radius of earth in meters $lat1 = ($lat1 * pi() ) / 180; $lng1 = ($lng1 * pi() ) / 180; $lat2 = ($lat2 * pi() ) / 180; $lng2 = ($lng2 * pi() ) / 180; $calcLongitude = $lng2 - $lng1; $calcLatitude = $lat2 - $lat1; $stepOne = pow(sin($calcLatitude / 2), 2) + cos($lat1) * cos($lat2) * pow(sin($calcLongitude / 2), 2); $stepTwo = 2 * asin(min(1, sqrt($stepOne))); $calculatedDistance = $earthRadius * $stepTwo; return round($calculatedDistance); }
实际场景,附近的工作室
代码示例
$latitude=$this->request->param('latitude',39.4);//纬度 $longitude=$this->request->param('longitude',116);//经度 if(!$longitude||!$latitude){ $this->error('位置参数不全'); } $now_geohash=encode_geohash($latitude, $longitude, $deep=3);//当前位置的区域码,码位数越短范围越大,可以通过增大deep的值来缩小搜索范围 $list=Db::name('studio')->where('geohash','like',$now_geohash.'%')->page($page,100)->field('id,name,createtime,geohash,latitude,longitude')->select();
配合数据就达到了搜索附近工作室的效果
小技巧,工作室入住的时候存经纬度,同时生成geohash码,存的时候位数保留最长
关于geohash原理参考此篇文档 http://blog.mryxh.cn/291.html
版权声明:
作者:admin
链接:http://blog.mryxh.cn/289.html
文章版权归作者所有,未经允许请勿转载。
THE END