Elasticsearch:如何制作 GeoJSON 文件并进行地理位置搜索

Elastic 专栏收录该内容
494 篇文章 87 订阅

我发现我之前的文章 “Elasticsearch:使用 Elasticsearch 进行地理位置搜索” 还是蛮受欢迎的。我觉得大家喜欢是因为里面有一些图片把复制的问题简单化,一目了然。在使用 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",
        "_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" : {
          "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 被定义,那么在它之外的所有文档将被过滤掉。

  • 2
    点赞
  • 7
    评论
  • 3
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值