Geohash:使用libgeohash查找邻居

时间:2015-07-10 04:09:39

标签: algorithm geolocation geocoding geo geohashing

在我的应用程序中,我将所有用户的Geohash存储在一个表中,并希望使用这些Geohashes找到用户的邻居。

根据信息,我在Wiki上收集了关于Geohash的信息:

  

在数据库中使用时,地理数据的结构有两个   好处。首先,由geohash索引的数据将包含a的所有点   给定连续切片中的矩形区域(切片数量)   取决于所需的精度和geohash"故障的存在   线&#34)。这在查询的数据库系统中特别有用   单个索引比多索引更容易或更快   查询。其次,这个索引结构可以用于a   快速和肮脏的邻近搜索 - 最近的点往往是   最近的地理位置。

因此,例如为了找到" sj8101b085"的邻居,我刚刚计划通过这样做搜索哈希:

SELECT * FROM Users WHERE Geohash LIKE 'sj8101b085%'

然后通过逐个减少哈希长度来触发相同的查询,即" sj8101b08%"," sj8101b0%"等等,直到我得到所需数量的邻居。我觉得这就是我需要做的全部。

但后来我在同一篇文章的底部发现了这个C库libgeohash。该库有一个名为GEOHASH_get_adjacent的函数,它给出了给定散列的相邻散列。 geohash字符串表示地球上的矩形区域。此函数返回表示相邻矩形的geohashes。这意味着我必须在递归(邻居和邻居的邻居等)中运行此函数,直到我得到所需数量的邻居。

现在我真的很困惑如何编写搜索算法?使用第一种方法还是使用第二种方法?

2 个答案:

答案 0 :(得分:2)

geohash是一个位串,其中偶数位表示经度,奇数位表示纬度。例如,经度表示的每个比特选择可行区域的一半。初始可行区域为[-180,180],如果经度的第一位为0,则下一个可行区域变为[-180,0],如果为1,则变为[0,180]。前两位一起选择赤道上方或下方的地球的一半以及本初子午线左侧或右侧的地球的一半。您可以将其视为“矩形区域”,因为它在您的维基百科链接中调用。前四位合在一起,选择了北半球或南半球的一半,以及东半球或西半球的一半。等等。

你的链接中显示的geohash,ezs42是32,所以每个字符代表geohash的5位。示例散列的含义是5个字符,即geohash是25位,其中13位用于经度,其中12位用于纬度。这意味着经度被分成一半13次,而纬度被分成两半12次,并且geohash选择十二个纬度范围中的一个和十三个纵向范围中的一个。从哈希末尾删除的每个字符都会从geohash中删除5位;这相当于经度的3个分区和纬度的2个分区,反之亦然。否则,它会使您的纵向范围增加8倍,纬度范围增加4倍,反之亦然。查询该geohash会给出落在相应“矩形”区域内的所有点。

我不熟悉libgeohash;但是,根据您的描述,听起来好像您给它一个geohash并且它会返回一组geohashes,它们以输入所隐含的粒度表示相邻的“矩形”区域。据推测,如果你用这个来找到最近的邻居,你需要跟踪你已经访问过的那些地理位置以及你没有访问过的地理位置,你必须反复询问邻居,直到找到你所在的点数为止。正在寻找。从视觉上看,这看起来就像是从你最初的“矩形”geohash开始,它是你原来的“矩形”的大小。您需要注意不要只考虑在其中一个相邻区域中找到的第一个点,因为另一个相邻区域可能有一个更靠近查询点的点;也就是说,在搜索距离查询点最近的k之前,您需要考虑来自所有邻居的点(例如,这意味着您需要询问并查询来自原始“矩形”的所有8个邻居的邻居,然后在邻居方法的第二次迭代中寻找你最近的k。)

考虑到libgeohash邻居的方法,如果你的原始“矩形”很小(比如英寸,英寸),并且你的点数足够稀疏,那么你需要花费大量的时间才能通过这个扇形覆盖足够的地球直到找到你的观点才结出技术。另一方面,使用前缀方法,可能是您的点足够密集,将范围增加4倍和8倍会产生大量要考虑的点。在任何一种情况下,如果你正在寻找k个最近邻居,你仍然需要测试所有得到的距离点,以选择最近的k。最后,您的选择将取决于您的数据;但是,我建议从前缀方法开始,因为它比相邻的“矩形”区域方法简单得多。

答案 1 :(得分:0)

public Set<String> getMoreNeighbours(int surroundRange, String originHash){
    int matrixSize = nthOddNumber(surroundRange / 5);
    Set<String> locationSet = new HashSet<>();
    locationSet.add(originHash);
    List<String> tempNbHash = new ArrayList<>();
    for(int i=0; i < matrixSize / 2; i++) {
        if(tempNbHash.isEmpty()) {
        Map<String, Boolean> memo = new HashMap<>();
            Set<String> collection = new HashSet<>();
            locationSet.forEach(loc -> {
                if (!memo.containsKey(loc)) {
                    Collection<? extends CharSequence> neighbors = GeoHashUtils.neighbors(loc);
                    neighbors.forEach(nb -> collection.add(nb.toString()));
                }
                memo.put(loc, true);
            });
            locationSet.addAll(collection);
            tempNbHash.addAll(collection);
        } else {
            Map<String, Boolean> memo = new HashMap<>();
            Set<String> collection = new HashSet<>();
            tempNbHash.forEach(loc -> {
                if (!memo.containsKey(loc)) {
                    Collection<? extends CharSequence> neighbors = GeoHashUtils.neighbors(loc);
                    neighbors.forEach(nb -> collection.add(nb.toString()));
                }
                memo.put(loc, true);
            });
            locationSet.addAll(collection);
            tempNbHash.clear();
            tempNbHash.addAll(collection);
        }
    }
    return locationSet;
}

public int nthOddNumber(int n){
    return (2 * n - 1);
}