OverlayViewを使って、Google Maps(グーグルマップ)で使えるラベルを自作する方法

今回は「google.maps.OverlayView」を使ってカスタムのオーバーレイを利用した、ラベルオブジェクトを自作してみます。

なお、Google Maps関連の記事についてはこちらにまとめてありますので、ご覧ください。

Google Maps(グーグルマップ)自作ラベルクラス

Javascriptソース/クラス定義


<script type="text/javascript">
   /* ラベルクラス定義 */
   class MyLabel extends google.maps.OverlayView {
      constructor(options) {
         super();
         this.latlng = options.marker.getPosition();
         this.setMap(options.map);
         this.div_ = null;
         this.content = options.content;
         this.className = options.cssClass;
         this.marker_ = options.marker;
         this.MarkerImage = options.marker.getIcon();
         if ( this.MarkerImage == undefined ) {
            /* 通常マーカーの場合*/
            this.TopPosition = options.top|| 35;
         } else {
            /* アイコン指定時 */
            this.TopPosition = options.top|| -5;
         }
         this.ZIndex = options.zIndex || null;
         this.Visible = options.visible || null;
      }
      onAdd() {
         var bVisible = this.Visible;
         if (!this.div_) {
            /* 出力したい要素生成 */
            this.div_ = document.createElement( "div" );
            this.div_.innerHTML = "";
            this.div_.style.display = "none";
            this.div_.style.zIndex = 3;

            if ( bVisible != undefined ) {
               if ( bVisible ) {
                  this.div_.style.display = "block";
                  this.div_.style.zIndex = 1;
                  if ( this.ZIndex != undefined ) {
                     this.div_.style.zIndex = this.ZIndex;
                  }
               }
            }
            var me = this;
            if ( bVisible == false || bVisible == null) {
               /* ラベル初期設定が、非表示の場合は、マウスオーバー/マウスアウトイベントで表示/非表示するようにする */
               google.maps.event.addListener(this.marker_, 'mouseover', function() {
                  me.show();
               });
               /* マウスアウトでラベル非表示.*/
               google.maps.event.addListener(this.marker_, 'mouseout', function() {
                  me.hide();
               });
            }
         }
      }
      draw() {
         /* 何度も呼ばれる可能性があるので、div_が未設定の場合のみ要素生成 */
         if (this.div_) {
            /* 出力したい要素生成 */
            this.div_.style.position = "absolute";
            if ( this.content != undefined ) {
               this.div_.innerHTML = this.content;
               this.div_.className = this.className;
            }

            /* 要素を追加する子を取得 */
            var panes = this.getPanes();
            panes.floatPane.appendChild( this.div_ );

            /* ラベルオブジェクト再配置 */
            this.resetPosition();
         }
      }


      /* setMap(null); とすると呼びされます */
      onRemove() {
         if (this.div_) {
            this.marker_.setMap(null);
            this.div_.parentNode.removeChild(this.div_);
            this.div_ = null;
            this.setMap(null);
         }
      }

      resetPosition() {
         if (this.div_) {
            var point = this.getProjection().fromLatLngToDivPixel( this.latlng );
            var nImageHeight = 0; 
            try {
               nImageHeight = this.MarkerImage.size.height;
            } catch(e){}

            if ( this.TopPosition != undefined ) {
               nImageHeight = nImageHeight + this.TopPosition;
            }
            if ( this.content != undefined ) {
               if ( this.content != "" ) {
                  /* 取得したPixel情報の座標に、要素の位置を設定 */
                  this.div_.style.left = String(point.x - 5) +'px';
                  this.div_.style.top = String(( point.y - ( this.div_.clientHeight + nImageHeight ) )) +'px';
               } 
            }
         }
      }
      show() {
         if (this.div_) {
            this.div_.style.display="block";
            this.resetPosition();
         }
      }
      hide() {
         if (this.div_) {
            this.div_.style.display = "none";
         }
      }
   }
</script>

OverlayViewクラス

OverlayViewクラスを継承する

まず、OverlayViewクラスを利用するには、継承してください。

class MyLabel extends google.maps.OverlayView {

class [クラス名] extends google.maps.OverlayView という感じでまずクラスを定義し、OverlayViewクラスを継承してください。

コンストラクタ

