🦔

GeoPolygon検索したくない?

2023/02/10に公開

概要

スペースマーケットにてバックエンドエンジニアをしているYusukeです。
今日はGeoJsonのポリゴンを使った検索について書きたいと思います。

スペースマーケットではスペースを貸し借りできるスペースシェアのプラットフォーム運営をしております。
各スペースの位置情報はgeocodeと住所の両方で管理しており、市区町村検索では住所情報を使った検索を行っていました。
今回はgeocodeを使った市区町村検索を試してみたという内容の記事となっております。

Elasticsearchのindex作成、データ挿入、GeoPolygon検索、取得結果確認するところまでを目指したいと思います。
この記事の対象としては、最近Elasticsearchを触りはじめた人、geocodeを使った検索してみたい人、などを想定しています。

まず、GeoJsonとかポリゴンってなんぞやという話なんですが、Wikipediaさんによると、
「JavaScript Object Notation(JSON)を用いて空間データをエンコードし非空間属性を関連付ける...」
はい、わかりませんね笑
緯度経度を使って、点・線・領域等の地理情報をJSON形式で表現したフォーマット。という理解で問題ないと思います。
今回はこちらのGeoJsonの領域データを使って位置情報検索を試してみようと思います。

まずは課題を整理しましょう。大きく下記の2点でしょうか?

  1. 市区町村のGeoJsonのポリゴンデータをどこから入手するか?
  2. ポリゴン境界の判定をどうやるか?

課題1について

まず、こちらがないとお話になりません。GeoJsonのポリゴンデータは下記から入手可能なようです。

国土交通省

https://nlftp.mlit.go.jp/ksj/gml/datalist/KsjTmplt-N03-v3_1.html

スマートニュースさん

https://github.com/smartnews-smri/japan-topography
候補としては国土交通省、スマートニュースさん(元データは国土交通省の模様。)のリポジトリあたりになりそうです。

課題2について

ポリゴンデータを手に入れて、対象ドキュメントが境界内に入っていることをどうやって判定するかです。
これはもうネタバレですが、Elasticsearchさんが頑張ってくれます。
Geo-polygon queryというものが用意されており、こちらを使うと実現できそうです。
https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-geo-polygon-query.html
スペースマーケットでも検索はElasticsearchを使用しているのでそのまま使えそうです。
ただ、7.12からGeoshape queryが推奨されているようですが、弊社では7.12以上へ移行できておらず、今回はGeo-polygon queryで試してみます。

では試してみましょう!

  1. Elasticsearchのindex作成
  2. ドキュメント登録
  3. GeoPolygonクエリ作成
  4. 取得結果

という流れでやってみましょう。
今回は実験として、スペース情報の代わりに新宿区、渋谷区、目黒区のスターバックスの位置情報を登録して、GeoPolygon検索をしてみます。

登録する情報としては、店名、住所、geocodeあたりでしょうか。
geocodeはgeo_point型としておきましょう。

index作成

PUT geo_polygon_test
{
  "mappings": {
    "properties": {
      "store_name": {
	"type": "text"
      },
      "address": {
        "type": "text"
      },
      "location": {
        "type": "geo_point"
      }      
    }
  }
}

こちらにGoogle Mapで調べた情報を、新宿区のスターバックス3店、渋谷区2店、目黒区1店を登録します。
geocode情報ですが、今回はWKT形式のポイント型としておきます。

ドキュメント登録

