ElasticSearch地理空间数据了解
使用场景
Elasticsearch 的地理空间数据处理功能在现代社会中有着广泛的应用,以下是一些常见的使用场景和方向:
1. 位置搜索和导航
- 本地服务发现:应用程序可以使用 Elasticsearch 查找用户附近的餐馆、商店、医院等服务。
- 导航系统:基于地理空间数据提供路线规划和实时交通信息。
- 实时位置跟踪:例如共享单车、外卖配送的实时位置追踪和展示。
2. 地理围栏(Geofencing)
- 安全区域管理:例如设定虚拟边界,当设备进入或离开某个区域时触发警报。
- 营销活动:根据用户位置发送定向广告或优惠信息。
3. 灾害管理和应急响应
- 自然灾害预警:利用地理空间数据监测和预测地震、洪水等自然灾害。
- 应急响应调度:在灾害发生时,快速定位和调度最近的应急服务资源。
4. 智能城市和物联网(IoT)
- 城市规划:通过分析城市的地理空间数据,优化公共设施布局和交通流量。
- 环境监测:传感器网络监测空气质量、水质等环境参数,并基于地理位置进行数据分析。
5. 房地产和物业管理
- 房产搜索:根据用户的地理位置需求,查找和推荐符合条件的房产。
- 物业管理:管理和维护物业的地理空间数据,进行资产跟踪和设施管理。
6. 物流和供应链管理
- 运输路线优化:分析运输路线,优化物流配送路径,减少运输时间和成本。
- 库存管理:跟踪和管理仓库的地理位置及库存分布。
7. 社交媒体和用户行为分析
- 位置打卡:社交媒体应用通过地理空间数据实现用户位置打卡功能。
- 用户行为分析:分析用户地理位置数据,了解用户活动模式和偏好。
8. 旅游和娱乐
- 旅游景点推荐:根据游客的当前位置和偏好,推荐附近的旅游景点和活动。
- 活动定位:例如音乐节、体育赛事等大型活动的地理位置管理和观众引导。
9. 零售和电子商务
- 店铺选址:分析地理数据,选择最佳的店铺开设位置。
- 库存分布优化:根据地理数据调整库存分布,优化供应链。
10. 公共安全和执法
- 犯罪热点分析:基于地理空间数据分析犯罪热点,制定更有效的执法策略。
- 巡逻路径优化:优化巡逻车辆的路径,提高巡逻效率和覆盖范围。
这些场景展示了 Elasticsearch 在处理和分析地理空间数据方面的强大能力,它能够帮助企业和机构更好地理解地理位置数据,做出更明智的决策,提高运营效率和服务质量。
ES与PostGIS对比
在地理信息行业中PostGIS是广大GISer常用的数据库工具。
Elasticsearch 和 PostGIS 都是处理地理空间数据的强大工具,但它们有不同的优点和用途。下面是对比这两者各自优点和应用场景的详细分析:
Elasticsearch
优点
- 分布式架构:Elasticsearch 是一个分布式搜索和分析引擎,能够处理大规模的数据集并提供高可用性和容错性。
- 全文搜索:除了地理空间查询,Elasticsearch 还擅长处理全文搜索,可以将地理空间数据与文本数据结合进行复杂查询。
- 实时性:Elasticsearch 擅长处理实时数据,能够快速索引和查询,适用于需要实时搜索和分析的场景。
- 地理空间聚合:支持地理空间聚合,可以进行复杂的地理空间分析,例如按距离或区域分组。
- 生态系统:Elasticsearch 是 Elastic Stack 的一部分,易于与 Kibana、Logstash 等工具集成,提供可视化和数据处理能力。
用途
- 本地服务和导航应用:例如本地商店搜索、地图导航、实时位置跟踪等。
- 社交媒体和用户行为分析:结合位置数据进行用户行为分析和内容推荐。
- 实时数据分析:适用于需要实时索引和查询地理空间数据的场景,如灾害预警、应急响应等。
- 搜索引擎优化:结合文本搜索和地理空间搜索,为用户提供更加精准的搜索结果。
PostGIS
优点
- 关系型数据库扩展:PostGIS 是 PostgreSQL 的地理空间扩展,继承了 PostgreSQL 的关系型数据库优势,支持复杂的 SQL 查询和事务处理。
- 丰富的地理空间功能:PostGIS 提供了大量的地理空间函数和操作,支持复杂的地理空间分析和处理。
- 精确性:PostGIS 在处理精确的地理空间计算方面表现出色,适用于需要高精度地理空间数据处理的场景。
- 标准化支持:完全支持 OGC 标准(Open Geospatial Consortium),确保与其他 GIS 系统的兼容性。
- 复杂查询:可以进行复杂的多表联接、嵌套查询和子查询,适用于复杂的数据分析和处理。
用途
- 地理信息系统(GIS):广泛用于 GIS 系统中,用于存储、查询和分析复杂的地理空间数据。
- 城市规划和管理:适用于城市规划、土地管理等需要精确地理空间数据处理的领域。
- 环境监测和管理:例如监测水质、空气质量、自然资源管理等。
- 基础设施管理:用于管理和分析基础设施数据,如电网、水管、交通网络等。
- 科学研究:适用于地理空间数据密集的科学研究,如地质调查、生态研究等。
对比总结
特性 | Elasticsearch | PostGIS |
---|---|---|
架构 | 分布式搜索和分析引擎 | 关系型数据库的地理空间扩展 |
实时性 | 强,适合实时数据处理 | 较弱,主要用于批处理和精确查询 |
全文搜索 | 强,适合结合地理数据的全文搜索 | 不支持 |
地理空间功能 | 丰富,但不如 PostGIS 详细 | 非常丰富,支持复杂地理空间分析 |
精确性 | 较高,但不如 PostGIS | 非常高,适用于精确地理计算 |
复杂查询 | 较弱,适合简单和聚合查询 | 强,支持复杂的多表联接和子查询 |
标准化支持 | 部分支持 OGC 标准 | 完全支持 OGC 标准 |
集成生态系统 | Elastic Stack(Kibana、Logstash) | PostgreSQL 及其扩展 |
选择建议
- Elasticsearch 适合需要实时搜索、分析以及处理大规模数据的应用,如本地服务发现、导航、社交媒体分析等。
- PostGIS 适合需要精确地理空间分析、复杂查询和事务处理的应用,如 GIS 系统、城市规划、环境监测等。
根据具体的应用需求和数据处理要求,选择合适的工具可以更好地满足业务需求。
ES空间数据类型
ES支持的坐标系是WGS84经纬度坐标(EPSG:4326)。主要原因如下:
- 全球标准:WGS 84 是全球标准的地理坐标系,被广泛应用于全球定位系统(GPS)和许多地理信息系统(GIS)。
- 简单性:通过支持一个主要的坐标系,简化了 Elasticsearch 的内部实现和优化。
- 兼容性:大多数地理空间数据和服务默认使用 WGS 84 坐标系,这使得与其他系统和服务的集成更加顺畅。
1. 地理空间字段类型
Elasticsearch 支持多种地理空间字段类型,主要包括:
- geo_point:用于表示单个地理点(经度和纬度)。
- geo_shape:用于表示复杂的地理形状,如多边形、线、多点、组合形状等。
geo_point
geo_point
字段类型用于表示单个地理点(即经度和纬度)。这是最常用的地理空间字段类型之一,适用于存储和查询具体的地理位置。
示例
定义一个包含 geo_point
字段的索引:
PUT /my_index
{
"mappings": {
"properties": {
"location": {
"type": "geo_point"
}
}
}
}
索引一个地理点:
PUT /my_index/_doc/1
{
"location": {
"lat": 40.73,
"lon": -74.1
}
}
支持的格式
- 字符串格式:例如
"40.73,-74.1"
- 对象格式:例如
{"lat": 40.73, "lon": -74.1}
- 数组格式:例如
[ -74.1, 40.73 ]
(注意顺序:经度,纬度)
geo_shape
geo_shape
字段类型用于表示复杂的地理形状,例如多边形、线、多点、组合形状等。这种类型适用于需要存储和查询复杂地理区域的场景。
示例
定义一个包含 geo_shape
字段的索引:
PUT /my_index
{
"mappings": {
"properties": {
"location": {
"type": "geo_shape"
}
}
}
}
索引一个多边形:
PUT /my_index/_doc/1
{
"location": {
"type": "polygon",
"coordinates": [
[
[-77.03653, 38.897676],
[-77.009051, 38.889939],
[-77.020945, 38.881091],
[-77.03653, 38.897676]
]
]
}
}
支持的几何形状类型
- point:单个点。
- linestring:线,由一系列点连接而成。
- polygon:多边形,由一系列点构成,形成闭合区域。
- multipoint:多个点。
- multilinestring:多条线。
- multipolygon:多个多边形。
- geometrycollection:几何集合,可以包含上述任意组合。
3. 地理空间查询
Elasticsearch 提供多种地理空间查询,用于查找和过滤地理空间数据,包括:
- geo_bounding_box:查找位于指定的矩形边界框内的地理点。
- geo_distance:查找距离某个地理点一定范围内的地理点。
- geo_polygon:查找位于指定多边形内的地理点。
- geo_shape:基于形状的查询,支持查找与指定形状相交、包含或被包含的地理形状。
geo_bounding_box
查找位于指定的矩形边界框内的地理点。
GET /my_index/_search
{
"query": {
"geo_bounding_box": {
"location": {
"top_left": {
"lat": 40.73,
"lon": -74.1
},
"bottom_right": {
"lat": 40.01,
"lon": -71.12
}
}
}
}
}
geo_distance
查找距离某个地理点一定范围内的地理点。
GET /my_index/_search
{
"query": {
"geo_distance": {
"distance": "200km",
"location": {
"lat": 40.73,
"lon": -74.1
}
}
}
}
geo_polygon
查找位于指定多边形内的地理点。
GET /my_index/_search
{
"query": {
"geo_polygon": {
"location": {
"points": [
{"lat": 40.73, "lon": -74.1},
{"lat": 40.01, "lon": -71.12},
{"lat": 40.10, "lon": -72.12}
]
}
}
}
}
geo_shape 查询
上面的查询主要针对的是geo_point类型进行的查询。下面介绍geo_shape的查询(基于形状的查询),支持查找与指定形状相交、包含或被包含的地理形状。
常见的关系包括:
- intersects:查找与查询形状相交的索引形状
- disjoint:查找与查询形状不相交的索引形状
- within:查找被查询形状包含的索引形状
- contains:查找包含查询形状的索引形状
查询相交的形状
查找与查询形状相交的索引形状:
GET /my_index/_search
{
"query": {
"geo_shape": {
"location": {
"shape": {
"type": "polygon",
"coordinates": [
[
[-77.05, 38.90],
[-77.00, 38.88],
[-77.04, 38.85],
[-77.05, 38.90]
]
]
},
"relation": "intersects"
}
}
}
}
查询包含的形状
查找包含查询形状的索引形状:
GET /my_index/_search
{
"query": {
"geo_shape": {
"location": {
"shape": {
"type": "point",
"coordinates": [-77.03653, 38.897676]
},
"relation": "contains"
}
}
}
}
查询被包含的形状
查找被查询形状包含的索引形状:
GET /my_index/_search
{
"query": {
"geo_shape": {
"location": {
"shape": {
"type": "envelope",
"coordinates" : [[-77.05, 38.90], [-77.00, 38.85]]
},
"relation": "within"
}
}
}
}
查询不相交的形状
查找被查询形状不想交的索引形状:
GET /my_index/_search
{
"query": {
"geo_shape": {
"location": {
"shape": {
"type": "polygon",
"coordinates": [
[
[-77.05, 38.90],
[-77.04, 38.88],
[-77.03, 38.87],
[-77.02, 38.86],
[-77.05, 38.90]
]
]
},
"relation": "disjoint"
}
}
}
}
组合查询
在 Elasticsearch 中,可以通过组合不同的地理空间查询来实现更复杂的查询需求。虽然 Elasticsearch 不直接支持在单个 geo_shape
查询中组合多个关系,但可以使用布尔查询 (bool query
) 来组合多个地理空间查询。这种方法允许你在一个查询中使用多个 geo_shape
查询,每个查询可以使用不同的关系。
我们想组合以下查询
- 查找与查询形状相交的形状
- 查找与查询形状不相交的形状
- 查找被查询形状包含的形状
- 查找包含查询形状的形状
可以使用布尔查询来实现:
GET /my_index/_search
{
"query": {
"bool": {
"must": [
{
"geo_shape": {
"location": {
"shape": {
"type": "polygon",
"coordinates": [
[
[-77.05, 38.90],
[-77.04, 38.88],
[-77.03, 38.87],
[-77.02, 38.86],
[-77.05, 38.90]
]
]
},
"relation": "intersects"
}
}
},
{
"geo_shape": {
"location": {
"shape": {
"type": "polygon",
"coordinates": [
[
[-77.06, 38.91],
[-77.05, 38.89],
[-77.04, 38.88],
[-77.06, 38.91]
]
]
},
"relation": "disjoint"
}
}
}
],
"should": [
{
"geo_shape": {
"location": {
"shape": {
"type": "polygon",
"coordinates": [
[
[-77.07, 38.92],
[-77.06, 38.90],
[-77.05, 38.89],
[-77.07, 38.92]
]
]
},
"relation": "within"
}
}
},
{
"geo_shape": {
"location": {
"shape": {
"type": "polygon",
"coordinates": [
[
[-77.08, 38.93],
[-77.07, 38.91],
[-77.06, 38.90],
[-77.08, 38.93]
]
]
},
"relation": "contains"
}
}
}
],
"minimum_should_match": 1
}
}
}
解释
bool
查询:允许组合多个查询条件。must
子句:必须满足的条件。这里包括两个geo_shape
查询,一个使用intersects
关系,一个使用disjoint
关系。should
子句:可选条件。如果至少有一个should
子句满足,则文档符合查询条件。这里包括两个geo_shape
查询,一个使用within
关系,一个使用contains
关系。minimum_should_match
:设置should
子句中至少有一个条件必须满足。
结果
这个组合查询将查找满足以下条件的文档:
- 必须与第一个查询形状相交
- 必须与第二个查询形状不相交
- 以及/或者:
- 被第三个查询形状包含
- 包含第四个查询形状
通过这种方式,可以组合多个地理空间查询来实现复杂的查询需求。使用布尔查询,可以灵活地构建各种组合条件,从而满足不同的业务需求。
4. 地理空间聚合
Elasticsearch 提供了一些地理空间聚合功能,可以对地理空间数据进行分析和汇总,包括:
- geo_distance 聚合:按距离分桶,将地理点按与指定点的距离进行分组。
- geohash_grid 聚合:按 geohash 网格分桶,将地理点按地理区域分组。
- geo_centroid 聚合:计算地理点的中心点(质心)。
geo_distance 聚合
geo_distance 聚合按距离分桶,将地理点按与指定点的距离进行分组。它可以帮助分析某个地点周围的分布情况。
解释
- field:指定地理字段。
- origin:指定中心点(经纬度)。
- ranges:定义距离范围(分桶)。
示例
GET /locations/_search
{
"size": 0,
"aggs": {
"by_distance": {
"geo_distance": {
"field": "location",
"origin": "40.73, -74.1",
"unit": "km",
"ranges": [
{ "to": 10 },
{ "from": 10, "to": 20 },
{ "from": 20 }
]
}
}
}
}
结果解释
结果会按指定距离范围分桶,比如 0-10 公里,10-20 公里,以及 20 公里以上的地理点的数量。
geohash_grid 聚合
geohash_grid 聚合按 geohash 网格分桶,将地理点按地理区域分组。geohash 是一种将经纬度编码为字符串的方法,每个字符串表示一个区域。
解释
- field:指定地理字段。
- precision:指定 geohash 精度(长度越大,网格越小)。
示例
执行 geohash_grid
聚合查询:
GET /locations/_search
{
"size": 0,
"aggs": {
"by_geohash": {
"geohash_grid": {
"field": "location",
"precision": 5
}
}
}
}
结果解释
结果会按 geohash 网格分桶,每个桶包含在该网格区域内的地理点数量。precision
参数控制网格的大小,值越大,网格越小。
geo_centroid 聚合
geo_centroid 聚合计算地理点的中心点(质心),即所有点的平均位置。
解释
- field:指定地理字段。
示例
假设我们有一个相同的 locations
索引。
执行 geo_centroid
聚合查询:
GET /locations/_search
{
"size": 0,
"aggs": {
"centroid": {
"geo_centroid": {
"field": "location"
}
}
}
}
结果解释
结果会包含所有地理点的中心点的经纬度值。
组合查询
我们可以组合多个聚合查询来进行复杂的地理空间分析。假设我们需要按距离分桶,然后在每个桶内计算中心点。
GET /locations/_search
{
"size": 0,
"aggs": {
"by_distance": {
"geo_distance": {
"field": "location",
"origin": "40.73, -74.1",
"unit": "km",
"ranges": [
{ "to": 10 },
{ "from": 10, "to": 20 },
{ "from": 20 }
]
},
"aggs": {
"centroid": {
"geo_centroid": {
"field": "location"
}
}
}
}
}
}
结果解释
结果会按距离分桶,每个桶内再计算该桶内地理点的中心点。这样可以得到不同距离范围内地理点的分布情况和每个范围内的中心点。
通过这些聚合功能,Elasticsearch 可以进行强大的地理空间数据分析,适用于各种需要地理数据分析和可视化的应用场景。
5. 地理空间索引配置
为了优化地理空间查询的性能,可以在创建索引时配置地理空间字段的属性。例如,可以设置 precision
参数来控制 geo_shape
字段的精度。
geo_point 字段的配置
示例
假设我们有一个包含地理点数据的索引 locations
,我们可以配置 geo_point
字段来优化查询性能。
PUT /locations
{
"mappings": {
"properties": {
"location": {
"type": "geo_point"
}
}
}
}
对于 geo_point
字段,配置选项相对简单,主要是字段类型的声明。
geo_shape 字段的配置
示例
假设我们有一个包含复杂地理形状数据的索引 regions
,我们可以配置 geo_shape
字段来优化查询性能。
PUT /regions
{
"mappings": {
"properties": {
"boundary": {
"type": "geo_shape",
"tree": "quadtree",
"precision": "1m",
"tree_levels": 26,
"distance_error_pct": 0.025
}
}
}
}
参数解释
- type:字段类型,这里为
geo_shape
。 - tree:用于索引地理形状的树结构类型。常用的有
quadtree
和geohash
。 - precision:控制索引的精度。它决定了树的每个节点代表的地理区域的大小。值可以是距离单位(如
1m
表示 1 米,1km
表示 1 公里)或地理精度(如1
表示 1 个地理单元)。 - tree_levels:指定树的最大层级数。更多的层级数会带来更高的精度,但也会增加索引的大小和复杂性。
- distance_error_pct:允许的最大距离误差百分比。较低的误差会提高精度,但也会增加索引的复杂度和大小。
参数详细解释
tree(树结构类型)
- quadtree:一种将空间递归细分为四个象限的树结构。适用于大多数地理形状的索引。
- geohash:使用 Geohash 算法将地理空间编码为字符串。适用于简单的地理点和矩形区域。
选择树结构类型取决于数据的特点和查询需求。quadtree
通常适用于需要高精度的复杂形状,geohash
则适用于较简单的形状和点。
precision(精度)
- 精度越高,索引越精细,查询的精确度也越高,但索引的大小和生成时间也会增加。
- 精度的值可以是具体的距离单位,例如
1m
(1 米)、10m
(10 米)等,或地理精度的具体值。
示例:精度设置为 1m
表示每个树节点代表的地理区域的大小为 1 米。
tree_levels(树层级数)
- 控制树的最大层级数,更多的层级数意味着更高的索引精度,但也会增加索引的大小和复杂性。
- 通常,树层级数与精度参数一起使用,以平衡索引的精度和大小。
示例:设置 tree_levels
为 26,表示树的最大层级数为 26 级。
distance_error_pct(距离误差百分比)
- 控制允许的最大距离误差百分比。较低的误差百分比意味着更高的索引精度,但也会增加索引的复杂性和大小。
- 这个参数帮助平衡查询精度和索引大小。
示例:设置 distance_error_pct
为 0.025,表示允许最大 2.5% 的距离误差。
综合示例
假设我们有一个复杂的地理形状数据,包含城市边界,我们希望在查询时能获得高精度的结果,同时考虑索引大小和性能。可以这样配置索引:
PUT /city_boundaries
{
"mappings": {
"properties": {
"boundary": {
"type": "geo_shape",
"tree": "quadtree",
"precision": "10m",
"tree_levels": 15,
"distance_error_pct": 0.01
}
}
}
}
在这个示例中:
- 使用
quadtree
作为树结构类型,以获得更高的精度。 - 设置
precision
为10m
,表示每个节点代表的区域大小为 10 米。 - 设置
tree_levels
为 15,限制树的最大层级数。 - 设置
distance_error_pct
为 0.01,允许最大 1% 的距离误差。
通过合理配置地理空间字段的属性,可以显著优化地理空间查询的性能,满足不同应用场景的需求。