俳句保存アプリの解説とソースコード

前回作ったHTML5+jQueryMobile1.4.5で俳句保存アプリ(= Haiker)の解説です。

HTML5+jQueryMobile1.4.5で俳句保存アプリ
わたしは俳句を詠むことが好きだ。 スマートフォンを使って詠んだ俳句と場所を保存するアプリを作った。 俳句を作る人という意味をもじって、タイトルは「Haiker」 写真をタップしてHaikerに移動

ソースコード一式はこちら

写真をタップしてHaikerに移動
2015-10-14 at 21.47.11

最初のバージョンはjQueryMobile1.3で作ってあったのですが、jQueryMobileを最新版の1.4.5にしたら画面はめちゃくちゃ、プログラムもページ遷移が関わる部分はほぼ動きませんでした。

ページ遷移によるイベント処理部分(jQueryMobile1.4.5)

Haikerでは、保存した俳句がリスト表示されます。

2015-10-14 at 21.55.32

俳句部分をタップすると、Googleマップ上に俳句が吹き出し表示されます。つまりはページ遷移と同時にGoogleマップを表示するプログラムを呼び出しているわけです。

jQuery1.3で作ったバージョンでは、pageshowを用いて動作していました。
こんな感じ。

/*
 * イベント処理
 */
$(document).on('pageshow', '#pgIndex', updateHaikuList);		// トップページが表示されたとき
$(document).on('pageshow', '#pgHaikuMap', drawGoogleMap);		// 俳句マップページが表示されたとき
$(document).on('pageshow', '#pgReadWrite', readWriteHaiku);			// 俳句読み書きページが表示されたとき
$(document).on('pageshow', '#pgInfo', drawGoogleMap);			// 情報ページが表示されたとき

jQuery1.4.5でのバージョンでは、pagecontainershowイベントが推奨されているのですが、単純にpageshow部分をpagecontainershowに変更してもうまくいきません。

まずはじめにpagecontainershowイベント時の処理として、移動先のページID(HTMLのid指定)を取得します。

// 現在表示されているページID(#xxxx)を取得
var thisPage = $(":mobile-pagecontainer").pagecontainer("getActivePage").attr("id");

