查找是我们经常会碰到的问题,以前我做过一个这样的算法,在有序的数列(80万条左右),这批数据是根据维度由小到大排序的,寻找已知数据的位置,并且所相应的运算,由于这个算法要在嵌入式系统中做,如果一次在内存中载入80万条数据是不明智的。而且这个计算过程是每秒都要做一次,如果不在内存中载入数据时效性上又无法满足,那么只能根据已知数据在整个数列中的位置,然后将这个位置附近的数据载入内存。这个算法与在百度地图上查找最近的饭店基本是一个意思。我们需要对查找的数据库做处理,一维数据要变成二维数据,这是我自己的想法,将80万条数据放在一个个的方框里。然后确定每一个方框边界的经度值与维度值,如下图所示,然后将这些边界值根据经度与维度分别做两张索引表,当然这个表也是有序的,然后根据自己当前的经纬度坐标值(lat,lon)分别用二分法查找自己在表里的位置,得出自己坐标点所在方框的坐标。然后将方框里的数据全部读入内存。当然里面还有边界问题,实际上需要将自己所在方框的周围8个方框都读入内存。如下图所示:

这个其实就是哈希散列的思想。这个方法是可行的,而且已经实现,不过有一个问题是,如何将这80万比数据变成一个一个方框的数据结构按照线性保存起来。这个存储结构应该是这样的:

这是一个示意图,估算一下大概的计算量,如果要将80万笔数据分别根据经度维度一个一个对比放入框框中,这个时间复杂度是无法接受的。同样这也要设计一个哈希函数来完成这个工作,下面说重点:

所谓的GeoHash算法。

我们已知经纬度(22.527051,113.918646)先用逼近算法对其进行编码,具体做法如下:

以经度为例,value =113.918646,大于中间值为1,小于中间值为0。这个区间如何得来的呢?要知道经度的范围是[180,-180],那么中间值就是0,判断是在[180,0]还是[0,-180]如果在[180,0]就是1,然后缩小区间,在[180,0]这个区间

与中间值90比较,以此类推。最后得到编码值是:11010 00100,一共10位,为什么是10位与后面混合编码转换成base32编码有关。

同理得到维度22.527051的编码值是:10100 00000,最后要将纬度编码与经度编码混合,偶数放经度,奇数放纬度,这个可能不好理解我做一张图:

合成之后在转换成Base32编码,Base32编码是0-9、b-z(去掉a, i, l, o),这种编码方式是32以内的十进制,正好是5位二进制数,所以之前经纬度编码选择的是10位,因此正好可以换成2个字符。如下表:

最后可得出对应的值:ws10。

这个GeoHash算法网上有很多,我参考的是这个:http://www.cnblogs.com/LBSer/p/3310455.html

至于为什么这样编码里面有解释自己理解吧。我用c写了一下实现,不具有普遍性。

void GeoHash(double lat,double lon,int flag,char* hashCode)
{
double left,right,middle,value;
int ret_lat,ret_lon,temp,temp1;
int mixLatLon=;
right = ;
left = -;
ret_lat = ret_lon = ;
temp = flag;
value = lon;
while(flag){
middle = (float)((right+left)/);
if(value>middle){
ret_lon |= (<<(flag-));
left = middle;
}else if(value < middle){
ret_lon |= (<<(flag-));
right = middle;
}else{
ret_lon |= (<<(flag-));
left = middle;
}
flag--;
}
flag = temp;
right = ;
left = -;
value = lat;
printf("value:%f\r\n",value);
while(flag){
middle = (float)((right+left)/);
if(value>middle){
ret_lat |= (<<(flag-));
left = middle;
}else if(value < middle){
ret_lat |= (<<(flag-));
right = middle;
}else{
ret_lat |= (<<(flag-));
left = middle;
}
flag--;
}
flag = temp;
flag = flag*;
temp1 = temp;
while(temp1){
if(ret_lon & (<<(temp1-))){
mixLatLon |= (<<(flag-));
}else{
mixLatLon |= (<<(flag-));
}
flag--;
if(ret_lat & (<<(temp1-))){
mixLatLon |= (<<(flag-));
}else{
mixLatLon |= (<<(flag-));
}
temp1--;
flag--;
}
flag = ;
while(flag){
hashCode[flag-] = ToBase32(mixLatLon&0x1F);
mixLatLon >>= ;
flag--;
}
} char ToBase32(int dec)
{
char ret;
switch(dec){
case :ret='';break;
case :ret='';break;
case :ret='';break;
case :ret='';break;
case :ret='';break;
case :ret='';break;
case :ret='';break;
case :ret='';break;
case :ret='';break;
case :ret='';break;
case :ret='b';break;
case :ret='c';break;
case :ret='d';break;
case :ret='e';break;
case :ret='f';break;
case :ret='g';break;
case :ret='h';break;
case :ret='j';break;
case :ret='k';break;
case :ret='m';break;
case :ret='n';break;
case :ret='p';break;
case :ret='q';break;
case :ret='r';break;
case :ret='s';break;
case :ret='t';break;
case :ret='u';break;
case :ret='v';break;
case :ret='w';break;
case :ret='x';break;
case :ret='y';break;
case :ret='z';break;
default:ret='a';
}
return ret;
}

调用代码:

