OpenWeather API を使い現在地の今日の天気を取得。
一時間以内に雨が降るなら音声で知らせるものを作る
以前
https://github.com/Snowpooll/face_weather
で作成した顔を認識したら今日の天気を知らせる
の応用で
顔を認識した時に1時間以内に雨が降るなら
音声で知らせる
を作成する
GPTで
OpenWeather API を使い現在地の今日の天気を取得。
一時間以内に雨が降るならアラートを表示するpythonコード
としたとき
import requests import json # OpenWeather API キーと緯度経度を設定 api_key = 'YOUR_API_KEY' latitude = 'YOUR_LATITUDE' longitude = 'YOUR_LONGITUDE' # OpenWeather One Call API の URL url = f"https://api.openweathermap.org/data/2.5/onecall?lat={latitude}&lon={longitude}&exclude=minutely,daily&appid={api_key}&units=metric" def get_weather_data(url): response = requests.get(url) return response.json() def check_for_rain(weather_data): hourly_forecast = weather_data.get('hourly', [])[:1] # 次の1時間の予報だけをチェック for hour in hourly_forecast: for weather in hour['weather']: if weather['main'] == 'Rain': return True return False # API から天気データを取得 weather_data = get_weather_data(url) # 雨が予報されているかどうかをチェック if check_for_rain(weather_data): print("Alert: Rain is expected within the next hour!") else: print("No rain expected in the next hour.")
となった
しかし
現在地をどうせなら自動で取得したいので
GPTで
現在地の緯度経度を取得するpythonコード
とすると
ipinfo.ioとNominatim ジオコーダ
2つの取得方法が提示される
なお移動しながらの位置情報取得はGPSをお勧めされた
とりあえず精度などからだと
家庭のように固定された場所の位置情報を取得する場合は、Nominatim ジオコーダを使用するのが一般的に適しています。その理由は以下の通りです:
1. 正確性:Nominatim は OpenStreetMap のデータに基づいており、具体的な住所や場所名から精密な緯度と経度を提供できます。これは家庭や建物の正確な位置を特定するのに最適です。
2. 詳細レベル:Nominatim は詳細な逆ジオコーディングを提供し、具体的な住所情報を含めることができます。これにより、特定の住所の地理的な位置を正確に把握できます。
3. 汎用性:地図上での場所検索や、特定の地点の座標を取得するために、Nominatim は広範なアプリケーションで利用可能です。地図関連の多くの機能を実装する場合にも対応しやすいです。
一方で、ipinfo.io は IP アドレスに基づいて地理的な位置を特定するサービスであり、特定の住所に対してはそれほど詳細な情報を提供することはできません。IP ベースの位置特定は、あくまでおおまかな地域情報を提供するため、家庭のように固定された具体的な位置を必要とする場合には適していません。
したがって、家庭のような固定の場所の正確な位置情報を求める場合は、Nominatim ジオコーダがより適切な選択肢と言えるでしょう。
とのこと
とりあえず
pip install geopy
でライブラリを入れる
しかし

