6
3

More than 1 year has passed since last update.

[Python] Elasticsearchを用いた位置情報検索

Last updated at Posted at 2022-02-24

はじめに

Elasticsearchは緯度経度を用いた位置情報検索ができます。
今回は山手線の緯度経度データをElasticsearchに投入し、位置情報を用いた範囲検索を実装してみます。

インデックスの作成

  • 今回は山手線の駅名と緯度経度情報を持つデータを投入します。
  • geo_pointタイプはdynamic mappingされず、事前に定義する必要があるので注意です。
from elasticsearch import Elasticsearch

# Elasticsearchインスタンスの作成
es = Elasticsearch(
    "elasticsearchのURL",
    http_auth=("user_id", "password")
)

# マッピング定義
mapping = {
    "mappings": {
        "properties": {
            "name": {
                "type": "text"
            },
            "location": {
                "type": "geo_point"
            },
        }
    }
}

# インデックスの作成
es.indices.create(index="yamanote_station", body=mapping)

テストデータのバルクインサート

  • 作成したインデックスにバルクインサートを用いて山手線のデータを投入します。
from elasticsearch import helpers

def bulk_insert(index, geo_location_dict):
    yamanote_info_list = [{"name": station, "location": geo_location} for station, geo_location in geo_location_dict.items()]

    # bulkで扱えるデータ構造に変換します
    for yamanote_info in yamanote_info_list:
        yield {
            "_op_type": "create",
            "_index": index,
            "_source": yamanote_info
        }

yamanote_station_dict = {
 '上野駅': {'lat': 35.7141672, 'lon': 139.7774091},
 '五反田駅': {'lat': 35.6261591, 'lon': 139.7236022},
 '代々木駅': {'lat': 35.683033, 'lon': 139.7020555},
 '原宿駅': {'lat': 35.6702285, 'lon': 139.7026976},
 '品川駅': {'lat': 35.6284713, 'lon': 139.7387597},
 '大塚駅': {'lat': 35.7318309, 'lon': 139.7281112},
 '大崎駅': {'lat': 35.6198513, 'lon': 139.7281892},
 '巣鴨駅': {'lat': 35.7334192, 'lon': 139.7392848},
 '御徒町駅': {'lat': 35.7075185, 'lon': 139.7748564},
 '恵比寿駅': {'lat': 35.6467139, 'lon': 139.7100777},
 '新大久保駅': {'lat': 35.7012459, 'lon': 139.7002258},
 '新宿駅': {'lat': 35.6896067, 'lon': 139.7005713},
 '新橋駅': {'lat': 35.666379, 'lon': 139.7583398},
 '日暮里駅': {'lat': 35.7281578, 'lon': 139.7706414},
 '有楽町駅': {'lat': 35.6749187, 'lon': 139.7628199},
 '東京駅': {'lat': 35.68123620000001, 'lon': 139.7671248},
 '池袋駅': {'lat': 35.7295028, 'lon': 139.7109001},
 '浜松町駅': {'lat': 35.6553809, 'lon': 139.7571289},
 '渋谷駅': {'lat': 35.6580339, 'lon': 139.7016358},
 '田町駅': {'lat': 35.6457361, 'lon': 139.7475624},
 '田端駅': {'lat': 35.7381581, 'lon': 139.7608154},
 '目白駅': {'lat': 35.7212199, 'lon': 139.7066115},
 '目黒駅': {'lat': 35.6340929, 'lon': 139.7158331},
 '神田駅': {'lat': 35.6918216, 'lon': 139.7709318},
 '秋葉原駅': {'lat': 35.698383, 'lon': 139.7730717},
 '西日暮里駅': {'lat': 35.73200569999999, 'lon': 139.7668856},
 '駒込駅': {'lat': 35.7365665, 'lon': 139.7470098},
 '高田馬場駅': {'lat': 35.7125654, 'lon': 139.7038615},
 '高輪ゲートウェイ駅': {'lat': 35.6355207, 'lon': 139.7406809},
 '鶯谷駅': {'lat': 35.7214573, 'lon': 139.7780133}
 }

helpers.bulk(es, bulk_insert("yamanote_station", yamanote_station_dict))

位置情報検索

  • 今回は東京駅から半径3km以内に存在する山手線の駅を検索してみます
  • 円形範囲検索はgeo_distanceクエリを使用します。(その他の位置情報検索は公式を参照してみてください)
query = {
  "query": {
    "geo_distance": {
      "distance": "3km",
      "location": {
        "lat": yamanote_station_dict['東京駅']['lat'],
        "lon": yamanote_station_dict['東京駅']['lon']
      }
    }
  }
}

res = es.search(index="yamanote_station", body=query, size=10)
  • 検索結果
    • 新橋・有楽町・神田・秋葉原と東京駅から3km以内の駅がしっかり検索できていますね。
{'hits': [{'_id': 'JeizK38BB0c_3d13lkrt',
   '_index': 'yamanote_station',
   '_score': 1.0,
   '_source': {'location': {'lat': 35.666379, 'lon': 139.7583398},
    'name': '新橋駅'},
   '_type': '_doc'},
  {'_id': 'J-izK38BB0c_3d13lkrt',
   '_index': 'yamanote_station',
   '_score': 1.0,
   '_source': {'location': {'lat': 35.6749187, 'lon': 139.7628199},
    'name': '有楽町駅'},
   '_type': '_doc'},
  {'_id': 'KOizK38BB0c_3d13lkrt',
   '_index': 'yamanote_station',
   '_score': 1.0,
   '_source': {'location': {'lat': 35.68123620000001, 'lon': 139.7671248},
    'name': '東京駅'},
   '_type': '_doc'},
  {'_id': 'MOizK38BB0c_3d13lkrt',
   '_index': 'yamanote_station',
   '_score': 1.0,
   '_source': {'location': {'lat': 35.6918216, 'lon': 139.7709318},
    'name': '神田駅'},
   '_type': '_doc'},
  {'_id': 'MeizK38BB0c_3d13lkrt',
   '_index': 'yamanote_station',
   '_score': 1.0,
   '_source': {'location': {'lat': 35.698383, 'lon': 139.7730717},
    'name': '秋葉原駅'},
   '_type': '_doc'}],
 'max_score': 1.0,
 'total': {'relation': 'eq', 'value': 5}}

参考記事

6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3