WP-ImaGeoMap 開発日誌 2
作成中の WordPress 用プラグイン、WP-ImaGeoMap の開発日誌。今日はエディタに以下の機能を追加した。
- マーカーを独自画像に変更して選択状態を分かりやすくする
- フォームに画像を表示して、マーカーに合わせて切り替える
- 位置情報から標高を動的に取得する
- 位置情報から住所を動的に取得する
最新版エディタのスクリーンショットは以下。
マーカーはピンクが通常、ブルーは選択状態となる。マーカーをクリックなどで選択するとブルーに変わりフォーム左の画像も変更される。独自マーカーと画像の組み合わせの見た目は意外によかったので、マップ表示もこれで検討してゆく。
全体の進捗率的は 85% といったところか。以降に本日の実装メモを残す。
独自画像のマーカーと選択による切り替え
マーカーを選択・非選択で切り替える為に独自の画像を作成する事にした。これは Expression Design で編集して 28x28 ピクセルの PNG にエクスポートする。影の作成は以下のサイトが提供するサービスを使用した。
ここに画像の URL を指定するとアルファ値を考慮した影画像を生成してくれる。画像がドーナッツ状に切り抜かれていても、影はその形に従う。
用意した画像を切り替えられるようにする。Google Map API V3 のマーカーは icon
と shadow
プロパティに google.maps.MarkerImage
を指定することで、独自の画像と影を設定可能。選択・非選択のマーカーは同一形状の色違いにしたので選択・非選択と影の計 3 種類を必要とする。これらは一度だけインスタンスを生成してマーカー間で共有するように設計した。実装は以下のようになる。
var markerImage = null; //! 通常時のマーカー画像。
var markerImageSelect = null; //! 選択時のマーカー画像。
var markerImageShadow = null; //! マーカーの影となる画像。
function initialize()
{
// ...中略
// マーカー ( 通常・選択 ) と影画像
markerImage = new google.maps.MarkerImage( "http://***/marker.png", new google.maps.Size( 28, 28 ), new google.maps.Point( 0,0 ), new google.maps.Point( 14, 28 ) );
markerImageSelect = new google.maps.MarkerImage( "http://***/marker-select.png", new google.maps.Size( 28, 28 ), new google.maps.Point( 0,0 ), new google.maps.Point( 14, 28 ) );
markerImageShadow = new google.maps.MarkerImage( "http://***/marker-shadow.png", new google.maps.Size( 43, 28 ), new google.maps.Point( 0,0 ), new google.maps.Point( 14, 28 ) );
}
これらをマーカーに設定する場合は以下のようになる。
// 初期化の場合
var marker = new google.maps.Marker( { position: latlng, map: map, title: "test", draggable: true, icon: markerImage, shadow: markerImageShadow } );
// 切り替えの場合
marker.setIcon( markerImageSelect );
初期化時は、icon と shadow プロパティに画像のインスタンスを設定する。切り替えの場合は icon プロパティに代入するのではなく、setIcon メソッドに画像インスタンスを指定する。
標高の取得
標高の取得はアメリカ地質調査所の getElevation サービスを使用する。このサービスは緯度と経度を指定すると、標高を REST (XML) として返す。以前に C# から取得する方法を実装済みなので、今回はそれを参考にする
Google Map API V2 まではライブラリに XML パーサーも含まれていたが、V3 では無くなった。つまり JavaScript から getElevation のデータを解析する場合、自前でパーサーを用意する事になるが、これは面倒なので PHP で取得する事にした。
JavaScript から PHP の CGI を呼び出し、PHP は処理結果を JSON 形式で標準出力に書き出す。
PHP の実装としては以下のようになる。標高は取得した値に m を付けて 29m のような文字列にしているが、余計な処理にも思えてきたので付けるのをやめるかもしれない。
<?php
/**
* 指定された緯度と経度から標高を取得します。
* 取得された標高は JSON 形式で標準出力に書き込まれます。
*
* 緯度・経度の指定が不正な場合や、取得に失敗した場合は空文字を書き込みます。
*/
function getElevation()
{
$data = array( "altitude" => "", "id" => ( isset( $_GET[ "id" ] ) ? $_GET[ "id" ] : -1 ) );
$latitude = ( isset( $_GET[ "latitude" ] ) ? $_GET[ "latitude" ] : -1 );
$longitude = ( isset( $_GET[ "longitude" ] ) ? $_GET[ "longitude" ] : -1 );
if( $latitude != -1 && $longitude != -1 )
{
$feed = "http://gisdata.usgs.gov/xmlwebservices2/elevation_service.asmx/getElevation?X_Value={$longitude}&Y_Value={$latitude}&Elevation_Units=METERS&Source_Layer=-1&Elevation_Only=true";
$xml = simplexml_load_file( $feed );
if( $xml )
{
// 不正な緯度・経度を指定した場合は "-1.79769313486231E+308" という文字列が入るので、
// 必ず数値判定を行って安全性を保証する必要がある。
//
$altitude = ( string )$xml;
if( is_numeric( $altitude ) )
{
$data[ "altitude" ] = "{$altitude}m";
}
}
}
header( "Content-Type: application/json; charset=utf-8" );
$json = json_encode( $data );
echo "{$_GET[ 'callback' ]}({$json});";
}
getElevation();
?>
JavaScript からの呼び出しには、jQuery.getJSON
を使用する。これは非同期に実行されるが呼び出す CGI 用 URL の末尾に "callback=?" を付与することによりコールバック関数を指定できる。JavaScript の実装は以下。
/**
* 指定されたマーカーに標高を設定します。
*
* @param marker マーカー。
*/
function setElevation( marker )
{
// CGI の URL は仮のもの
var target = "http://****/read-elevation.php?id=" + marker.id + "&latitude=" + marker.position.lat() + "&longitude=" + marker.position.lng() + "&callback=?";
// getJSON は非同期実行されるので、結果が返された時は必ず id のチェックを行う事
jQuery.getJSON( target, function( data )
{
// data.id に id が入っている
// data.altitude に標高が入っている
});
}
エディタの JavaScript では google.maps.Marker.prototype.id
を宣言することでマーカーに id を割り当て、個体識別できるようにしている。
jQuery.getJSON
は非同期に実行されるため、その前後で選択しているマーカーが変更される可能性もある。対策として PHP に id を渡して戻り値 JSON にそれを含めることで、標高の反映先を確実に指定できるようにしている。
前述の PHP の最後の処理が実行されると出力された JSON が解析され、コールバック関数に指定された引数のプロパティとなる。