POST _bulk
{"index": {"_index": "geo_polygon_test"}}
{"store_name": "スターバックス コーヒー 新宿新南口店", "address": "東京都新宿区", "location": "POINT (139.70261979260934 35.689101523599426)"}
{"index": {"_index": "geo_polygon_test"}}
{"store_name": "スターバックス コーヒー ルミネエスト新宿店", "address": "東京都新宿区", "location": "POINT (139.70117162378475 35.69176191960187)"}
{"index": {"_index": "geo_polygon_test"}}
{"store_name": "スターバックス コーヒー 新宿西口店", "address": "東京都新宿区", "location": "POINT (139.69741889615864 35.690380546306415)"}
{"index": {"_index": "geo_polygon_test"}}
{"store_name": "スターバックス コーヒー 渋谷ストリーム店", "address": "東京都渋谷区", "location": "POINT (139.702273373072 35.658682657537625)"}
{"index": {"_index": "geo_polygon_test"}}
{"store_name": "スターバックス コーヒー 渋谷ヒカリエ ShinQs店", "address": "東京都渋谷区", "location": "POINT (139.70313500770553 35.65911347584828)"}
{"index": {"_index": "geo_polygon_test"}}
{"store_name": "スターバックス コーヒー 中目黒GTタワー店", "address": "東京都目黒区", "location": "POINT (139.6986550818626 35.64398641726896)"}

ではまず、新宿区内のスターバックスを検索してみましょう。
検索したい新宿区のGeoJsonポリゴンデータを拝借したいのですが、スマートニュースさんのリポジトリデータがいい感じに簡素化されているので、今回のテストではこちらを使用します。
(国土交通省からは地図xml形式のみと思いきや、ダウンロードデータ内にgeojsonも提供されておりました)

GeoPolygonクエリ作成

GET /geo_polygon_test/_search/
{
      "query": {
        "bool": {
            "filter" : {
                "geo_polygon" : {
                    "location" : {
                        "points" : [[139.74587044136786,35.702911054002016],[139.74484961102075,35.703620225193276],[139.74340379974558,35.70774272104978],[139.742807691322,35.70813244135911],[139.73585241276766,35.70918217128105],[139.73541600514454,35.7082235588702],[139.7324465507678,35.707393838055225],[139.73184461743517,35.70926605388536],[139.7259879121125,35.709209667292384],[139.72325902728232,35.71041410862426],[139.72250961142947,35.71226577494559],[139.71441236124156,35.71317550484571],[139.71211347555652,35.71476022540412],[139.7104209730495,35.71345913483128],[139.70734622604755,35.713853305885436],[139.70587678328695,35.71343941449743],[139.70456347646947,35.71587608090944],[139.70593878075022,35.71885077464361],[139.70486516199765,35.72043633337012],[139.7045996371636,35.723013559228434],[139.69981686085953,35.723546054107146],[139.69897294424607,35.72243217090801],[139.69493430657303,35.72182577513945],[139.6939379126046,35.72219077488438],[139.68664900095075,35.72272358632449],[139.68440871598955,35.722513865322696],[139.68272204918333,35.723644413858665],[139.68134985731012,35.727044387188755],[139.6773443065946,35.72937272117417],[139.67569202266873,35.725837468224654],[139.67518564230363,35.723706919438825],[139.67576450083106,35.72194941393434],[139.67367613474585,35.72026305379694],[139.67358444886327,35.71862027930251],[139.67641114126127,35.71966219785219],[139.6796911412798,35.71637361307418],[139.6798758370004,35.71418750474959],[139.6814502720125,35.71401305425911],[139.6816236064443,35.7122144416432],[139.68034219213848,35.71126389241476],[139.68278274982413,35.710122505249444],[139.68786250385153,35.71129083790191],[139.6900755771262,35.711483612978384],[139.6943311411693,35.711039999896116],[139.6930775231093,35.709631946657055],[139.68869053214246,35.70733722483311],[139.68758855956446,35.70449972078552],[139.68745102534456,35.70178419808235],[139.68841241230302,35.699331414113544],[139.68561208802748,35.69421394611675],[139.6839901175499,35.69199061337616],[139.68481844437514,35.69142338669698],[139.6832189488954,35.68871644081116],[139.6841883919808,35.68755699975577],[139.68565089467313,35.683787279159674],[139.68767308736244,35.681593694501466],[139.69502955916687,35.68664894619195],[139.70210294383497,35.689169467846824],[139.70271014265904,35.68777197279837],[139.70578016911952,35.68804808085429],[139.70711211363323,35.686149198326916],[139.7074076389514,35.684462802216615],[139.71140739263717,35.68336725169735],[139.7147895845717,35.68086225219122],[139.71290511008283,35.67728780229032],[139.71289425436646,35.67461835994942],[139.71485955902153,35.675078080788865],[139.71655841883603,35.673433081371684],[139.71947566756808,35.67449808102168],[139.71994844386563,35.67532419825318],[139.71958403407598,35.678310000506144],[139.7249332170491,35.67934527927807],[139.72674849509454,35.67928805428276],[139.72845632899646,35.682814171336986],[139.7314993901848,35.681495279583544],[139.73001469582164,35.68538413516973],[139.73165861245502,35.68962163992677],[139.73628364497426,35.69218997328869],[139.73886814544858,35.69469189171747],[139.74160483819276,35.698390775414],[139.74587044136786,35.702911054002016]]
                    }
                }
            }
        }
  }
}

