13
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

MIERUNEAdvent Calendar 2023

Day 18

OpenSearchに「全国の道の駅」の位置情報を突っ込んでサクッと可視化する

Last updated at Posted at 2023-12-17

先日、こんな記事を書きました!
今すぐに!FastAPIとOpenSearchで開発を始めるためのテンプレート!

このテンプレートを利用するとFastAPIですぐに開発を始めることができますが、OpenSearchの実行環境としても活用できるので、今回は適当なデータを投入して検索・地図上で確認してみましょう!

image.png

データ投入

今回は「全国の道の駅」のデータを投入してみましょう!

データは国土数値情報というところから入手できます。
https://nlftp.mlit.go.jp/ksj/gml/datalist/KsjTmplt-P35.html

上記URLの最下部にダウンロード用のボタンがあるので、そこからzipファイルをダウンロードしましょう。

image.png

作業環境構築

今すぐに!FastAPIとOpenSearchで開発を始めるためのテンプレート!

に従ってテンプレートをダウンロードし、OpenSearchを実行できるようにしておきましょう!

さらに、api/src/dataフォルダを作成し、解凍したzipファイルの中身を入れましょう。
(dataフォルダにはgeojsonファイルだけ入っていればいいので、api/src/data/P35-18_Roadside_Station.geojsonのようなフォルダ構成になるようにしましょう。)

データ投入プログラムを書く

Elasticsearch用のPythonライブラリの情報はたくさんありますが、OpenSearchだと微妙に書き方が違ったりするので注意しましょう!
また、OpenSearchはhttpsで通信します。その辺りも微妙に注意しましょう。

まずはファイルを作成し…

mkdir api/src/utils
touch api/src/utils/register.py

プログラムを書いていきます。

import ujson as json
from opensearchpy import OpenSearch
from opensearchpy.helpers import bulk, parallel_bulk

opensearch_url = "https://localhost:9200"
opensearch_master_user = "admin"
opensearch_master_password = "admin"

auth = (opensearch_master_user, opensearch_master_password)
opensearch = OpenSearch(
    opensearch_url,
    http_auth=auth,
    use_ssl=True,
    verify_certs=False,
    ssl_assert_hostname=False,
    ssl_show_warn=False,
)

index_name = "road_station"

if opensearch.indices.exists(index=index_name):
    opensearch.indices.delete(index=index_name)

res = opensearch.indices.create(
    index=index_name,
    body={
        "settings": {
            "number_of_shards": 3,
            "number_of_replicas": 1,
        },
        "mappings": {
            "dynamic": "strict",
            "_source": {"excludes": ["geometry"]},
            "properties": {
                "P35_001": {"type": "double"},
                "P35_002": {"type": "double"},
                "P35_003": {"type": "text"},
                "P35_004": {"type": "text"},
                "P35_005": {"type": "keyword"},
                "P35_006": {"type": "text"},
                "P35_007": {"type": "text"},
                "P35_008": {"type": "text"},
                "P35_009": {"type": "text"},
                "P35_010": {"type": "text"},
                "P35_011": {"type": "integer"},
                "P35_012": {"type": "integer"},
                "P35_013": {"type": "integer"},
                "P35_014": {"type": "integer"},
                "P35_015": {"type": "integer"},
                "P35_016": {"type": "integer"},
                "P35_017": {"type": "integer"},
                "P35_018": {"type": "integer"},
                "P35_019": {"type": "integer"},
                "P35_020": {"type": "integer"},
                "P35_021": {"type": "integer"},
                "P35_022": {"type": "integer"},
                "P35_023": {"type": "integer"},
                "P35_024": {"type": "integer"},
                "P35_025": {"type": "integer"},
                "P35_026": {"type": "integer"},
                "P35_027": {"type": "integer"},
                "P35_028": {"type": "integer"},
                "location": {"type": "geo_point"},
            },
        },
    },
)


def iter_docs():
    filepath = "src/data/P35-18_Roadside_Station.geojson"
    with open(
        filepath,
        encoding="utf-8",
    ) as f:
        data = json.load(f)
        features = data["features"]

        for d in features:
            geom = d["geometry"]["coordinates"]
            d["properties"]["location"] = [geom[0], geom[1]]

            yield {
                "_index": index_name,
                "_id": f"{d['properties']['P35_001'],d['properties']['P35_002']}",
                "_source": d["properties"],
            }


for _res in parallel_bulk(
    opensearch,
    iter_docs(),
    index=index_name,
    chunk_size=100,
    raise_on_error=True,
    request_timeout=60,
):
    print(res)

以下のように実行します。

cd api
poetry run python ./src/utils/register.py

うまく実行されるとこんな感じになると思います。

...
{'acknowledged': True, 'shards_acknowledged': True, 'index': 'road_station'}
{'acknowledged': True, 'shards_acknowledged': True, 'index': 'road_station'}

データを見てみる

OpenSearch dashboardでデータを見てみましょう!

http://localhost:5601/に接続してください。

サイドメニューを開きMapをクリックします。

image.png

次のように遷移するので、Create index patternをクリック

image.png

今回作ったroad_stationインデックスを指定し、Next stepをクリックし次の画面ではそのままCreate index patternもクリック

image.png

再度サイドバーからMapsをクリックすると、先ほどとは別の画面になっているはずなので、Create mapをクリック

image.png

すると地図画面が出てきましたね!

image.png

Add layerをクリックし、Documentsを選択しましょう。

image.png

以下のように設定し、Updateを押すと…

image.png

道の駅が出てきました!結構多いですね!

image.png

ラベルなどを表示したい場合は、レイヤーを選んでStyleなどを変更することで可視化できます。

image.png

地図上で矩形を描いて…

image.png

その範囲外のデータをフィルタリングする、なども可能です!

image.png

終わりに

超便利ですね!

Elasticsearch同様、OpenSearchでも地図上に位置情報を可視化させることが可能です!

kibanaとOpenSearch Dachboardsではちょっと使い勝手が異なったりしますが、基本はほぼ変わらず利用できるかと思いますし、AWSは最近、地理空間情報にも力を入れているので、強力なアップデートが突如きたりするかも!?

すでに便利ですが、もっと便利に使えると嬉しいですね!

13
1
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
13
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?