根据GEOHASH,查找附近的人,判断距离远

思路:提取用户经纬度,生成GEOHASH,根据geohash的精度判断距离远近,结合 http://bbs.appcan.cn/forum.php?m ... 7391&extra=page%3D1 这个帖子,可以完全计算出附近的人以及精确的距离(不过微信用的是大致距离,计算精确距离太耗费资源了。)


先普及一下GEOHASH的知识:
http://blog.jobbole.com/80633/  GEO核心原理,懒得复制了,大家移步看一下就好了。

反正就是可以把精度和维度,这两段数字,处理成一段若干长度的字符,这一串字符越长,距离信息越精确,同时,两个人的距离越近,GEOHASH的字符越接近。
比如我在B点,我的经纬度转换成GEOHASH是:45sadfdagfgfa43
你在A店,你的经纬度转换成GEOHASH是:       45sadfdagfgfg34
我可以根据geohash的字符位数情况,判断两点的大致距离。
判断依据:



从上图可以看出,当GEOHASH长度为8的时候,转换成经纬度的长度是20位,那么距离误差是正负0.019千米。
长度为7,那么距离误差是正负0.076千米。
好了。有了这个思路,就OK了。
我们把每个人的经纬度都转换成geohash,根据geohash选择周围的事物距离,比如截取geohash前5位,进行对比,前五位全部相同,说明这些geohash距离是2.4公里以内的(微信的附近人最远就是2.5公里,估计也是这个东西搞出来的)

那么,怎么取得GEOHASH呢?
别的不会,对PHP稍有了解。网上翻了翻,就用PHP的GEOHASH扩展来搞。
首先下载gethub,把他**到自己的服务器里,
https://github.com/shenzhe/geohash
从新编译PHP:
然后到geohash目录。
执行:phpize  (最好带上PHP安装目录的绝对路径)
./configure
make
make install
然后把 geohash.so 加入到php.ini中

装好之后看一下PHPINFO是否成功启用了。

然后就可以直接在PHP页面里转换了:

/** *  $latitude    //纬度
*  $longitude   //经度
*  $precision   //精密度, 默认是12
*  返回 $precision 长度的 string 
*/
geohash_encode($latitude, $longitude, $precision=12);  



/**
*  $hash    //geohash_encode后的值
*  返回 array // Array
*                  (
*                      [latitude] => 39.416916975752
*                      [longitude] => 100.92223992571
*                      [north] => 39.416917059571
*                      [east] => 100.92224009335
*                      [south] => 100.92223992571
*                      [west] => 100.92223975807
*                  )
*/
geohash_decode($hash);

/**
*  $hash    //geohash_encode后的值
*  返回 在$hash 8个 (东南西北各二个)附近的hash值
*/
geohash_neighbors($hash);

/**
*  $precision    //精密度
*  返回 数组,array("width"=>12.0, "height"=>12.0) 
*  表示矩形的宽和高
*/
geohash_dimension($hash);


上面教程是gethub里复制的,有问题可以私信我。
通过geohash_encode取得hash之后,我们可以通过各种方式,将他截取下来,
然后作为KEY,写入redis或者mysql。(强烈建议用redis,用起来太爽了)
我直接写了一个方法,根据各种距离,返回不同的字符长度
        function getgeohash($lat,$log){
                $geohash=geohash_encode($lat,$log,12); //取得原始GEOHASH
                $arr['0']=$geohash;//原始HASH
                $arr['9']=substr($geohash,0,9); //9位hash 距离最精确,
                $arr['8']=substr($geohash,0,8);//8位,距离相对精确,具体精度看上面的表。
                $arr['7']=substr($geohash,0,7);//7位,距离也挺精确
                $arr['6']=substr($geohash,0,6);
                $arr['5']=substr($geohash,0,5);
                $arr['4']=substr($geohash,0,4);//4位hash,只要前4位相同,可以找出附近20KM的人事物。
                return $arr;
        }









redis的PHP扩展不解释了,有疑问的可以私信我。
$redis->sAdd('near:near:'.$arr[4],value);//20KM内的事物集合。