      constructor(options) {
         super();
         this.latlng = options.marker.getPosition();
         this.setMap(options.map);
         this.div_ = null;
         this.content = options.content;
         this.className = options.cssClass;
         this.marker_ = options.marker;
         this.MarkerImage = options.marker.getIcon();
         if ( this.MarkerImage == undefined ) {
            /* 通常マーカーの場合*/
            this.TopPosition = options.top|| 35;
         } else {
            /* アイコン指定時 */
            this.TopPosition = options.top|| -5;
         }
         this.ZIndex = options.zIndex || null;
         this.Visible = options.visible || null;
      }

まず最初に「super()」を実行し、google.maps.OverlayViewクラスのコンストラクタ処理を実行させます。

その後、パラメータの「option」をメンバ変数に格納するという処理になっています。

onAdd()

onAdd()メソッドはオブジェクトが生成されたときに実行されるメソッドです。ここでは、DOMオブジェクトを生成し、それをMapオブジェクト(ペイン)に追加する処理になります。

            this.div_ = document.createElement( "div" );
            this.div_.innerHTML = "";
            this.div_.style.display = "none";
            this.div_.style.zIndex = 3;

まず、「createElement」でDIVオブジェクトを生成します。これが実際のラベルになります。

            if ( bVisible != undefined ) {
               if ( bVisible ) {
                  this.div_.style.display = "block";
                  this.div_.style.zIndex = 1;
                  if ( this.ZIndex != undefined ) {
                     this.div_.style.zIndex = this.ZIndex;
                  }
               }
            }

optionのプロパティーでvisibleオプションがtrueの場合は、生成したDIVオブジェクトを表示させます(style.display=’block’にする)。

今回のラベルで初期表示の状態が非表示(visible=false)の場合は、マウスオーバーで表示、マウスアウトで表示にする処理を追加させます。

            var me = this;
            if ( bVisible == false || bVisible == null) {
               /* ラベル初期設定が、非表示の場合は、マウスオーバー/マウスアウトイベントで表示/非表示するようにする */
               google.maps.event.addListener(this.marker_, 'mouseover', function() {
                  me.show();
               });
               /* マウスアウトでラベル非表示.*/
               google.maps.event.addListener(this.marker_, 'mouseout', function() {
                  me.hide();
               });
            }

draw()

次にdraw()メソッドの定義になります。オブジェクト生成時や、地図中央位置変更、ズームレベル変更時などのイベントが起きると呼び出される処理になります。

onAdd()メソッドで生成したDIVオブジェクトをの位置を再設定することになります。

            /* 出力したい要素生成 */
            this.div_.style.position = "absolute";
            if ( this.content != undefined ) {
               this.div_.innerHTML = this.content;
               this.div_.className = this.className;
            }

まず、DIVオブジェクトのpositionをabsoluteにして、絶対位置で地図上のどこにでも設定できるようにします。

            /* 要素を追加する子を取得 */
            var panes = this.getPanes();
            panes.floatPane.appendChild( this.div_ );

次にDIVオブジェクトをマップ上に追加します。

今回のサンプルではfloatPaneに追加してあります。追加したいマップ上の層に追加してください。

MapPanesには5つの階層があります。

floatPaneペイン 4このペインには情報ウィンドウが含まれます。すべての地図オーバーレイの上にあります
overlayMouseTargetペイン 3このペインには、マーカーの透明なターゲットなど、マーカーの DOM マウス イベントを受け取る要素が含まれています。それは floatShadow の上にあります。そのため、情報ウィンドウの影に隠れたマーカーをクリックできます
markerLayerペイン 2このペインは、マーカーを含むレイヤー。DOM イベントは受信できません
overlayLayerペイン 1このペインには、ポリライン、ポリゴン、グラウンド オーバーレイ、タイル レイヤ オーバーレイが含まれますDOM イベントは受信できません
mapPaneペイン 0このペインは最下部のペインで、タイルの上にありますDOM イベントは受信できません

次に実際にDIVオブジェクトの位置を調整します。

      resetPosition() {
         if (this.div_) {
            var point = this.getProjection().fromLatLngToDivPixel( this.latlng );
            var nImageHeight = 0; 
            try {
               nImageHeight = this.MarkerImage.size.height;
            } catch(e){}

            if ( this.TopPosition != undefined ) {
               nImageHeight = nImageHeight + this.TopPosition;
            }
            if ( this.content != undefined ) {
               if ( this.content != "" ) {
                  /* 取得したPixel情報の座標に、要素の位置を設定 */
                  this.div_.style.left = String(point.x - 5) +'px';
                  this.div_.style.top = String(( point.y - ( this.div_.clientHeight + nImageHeight ) )) +'px';
               } 
            }
         }
      }

resetPosition()関数でやっているのが二つ。マーカーの位置情報からピクセルの位置情報を取得しています。

OverlayViewクラスの「getProjection」メソッドを実行すると、MapCanvasProjectionオブジェクトを取得できます。

MapCanvasProjectionオブジェクトのfromLatLngToDivPixel()メソッドを使えば、位置情報から座標オブジェクト(google.maps.Point)を取得できます。

この座標をDIVオブジェクトの位置を設定しています。

「point.x – 5」としているのは、ラベルの位置を少し、左に移動させています。

point.yは、設定しているマーカーやマーカーアイコンの高さに合わせて少し、調整してあります。

onRemove

onRemoveはsetMap(null)が呼び出された時の動きになります。

