👌

Pythonと国土地理院APIで施設名から緯度経度を一括取得してCSV出力

2023/05/05に公開

はじめに

Google Maps APIやLeafletなどのマップ上にマーカーを表示する際、緯度経度が必要です。
しかし、施設名から緯度経度を調べることは手間がかかるため、本記事では国土地理院APIを使ってCSVファイルから一括で緯度経度を取得する方法を紹介します。

国土地理院APIについて

  • 無料で施設名から緯度経度を取得できるAPI
  • いつ使えなくなるかわからないので注意
  • 精度
    • 有名施設:かなり近い位置まで緯度経度を取得できる
    • あまり有名ではない施設:市町村まで・取得できない

国土地理院APIは情報の誤差が許容できないサービスには向きません。
有料ですがGoogleMapAPIを使う方がよいでしょう。
しかし、目安程度のマーカーとして緯度経度を取得したい場合、有力なAPIです。

コードはこちら
https://github.com/Nyamadamadamada/waste-watch/tree/main/python

ディレクトリ

.
├── Dockerfile
├── docker-compose.yml
└── src
    ├── export_csv.py
    ├── get_coordinate.py
    ├── input
    │   └── tower_prefecture.csv
    └── output
        ├── error.csv
        └── output.csv

Docker環境構築

Pythonスクリプトを実行するための環境をDockerで構築します。

Dockerfile

Dockerfile
FROM python:3.10.11

RUN python -m pip install requests
RUN pip install --upgrade pip

おまじないapt-get update && apt-get install -yはいらないの?

結論から言うと不要です。

Dockerfileに次のレイヤーを追加している記事もありますが、ベースイメージにpython3python3-pipは含まれているため、省きます。
Dockerはレイヤーが増えるとファイルが肥大化するため、最小限の記述で抑えたいですね!

Dockerfile
# ベースイメージにすでにあるため不要
RUN apt-get update && apt-get install -y python3 python3-pip

docker-compose.yml

docker-compose.yml
version: '3.9'
services:
  python3:
    build: .
    container_name: 'python3'
    working_dir: '/root/src/'
    tty: true
    volumes:
      - ./src:/root/src

tty: trueはなにしてるの?

ターミナルからコンテナに入り、コマンドを実行できるようにしています。

ターミナル(ttyがfalseの場合)
$ docker-compose exec python3 bash
docker-compose exec python3 bash
# コマンド実行できない
service "python3" is not running container #1
make: *** [bash] Error 1

動作確認

# コンテナのビルドと起動
docker-compose build
docker-compose up -d
# コンテナに入る
docker-compose exec python3 bash
# バージョン確認
~/src# python --version
Python 3.10.11

施設名から緯度経度を取得する

国土地理院APIは候補を配列で返してくる

下記APIを叩くと、候補の緯度経度を配列で返してくれます。
しかし、関係ない緯度経度が返される場合があるため、取得したい緯度経度のみを抽出する必要があります。

https://msearch.gsi.go.jp/address-search/AddressSearch?q=東京タワー

WEBで実行するとこんな感じ

関係ない緯度経度めっちゃある笑

Pythonスクリプトで緯度経度を取得する

Pythonで住所から緯度経度を取得するための関数 get_coordinate()関数を実装します。
この関数の処理内容は以下になります。

  • 施設名から緯度経度を取得
  • 施設名が一致しなかった場合は、都道府県名から緯度経度を取得
  • いずれもデータが見つからないorエラーの場合は、 Noneを返します。
get_coordinate.py
import requests

def get_coordinate(place_name, prefecture= ""):
    """
    国土地理院APIを使用して、住所から緯度経度を取得する関数。
    """
    url = "https://msearch.gsi.go.jp/address-search/AddressSearch"
    params = {"q": place_name}
    r = requests.get(url, params=params)
    data = r.json()
    if "error" in data:
        print(data["error"])
        return None, None
    if not data:
        return None, None
    else:
        # レスポンスと施設名が一致する緯度経度を返す
        for row in data:
            if row["properties"]["title"].startswith(place_name):
                coordinate = row["geometry"]["coordinates"]
                title = row["properties"]["title"]
                return coordinate, title
        # レスポンス値と都道府県が一致する緯度経度を返す
        for row in data:
            if row["properties"]["title"].startswith(prefecture):
                coordinates = row["geometry"]["coordinates"]
                title = row["properties"]["title"]
                return coordinates, title
        # 見つからない場合
        return None, None

        
