アカベコマイリ

HEAR NOTHING SEE NOTHING SAY NOTHING

WP-ImaGeoMap 開発日誌 2

作成中の WordPress 用プラグイン、WP-ImaGeoMap の開発日誌。今日はエディタに以下の機能を追加した。

  • マーカーを独自画像に変更して選択状態を分かりやすくする
  • フォームに画像を表示して、マーカーに合わせて切り替える
  • 位置情報から標高を動的に取得する
  • 位置情報から住所を動的に取得する

最新版エディタのスクリーンショットは以下。

エディタ

マーカーはピンクが通常、ブルーは選択状態となる。マーカーをクリックなどで選択するとブルーに変わりフォーム左の画像も変更される。独自マーカーと画像の組み合わせの見た目は意外によかったので、マップ表示もこれで検討してゆく。

全体の進捗率的は 85% といったところか。以降に本日の実装メモを残す。

独自画像のマーカーと選択による切り替え

マーカーを選択・非選択で切り替える為に独自の画像を作成する事にした。これは Expression Design で編集して 28x28 ピクセルの PNG にエクスポートする。影の作成は以下のサイトが提供するサービスを使用した。

ここに画像の URL を指定するとアルファ値を考慮した影画像を生成してくれる。画像がドーナッツ状に切り抜かれていても、影はその形に従う。

用意した画像を切り替えられるようにする。Google Map API V3 のマーカーは iconshadow プロパティに 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 が解析され、コールバック関数に指定された引数のプロパティとなる。