geohash java算法

8 篇文章 0 订阅
  1. geohash有以下几个特点:

    首先,geohash用一个字符串表示经度和纬度两个坐标。某些情况下无法在两列上同时应用索引 (例如MySQL 4之前的版本,Google App Engine的数据层等),利用geohash,只需在一列上应用索引即可。

    其次,geohash表示的并不是一个点,而是一个矩形区域。比如编码wx4g0ec19,它表示的是一个矩形区域。 使用者可以发布地址编码,既能表明自己位于北海公园附近,又不至于暴露自己的精确坐标,有助于隐私保护。

    第三,编码的前缀可以表示更大的区域。例如wx4g0ec1,它的前缀wx4g0e表示包含编码wx4g0ec1在内的更大范围。 这个特性可以用于附近地点搜索。首先根据用户当前坐标计算geohash(例如wx4g0ec1)然后取其前缀进行查询 (SELECT * FROM place WHERE geohash LIKE 'wx4g0e%'),即可查询附近的所有地点。

    Geohash比直接用经纬度的高效很多。

     

    Geohash的原理

    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。

    纬度范围

    划分区间0

    划分区间1

    39.92324所属区间

    (-90, 90)

    (-90, 0.0)

    (0.0, 90)

    1

    (0.0, 90)

    (0.0, 45.0)

    (45.0, 90)

    0

    (0.0, 45.0)

    (0.0, 22.5)

    (22.5, 45.0)

    1

    (22.5, 45.0)

    (22.5, 33.75)

    (33.75, 45.0)

    1

    (33.75, 45.0)

    (33.75, 39.375)

    (39.375, 45.0)

    1

    (39.375, 45.0)

    (39.375, 42.1875)

    (42.1875, 45.0)

    0

    (39.375, 42.1875)

    (39.375, 40.7812)

    (40.7812, 42.1875)

    0

    (39.375, 40.7812)

    (39.375, 40.0781)

    (40.0781, 40.7812)

    0

    (39.375, 40.0781)

    (39.375, 39.7265)

    (39.7265, 40.0781)

    1

    (39.7265, 40.0781)

    (39.7265, 39.9023)

    (39.9023, 40.0781)

    1

    (39.9023, 40.0781)

    (39.9023, 39.9902)

    (39.9902, 40.0781)

    0

    (39.9023, 39.9902)

    (39.9023, 39.9462)

    (39.9462, 39.9902)

    0

    (39.9023, 39.9462)

    (39.9023, 39.9243)

    (39.9243, 39.9462)

    0

    (39.9023, 39.9243)

    (39.9023, 39.9133)

    (39.9133, 39.9243)

    1

    (39.9133, 39.9243)

    (39.9133, 39.9188)

    (39.9188, 39.9243)

    1

    (39.9188, 39.9243)

    (39.9188, 39.9215)

    (39.9215, 39.9243)

    1

     

    经度也用同样的算法,对(-180, 180)依次细分,得到116.3906的编码为1101 0010 1100 0100 0100。

    经度范围

    划分区间0

    划分区间1

    116.3906所属区间

    (-180, 180)

    (-180, 0.0)

    (0.0, 180)

    1

    (0.0, 180)

    (0.0, 90.0)

    (90.0, 180)

    1

    (90.0, 180)

    (90.0, 135.0)

    (135.0, 180)

    0

    (90.0, 135.0)

    (90.0, 112.5)

    (112.5, 135.0)

    1

    (112.5, 135.0)

    (112.5, 123.75)

    (123.75, 135.0)

    0

    (112.5, 123.75)

    (112.5, 118.125)

    (118.125, 123.75)

    0

    (112.5, 118.125)

    (112.5, 115.312)

    (115.312, 118.125)

    1

    (115.312, 118.125)

    (115.312, 116.718)

    (116.718, 118.125)

    0

    (115.312, 116.718)

    (115.312, 116.015)

    (116.015, 116.718)

    1

    (116.015, 116.718)

    (116.015, 116.367)

    (116.367, 116.718)

    1

    (116.367, 116.718)

    (116.367, 116.542)

    (116.542, 116.718)

    0

    (116.367, 116.542)

    (116.367, 116.455)

    (116.455, 116.542)

    0

    (116.367, 116.455)

    (116.367, 116.411)

    (116.411, 116.455)

    0

    (116.367, 116.411)

    (116.367, 116.389)

    (116.389, 116.411)

    1

    (116.389, 116.411)

    (116.389, 116.400)

    (116.400, 116.411)

    0

    (116.389, 116.400)

    (116.389, 116.394)

    (116.394, 116.400)

    0

    接下来将经度和纬度的编码合并,奇数位是纬度,偶数位是经度,得到编码 11100 11101 00100 01111 00000 01101 01011 00001。

    最后,用0-9、b-z(去掉a, i, l, o)这32个字母进行base32编码,得到(39.92324, 116.3906)的编码为wx4g0ec1。

    十进制

    0

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    base32

    0

    1

    2

    3

    4

    5

    6

    7

    8

    9

    b

    c

    d

    e

    f

    g

    十进制

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    base32

    h

    j

    k

    m

    n

    p

    q

    r

    s

    t

    u

    v

    w

    x

    y

    z

     

    解码算法与编码算法相反,先进行base32解码,然后分离出经纬度,最后根据二进制编码对经纬度范围进行细分即可,这里不再赘述。






  2. View Code  
  3.  import java.io.File;    
  4.  import java.io.FileInputStream;    
  5.  import java.util.BitSet;    
  6.  import java.util.HashMap;    
  7.      
  8.      
  9.  public class Geohash {    
  10.      
  11.      private static int numbits = 6 * 5;    
  12.      final static char[] digits = { '0''1''2''3''4''5''6''7''8',    
  13.              '9''b''c''d''e''f''g''h''j''k''m''n''p',    
  14.              'q''r''s''t''u''v''w''x''y''z' };    
  15.          
  16.      final static HashMap<Character, Integer> lookup = new HashMap<Character, Integer>();    
  17.      static {    
  18.          int i = 0;    
  19.          for (char c : digits)    
  20.              lookup.put(c, i++);    
  21.      }    
  22.      
  23.      public static void main(String[] args)  throws Exception{    
  24.      
  25.          System.out.println(new Geohash().encode(45125));    
  26.                  
  27.      }    
  28.    
  29.      public double[] decode(String geohash) {    
  30.          StringBuilder buffer = new StringBuilder();    
  31.          for (char c : geohash.toCharArray()) {    
  32.      
  33.              int i = lookup.get(c) + 32;    
  34.              buffer.append( Integer.toString(i, 2).substring(1) );    
  35.          }    
  36.              
  37.          BitSet lonset = new BitSet();    
  38.          BitSet latset = new BitSet();    
  39.              
  40.          //even bits    
  41.          int j =0;    
  42.          for (int i=0; i< numbits*2;i+=2) {    
  43.              boolean isSet = false;    
  44.              if ( i < buffer.length() )    
  45.                isSet = buffer.charAt(i) == '1';    
  46.              lonset.set(j++, isSet);    
  47.          }    
  48.              
  49.          //odd bits    
  50.          j=0;    
  51.          for (int i=1; i< numbits*2;i+=2) {    
  52.              boolean isSet = false;    
  53.              if ( i < buffer.length() )    
  54.                isSet = buffer.charAt(i) == '1';    
  55.              latset.set(j++, isSet);    
  56.          }    
  57.              
  58.          double lon = decode(lonset, -180180);    
  59.          double lat = decode(latset, -9090);    
  60.              
  61.          return new double[] {lat, lon};         
  62.      }    
  63.          
  64.      private double decode(BitSet bs, double floor, double ceiling) {    
  65.          double mid = 0;    
  66.          for (int i=0; i<bs.length(); i++) {    
  67.              mid = (floor + ceiling) / 2;    
  68.              if (bs.get(i))    
  69.                  floor = mid;    
  70.              else    
  71.                  ceiling = mid;    
  72.          }    
  73.          return mid;    
  74.      }    
  75.          
  76.          
  77.      public String encode(double lat, double lon) {    
  78.          BitSet latbits = getBits(lat, -9090);    
  79.          BitSet lonbits = getBits(lon, -180180);    
  80.          StringBuilder buffer = new StringBuilder();    
  81.          for (int i = 0; i < numbits; i++) {    
  82.              buffer.append( (lonbits.get(i))?'1':'0');    
  83.              buffer.append( (latbits.get(i))?'1':'0');    
  84.          }    
  85.          return base32(Long.parseLong(buffer.toString(), 2));    
  86.      }    
  87.      
  88.      private BitSet getBits(double lat, double floor, double ceiling) {    
  89.          BitSet buffer = new BitSet(numbits);    
  90.          for (int i = 0; i < numbits; i++) {    
  91.              double mid = (floor + ceiling) / 2;    
  92.              if (lat >= mid) {    
  93.                  buffer.set(i);    
  94.                  floor = mid;    
  95.              } else {    
  96.                  ceiling = mid;    
  97.              }    
  98.          }    
  99.          return buffer;    
  100.      }    
  101.      
  102.      public static String base32(long i) {    
  103.          char[] buf = new char[65];    
  104.          int charPos = 64;    
  105.          boolean negative = (i < 0);    
  106.          if (!negative)    
  107.              i = -i;    
  108.          while (i <= -32) {    
  109.              buf[charPos--] = digits[(int) (-(i % 32))];    
  110.              i /= 32;    
  111.          }    
  112.          buf[charPos] = digits[(int) (-i)];    
  113.      
  114.          if (negative)    
  115.              buf[--charPos] = '-';    
  116.          return new String(buf, charPos, (65 - charPos));    
  117.      }    
  118.      
  119.  } 
