geohash的代码及讲解
来源:互联网 发布:ubuntu安装vim失败 编辑:程序博客网 时间:2024/05/16 19:20
网上一篇关于geohash的文章
代码可以引用这里:http://code.google.com/p/geospatialweb/source/browse/trunk/geohash/src/Geohash.java?r=104
import java.util.BitSet;
import java.util.HashMap;
public classGeohash {
privatestatic int numbits= 6* 5;
finalstatic char[] digits= {'0', '1', '2','3', '4', '5','6', '7', '8',
'9','b', 'c', 'd','e', 'f', 'g','h', 'j', 'k','m', 'n', 'p',
'q','r', 's', 't','u', 'v', 'w','x', 'y', 'z'};
finalstatic HashMap<Character,Integer> lookup= newHashMap<Character,Integer>();
static{
int i= 0;
for(char c: digits)
lookup.put(c, i++);
}
publicstatic void main(String[] args){
double[] latlon= newGeohash().decode("dj248j248j24");
System.out.println(latlon[0]+ " "+ latlon[1]);
Geohash e= newGeohash();
String s= e.encode(30,-90.0);
System.out.println(s);
latlon = e.decode(s);
System.out.println(latlon[0]+ ", "+ latlon[1]);
}
publicdouble[] decode(String geohash){
StringBuilder buffer= newStringBuilder();
for(char c: geohash.toCharArray()){
int i= lookup.get(c)+ 32;
buffer.append(Integer.toString(i,2).substring(1));
}
BitSet lonset= newBitSet();
BitSet latset= newBitSet();
//even bits
int j=0;
for(int i=0; i< numbits*2;i+=2){
boolean isSet= false;
if( i < buffer.length())
isSet = buffer.charAt(i)== '1';
lonset.set(j++, isSet);
}
//odd bits
j=0;
for(int i=1; i< numbits*2;i+=2){
boolean isSet= false;
if( i < buffer.length())
isSet = buffer.charAt(i)== '1';
latset.set(j++, isSet);
}
double lon= decode(lonset,-180,180);
double lat= decode(latset,-90,90);
returnnew double[]{lat, lon};
}
privatedouble decode(BitSet bs,double floor,double ceiling){
double mid= 0;
for(int i=0; i<bs.length(); i++) {
mid =(floor + ceiling)/ 2;
if(bs.get(i))
floor = mid;
else
ceiling = mid;
}
return mid;
}
publicString encode(double lat,double lon){
BitSet latbits= getBits(lat,-90,90);
BitSet lonbits= getBits(lon,-180,180);
StringBuilder buffer= newStringBuilder();
for(int i= 0; i< numbits; i++){
buffer.append((lonbits.get(i))?'1':'0');
buffer.append((latbits.get(i))?'1':'0');
}
return base32(Long.parseLong(buffer.toString(),2));
}
privateBitSet getBits(double lat,double floor,double ceiling){
BitSet buffer= newBitSet(numbits);
for(int i= 0; i< numbits; i++){
double mid= (floor+ ceiling)/ 2;
if(lat >= mid){
buffer.set(i);
floor = mid;
}else {
ceiling = mid;
}
}
return buffer;
}
publicstatic String base32(long i){
char[] buf= newchar[65];
int charPos= 64;
boolean negative= (i< 0);
if(!negative)
i =-i;
while(i <=-32){
buf[charPos--]= digits[(int)(-(i %32))];
i /=32;
}
buf[charPos]= digits[(int)(-i)];
if(negative)
buf[--charPos]= '-';
returnnew String(buf, charPos,(65 - charPos));
}
}
版权声明:可以任意转载,但转载时必须标明原作者charlee、原始链接http://tech.idv2.com/2011/07/05/geohash-intro/以及本声明。
上回说到了用经纬度范围实现附近地点搜索。 一些小型应用中这样做没问题,但在大型应用中它有个显著的缺点:速度慢。慢的原因有两个, 第一是范围比较的索引利用率并不高,第二是SQL语句极其不稳定(不同的当前位置会产生完全不同的SQL查询),很难缓存。
可以考虑使用geohash算法。
geohash是一种地址编码,它能把二维的经纬度编码成一维的字符串。比如,北海公园的编码是wx4g0ec1。
geohash有以下几个特点:
首先,geohash用一个字符串表示经度和纬度两个坐标。某些情况下无法在两列上同时应用索引 (例如MySQL 4之前的版本,Google App Engine的数据层等),利用geohash,只需在一列上应用索引即可。
其次,geohash表示的并不是一个点,而是一个矩形区域。比如编码wx4g0ec19,它表示的是一个矩形区域。 使用者可以发布地址编码,既能表明自己位于北海公园附近,又不至于暴露自己的精确坐标,有助于隐私保护。
第三,编码的前缀可以表示更大的区域。例如wx4g0ec1,它的前缀wx4g0e表示包含编码wx4g0ec1在内的更大范围。 这个特性可以用于附近地点搜索。首先根据用户当前坐标计算geohash(例如wx4g0ec1)然后取其前缀进行查询 (SELECT * FROM place WHERE geohash LIKE 'wx4g0e%'),即可查询附近的所有地点。
geohash的算法
下面以(39.92324, 116.3906)为例,介绍一下geohash的编码算法。首先将纬度范围(-90, 90)平分成两个区间(-90, 0)、(0, 90), 如果目标纬度位于前一个区间,则编码为0,否则编码为1。由于39.92324属于(0, 90),所以取编码为1。然后再将(0, 90)分成(0, 45), (45, 90)两个区间,而39.92324位于(0, 45),所以编码为0。以此类推,直到精度符合要求为止,得到纬度编码为1011 1000 1100 0111 1001。
经度也用同样的算法,对(-180, 180)依次细分,得到116.3906的编码为1101 0010 1100 0100 0100。
接下来将经度和纬度的编码合并,奇数位是纬度,偶数位是经度,得到编码 11100 11101 00100 01111 00000 01101 01011 00001。
最后,用0-9、b-z(去掉a, i, l, o)这32个字母进行base32编码,得到(39.92324, 116.3906)的编码为wx4g0ec1。
解码算法与编码算法相反,先进行base32解码,然后分离出经纬度,最后根据二进制编码对经纬度范围进行细分即可,这里不再赘述。 不过由于geohash表示的是区间,编码越长越精确,但不可能解码出完全一致的地址。
geohash的应用:附近地址搜索
geohash的最大用途就是附近地址搜索了。不过,从geohash的编码算法中可以看出它的一个缺点:位于格子边界两侧的两点, 虽然十分接近,但编码会完全不同。实际应用中,可以同时搜索当前格子周围的8个格子,即可解决这个问题。
以geohash的python库为例,相关的geohash操作如下:
>>> import geohash>>> geohash.encode(39.92324, 116.3906, 5) # 编码,5表示编码长度'wx4g0'>>> geohash.expand('wx4g0') # 求wx4g0格子及周围8个格子的编码['wx4ep', 'wx4g1', 'wx4er', 'wx4g2', 'wx4g3', 'wx4dz', 'wx4fb', 'wx4fc', 'wx4g0']
最后,我们来看看本文开头提出的两个问题:速度慢,缓存命中率低。使用geohash查询附近地点,用的是字符串前缀匹配:
SELECT * FROM place WHERE geohash LIKE 'wx4g0%';
而前缀匹配可以利用geohash列上的索引,因此查询速度不会太慢。另外,即使用户坐标发生微小的变化, 也能编码成相同的geohash,这就保证了每次执行相同的SQL语句,使得缓存命中率大大提高。
相关资源
- python-geohash: http://code.google.com/p/python-geohash/
- geohash演示: http://openlocation.org/geohash/geohash-js/
- geohash的代码及讲解
- OpenCV 例子代码的讲解、简介及库的安装
- OpenCV 例子代码的讲解、简介及库的安装
- OpenCV 例子代码的讲解、简介及库的安装
- geohash的简单分享
- OpenCV-github代码,及作者相关的讲解
- 单链表的建立和增删改查代码及讲解
- iOS基础框架的搭建 / 及国际化操作 代码讲解
- 简单讲解一下冒泡排序的原理 及代码
- 基于matlab的卷积神经网络(CNN)讲解及代码
- geohash
- geohash
- GEOHash
- geoHash
- geohash
- Geohash
- GeoHash
- geohash
- vc2005和IE冲突 主要是在通过向导添加方法或变量的时候会报脚本错误。
- js表单验证
- 【经典C/C++系列】解析“extern”及extern "C"
- 利用cookie显示浏览过的商品
- JDK 5.0 Concurrency Utilities 并发处理 ReentrantLock 可重入锁
- geohash的代码及讲解
- 你的网站里有“设为首页”的链接吗 ?
- BSTR && std::string
- (32)关于Session的相关知识
- JDK中的设计模式之适配器模式
- Hosting the Data Service (ADO.NET Data Services)
- hibernate 用联接表映射
- 移植Linux2.6.39到杨创utu2440
- Linux C SQLite3 编程