JavaScriptの習作。Yahooの住所ディレクトリAPIと気象情報APIを利用して一時間後の予想降水量を調べるWEBアプリのサンプルを作ってみました。

Yahoo デベロッパーネットワーク というYahooが開発/提供しているツール群があります。その中のに全国の市町村名を取得することができるAPIと、予想降水量を取得できるAPIがあるのですが、それを利用して指定した地域の一時間後の予想降水量を取得できるWEBアプリのサンプルを練習がてら作ってみました。

アプリにアクセスしたりクリックイベントが発火するとJavaScriptのgetJSONからproxy_rest.phpにデータが渡され、そのデータに基づいて各種APIからデータを取り出し、htmlに表示する、という流れで動作します。

上記のデモページにアクセスすると、select要素の中に都道府県が入っています。loadイベント実行時に、getJSONを使ってAPIから都道府県のデータを取得して表示します。

select要素から任意の選択肢を選ぶと自動的にchangeイベントが発火し、市町村名を五十音順でフィルタリングしたボタンが表示されます。このボタンはjsファイルに記述してある連想配列と、前段階で取得しているAPIから得たデータを組み合わせて生成しています。

同時に戻るボタンも出ます。文字通りひとつ前の段階に戻る機能を持っていますが、やっていることはhtml要素にclassをつけたり外したりしてCSS3のtransitionを利用してウィンドウの外に追い出したり引き入れたりして画面表示を変更しているだけです。

五十音ボタンをクリックすると、属する市町村名ボタンが出ます。こちらもAPIからデータを取得して生成しています。次の段階で利用する緯度経度の値をdata属性として持たせています。

最後に市町村名ボタンをクリックすると、一時間後の予想降水量が表示されます。ボタンの中に入れてある緯度経度の値をAPIに渡してデータを取得することにより実現してます。

と、文章で書いてもなかなか伝わりませよね。コードを掲載してみます。

JavaScript

JavaScript初学者が作った学習用のコードのため、間違いや非効率なコードが含まれている可能性があります。ご了承ください。ご指摘、ご助言いただけますと非常にありがたいです!コメント欄をご利用ください。
//script.js

