ES地理位置坐标检索功能调研

83 阅读4分钟

1、常用坐标系

  • WGS84 坐标系
    即地球坐标系,国际上通用的坐标系。 设备一般包含 GPS 芯片或者北斗芯片获取的经纬度为 WGS84 地理坐标系。谷歌地图采用的是 WGS84 地理坐标系(中国范围除外,谷歌中国地图采用的是GCJ02地理坐标系。)

  • GCJ02 坐标系
    即火星坐标系,WGS84坐标系经加密后的坐标系。 出于国家安全考虑,国内所有导航电子地图必须使用国家测绘局制定的加密坐标系统,即将一个真实的经纬度坐标加密成一个不正确的经纬度坐标。

  • BD09 坐标系
    即百度坐标系,GCJ0 2坐标系经加密后的坐标系。搜狗坐标系、图吧坐标系等,估计也是在GCJ02基础上加密而成的。

国内除了百度,大部分地图及地理位置服务商使用的都是火星坐标系。

注:如果使用GCJ-02坐标系,Geohash函数和距离计算函数理论上都应在WGS84坐标系下使用,在火星坐标系下会存在一定的偏差,主要是火星坐标系的加偏处理带来的,经过查阅资料及抽样测试,认为该误差在可接受范围内。

地理位置坐标系在线转化 golang-坐标系转换

2、Elasticsearch(GEO)空间检索查询

Elasticsearch 提供了 两种表示地理位置的方式:用纬度-经度表示的坐标点使用 geo_point 字段类型, 以 GeoJSON 格式定义的复杂地理形状,使用 geo_shape 字段类型。

Geo-points 允许你找到距离另一个坐标点一定范围内的坐标点、计算出两点之间的距离来排序或进行相关性打分、或者聚合到显示在地图上的一个网格。另一方面,Geo-shapes 纯粹是用来过滤的。它们可以用来判断两个地理形状是否有重合或者某个地理形状是否完全包含了其他地理形状。

(1)、地理坐标过滤

有四种地理坐标点相关的过滤器可以用来选中或者排除文档:

  • geo_bounding_box
    找出落在指定矩形框中的点。
  • geo_distance
    找出与指定位置在给定距离内的点。
  • geo_distance_range
    找出与指定点距离在给定最小距离和最大距离之间的点。
  • geo_polygon
    找出落在多边形中的点。 这个过滤器使用代价很大 。当你觉得自己需要使用它,最好先看看 geo-shapes 。

这些过滤器判断点是否落在指定区域时的计算方法稍有不同,但过程类似。指定的区域被转换成一系列以quad/geohash为前缀的tokens,并被用来在倒排索引中搜索拥有相同tokens的文档。

Tips: 地理坐标过滤器使用代价昂贵 — 所以最好在文档集合尽可能少的场景下使用。你可以先使用那些简单快捷的过滤器,比如 term 或 range ,来过滤掉尽可能多的文档,最后才交给地理坐标过滤器处理。
布尔型过滤器 bool filter 会自动帮你做这件事。它会优先让那些基于“bitset”的简单过滤器(见 关于缓存 )来过滤掉尽可能多的文档,然后依次才是更昂贵的地理坐标过滤器或者脚本类的过滤器。

geo_bounding_box 是这是目前为止最有效的地理坐标过滤器了,因为它计算起来非常简单,当精度要求不高时建议使用此方法。

优化方案:

[1]、把 geo_point 字段 用 lat 和 lon 的方式分别映射到索引中

{
  "mappings": {
    "restaurant": {
      "properties": {
        "name": {
          "type": "string"
        },
        "location": {
          "type":    "geo_point",
          "lat_lon": true 
        }
      }
    }
  }
}

查询时设置 type 参数为 indexed (替代默认值 memory )来明确告诉 Elasticsearch 对这个过滤器使用倒排索引。

{
  "query": {
    "filtered": {
      "filter": {
        "geo_bounding_box": {
          "type":    "indexed", 
          "location": {
            "top_left": {
              "lat":  40.8,
              "lon": -74.0
            },
            "bottom_right": {
              "lat":  40.7,
              "lon":  -73.0
            }
          }
        }
      }
    }
  }
}

注:geo_point 类型的字段可以包含多个地理坐标点,但是针对经度纬度分别索引的这种优化方式只对包含单个坐标点的字段有效。

[2]、地理距离过滤器计算代价昂贵。为了优化性能,Elasticsearch 先画一个矩形框来围住整个圆形,这样就可以先用消耗较少的盒模型计算方式来排除掉尽可能多的文档。 然后只对落在盒模型内的这部分点用地理距离计算方式处理。

参考文档

WGS84地球坐标系,GCJ02火星坐标系,BD09百度坐标系简介与转换

Geohash算法