WP-ImaGeoMap 1.0 Release

2009年12月7日 0 開発 , , , , , ,

WordPress プラグイン、WP-ImaGeoMap 1.0 をリリースした。

シリーズまとめ
WP-ImaGeoMap 関連

このプラグインでは、画像と Google Map を組み合わせた情報を WordPress の記事やページに貼り付ける事を目的としている。

スクリーンショット

スクリーンショット

ぱっと思いつく範囲では、簡易な旅日記や散歩の記録、お店の紹介などに使えそう。動作デモのページでは、昨年の初秋に訪れた熱川バナナワニ園の記録を付けてみた。

ユーザビリティやデザインなどに改善の余地はあるが、一通りの機能を実装したので公開する事にした。開発は今後も継続するが、とりあえず一区切りという事で。

WP-ImaGeoMap 開発日誌

2009年11月20日 0 開発 , , , , , ,

作成中の WordPress 用プラグイン、WP-ImaGeoMap の開発日誌。

シリーズまとめ
WP-ImaGeoMap 関連

サクッと作って素早く公開するつもりだったのだが、思ったより手間取っているので開発日誌を付ける事にした。開発状況を文章化する事で、仕様の穴に気付いたり良いアイディアが浮かぶ事を期待している。

プラグインの仕様

思いつく限りの仕様を列挙してみる。

  • プラグイン名は Image + GeoTag + Map で ImaGeoMap とする
  • 指定した画像を Google Map 上にマーカーとして表示する
  • 画像は複数指定できる
  • マップとマーカーの配置は専用のエディタで行う
  • エディタは投稿画面からモーダルダイアログとして起動する
  • エディタは Google Map とマーカー編集用フォームを持つ
  • 画像の EXIF からマーカー位置を設定できる
  • EXIF 取得は PHP + PEL で処理し、JSON 形式で出力する
  • 出力内容は JavaScript から jQuery.getJSON で得る
  • EXIF がない場合のマーカー位置は、表示中のマップ中央とする
  • エディタの編集内容はショートコードとして投稿に埋め込む
  • ショートコードの書式は JavaScript コードの断片とする
  • コード断片は、マップとマーカーの変数初期化処理となる
  • コード断片とする事で記事データは肥大するが、処理は簡単になる
  • ショートコードはマップ表示領域の div と JavaScript の組み合わせとして記事に埋め込まれる
  • 複数マップを考慮して、div と JavaScript の関数には連番を割り当てる。このアイディアは Google Map Anywhere から得た
  • マップ表示はなるべくリッチにしたい ( 努力目標 )

専用エディタ画面

専用エディタの GUI を検討してみる。スクリーンショットは現時点のもの。

エディタ

エディタ

改めて見ると、画像表示がないので、どの画像を選択しているのかがわかりにくい事に気付いた。フォームの左端あたりに、マーカー選択と連動したサムネイル画像を表示した方がよさそうだ。

また、「選択している画像を削除」ボタンの表記は誤解を生みそうだ。マーカーを消すだけなので、「選択しているマーカーを削除」の方が適切だろう。

マップ表示

ショートコードの解析結果となるマップ表示方法の検討。スクリーンショットは現時点のもの。

マーカー配置

マーカー配置

マーカー配置の場合、見た目は味気ないがマップ操作が快適に動作する上に、マーカーの豊富な機能を利用できるというメリットがある。

ただ、このプラグインでは画像が主役なので、マップ外に画像リストを表示するなど、見た目でマップと画像が関連付いている事を示したい。

マーカーをクリックした時に InfoWIndow を表示し、その中に画像を表示する手もあるが、この方法ではクリックするまで気付かれないだろう。

この方法を採用する場合、画像との関連づけを明示する事が前提と考えている。

サムネイル配置

サムネイル配置

サムネイル配置の場合、見た目が直感的である。Google 自身が提供している地図検索でも、右上のメニューから「その他 → 写真」を選ぶと、地域と連動した写真が表示されるので、広く親しまれた表示方法といえる。

画像を表示する場合はマーカーの代わりに OverlayView を使用する。マーカーも独自の画像を指定できるが、縮小表示されないのでサムネイルとして使用できない。

試して気がついたのだが、OverlayView をマップ上に配置するとマップ操作が極端に重くなる。画像の代わりにテキストを表示してみたが、症状は変わらなかった。OverlayView が一つでもこうなるので、そもそも重いのだろう。

あまりマップを動かす事はないだろうが、留意すべき問題だ。

また、OverlayViiew は InfoWindow やクリックの処理に一工夫必要となる。OverlayView でクリックなどをハンドリングする場合、マーカーと異なり addDomListener で処理する必要がある。

以下のページで具体的な方法が説明されており、非常に参考になった。

ただし、InfoWIndow を表示してみたところ、Chrome や Firefox では問題ないのだが、IE だと表示が崩れてしまった。原因は不明だが、それを調査するよりも jQuery などのリッチなポップアップを利用した方が賢明な気がしている。

地図に画像だけ並んでいても意味がないので、ポップアップによる画像情報の表示は必須だろう。

今回のまとめ

現時点の進捗率としては 80% ぐらいか。基本機能はできたので、今後は GUI を洗練させたい。

また、マップの表示形式をマーカーとサムネイルのどちらにするかを決める。両方サポートしても良いが、一方を選んで注力した方が良いだろう。

今後、開発に詰まった時などに日誌を付けたい。スムーズにいけば良いのだが。

PHP で EXIF を読む 2

2009年11月17日 0 開発 , , ,

PHP で EXIF を読むシリーズその 2。

シリーズまとめ
PHP で EXIF を読む

