C语言 c++ php mysql nginx linux lnmp lamp lanmp memcache redis 面试 笔记 ppt 设计模式 问题 远程连接

关于计算周围多少里以内的楼盘

 

    /**
     * 根据经纬度计算距离 其中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/

http://www.wubiao.info/401

 

  

 

posted on 2013-04-23 15:22  思齐_  阅读(389)  评论(0编辑  收藏  举报