本文實例講述了PHP進階學習之Geo的地圖定位算法。分享給大家供大家參考,具體如下:
前言
日常開發中我們經常需要查找某個物體的定位,或者查找附近的范圍等,我們自然而然會想到的方法就是利用各種提供服務的地圖網站的API,基于API,用經緯度去實現定位和查找附近范圍等等。然而,由于原理沒有做一個了解和一定的認識,在對比距離遠近關系或者控制精確程度方面,我們并不了解怎么利用這些經緯度數值去實現距離轉化和對比。本章節我們就來探討一下基于geo的位置算法原理。
在實際應用中,如果要用兩個維度去確定一個點,則計算量會很大,因為一個二維確定一個平面,如果我們把二維平面上的所有點都用一個數字表示,即經緯度換算成一個字符串,則可以轉為一維坐標來表示,大大減少計算量。這就是現在應用廣泛的geoHash。
geoHash:Geohash是公共領域地理編碼系統,它將地理位置編碼為一串字母和數字。Geohash提供了像任意精度這樣的屬性,以及逐漸從代碼末尾刪除字符以減小其大?。ú⒅饾u失去精度)的可能性。由于逐步精度下降的結果,附近的地方往往(但不總是)呈現類似的前綴。共享前綴越長,兩個地方越接近。
能將一個地球上的點表示成一串字母,并且相近的地點字母的共同前綴越多。這能讓位置搜索在開發中變得很容易。它的原理就是依據上述說的geoHash值。下面就來詳細說明geoHash值是怎么算出來的:
Geohash其實就是將整個地圖或者某個分割所得的區域進行一次劃分,由于采用的是base32編碼方式,即Geohash中的每一個字母或者數字(如wx4g0e中的w)都是由5bits組成(2^5 = 32,base32),這5bits可以有32中不同的組合(0~31),這樣我們可以將整個地圖區域分為32個區域,通過00000 ~ 11111來標識這32個區域。第一次對地圖劃分后的情況如下圖所示(每個區域中的編號對應于該區域所對應的編碼):
進行多次分解后,我們就可以得到更精確的位置劃分,如上述計算的wx4g已經可以精確到一個城市城區了:
從上圖中可以看出,相鄰城區的geoHash值的共同前綴越多,由此我們就可以應用共同前綴來判斷附近相鄰的位置了。當然精確范圍也是根據經緯度和hash值的范圍來確定的,如下表,geo精確到8位的共同前綴,可以表示附近約20米內的范圍了:
在了解了geo的位置算法原理后,PHP開發過程中我們便可以使用這一定位功能,目前解決位置定位和搜索功能的方案有很多種,基于PHP的,從本人自身實踐中推薦一下幾種:
$redis->geoRadius($key, $longitude, $latitude, $radius, $unit [, Array $options]);
private $coding = '0123456789bcdefghjkmnpqrstuvwxyz';/*** calculate geoHash by longitude and latitude* @param $lat* @param $long* @return string*/public function calcGeoHash($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;}/*** @param $number* @return float|int*/private function precision($number){$precision=0;$pt=strpos($number,'.');if ($pt!==false){$precision=-(strlen($number)-$pt-1);}return pow(10,$precision)/2;}/*** @param $number* @param $min* @param $max* @param $bitcount* @return string*/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);elsereturn '0'.$this->binEncode($number, $min, $mid,$bitcount-1);}
GeoHash算法是一種將二維坐標換算成一位字符串的算法,可以通過不同字符串的共同前綴來判斷相距遠近。在日常業務中也常常需要用到,本文也介紹了不同的實現方法,具體實現方案還需以實際業務需要為準。如果屬于精確度要求很高或者企業級的大規模應用,可以首先考慮MongoDB或者其他提供Geo功能的存儲組件,如果較為輕量級,可以借助第三方地區API、或者利用redis做geo的簡單應用。如果業務需求復雜度不高,在這里并不推薦直接使用PHP寫,畢竟效率會比較低,而且這也不是業務關注的重點,所以沒必要重新造輪子。
希望本文所述對大家PHP程序設計有所幫助。
新聞熱點
疑難解答
圖片精選