search
LoginSignup
1

More than 1 year has passed since last update.

Lisp Advent Calendar 2020 Day 1

posted at

CommonLispでOpenWeatherMapを使って気象データを取得してみる

はじめに

OpenWeatherMap は現在の天気や予測履歴を含む各種気象データの無料APIを提供しているオンラインサービスです
これをCommonLispで使ってみます

事前準備

手始めに OpenWeatherMap のページにアクセスし、サインアップしてAPI Keyを取得

API Keyを取得したらスペシャル変数として定義しておく

(defparameter *api-key* "Your API Key")

今回は Current weather data に書かれている通りに現在の気象データを取得してみようと思うので、そのベースURLをスペシャル変数として定義しておく

(defparameter *base-url* "http://api.openweathermap.org/data/2.5/weather")

HTTPクライアントとして dexador を使用するのでロードしておく

(ql:quickload :dexador)

都市名で取得

とりあえず 東京都 を指定してみる
都市名で取得する場合のクエリパラメータは以下のように指定する

q={city name}
q={city name},{state code}
q={city name},{state code},{country code}

今回は以下のように定義しておく

(defparameter *city-name* "Tokyo,JP")

これで準備完了、早速取得してみる

(dex:get (format nil "~A?q=~A&appid=~A" *base-url* *city-name* *api-key*))
取得結果
"{\"coord\":{\"lon\":139.69,\"lat\":35.69},\"weather\":[{\"id\":801,\"main\":\"Clouds\",\"description\":\"few clouds\",\"icon\":\"02n\"}],\"base\":\"stations\",\"main\":{\"temp\":282.15,\"feels_like\":277.24,\"temp_min\":281.48,\"temp_max\":283.15,\"pressure\":1026,\"humidity\":61},\"visibility\":10000,\"wind\":{\"speed\":4.6,\"deg\":20},\"clouds\":{\"all\":20},\"dt\":1606765942,\"sys\":{\"type\":1,\"id\":8074,\"country\":\"JP\",\"sunrise\":1606771962,\"sunset\":1606807693},\"timezone\":32400,\"id\":1850144,\"name\":\"Tokyo\",\"cod\":200}"
200
#<HASH-TABLE :TEST EQUAL :COUNT 9 {1002445B13}>
#<QURI.URI.HTTP:URI-HTTP http://api.openweathermap.org/data/2.5/weather?q=Tokyo,JP&appid=6bee98cb406419564595b5c7aa0a4a78>
#<SB-SYS:FD-STREAM for "socket 192.168.11.6:48504, peer: 178.128.25.248:80" {1002443523}>

色々ごちゃっと取れたけど、とりあえず使うのは最初のJSONデータだけ
JSONデータの内容の詳細は Weather fields in API response に記載されている

JSONのままだとCommonLispでは使いづらいので、連想リストに変換する
そのために jonathan をロードする

(ql:quickload :jonathan)

もう一回取得

(jojo:parse
 (dex:get (format nil "~A?q=~A&appid=~A" *base-url* *city-name* *api-key*))
 :as :alist)
取得結果
(("cod" . 200) ("name" . "Tokyo") ("id" . 1850144) ("timezone" . 32400)
 ("sys" ("sunset" . 1606807693) ("sunrise" . 1606771962) ("country" . "JP")
  ("id" . 8074) ("type" . 1))
 ("dt" . 1606765942) ("clouds" ("all" . 20))
 ("wind" ("deg" . 20) ("speed" . 4.6)) ("visibility" . 10000)
 ("main" ("humidity" . 61) ("pressure" . 1026) ("temp_max" . 283.15)
  ("temp_min" . 281.48) ("feels_like" . 277.24) ("temp" . 282.15))
 ("base" . "stations")
 ("weather"
  (("icon" . "02n") ("description" . "few clouds") ("main" . "Clouds")
   ("id" . 801)))
 ("coord" ("lat" . 35.69) ("lon" . 139.69)))

さらに、連想リストを操作するのに便利なユーティリティ assoc-utils があるのでこれもロードする

(ql:quickload :assoc-utils)

取得した現在の気象データをスペシャル変数として定義しておく

(defparameter *current-weather-data*
  (jojo:parse
   (dex:get (format nil "~A?q=~A&appid=~A" *base-url* *city-name* *api-key*))
   :as :alist))

以下のように各要素にアクセスできる

(assoc-utils:aget *current-weather-data* "name")
取得結果
"Tokyo"

時刻はUnix時間なので時間操作ライブラリ local-time をロードして

