1. 程式人生 > >Geohash——地理座標索引

Geohash——地理座標索引

今天看july的部落格:第三十六~三十七章、搜尋智慧提示suggestion,附近地點搜尋

http://blog.csdn.net/v_july_v/article/details/11288807

裡面提到了geohash演算法對地理座標的索引,但是引用的文章和例子讓我產生了疑問,對於座標的經緯度不應該是直接讓緯度跟隨在經度之後形成一個索引值的,這樣只能保證經度相同的且靠近的點排列的比較近,而緯度相同經度只差1的點確不能排列的比較近。

於是查閱了geohash的維基百科說明:http://en.wikipedia.org/wiki/Geohash#Decode_from_base_32

文中舉了個例子:

This operation results in the bits 01101 11111 11000 00100 00010. Assuming that counting starts at 0 in the left side, the even bits are taken for the longitude code (0111110000000), while the odd bits are taken for the latitude code (101111001001).

原來是經度和緯度相互交錯形成的geohash值。這樣就能比較好的讓靠近的點按照geohash值也能排列的靠近一些。

寫了一段程式來看看經緯度(x座標和y座標)互相交錯形成的有序值對應的座標是多少。

//vs2008

#include "stdafx.h"
#include <atlstr.h>
#include <conio.h>

void GetEvenAndOdd(int nNum, int &nEven, int &nOdd)
{
    int k = 1;
    nEven = nOdd = 0;
    for (int i = 0; i < 8; i++)
    {
        if (0 != i%2)
        {
            nEven += k*(nNum & 1);
        } 
        else
        {
            nOdd += k*(nNum & 1);
        }
        if (0 != i%2) k *= 2;
        nNum = nNum >> 1;
    }
}

CString GetBinaryString(int nNum, int nLen)
{
    CString csRet;
    for (int i = nLen; i > 0; i--)
    {
        if ((nNum >> (i-1)) & 1)
            csRet += "1";
        else
            csRet += "0";
    }
    return csRet;
}

int _tmain(int argc, _TCHAR* argv[])
{
    printf("十進位制	geohash值	偶    奇    座標值\n");
    for (int i = 0; i < 64; i++)
    {
        int nEven, nOdd;
        GetEvenAndOdd(i, nEven, nOdd);
         CString strGeohash = GetBinaryString(i, 8);
         CString strEven    = GetBinaryString(nEven, 4);
         CString strOdd     = GetBinaryString(nOdd, 4);
        printf("%02d\t%S\t%S\t%S\t(%d,%d)\n", i, strGeohash.GetBuffer(), 
            strEven.GetBuffer(), strOdd.GetBuffer(), nEven, nOdd);
    }
    _getch();
	return 0;
}

跑出來的座標值如下:

十進位制 geohash值 座標值 十進位制 geohash 值 座標值
00 00000000 0000 0000 (0,0) 32 00100000 0100 0000 (4,0)
01 00000001 0000 0001 (0,1) 33 00100001 0100 0001 (4,1)
02 00000010 0001 0000 (1,0) 34 00100010 0101 0000 (5,0)
03 00000011 0001 0001 (1,1) 35 00100011 0101 0001 (5,1)
04 00000100 0000 0010 (0,2) 36 00100100 0100 0010 (4,2)
05 00000101 0000 0011 (0,3) 37 00100101 0100 0011 (4,3)
06 00000110 0001 0010 (1,2) 38 00100110 0101 0010 (5,2)
07 00000111 0001 0011 (1,3) 39 00100111 0101 0011 (5,3)
08 00001000 0010 0000 (2,0) 40 00101000 0110 0000 (6,0)
09 00001001 0010 0001 (2,1) 41 00101001 0110 0001 (6,1)
10 00001010 0011 0000 (3,0) 42 00101010 0111 0000 (7,0)
11 00001011 0011 0001 (3,1) 43 00101011 0111 0001 (7,1)
12 00001100 0010 0010 (2,2) 44 00101100 0110 0010 (6,2)
13 00001101 0010 0011 (2,3) 45 00101101 0110 0011 (6,3)
14 00001110 0011 0010 (3,2) 46 00101110 0111 0010 (7,2)
15 00001111 0011 0011 (3,3) 47 00101111 0111 0011 (7,3)
16 00010000 0000 0100 (0,4) 48 00110000 0100 0100 (4,4)
17 00010001 0000 0101 (0,5) 49 00110001 0100 0101 (4,5)
18 00010010 0001 0100 (1,4) 50 00110010 0101 0100 (5,4)
19 00010011 0001 0101 (1,5) 51 00110011 0101 0101 (5,5)
20 00010100 0000 0110 (0,6) 52 00110100 0100 0110 (4,6)
21 00010101 0000 0111 (0,7) 53 00110101 0100 0111 (4,7)
22 00010110 0001 0110 (1,6) 54 00110110 0101 0110 (5,6)
23 00010111 0001 0111 (1,7) 55 00110111 0101 0111 (5,7)
24 00011000 0010 0100 (2,4) 56 00111000 0110 0100 (6,4)
25 00011001 0010 0101 (2,5) 57 00111001 0110 0101 (6,5)
26 00011010 0011 0100 (3,4) 58 00111010 0111 0100 (7,4)
27 00011011 0011 0101 (3,5) 59 00111011 0111 0101 (7,5)
28 00011100 0010 0110 (2,6) 60 00111100 0110 0110 (6,6)
29 00011101 0010 0111 (2,7) 61 00111101 0110 0111 (6,7)
30 00011110 0011 0110 (3,6) 62 00111110 0111 0110 (7,6)
31 00011111 0011 0111 (3,7) 63 00111111 0111 0111 (7,7)

將座標點依次在座標系上連起來如下圖:


可以看出來,geohash和 july文中講述的R數方法有圖形上的分割槽域相似之處,這種編碼方式在區塊內儘可能的讓鄰近點的索引值靠近,但在區塊與區塊之間還是存在跳躍的地方。所以只簡單使用這一種編碼技術並不能完全解決鄰近點搜尋的問題。

要想完美解決問題,還需要考慮的更多一些。

這些問題留待以後繼續研究。先寫這麼多吧。

update:

早上起來搜尋了下別人的文章,看到了下面的話:

geohash的最大用途就是附近地址搜尋了。不過,從geohash的編碼演算法中可以看出它的一個缺點:位於格子邊界兩側的兩點, 雖然十分接近,但編碼會完全不同。實際應用中,可以同時搜尋當前格子周圍的8個格子,即可解決這個問題。

(from: http://tech.idv2.com/2011/07/05/geohash-intro/)