zl程序教程

您现在的位置是:首页 >  其他

当前栏目

geohash实现附近的工作室,php函数实现-tp5

2023-03-07 09:47:15 时间

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

未经允许不得转载:肥猫博客 » geohash实现附近的工作室,php函数实现-tp5