coordinates1,title1 = get_coordinate("東京タワー")
coordinates2,title2 = get_coordinate("町田リス園", "東京都")
print("有名施設:東京タワー",coordinates1,title1)
print("あまり有名でない施設:町田リス園",coordinates2,title2)

実行結果はこんな感じです。
有名かどうかの基準はよくわかりません。。。レスポンスが空で返される場合もあります。

ターミナル
root@xxx:~/src# python get_coordinate.py
有名施設:東京タワー [139.745384871667, 35.6579641891667] 東京タワー
あまり有名でない施設:町田リス園 [139.438644, 35.546604] 東京都町田市

CSVから施設名をインポートして一括出力

インポートするCSVはこちら。
全国のタワーとその県名を載せています。

tower_prefecture.csv
施設名,都道府県
五稜郭タワー,北海道
名古屋テレビ塔,愛知県
大阪ビジネスパーク,大阪府
夢みなとタワー,鳥取県

process_csv()関数は施設名をインポートし、その緯度経度を出力します。
緯度経度が見つかればoutput.csvに、見つからなければerror.csvにデータを出力します。

export_csv.py
import csv
from get_coordinate import get_coordinate

# 入力ファイル名
input_file = "./input/tower_prefecture.csv"

# 出力ファイル名
output_file = "./output/output.csv"
error_file = "./output/error.csv"


def process_csv(input_file, output_file):
    with open(input_file, "r", encoding="utf-8") as csvfile:
        next(csv.reader(csvfile))
        reader = csv.reader(csvfile)
        with open(output_file, "w", encoding="utf-8", newline="") as outputcsv, open(
            error_file, "w", encoding="utf-8", newline=""
        ) as errorfile:
            writer = csv.writer(outputcsv)
            # 施設名,都道府県,緯度経度,タイトル
            writer.writerow(
                [
                    "place_name",
                    "title",
                    "coordinates",
                    "prefecture",
                ]
            )
            writer_error = csv.writer(errorfile)

            for row in reader:
                coordinates, title = get_coordinate(row[0], row[1])
                if not coordinates:
                    # 住所不定
                    writer_error.writerow([row[0]])
                else:
                    writer.writerow(
                        [
                            row[0], 
                            row[1],
                            coordinates,
                            title,
                        ]
                    )
    return "CSVファイルの処理が完了しました。"


result = process_csv(input_file, output_file)
print(result)

ターミナルでスクリプトを実行します。

ターミナル
root@xxx:~/src# python export_csv.py 
CSVファイルの処理が完了しました。

結果はこんな感じ。
五稜郭タワーは施設名が一致し、正確な緯度経度を出力できていますね。
大阪ビジネスパークは施設名と前方一致で「大阪ビジネスパーク駅」の緯度経度が出力されています。
名古屋テレビ塔は施設名が一致するものがなかったため、県名から抽出し「愛知県名古屋市」の緯度経度を出力しているようです。

output.csv
place_name,title,coordinates,prefecture
五稜郭タワー,北海道,"[140.753809277778, 41.7945478055556]",五稜郭タワー
名古屋テレビ塔,愛知県,"[136.90657, 35.181438]",愛知県名古屋市
大阪ビジネスパーク,大阪府,"[135.523367535278, 34.6920521472222]",大阪ビジネスパーク駅

鳥取県にある「夢みなとタワー」は国土地理院APIから検索できませんでした。
APIの戻り値にない施設名は諦めてGoogleMapAPIを使うか、手動で調べましょう。

error.csv
夢みなとタワー

(おまけ)何回もAPI叩いて大丈夫...?

結論、大丈夫です。

地名検索APIに関しましてもリクエスト数の具体的な制限値はもうけておりませんが、主に地理院地図からの利用を想定しており、そのため、必ずしも常にまた長期的に提供できるとは限らないことをご承知おきください。
また、当該機能の仕様や利用方法は、予告なく変更する場合があります。

https://github.com/gsi-cyberjapan/gsimaps/issues/113

終わりに

国土地理院APIを使って、施設名(と都道府県)から緯度経度を取得するスクリプトを実装しました。
有名施設でないと精度にかける点がありますが、おおまかな緯度経度でもいい場合はおおむね問題なく使えると思います。

参考リンク

Docker構築
https://zenn.dev/isi00141/articles/58d3b69af40284

ttyとは
https://keymaso.com/programemory/docker/run-option-d/

国土地理院APIをpythonで使う
https://elsammit-beginnerblg.hatenablog.com/entry/2021/07/11/122916

Discussion