PHPで天気予報を求める

(1/1)
livedoor の WeatherHacks は、日本全国の天気予報を無償 API で提供している。これを利用することで、任意の都市の今日/明日/明後日の天気を知ることができる。

API の結果が XML から JSON に変更になったので、プログラムを改良した。

サンプル・プログラムの実行例

PHPで天気予報を求める

サンプル・プログラム

「WeatherHacks」の天気予報検索

WeatherHacksは、入力パラメータ(IN)は GET 渡しで、出力結果(OUT)は JSON で戻るという形である。
なお、入力パラメータ city に渡す ID番号は、全国の地点定義表(RSS)を参照する必要がある。
WebAPIのURL
URL
http://weather.livedoor.com/forecast/webservice/json/v1

入力パラメータ
項目名 フィールド名 内  容
地域別ID番号 city integer 【必須】
「全国地点定義表」参照
応答データ構造(json) location (予報発表地域) area 地方名 prefecture 都道府県名 city 1次細分区名 title タイトル・見出し link livedoor天気情報のURL publicTime 予報の発表日時 description text 天気概況文 publicTime 天気概況文の発表時刻 forecasts (予報日毎の配列) date 予報日 dateLabel 予報日 telop 天気 image title 天気 link livedoor天気情報のURL url 天気アイコンのURL width 天気アイコンの幅 height 天気アイコンの高さ temperature (max:最高気温,min:最低気温) celsius 摂氏 fahrenheit 華氏 pinpointLocation (ピンポイント予報配列) name 市区町村名 link livedoor天気情報のURL copyright title コピーライト link livedoor天気情報のURL image アイコンなど provider 気象データ配信元

サンプル・プログラムの流れ

PHPで天気予報を求める
プログラムの流れは前回のものと同じだが、都道府県名・都市名が別テーブル(全国地点定義表;RSS 形式)として用意されているので、事前にこれを読み込んでおく必要がある。

サンプル・プログラムの解説:メインプログラム

