PHPで天気予報を求める

(3/3)

サンプル・プログラムの解説:入力処理

まず、天気予報を求める地点の番号を $_GET['id'] で受け取るが、これが空(またはゼロ)の場合は、ユーザー関数 parseForecastMap により、全国地点定義表を配列変数 $ForecastTable に取り込み、コンボボックスで選択させる。
コンボボックスは最初に地域名(area)を選択させ、地域名に応じた都道県名(pref)を、都道府県名に応じた都市名(city)を絞り込んでいく。このため、最低でも3回、本スクリプトが繰り返し実行される。area, pref, city は各々 GET 渡しされる。その度に全国地点定義表を読み込むというオーバーヘッドがあるが、これは、Web アプリではやむを得ないところだろう。
space
変数 $area, $pref, $city と 配列変数 $ForecastTable から、天気予報を求める地点の番号 $id を取得する。
一方、入力時に予報時期(today, tomorrow, dayaftertomorrow)を指定するわけだが、これを納めた変数 $day$id が空(またはゼロ)でなければ、ユーザー関数 getResults により、天気予報情報を取得する。

0276: $id   = isset($_GET['id'])   ? $_GET['id']   : 0;
0277: $area = isset($_GET['area']) ? $_GET['area'] : '';
0278: $pref = isset($_GET['pref']) ? $_GET['pref'] : '';
0279: $city = isset($_GET['city']) ? $_GET['city'] : '';
0280: 
0281: if (isset($_GET['today'])) {
0282:     $day = 'today';
0283: else if (isset($_GET['tomorrow'])) {
0284:     $day = 'tomorrow';
0285: else if (isset($_GET['dayaftertomorrow'])) {
0286:     $day = 'dayaftertomorrow';
0287: else if (isset($_GET['day'])) {
0288:     $day = $_GET['day'];
0289: else {
0290:     $day = '';
0291: }
0292: 
0293: //ForecastMap(全国地点定義表)の解釈
0294: if ($id == 0) {
0295:     $n = parseForecastMap($ForecastMap$ForecastXmlns$ForecastTable);
0296:     if ($n == 0) {
0297:         echo "ForecastMapが見つかりません!";
0298:         exit(1);
0299:     }
0300:     $id = isset($ForecastTable[$area][$pref][$city]) ?
0301:             $ForecastTable[$area][$pref][$city] : 0;
0302: }

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

0131: /**
0132:  * ForecastMap(全国地点定義表)を解釈する
0133:  * @param string $forecastmap ForecastMapのURL
0134:  * @param string $forecastxmlns ForecastMapのXML定義
0135:  * @param array $forecasttable ForecastMapを格納する配列
0136:  * @return int 配列に格納した都市数
0137: */
0138: function parseForecastMap($forecastmap$forecastxmlns, &$forecasttable) {
0139: //PHP4用; DOM XML利用
0140:     if (isphp5() == FALSE) {
0141:         $dom = read_xml($forecastmap);
0142:         $cnt = 0;
0143:         if ($dom != FALSE) {
0144:             //DOMから必要な情報を配列へ
0145:             $arr_area = $dom->get_elements_by_tagname('area');
0146:             foreach ($arr_area as $elm_area) {
0147:                 $area = utf82sjis($elm_area->get_attribute('title'));
0148:                 $arr_pref = $elm_area->get_elements_by_tagname('pref');
0149:                 foreach ($arr_pref as $elm_pref) {
0150:                     $pref = utf82sjis($elm_pref->get_attribute('title'));
0151:                     $arr_city = $elm_pref->get_elements_by_tagname('city');
0152:                     foreach ($arr_city as $elm_city) {
0153:                         $city = utf82sjis($elm_city->get_attribute('title'));
0154:                         $id = $elm_city->get_attribute('id');
0155:                         $forecasttable[$area][$pref][$city] = $id;
0156:                         $cnt++;
0157:                     }
0158:                 }
0159:             }
0160:         }
0161: //PHP5用; SimpleXML利用
0162:     } else {
0163:         $rss = simplexml_load_file($forecastmap);
0164:         //レスポンス・チェック
0165:         if (isset($rss->channel) == FALSE)   return FALSE;
0166:         //必要な情報を配列へ
0167:         $cnt = 0;
0168:         $ldWeather = $rss->channel->children('http://weather.livedoor.com/ns/rss/2.0');
0169:         $arr_area  = $ldWeather->source->children();
0170:         foreach ($arr_area as $elm_area) {
0171:             $area = utf82sjis($elm_area['title']);
0172:             $arr_pref = $elm_area->pref;
0173:             foreach ($arr_pref as $elm_pref) {
0174:                 $pref = utf82sjis($elm_pref['title']);
0175:                 $arr_city = $elm_pref->city;
0176:                 foreach ($arr_city as $elm_city) {
0177:                     $city = utf82sjis($elm_city['title']);
0178:                     $id = $elm_city['id'];
0179:                     $forecasttable[$area][$pref][$city] = $id;
0180:                     $cnt++;
0181:                 }
0182:             }
0183:         }
0184:     }
0185: 
0186:     return $cnt;
0187: }

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

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

0189: /**
0190:  * WeatherHacks API から必要な情報を配列に格納する
0191:  * @param array $items  情報を格納する配列
0192:  * @param int $city     地域別に定義されたID番号
0193:  * @param string $day   リクエストする予報日(today/tomorrow/dayaftertomorrow)
0194:  * @return int ヒット数
0195: */
0196: function getResults(&$items$city$day) {
0197: //受信データの要素名
0198: $tbl = array(
0199:     'location',        //予報を発表した地域
0200:     'title',        //タイトル
0201:     'link',            //リクエストされたデータの地域に該当する
0202:                     //livedoor 天気情報のURL 
0203:     'forecastday',    //予報日(today,tomorrow,dayaftertomorrowの3種)
0204:     'day',            //曜日
0205:     'forecastdate',    //予報日
0206:     'publictime',    //予報の発表日時
0207:     'telop',        //天気(晴れ、曇り、雨など)
0208:     'description',    //天気概況文
0209:     'image',        //タグ名 内容
0210:     'temperature',    //
0211:     'pinpoint',        //ピンポイント天気予報
0212:     'copyright'     //著作権情報
0213: );
0214: 
0215:     $url = getURL($city$day);      //リクエストURL
0216: 
0217: //PHP4用; DOM XML利用
0218:     if (isphp5() == FALSE) {
0219:         if (($dom = read_xml($url)) == NULL)    return FALSE;
0220:         $results = $dom->get_elements_by_tagname("lwws");
0221:         $cnt = 1;
0222:         //検索結果取りだし
0223:         foreach ($results as $element) {
0224:             foreach ($tbl as $name) {
0225:                 $node = $element->get_elements_by_tagname($name);
0226:                 if ($node != NULL) {
0227:                     switch ($name) {
0228:                         case 'image':
0229:                             $n2 = $node[0]->get_elements_by_tagname('title');
0230:                             $items[$cnt][$name]['title'] = $n2[0]->get_content();
0231:                             $n2 = $node[0]->get_elements_by_tagname('link');
0232:                             $items[$cnt][$name]['link'] = $n2[0]->get_content();
0233:                             $n2 = $node[0]->get_elements_by_tagname('url');
0234:                             $items[$cnt][$name]['url'] = $n2[0]->get_content();
0235:                             $n2 = $node[0]->get_elements_by_tagname('width');
0236:                             $items[$cnt][$name]['width'] = $n2[0]->get_content();
0237:                             $n2 = $node[0]->get_elements_by_tagname('height');
0238:                             $items[$cnt][$name]['height'] = $n2[0]->get_content();
0239:                             break;
0240:                         default:
0241:                             $items[$cnt][$name] = utf82sjis($node[0]->get_content());
0242:                     }
0243:                 }
0244:             }
0245:             $cnt++;
0246:         }
0247: 
0248: //PHP5用; SimpleXML利用
0249:     } else {
0250:         $lwws = simplexml_load_file($url);
0251:         //レスポンス・チェック
0252:         if (isset($lwws->image) == FALSE)    return FALSE;
0253:         $cnt = 1;
0254:         //検索結果取りだし
0255:         foreach ($tbl as $name) {
0256:             if (isset($lwws->$name)) {
0257:                 switch ($name) {
0258:                     case 'image':
0259:                         $items[$cnt][$name]['title']  = $lwws->image->title;
0260:                         $items[$cnt][$name]['link']   = $lwws->image->link;
0261:                         $items[$cnt][$name]['url']    = $lwws->image->url;
0262:                         $items[$cnt][$name]['width']  = $lwws->image->width;
0263:                         $items[$cnt][$name]['height'] = $lwws->image->height;
0264:                         break;
0265:                     default:
0266:                         $items[$cnt][$name] = utf82sjis($lwws->$name);
0267:                 }
0268:             }
0269:         }
0270:     }
0271: 
0272:     return $cnt;
0273: }

お詫びと訂正

2009 年 11 月 1 日時点で、サンプル・プログラムが明日の天気を表示しない(16 時以降には表示するようになる)というバグが見つかりました。ご指摘いただいた あんどー 様、ありがとうございます。
space
調べたところ、WebAPI から渡されるデータの中で、明日の最高気温が空白になっていました(ただし 16 時以降になると正常に記入されている)。最高気温が空白になっていると、その日が存在しないと判断していたのが原因でした。
そこで、最高気温が空白でも天気予報データだけを取り出すようにプログラムを修正し、バージョン 1.1 としてリリースしました。ダウンロードしてご利用ください。
space
また、WebAPI の提供元には、この問題を報告をしておきました。

参考書籍

参考サイト

(この項おわり)
header