ページIDを取得後、各ページ毎の処理に分岐させるとすっきりと記述できました。(この部分は英語ページhttp://stackoverflow.com/questions/21801315/jqm-1-4-1-the-new-event-pagecontainershowを参考にしました)

// 各ページが表示されたときの処理
$(document).on("pagecontainershow", function(event, ui){
	// 現在表示されているページID(#xxxx)を取得
	var thisPage = $(":mobile-pagecontainer").pagecontainer("getActivePage").attr("id");
	console.log(thisPage);
	
	if(thisPage == "pgIndex"){			/* トップページ */
		updateHaikuList();
	}
	else if(thisPage == "pgHaikuMap"){	/* 俳句マップページ */
		drawGoogleMap();
	}
	else if(thisPage == "pgInfo"){			/* 情報ページ */
		drawGoogleMap();
	}
	else if(thisPage == "pgEdit"){			/* 修正ページ */
		editHaiku();
	}
});

この方が分かり易いです。

俳句保存部分(ローカルストレージを使用)

Haikerでは、JavaScriptでどこまで出来るかを考えてみました。
データの読み書きは、ローカルストレージ機能を使えば何とかなります。

保存するデータはカンマ区切りにしてあります。(日付、五、七、五、緯度、経度の順)

2015-10-14,夕焼けを,うけて景色と,同化する,33.2089216,128.9252061
2015-10-13,化粧する,彼女いつもと,違う人,34.39383,132.3716595
2015-10-12,満開の,桜も見ずに,カメラかな,35.2462876,136.7942462
2015-10-12,コンビニが,吸って吐き出す,人や物,35.703981,140.562797

上記データでは一句ごとに改行していますが、実際にローカルストレージに保存する時は、すべてカンマで区切って一つにまとめています。

2015-10-14,夕焼けを,うけて景色と,同化する,33.2089216,128.9252061,2015-10-13,化粧する,彼女いつもと,違う人,34.39383,132.3716595,2015-10-12,満開の,桜も見ずに,カメラかな,35.2462876,136.7942462,2015-10-12,コンビニが,吸って吐き出す,人や物,35.703981,140.562797

また、リスト表示した時に新しい俳句が一番上にくるようにするため、unshiftメソッドを使って配列の先頭に追加しています。

俳句書き込み処理

/*
 * 投句ボタンの処理(#pgIndex)
 */
function throwHaiku(){
	var allbody = $("#haiku").val();
	haiku = allbody.split("\n");		// 俳句を取り出す
	// データチェック
	if(allbody == "" || haiku.length < 3) return;		// データがない or 五七五になっていない
	for(var i=0; i<3; i++){						// 五七五が空データかどうか
		if(haiku[i] === "") return;
	}
	
	// 日時取得
	var date = getDate();
	
	// 保存用データ生成
	var savedata = date + SEP + haiku[0] + SEP + haiku[1] + SEP + haiku[2] + SEP + lat + SEP + lng;
	// 日時、俳句、位置情報をローカルストレージに保存する
	haikuData.unshift(savedata);				// 配列の先頭に追加
	var longHaikuData = haikuData.join(SEP);	// 配列haikuDataの要素を全てSEPでつなぎ一つの文字列にする
											// ローカルストレージに一括して保存するため
											
	localStorage.setItem(MYKEY, longHaikuData);	// ローカルストレージに保存
	$("#haiku").val("");						// 入力した俳句をクリア
	/* $("#haiku").focus();					スマートフォンでは使い勝手が悪いためコメントアウト */
	updateHaikuList();						// トップページの俳句リストを更新する
}

読み込む時は、カンマ区切りで配列に一旦取り出しておいて6つずつ(日付、五、七、五、緯度、経度)別の配列にpushしています。

俳句読み込み処理

/*
 * ローカルストレージの俳句データをチェックする
 */
function checkHaikuStorage(){
	var allbody = localStorage.getItem(MYKEY);
	if(allbody != null && allbody != ""){
		console.log(allbody);
		var columns = allbody.split(SEP);
		console.log("columns.length = " + columns.length);
		for(var i = 0; i<columns.length;i = i + 6){
			var s = columns[i] + SEP + columns[i+1] + SEP + columns[i+2] + SEP + columns[i+3] +SEP + columns[i+4] + SEP + columns[i+5];
			haikuData.push(s);
		}
	}
}

GoogleMap APIを使う

Googleが提供するGoogleMap APIは、緯度と経度、ズームレベルなどを指定すると、マップ上にマーカーと吹き出しを表示してくれます。吹き出し部分は文字ですが、aタグなどのHTMLタグも使用可能です。

HTMLファイルのhead部分にライブラリを読み込んで使います。

<!-- Google Map API -->
<script src="http://maps.google.com/maps/api/js?sensor=false&language=ja" charset="utf-8"></script>

地図を表示するエリアは、divタグにidをつければ大丈夫です。

<div id="haikuMap"></div>

ただしGoogleMap APIでは、表示エリアは必ず幅と高さを指定しておかないと何も表示されません。注意が必要です。
HaikerではJavaScript側で幅と高さを指定しています。もちろんCSSで指定可能です。

専用の関数を作りました。(俳句マップページと情報ページの二箇所でこの関数を使っているため分岐処理が長たらしいです。普通に作ればもっとすっきりするはずです)

/*

	gma.js : Google Map API用の関数

*/

var myMap = null;

function drawGoogleMap(){
	// 緯度、経度、吹き出しの文字を指定
	var lat, lng, haiku, mapId;
	if(haikuIndex != null){		// 俳句マップページでのマップ表示(詠んだ位置を表示)
		mapid = "haikuMap";
		var row = haikuData[haikuIndex].split(SEP);
		lat = row[4];
		lng = row[5];
		var haiku = "<p style='padding:0.2em;text-align:center'>" + row[1] + "<br>" + row[2] + "<br>" + row[3] + "</p>";
		haikuIndex = null;
	}
	else{					// 情報ページでのマップ表示(松尾芭蕉の句を表示するだけです)
		mapid = "infoMap";
		lat = 38.998281;
		lng = 141.113579;
		haiku = "<p style='padding:0.5em;text-align:center'>夏草や 兵どもが 夢の跡</p><p style='text-align:right'>芭蕉</p>";
	}
	// 地図のサイズを設定
	$('#' + mapid).css('width', screenWidth);	// 地図の幅
	$('#' + mapid).css('height', screenHeight/3*2-20);	// 地図の高さ
	
	// マップの初期化
	var mapId = document.getElementById(mapid);

	myMap = new google.maps.Map(mapId,{
		zoom: 12,										// ズームレベル
		center: new google.maps.LatLng(lat, lng),			// 中心点緯度経度
		scaleControl: true,								// 距離目盛の表示
		mapTypeId: google.maps.MapTypeId.ROADMAP		// 地図の種類
	});
	// マーカーの設定
	var myMarker = new google.maps.Marker({
		position: new google.maps.LatLng(lat, lng),	// マーカーの緯度経度
		map: myMap
	});
	// 吹き出しの設定
	var myInfoWindow = new google.maps.InfoWindow({
		content: haiku
	});
	// 吹き出しを表示
	myInfoWindow.open(myMap, myMarker);
	// 吹き出しが閉じられた場合、マーカークリックすると再度開く設定
	google.maps.event.addListener(myInfoWindow, "closeclick", function(){
		google.maps.event.addListenerOnce(myMarker, "click", function(event){
			myInfoWindow.open(myMap, myMarker);
		});
	});
}

GPS情報の取得(geolocation)

GPS情報は、navigator.geolocation.getCurrentPositionを使っています。
一度だけGPS情報を取得するだけなので、誤差が大きいときがあります。(実際に使ってみたら700mも離れた場所を示したときがあった)

精度を上げる対策としては、watchPositionを使った方法などがあるようですので、次回バージョンで試したいと思います。

/*

	gps.js: GPS情報を取得し緯度・経度を得る

*/

/*
 * グローバル変数
 */
// GPS用
var WP;				// 位置情報取得識別ID
var lat = null;		// 緯度
var lng = null;		// 経度

/*
 * 位置情報の取得に成功した場合(geolocation用関数)
 */
function SccCB(position){
	// 緯度の取得
	lat = position.coords.latitude;
	// 経度の取得
	lng = position.coords.longitude;
	// 位置情報取得を終了
	stopWP();
	// 緯度・経度表示
	alert("位置情報を更新しました");
	showGPS();
}

/*
 * 位置情報の取得に失敗した場合(geolocation用関数)
 */
function ErrCB(error){
	alert("位置情報を取得できませんでした");
	// GPS情報が取得できない場合、緯度と経度を固定(福島第一原発の場所)
	lat = 37.421063;
	lng = 141.032765;
	
	$("#gps_info").html("GPS情報を取得できません");
}

/*
 * 位置情報を更新(geolocation用関数)
 */
function updateWP(error){
	stopWP();
	// 端末の位置情報を取得する(watchPositionにて再作成予定)
	WP = navigator.geolocation.getCurrentPosition(SccCB, ErrCB, {enableHighAccuracy: true});
}

/*
 * 位置情報の監視を停止(geolocation用関数)
 */
function stopWP(error){
	navigator.geolocation.clearWatch(WP);
}

/*
 * GPS情報を整形して表示
 */
function showGPS(){
	var gpsInfo = "緯度: " + lat + " 経度: " + lng;
	$("#gps_info").html(gpsInfo);
}

リスト表示した部分をタップした時にインデックス番号を取り出す(jQueryMobile)

一番苦し紛れの処理がこれです。
俳句リストをタップすると俳句に対応した緯度と経度を配列から取得してグーグルマップを表示するのですが、リストの先頭をタップしたら「0」その次なら「1」とリストのインデックス番号を取得したいと考えました。イベント処理にまとめようとしてうまく動作しなかった為、リストを表示するときにHTML内にJavaScriptを埋め込んでチェックしています。

/*
 * トップページの俳句リストを更新する(#pgIndex)
 */
function updateHaikuList(){
	var lists = "";
	// 俳句データが空のとき
	if(haikuData == null || haikuData.length == 0){
		$("#haikuArea").html(lists).trigger("create");
		return;
	}
	
	lists += "<ul data-role='listview'>";
	lists += "<li data-role='list-divider'>句集</li>";
	
	// 俳句データから一句ずつ取り出してリスト表示
	for(var i in haikuData){
		var row = haikuData[i].split(SEP);	// 一行のデータ並び	row[0]~[5] = 日付 , 五 , 七 , 五 , 緯度 , 経度

		var url = "#pgHaikuMap";
		var midashi = "<h2>" + row[1] + "</h2>";
		var haiku = "<p>" + row[2] + " " + row[3] + "</p>";
		var date = "<p class='ui-li-aside'>" + row[0] + "</p>";

		var list = "<li class='haikulist' haikuIndex='" + i + "'><a href='" + url + "'>" + midashi + haiku + date + "</a></li>";
		lists += list;
	}
	lists += "</ul>";
	// 俳句リストタップ時にインデックス番号を取り出す処理
	lists += "<script>$('li').tap(function(){";
	lists += "haikuIndex = $(this).attr('haikuIndex');";
	lists += "});</script>";

	console.log(lists);
	// コンポーネントの書き換えをjQueryに通知する
	$("#haikuArea").html(lists).trigger("create");
}

この部分はもう少しなんとかなりそうです。

長かったですが、解説は以上になります。

バージョンアップとして「俳句を百句詠んだら、奥の細道みたいに大きな地図上に吹き出しで作った俳句が表示される」などの機能もいいかなあと考えています。

個人的には俳句をスマートフォンで入力するより、紙にさらさらと書きたいです。(笑)

コメント

  1. […] ソースコードの解説はこちら […]

タイトルとURLをコピーしました