0270: //予報日
0271: if (isset($_GET['today'])) {
0272:     $day = '今日';
0273: else if (isset($_GET['tomorrow'])) {
0274:     $day = '明日';
0275: else if (isset($_GET['dayaftertomorrow'])) {
0276:     $day = '明後日';
0277: else if (isset($_GET['day'])) {
0278:     $day = $_GET['day'];
0279: else {
0280:     $day = '';
0281: }
0282: 
0283: //ForecastMap(全国地点定義表)の解釈
0284: if ($id == 0) {
0285:     $n = parseForecastMap($ForecastMap$ForecastXmlns$ForecastTable);
0286:     if ($n == 0) {
0287:         echo "ForecastMapが見つかりません!";
0288:         exit(1);
0289:     }
0290:     $id = isset($ForecastTable[$pref][$city]) ?
0291:             $ForecastTable[$pref][$city] : 0;
0292: }
0293: //検索処理
0294: if ($day != '')     $n = getResults($items$id$day);
0295: 
0296: $HtmlBody = makeCommonBody($items$ForecastTable$pref$city);
0297: echo $HtmlHeader;
0298: echo $HtmlBody;
0299: echo $HtmlFooter;

まず、天気予報を求める地点の番号を変数 $id に格納するが、これが空(またはゼロ)の場合は、ユーザー関数 parseForecastMap により、全国地点定義表を配列変数 $ForecastTable に取り込み、コンボボックスで選択させる。
コンボボックスは、最初に都道県名(pref)を、次に都道府県名に応じた都市名(city)を絞り込んでいく。このため、最低でも 2 回、本スクリプトが繰り返し実行される。pref, city は各々 GET 渡しされる。その度に全国地点定義表を読み込むというオーバーヘッドがあるが、これは、Web アプリではやむを得ないところだろう。

変数 $pref, $city と 配列変数 $ForecastTable から、天気予報を求める地点の番号 $id を取得する。
一方、入力時に予報時期(today, tomorrow, dayaftertomorrow)を指定するわけだが、これを納めた変数 $day$id が空(またはゼロ)でなければ、ユーザー関数 getResults により、天気予報情報を取得する。

0104: /**
0105:  * WeatherHacks のURLを取得する
0106:  * @param int $city 地域別に定義されたID番号
0107:  * @param string $day  リクエストする予報日(today/tomorrow/dayaftertomorrow)
0108:  * @return string URL
0109: */
0110: function getWeatherHacksURL($city$day) {
0111:     $url = 'http://weather.livedoor.com/forecast/webservice/json/v1?city=' . $city;
0112:     return $url;
0113: }
0114: 
0115: /**
0116:  * ForecastMap(全国地点定義表)を解釈する
0117:  * @param string $forecastmap ForecastMapのURL
0118:  * @param string $forecastxmlns ForecastMapの名前空間
0119:  * @param array  $forecasttable ForecastMapを格納する配列
0120:  * @return int 配列に格納した都市数
0121: */
0122: function parseForecastMap($forecastmap$forecastxmlns, &$forecasttable) {
0123:     $xml = simplexml_load_file($forecastmap);
0124: 
0125:     //レスポンス・チェック
0126:     if (isset($xml->channel) == FALSE)   return FALSE;
0127: 
0128:     //必要な情報を配列へ
0129:     $cnt = 0;
0130:     $node = $xml->channel->children($forecastxmlns);
0131:     $prefs = $node->source->children();
0132: 
0133:     foreach ($prefs as $pref) {
0134:         $pref_title = (string)$pref['title'];
0135:         $cities = $pref->city;
0136:         foreach ($cities as $city) {
0137:             $city_title = (string)$city['title'];
0138:             $city_id    = (string)$city['id'];
0139:             $forecasttable[$pref_title][$city_title] = $city_id;
0140:             $cnt++;
0141:         }
0142:     }
0143:     return $cnt;
0144: }

全国地点定義表(RSS 形式)を解釈するユーザー関数 parseForecastMap は、SimpleXML 関数群を使って、RSS 形式のデータ構造を $forecasttable に格納する。

サンプル・プログラムの解説:WebAPI

0146: /**
0147:  * WeatherHacks API から必要な情報を配列に格納する
0148:  * @param array $items  情報を格納する配列
0149:  * @param int   $city   地域別に定義されたID番号
0150:  * @param string $day   予報日(今日/明日/明後日)
0151:  * @return bool TRUE/FALSE;
0152: */
0153: function getResults(&$items$city$day) {
0154:     $url = getWeatherHacksURL($city$day);      //リクエストURL
0155:     $lwws = json_decode(file_get_contents($url), TRUE);
0156:     $res = FALSE;
0157: 
0158:     $items['title'] = $lwws['title'];
0159:     $items['url']   = $lwws['link'];
0160:     $items['pref']  = $lwws['location']['prefecture'];
0161:     $items['city']  = $lwws['location']['city'];
0162:     foreach ($lwws['forecasts'] as $val) {
0163:         if ($val['dateLabel'] == $day) {
0164:             $items['date'] = $val['date'];
0165:             $items['telop'] = $val['telop'];
0166:             $items['imageurl'] = $val['image']['url'];
0167:             $items['temp_min'] = $val['temperature']['min']['celsius'];
0168:             $items['temp_max'] = $val['temperature']['max']['celsius'];
0169:             $res = TRUE;
0170:         }
0171:     }
0172: 
0173:     return $res;
0174: }

WeatherHacks API から必要な情報を配列に格納するユーザー関数 getResults は、前回までに説明したユーザー関数と同じ働きをしている。
ただし、WeatherHacks API では、image タグなど、要素が入れ子になっているタグがあるので、不本意ながら switch ~ case 文で対応している。

これ以降は、前回までと同じ流れになる。

お詫びと訂正

2009 年(平成 21 年)11 月 1 日時点で、サンプル・プログラムが明日の天気を表示しない(16 時以降には表示するようになる)というバグが見つかりました。ご指摘いただいた あんどー 様、ありがとうございます。

調べたところ、WebAPI から渡されるデータの中で、明日の最高気温が空白になっていました(ただし 16 時以降になると正常に記入されている)。最高気温が空白になっていると、その日が存在しないと判断していたのが原因でした。
そこで、最高気温が空白でも天気予報データだけを取り出すようにプログラムを修正し、バージョン 1.1 としてリリースしました。ダウンロードしてご利用ください。

また、WebAPI の提供元には、この問題を報告をしておきました。

参考サイト

(この項おわり)
header