GoogleMaps の JavaScript API(v3) を使って、常に1つの InfoWindow だけ表示されるように制御する

公開日: : 最終更新日:2014/05/05 JavaScript , , , ,

前回アップした GoogleMaps の JavaScript API (v3) を使って地図上に複数の Marker を表示するサンプル の続き記事になります。

この記事で作成したサンプルは、次の問題点がありました。

使っていただくとすぐに分かると思いますが、Marker が次々に立つのはいいのですが、InfoWindow が重なって下に隠れたものが見えなくなってしまいます。

GoogleMaps の JavaScript API (v3) を使って地図上に複数の Marker を表示するサンプル

今回はこの点を解消したサンプルを紹介します。
なお、サンプルは次の URL で公開しています。

icatch-flag_1395019914_mini

photo credit: ►Milo► via photopin cc

目次

1. v0.4 からの変更点

次の URL で公開しているものは v0.4 として作成したものです。

今回の改修では、このバージョンをベースに機能を修正して v0.4.1 としました。

具体的な機能修正内容は次の通りです。

1-1. 表示する InfoWindow は常に1つだけに制御

v0.4 では InfoWindow 同士が重なりあってしまう問題がありました。
また、InfoWindow を再び表示させる処理を実装していなかったので、一度 InfoWindow を閉じてしまうとその Marker の InfoWindow を表示できないという問題もありました。

v0.4.1 ではこの2点に対応しました。

  • 1点目の問題 → 常に最後に選択された Marker の InfoWindow を表示する
  • 2点目の問題 → Marker に click イベントの Listener を追加。クリック時に InfoWindow を表示する

言葉だけでは伝わりにくい部分もあるかと思いますので、実際にサンプルを触ってみてください。

2. 使用したライブラリ

ライブラリの変更はありませんが、初めてこの記事を見る方もいらっしゃると思いますので載せておきます。

2-1. jQuery

<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>

2-2. Google Maps JavaScript API v3

<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false"></script>

2-3. Underscore.js

<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.5.2/underscore-min.js"></script>

3. ソースコード

以下は、作成したソースコードについてになります。

3-1. ファイル構成

プロジェクト直下で ls した結果は次の通りです。

$ ls -1
css
Gruntfile.js
index.html
js
LICENSE
node_modules
package.json
README.md

次のファイル・ディレクトリは、このプロジェクトに Grunt: The JavaScript Task Runner の LiveReload を適用したので、その関連ファイルです。

  • Gruntfile.js
  • node_modules
  • package.json

適用方法については次の記事を参照してください。

CSS は追加したファイルはありません。
“style.css” の1ファイルのみです。

$ ls css
style.css

JavaScript は “stock.js” を追加しました。

$ ls -1 js
script.js
stock.js

内容については、このあとで取り上げます。

3-2. index.html

上記問題点の対応は、JavaScript の変更だけで解決できる話なので、今回 index.html の変更はありません。

いちおう、コードを載せておきます。

<!DOCTYPE HTML>
<html>
    <head>
        <meta charset=utf8>
        
        <meta name="description" content="入力した住所から緯度・経度を GoogleMap の InfoWindow に表示するだけの簡単なプログラムです。">

        <!-- CSS -->
        <!-- Latest compiled and minified CSS -->
        <link rel="stylesheet" href="https://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
        <!-- Optional theme -->
        <link rel="stylesheet" href="https://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap-theme.min.css">

        <link rel="stylesheet" type="text/css" href="./css/style.css" />
    </head>
    <body>
        <div class="container">
            <div id="header-hollow" class="row">
                <div class="col-md-5">
                    <div class="input-group">
                        <span class="input-group-addon">
                            <span class="glyphicon glyphicon-map-marker"></span>
                        </span>
                        <input type="text" id="address" class="form-control" value="東京都墨田区押上1−1−2" />
                    </div>
                </div>
            </div><!-- /#header-hollow -->

            <div class="row">
                <div id="main" class="col-md-9">
                    <div class="well well-sm">
                        <div id="map-canvas"></div>
                    </div>
                </div>
                <div id="right-side" class="col-md-3">
                    <div class="google-adsense">
                        <div class="label-ad">
                            <span>広告</span>
                        </div>
                        <div class="adsense">
                            ※adsense で発行したタグを貼り付ける
                        </div>
                        <div class="adsense">
                            ※adsense で発行したタグを貼り付ける
                        </div>
                    </div><!-- /.google-adsense-->

                    <div class="amazon">
                        <div class="label-ad">
                            <span>GoogleMaps 関連本</span>
                        </div>
                        <span class="amazon-book">
                            ※Amazon のアフィリエイトリンクを貼り付ける
                        </span>
                        <span class="amazon-book">
                            ※Amazon のアフィリエイトリンクを貼り付ける
                        </span>
                    </div>
                </div><!-- /#right-side -->
            </div>

            <footer>
                <span>Copyright © 2014 <a href="http://tomoyamkung.net">tomoyamkung.net</a> All Rights Reserved.</span>
            </footer>

        </div><!-- /.container -->

        <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
        <script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false"></script>
        <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.5.2/underscore-min.js"></script>
        <script type="text/javascript" src="./js/script.js" charset="utf8"></script>
        <script type="text/template" id="infowindow_template">
            <div class="well info-window">
                <div class="row">
                    <span>緯度: <%= latitude %></span>
                </div>
                <div class="row">
                    <span>経度: <%= longitude %></span>
                </div>
            </div>
        </script>
    </body>
