これはMIERUNE Advent Calendar 2023の18日目の記事です。昨日は、@Takayuki_Kawajiri さんによるCesiumでカメラ移動毎に標高値取得したら、とてつもなく重かった話でした。
先日、こんな記事を書きました!
今すぐに!FastAPIとOpenSearchで開発を始めるためのテンプレート!
このテンプレートを利用するとFastAPIですぐに開発を始めることができますが、OpenSearchの実行環境としても活用できるので、今回は適当なデータを投入して検索・地図上で確認してみましょう!
データ投入
今回は「全国の道の駅」のデータを投入してみましょう!
データは国土数値情報というところから入手できます。
https://nlftp.mlit.go.jp/ksj/gml/datalist/KsjTmplt-P35.html
上記URLの最下部にダウンロード用のボタンがあるので、そこからzipファイルをダウンロードしましょう。
作業環境構築
今すぐに!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
をクリックします。
次のように遷移するので、Create index pattern
をクリック
今回作ったroad_station
インデックスを指定し、Next step
をクリックし次の画面ではそのままCreate index pattern
もクリック
再度サイドバーからMaps
をクリックすると、先ほどとは別の画面になっているはずなので、Create map
をクリック
すると地図画面が出てきましたね!
Add layer
をクリックし、Documents
を選択しましょう。
以下のように設定し、Update
を押すと…
道の駅が出てきました!結構多いですね!
ラベルなどを表示したい場合は、レイヤーを選んでStyleなどを変更することで可視化できます。
地図上で矩形を描いて…
その範囲外のデータをフィルタリングする、なども可能です!
終わりに
超便利ですね!
Elasticsearch同様、OpenSearchでも地図上に位置情報を可視化させることが可能です!
kibanaとOpenSearch Dachboardsではちょっと使い勝手が異なったりしますが、基本はほぼ変わらず利用できるかと思いますし、AWSは最近、地理空間情報にも力を入れているので、強力なアップデートが突如きたりするかも!?
すでに便利ですが、もっと便利に使えると嬉しいですね!