GeoHash补充

最近在逛cocoa的时候发现有一些小细节可以优化的
- 我们在设定二分精度时可以设置精度为个体的影响范围为精度,这样我们在搜索的时候可以直接用“==”而非“like”
- 我们可以不用进行base32编码,直接将其转化为unsinged long,可以大大减少计算,数据库使用字符串是因为很多数据库对字符串类型有很好的支持,c上面用字符串效率低得可以
- 之前那个写得仓促,有很多错误没整理,这次这个是完整版,当然,只是对我的游戏进行了优化,其他实现需要开发者动起手来

#pragma once

#include"BaseEntity.h"
#include"cocos2d.h"
#include<algorithm>
#include<bitset>

using namespace std;

class GeoHash
{
public:
    static char base32_encode[32];

#define precision 10
#define binaryLength 20

#define world_size Vec2(1000,1000)//wathc out!if x!=y,there will be problems in combining binary code 

protected://dividision
    static void halfDivision(double, double, double,string&);//half division,declare the left part as '0' there
    static bool between(double value,double min,double max)
    {
        return value > min&&value < max;
    }
protected://convert to base32 code
    static string base32Encoding(string);

public:
    static string getIndex(Vec2);//get GeoHash code
    static unsigned long getUlIndex(Vec2);
    static std::vector<BaseEntity*> getNeighbors(Vec2, std::vector<BaseEntity*>);
};

char GeoHash::base32_encode[] = {
    '0', '1', '2', '3', '4', '5', '6', '7',
    '8', '9', 'b', 'c', 'd', 'e', 'f', 'g',
    'h', 'j', 'k', 'm', 'n', 'p', 'q', 'r',
    's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
};

string GeoHash::base32Encoding(string bitStr)
{
    string outPutStr;
    int count = 0;
    while (bitStr.length>count * 5)
    {
        string eachStr;
        for (int i = 0; i < 5; i++)
        {
            eachStr.push_back(bitStr.at(count + i));
        }
        int refer = 0;
        for (int j = 0; j < 5; j++)
        {
            refer = refer * 2 + (eachStr[j] - '0');
        }
        outPutStr.push_back(base32_encode[refer]);
    }

    if (bitStr.length > count * 5)//push '0' to fill the rest
    {
        int addTo5 = (count + 1) * 5 - bitStr.length;
        string eachStr;
        for (int i = 0; i < addTo5; i++)
        {
            eachStr.push_back('0');
        }
        int refer = 0;
        for (int j = 0; j < 5; j++)
        {
            refer = refer * 2 + (bitStr[j] - '0');
        }
        outPutStr.push_back(base32_encode[refer]);
    }
    return outPutStr;
}

string GeoHash::getIndex(Vec2 po)
{
    string bitX;
    halfDivision(world_size.x, world_size.y, po.x, bitX);

    string bitY;
    halfDivision(world_size.x, world_size.y, po.y, bitY);

    //将xy轴组合,偶数位放y,奇数位放x
    string totalBit;
    for (int i = 0; i < bitX.size(); i++)
    {
        totalBit.push_back(bitY.at(i));
        totalBit.push_back(bitX.at(i));
    }

    return base32Encoding(totalBit);
}
unsigned long GeoHash::getUlIndex(Vec2 po)
{
    string bitX;
    halfDivision(world_size.x, world_size.y, po.x, bitX);

    string bitY;
    halfDivision(world_size.x, world_size.y, po.y, bitY);

    //将xy轴组合,偶数位放y,奇数位放x
    string totalBit;
    for (int i = 0; i < bitX.size(); i++)
    {
        totalBit.push_back(bitY.at(i));
        totalBit.push_back(bitX.at(i));
    }

    bitset<binaryLength> binaryIndex(totalBit);
    return binaryIndex.to_ulong();
}

void GeoHash::halfDivision(double min, double max, double value,string& bit)
{
    if (max - min < precision)
    {
        return;
    }
    if (between(value, min, (min + max) / 2))
    {
        bit += "0";
        halfDivision(value, min, (min + max) / 2, bit);
    }
    else
    {
        bit += "1";
        halfDivision(value, (min + max) / 2, max, bit);
    }
}

std::vector<BaseEntity*> GeoHash::getNeighbors(Vec2 centre, vector<BaseEntity*> preSelectedEntities)
{
    vector<BaseEntity*> temp;
    for_each(preSelectedEntities.cbegin(), preSelectedEntities.cend(), [&temp,centre](BaseEntity* entity){
        if (entity->getGeoHashIndex() == getUlIndex(centre))
        {
            temp.push_back(entity);
        }
    });

    return temp;
}
### 使用 Geohash 进行范围查询的实现方法 Geohash 是一种将地理位置(经纬度)编码成字符串的方法,它能够有效地用于空间索引和距离计算。通过 Geohash 的特性可以快速缩小查找范围并提高效率。 #### 1. 编码与解码 在进行范围查询之前,通常需要先掌握如何对地理坐标进行编码和解码。以下是基于 PHP 的简单示例: ```php function encode($lat, $lon, $precision) { // 将给定的纬度、经度和精度编码为 Geohash 字符串 // 此处省略具体实现逻辑,可参考标准库或开源项目[^2] } function decode($geohash) { // 将给定的 Geohash 字符串解码为纬度和经度 // 返回数组形式的结果 [latitude, longitude] } ``` #### 2. 范围查询的核心思路 为了实现范围查询,可以通过以下方式完成: - **邻近点扩展**:对于目标位置的 Geohash 值,生成其相邻区域的多个可能值。 - **距离过滤**:利用球面几何公式或其他高效的距离计算工具进一步筛选符合条件的数据点。 ##### Java 示例代码 下面是一个简单的 Java 实现案例,展示如何使用 Geohash 来执行范围查询操作: ```java import com.spatial4j.core.context.SpatialContext; import com.spatial4j.core.shape.Shape; public List<Map<String, Object>> geohashRangeQuery(String strGeohash, double dist, List<Map> _list) { List<Map<String, Object>> result = new ArrayList<>(); for (int i = 0; i < _list.size(); i++) { Map map = (Map)_list.get(i); String _geohash = map.get("geohash").toString(); // 计算两点之间的实际距离 double _dist = geoHash.distance(strGeohash, _geohash)[^1]; if (_dist < dist) { result.add(map); } } return result; } ``` 此函数接受三个参数:`strGeohash` 表示中心点的 Geohash;`dist` 定义最大允许半径内的距离;以及 `_list` 存储待匹配的所有数据记录集合。 #### 3. 邻居生成策略 由于单个 Geohash 可能无法完全覆盖圆形查询边界,因此还需要考虑生成周围邻居节点来补充缺失部分。例如,在 Python 中有如下做法: ```python def get_neighbors(geohash): import pygeohash as pgh neighbors = set() base_geohash_length = len(geohash) # 获取当前格子及其八个方向上的直接邻居 neighbor_hashes = [ pgh.neighbors(geohash), geohash, ] for h in neighbor_hashes: if isinstance(h, list): neighbors.update(set(h)) elif isinstance(h, str): neighbors.add(h[:base_geohash_length]) return list(neighbors) ``` 以上脚本会返回输入 Geohash 所属单元格连同周边所有潜在关联项组成的列表。 --- ### 性能优化建议 当处理大规模数据集时,单纯依靠逐条对比可能会显得低效。此时推荐引入专门的空间数据库支持,比如 Elasticsearch 或 MongoDB 提供内置 Geo-Spatial 查询能力,从而显著提升检索速度。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值