</html>

3-3. js/stock.js

このファイルでは InfoWindowStock というクラスを定義しています。
その名の通り、生成した InfoWindow を保持する役割を持っています。

内部にハッシュを持っていて、put でハッシュに InfoWindow を追加、get で保持している InfoWindow を取り出します。

ハッシュのキーには、緯度と経度をコロンで繋いだ文字列を使っていて、createKey で生成します。

パラメータのチェックなどをしていないので叩けばバグが出てきますが、とりあえずサンプルは動きます。

/**
 * InfoWinow オブジェクトをストックするクラス。
 * 
 */
var InfoWindowStock = function() {
    /**
     * InfoWindow をストックするハッシュ。
     * @type {Array}
     */
    this.stock = [];
};
InfoWindowStock.prototype = {
    /**
     * ストックする InfoWindow のキーを生成する。
     * 
     * @param  {Object} location 緯度・経度を格納したオブジェクト
     * @return {String} 生成したキー
     */
    createKey: function(location) {
        var key = location.k + ":" + location.A;
        return key;
    },
    /**
     * InfoWindow をストックする。
     * 
     * @param  {Object} location 緯度・経度を格納したオブジェクト
     * @param  {Object} infoWindow ストックする InfoWindow オブジェクト
     */
    put: function(location, infoWindow) {
        var key = this.createKey(location);
        this.stock[key] = infoWindow;
    },
    /**
     * ストックしてある InfoWindow から該当するものを取得する。
     * 
     * @param  {Object} location 緯度・経度を格納したオブジェクト
     * @return {Object} ストックしてあった InfoWindow オブジェクト
     */
    get: function(location) {
        var key = this.createKey(location);
        return this.stock[key];
    }
};

3-4. js/script.js

/**
 * 指定の場所に InfoWindow を設定した Marker を表示する。
 * 
 * @param  {Object} map Marker を立てる GoogleMap オブジェクト
 * @param  {Object} location Marker を立てる位置
 */
function setupMarker(map, location) {
    var marker = new google.maps.Marker({map: map, position: location}); // Marker オブジェクトを生成する

    this.disableCurrentInfoWindow(); // 表示中の InfoWindow があれば非表示にする

    currentInfoWindow = createInfoWindow(location.k, location.A); // InfoWindow オブジェクトを生成し、、、
    currentInfoWindow.open(marker.getMap(), marker); // InfoWindow を表示し、、、
    stock.put(location, currentInfoWindow); // stock に追加する

    google.maps.event.addListener(marker, 'click', function(event) { // Marker がクリックされたら、、、
        disableCurrentInfoWindow(); // 表示中の InfoWindow があれば非表示にし、、、
        currentInfoWindow = stock.get(event.latLng); // stock から InfoWindow を取り出して、、、
        currentInfoWindow.open(marker.getMap(), marker); // Marker に InfoWindow を表示する
    });
}

setupMarker で Marker と InfoWindow の表示を実装しています。

コメントにもありますが、、、

  1. 表示中の InfoWindow があれば非表示にして、
  2. Marker と InfoWindow を表示して、
  3. InfoWindow を stock に追加する

ってことをやってます。

InfoWindow が消されても再度表示できるように Marker に addListener でクリックイベントを設定しています。

/**
 * 表示中の InfoWindow があれば非表示にする。
 * 
 */
function disableCurrentInfoWindow () {
    if(this.currentInfoWindow) {
        this.currentInfoWindow.close();
    }
}

「表示中の InfoWindow があれば非表示にして」という処理を担当しているメソッドです。

表示中の InfoWindow は、スクリプトの先頭で var currentInfoWindow; と宣言してあります。
InfoWindow を非表示にするのは close() メソッドを呼ぶだけです。

次は、この script.js の全文になります。

/**
 * 表示中の InfoWindow オブジェクト。
 * 
 */
var currentInfoWindow;

/**
 * 生成した InfoWindow をストックする。
 * 
 */
var stock = new InfoWindowStock();