from geopy.geocoders import Nominatim import geopy def get_location(): # ジオロケーターを初期化 geolocator = Nominatim(user_agent="geoapiExercises") try: # IPアドレスから位置情報を取得 location = geolocator.geocode("現在地の住所", timeout=10) # 緯度と経度を取得 latitude = location.latitude longitude = location.longitude return latitude, longitude except geopy.exc.GeocoderTimedOut: return "Geocoder service timed out" except AttributeError: return "Location not found" # 現在地の緯度経度を取得 current_location = get_location() print("Latitude:", current_location[0]) print("Longitude:", current_location[1])
を実行すると
Latitude: L Longitude: o
となる
ご提示のコードで Latitude: L と Longitude: o という結果が出力されるのは、おそらく何らかのエラーが発生しているため、エラーメッセージの文字列を返している可能性が高いです。get_location 関数では、例外が発生した場合に文字列を返しています。しかし、それが数字のタプルではないため、current_location[0] と current_location[1] で文字列の最初の文字が表示されてしまうのです。
問題を解決するために、エラー発生時のハンドリングを少し変更し、エラーメッセージを確認できるように修正すると良いでしょう。以下に修正したコードを示します。
from geopy.geocoders import Nominatim import geopy def get_location(): # ジオロケーターを初期化 geolocator = Nominatim(user_agent="geoapiExercises") try: # 住所から位置情報を取得 location = geolocator.geocode("現在地の住所", timeout=10) # 緯度と経度を取得 if location: latitude = location.latitude longitude = location.longitude return (latitude, longitude) else: return "Location not found", None except geopy.exc.GeocoderTimedOut: return "Geocoder service timed out", None except Exception as e: return str(e), None # 現在地の緯度経度を取得 current_location = get_location() if isinstance(current_location, tuple) and None not in current_location: print("Latitude:", current_location[0]) print("Longitude:", current_location[1]) else: print(current_location[0]) # エラーメッセージを出力
の結果は
Location not found
住所の入力方法に問題ありかと思って
https://note.com/asahi_ictrad/n/nc948388e7c72#66ef102d-ebb9-421c-9e94-ea6e86c1a063
を参考に
from geopy.geocoders import Nominatim import geopy def get_location(): # ジオロケーターを初期化 geolocator = Nominatim(user_agent="geoapiExercises") try: # 住所から位置情報を取得 location = geolocator.geocode("日本、〒郵便番号 住所", timeout=10) # 緯度と経度を取得 if location: latitude = location.latitude longitude = location.longitude return (latitude, longitude) else: return "Location not found", None except geopy.exc.GeocoderTimedOut: return "Geocoder service timed out", None except Exception as e: return str(e), None # 現在地の緯度経度を取得 current_location = get_location() if isinstance(current_location, tuple) and None not in current_location: print("Latitude:", current_location[0]) print("Longitude:", current_location[1]) else: print(current_location[0]) # エラーメッセージを出力
としたが変わらない
このため
現在地の緯度と経度を取得するには、Pythonで geopy ライブラリを使用するのが一般的です。geopy を使って、デバイスの IP アドレスを基にして位置情報を推測する方法
を試す
pip install geopy requests
でライブラリインポート
from geopy.geocoders import Nominatim import requests def get_public_ip(): # 公開 API を使用して公衆 IP アドレスを取得 response = requests.get('https://api.ipify.org') return response.text def get_location_by_ip(ip_address): # IP アドレスから位置情報を推定 url = f'https://ipinfo.io/{ip_address}/json' response = requests.get(url) data = response.json() loc = data.get('loc', None) if loc: latitude, longitude = loc.split(',') return latitude, longitude return None, None # 公衆 IP アドレスを取得 public_ip = get_public_ip() # IP アドレスから位置情報を取得 latitude, longitude = get_location_by_ip(public_ip) print(f"Latitude: {latitude}, Longitude: {longitude}")
これなら成功
なのでこれをモジュールにして呼び出すようにする
location_utils.py
として
# location_utils.py from geopy.geocoders import Nominatim import requests def get_public_ip(): """公開 API を使用して公衆 IP アドレスを取得する関数""" response = requests.get('https://api.ipify.org') return response.text def get_location_by_ip(ip_address): """IP アドレスから位置情報(緯度と経度)を推定する関数""" url = f'https://ipinfo.io/{ip_address}/json' response = requests.get(url) data = response.json() loc = data.get('loc', None) if loc: latitude, longitude = loc.split(',') return latitude, longitude return None, None
で保存
次に

