GEO的底层结构使用的是Sorted set,由于其score不支持多参数,所以使用GeoHash算法进行编码
使用二分区间 + 区间编码,将经纬度转换成二进制表示
/**
* GEO
* Class GEO
*/
class GEO
{
public function hash($longitude, $latitude, $count)
{
$longitudeHash = $this->compute($longitude, $count, 180);
$latitudeHash = $this->compute($latitude, $count, 90);
$str = '';
for ($i = 0; $i < $count; $i++) {
$str .= $longitudeHash[$i] . $latitudeHash[$i];
}
return $str;
}
private function compute($value, $count = 5, $max = 180)
{
$left = [-$max, 0];
$right = [0, $max];
$str = '';
while ($count-- > 0) {
$copy = [];
if ($value >= $right[0] && $value <= $right[1]) {
$str .= '1';
$copy = $right;
} else {
$str .= '0';
$copy = $left;
}
$avg = array_sum($copy) / 2;
$left = [$copy[0], $avg];
$right = [$avg, $copy[1]];
}
return $str;
}
}
$geo = new GEO();
echo $geo->hash(116.37, 39.86, 20);
// 第一列
echo $geo->hash(-170, -70, 2) . PHP_EOL; //0000
echo $geo->hash(-170, -30, 2) . PHP_EOL; //0001
echo $geo->hash(-170, 30, 2) . PHP_EOL; //0100
echo $geo->hash(-170, 70, 2) . PHP_EOL; //0101
//第二列
echo $geo->hash(-70, -70, 2) . PHP_EOL; //0010
echo $geo->hash(-70, -30, 2) . PHP_EOL; //0011
echo $geo->hash(-70, 30, 2) . PHP_EOL; //0110
echo $geo->hash(-70, 70, 2) . PHP_EOL; //0111
//第三列
echo $geo->hash(70, -70, 2) . PHP_EOL; //1000
echo $geo->hash(70, -30, 2) . PHP_EOL; //1001
echo $geo->hash(70, 30, 2) . PHP_EOL; //1100
echo $geo->hash(70, 70, 2) . PHP_EOL; //1101
//第四列
echo $geo->hash(170, -70, 2) . PHP_EOL; //1010
echo $geo->hash(170, -30, 2) . PHP_EOL; //1011
echo $geo->hash(170, 30, 2) . PHP_EOL; //1110
echo $geo->hash(170, 70, 2) . PHP_EOL; //1111
可能GeoHash不太好理解,下面我从其大致的结果画图进行分析

从图中不难看出,值越接近的块的区域是比较近的,但是存在一点误差(比如:0111->1000),所以要多取周围几个块进行比较
# GEOADD key longitude latitude member 给key的指定位置(member) 添加经纬度信息
# member:key中的位置
geoadd car_locations 116.37 39.86 1001
geoadd car_locations 116.55 39.93 1002
geoadd car_locations 117.12 41.22 1003
# geopos key member 展示指定key的指定位置
geopos car_locations 1001
# GEODIST key member1 member2 [m|km|ft|mi] 返回两个位置的距离,常用单位m,km
geodist car_locations 1001 1002 km
# GEORADIUS key longitude latitude radius m|km|ft|mi,以经纬度为圆心,从key中查找符合指定radius距离的位置
georadius car_locations 116.33 39.82 8 km
# 后面可以跟一些排序,条数
# 获取符合条件的倒序的两个位置
georadius car_locations 116.33 39.82 200 km desc count 2
5.由于底层使用的是Sorted sort,故可以使用其相关操作方法