$(function() {
    var geocoder = new google.maps.Geocoder();

    // 初期表示
    var address = $('#address').val();
    geocoder.geocode({'address': address}, callbackRender);

    // 住所が入力された場合の対応
    $('#address').change(function(event) {
        address = $(this).val();
        geocoder.geocode({'address': address}, callbackRender);
    });
});

/**
 * ジオコーダの結果を取得したときに実行するコールバック関数。
 * 
 * この関数内で GoogleMap を出力する。
 * 
 * @param results ジオコーダの結果
 * @param status ジオコーディングのステータス
 * 
 */
function callbackRender(results, status) {
    if(status == google.maps.GeocoderStatus.OK) {
        var options = {
            zoom: 18,
            center: results[0].geometry.location, // 指定の住所から計算した緯度経度を指定する
            mapTypeId: google.maps.MapTypeId.ROADMAP // 「地図」で GoogleMap を出力する
        };
        var gmap = new google.maps.Map(document.getElementById('map-canvas'), options);
            // #map-canvas に GoogleMap を出力する

        setupMarker(gmap, results[0].geometry.location);
            // 初期値の住所から計算した緯度経度の位置に Marker を立てる
        google.maps.event.addListener(gmap, 'click', function(event) {
            // GoogleMap 上で左クリックがあったら、、、
            setupMarker(gmap, event.latLng);
                // その場所に Marker を立てる
        });

        adjustMapSize();
    }
}

/**
 * 指定の場所に InfoWindow を設定した Marker を表示する。
 * 
 * @param  {Object} map Marker を立てる GoogleMap オブジェクト
 * @param  {Object} location Marker を立てる位置
 */
function setupMarker(map, location) {
    var marker = new google.maps.Marker({map: map, position: location}); // Marker オブジェクトを生成する

    this.disableCurrentInfoWindow(); // 表示中の InfoWindow があれば非表示にする

    currentInfoWindow = createInfoWindow(location.k, location.A); // InfoWindow オブジェクトを生成し、、、
    currentInfoWindow.open(marker.getMap(), marker); // InfoWindow を表示し、、、
    stock.put(location, currentInfoWindow); // stock に追加する

    google.maps.event.addListener(marker, 'click', function(event) { // Marker がクリックされたら、、、
        disableCurrentInfoWindow(); // 表示中の InfoWindow があれば非表示にし、、、
        currentInfoWindow = stock.get(event.latLng); // stock から InfoWindow を取り出して、、、
        currentInfoWindow.open(marker.getMap(), marker); // Marker に InfoWindow を表示する
    });
}

/**
 * 表示中の InfoWindow があれば非表示にする。
 * 
 */
function disableCurrentInfoWindow () {
    if(this.currentInfoWindow) {
        this.currentInfoWindow.close();
    }
}

/**
 * InfoWindow オブジェクトを生成する。
 * 
 * @param  {Number} latitude 緯度
 * @param  {Number} longitude 経度
 * @return {Object} InfoWindow オブジェクト
 */
function createInfoWindow(latitude, longitude) {
    var infoWindow = new google.maps.InfoWindow({
        content: createTag(latitude, longitude), // InfoWindow に表示するコンテンツ
        // maxWidth: 1000 // width は CSS で制御するようにしたのでコメントアウト
    });
    return infoWindow;
}

/**
 * InfoWindow 内に設定する HTML を生成する。
 *
 * HTML の生成は Underscore.js を使い、テンプレートは index.html 内に定義してある。
 * 
 * @param  {Number} latitude 緯度
 * @param  {Number} longitude 経度
 * @return {Object} InfoWindow に設定するタグ
 */
function createTag(latitude, longitude) {
    var template = _.template($('#infowindow_template').text());
    var tag = template({latitude: latitude, longitude: longitude});
    return tag;
}

/**
 * GoogleMap を表示する div タグのサイズを調整する。
 * 
 */
function adjustMapSize() {
    var padding = $('#header-hollow').height(); // 住所入力欄の height を取得

    var mapCanvas = $('#map-canvas');
    mapCanvas.css("height", ($(window).height() - mapCanvas.offset().top) - padding + "px");
}

3-5. css/style.css

CSS も index.html と同様に変更なしです。

@charset "utf-8";

#map-canvas {
    width: 100%;
}

#header-hollow {
    padding: 10px 0px;
}

.info-window {
    width: 250px;
    display: table-cell;
    vertical-align: middle;
}

#main {
    width: 820px;
}

#right-side {
    width: 334px;
    /*background-color: #dcdcdc;*/
}

.label-ad {
    margin-bottom: 10px;
    padding: 5px 10px;
    background-color: #dcdcdc;
}

.label-ad span {
    font-weight: bold;
}

