天気情報を表示する1(基本編)

ウィジェットの定番の1つに「天気予報」があります。
しかし、海外のウィジェットしか見たことがありません。
そのせいなのか、デフォルト地点は海外しか見たことがなく、天気の表示も見慣れたパターンしかありません。
せっかく、ウィジェットを作ることを紹介しているので、天気予報もやろうと思います。

天気情報の取得にもいろいろありますが、取得した天気情報によって、表示のレイアウトもいろいろ考えることができます。(考えなければなりません)
ここでは、天気情報の取得方法をメインに紹介します。

天気情報を取得する

天気情報を取得する方法はいくつかありますが、ここでは天気情報を配信しているサーバーからXMLを取得し、表示する方法を紹介します。
個人的な判断ですが、このやり方を「基本」とします。

Webサイトではピンポイント天気まで表示できるのに、情報配信は主要都市までというところがほとんどですので、ピンポイント天気を開いた状態のWebサイトから、直接情報を取得するやり方もどこかで紹介します。

いきなり取得してみる

早速、天気情報を取得してみましょう。
当サイト「都市コード」を参考にして、パソコンのブラウザで、以下のURLを開いて下さい。

http://weather.yahooapis.com/forecastrss?p=JAXX0005&u=c

北海道旭川市の天気情報が表示されましたでしょうか?
お分かりかと思いますが、Yahooウェザーの天気情報で、温度を摂氏で指定して取得しています。
URLに、都市コード「JAXX0005」を指定していますが、都市コードの部分はお好みで変更して下さい。

ただし、その表示方法はブラウザによって違います
スクリーンショットは、FireFoxによる表示です。
それこそ、「天気情報の取得先によって情報が違う」という事実もあります。
情報が変われば、表示方法も変わりますので、天気情報表示ウィジェットを進めながら対応していきます。

取得した情報を見てみる

天気情報なるものが取得できることは分かりました。
そして、その情報をブラウザで確認することもできました。
ただ、情報自体はどうなってるのでしょうか?
では、先ほどの情報をGoogle Chromeで表示させてみましょう。
FireFoxでの表示とは違い、文字だらけのデータっぽい表示にはなったものの、文字化けしている感じのデータではなく、何となく整形されているように見えますね。
データ構造を理解すれば、人間が読み取ることも容易い内容かと思います。
これがXMLですので、今後のためにイメージを掴んでおいて下さい。

ここで、取得した天気情報をそのまま表示するサンプルを作ってみました。
ダウンロードしてLockBackground.htmlを開いてみて下さい。
ダウンロードはこちら ー> wf_11のダウンロード

先ほどのChromeによる表示とそれほど変化がある訳ではありませんが、Safariで表示したスクリーンショットの一部を紹介しておきます。
取得したデータを文字列として表示しますので、RSSの表示ができなくなったSafari6でも表示されます。
もちろんiPhoneでも見ることができますが、iFileなどのファイラーで表示させるか、テーマとしてWinterboardで適用するとロック画面に表示されるファイル構造になっております。
ブラウザ表示はこちら ー> wf_11の表示

情報提供元によって、XMLの構造に違いはあるものの、データを見ることで、何となくでも分かることができれば、それでOKです。

取得方法を知る

さきほどのサンプルで、取得した天気情報XMLをそのまま表示しましたが、その中の取得方法を抜き出して説明します。個別で調べたい方は「ajax」でググって下さい。

LockBackground.htmlには、"data"というidを持つpタグを用意しました。
読み込み後に、JavaScriptファイルに記述してあるsetWeatherという関数を実行します。
HTMLファイルはこれくらいです。

では、JavaScriptファイル(script.js)の、setWeather()を見て行きましょう。

var url="http://weather.yahooapis.com/forecastrss?p=JAXX0005&u=c";

urlという文字列変数に、いきなり取得したURLを入れています。
後から出てきますが、情報を要求する行がやたら長くなってしまうので、その対応にもなりますし、都市コードを書き換える場所が分かりやすくなるという利点もあり、このようにしました。

var httpRequest = new XMLHttpRequest();

このXMLHttpRequest()が、今回の最大の情報です。
XMLHttpRequest は、サーバとの通信を行うための組み込みオブジェクトですので、これを使って、サーバーから情報を得ることにします。
すでに、天気情報を表示する何らかのウィジェットを使っている方は、ソースを眺めてみて下さい。ソースのどこかに XMLHttpRequest があると思います。
今回は、このオブジェクトを httpRequest に格納しているので、この先は httpRequest に対して処理をします。