(ql:quickload :local-time)

unix-to-timestamp してやれば

(local-time:unix-to-timestamp (assoc-utils:aget *current-weather-data* "dt"))
取得結果
@2020-12-01T04:52:22.000000+09:00

見慣れた表示になる

天気情報 weather の中の main にアクセスするには以下のようにする

(assoc-utils:aget
 (first (assoc-utils:aget *current-weather-data* "weather"))
 "main")
取得結果
"Clouds"

曇りのようです

City IDで取得

東京の City ID は 1850147

(defparameter *city-id* "1850147")

City IDで取得する場合のクエリパラメータは以下のように指定する

id={city id}
(jojo:parse
 (dex:get (format nil "~A?id=~A&appid=~A" *base-url* *city-id* *api-key*))
 :as :alist)
取得結果
(("cod" . 200) ("name" . "Tokyo") ("id" . 1850147) ("timezone" . 32400)
 ("sys" ("sunset" . 1606807693) ("sunrise" . 1606771962) ("country" . "JP")
  ("id" . 8074) ("type" . 1))
 ("dt" . 1606765634) ("clouds" ("all" . 20))
 ("wind" ("deg" . 350) ("speed" . 4.6)) ("visibility" . 10000)
 ("main" ("humidity" . 66) ("pressure" . 1026) ("temp_max" . 284.15)
  ("temp_min" . 281.48) ("feels_like" . 277.74) ("temp" . 282.42))
 ("base" . "stations")
 ("weather"
  (("icon" . "02n") ("description" . "few clouds") ("main" . "Clouds")
   ("id" . 801)))
 ("coord" ("lat" . 35.69) ("lon" . 139.69)))

結果は大体同じ

緯度経度で取得

東京駅の緯度経度は 35.681236 139.767125 なのでこれを使う

(defparameter *lat* "35.681236")
(defparameter *lon* "139.767125")

緯度経度で取得する場合のクエリパラメータは以下のように指定する

lat={lat}&lon={lon}
(jojo:parse
 (dex:get (format nil "~A?lat=~A&lon=~A&appid=~A" *base-url* *lat* *lon* *api-key*))
 :as :alist)
取得結果
(("cod" . 200) ("name" . "Marunouchi") ("id" . 1857654) ("timezone" . 32400)
 ("sys" ("sunset" . 1606807675) ("sunrise" . 1606771941) ("country" . "JP")
  ("id" . 8074) ("type" . 1))
 ("dt" . 1606765782) ("clouds" ("all" . 20))
 ("wind" ("deg" . 350) ("speed" . 4.6)) ("visibility" . 10000)
 ("main" ("humidity" . 66) ("pressure" . 1026) ("temp_max" . 284.15)
  ("temp_min" . 281.48) ("feels_like" . 277.66) ("temp" . 282.35))
 ("base" . "stations")
 ("weather"
  (("icon" . "02n") ("description" . "few clouds") ("main" . "Clouds")
   ("id" . 801)))
 ("coord" ("lat" . 35.68) ("lon" . 139.77)))

name が何故か Marunouchi になってる

郵便番号で取得

東京タワーの郵便番号 105-0011 を使ってみる

郵便番号で取得する場合のクエリパラメータは以下のように指定する

zip={zip code},{country code}
(defparameter *zip-code* "105-0011,JP")
(jojo:parse
 (dex:get (format nil "~A?zip=~A&appid=~A" *base-url* *zip-code* *api-key*))
 :as :alist)
取得結果
(("cod" . 200) ("name" . "Shibakouen") ("id" . 0) ("timezone" . 32400)
 ("sys" ("sunset" . 1606807683) ("sunrise" . 1606771943) ("country" . "JP")
  ("id" . 8074) ("type" . 1))
 ("dt" . 1606766365) ("clouds" ("all" . 20))
 ("wind" ("deg" . 20) ("speed" . 4.6)) ("visibility" . 10000)
 ("main" ("humidity" . 61) ("pressure" . 1026) ("temp_max" . 283.15)
  ("temp_min" . 281.48) ("feels_like" . 276.9) ("temp" . 281.86))
 ("base" . "stations")
 ("weather"
  (("icon" . "02n") ("description" . "few clouds") ("main" . "Clouds")
   ("id" . 801)))
 ("coord" ("lat" . 35.66) ("lon" . 139.75)))

name が何故か Shibakouen になってる

最後に

こういうのが簡単に無料で使えるのは良いですね
色々応用できそうです

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
What you can do with signing up
1