$redis->sAdd('near:near:'.$arr[5],value);//2.4KM内的事物集合。
$redis->sAdd('near:near:'.$arr[6],value);//0.61KM内的事物集合。
$redis->sAdd('near:near:'.$arr[7],value);//0.076KM内的事物集合。
$redis->sAdd('near:near:'.$arr[8],value);//0.019KM内的事物集合。


这里的$value,各位随意存数据,可以是用户信息的json,或者只放一个用户的uid随意啦。
'near:near arr[4-8] 这主要作寻址用,各位可以自己配置自己的路径,是各个数据集的key,根据情况酌情取就行了。


使用:
用户A,登陆后,先写入redis:
$redis->sAdd('near:near:'.$arr[4],value);//20KM内的事物集合。
当用户选择查询周围20KM的事物,
直接取得用户当前的geohash,然后截取geohash前4位。如:$geoarr[4];
然后读取redis
$redis->sort('near:near:'.$geoarr[4]);
这里直接返回了所有20KM内用户的集合。


不明白的私信吧,如果redis搞不定,可以用mysql存。


用redis有一个好处是可以结合sting或者hash等设置过期时间,比如30分钟后自动生效,这个各位自己摸索吧。
我这边的产品未成形,没有完整代码共享。看 杨焕昭 朋友发的:计算两个经纬度之间的直线距离 http://bbs.appcan.cn/forum.php?m ... 1&fromuid=55258


一时脑热,我也发一个,,,很轻松就实现了附近人,或者摇一摇搜索附近人,附近商家,等等好玩的东西。

##############################################################

下面评论的哥们给了一个geohash的github地址

```

    - https://github.com/CloudSide/geohash

```




https://blog.csdn.net/qq_36373262/article/details/62419390