httpRequest.open("GET", url);

urlで指定されているサーバーURLに対し、リクエストの初期化・設定を行ないます。
今回は、データの取得が目的なので、"GET"としていますが、ここではまだ、サーバーに対するリクエストは行いません。

httpRequest.overrideMimeType("text/xml");

サーバーから返ってくるMIMEタイプを強制的にtext/xmlとします。
サーバーがどのようなMIMEタイプで返すか分からなくても、このように記述しておけば安心できるため、私はこうしています。

httpRequest.setRequestHeader('Cache-Control', 'no-cache');

ブラウザ内のキャッシュデータが取得されてしまい、最新の情報が取得できない可能性があるため、このような記述をし、キャッシュ読み込みを回避します。

httpRequest.send(null);

これまでは設定でしたが、ここにきてやっとサーバーにリクエストを送信します。
情報を得るだけなので、ここで送信するデータはありません。
サンプルではnullを入れていますが、空文字列の""でも構いません。

httpRequest.onload = function() {xml_responded(httpRequest);}

今回は、取得したデータをそのまま表示することにしましたので、レスポンスデータの読込みが完了した時に、データを表示する関数を呼び出します。

function xml_responded (request) {
 var resData = request.responseText;
 document.getElementById("data").innerText = resData;
}

こちらが、その関数です。
読み込みが完了してから実行される関数なので、データがあるかどうか関係なく処理をします。
リクエストによって得られたレスポンスをテキスト形式で取得しますが、一度resDataに格納します。
"data"というidを持つエレメントのテキストを、ごっそりresDataと入れ替えます。
入れ替えると言っても、もともと文字列が入っていない"data"ですので、「テキスト形式で得たデータを、エレメント"data"を使って表示する」と理解した方が簡単だと思います。

取得データから抜き出す

取得したデータは、XMLと言われるもので、冒頭の「いきなり取得してみる」で取得したような形式のテキストデータです。
このデータは、記述方法が決まっていますので、そのルールが分かれば簡単に読み取ることができます。
ただ、あなたが「読み取る」ことと、プログラミング上において「読み取る」ということは、まったく違います。
理解したことを、どのように「プログラム」という形にするかを紹介していきます。

より理解しやすい形にする

これは、あくまでも頭の中でやることであって、実際に形を変える訳ではありません。
これまで作ってきたサンプルで意識してきたことなのですが、後で見直しても分かりやすい記述をしてきたつもりです。
その筆頭に挙げられる方法が「インデント」と言われるもので、構造(階層が)一目で理解することができます。
XMLはテキストデータですが、HTMLと同様にタグを利用して記述されています。
タグで記述されているということは、「入れ子」が存在することを意識しなければなりません。
しかし、実際に取得したYahooウェザーのXMLを見て、「入れ子」になっていることの分かる人がどれほどいるでしょうか?
私は、このページを作成するためにXMLを見直して始めて気付きました。

<?xml ... >
<rss ... >
 <channel>
  <title>Yahoo! Weather - ... </title>
  <link>http://us ... </link>
   ・
   ・
   ・
  <image>
   <title>Yahoo! Weather</title>
   <width>142</width>
   <height>18</height>
     ・
     ・
     ・
  </image>
  <item>
   <title>Conditions for Asahikawa ... </title>
   <geo:lat>43.77</geo:lat>
     ・
     ・
   <yweather:condition text= ... />
     ・
     ・
  </item>
 </channel>
</rss>

すべてを記載するのは場所を取るだけなので、「入れ子」になっていることが分かるような書き方で紹介します。
取得したデータ構造が分かっていると、その後が楽になりますので、構造用のテキストファイルを新規で作るくらいしても良いと思います。

私は、ブラウザーに表示されたXMLをコピーし、テキストエディターで新たにファイルを作成しています。
頭の中でできる方は、ここまでする必要はありません。

Google Chromeで表示させたものが見やすく、理解しやすいかもしれませんね。

抜き出してみる

XMLを眺めることで、何となくでも構いませんが、「目的の情報はここっぽい」くらいの目処は立つと思います。
ここでは、その目的の情報まで、どのように辿っているかを考えます。
そもそも、タグを利用して記述されているのですから、さきほどの「インデント」で見やすくした抜粋XMLでピンと来る方も多いと思います。
もう答えは出ているのですが、「タグを辿る」のですね!
上から○番目のタグの中の、上から□番目のタグの中の、上から△番目のタグの中にあるテキスト」という具合に辿れば、誰でも同じ情報に辿り着けますよね?
これは、私と同じようにやっていただき、同じような結果が出れば納得していただけることなので、ぜひ同じようにやっていただきたいと思います。