int main(int argc, char *argv[]) {
char hashCode[];
GeoHash(22.527051,113.918646,,hashCode);
printf("hashCode:%s",hashCode);
printf("\r\n");
system("pause");
return ;
}

结果是:ws10

这里碰到一个问题,在c中,如果想要函数返回一个字符串有哪些做法呢:

1.使用堆空间,返回申请的堆地址,注意释放
2.函数参数传递指针,返回该指针
3.返回函数内定义的静态变量(共享)
4.返回全局变量
另外注意一点,在c中,浮点数运算要注意,如果定义的是float,如果超过范围会自动变成double。例如如果我定义函数是void GeoHash(float lat,float lon,int flag,char* hashCode);
而在调用的时候传入的实参超出范围这个时候会变成double,而在函数体中保存实参的变量lat与lon定义的是float,这个时候就会出现double 强制转换成float,因引起精度损失出错。

GeoHash的更多相关文章

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

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

  2. WebGIS中GeoHash编码的研究和扩展

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 1.背景 1.1普通地理编码流程 将采集的POI入库后,数据库里保存有 ...

  3. GeoHash原理解析

    GeoHash 核心原理解析       引子 一提到索引,大家脑子里马上浮现出B树索引,因为大量的数据库(如MySQL.oracle.PostgreSQL等)都在使用B树.B树索引本质上是对索引字段 ...

  4. geohash基本原理

    geohash基本原理是将地球理解为一个二维平面,将平面递归分解成更小的子块,每个子块在一定经纬度范围内拥有相同的编码,这种方式简单粗暴,可以满足对小规模的数据进行经纬度的检索 目录: 经纬度常识 认 ...

  5. 【转】GeoHash核心原理解析

    好久没更新过博客了,先转载一篇文章吧. 源地址:http://www.cnblogs.com/LBSer/p/3310455.html 引子 机机是个好动又好学的孩子,平日里就喜欢拿着手机地图点点按按 ...

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

    1.geohash特点 2.geohash原理 3.geohash的php .python.java.C#实现代码 4.观点讨论 w微博:http://weibo.com/dxl0321 geohas ...

  7. GeoHash核心原理解析

    http://www.cnblogs.com/LBSer/p/3310455.html 引子 机机是个好动又好学的孩子,平日里就喜欢拿着手机地图点点按按来查询一些好玩的东西.某一天机机到北海公园游玩, ...

  8. [转]GeoHash核心原理解析

    原文出处: zhanlijun    引子 机机是个好动又好学的孩子,平日里就喜欢拿着手机地图点点按按来查询一些好玩的东西.某一天机机到北海公园游玩,肚肚饿了,于是乎打开手机地图,搜索北海公园附近的餐 ...

  9. Redis GEO ,GEOHASH,Spatial_index

    https://matt.sh/redis-geo http://antirez.com/latest/0 http://invece.org/ https://github.com/davidmot ...

随机推荐

  1. 使用Visual Studio 2015开发Android 程序

    环境配置: 操作系统:win 7 64位 IDE:Visual Studio 2015 SDK:installer_r24.3.3-windows 安装前提: 编辑hosts文件(在附件可下载)因为安 ...

  2. Wordpress去除管理员工具条

    想去掉这条东西有多种方式,个人比较喜欢这个,灵活~ 打开用户,在用户选项里,把这个勾走.

  3. Requirejs之AMD规范

    一.什么是AMD规范 AMD是Asynchronous Module Definition-----异步模块定义 AMD规范定义了2个函数define()与require() 下面我们来看一下定义方法 ...

  4. MySQL 的 utf8_general_ci 和 utf8_unicode_ci 有什么区别,应如何选择?

    一般的解说是 utf8_general_ci 速度较快,utf8_unicode_ci 准确性较好 … 但快在那里,又好在什么地方呢? 首先就其准确性说,这世界上有无数种文字,除了通用的英文使用我们较 ...

  5. Asp.net mvc web api 在项目中的实际应用

    Asp.net mvc web api 在项目中的实际应用 前言:以下只是记录本人在项目中的应用,而web api在数据传输方面有多种实现方式,具体可根据实际情况而定! 1:数据传输前的加密,以下用到 ...

  6. M1卡修改各区块控制位值和数据

    (一),以常用设置"08 77 8F 69"控制条件为例,先搞清楚它――具有的访问权限. 1.对"08 77 8F 69"值进行计算,该值定位于各区块3的6,7 ...

  7. iOS小技巧3

    将颜色合成图片 将颜色合成图片 +(UIImage *)imageWithColor:(UIColor *)color { CGRect rect = CGRectMake(0.0f, 0.0f, 1 ...

  8. 深入研究虚拟机之垃圾收集(GC)算法实现

    一.         What, Why 1.   GC是什么?为什么需要GC GC,全写是Garbage Collection , 即垃圾回收.GC是一种自动内存管理机制.通常我们在需要时手动的分配 ...

  9. Linux:Ubuntu 14.04 Server 离线安装Jjava8(及在线安装)

    (离线安装)首先,通过winscp上传本地下载好的jdk-8u102-linux-x64.gz (离线安装)将jdk-8u102-linux-x64.gz解压到到/usr/lib/jvm下,并把文件夹 ...

  10. 学习OpenCV——HOG+SVM

    #include "cv.h" #include "highgui.h" #include "stdafx.h" #include < ...