カスタムOverlayViewを使う

概要
Google Maps API V3で、fromLatLngToDivPixelとfromDivPixelToLatLngを使って、緯度経度(latlng)と画面上のポジション(point)を変換しつつ、画像を配置したり動かしたりしたかった。

この場合、OverlayViewを使用するっぽいことが書いてあったので、実際にコードを書いてみた。
OverlayView詳細
OverlayViewは「TestMarker.prototype = new google.maps.OverlayView();」のように継承(?)して、後はTestMarker側にゴテゴテ実装をしていく。

OverlayViewの詳細はこの辺に載ってます。
http://code.google.com/intl/ja/apis/maps/documentation/v3/reference.html#OverlayView

上のURLを見ると、draw()remove()を実装しろと書いてあります。そこに書かないといけない内容は、名前の通り表示処理と削除処理です。詳しくは後述。

これをすると、以下のメソッドが使えるようになります。

getMap()
getPanes()
getProjection()

getProjectionMapCanvasProjectionを返します。MapCanvasProjectionは、緯度経度から現在のマップのPixelを取得したり、逆にPixelから緯度経度を取得するメソッドを提供してくれています。
簡易サンプル
とりあえず今回のサンプルでは、先人(リッチーとか)への敬意を込めて、座標を指定して「Hello World」をMap上に出力します。

こんな感じです。あまり目立たないですが、赤字でhello worldと出力しています。

この地図のコードはこんな感じです。

<html>
  <head>
    <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>

    <script type="text/javascript">
      window.onload = initialize;

      /* マップ(google.maps.Map)を入れる予定 */
      var map;
      /* カスタムマーカーを入れる予定 */
      var marker;

      /* マップ初期化。onload時に呼び出される。 */
      function initialize() {

        // マップ初期化
        var myOptions = {
          zoom: 7,
          center: new google.maps.LatLng(35.5, 140.0),
          mapTypeId: google.maps.MapTypeId.ROADMAP
        };
        map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);

        // マーカー生成
        marker = new HelloMarker( map, 35.5, 140.0 );
      }

      /* HelloMarkerのコンストラクタ。緯度、軽度をメンバ変数に設定する。 */
      function HelloMarker(map, lat, lng) {
        this.lat_ = lat;
        this.lng_ = lng;
        this.setMap(map);
      }

      /** google.maps.OverlayViewを継承 */
      HelloMarker.prototype = new google.maps.OverlayView();

      /** drawの実装。hello, worldと書いたdiv要素を生成 */
      HelloMarker.prototype.draw = function() {
        // 何度も呼ばれる可能性があるので、div_が未設定の場合のみ要素生成
        if (!this.div_) {
          // 出力したい要素生成
          this.div_ = document.createElement( "div" );
          this.div_.style.position = "absolute";
          this.div_.style.fontSize = "200%";
          this.div_.innerHTML = "hello, world";
          // 要素を追加する子を取得
          var panes = this.getPanes();
          // 要素追加
          panes.overlayLayer.appendChild( this.div_ );
        }

        // 緯度、軽度の情報を、Pixel(google.maps.Point)に変換
        var point = this.getProjection().fromLatLngToDivPixel( new google.maps.LatLng( this.lat_, this.lng_ ) );

        // 取得したPixel情報の座標に、要素の位置を設定
        // これで35.5, 140.0の位置を左上の座標とする位置に要素が設定される
        this.div_.style.left = point.x + 'px';
        this.div_.style.top = point.y + 'px';
      }

      /* 削除処理の実装 */
      HelloMarker.prototype.remove = function() {
        if (this.div_) {
          this.div_.parentNode.removeChild(this.div_);
          this.div_ = null;
        }
      }
    </script>
  </head>
  <body>
    <div id="map_canvas" style="width:600px; height:200px;"></div>
  </body>
</html>
          

このソースをそのままコピペするだけで、一応、カスタムOverlayViewの出来上がりです。
処理を少し追加してみる
hello worldだけでは少し寂しいので、表示位置を移動するメソッドを追加してみます。座標でもPixelでもどちらでも指定可能なようにします。

こんな感じです。



ボタンを押すと、矢印の方向にhello worldが移動します。ピクセル、緯度経度、どちらでも位置指定をできるようになています。


      /* 現在座標で位置を設定する。受け取った座標はfromLatLngToDivPixelでPixelに変換してDivのスタイルに設定。 */
      HelloMarker.prototype.setPosition = function(lat, lng) {
        this.lat_ = lat;
        this.lng_ = lng;
        var point = this.getProjection().fromLatLngToDivPixel( new google.maps.LatLng( this.lat_, this.lng_ ) );
        this.div_.style.left = point.x + 'px';
        this.div_.style.top = point.y + 'px';
      }

      /* 現在座標をLatLng型で返す */
      HelloMarker.prototype.getPosition = function() {
        return new google.maps.LatLng( this.lat_, this.lng_ );
      }

      /* 現在座標をPixelで設定する。fromDivPixelToLatLngでlat,lngを取得し、メンバ変数に現在座標を設定しておく */
      HelloMarker.prototype.setPoint = function(x, y) {
        var latlng = this.getProjection().fromDivPixelToLatLng( new google.maps.Point( x, y ) );
        this.lat_ = latlng.lat();
        this.lng_ = latlng.lng();
        this.div_.style.left = x + 'px';
        this.div_.style.top = y + 'px';
      }

      /* 現在座標をPixelで返す */
      HelloMarker.prototype.getPoint = function() {
        return this.getProjection().fromLatLngToDivPixel( new google.maps.LatLng( this.lat_, this.lng_ ) );
      }

      /* テストコード : setPointを呼び出してみる*/
      function markerMoveByPixel( marker, x, y ) {
        var point = marker.getPoint();
        marker.setPoint( point.x + x, point.y + y );
      }

      /* テストコード:setPositionを呼び出してみる */
      function markerMoveByLatlng( marker, lat, lng ) {
        var latlng = marker.getPosition();
        marker.setPosition( latlng.lat() + lat, latlng.lng() + lng );
      }
          
総評
というわけで、Google Maps API V3のカスタムオーバーレイでした。

単純に画像を置くだけならMarkerで十分ですが、opacity指定した半透明なマスクを作って指定座標に配置するとか、クリック時にちょっとしたアニメーションをさせるとか、ちょっと複雑なことをさせたい時には重宝するんじゃないかと思います。
戻る    ご意見、ご要望