geohash有以下几个特点:

首先,geohash用一个字符串表示经度和纬度两个坐标。利用geohash,只需在一列上应用索引即可。

其次,geohash表示的并不是一个点,而是一个矩形区域。比如编码wx4g0ec19,它表示的是一个矩形区域。 使用者可以发布地址编码,既能表明自己位于北海公园附近,又不至于暴露自己的精确坐标,有助于隐私保护。

第三,编码的前缀可以表示更大的区域。例如wx4g0ec1,它的前缀wx4g0e表示包含编码wx4g0ec1在内的更大范围。 这个特性可以用于附近地点搜索。首先根据用户当前坐标计算geohash(例如wx4g0ec1)然后取其前缀进行查询 (SELECT * FROM place WHERE geohash LIKE 'wx4g0e%'),即可查询附近的所有地点。

Geohash比直接用经纬度的高效很多。

Geohash基础类:

<?php

/**
* Geohash generation class for php
*/
/**
*
* Encode and decode geohashes
*
* Find neighbors
*
*/
class Geohash
{
private $bitss = [16, 8, 4, 2, 1];
private $neighbors = [];
private $borders = []; private $coding = "0123456789bcdefghjkmnpqrstuvwxyz";
private $codingMap = []; public function __construct()
{ $this->neighbors['right']['even'] = 'bc01fg45238967deuvhjyznpkmstqrwx';
$this->neighbors['left']['even'] = '238967debc01fg45kmstqrwxuvhjyznp';
$this->neighbors['top']['even'] = 'p0r21436x8zb9dcf5h7kjnmqesgutwvy';
$this->neighbors['bottom']['even'] = '14365h7k9dcfesgujnmqp0r2twvyx8zb'; $this->borders['right']['even'] = 'bcfguvyz';
$this->borders['left']['even'] = '0145hjnp';
$this->borders['top']['even'] = 'prxz';
$this->borders['bottom']['even'] = '028b'; $this->neighbors['bottom']['odd'] = $this->neighbors['left']['even'];
$this->neighbors['top']['odd'] = $this->neighbors['right']['even'];
$this->neighbors['left']['odd'] = $this->neighbors['bottom']['even'];
$this->neighbors['right']['odd'] = $this->neighbors['top']['even']; $this->borders['bottom']['odd'] = $this->borders['left']['even'];
$this->borders['top']['odd'] = $this->borders['right']['even'];
$this->borders['left']['odd'] = $this->borders['bottom']['even'];
$this->borders['right']['odd'] = $this->borders['top']['even']; //build map from encoding char to 0 padded bitfield
for($i=0; $i<32; $i++) {
$this->codingMap[substr($this->coding, $i, 1)] = str_pad(decbin($i), 5, "0", STR_PAD_LEFT);
} } /**
* Decode a geohash and return an array with decimal lat,long in it
*/
public function decode($hash)
{
//decode hash into binary string
$binary = "";
$hl = strlen($hash);
for ($i=0; $i<$hl; $i++) { $binary .= $this->codingMap[substr($hash, $i, 1)];
} //split the binary into lat and log binary strings
$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);
}
} //now concert to decimal
$lat = $this->binDecode($blat, -90, 90);
$long = $this->binDecode($blong, -180, 180); //figure out how precise the bit count makes this calculation
$latErr = $this->calcError(strlen($blat), -90, 90);
$longErr = $this->calcError(strlen($blong), -180, 180); //how many decimal places should we use? There's a little art to
//this to ensure I get the same roundings as geohash.org
$latPlaces = max(1, -round(log10($latErr))) - 1;
$longPlaces = max(1, -round(log10($longErr))) - 1; //round it
$lat = round($lat, $latPlaces);
$long = round($long, $longPlaces);
return array($lat, $long);
} private function calculateAdjacent($srcHash, $dir)
{
$srcHash = strtolower($srcHash);
$lastChr = $srcHash[strlen($srcHash) - 1];
$type = (strlen($srcHash) % 2) ? 'odd' : 'even';
$base = substr($srcHash, 0, strlen($srcHash) - 1); if (strpos($this->borders[$dir][$type], $lastChr) !== false) {
$base = $this->calculateAdjacent($base, $dir);
} return $base . $this->coding[strpos($this->neighbors[$dir][$type], $lastChr)];
} public function neighbors($srcHash)
{
$geohashPrefix = substr($srcHash, 0, strlen($srcHash) - 1); $neighbors['top'] = $this->calculateAdjacent($srcHash, 'top');
$neighbors['bottom'] = $this->calculateAdjacent($srcHash, 'bottom');
$neighbors['right'] = $this->calculateAdjacent($srcHash, 'right');
$neighbors['left'] = $this->calculateAdjacent($srcHash, 'left'); $neighbors['topleft'] = $this->calculateAdjacent($neighbors['left'], 'top');
$neighbors['topright'] = $this->calculateAdjacent($neighbors['right'], 'top');
$neighbors['bottomright'] = $this->calculateAdjacent($neighbors['right'], 'bottom');
$neighbors['bottomleft'] = $this->calculateAdjacent($neighbors['left'], 'bottom'); return $neighbors;
} /**
* Encode a hash from given lat and long
*/
public function encode($lat, $long)
{
//how many bits does latitude need?
$plat = $this->precision($lat);
$latbits = 1;
$err = 45;
while ($err > $plat) {
$latbits++;
$err /= 2;
} //how many bits does longitude need?
$plong = $this->precision($long);
$longbits = 1;
$err = 90;
while ($err > $plong) {
$longbits++;
$err /= 2;
} //bit counts need to be equal
$bits = max($latbits, $longbits); //as the hash create bits in groups of 5, lets not
//waste any bits - lets bulk it up to a multiple of 5
//and favour the longitude for any odd bits
$longbits = $bits;
$latbits = $bits;
$addlong = 1;
while (($longbits + $latbits) % 5 != 0) {
$longbits += $addlong;
$latbits += !$addlong;
$addlong = !$addlong;
} //encode each as binary string
$blat = $this->binEncode($lat, -90, 90, $latbits);
$blong = $this->binEncode($long, -180, 180, $longbits); //merge lat and long together
$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;
} //convert binary string to hash
$hash = "";
for ($i=0; $i<strlen($binary); $i+=5) {
$n = bindec(substr($binary, $i, 5));
$hash = $hash.$this->coding[$n];
} return $hash;
} /**
* What's the maximum error for $bits bits covering a range $min to $max
*/
private function calcError($bits, $min, $max)
{
$err = ($max - $min) / 2; while ($bits--) {
$err /= 2;
} return $err;
} /*
* returns precision of number
* precision of 42 is 0.5
* precision of 42.4 is 0.05
* precision of 42.41 is 0.005 etc
*
*/
private function precision($number)
{
$precision = 0;
$pt = strpos($number,'.');
if ($pt !== false) {
$precision = -(strlen($number) - $pt - 1);
} return pow(10, $precision) / 2;
} /**
* create binary encoding of number as detailed in http://en.wikipedia.org/wiki/Geohash#Example
* removing the tail recursion is left an exercise for the reader
*
* Author: Bruce Chen (weibo: @一个开发者)
*/
private function binEncode($number, $min, $max, $bitcount)
{
if ($bitcount == 0) return ""; #echo "$bitcount: $min $max<br>"; //this is our mid point - we will produce a bit to say
//whether $number is above or below this mid point
$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);
}
} /**
* decodes binary encoding of number as detailed in http://en.wikipedia.org/wiki/Geohash#Example
* removing the tail recursion is left an exercise for the reader
*
*/
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);
}
}
}

  测试实例:

$geohash = new GeoHash();
$hash = $geohash->encode($latitude, $longitude);
//var_dump($hash);exit;
//决定查询范围,值越大,获取的范围越小
//当geohash base32编码长度为8时,精度在19米左右,而当编码长度为9时,精度在2米左右,编码长度需要根据数据情况进行选择。
$pre_hash = substr($hash, 0, 5);
//取出相邻八个区域
$neighbors = $geohash->neighbors($pre_hash);
array_push($neighbors, $pre_hash); $values = '';
foreach ($neighbors as $key=>$val) {
$values .= '\'' . $val . '\'' .',';
}
$values = substr($values, 0, -1);
// var_dump($values);
$stores=\DB::select("SELECT * FROM `stores` WHERE LEFT(`geohash`,5) IN ($values)"); foreach ($stores as $key => $value) {
$geohash_arr=$geohash->decode($value->geohash);
$stores[$key]->latitude=$geohash_arr[0];//纬度
$stores[$key]->longitude=$geohash_arr[1];//经度
$distance=$this->getDistance($request['latitude'],$request['longitude'],$value->latitude,$value->longitude);
$stores[$key]->distance=$distance;
$sortdistance[$key] = $distance;
}
array_multisort($sortdistance,SORT_ASC,$stores);
// var_dump($stores);
return response()->json(['status_code'=>0,'nearby_stores'=>$stores]); /**
* @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); //四舍五入
}

  

php通过geohash算法实现查找附近的商铺的更多相关文章

  1. 【算法】(查找你附近的人) GeoHash核心原理解析及代码实现

    本文地址 原文地址 分享提纲: 0. 引子 1. 感性认识GeoHash 2. GeoHash算法的步骤 3. GeoHash Base32编码长度与精度 4. GeoHash算法 5. 使用注意点( ...

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

    參考文档: http://blog.csdn.net/wangxiafghj/article/details/9014363geohash  算法原理及实现方式 http://blog.charlee ...

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

    h1,h2,h3,h4,h5,h6,p,blockquote { margin: 0; padding: 0 } body { font-family: "Helvetica Neue&qu ...

  4. geohash 算法原理及实现方式

    转自:http://www.cnblogs.com/dengxinglin/archive/2012/12/14/2817761.html geohash 算法原理及实现方式 1.geohash 特点 ...

  5. Java中常用的查找算法——顺序查找和二分查找

    Java中常用的查找算法——顺序查找和二分查找 神话丿小王子的博客 一.顺序查找: a) 原理:顺序查找就是按顺序从头到尾依次往下查找,找到数据,则提前结束查找,找不到便一直查找下去,直到数据最后一位 ...

  6. 基于GeoHash算法的附近点搜索实现(一)

    1. 引入 最近在参加学校的计算机仿真大赛,时间好像有点不够,所以只完成了前面的一部分最基础的功能,中途还是选择了放弃.但是之前的部分的确觉得完成得还不错,在这里分享一下.题目是要完成一个宇宙飞船加油 ...

  7. PHP数组基本排序算法和查找算法

    关于PHP中的基础算法,小结一下,也算是本博客的第一篇文章1.2种排序算法冒泡排序:例子:个人见解 5 6 2 3 7 9 第一趟 5 6 2 3 7 9 5 2 6 3 7 9 5 2 3 6 7 ...

  8. cb34a_c++_STL_算法_查找算法_(7)_lower_bound

    cb34a_c++_STL_算法_查找算法_(7)_lower_bound//针对已序区间的查找算法,如set,multiset关联容器-自动排序lower_bound()--第一个可能的位置uppe ...

  9. cb33a_c++_STL_算法_查找算法_(6)binary_search_includes

    cb33a_c++_STL_算法_查找算法_(6)binary_search_includes//针对已序区间的查找算法,如set,multiset关联容器-自动排序binary_search(b,e ...

随机推荐

  1. Codecraft-17 and Codeforces Round #391 (Div. 1 + Div. 2, combined) B

    Bash has set out on a journey to become the greatest Pokemon master. To get his first Pokemon, he we ...

  2. 命令行音乐播放器 CMus

    自从接触到 CMus (C* Music Player) 这个工作在终端里的音乐播放器后,在 Linux 环境里就很少使用 GUI 音乐播放器播放本地音乐文件了.它消耗资源少使用方便,对中文的支持也不 ...

  3. Net Core应用,在CentOS上运行

    Net Core应用,在CentOS上运行 本文主要介绍下运用docker虚拟技术打包Asp.net core应用. Docker作为一个开源的应用容器引擎,近几年得到广泛的应用,使用Docker我们 ...

  4. HDU 5883 F - The Best Path 欧拉通路 & 欧拉回路

    给定一个图,要求选一个点作为起点,然后经过每条边一次,然后把访问过的点异或起来(访问一次就异或一次),然后求最大值. 首先为什么会有最大值这样的分类?就是因为你开始点选择不同,欧拉回路的结果不同,因为 ...

  5. CUBE 隐藏维度中的Unknown

     纬度,属性里面有个unknowmember设置为hidden   

  6. This file's format is not supported or you don't specify a correct format. 解决办法

    string path = @"c:\请假统计表.xlsx"; Workbook workBook = new Workbook(); workBook.Open(path); A ...

  7. WebStorm技巧-集成命令行工具插件

    打开菜单项 File -> Settings-   搜索插件 CMD Support,并安装.   重启WebStorm,在你的项目中新建一个Cmd script 文件,命名为build.cmd ...

  8. 电脑Bois中usb模式启动热键

    组装机主板 品牌笔记本 品牌台式机 主板品牌 启动按键 笔记本品牌 启动按键 台式机品牌 启动按键 华硕主板 F8 联想笔记本 F12 联想台式机 F12 技嘉主板 F12 宏基笔记本 F12 惠普台 ...

  9. linux-2.6.22.6/Makefile:416: *** mixed implicit and normal rules: deprecated syntax

    今天在按照韦东山大哥的教程流程编译内核的时候出现了这个问题      linux-2.6.22.6/Makefile:416: *** mixed implicit and normal rules: ...

  10. 洛谷 P1200 [USACO1.1]你的飞碟在这儿Your Ride Is Here

    你的飞碟在这儿 难度:☆ Code: #include<iostream> #include<cstring> #include<cstdio> using nam ...