Elasticsearch:如何制作 GeoJSON 文件并进行地理位置搜索
2021年4月16日 | by mebius
我发现我之前的文章 “Elasticsearch:使用 Elasticsearch 进行地理位置搜索” 还是蛮受欢迎的。我觉得大家喜tgcode欢是因为里面有一些图片把复制的问题简单化,一目了然。在使用 Geo Search 进行讲解时,如果能在地图上清楚地展示各个文档,边界,那么一切问题就变得非常简单了。在今天的文章中,我来讲述如何使用 GeoJSON 来创建一些边界。这对于展示位置搜索非常有用。我们将讲述 Geo-shape 已经 Geo-bounding box 的搜索。
GeoJSON 文件格式
我们首先来看一下一个简单的 GeoJSON 文件格式:
sample.json
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-101.81536,
42.54837
],
[
-99.84026,
43.15224
],
[
-97.18318,
41.95177
],
[
-98.96342,
41.01625
],
[
-102.09878,
41.54207
],
[
-101.81536,
42.54837
]
]
]
}
}
]
}
在上面,它描述的是一个 Polygon 类型的 geomerty。这里的地理位置:
[ -101.81536, 42.54837 ], [ -99.84026, 43.15224 ], [ -97.18318, 41.95177 ], [ -98.96342, 41.01625 ], [ -102.09878, 41.54207 ], [ -101.81536, 42.54837 ]
是一个以 [精度,纬度] 组合的数组。你们可能已经注意到第一个位置和最后的一个位置的值是一样的,也就是一个闭环。这样就形成了一个边界。上面的位置信息虽然有了,但是我们没法知道它在地图上是什么样子的。在下面,我们将通过 Elastic Maps 来进行展示。这样大家看的一目了然!
在 Elastic Maps 上展示 GeoJSON
为了能启动 Elastic Maps,我们必须有一个索引模式供使用。我们在 Kibana 中打入如下的命令:
PUT my_locations
{
"mappings": {
"properties": {
"location": {
"type": "geo_point"
}
}
}
}
我们使用如下的方法来创建几个文档:
POST my_locations/_bulk
{ "index" : { "_id" : "1" } }
{ "location" : [ -100.41435, 42.34239 ] }
{ "index" : { "_id" : "2" } }
{ "location" : [ -99.25813, 42.13875 ] }
{ "index" : { "_id" : "3" } }
{ "location" : [ -99.74299, 41.84769 ] }
{ "index" : { "_id" : "4" } }
{ "location" : [ -100.65678, 41.92405 ] }
大家也许不知道这几个文档在 Maps 的什么位置。我们首先为 my_locations 来创建一个索引模式:
然后打开 Maps 应用:
从地图上,我们可以看到 my_locations 的四个文档的位置已经被清楚地显示出来了。
接下来,我们想搜索一个边界,它是由一个 shape 来定义的。这个 shape 其实我们可以通过 GeoJSON 的形式来定义。在文章的一开始,我们已经定义了一个 GeoJSON 的文档 sample.json。 我们接下来加载这个文件:
我们甚至可以移动上面的两个层的上下关系,让 my_locations 的图层位于上面:
这样我们可以清楚地看到四个文档被包含在上面所述的 Polygon 所定义的边界里。
Geo-shape query
那么我们使用 geo_shape 所做的如下的搜索就不难理解了:
GET my_locations/_search
{
"query": {
"geo_shape": {
"location": {
"shape": {
"type": "polygon",
"coordinates": [
[
[
-101.81536,
42.54837
],
[
-99.84026,
43.15224
],
[
-97.18318,
41.95177
],
[
-98.96342,
41.01625
],
[
-102.09878,
41.54207
],
[
-101.81536,
42.54837
]
]
]
}
}
}
}
}
请注意在上面的 coordinates 里定义的其实就是我们之前在 GeoJSON 里定义的 Polygon。上面的返回结果为 my_locations 的所有四个文档。
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 4,
"relation" : "eq"
},
"max_score" : 0.0,
"hits" : [
{
"_index" : "my_locations",
"_type" : "_doc",
"_id" : "1",
tgcode "_score" : 0.0,
"_source" : {
"location" : [
-100.41435,
42.34239
]
}
},
{
"_index" : "my_locations",
"_type" : "_doc",
"_id" : "2",
"_score" : 0.0,
"_source" : {
"location" : [
-99.25813,
42.13875
]
}
},
{
"_index" : "my_locations",
"_type" : "_doc",
"_id" : "3",
"_score" : 0.0,
"_source" : {
"location" : [
-99.74299,
41.84769
]
}
},
{
"_index" : "my_locations",
"_type" : "_doc",
"_id" : "4",
"_score" : 0.0,
"_source"tgcode : {
"location" : [
-100.65678,
41.92405
]
}
}
]
}
}
Geo-bounding box query
如法炮制,我们也可以在之前的 GeoJSON 文档中定义一个长方形。我们重新定义 simple.json 文件:
simple.json
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-101.81536,
42.54837
],
[
-99.84026,
43.15224
],
[
-97.18318,
41.95177
],
[
-98.96342,
41.01625
],
[
-102.09878,
41.54207
],
[
-101.81536,
42.54837
]
]
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-99.97271,
42.47435
],
[
-99.97271,
41.59785
],
[
-98.57871,
41.59785
],
[
-98.57871,
42.47435
],
[
-99.97271,
42.47435
]
]
]
}
}
]
}
这次我们添加了一个长方形的边界。重新加载我们的 GeoJSON 文档:
从上面我们可以看出来两个边界:一个是之前的 Polygon,另外一个是我们刚刚增加的 Rectangle。从上面我们可以清楚地看到有两个文档在 Rectangle 里。我们可以使用如下的方式来进行查询:
GET my_locations/_search
{
"query": {
"bool": {
"must": {
"match_all": {}
},
"filter": {
"geo_bounding_box": {
"location": {
"top_left": {
"lat": 42.47435,
"lon": -99.97271
},
"bottom_right": {
"lat": 41.59785,
"lon": -98.57871
}
}
}
}
}
}
}
在上面的 top_left 以及 bottom_right 里的坐标信息其实就是我们刚才在 GeoJSON 文件 simple.json 中定义的位置。
上面的返回结果是:
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "my_locations",
"_type" : "_doc",
"_id" : "2",
"_score" : 1.0,
"_source" : {
"location" : [
-99.25813,
42.13875
]
}
},
{
"_index" : "my_locations",
"_type" : "_doc",
"_id" : "3",
"_score" : 1.0,
"_source" : {
"location" : [
-99.74299,
41.84769
]
}
}
]
}
}
从上面,我们可以看出来有两个被搜索到的文档。
如何定制 filter
我们可以在地图上采用如下的步骤来创建一个 filter:
在上面,我们可以看到一个被创建的 filter。我们可以点击上面的 filter,并点击 Edit:
在上面,我们可以清楚得看到所有的坐标信息。这些坐标可以被用于我们的 GeoJSON 的边界定义中去。一旦这个 filter 被定义,那么在它之外的所有文档将被过滤掉。
文章来源于互联网:Elasticsearch:如何制作 GeoJSON 文件并进行地理位置搜索
相关推荐: Beats:为 Beats => Logstash => Elasticsearch 架构创建 template 及 Dashboard
前一段时间有一个开发者私信我说自己的 Beats 连接到 Logstash,然后连接到 Elasticsearch。等数据在 Elasticsearch 中收集完后,发现 Kibana 中的 Dashboard 不能被使用。数据类型不匹配。这个到底是什么原因呢…