关于计算周围多少里以内的楼盘
/** * 根据经纬度计算距离 其中A($lat1,$lng1)、B($lat2,$lng2) * 注意弧度角度的计算 * 单位:km */ function _getDistance($lat1,$lng1,$lat2,$lng2) { //地球半径 $R = 6378.137; //km //将角度转为狐度 $radLat1 = deg2rad($lat1); $radLat2 = deg2rad($lat2); $radLng1 = deg2rad($lng1); $radLng2 = deg2rad($lng2); //结果 $s = acos(cos($radLat1)*cos($radLat2)*cos($radLng1-$radLng2)+sin($radLat1)*sin($radLat2))*$R; //精度 $s = round($s* 10000)/10000; return round($s); }
/** *根据传入的中心点的经纬度和半径,计算出矩形区域 * @param float $center_lat * @param float $center_lng * @param int $radius unit:km */ function getAroundRectangle($center_lat, $center_lng, $radius) { //先来求东西两侧的的范围边界 经度 $earth_radius = 6378.137; //km $dlng = rad2deg(2 * asin(sin($radius / (2 * $earth_radius)) / cos(deg2rad($center_lat)))); //角度 //然后求南北两侧的范围边界 维度 $dlat = rad2deg($radius/$earth_radius); $data = array( 'lat_min' => $center_lat-$dlat,//维度最小 'lat_max' => $center_lat+$dlat,//唯独 最大 'lng_min' => $center_lng-$dlng,//经度最小 'lng_max' => $center_lng+$dlng,//经度最大 ); return $data; }
View Code
1 /** 2 * Geohash generation class 3 * http://blog.dixo.net/downloads/ 4 * 5 * This file copyright (C) 2008 Paul Dixon (paul@elphin.com) 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * as published by the Free Software Foundation; either version 3 10 * of the License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 20 */ 21 22 23 24 /** 25 * Encode and decode geohashes 26 * 27 */ 28 class Geohash 29 { 30 private $coding="0123456789bcdefghjkmnpqrstuvwxyz"; 31 private $codingMap=array(); 32 33 public function Geohash() 34 { 35 //build map from encoding char to 0 padded bitfield 36 for($i=0; $i<32; $i++) 37 { 38 $this->codingMap[substr($this->coding,$i,1)]=str_pad(decbin($i), 5, "0", STR_PAD_LEFT); 39 } 40 41 } 42 43 /** 44 * Decode a geohash and return an array with decimal lat,long in it 45 */ 46 public function decode($hash) 47 { 48 //decode hash into binary string 49 $binary=""; 50 $hl=strlen($hash); 51 for($i=0; $i<$hl; $i++) 52 { 53 $binary.=$this->codingMap[substr($hash,$i,1)]; 54 } 55 56 //split the binary into lat and log binary strings 57 $bl=strlen($binary); 58 $blat=""; 59 $blong=""; 60 for ($i=0; $i<$bl; $i++) 61 { 62 if ($i%2) 63 $blat=$blat.substr($binary,$i,1); 64 else 65 $blong=$blong.substr($binary,$i,1); 66 67 } 68 69 //now concert to decimal 70 $lat=$this->binDecode($blat,-90,90); 71 $long=$this->binDecode($blong,-180,180); 72 73 //figure out how precise the bit count makes this calculation 74 $latErr=$this->calcError(strlen($blat),-90,90); 75 $longErr=$this->calcError(strlen($blong),-180,180); 76 77 //how many decimal places should we use? There's a little art to 78 //this to ensure I get the same roundings as geohash.org 79 $latPlaces=max(1, -round(log10($latErr))) - 1; 80 $longPlaces=max(1, -round(log10($longErr))) - 1; 81 82 //round it 83 $lat=round($lat, $latPlaces); 84 $long=round($long, $longPlaces); 85 86 return array($lat,$long); 87 } 88 89 90 /** 91 * Encode a hash from given lat and long 92 */ 93 public function encode($lat,$long) 94 { 95 //how many bits does latitude need? 96 $plat=$this->precision($lat); 97 $latbits=1; 98 $err=45; 99 while($err>$plat) 100 { 101 $latbits++; 102 $err/=2; 103 } 104 105 //how many bits does longitude need? 106 $plong=$this->precision($long); 107 $longbits=1; 108 $err=90; 109 while($err>$plong) 110 { 111 $longbits++; 112 $err/=2; 113 } 114 115 //bit counts need to be equal 116 $bits=max($latbits,$longbits); 117 118 //as the hash create bits in groups of 5, lets not 119 //waste any bits - lets bulk it up to a multiple of 5 120 //and favour the longitude for any odd bits 121 $longbits=$bits; 122 $latbits=$bits; 123 $addlong=1; 124 while (($longbits+$latbits)%5 != 0) 125 { 126 $longbits+=$addlong; 127 $latbits+=!$addlong; 128 $addlong=!$addlong; 129 } 130 131 132 //encode each as binary string 133 $blat=$this->binEncode($lat,-90,90, $latbits); 134 $blong=$this->binEncode($long,-180,180,$longbits); 135 136 //merge lat and long together 137 $binary=""; 138 $uselong=1; 139 while (strlen($blat)+strlen($blong)) 140 { 141 if ($uselong) 142 { 143 $binary=$binary.substr($blong,0,1); 144 $blong=substr($blong,1); 145 } 146 else 147 { 148 $binary=$binary.substr($blat,0,1); 149 $blat=substr($blat,1); 150 } 151 $uselong=!$uselong; 152 } 153 154 //convert binary string to hash 155 $hash=""; 156 for ($i=0; $i<strlen($binary); $i+=5) 157 { 158 $n=bindec(substr($binary,$i,5)); 159 $hash=$hash.$this->coding[$n]; 160 } 161 162 163 return $hash; 164 } 165 166 /** 167 * What's the maximum error for $bits bits covering a range $min to $max 168 */ 169 private function calcError($bits,$min,$max) 170 { 171 $err=($max-$min)/2; 172 while ($bits--) 173 $err/=2; 174 return $err; 175 } 176 177 /* 178 * returns precision of number 179 * precision of 42 is 0.5 180 * precision of 42.4 is 0.05 181 * precision of 42.41 is 0.005 etc 182 */ 183 private function precision($number) 184 { 185 $precision=0; 186 $pt=strpos($number,'.'); 187 if ($pt!==false) 188 { 189 $precision=-(strlen($number)-$pt-1); 190 } 191 192 return pow(10,$precision)/2; 193 } 194 195 196 /** 197 * create binary encoding of number as detailed in http://en.wikipedia.org/wiki/Geohash#Example 198 * removing the tail recursion is left an exercise for the reader 199 */ 200 private function binEncode($number, $min, $max, $bitcount) 201 { 202 if ($bitcount==0) 203 return ""; 204 205 #echo "$bitcount: $min $max<br>"; 206 207 //this is our mid point - we will produce a bit to say 208 //whether $number is above or below this mid point 209 $mid=($min+$max)/2; 210 if ($number>$mid) 211 return "1".$this->binEncode($number, $mid, $max,$bitcount-1); 212 else 213 return "0".$this->binEncode($number, $min, $mid,$bitcount-1); 214 } 215 216 217 /** 218 * decodes binary encoding of number as detailed in http://en.wikipedia.org/wiki/Geohash#Example 219 * removing the tail recursion is left an exercise for the reader 220 */ 221 private function binDecode($binary, $min, $max) 222 { 223 $mid=($min+$max)/2; 224 225 if (strlen($binary)==0) 226 return $mid; 227 228 $bit=substr($binary,0,1); 229 $binary=substr($binary,1); 230 231 if ($bit==1) 232 return $this->binDecode($binary, $mid, $max); 233 else 234 return $this->binDecode($binary, $min, $mid); 235 } 236 }
方案1:
根据中心点,和 上面的算法计算出几公里以内的最大/最小经纬度,然后搜索时用这个条件 (我们想要的为圆型的,需要过滤一次数据在),使用于数据量相对较小的
缺点:1.范围比较的索引利用率并不高,2.SQL语句极其不稳定(不同的当前位置会产生完全不同的SQL查询),很难缓存。
方案2:
运用geohash, geohash是一种地址编码,它能把二维的经纬度编码成一维的字符串, 字符串匹配度越大,离的越近,适用于数据量较大的,
缺点:匹配程度并不能准确控制距离,只能找出比他大的范围,然后在用程序去判断
文章链接:
http://tech.idv2.com/2011/06/17/location-search/
http://tech.idv2.com/2011/07/05/geohash-intro/