要在Python中实现geohash算法,你可以使用geohash库。首先,你需要确保已经安装了geohash库。你可以使用pip命令进行安装,命令如下:pip install geohash。如果安装成功后,仍然无法导入geohash模块并提示ImportError: No module named 'geohash'的错误,你可以尝试以下方法进行修复:将Geohash文件名改为geohash,然后在geohash文件夹下的__init__.py文件中将from geohash import decode_exactly, decode, encode改为from .geohash import decode_exactly, decode, encode(在geohash前面加一个'.')。这样应该可以解决导入模块的问题。[1] 一旦你成功导入了geohash库,你就可以使用它来进行geohash算法的实现。例如,你可以使用decode_exactly函数来将geohash字符串解码为经度和纬度的坐标。例如,你可以使用以下代码来解码geohash字符串"wm6nc":print(geohash.decode_exactly("wm6nc")),这将返回一个包含经度、纬度、经度精度和纬度精度的元组。(30.73974609375, 104.12841796875, 0.02197265625, 0.02197265625)[2] geohash库还提供了其他功能模块,如距离度量和几何计算。距离度量模块提供了与距离相关的函数,如distance和dimensions。几何模块提供了将多边形转换为geohash列表的函数,如polygon_to_geohashgeohash_to_polygon。这些功能可以帮助你在地理区域中进行近似地理差异的计算。你可以使用shapely库进行几何计算[3]。 综上所述,要在Python中实现geohash算法,你可以使用geohash库,并根据需要使用其提供的不同功能模块。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值