では、「タグを辿る」には、どのような記述でプログラミングすれば良いのでしょうか。
入れ子」というキーワードを思い出して下さい。これが、答えに直結しています。
さきほどの抜粋XMLを、家系図と思って下さい。(段差の所だけ)
○番目の子供の□番目の子供の△番目の子供の名前」という感じに、さきほどのイメージを置き換えることができれば、プログラミングも簡単です。
なぜなら、"child"を使ってプログラミングしていくからです。

では、実際のプログラミングではどのように記述するかを紹介します。
ものすごくシンプルなのですが、1番目の子供は、「childNodes(0)」と表します。
プログラミングの基本なのですが、数字は0から始まるものと思って下さい。
ですから、childNodes(2)という記述があれば、それは3番目を指しています。
「ノード」は要素と理解して結構です。
そして、「タグを辿る」というのは、プログラミング的な言葉にすると、「子ノード」を探索するという感じで良いと思います。
この先、「ノード」という言葉が多く出てくると思いますが、文書構造を示すものはすべて「ノード」と呼びますので、覚えておいて下さい。

家系図であれば、きっとアダムとイブから辿らなければならないのかもしれませんが(大笑)、XMLはそんな大袈裟に考える必要はありません。
今回は、天気情報をXMLで取得しているので、サンプルで紹介した記述のまま紹介すると、

request.responseXML.childNodes(0).childNodes(1).childNodes(2)

このように書くことで、取得した情報の、1番目のタグの中の、2番目のタグの中の、3番目のタグを示しています。
では、Yahooウェザーのタグを辿って、目的の天気を見つけましょう。
さきほどの抜粋XMLで、色の変わっている部分を探して下さい。
ただ、XMLの最初にある "<?XML ... " は、XMLであることの宣言なので、タグとしては数えません

では、一緒に辿りましょう!
1番目の<rss...の中の、1番目の<channel>の中の、13番目の<item>の中の、6番目のタグが、<yweather:condition text="... になっていますでしょうか?
目的のタグでは、「text」「code」「temp」などが入っていますので、この中の特定のものを抜き出すには、getAttributeを使います。
ここで、実際に数えて辿り着いた情報までの道のりを、childNodesで表現すると、

XML.childNodes(0).childNodes(0).childNodes(12).childNodes(5).getAttribute("text")

このようになります。
便宜上、XMLから辿っているように書きましたが、数字は0から数えますので、1番目が0になることに注意して下さい。
この記述で、都市コードで指定した都市の現在の天気情報が文字で取得できます
これを組み込んだサンプルを用意しました。
ダウンロードはこちら ー> wf_12のダウンロード
ブラウザ表示はこちら ー> wf_12の表示

ダウンロードしたフォルダ内のLockBackground.htmlをブラウザで開いて下さい。
現在の天気が表示されていますでしょうか?

ん?? 表示されない??
ホントだねぇ・・・

ちょっと調べてみましょうか。
興味のある方は、Safariの環境設定の詳細から、「メニューバーに"開発"メニューを表示」のチェックを入れて下さい。
メニューに「開発」が出ますので、そこから「Webインスペクタを表示」を選択します。
では、LockBackground.htmlを、Safariで開いてみましょう。
表示されませんね・・・

では、この状態のまま、WebインスペクタでJavaScriptの動きを見てみましょう。
図のように、リソースを選択し、script.jsを選択します。

エラーこそ出ていませんが、13行目で何か気に入らないみたいですね。

では、この13行目でプログラムを止めるために、行番号を選択して青色の印を残し、ページを更新します。
図のようになるはずですので、確認して下さい。

プログラムが13行目で一時停止している状態です。
この行の何かが気に入らないようなので、weatherに代入するデータ部分を表示してみます。

下の ">" 部分に、request以下childNodes[5]まで入力(コピー)し、その内容を表示させてみました。

"TypeError"と出ていますね。
その後の、"'undefined' is not an object" とあるので、childNodes[12]で取得した内容が定義されていないということですので、childNodes[5]が "TypeError" になっているのでしょうね。

では、同様の手法で、今度はchildNodes[12]までを入力し、表示させてみます。