.adsense {
    width: 304px;
    height: 254px;
    margin-top: 10px;
    margin-bottom: 15px;
    padding: 1px;
    border: solid 1px;
}

.amazon-book {
    margin: 10px 15px;
}

footer {
    text-align: center;
    padding: 25px 0;
    margin-top: 10px;
    background-color: #dcdcdc;
}

4. サンプル

このサンプルプログラムは、次の場所に公開してあります。

地図上をクリックすると次々に Marker が立ち InfoWindow も表示されますが、表示されていた InfoWindow は非表示になります。

v0.4 が最新の間は http://labo.tomoyamkung.net/googlemap-sample/ でも同じものが表示されます。

5. まとめ

問題点を解消して、さらにそれっぽい感じになってきました。

今回は InfoWindow の表示について排他制御を行いましたが、同様のことを Marker に対しても行うことができます。「いくつか Marker を立てたけど、今はこのエリアにある Marker だけ表示したい」ということができるので、次回はその辺りのサンプルを実装してみます。

6. プログラムを Github に登録しました

今回の記事で使用したサンプルプログラムを Github に登録しました。

よかったら参考にしてみてください。

7. その他の GoogleMap に関する記事

その他の GoogleMap に関する記事は次の通りです。
気になる記事があったらぜひチェックしてみてください!

Googleアドセンス用(PC)

  • このエントリーをはてなブックマークに追加
  • follow us in feedly

関連記事

Head in Hands

JSON をオブジェクトに変換するときに注意したいこと

おそらくですが、これから書く内容は常識なんだと思います。 が、その常識を知らなかったためにかなりの

記事を読む

icatch-checklist_4439276478_mini-thumbnail

BootstrapValidator の基本的なバリデーションを試すサンプルを作成しました

TwitterBootstrap3 用のバリデーションチェックプラグイン BootstrapVali

記事を読む

icatch-where

jQuery を使ってファイルアップロードフォームのファイルが選択されているかを確認する方法

HTML に設置したファイルアップロードフォームにファイルが選択されているかを確認する方法を調べまし

記事を読む

icatch-plug_8436280178_mini-thumbnail

TwitterBootstrap3 用のバリデーションチェックプラグイン BootstrapValidator の導入から Live チェックまでを試してみた

BootstrapValidator は、TwitterBootstrap3 で構築したサイト用の

記事を読む

icatch-googlemap_3384995399_mini-thumbnail

GoogleMap を使って住所から緯度・経度を計算する

先日も GoogleMpa のジオコーディングを使った地図を表示するメモをアップしましたが、今回も

記事を読む

icatch-googlemap_3384984991_mini-thumbnail

住所から GoogleMap を表示するサンプル

ここ直近の2プロジェクトで緯度・経度(もしくは住所)から GoogleMap を表示する要件がありま

記事を読む

icatch-browser_3376956889_mini-thumbnail

GoogleMap サンプルのプロジェクトに Grunt の LiveReload を有効にする設定を追加しました

ウチのブログで公開している GoogleMaps API を使った GoogleMap のサンプルプ

記事を読む

icatch-exception_2482521750_mini-thumbnail

Error オブジェクトの種類と独自例外オブジェクトの作成方法

JavaScript で例外オブジェクトを扱ったことがなかったので、簡単な内容ですが例外オブジェクト

記事を読む

icatch-googlemap_4866826566_mini-thumbnail

GoogleMap の Marker に InfoWindow を設定するサンプル

GoogleMap の Marker には InfoWindow を使って付加情報を設定できます。

記事を読む

icatch-switch_mini

jQuery を使って表示する画像を setInterval を使って切り替える

画面に表示する画像を一定期間で切り替えるスニペットです。 スライドショー的なプラグインで済ますこと

記事を読む

Googleアドセンス用(PC)

Message

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です


× 3 = 十八

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Googleアドセンス用(PC)

icatch-jersey_multi_pathparams
Jerseyの@PathParamはスラッシュの間に複数指定できる

http://hoge-api/user/{id}.{format}

icatch-vagrant_box_customize
VagrantのBoxファイルをカスタマイズして独自のBoxファイルを作成する

配布されている Vagrant の Box ファイルを使って検証環境を

icatch-2015-006-1
バリデーションチェックにJava8のOptionalを使ってスマートに書く(自分比)

Web アプリのバリデーションチェックにアノテーションを使うことが増え

icatch-2015-005-1
ユニットテストの偏りを防ぐ命名規則の付け方

ユニットテスト名に以下の命名規則を付けるようにして二ヶ月ぐらい経った。

icatch-2015-004-1
Vagrantで起動したCentOS上のOctopressをホストOSから確認する設定

タイトルの通りだが、Vagrant を使って起動した CentOS に

→もっと見る

PAGE TOP ↑