前回の記事では、PHP の標準ライブラリ exif_read_data を使用して EXIF の読み込みを行った。しかし exif_read_data は引数に URL を指定できず不便である。また、この関数は標準ライブラリであるにもかかわらず、使用できない環境もあるとの事。

そこで、今回は PEL というライブラリを検討してみる。

PEL : PHP Exif Library

PEL というのは、オープンソースな PHP 用 EXIF 読み書きライブラリである。

OpenSource Translative 日本語
PEL: PHP Exif Library

上記のページのリンク先から PEL を入手できる。

インストールに関する設定も特になく、入手したファイルをそのまま require_once で PHP スクリプトに取り込むだけで使用できる。

以下に、前回の記事で行った処理を PEL 向けに修正したクラスをサンプルとして掲載する。

require_once('pel-0.9.1/PelJpeg.php');

/**
 * EXIF 情報の読み取り機能を提供します。
 */
class ExifReader
{
	/**
	 * EXIF の GPS 情報から GeoTag として値へ変換する。
	 *
	 * @param	$fraction	GPS 情報となる、分子・分母の計 2 要素の配列。
	 *
	 * @return	成功時は変換した値。失敗時は false。
	 */
	private function getGeoTagValue( $fraction )
	{
		$numerator   = ( double )$fraction[ 0 ];
		$denominator = ( double )$fraction[ 1 ];

		return ( $numerator / $denominator );
	}

	/**
	 * EXIF の GPS 情報から GeoTag を取得する。
	 *
	 * @param	$values	GPS 情報の配列 ( 度・分・秒 )。
	 *
	 * @return	成功時は GeoTag 用の値。失敗時は false。
	 */
	private function getGeoTagLocationValue( $values )
	{
		if( count( $values ) != 3 ) { return false; }

		$degrees = $this->getGeoTagValue( $values[ 0 ] );
		if( !$degrees ) { return false; }

		$minutes = $this->getGeoTagValue( $values[ 1 ] );
		if( !$minutes ) { return false; }

		$seconds = $this->getGeoTagValue( $values[ 2 ] );
		if( !$seconds ) { return false; }

		return $degrees + ( $minutes / 60.0 ) + ( $seconds / 3600 );
	}

	/**
	 * 画像から読み取った EXIF 情報を取得します。
	 *
	 * @param	$url	画像の URL。
	 *
	 * @return	成功時は EXIF 情報。失敗時は false。
	 */
	public function read( $url )
	{
		try
		{
			$jpeg = new PelJpeg($url);
			$app1 = $jpeg->getExif();
			if( !$app1 ) { return false; }

			$tiff = $app1->getTiff();
			if( !$tiff ) { return false; }

			$ifd = $tiff->getIfd();
			if( !$ifd ) { return false; }

			$gps = $ifd->getSubIfd( PelIfd::GPS );
			if( !$gps ) { return false; }

			// 緯度・経度が取得できないなら無効とする
			$v = $gps->getEntries();
			if( !isset( $v[ 1 ] ) || !isset( $v[ 2 ] ) || !isset( $v[ 3 ] ) || !isset( $v[ 4 ] ) ) { return false; }

			$data = array();

			// 緯度
			{
				$latitude = $this->getGeoTagLocationValue( $v[ 2 ]->getValue() );
				if( !$latitude ) { return false; }

				// 南半球なら負の値
				$direction = $v[ 1 ]->getText();
				if( $direction == "S" )
				{
					$latitude *= -1;
				}

				$data[ "latitude" ] = $latitude;
			}

			// 経度
			{
				$longitude = $this->getGeoTagLocationValue( $v[ 4 ]->getValue() );
				if( !$longitude ) { return false; }

				// 西半球なら負の値
				$direction = $v[ 3 ]->getText();
				if( $direction == "W" )
				{
					$longitude *= -1;
				}

				$data[ "longitude" ] = $longitude;
			}

			// 測地系
			if( isset( $v[ 18 ] ) )
			{
				$data[ "mapDatum" ] = $v[ 18 ]->getText();
			}

			// 標高
			if( isset( $v[ 6 ] ) )
			{
				$altitude = $this->getGeoTagValue( $v[ 6 ]->getValue() );
				if( $altitude )
				{
					$data[ "altitude" ] = ( string )$altitude . "m";
				}
			}

			// GPS 以外の情報
			$v = $ifd->getEntries();
			{
				// 撮影日
				if( isset( $v[ 306 ] ) )
				{
					// 撮影日の書式は "yyyy:MM:dd hh:mm:ss" となり、終端の NULL 文字と合わせて 20 バイトの文字列となる。
					// 日時の各桁は、データが存在しない場合はスペースで埋められる。
					//
					$chars = str_split( $v[ 306 ]->getText() );
					$count = count( $chars );
					if( $count == 19 )
					{
						// 年と月の区切り文字をスラッシュに変換
						$chars[ 4 ] = "/";
						$chars[ 7 ] = "/";

						$data[ "dateTime" ] = implode( $chars );
					}
				}
			}

			// 画像の URL
			$data[ "url" ] = $url;

			return $data;
		}
		catch( Exception $e )
		{
			return false;
		}
	}
}

手元の環境で試したところ、PEL の場合は URL 上の画像からも EXIF を読み取れる事が確認できた。

ただしオンライン上の JPEG を読み込んだ場合、PelJpeg のコンストラクタに時間が掛かり、時には数分待たされた事もあった。

常に時間が掛かるわけではないが、この現象は結構な頻度で発生していたので気がかりである。ローカルホストでも時間が掛かっていたので、通信速度以外の要因があるのだと思う。