Closed4

geoindexで緯度経度のリストからある地点に最も近い緯度経度を求める

kun432kun432

メモ

https://qiita.com/ninomiyt/items/96e6c11e412daaf7b398

https://pypi.org/project/geoindex/

この通りにやれば求めれるのだけど、PyPIの方のUsageを見ると緯度経度にリファレンス情報を紐づけれる様子。(余談だけどこのUsage間違ってる・・・)

気象庁ホームページ防災気象情報のJSONデータを読み込んで、ある地点から最も近い地点を取得してみる。

import json
import requests

url = requests.get("https://www.jma.go.jp/bosai/amedas/const/amedastable.json")
amedas_table_json = json.loads(url.text)

amedas_table_jsonはこうなる。

{
  '11001': {
    'type': 'C',
    'elems': '11112010',
    'lat': [45, 31.2],
    'lon': [141, 56.1],
    'alt': 26,
    'kjName': '宗谷岬',
    'knName': 'ソウヤミサキ',
    'enName': 'Cape Soya'},
 '11016': {
    'type': 'A',
    'elems': '11111111',
    'lat': [45, 24.9],
    'lon': [141, 40.7],
    'alt': 3,
    'kjName': '稚内',
    'knName': 'ワッカナイ',
    'enName': 'Wakkanai'
  },
  (snip)

地点情報をgeoindexに登録

from geoindex import GeoGridIndex, GeoPoint

index = GeoGridIndex(precision=4)

for k, v in amedas_table_json.items():
    stnid = k
    lat = v["lat"][0] + v["lat"][1]/60     # 気象庁の緯度経度は度・分になっている
    lon = v["lon"][0] + v["lon"][1]/60
    index.add_point(GeoPoint(lat, lon, ref=stnid))     # 地点IDをリファレンスとして追加

ある地点の緯度経度を与えて検索してみる。

center_point = GeoPoint(34.90716228395375, 135.72457797794596, ref="京都競馬場")

for point, distance in index.get_nearest_points(center_point, 10, 'km'):
    print("{} に近い地点IDは {} で {} km以内にあります".format(center_point.ref, point.ref, distance))
京都競馬場 に近い地点IDは 61306 で 4.9218383829330135 km以内にあります
京都競馬場 に近い地点IDは 61326 で 9.168016787097525 km以内にあります

気象庁ホームページ防災気象情報のJSONから該当の地点IDを見てみる。

amedas_table_json["61306"]
{
  'type': 'C',
  'elems': '01000000',
  'lat': [34, 55.8],
  'lon': [135, 40.7],
  'alt': 71,
  'kjName': '長岡京',
  'knName': 'ナガオカキョウ',
  'enName': 'Nagaokakyo'
}
amedas_table_json["61326"]
{
  'type': 'C',
  'elems': '11112010',
  'lat': [34, 49.8],
  'lon': [135, 45.6],
  'alt': 20,
  'kjName': '京田辺',
  'knName': 'キョウタナベ',
  'enName': 'Kyotanabe'
}

全競馬場に一番近い地点IDを取得する。index.get_nearest_pointsはジェネレータなので複数返してくるので、nextで1つだけ取り出すようにすれば最も近いものが取れる。

# 中央競馬全10場の緯度経度
race_courses = {
    "札幌": (43.078580672154054, 141.32524842946154),
    "函館": (41.78411980890134, 140.77609014480302),
    "福島": (37.766008484884956, 140.47998205275408),
    "新潟": (37.949722004105155, 139.18358482946152),
    "中山": (35.7260195321711, 139.96297367740598),
    "東京": (35.66554466853543, 139.4863561967988),
    "中京": (35.066493942905645, 136.99223323417795),
    "京都": (34.90716228395375, 135.72457797794596),
    "阪神": (34.779152692687816, 135.361180942083),
    "小倉": (33.84298062548288, 130.87283248906468),
}

for k, v in race_courses.items():
    center_point = GeoPoint(*v)
    point, distance = next(index.get_nearest_points(center_point, 10, 'km'))
    print("{}競馬場に一番近い地点は {}(地点ID:{}) で {:.2f} km 以内にあります".format(k, amedas_table_json[point.ref]["kjName"], point.ref, distance))
札幌競馬場に一番近い地点は 札幌(地点ID:14163) で 2.08 km 以内にあります
函館競馬場に一番近い地点は 函館(地点ID:23232) で 4.08 km 以内にあります
福島競馬場に一番近い地点は 福島(地点ID:36127) で 1.22 km 以内にあります
新潟競馬場に一番近い地点は 松浜(地点ID:54236) で 6.33 km 以内にあります
中山競馬場に一番近い地点は 船橋(地点ID:45106) で 7.43 km 以内にあります
東京競馬場に一番近い地点は 府中(地点ID:44116) で 2.00 km 以内にあります
中京競馬場に一番近い地点は 大府(地点ID:51216) で 9.11 km 以内にあります
京都競馬場に一番近い地点は 長岡京(地点ID:61306) で 4.92 km 以内にあります
阪神競馬場に一番近い地点は 西宮(地点ID:63477) で 4.03 km 以内にあります
小倉競馬場に一番近い地点は 東谷(地点ID:82097) で 7.22 km 以内にあります
kun432kun432

少し本題から蛇足するけれども、観測所によって取得できるデータが異なる。

https://www.dpac.dpri.kyoto-u.ac.jp/enomoto/pymetds/JSON.html

typeは観測点の種類で,Aは管区気象台・地方気象台・沖縄気象台,Bは測候所・特別地域気象観測所(元測候所),Cは通常のアメダス,D, E, Fはそれぞれ父島気象観測所,南鳥島気象観測所,富士山特別気象観測所を表すものと思われます。elemsは観測要素で,順に気温,降水量,風向,風速,日照時間,積雪深,湿度,気圧の有(1)無(0)に対応しているようです。

ただ見ていると、0/1だけじゃなくて2もあるのよな。。。0は確かに取れてないっぽいのだけども、12の違いはわからない。。。

'11001': {'type': 'C',
  'elems': '11112010',
  'lat': [45, 31.2],
  'lon': [141, 56.1],
  'alt': 26,
  'kjName': '宗谷岬',
  'knName': 'ソウヤミサキ',
  'enName': 'Cape Soya'},

で風向・風速・降水量・気温に対応している観測所をリストアップしたいということで少し工夫。

from geoindex import GeoGridIndex, GeoPoint

index = GeoGridIndex(precision=4)

for k, v in amedas_table_json.items():
    stnid = k
    # elems: 00001111は以下となる
    # 0: 気温
    # 1: 降水量
    # 2: 風向
    # 3: 風速
    # 4: 日照時間
    # 5: 積雪深
    # 6: 湿度
    # 7: 気圧
    elems = [True if c != '0' else False for c in v["elems"]]
    (
        temperature,
        precipitation,
        wind_direction,
        wind_speed,
        sunshine_hours,
        snow_depth,
        humidity,
        air_pressure,
    ) = elems
    if all([wind_direction, wind_speed, precipitation, temperature]):
        lat = v["lat"][0] + v["lat"][1]/60
        lon = v["lon"][0] + v["lon"][1]/60
        index.add_point(GeoPoint(lat, lon, ref=stnid))

で、再度算出してみたけども、

race_courses = {
    "札幌": (43.078580672154054, 141.32524842946154),
    "函館": (41.78411980890134, 140.77609014480302),
    "福島": (37.766008484884956, 140.47998205275408),
    "新潟": (37.949722004105155, 139.18358482946152),
    "中山": (35.7260195321711, 139.96297367740598),
    "東京": (35.66554466853543, 139.4863561967988),
    "中京": (35.066493942905645, 136.99223323417795),
    "京都": (34.90716228395375, 135.72457797794596),
    "阪神": (34.779152692687816, 135.361180942083),
    "小倉": (33.84298062548288, 130.87283248906468),
}

for k, v in race_courses.items():
    center_point = GeoPoint(*v)
    point, distance = next(index.get_nearest_points(center_point, 10, 'km'))
    print("{}競馬場に一番近い地点は {}(地点ID:{}) で {:.2f} km 以内にあります".format(k, amedas_table_json[point.ref]["kjName"], point.ref, distance))
札幌競馬場に一番近い地点は 札幌(地点ID:14163) で 2.08 km 以内にあります
函館競馬場に一番近い地点は 函館(地点ID:23232) で 4.08 km 以内にあります
福島競馬場に一番近い地点は 福島(地点ID:36127) で 1.22 km 以内にあります
新潟競馬場に一番近い地点は 松浜(地点ID:54236) で 6.33 km 以内にあります
中山競馬場に一番近い地点は 船橋(地点ID:45106) で 7.43 km 以内にあります
東京競馬場に一番近い地点は 府中(地点ID:44116) で 2.00 km 以内にあります
中京競馬場に一番近い地点は 大府(地点ID:51216) で 9.11 km 以内にあります
京都競馬場に一番近い地点は 京田辺(地点ID:61326) で 9.17 km 以内にあります
阪神競馬場に一番近い地点は 豊中(地点ID:62051) で 7.06 km 以内にあります
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-12-5fcd2bad3374> in <cell line: 14>()
     14 for k, v in race_courses.items():
     15     center_point = GeoPoint(*v)
---> 16     point, distance = next(index.get_nearest_points(center_point, 10, 'km'))
     17     print("{}競馬場に一番近い地点は {}(地点ID:{}) で {:.2f} km 以内にあります".format(k, amedas_table_json[point.ref]["kjName"], point.ref, distance))
     18 

StopIteration: 

なるほど、指定した半径内には観測項目に対応した地点がないのかもしれない。ということで少し半径を広げてみたのと少し気になったので、ジェネレータから取得できる値を全部取ってみた。

for k, v in race_courses.items():
    center_point = GeoPoint(*v)
    point, distance = next(index.get_nearest_points(center_point, 15, 'km'))
    print("{}競馬場に一番近い地点は {}(地点ID:{}) で {:.2f} km 以内にあります".format(k, amedas_table_json[point.ref]["kjName"], point.ref, distance))

print()

for k, v in race_courses.items():
    center_point = GeoPoint(*v)
    for point, distance in index.get_nearest_points(center_point, 15, 'km'):
        print("{}競馬場に近い地点は {}(地点ID:{}) で {:.2f} km 以内にあります".format(k, amedas_table_json[point.ref]["kjName"], point.ref, distance))
札幌競馬場に一番近い地点は 山口(地点ID:14116) で 11.44 km 以内にあります
函館競馬場に一番近い地点は 函館(地点ID:23232) で 4.08 km 以内にあります
福島競馬場に一番近い地点は 茂庭(地点ID:36056) で 14.48 km 以内にあります
新潟競馬場に一番近い地点は 松浜(地点ID:54236) で 6.33 km 以内にあります
中山競馬場に一番近い地点は 江戸川臨海(地点ID:44136) で 13.27 km 以内にあります
東京競馬場に一番近い地点は 所沢(地点ID:43266) で 13.68 km 以内にあります
中京競馬場に一番近い地点は 名古屋(地点ID:51106) で 11.41 km 以内にあります
京都競馬場に一番近い地点は 京都(地点ID:61286) で 11.82 km 以内にあります
阪神競馬場に一番近い地点は 豊中(地点ID:62051) で 7.06 km 以内にあります
小倉競馬場に一番近い地点は 下関(地点ID:81428) で 12.66 km 以内にあります

札幌競馬場に近い地点は 山口(地点ID:14116) で 11.44 km 以内にあります
札幌競馬場に近い地点は 石狩(地点ID:14121) で 13.27 km 以内にあります
札幌競馬場に近い地点は 札幌(地点ID:14163) で 2.08 km 以内にあります
函館競馬場に近い地点は 函館(地点ID:23232) で 4.08 km 以内にあります
函館競馬場に近い地点は 高松(地点ID:23281) で 4.09 km 以内にあります
福島競馬場に近い地点は 茂庭(地点ID:36056) で 14.48 km 以内にあります
福島競馬場に近い地点は 梁川(地点ID:36066) で 13.47 km 以内にあります
福島競馬場に近い地点は 福島(地点ID:36127) で 1.22 km 以内にあります
新潟競馬場に近い地点は 松浜(地点ID:54236) で 6.33 km 以内にあります
中山競馬場に近い地点は 江戸川臨海(地点ID:44136) で 13.27 km 以内にあります
中山競馬場に近い地点は 船橋(地点ID:45106) で 7.43 km 以内にあります
東京競馬場に近い地点は 所沢(地点ID:43266) で 13.68 km 以内にあります
東京競馬場に近い地点は 練馬(地点ID:44071) で 12.49 km 以内にあります
東京競馬場に近い地点は 府中(地点ID:44116) で 2.00 km 以内にあります
中京競馬場に近い地点は 名古屋(地点ID:51106) で 11.41 km 以内にあります
中京競馬場に近い地点は 大府(地点ID:51216) で 9.11 km 以内にあります
京都競馬場に近い地点は 京都(地点ID:61286) で 11.82 km 以内にあります
京都競馬場に近い地点は 京田辺(地点ID:61326) で 9.17 km 以内にあります
京都競馬場に近い地点は 枚方(地点ID:62046) で 12.00 km 以内にあります
阪神競馬場に近い地点は 豊中(地点ID:62051) で 7.06 km 以内にあります
小倉競馬場に近い地点は 下関(地点ID:81428) で 12.66 km 以内にあります
小倉競馬場に近い地点は 八幡(地点ID:82056) で 12.00 km 以内にあります
小倉競馬場に近い地点は 空港北町(地点ID:82068) で 14.98 km 以内にあります

うーん、必ずしも一番近いものが最初に出てくるというわけではないのかな???ジェネレータから返ってきた値を保存しておいて一番近いものをリストアップしたほうが良さそう。

for k, v in race_courses.items():
    center_point = GeoPoint(*v)
    nearest_point = {"distance": None, "point": None, "ref": None}
    for point, distance in index.get_nearest_points(center_point, 15, 'km'):
        if nearest_point['distance'] is None or distance < nearest_point['distance']:
            nearest_point = {"distance": distance, "point": point, "ref": point.ref}
    print(
        "{}競馬場に近い地点は {}(地点ID:{}) で {:.2f} km 以内にあります".format(
            k, amedas_table_json[nearest_point['ref']]["kjName"],
            nearest_point['ref'],
            nearest_point['distance']
        )
    )
札幌競馬場に近い地点は 札幌(地点ID:14163) で 2.08 km 以内にあります
函館競馬場に近い地点は 函館(地点ID:23232) で 4.08 km 以内にあります
福島競馬場に近い地点は 福島(地点ID:36127) で 1.22 km 以内にあります
新潟競馬場に近い地点は 松浜(地点ID:54236) で 6.33 km 以内にあります
中山競馬場に近い地点は 船橋(地点ID:45106) で 7.43 km 以内にあります
東京競馬場に近い地点は 府中(地点ID:44116) で 2.00 km 以内にあります
中京競馬場に近い地点は 大府(地点ID:51216) で 9.11 km 以内にあります
京都競馬場に近い地点は 京田辺(地点ID:61326) で 9.17 km 以内にあります
阪神競馬場に近い地点は 豊中(地点ID:62051) で 7.06 km 以内にあります
小倉競馬場に近い地点は 八幡(地点ID:82056) で 12.00 km 以内にあります
kun432kun432

まあ古いライブラリだしissueとかPRも放置されているので、その辺りも鑑みて・・・

このスクラップは11日前にクローズされました