jQuery(function() {
/**
 * API設定
 */

//name space
var gVar = gVar || {};

//JSON取得用URLを切り替えるワード
gVar.changingWord = {
	country : 'country',
	city : 'city',
	geometry: 'geometry'
};

//市区町村 絞込 リクエストパラメーター 頭文字コード
gVar.cityFiltering = [
	{
		code: 'a',
		codeVal: 'あ行',
	} , {
		code : 'k',
		codeVal : 'か行、が行'
	} , {
		code: 's',
		codeVal: 'さ行、ざ行',
	} , {
		code: 't',
		codeVal: 'た行、だ行',
	} , {
		code: 'n',
		codeVal: 'な行',
	} , {
		code: 'h',
		codeVal: 'は行、ば行、ぱ行',
	} , {
		code: 's',
		codeVal: 'ま行',
	} , {
		code: 'y',
		codeVal: 'や行',
	} , {
		code: 'r',
		codeVal: 'ら行',
	} , {
		code: 'w',
		codeVal: 'わ行',
	}
];

/**
 * 都道府県名表示
 */
 (function(){

	jQuery(window).one('load',function(){

		jQuery.getJSON('proxy_rest.php',{changingWord:gVar.changingWord.country})
		.done(function(data){

			jQuery('.country select').append('');

			for(var i = 0; i < data.ResultInfo.Count; i++ ){
				var val = {
					name : data.Feature[0].Property.AddressDirectory[i].Name,
					AreaCode : data.Feature[0].Property.AddressDirectory[i].AreaCode
				}
				jQuery('.country select').append('');
			}
		})
		.fail(function(){
			window.alert('失敗');
		});

	});

 }());


/**
 * 市区町村名50音 表示
 */
(function(){

	jQuery(document).on('change','.country select', function(){
		jQuery('.city_filtering_body').empty();
		var $cityName = jQuery('.country select option:selected').text();
		var $areaCode = jQuery(this).val();
		for(var i = 0; i <= gVar.cityFiltering.length -1; ++i){
			jQuery('.city_filtering_body').append('');
		}
		removeOut('.city_filtering');
		addOut('.country');
		setName('.city_filtering_body',$cityName);
		removeNone('.controller_city_filtering');
	});

}());


/**
 * 市区町村名/座標情報(緯度経度) 表示
 */
 (function(){

 	jQuery(document).on('click','.city_filtering_body button',function(){

 		var cityfiltering_code = jQuery(this).attr('data-cityfiltering_code');

		jQuery.getJSON('proxy_rest.php',{
			changingWord:gVar.changingWord.city,
			cityFilteringCode:cityfiltering_code
		})
		.done(function(data){
			jQuery('.city_body').empty();
			for(var i = 0; i < data.ResultInfo.Count; i++ ){
				var val = {
					name : data.Feature[0].Property.AddressDirectory[i].Name,
					coordinates : data.Feature[0].Property.AddressDirectory[i].Geometry.Coordinates
				}
				jQuery('.city_body').append('');
			}
			removeOut('.city');
			addOut('.city_filtering');
			removeNone('.controller_city');
			removeNone('.city_body');
			addNone('.controller_city_filtering');
		})
		.fail(function(){
			window.alert('失敗');
		});

	});

}());


/**
 * 天気情報 表示
 */
(function(){
	jQuery(document).on('click','.city_body button',function(){

		var dataCoordinates = jQuery(this).attr('data-coordinates');
		addNone('.city_body');

		jQuery.getJSON('proxy_rest.php',{
			changingWord:gVar.changingWord.geometry,
			coordinates:dataCoordinates
		})
		.done(function(data){
			jQuery('.weather_body').empty();
			var val = {
				weather :data.Feature[0].Property.WeatherList.Weather[6].Rainfall
			}
			jQuery('.weather_body').append('

1時間後の予想降水量
' + val.weather + '
mm/h

'); removeOut('.weather'); addOut('.city'); removeNone('.controller_weather'); addNone('.controller_city'); }) .fail(function(){ window.alert('失敗'); }); }); }()); /** * リターン/クローズボタン */ jQuery('.controller_city_filtering button[name=return]').on('click',function(){ selectClear('.country select'); addOut('.city_filtering'); removeOut('.country'); addNone('.controller_city_filtering'); }) jQuery('.controller_city button[name=return]').on('click',function(){ addOut('.city'); removeOut('.city_filtering'); addNone('.controller_city'); removeNone('.controller_city_filtering'); jQuery('.city_body').empty(); }) jQuery('.controller_weather button[name=return]').on('click',function(){ removeNone('.city_body'); addOut('.weather'); removeOut('.city'); addNone('.controller_weather'); removeNone('.controller_city'); }) jQuery('.controller button[name=close]').on('click',function(){ removeOut('.country'); addOut('.city'); addOut('.weather'); addNone('.controller_city_filtering'); addNone('.controller_city'); addNone('.controller_weather'); selectClear('.country select'); jQuery('.city_body').empty(); }) /** functions */ function setName($name,$elm){ jQuery($name).prepend('

' + $elm + '

'); } function removeOut($elm){ jQuery($elm).removeClass('out'); } function addOut($elm){ jQuery($elm).addClass('out'); } function removeNone($elm){ jQuery($elm).removeClass('none'); } function addNone($elm){ jQuery($elm).addClass('none'); } function selectClear($elm){ jQuery($elm).each(function() { this.selectedIndex = 0; }); } });

PHP

<?php

//proxy_rest.php

mb_http_output('utf-8');
mb_internal_encoding('utf-8');
header('Content=Type: text/xml;charset=UTF-8');

/**
 * 受信したデータを判定し、JSONを取得するURL生成メソッドを切り替えて呼び出す
 * changingWordはURLを切り替えるために使う
 */
if (!isset($_GET["changingWord"]) || $_GET["changingWord"] === ''){

	echo 'no data';

} else {

	$json = new createJson();
	$changingWord = $_GET["changingWord"];

	if ($_GET["changingWord"] === 'country') {

		//都道府県名をリクエスト
		$json->responseData($changingWord);

	} else if ($_GET["changingWord"] === 'city') {

		//市区町村名(五十音順)をリクエスト
		if (isset($_GET["cityFilteringCode"])){
			$json->responseData($changingWord,$_GET["cityFilteringCode"]);
		}

	} else if ($_GET["changingWord"] === 'geometry') {

		//天気をリクエスト
		if (isset($_GET["coordinates"])){
			$json->responseData($changingWord,$_GET["coordinates"]);
		}

	}

}

/**
 * APIからJSON形式のデータを取得/表示する
 */
class createJson{

	//API URL 住所ディリクトリAPI
	private $url_dir     = 'http://search.olp.yahooapis.jp/OpenLocalPlatform/V1/addressDirectory';

	//API URL 気象情報API
	private $url_weather = 'http://weather.olp.yahooapis.jp/v1/place';
	
	//アプリケーションID
	private $appid       = ***; //Yahooより取得したアプリケーションIDを入力する

	//出力形式
	private $output      = 'json';

	//住所コード 地域名を表現するリテラルを格納する
	private $ac;

	//緯度経度を表す数値を格納する
	private $cd;

	//url生成/返却データ表示
	function responseData($word,$setVal = false){

		//条件に応じたリクエストを呼び出す
		if ($word == 'country') {

			//都道府県名をリクエスト
			$url = $this->requestUrl_country();

		} else if ($word == 'city') {

			//住所コードをセット
			$this->ac = $setVal;

			//市区町村名(五十音順)をリクエスト
 			$url = $this->requestUrl_city();

		} else if ($word == 'geometry') {

			//緯度経度をセット
			$this->cd = $setVal;

			//天気をリクエスト
			$url = $this->requestUrl_weather();

		}

		//返却されたjsonデータを表示
		echo file_get_contents($url);
		
	}

	function requestUrl_country(){
		return $this->url_dir."?appid=".$this->appid."&output=".$this->output."&ac=JP&callBack=?";
	}

	function requestUrl_city(){
		return $this->url_dir."?appid=".$this->appid."&output=".$this->output."&ac=".$this->ac."&mode=1"."&callBack=?";
	}

	function requestUrl_weather(){
		//"http://weather.olp.yahooapis.jp/v1/place?coordinates=139.732293,35.663613&output=json&appid=dj0zaiZpPUpUR2x2UG04azBPbiZzPWNvbnN1bWVyc2VjcmV0Jng9ZmE-&callBack"
		return $this->url_weather."?appid=".$this->appid."&output=".$this->output."&coordinates=".$this->cd."&callBack";
	}

} //createJson end

Html

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<link href="style.css" rel="stylesheet" type="text/css" media="all" />
<script src="http://code.jquery.com/jquery-2.2.3.min.js"></script>
<script src="js/custom.js"></script>
</head>

<body>

    <div class="controller">
        <div class="controller_city_filtering none">
            <button name="return" class="btn">戻る</button>
        </div>
        <div class="controller_city none">
            <button name="return" class="btn">戻る</button>
            <button name="close" class="btn">閉じる</button>
        </div>
        <div class="controller_weather none">
            <button name="return" class="btn">戻る</button>
            <button name="close" class="btn">閉じる</button>
        </div>        
    </div>

    <div class="country">
        <div  class="country_inner">
            <h1 class="title">一時間後の予想降水量</h1>
            <div class="country_body"><select></select></div>        
        </div>
    </div>

    <div class="city_filtering out">
        <div class="city_filtering_body"></div>
    </div>

    <div class="city out">
        <div class="city_body"></div>
    </div>

    <div class="weather out">
        <div class="weather_body"></div>
    </div>


</body>
</html>

まとめ

一部例外処理を省略してはいますが、概ね期待した動作をしています。コードは割りとシンプルに書けたかな、とは思いますが、いかんせん経験不足なためコードの良し悪しを自己評価するのが難しいです。良くないところとか改善できるところがたくさんあるのだろうとは思うのですが、もうこれは経験を積み重ねた後に解ることなので精進あるのみですね。

もっと機能を追加する場合は、APIから取得したデータをhtmlに埋め込むのではなく、配列を作ってキャッシュし、必要に応じてデータを再利用しやすくするような感じがいいのでしょうかね。

たとえば、複数の地域の降水量情報を並列で表示して、一方のデータのみ切り替えて比較するような動作とか、降水量は一時間おきだけじゃなく、2時間後、3時間後とかもたしかとれるはずなので平均値を求めたり、まぁいろいろあると思います。

そうなるとAngularJSとかVue.jsとかMVCフレームワークの出番があるのかなーとか思います。想像していると楽しくなってきますね。そういったフレームワークも機会があれば勉強してみたいと思います。

今後の予定は、さしあたってアニメーションに特化したjsライブラリをやってみようかなと検討しています。候補にあるのがTweenMaxというやつ。なんか便利そう。p5.jsもいいらしい。いろいろ試してみようと思います。

コメントをどうぞ!