zoukankan      html  css  js  c++  java
  • Geohash 算法学习

    Geohash 算法:

        这是一套纬度/经度地理编码算法,把纬度/经度编码成base32位的字符串。这种编码和纬度/经度不是唯一对应,其实是一个纬度/经度区间。算法有一个精度概念,精度越高,字符串越长,所表示的区间越小。可以编码后的字符串想象成一个格子,里面存放一些纬度/经度值。格子趋近很小的时候,只能存放一纬度/经度值,那么编码和纬度/经度就是唯一对应的关系。但是这个不是重点,这套算法目的就是把纬度/经度编码成近似值,通过近似值搜索,就能很高效的缩小范围,然后再从小范围里查找精确值。

    例如,坐标57.64911,10.40744(日德兰半岛的顶端附近,在丹麦)产生一个u4pruydqqvj字符串。参考Wikipedia:http://en.wikipedia.org/wiki/Geohash

    算法原理:

    以A[-170,42.6]  为例,纬度范围(-90, 90)平分成两个区间(-90, 0)、(0, 90),位于前一个区间,则编码为0,否则编码为1。由于42.6属于(0, 90),所以取编码为1。

    再将(0, 90)分成 (0, 45), (45, 90)两个区间,而42.6位于(0, 45),所以编码为0,

    再将(0, 45)分成 (0, 22.5), (22.5, 45)两个区间,而42.6位于(22.5, 45),所以编码为1,

    再将(22.5, 45)分成 (22.5, 33.7.5), (33.7.5, 45)两个区间

    最后划分四次后纬度编码为:1011

    同理经度编码为:0000

    如图绿色格子就是此编码代表的区间范围

    算出经纬度编码后,从高到低,奇数为经度,偶数为纬度,合并经纬度编码。

    lng:0111110000000

    lat:101111001001

    合并后:01101 11111 11000 00100 00010

    然后再把二进制按每五个一组,按base 32 编码成字符串。

    01101 11111 11000 00100 00010  

    13       31      24       4         2

    e         z         s       4          2

    最后的Geohash 编码为:ezs42 

    应用场景:

    前面介绍了下编码的规则,现在来讨论下一些应用场景。我们知道,地球是一个近似球体。球面上两点相对球心的角度偏差,和两点的球面距离是一个等比关系。而Geohash 编码其实就是一个纬度/经度区间,区间的角度范围就决定了区间内的点之间的距离范围。通过这个原理,就可以通过一个坐标的经纬度,找出所在的区间和周边区间来搜索 该点周边的坐标。

    Wikipedia上以纬度42.6 为例,统计出每次划分后的每个区间的纬度范围。

    划分十二次后,每个区间的纬度范围 0.044 ,根据地球半径可心算出每个距离范围为4.8 公里

      当geohash length=5 时,通过搜索某点周边的8个相邻区间,可以大概找出周边5公里的坐标。

    这个算法有一定限制,纬度越高,基于经度偏差和距离的比值越低,表格中的距离计算精度也随着降低,需要根据cos(纬度)的值进行调整。

    下面是官方提供的代码,一个是根据经纬度计算HashCode ,另一个是根据HashCode 计算周边的8个HashCode 。在实际应用中就可以用这几个方法

    构建地标的hashCode 并通过hashCode来检索。

    C#代码:

      1         public enum Direction
      2         {
      3             Top = 0,
      4             Right = 1,
      5             Bottom = 2,
      6             Left = 3
      7         }
      8 
      9         private const string Base32 = "0123456789bcdefghjkmnpqrstuvwxyz";
     10         private static readonly int[] Bits = new[] { 16, 8, 4, 2, 1 };
     11 
     12         private static readonly string[][] Neighbors = {
     13                                                            new[]
     14                                                               {
     15                                                                 "p0r21436x8zb9dcf5h7kjnmqesgutwvy", // Top
     16                                                                 "bc01fg45238967deuvhjyznpkmstqrwx", // Right
     17                                                                 "14365h7k9dcfesgujnmqp0r2twvyx8zb", // Bottom
     18                                                                 "238967debc01fg45kmstqrwxuvhjyznp", // Left
     19                                                                }, 
     20                                                             new[]
     21                                                                {
     22                                                                 "bc01fg45238967deuvhjyznpkmstqrwx", // Top
     23                                                                 "p0r21436x8zb9dcf5h7kjnmqesgutwvy", // Right
     24                                                                 "238967debc01fg45kmstqrwxuvhjyznp", // Bottom
     25                                                                 "14365h7k9dcfesgujnmqp0r2twvyx8zb", // Left
     26                                                                 }
     27                                                        };
     28 
     29         private static readonly string[][] Borders = {
     30                                                          new[] {"prxz", "bcfguvyz", "028b", "0145hjnp"},//Top,Right,Bottom,Left
     31                                                          new[] {"bcfguvyz", "prxz", "0145hjnp", "028b"}//Top,Right,Bottom,Left
     32                                                      };
     33 
     34 
     35         public static String CalculateAdjacent(String hash, Direction direction)
     36         {
     37             if (string.IsNullOrEmpty(hash))
     38             {
     39                 return "";
     40             }
     41             hash = hash.ToLower();
     42             char lastChr = hash[hash.Length - 1];
     43             int type = hash.Length % 2;
     44             var dir = (int)direction;
     45             string nHash = hash.Substring(0, hash.Length - 1);
     46 
     47             if (Borders[type][dir].IndexOf(lastChr) != -1)
     48             {
     49                 nHash = CalculateAdjacent(nHash, (Direction)dir);
     50                 //南北极的纬度处理,直接返回原值
     51                 if (nHash == hash.Substring(0, hash.Length - 1) && (direction == Direction.Top || direction == Direction.Bottom))
     52                 {
     53                     return nHash + lastChr;
     54                 }
     55             }
     56 
     57             return nHash + Base32[Neighbors[type][dir].IndexOf(lastChr)];
     58 
     59         }
     60 
     61         public static void RefineInterval(ref double[] interval, int cd, int mask)
     62         {
     63             if ((cd & mask) != 0)
     64             {
     65                 interval[0] = (interval[0] + interval[1]) / 2;
     66             }
     67             else
     68             {
     69                 interval[1] = (interval[0] + interval[1]) / 2;
     70             }
     71         }
     72 
     73 
     74         public static double[] GeohashDecode(String geohash)
     75         {
     76             bool even = true;
     77             double[] lat = { -90.0, 90.0 };
     78             double[] lon = { -180.0, 180.0 };
     79 
     80             foreach (char c in geohash)
     81             {
     82                 int cd = Base32.IndexOf(c);
     83                 for (int j = 0; j < 5; j++)
     84                 {
     85                     int mask = Bits[j];
     86                     if (even)
     87                     {
     88                         RefineInterval(ref lon, cd, mask);
     89                     }
     90                     else
     91                     {
     92                         RefineInterval(ref lat, cd, mask);
     93                     }
     94                     even = !even;
     95                 }
     96             }
     97 
     98             return new[] { (lat[0] + lat[1]) / 2, (lon[0] + lon[1]) / 2 };
     99         }
    100 
    101         public static String GeohashEncode(double latitude, double longitude)
    102         {
    103             bool even = true;
    104             int bit = 0;
    105             int ch = 0;
    106             int precision = 12;
    107             string geohash = "";
    108 
    109             double[] lat = { -90.0, 90.0 };
    110             double[] lon = { -180.0, 180.0 };
    111 
    112 
    113             while (geohash.Length < precision)
    114             {
    115                 double mid;
    116 
    117                 if (even)
    118                 {
    119                     mid = (lon[0] + lon[1]) / 2;
    120                     if (longitude > mid)
    121                     {
    122                         ch |= Bits[bit];
    123                         lon[0] = mid;
    124                     }
    125                     else
    126                     {
    127                         lon[1] = mid;
    128                     }
    129                 }
    130                 else
    131                 {
    132                     mid = (lat[0] + lat[1]) / 2;
    133                     if (latitude > mid)
    134                     {
    135                         ch |= Bits[bit];
    136                         lat[0] = mid;
    137                     }
    138                     else
    139                     {
    140                         lat[1] = mid;
    141                     }
    142                 }
    143 
    144                 even = !even;
    145                 if (bit < 4)
    146                 {
    147                     bit++;
    148                 }
    149                 else
    150                 {
    151                     geohash += Base32[ch];
    152                     bit = 0;
    153                     ch = 0;
    154                 }
    155             }
    156             return geohash;
    157         }
    View Code

    JS代码—引用 https://github.com/davetroy/geohash-js/blob/master/geohash.js

      1 <script type="text/javascript">
      2         BITS = [16, 8, 4, 2, 1];
      3 
      4         BASE32 = "0123456789bcdefghjkmnpqrstuvwxyz";
      5         NEIGHBORS = { right: { even: "bc01fg45238967deuvhjyznpkmstqrwx" },
      6             left: { even: "238967debc01fg45kmstqrwxuvhjyznp" },
      7             top: { even: "p0r21436x8zb9dcf5h7kjnmqesgutwvy" },
      8             bottom: { even: "14365h7k9dcfesgujnmqp0r2twvyx8zb" }
      9         };
     10         BORDERS = { right: { even: "bcfguvyz" },
     11             left: { even: "0145hjnp" },
     12             top: { even: "prxz" },
     13             bottom: { even: "028b" }
     14         };
     15 
     16         NEIGHBORS.bottom.odd = NEIGHBORS.left.even;
     17         NEIGHBORS.top.odd = NEIGHBORS.right.even;
     18         NEIGHBORS.left.odd = NEIGHBORS.bottom.even;
     19         NEIGHBORS.right.odd = NEIGHBORS.top.even;
     20 
     21         BORDERS.bottom.odd = BORDERS.left.even;
     22         BORDERS.top.odd = BORDERS.right.even;
     23         BORDERS.left.odd = BORDERS.bottom.even;
     24         BORDERS.right.odd = BORDERS.top.even;
     25 
     26         function refine_interval(interval, cd, mask) {
     27             if (cd & mask)
     28                 interval[0] = (interval[0] + interval[1]) / 2;
     29             else
     30                 interval[1] = (interval[0] + interval[1]) / 2;
     31         }
     32 
     33         function calculateAdjacent(srcHash, dir) {
     34             srcHash = srcHash.toLowerCase();
     35             var lastChr = srcHash.charAt(srcHash.length - 1);
     36             var type = (srcHash.length % 2) ? 'odd' : 'even';
     37             var base = srcHash.substring(0, srcHash.length - 1);
     38             if (BORDERS[dir][type].indexOf(lastChr) != -1)
     39                 base = calculateAdjacent(base, dir);
     40             return base + BASE32[NEIGHBORS[dir][type].indexOf(lastChr)];
     41         }
     42 
     43         function decodeGeoHash(geohash) {
     44             var is_even = 1;
     45             var lat = []; var lon = [];
     46             lat[0] = -90.0; lat[1] = 90.0;
     47             lon[0] = -180.0; lon[1] = 180.0;
     48             lat_err = 90.0; lon_err = 180.0;
     49 
     50             for (i = 0; i < geohash.length; i++) {
     51                 c = geohash[i];
     52                 cd = BASE32.indexOf(c);
     53                 for (j = 0; j < 5; j++) {
     54                     mask = BITS[j];
     55                     if (is_even) {
     56                         lon_err /= 2;
     57                         refine_interval(lon, cd, mask);
     58                     } else {
     59                         lat_err /= 2;
     60                         refine_interval(lat, cd, mask);
     61                     }
     62                     is_even = !is_even;
     63                 }
     64             }
     65             lat[2] = (lat[0] + lat[1]) / 2;
     66             lon[2] = (lon[0] + lon[1]) / 2;
     67 
     68             return { latitude: lat, longitude: lon };
     69         }
     70 
     71         function encodeGeoHash(latitude, longitude) {
     72             var is_even = 1;
     73             var i = 0;
     74             var lat = []; var lon = [];
     75             var bit = 0;
     76             var ch = 0;
     77             var precision = 12;
     78             geohash = "";
     79 
     80             lat[0] = -90.0; lat[1] = 90.0;
     81             lon[0] = -180.0; lon[1] = 180.0;
     82 
     83             while (geohash.length < precision) {
     84                 if (is_even) {
     85                     mid = (lon[0] + lon[1]) / 2;
     86                     if (longitude > mid) {
     87                         ch |= BITS[bit];
     88                         lon[0] = mid;
     89                     } else
     90                         lon[1] = mid;
     91                 } else {
     92                     mid = (lat[0] + lat[1]) / 2;
     93                     if (latitude > mid) {
     94                         ch |= BITS[bit];
     95                         lat[0] = mid;
     96                     } else
     97                         lat[1] = mid;
     98                 }
     99 
    100                 is_even = !is_even;
    101                 if (bit < 4)
    102                     bit++;
    103                 else {
    104                     geohash += BASE32[ch];
    105                     bit = 0;
    106                     ch = 0;
    107                 }
    108             }
    109             return geohash;
    110         }
    111     </script>
    View Code
  • 相关阅读:
    powerdesigner得使用放法
    sql sever连接名忘记了该怎么办
    算法第四版 1.2.10
    算法第四版 1.2.8
    算法第四版 1.2.6
    算法第四版 1.2.2
    二分查找递归实现
    关于斐波那契数列和递归
    编写一段代码,打印一个M行N列的二维数组转置。(交换行和列)
    基础实验2-2.1 整数的分类处理 (20分)
  • 原文地址:https://www.cnblogs.com/zrhai/p/3832070.html
Copyright © 2011-2022 走看看