"undefined"と出ていますね。
これを確認したかったので、出てくれなければ逆に困りますね。

では、同様の手法で、今度はchildNodes[0]までを入力し、表示させてみます。

今度は、"Text"と出ました。
これまでの内容のないものではないので、ここまでなら取得できていそうな気がしますが、欲しいのはテキストではなくエレメントなので、これも取得できていないという判断をしないといけません。

では、もう1つ前のchildNodes[0]までを入力し、表示させてみます。

今度は、XMLが表示されていますね。
これぞ求めていたエレメントですので、きちんと取得できているのはここまでということになります。
ここからならきちんと取得できるということですので、確認しながら進めましょう。

responseXML以下、childNodesを辿ります。
ポイントとなる部分に緑線を引いておきました。
1つ目のchildNodesは、その0番目がエレメントであることが分かります。
このエレメントが、<rss・・・の部分です。
ここまでは、きちんと取得できていましたね。

問題は、2つ目のchildNodesです。
その0番目はテキストになっていることが分かります。
さきほど確認したテキストですね。
よく見ると、テキストの次にエレメントがあり、そのまた次にテキストがあります。
エレメントだけ並んでいれば、当初の記述で上手くできていたはずなのですが、このテキストのおかげで順番がズレているんですね。
ちょうどこのchildNodesには、エレメントが1つしかありませんので、このエレメントが求めるエレメントと見なしても良さそうですね。
このエレメントが、<channel>の部分です。
そして、その求め方は、request.responseXML.childNodes[0].childNodes[1] となります。
XMLの表示ではタグを辿りましたが、プログラミングにあたっては、このようにエレメントを辿ることがお分かりいただければ結構です。(やっていることは、タグもエレメントも同じです)
では、確認してみましょう。

思いっきり、<channel>タグの部分が抜き出せていますね。
この調子で3番目に進みましょう。

次は、13番目にある<item>なので、childNodes[12]としましたが、その前できちんと取得できていなかったので、この記述が正解なのかも分かりません。
ただ、2番目の<channel>を見つけようとしたとき、テキストのおかげで順番がズレたので、今回もその可能性があります。

そこで、さきほどの<channel>タグ、つまりchildNodes[1]の中身をさきほどと同様に見てみます。

すると、どうでしょう・・・

思いっきり交互に並んどるやんけ!!

これでは、順番がズレるはずですね・・・

まぁ、逆に、キレイに並んでくれているので、数え直すにはちょうど良いのかもしれませんね。
という訳で、13番目のエレメントが、実際には何番目なのか数えてみると、25番目でした。
実際に数えなくても、計算で求めることができます。
テキストとエレメントが交互に出現するので、2倍すれば良いことはお分かりいただけると思います。
ここでもそうなのですが、数字は0から始まりますので、1を引くことが重要です。
求めるエレメント<item>は13番目でしたので、2倍して1引くと25になりますね。
したがって、request.responseXML.childNodes[0].childNodes[1].childNodes[25]というのが、3番目までの求め方を修正した記述です。
これも確認しましょう。

こちらも、きちんと<item>タグの部分が抜き出せていますね。
このまま4番目に突入しましょう。

やり方は同じですので、さきほどの25番目の中身を見てみましょう。

やはり、テキストとエレメントが交互に並んでいますね。
では、こちらも先ほどと同様に、数えましょう。

次に求めるエレメントは、<yweather:condition text="... です。
これは、6番目ですので、2倍して1引くと、11になりますね。
したがって、childNodes[11]で次の求めるエレメントが取得できるはずです。


これも、きちんと取得できました。
ご自身で書き換えた方が早いかもしれませんが、このイレギュラーに対応したサンプルを用意しました。
ダウンロードはこちら ー> wf_13のダウンロード

では、LockBackground.htmlを、Safariで開いてみて下さい。
天気情報がテキストで表示されているはずです。
もちろん英語ですが・・・

念のため、iPhoneのロック画面で表示したスクリーンショットです。
白地に黒で表示させているのですが、ステータスバーがあるので、文字が目立ちにくくなっています。
フォントサイズを指定していないので、小さいままの表示なのですが、左上に表示されているのがお分かり頂けますでしょうか?
今回は、天気情報を抜き出すことが目的ですので、この状態でOKとします。

それにしても、Yahooウェザーで現在はSnow Showerという天気なんですね。
吹雪いてるのか!?

ページトップへ

2013/04/17