      onRemove() {
         if (this.div_) {
            this.marker_.setMap(null);
            this.div_.parentNode.removeChild(this.div_);
            this.div_ = null;
            this.setMap(null);
         }
      }

サンプルでは、markerにsetMap(null)を実行し、マーカーを地図上から削除します。

なおかつ、ラベルオブジェクトであるDIV自身を削除し、最後に、OverlayViewクラスのsetMap(null)を実行し、マップ上からラベルとマーカーを削除するようにしてあります。

サンプル

サンプル

HTMLコード

<!DOCTYPE html>
<html>
<head>
   <meta charset="utf-8" />
   <title>Simple Map</title>
   <script 
       src="https://maps.googleapis.com/maps/api/js?key=(APIキー)" >
   </script>
  <script type="text/javascript" src="https://www.single-life.tokyo/js/MyLabel.js" ></script>
   <style type="text/css">
      #map_canvas {
         width: 100%;
         height:80%;
      }
      html,
      body {
         height: 100%;
         margin: 0;
         padding: 0;
      }
      .MapTooltip {
         color: #000000; 
         background-color: #ffffff;
         border-style: solid;
         border-width: 2px;
         padding-top: 1px; 
         padding-bottom: 1px; 
         padding-right: 3px; 
         padding-left: 3px; 
         font-weight: normal; 
         font-size: small; 
         font-family: MS Pゴシック, sans-serif; 
         margin:0px;
         white-space:nowrap;
      }
      .BorderColorBlue {
         border-color: blue;
      }
      .BorderColorRed {
         border-color: red;
      }
   </style>
   <script type="text/javascript">
      var map;
      function createMarkerImage(sImageSrc,nWidth,nHeigth,nX,nY) {
         var oMarkerImg = null;
         if ( nX != undefined ) {
            oMarkerImg = new google.maps.MarkerImage
                  (
                     sImageSrc,             /* url */
                     new google.maps.Size(nWidth,nHeigth),    /* size */
                     new google.maps.Point(0,0),       /* origin */
                     new google.maps.Point(nX,nY)       /* anchor */
                  );
         } else {
            oMarkerImg = new google.maps.MarkerImage
                  (
                     sImageSrc,             /* url */
                     new google.maps.Size(nWidth,nHeigth)    /* size */
                  );
         }
         return oMarkerImg;
      }

      function createImageMarker(latlng,sImgSrc,sTitle,nWidth,nHeight) {
         var nDefaultWidth = 20;
         var nDefaultHeight= 20;
         if ( nWidth != undefined ) { 
            nDefaultWidth = nWidth;
         }
         if ( nHeight != undefined ) { 
            nDefaultHeight = nHeight;
         }
         var marker;
         if ( sImgSrc == "" ) { 
             marker = new google.maps.Marker(
               {
                  
                  map: map, 
                  position: latlng
               }
            );
         } else {
            var oImg = createMarkerImage(sImgSrc,nDefaultWidth,nDefaultHeight);
             marker = new google.maps.Marker(
               {
                  
                  map: map, 
                  position: latlng,
                  icon:oImg
               }
            );
         }
         if ( sTitle != undefined ) {
            marker.setTitle(sTitle);
         }
         return marker;
      }

      /* ラベル付きマーカー作成関数 */
      function createLabelMarker(latlng,sTitle,sCssClass,bVisible,imgsrc,nZIndex,nImgWidth,nImgHeight) {
         var marker = createImageMarker(latlng,imgsrc,sTitle,nImgWidth,nImgHeight);
         var tooltipOptions= { map :map, marker:marker, content:sTitle, cssClass:sCssClass,visible:bVisible,zIndex:nZIndex};
         var labe = new MyLabel(tooltipOptions);
         return labe;
      }

      var marker1;
      var marker1;
      var myLatlng1;
      var myLatlng2;

      function initMap() {
         myLatlng1 = new google.maps.LatLng(35.54866545042485, 139.78388605064137);
         myLatlng2 = new google.maps.LatLng(35.54504741220244, 139.76908139731486);
         var mapOptions = {
           zoom: 15,
           center: myLatlng1
         }
         map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
         marker1 = createLabelMarker(myLatlng1,"羽田空港","MapTooltip BorderColorBlue",false,"https://maps.google.com/mapfiles/ms/micons/blue.png",1,27,32);
         marker2 = createLabelMarker(myLatlng2,"羽田空港第3ターミナル","MapTooltip BorderColorRed",true,"",1,27,32);
      }
      function deleteMarker() {
         if ( marker1 != undefined ) {
            marker1.setMap(null);
            marker1 = null;
         }
      }
      function recreateMarker() {
         if ( marker1 == null ) {
            marker1 = createLabelMarker(myLatlng1,"羽田空港","MapTooltip BorderColorBlue",false,"https://maps.google.com/mapfiles/ms/micons/blue.png",1,27,32);
         }
      }
      function showMarker() {
         if ( marker1 != undefined ) {
            marker1.show();
         }
      }
      function hideMarker() {
         if ( marker1 != undefined ) {
            marker1.hide();
         }
      }
      google.maps.event.addDomListener(window, 'load', initMap);
   </script>