<think>好的,我现在需要详细解释一下Elasticsearch中的geo_shape类型。首先,我得回忆一下自己之前学过的相关知识,确保准确无误。 首先,geo_shape是用来处理地理空间数据的,比如多边形、线、点这些形状。和geo_point的区别在于,geo_point只处理经纬度点,而geo_shape可以处理更复杂的形状。这可能涉及到空间关系,比如判断一个点是否在某个区域内,或者两个区域是否有交集。 接下来,数据类型方面,geo_shape在Elasticsearch中是一种字段类型。用户定义映射时需要指定。例如,创建一个索引时,字段类型设置为geo_shape,可能还需要指定一些参数,比如精度或者使用的树结构,比如geohash或者quadtree。这里可能需要提到一些术语,比如prefix trees,比如Geohash是如何将地理数据编码成字符串的,quadtree又是如何分割空间的。 然后,关于空间关系,Elasticsearch支持哪些呢?比如INTERSECTS、DISJOINT、WITHIN、CONTAINS等。这些关系在查询时使用,比如查询某个形状与索引中的文档是否有交集。 索引和查询的过程需要解释。索引地理形状时,Elasticsearch会将其转换为相应的前缀树结构中的网格单元,这会影响查询的精度和性能。可能还需要提到一些参数,比如tree、precision、tree_levels等,这些参数如何影响索引的细节。 在查询时,用户需要使用geo_shape查询,指定形状的坐标和空间关系类型。例如,使用geo_shape查询来查找所有与某个多边形相交的文档。这里需要举一个具体的例子,比如定义一个多边形区域,然后构造查询语句,使用WKT(Well-Known Text)格式或者GeoJSON格式来表示形状。 另外,可能还要提到一些注意事项。比如性能问题,复杂的形状或者高精度的设置可能导致索引变大,查询变慢。还有精度和误差之间的权衡,如何调整参数来优化。同时,不同Elasticsearch版本之间可能有差异,需要确认支持的版本。 可能还需要比较一下geo_shape和geo_point的应用场景。例如,geo_point适合存储点数据,并进行距离计算或聚合,而geo_shape适合处理区域覆盖、形状相交等复杂空间关系。 最后,提供一些示例代码,比如如何创建映射,如何索引一个多边形,如何执行查询。这些示例需要确保语法正确,并且符合Elasticsearch的版本要求。例如,使用GeoJSON格式定义形状,或者在查询中使用envelope来指定一个矩形区域。 在整理这些信息时,要注意结构的清晰,分点说明,让用户能够逐步理解geo_shape的各个方面。同时,要检查是否有遗漏的重要点,比如支持的形状类型(点、线、多边形、多边形集合等),或者坐标系的注意事项(WGS84坐标系,经纬度顺序是经度在前,纬度在后)等。可能还要提到如何处理坐标数组,比如多边形需要闭合,即第一个点和最后一个点相同。 总结下来,需要涵盖定义、用途、数据类型结构、空间关系类型、索引和查询方法、参数配置、示例以及注意事项。确保每个部分都简明扼要,准确无误,并且用中文表达自然,不使用Markdown格式。</think>Elasticsearch 的 `geo_shape` 类型用于处理复杂地理空间数据(如多边形、线、点集合等),支持空间关系判断(如相交、包含等)。以下是详细说明: --- ### **1. 核心概念** - **与 geo_point 的区别** `geo_point` 仅存储单个经纬度点,适合坐标检索和距离计算; `geo_shape` 存储复杂形状(如区域边界),支持空间关系分析。 - **坐标系要求** 使用 **WGS-84** 坐标系(即标准 GPS 经纬度),坐标格式为 `[经度, 纬度]`。 --- ### **2. 数据结构** #### **索引映射定义** ```json PUT /my_index { "mappings": { "properties": { "location": { "type": "geo_shape", "tree": "quadtree", // 可选前缀树类型:quadtree 或 geohash "precision": "100m" // 控制精度,影响索引细节 } } } } ``` - **tree**: 空间索引算法(默认 `quadtree`)。 - **precision**: 精度越高,索引越细密,但资源消耗越大(如 `1km`、`10m`)。 --- ### **3. 支持的形状类型** - **Point**: 单点 `{ "type": "Point", "coordinates": [13.4, 52.5] }` - **LineString**: 线段 `{ "type": "LineString", "coordinates": [[13.4,52.5], [13.5,52.6]] }` - **Polygon**: 多边形(必须闭合) ```json { "type": "Polygon", "coordinates": [ [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ] ] } ``` - **MultiPolygon**: 多个多边形集合 - **Envelope**: 矩形(通过左上、右下两点定义) `{ "type": "envelope", "coordinates": [[100.0, 1.0], [101.0, 0.0]] }` --- ### **4. 空间关系查询** #### **主要关系类型** - **INTERSECTS** (默认): 形状有交集 - **DISJOINT**: 完全不相交 - **WITHIN**: 被目标形状包含 - **CONTAINS**: 包含目标形状 #### **查询示例** ```json GET /my_index/_search { "query": { "geo_shape": { "location": { "relation": "INTERSECTS", "shape": { "type": "Polygon", "coordinates": [ [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ] ] } } } } } ``` --- ### **5. 性能优化建议** - **精度调节**: 更高的 `precision` 提高准确性但增加计算量,需权衡场景需求。 - **简化形状**: 减少多边形顶点数量以降低复杂度。 - **预定义形状**: 对频繁查询的形状可预先存储,通过 `indexed_shape` 引用。 ```json "shape": { "indexed_shape": { "index": "shapes_library", "id": "berlin_boundary", "path": "location" } } ``` --- ### **6. 典型应用场景** - **地理围栏**: 判断设备点(`geo_point`)是否在某个区域内(`geo_shape`)。 - **区域叠加分析**: 查找与某规划区域相交的土地地块。 - **路径规划**: 分析道路(线)与禁行区域(多边形)的交叉情况。 --- ### **7. 注意事项** - **坐标顺序**: 始终为 `[经度, 纬度]`,写反会导致错误。 - **多边形闭合**: 首尾坐标点需相同。 - **版本兼容性**: 部分功能(如 `geo_shape` 查询语法)在较旧版本中可能不同。 - **性能监控**: 复杂查询可能影响集群性能,需结合 `_search/validate` 调试。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值