GoogleMapAPIv3まとめ。

# GoogleMapAPIv3まとめ

GoogleMapAPI v3はグーグルマップを操作するAPIです。

このAPIを使うとグーグルマップでオリジナルの地図を作ることができます。

WebブラウザでみるアプリのためのJavaScriptAPIについて書いています。

[Google Maps JavaScript API](https://developers.google.com/maps/documentation/javascript/tutorial?hl=ja)

## サンプルを作る

GoogleMapを使ったアプリは簡単に言うと以下の３つが基本だと思います。

マップを作る

マーカー打つ

吹き出し表示

これらを踏まえたサンプルを作ってみます。

### APIを読み込む

HTMLのbody閉じタグ直前もしくはhead内でAPIを読み込みます。

~~~ js
<script type="text/javascript" src="http://maps.googleapis.com/maps/api/js"></script>
~~~

### どこにJavaScriptを書くのか

GoogleMapApiのロード完了イベントで実行する関数を指定します。

~~~ js
<script type="text/javascript" src="http://maps.googleapis.com/maps/api/js"></script>
<script type="text/javascript">
function initialize() {
  // ここが処理を書く起点
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
~~~

### 初期表示

地図を作ります。

まずHTMLのbodyタグの中に好きなid名を付けたDivを設置します。

地図の幅高さはCSSで指定してください。

~~~ html
<style>
#myMap {
  width: 500px;
  height: 500px;
}
</style>
<body>
<div id="myMap"></div>
~~~

表示したい場所の緯度経度を地図の中心として設定します。

LatLngオブジェクトにパラメータとして緯度、経度を渡します。

今回は[グーグルマップ](https://www.google.co.jp/maps/@36.8604464,136.9912962,15z)の氷見漁港を右クリックして「この場所について」で表示されたの緯度経度を使用しました。

先ほどのDivとマップオプション(センターに氷見漁港の緯度経度、初期ズームを10に指定)をパラメータとしてMapオブジェクトを作成します。

~~~  js
function initialize() {
  // 緯度経度
  var latLng = new google.maps.LatLng(36.861751, 136.991468);
  // マップオプション
  var mapOptions = {
    zoom: 10,
    center: latLng
  };
  var div = document.getElementById("myMap");
  // マップ生成
  var map = new google.maps.Map(div, mapOptions);
}
~~~

ここまでで幅500px × 高さ500pxの氷見漁港を中心とした地図が表示されます。

[ソース1](https://gist.github.com/niiyz/4f7c603625ab2b054931)

### マーカーを打つ

クリックした地点にマーカーを表示します。

Mapオブジェクトのクリックイベントを使います。

クリックイベントで取得できる緯度経度をポジションとしてMarkerオブジェクトを作ります。

~~~  js
// Mapクリックイベント
google.maps.event.addListener(map, 'click', function(event) {
  // クリック地点の緯度経度
  var position = event.latLng;
  // マーカー生成
  var marker = new google.maps.Marker({
    position: position,
    map: map
  });
});
~~~

[ソース2](https://gist.github.com/niiyz/47f74dd96da4c51b8f95)

### マーカー移動

地図上に打ったマーカーをドラッグ&ドロップで移動できるようにします。

Markerオブジェクトを生成するときのオプションにdraggableをtureとします。

~~~ js
// マーカー生成
var marker = new google.maps.Marker({
  draggable: true,// ドラッグ許可
  position: position,
  map: map
});
~~~

[ソース3](https://gist.github.com/niiyz/c2fdb08840866f57f50e)

### 吹き出し表示

#### 吹き出し生成

マーカーを右クリックしたら吹き出しを表示するようにします。

吹き出しの中にコンテントとしてHTMLを挿入できます。

今回は右クリックしたマーカーが持っている緯度経度情報を表示してみます。

~~~ js
// マーカー生成
var marker = new google.maps.Marker({
  draggable: true,// ドラッグ許可
  position: position,
  map: map
});
// マーカー右クリックイベント
google.maps.event.addListener(marker, 'rightclick', function(event) {
    // 吹き出し表示内容
    var content = '';
    content += '<div>' + marker.position + '</div>';
    // 吹き出し生成
    var infoWindow = new google.maps.InfoWindow({
      content: content,
      position: marker.position,
      pixelOffset: new google.maps.Size(0, -25)
    });
    // 吹き出し表示
    infoWindow.open(map);
});
~~~

InfoWindowオブジェクトのオプションのpixelOffsetはpositionからの距離になります。

デフォルトのマーカーの目玉の黒い部分に吹き出しの先を合わせたいので距離を北に25px移動しています。

[ソース4](https://gist.github.com/niiyz/76ede084d3eddb3ecb2e)

#### 吹き出し削除

ドラッグ&ドロップして移動後に吹き出しがドラッグ前の位置にあったらかっこわるいのでドラッグ後に消します。

マーカーのイベントとして設定します。

変数infoWindowを定義する場所を変更していることに注意してください。

消すときはsetMapにnullを指定します。

~~~ js
// 吹き出しオブジェクト格納変数
var infoWindow;
// マーカー右クリックイベント
google.maps.event.addListener(marker, 'rightclick', function(event) {
    // 吹き出し表示内容
    var content = '';
    content += '<div>' + marker.position + '</div>';
    // 吹き出し生成
    infoWindow = new google.maps.InfoWindow({
      content: content,
      position: marker.position,
      pixelOffset: new google.maps.Size(0, -25)
    });
    // 吹き出し表示
    infoWindow.open(map);
});
// マーカードラッグ完了イベント
google.maps.event.addListener(marker, 'dragend', function(event) {
  // 吹き出し存在確認
  if (infoWindow) {
    // 吹き出し削除
    infoWindow.setMap(null);
    infoWindow = null;
  }
});
~~~

[ソース5](https://gist.github.com/niiyz/15b743da02836f3db6fc)

複数の吹き出しを表示してマーカーを動かして対象マーカーの吹き出しのみ消えることを確認してください。

#### 吹き出し表示管理

変数の位置を変えたついでによろしくない動作を修正します。

右クリックで吹き出しを作成するときに右クリックを連打すると連打した回数分吹き出しが作られてしまいます。

すでに吹き出しがある場合は作らないという処理を入れます。

さらにドラッグ移動後に消す以外に吹き出しにはデフォルトで×ボタンが付いています。

×ボタンを押して消したときのイベントを使って「消した」ということを覚えておきます。

~~~ js
// 右クリック
google.maps.event.addListener(marker, 'rightclick', function(event) {
    // 吹き出しが存在する場合はここで終了
    if (infoWindow) {
      return;
    }
    // 吹き出し表示内容
    var content = '';
    content += '<div>' + marker.position + '</div>';
    // 吹き出し生成
    infoWindow = new google.maps.InfoWindow({
      content: content,
      position: marker.position,
      pixelOffset: new google.maps.Size(0, -25)
    });
    // 吹き出しを右上の×で閉じたイベント
    google.maps.event.addListener(infoWindow, 'closeclick', function(event) {
      infoWindow = null;
    });
    // 吹き出し表示
    infoWindow.open(map);
});
~~~

[ソース6](https://gist.github.com/niiyz/0df7c748fed59e9f43c4)

### MVCObjectを継承する。

クロージャーを使って処理を書いているとだいぶ複雑になります。

今までのソースでマーカー部分をMyMarkerとしてMVCObjectを継承したオブジェクトにしてみます。

マップのクリックイベントの中をすっきりさせます。

~~~ js
  // マップイベント
  google.maps.event.addListener(map, 'click', function(event) {
    var position = event.latLng;
    var myMarker = new MyMarker(map, position);
  });
~~~

マイマーカークラスに処理を閉じ込めました。

今までmarker.positionとしていた部分をmarker.get('position')として取得しています。

~~~ js
/**
 * マイマーカー
 *
 * @param {google.maps.Map} map
 * @param {google.maps.LatLng} position
 *
 * @constructor
 */
function MyMarker(map, position) {
  this.set('map', map);
  this.set('position', position);

  var marker = new google.maps.Marker({
    draggable: true,
    title: '私の作ったマーカー'
  });

  // マーカーイベント
  var infoWindow; // 吹き出し
  // 右クリック
  google.maps.event.addListener(marker, 'rightclick', function (event) {
    if (infoWindow) {
      return;
    }
    var content = '';
    content += '<div>' + marker.get('position') + '</div>';
    // 吹き出し生成
    infoWindow = new google.maps.InfoWindow({
      content: content,
      position: marker.get('position'),
      pixelOffset: new google.maps.Size(0, -25)
    });
    // バツで閉じた
    google.maps.event.addListener(infoWindow, 'closeclick', function (event) {
      infoWindow = null;
    });
    infoWindow.open(map);
  });
  // ドラッグ完了
  google.maps.event.addListener(marker, 'dragend', function (event) {
    if (infoWindow) {
      // 吹き出し削除
      infoWindow.setMap(null);
      infoWindow = null;
    }
  });

  // バインド
  marker.bindTo('map', this);
  marker.bindTo('position', this);

}
// MVCObject継承
MyMarker.prototype = new google.maps.MVCObject();
~~~

[ソース7](https://gist.github.com/niiyz/8c28c3acae3f344dd99d)

## マップ表示

### 標準コントロール非表示

デフォルトだと拡大縮小、ズームのコントロールが表示されています。

地図によってはコントロールが不要なこともありますので消してみます。

~~~ js
var mapOptions = {
  zoom: 10,
  center: latLng,
  zoomControl: false,       // ズーム非表示
  streetViewControl: false, // ストリートビュー非表示
  mapTypeControl: false,    // マップ切り替え非表示
  panControl: false         // 移動コントロール非表示
};
~~~

全部消えたと思います。

グーグルのロゴは消せないみたいです。

[ソース8](https://gist.github.com/niiyz/e4957d7303f249dfc3a1)

### ズーム制限

最大ズームと最小ズームを指定します。

~~~ js
var mapOptions = {
  zoom: 10,
  center: latLng,
  minZoom: 12,
  maxZoom: 15
};
~~~

### ドラッグ移動制限

地図を掴んでの移動ができなくなります。

~~~ js
var mapOptions = {
  zoom: 10,
  center: latLng,
  draggable: false,// ドラッグ制限
};
~~~

### ダブルクリックズーム制限

ダブルクリックでズームが変更する処理を制御します。

地図クリックでマーカーを打つ処理を記述した場合など他の処理とかぶる場合など使用したくない場合に使います。

~~~ js
var mapOptions = {
  zoom: 10,
  center: latLng,
  disableDoubleClickZoom: false// ダブルクリックズーム制限
};
~~~

### スマートフォン用に全画面を地図にする

CSSでhtml,body,地図にするdivの幅高さを100%にします。

marginも0にして隙間を埋めます。

~~~ html
<style>
html, body, #myMap {
  width: 100%;
  height: 100%;
  margin: 0px;
}
</style>
<body>
<div id='myMap'></div>
~~~

[ソース9](https://gist.github.com/niiyz/a3000048a78057971fd4)

### スマートフォン判定

~~~ js
/**
 * スマホ判定
 * @return {Boolean}
 */
function isSmartPhone() {
    var userAgent = navigator.userAgent;
    if (userAgent.indexOf('iPhone') != -1 ||
        userAgent.indexOf('iPod') != -1 ||
        (userAgent.indexOf('Android') != -1 && userAgent.indexOf('Mobile') != -1)) {
        return true;
    }
    return false;
}
~~~

### モバイル判定

~~~ js
/**
 * モバイル判定
 * @return {Boolean}
 */
function isMobile() {
    var userAgent = navigator.userAgent;
    if (userAgent.indexOf('iPhone') != -1 ||
        userAgent.indexOf('iPad') != -1 ||
        userAgent.indexOf('iPod') != -1 ||
        userAgent.indexOf('Android') != -1) {
        return true;
    }
    return false;
}
~~~

## マップ操作

### 地図内にテキストボックスを表示

Mapオブジェクトのcontrolesにテキストボックスを追加します。

左上に追加したいのでTOP_LEFTを指定しています。

~~~ js
var textBox = document.createElement('input');
textBox.style.width = '250px';
textBox.style.marginTop = '30px';
textBox.value = 'てすと';
map.controls[google.maps.ControlPosition.TOP_LEFT].push(textBox);
~~~

[ソース10](https://gist.github.com/niiyz/7d5d2c5a8086dde2eee0)

### 住所から緯度経度を取得する

テキストボックスに入力した文字列から緯度経度を取得します。

緯度経度を取得したらそれを使用して地図のセンターを変更します。

~~~ js
function changeCenter(map, address) {
    var geocoder = new google.maps.Geocoder();
    geocoder.geocode({'address': address}, function (results, status) {
        if (status == google.maps.GeocoderStatus.OK) {
            var location = results[0].geometry.location;
            var lat = location.lat();
            var lng = location.lng();
            var latlng = {lat: lat, lng: lng};
            map.setCenter(latlng);
        } else {
            console.log("Geocode was not successful for the following reason: " + status);
        }
    });
}
~~~

[ソース11](https://gist.github.com/niiyz/a58a0967865aae8bf9e5)

### 郵便番号から緯度経度を取得する。

住所と全く同じです。

ハイフンが入っていてもいなくても関係ないようです。

9330016でも933-0016でもOKです。

~~~ js
function changeCenter(map, zip) {
    var geocoder = new google.maps.Geocoder();
    geocoder.geocode({'address': zip}, function (results, status) {
        if (status == google.maps.GeocoderStatus.OK) {
            var location = results[0].geometry.location;
            var lat = location.lat();
            var lng = location.lng();
            var latlng = {lat: lat, lng: lng};
            map.setCenter(latlng);
        } else {
            console.log("Geocode was not successful for the following reason: " + status);
        }
    });
}
~~~

[ソース12](https://gist.github.com/niiyz/416284532c11ee5c4690)

### マーカーをオリジナル画像に変更。

#### Base64形式

~~~ js
var icon = 'data:image/png;base64,iVBORw0KG・・・省略・・・';
var options = {
      map: map,
      position: latLng,
      icon: icon,
};
var marker = new google.maps.Marker(options);
~~~

[ソース13](https://gist.github.com/niiyz/f8357a4cd108dc1f84fa)

#### パス指定

画像ファイルを設置します。

~~~ js
var icon = {
 url: 'images/icon1.png'
};
var options = {
      map: map,
      position: latLng,
      icon: icon,
};
var marker = new google.maps.Marker(options);
~~~

### 現在地を取得

getCurrentPosition()を使用します。

ブラウザに現在位置の取得を許可しない場合エラーになります。

許可した場合に取得した現在地情報を地図の中心にしてみます。

~~~ js
function gotoCurrentPosition(map) {
    if (navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(
            // 成功処理
            function (info) {
                var lat = info.coords.latitude;
                var lng = info.coords.longitude;
                var center = new google.maps.LatLng(lat, lng);
                map.setCenter(center);
            },
            // エラー処理
            function (info) {
                console.log('現在地取得エラー: ' + info.code);
                return;
            }
        );
    } else {
        console.log('本ブラウザではGeolocationが使えません');
        return;
    }
}
~~~

[ソース14](https://gist.github.com/niiyz/c5d79b98d917350c7f54)

### 現在地を追跡

1回のみの取得はgetCurrentPosition()を使用しましたが追跡はwatchPosition()を使用します。

オプションで精度やタイムを設定します。

enableHighAccuracyをtrueにするとより正確な位置情報が得られます。

watchPositionオプション

|パラメータ名|意味|
|:--|:--|
|enableHighAccuracy|高精度 true or false|
|timeout|位置取得間隔(ミリ秒) 6000=6秒|
|maximumAge|有効期限(ミリ秒)|

~~~ js
function watchCurrentPosition(marker) {
    if (navigator.geolocation) {
        navigator.geolocation.watchPosition(
            // 成功処理
            function (info) {
                console.log(info.timestamp);
                var lat = info.coords.latitude;
                var lng = info.coords.longitude;
                var current = new google.maps.LatLng(lat, lng);
                marker.setPosition(current);
            },
            // エラー
            function (info) {
                console.log('現在地取得エラー: ' + info.code);
                return;
            },
            // オプション
            {enableHighAccuracy: true, timeout: 6000, maximumAge: 600000}
        );
    } else {
        console.log('本ブラウザではGeolocationが使えません');
        return;
    }
}
~~~

[ソース15](https://gist.github.com/niiyz/5501af745793153a712b)