import requests import json # OpenWeather API キーと緯度経度を設定 api_key = 'YOUR_API_KEY' latitude = 'YOUR_LATITUDE' longitude = 'YOUR_LONGITUDE' # OpenWeather One Call API の URL url = f"https://api.openweathermap.org/data/2.5/onecall?lat={latitude}&lon={longitude}&exclude=minutely,daily&appid={api_key}&units=metric" def get_weather_data(url): response = requests.get(url) return response.json() def check_for_rain(weather_data): hourly_forecast = weather_data.get('hourly', [])[:1] # 次の1時間の予報だけをチェック for hour in hourly_forecast: for weather in hour['weather']: if weather['main'] == 'Rain': return True return False # API から天気データを取得 weather_data = get_weather_data(url) # 雨が予報されているかどうかをチェック if check_for_rain(weather_data): print("Alert: Rain is expected within the next hour!") else: print("No rain expected in the next hour.")
の中で
location_utils.py
を呼び出して緯度経度を自動取得できるようにしたい
location_utils.py モジュールから緯度と経度を取得する機能を weather_check.py に組み込むために、以下のようにコードを修正します。まず、location_utils.py が緯度と経度を直接返すようにし、その後、この情報を weather_check.py で利用して OpenWeather API に問い合わせます。
location_utils.py を以下のように更新します。このコードは緯度と経度を直接取得し、戻り値として返します
# location_utils.py import requests def get_public_ip(): response = requests.get('https://api.ipify.org') return response.text def get_location_by_ip(ip_address): url = f'https://ipinfo.io/{ip_address}/json' response = requests.get(url) data = response.json() loc = data.get('loc', None) if loc: latitude, longitude = loc.split(',') return latitude, longitude return None, None def get_current_location(): ip_address = get_public_ip() return get_location_by_ip(ip_address)
次に
weather_check.py
location_utils から緯度と経度を取得し、その情報を使って天気情報を取得
# weather_check.py import requests import location_utils # OpenWeather API キー api_key = 'APIキー' # 緯度と経度を location_utils から取得 latitude, longitude = location_utils.get_current_location() if latitude is None or longitude is None: print("Failed to get location.") exit() # OpenWeather One Call API の URL url = f"https://api.openweathermap.org/data/2.5/onecall?lat={latitude}&lon={longitude}&exclude=minutely,daily&appid={api_key}&units=metric" def get_weather_data(url): response = requests.get(url) return response.json() def check_for_rain(weather_data): hourly_forecast = weather_data.get('hourly', [])[:1] for hour in hourly_forecast: for weather in hour['weather']: if weather['main'] == 'Rain': return True return False # API から天気データを取得 weather_data = get_weather_data(url) # 雨が予報されているかどうかをチェック if check_for_rain(weather_data): print("Alert: Rain is expected within the next hour!") else: print("No rain expected in the next hour.")
これで実行すると
No rain expected in the next hour.
となる
しかしこれだけだと本当に合っているかわからないので
IPアドレスから住所を表示するのと
一時間毎の天気を表示するものを作る
住所を確認したが違ってた
表示された緯度経度が本来の緯度経度とずれている
他のものも試してみた
pip install geocoder
の後に
import geocoder def get_current_location(): # 自分の IP アドレスに基づいて現在地の位置情報を取得 g = geocoder.ip('me') if g.ok: return g.latlng else: return None # 現在地の緯度と経度を取得して表示 location = get_current_location() if location: latitude, longitude = location print(f"Latitude: {latitude}, Longitude: {longitude}") else: print("Unable to determine current location")
国土地理院なら
import requests import urllib makeUrl = "https://msearch.gsi.go.jp/address-search/AddressSearch?q=" s_quote = urllib.parse.quote('千葉県南房総市富浦町青木123-1') response = requests.get(makeUrl + s_quote) print(response.json()[0]["geometry"]["coordinates"])
で
[137.91481, 34.743805]
取り合えず住所から入力して緯度経度取得にするなどを考えていたが
よくよく考えればどうやって緯度経度を出したか調べたら
import requests import json from pprint import pprint url = "https://api.openweathermap.org/data/2.5/weather?zip={zip_place}&units=metric&appid={API_key}" # xxxxx url = url.format(zip_place = "郵便番号,JP", API_key = "取得したAPIキー") jsondata = requests.get(url).json() pprint(jsondata) print("天気:",jsondata["weather"][0]["main"]) print("天気詳細:",jsondata["weather"][0]["description"]) print("都市名:",jsondata["name"]) print("気温:",jsondata["main"]["temp"]) print("体感気温:",jsondata["main"]["feels_like"]) print("最低気温:",jsondata["main"]["temp_min"]) print("最高気温:",jsondata["main"]["temp_max"]) print("気圧:",jsondata["main"]["pressure"]) print("湿度:",jsondata["main"]["humidity"]) print("風速:",jsondata["wind"]["speed"]) print("風の方角:",jsondata["wind"]["deg"])
で緯度経度を取得していた
つまり
import requests import json from pprint import pprint # url = "https://api.openweathermap.org/data/2.5/weather?zip={zip_place}&units=metric&appid={API_key}" url = "https://api.openweathermap.org/data/2.5/weather?zip={zip_place}&units=metric&lang=ja&appid={API_key}" # xxxxx url = url.format(zip_place = "郵便番号,JP", API_key = "取得したAPIキー") jsondata = requests.get(url).json() pprint(jsondata)
の結果で
{'base': 'stations', 'clouds': {'all': 100}, 'cod': 200, 'coord': {'lat': 緯度, 'lon': 経度}, 'dt': 1714340555, 'id': 0, 'main': {'feels_like': 17.59, 'grnd_level': 1014, 'humidity': 77, 'pressure': 1015, 'sea_level': 1015, 'temp': 17.75, 'temp_max': 17.75, 'temp_min': 17.75}, 'name': 'Kawai', 'rain': {'1h': 0.1}, 'sys': {'country': 'JP', 'id': 2092872, 'sunrise': 1714334403, 'sunset': 1714383067, 'type': 2}, 'timezone': 32400, 'visibility': 10000, 'weather': [{'description': '厚い雲', 'icon': '04d', 'id': 804, 'main': 'Clouds'}], 'wind': {'deg': 255, 'gust': 1.55, 'speed': 0.83}}
の実行結果の中から
'coord': {'lat': 緯度, 'lon': 経度},
の
緯度経度のみ抽出して表示したい
これは
# 緯度経度のデータを抽出 latitude = jsondata['coord']['lat'] longitude = jsondata['coord']['lon'] # 緯度経度を表示 print("緯度:", latitude) print("経度:", longitude)
とすればOK
次にモジュールにする
location_util2.pyの中身の
# location_utils.py import requests from geopy.geocoders import Nominatim def get_public_ip(): response = requests.get('https://api.ipify.org') return response.text def get_location_by_ip(ip_address): url = f'https://ipinfo.io/{ip_address}/json' response = requests.get(url) data = response.json() loc = data.get('loc', None) if loc: latitude, longitude = loc.split(',') return latitude, longitude return None, None def get_address_from_coordinates(latitude, longitude): geolocator = Nominatim(user_agent="geoapiExercises") location = geolocator.reverse((latitude, longitude), exactly_one=True) return location.address if location else "Address not found." def get_current_location_and_address(): ip_address = get_public_ip() location = get_location_by_ip(ip_address) if location: latitude, longitude = location address = get_address_from_coordinates(latitude, longitude) return latitude, longitude, address return None, None, "Location not found."
を
# location_utils2.py import requests from geopy.geocoders import Nominatim def get_current_location_and_address(): # APIキーとZIPコードは予め設定しておく必要があります。 API_key = "取得したAPIキー" zip_place = "郵便番号,JP" # OpenWeatherMap API URL url = f"https://api.openweathermap.org/data/2.5/weather?zip={zip_place}&units=metric&lang=ja&appid={API_key}" # データの取得 response = requests.get(url) jsondata = response.json() # 緯度と経度の取得 latitude = jsondata['coord']['lat'] longitude = jsondata['coord']['lon'] # 住所の取得(オプショナル) geolocator = Nominatim(user_agent="geoapiExercises") location = geolocator.reverse((latitude, longitude), language='ja') address = location.address if location else None return latitude, longitude, address
として保存
Address.pyの中身を
import location_utils2 latitude, longitude, address = location_utils2.get_current_location_and_address() if latitude and longitude: print(f"Latitude: {latitude}, Longitude: {longitude}") if address: print("Address:", address) から import location_utils2 latitude, longitude, address = location_utils2.get_current_location_and_address() if latitude and longitude: print(f"Latitude: {latitude}, Longitude: {longitude}") if address: print("Address:", address)
って同じじゃん
実行し
https://www.geocoding.jp
で調べたら
緯度 経度
が住所の緯度経度
つまり
Nominatim
による住所への変換がダメってことがわかった
# location_utils2.py import requests from geopy.geocoders import Nominatim def get_current_location_and_address(): # APIキーとZIPコードは予め設定しておく必要があります。 API_key = "取得したAPIキー" zip_place = "郵便番号,JP" # OpenWeatherMap API URL url = f"https://api.openweathermap.org/data/2.5/weather?zip={zip_place}&units=metric&lang=ja&appid={API_key}" # データの取得 response = requests.get(url) jsondata = response.json() # 緯度と経度の取得 latitude = jsondata['coord']['lat'] longitude = jsondata['coord']['lon'] # 住所の取得(オプショナル) geolocator = Nominatim(user_agent="geoapiExercises") location = geolocator.reverse((latitude, longitude), language='ja') address = location.address if location else None return latitude, longitude, address
の内容を
import requests def get_current_location_and_address(): # APIキーとZIPコードを設定 API_key = "YOUR_API_KEY" zip_place = "YOUR_ZIP_CODE,JP" # OpenWeatherMap APIを使用して緯度経度を取得 weather_url = f"https://api.openweathermap.org/data/2.5/weather?zip={zip_place}&units=metric&lang=ja&appid={API_key}" response = requests.get(weather_url) jsondata = response.json() latitude = jsondata['coord']['lat'] longitude = jsondata['coord']['lon'] # 国土地理院APIを使用して住所を取得 geo_url = f"https://mreversegeocoder.gsi.go.jp/reverse-geocoder/LonLatToAddress?lat={latitude}&lon={longitude}" geo_response = requests.get(geo_url) address_data = geo_response.json() # 住所情報を取得(存在する場合) address = address_data['results']['lv01Nm'] if 'results' in address_data and 'lv01Nm' in address_data['results'] else None return latitude, longitude, address
とした結果
とりあえず郵便番号まで取得できて大体の住所がでるなら
Nominatim
で事足りる
重要なのはそこじゃなくて緯度経度が合ってるかどうか
なので
住所検索の機能はオミットする予定だったが
import location_utils2 latitude, longitude, _ = location_utils2.get_current_location_and_address() # 住所情報は無視 print(f"Latitude: {latitude}, Longitude: {longitude}")
というように別のファイルで呼び出すときに
指定しなければ緯度経度のみ抽出できる
次に
# weather_check.py import requests import location_utils # OpenWeather API キー api_key = 'APIキー' # 緯度と経度を location_utils から取得 latitude, longitude = location_utils.get_current_location() if latitude is None or longitude is None: print("Failed to get location.") exit() # OpenWeather One Call API の URL url = f"https://api.openweathermap.org/data/2.5/onecall?lat={latitude}&lon={longitude}&exclude=minutely,daily&appid={api_key}&units=metric" def get_weather_data(url): response = requests.get(url) return response.json() def check_for_rain(weather_data): hourly_forecast = weather_data.get('hourly', [])[:1] for hour in hourly_forecast: for weather in hour['weather']: if weather['main'] == 'Rain': return True return False # API から天気データを取得 weather_data = get_weather_data(url) # 雨が予報されているかどうかをチェック if check_for_rain(weather_data): print("Alert: Rain is expected within the next hour!") else: print("No rain expected in the next hour.")
のコードを
これを使って緯度経度取得したものへ変更
# weather_check.py import location_utils2 import requests def get_weather_data(api_key, latitude, longitude): # OpenWeather One Call API の URL url = f"https://api.openweathermap.org/data/2.5/onecall?lat={latitude}&lon={longitude}&exclude=minutely,daily&appid={api_key}&units=metric" response = requests.get(url) return response.json() def check_for_rain(weather_data): hourly_forecast = weather_data.get('hourly', [])[:1] # 次の1時間の予報だけをチェック for hour in hourly_forecast: for weather in hour['weather']: if weather['main'] == 'Rain': return True return False # APIキーを設定 api_key = 'APIキー' # location_utils2から緯度と経度を取得 latitude, longitude, _ = location_utils2.get_current_location_and_address() # 天気データを取得 weather_data = get_weather_data(api_key, latitude, longitude) # 雨が予報されているかどうかをチェック if check_for_rain(weather_data): print("Alert: Rain is expected within the next hour!") else: print("No rain expected in the next hour.")
これを実行すれば雨が降らないので
No rain expected in the next hour.
となる
次に6時間毎の天気を表示する
weather_forecast_6hour.py
で
import requests def get_weather_forecast(api_key, latitude, longitude): url = f"https://api.openweathermap.org/data/2.5/onecall?lat={latitude}&lon={longitude}&units=metric&lang=ja&appid={api_key}" response = requests.get(url) return response.json() # api_key = 'YOUR_API_KEY' # APIキーを設定 api_key = 'APIキー' latitude = 35.681236 # 東京駅の緯度 longitude = 139.767125 # 東京駅の経度 weather_data = get_weather_forecast(api_key, latitude, longitude) # 現在の天気、1時間毎の天気(次の6時間)、1日毎の天気(次の7日間)を表示 print("Current Weather:", weather_data['current']) print("Hourly Forecast for next 6 hours:") for hour in weather_data['hourly'][:6]: print(hour) print("Daily Forecast for next 7 days:") for day in weather_data['daily'][:7]: print(day)
としたが
Traceback (most recent call last): File "/Users/snowpool/aw10s/rain_alert/weather_forecast_6hour.py", line 17, in <module> print("Current Weather:", weather_data['current']) KeyError: 'current'
となる
「現在・1時間毎・1日毎」の天気予報を取得
https://dev.classmethod.jp/articles/use-open-weather-map/
のコードが古いようだ
次に
https://3pysci.com/openweathermap-5/#index_id5
を参考にまずは全データの取得
だがだめ
公式を確認する
https://openweathermap.org/api/one-call-3#current
https://api.openweathermap.org/data/3.0/onecall?lat={lat}&lon={lon}&exclude={part}&appid={API key} となっていて optional By using this parameter you can exclude some parts of the weather data from the API response. It should be a comma-delimited list (without spaces). Available values: • current • minutely • hourly • daily • alerts
やるべきことは
現在の天気を取得し
雨以外の天気なら
一時間以内に雨が降るかを調べるものを作成する
雨が降りそうな時のみ音声を流す
これを顔認識したら実行するものを作成することで
洗濯物を取り込めるようにする
出かけたり買い物の時間を考えれば三時間がベストなので
print("天気:",jsondata["current"]["weather"][0]["main"])
で実験したらNG
3時間にしても
hourly_forecast = weather_data['hourly'][:24] # 24時間分のデータを取得
の部分が間違っている
つまり時間毎の指定ができないとだめ
https://zenn.dev/daifukuninja/articles/5e696cd0a75ba8#たたいてみた
を参考に
現在地の天気は
curl 'https://api.openweathermap.org/data/2.5/weather?zip=169-0072,JP&appid={your-api-key}&lang=jp'
で取得できるので
curl 'https://api.openweathermap.org/data/2.5/forecast?lon=緯度&lat=経度&appid={your-api-key}&lang=jp'
とりあえず現在の郵便番号から
緯度経度、天気を取得するように
# location_utils2.py import requests from geopy.geocoders import Nominatim def get_current_location_and_address(): # APIキーとZIPコードは予め設定しておく必要があります。 API_key = "APIキー" zip_place = "郵便番号,JP" # OpenWeatherMap API URL url = f"https://api.openweathermap.org/data/2.5/weather?zip={zip_place}&units=metric&lang=ja&appid={API_key}" # データの取得 response = requests.get(url) jsondata = response.json() # 緯度と経度の取得 latitude = jsondata['coord']['lat'] longitude = jsondata['coord']['lon'] #天気の取得 weather_description = jsondata['weather'][0]['description'] # 住所の取得(オプショナル) geolocator = Nominatim(user_agent="geoapiExercises") location = geolocator.reverse((latitude, longitude), language='ja') address = location.address if location else None return latitude, longitude, address, weather_description
へコード変更