</head>
<body>
   <div id="map_canvas"></div>
   <input type="button" onclick="showMarker();" value="表示" />
   <input type="button" onclick="hideMarker();" value="非表示" />
   <input type="button" onclick="deleteMarker();" value="削除" />
   <input type="button" onclick="recreateMarker();" value="再生成" />
</body>
</html>

デモ

自作ラベルクラスを使用してみる

ラベルデザイン(スタイルシート定義)

      .MapTooltip {
         color: #000000; 
         background-color: #ffffff;
         border-style: solid;
         border-width: 2px;
         padding-top: 1px; 
         padding-bottom: 1px; 
         padding-right: 3px; 
         padding-left: 3px; 
         font-weight: normal; 
         font-size: small; 
         font-family: MS Pゴシック, sans-serif; 
         margin:0px;
         white-space:nowrap;
      }
      .BorderColorBlue {
         border-color: blue;
      }
      .BorderColorRed {
         border-color: red;
      }

ラベルのデザインは、スタイルシートで定義します。

サンプルでは「MapTooltip」というクラスネームで、背景色白で、文字が黒のにしてあります。

枠線は「border-color」で定義し、「BorderColorBlue」と「BorderColorRed」のクラスを指定することで、枠線を指定できるようにしてあります。

ラベルオブジェクト生成

      /* ラベル付きマーカー作成関数 */
      function createLabelMarker(latlng,sTitle,sCssClass,bVisible,imgsrc,nZIndex,nImgWidth,nImgHeight) {
         var marker = createImageMarker(latlng,imgsrc,sTitle,nImgWidth,nImgHeight);
         var tooltipOptions= { map :map, marker:marker, content:sTitle, cssClass:sCssClass,visible:bVisible,zIndex:nZIndex};
         var labe = new MyLabel(tooltipOptions);
         return labe;
      }

MyLabelクラスをnewして、ラベルオブジェクトを生成します。コンストラクタにはオプションを指定します。

mapMapクラスオブジェクト
markerMarkerクラスオブジェクト
contentラベル文字列
cssClass指定するCSSクラス名
visible初期表示でラベルを表示させる場合には、true
初期表示でラベルを表示させないで、マウスオーバーで表示、マウスアウトで非表示の場合は、false
zIndexMyLabelで生成する、DIVオブジェクトのzIndex
topラベル位置の絶対位置ポジション
Mylabelのオプション

オプションを指定して、MyLabelのコンストラクタを呼び出せば、ラベルオブジェクトが完成です。

ラベル表示

「表示」ボタンを押してください。

      function showMarker() {
         if ( marker1 != undefined ) {
            marker1.show();
         }
      }

show()メソッドを実行し、ラベルを表示させます。

      show() {
         if (this.div_) {
            this.div_.style.display="block";
            this.resetPosition();
         }
      }

MyLabelのshowメソッドでは、事前に生成しているDIVオブジェクトのstyle.displayを「block」に設定し、ラベルを表示させています。

ラベル非表示

「非表示」ボタンを押してください。

      function hideMarker() {
         if ( marker1 != undefined ) {
            marker1.hide();
         }
      }

hide()メソッドを呼び出し、ラベルを非表示にします。

      hide() {
         if (this.div_) {
            this.div_.style.display = "none";
         }
      }

MyLabelのhideメソッドでは、事前に生成しているDIVオブジェクトのstyle.displayを「none」に設定し、ラベルを非表示させています。

マーカー削除

      function deleteMarker() {
         if ( marker1 != undefined ) {
            marker1.setMap(null);
            marker1 = null;
         }
      }

setMapにnullを渡せば、マーカーとラベルを一緒に削除できます。

MyLabelではonRemove()メソッドを呼び出して、ラベルオブジェクトをとマーカーオブジェクトの削除を実行しています。

ご参考

関連ページ