上記クエリをいざ実行!
ご覧のように新宿区のスターバックス3件取得できました。これはなかなか良さそうです。

{
  ...
  "hits" : {
    "total" : {
      "value" : 3,
      "relation" : "eq"
    },
    "max_score" : 0.0,
    "hits" : [
      {
        "_index" : "geo_polygon_test",
        "_type" : "_doc",
        "_id" : "6d_GL4YBVW06FOGnG7_J",
        "_score" : 0.0,
        "_source" : {
          "store_name" : "スターバックス コーヒー 新宿新南口店",
          "address" : "東京都新宿区",
          "location" : "POINT (139.70261979260934 35.689101523599426)"
        }
      },
      {
        "_index" : "geo_polygon_test",
        "_type" : "_doc",
        "_id" : "6t_GL4YBVW06FOGnG7_J",
        "_score" : 0.0,
        "_source" : {
          "store_name" : "スターバックス コーヒー ルミネエスト新宿店",
          "address" : "東京都新宿区",
          "location" : "POINT (139.70117162378475 35.69176191960187)"
        }
      },
      {
        "_index" : "geo_polygon_test",
        "_type" : "_doc",
        "_id" : "69_GL4YBVW06FOGnG7_J",
        "_score" : 0.0,
        "_source" : {
          "store_name" : "スターバックス コーヒー 新宿西口店",
          "address" : "東京都新宿区",
          "location" : "POINT (139.69741889615864 35.690380546306415)"
        }
      }
    ]
  }
}

念の為、渋谷区で取得してみると、こちらも渋谷区のスターバックス2件取得できました!

{
  ...
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 0.0,
    "hits" : [
      {
        "_index" : "geo_polygon_test",
        "_type" : "_doc",
        "_id" : "lt_UL4YBVW06FOGnvOrT",
        "_score" : 0.0,
        "_source" : {
          "store_name" : "スターバックス コーヒー 渋谷ストリーム店",
          "address" : "東京都渋谷区",
          "location" : "POINT (139.702273373072 35.658682657537625)"
        }
      },
      {
        "_index" : "geo_polygon_test",
        "_type" : "_doc",
        "_id" : "l9_UL4YBVW06FOGnvOrT",
        "_score" : 0.0,
        "_source" : {
          "store_name" : "スターバックス コーヒー 渋谷ヒカリエ ShinQs店",
          "address" : "東京都渋谷区",
          "location" : "POINT (139.70313500770553 35.65911347584828)"
        }
      }
    ]
  }
}

最後に

スペースマーケットでは、一緒にサービスを成長させていく仲間を探しています。

ビジネスサイド、エンジニアメンバー共に話しやすいメンバーが多く非常に働きやすい環境だと思います!
ご興味ある方ぜひ見てみて下さい!

https://spacemarket.co.jp/recruit/engineer/
https://www.wantedly.com/projects/1113544
https://www.wantedly.com/projects/1113570
https://www.wantedly.com/projects/1061116

スペースマーケット Engineer Blog

Discussion