Google Mapsを埋め込んで白黒にしたりなんかしてマーカーをオリジナルのものにしたいというありがちな依頼を受けるたびに、それ前もやったけどやり方忘れたわテヘペロってなるのでまとめておこうと思った。あとコピペすればいい話なんだけど複数のマップ埋め込むとコードがやたらと冗長になるのが嫌だった。あと単純にES6のClass使ってみたかった。
一応Google Mapsの基本的な埋め込み方もおさらいしておくと
Google Maps開いて場所指定して右クリックしてこの場所について選んで地図の埋め込み選んでコードコピって貼り付ける!!!これで分からなければググれ!!!
でしたね。簡単ですね。しかしこれでは地図をカスタマイズすることはできず、カスタマイズしたければGoogle Maps APIを利用するしかない。
目次
Google Maps APIを用いてGoogle Mapsを埋め込む方法のおさらい
下準備
去年あたりからGoogle Maps APIを叩くためにはAPIキーが必須になったのでとりあえずこれを取得しないと話にならない。調べればいくらでも出てくるのではしょって取得方法はGoogle Maps API ガイドを参考にどうぞ。
取得できたらGoogle Maps APIをロードするおまじないを記述しておく。
1 |
<script src="https://maps.googleapis.com/maps/api/js?key=yourAPIKey" type="text/javascript"></script> |
これで準備はOK
Google Mapsインスタンスを生成する
Google公式ドキュメントから引用すると
1 2 3 4 5 6 7 |
var map; function initMap() { map = new google.maps.Map(document.getElementById('map'), { center: {lat: -34.397, lng: 150.644}, zoom: 8 }); } |
こんな関数を作ってonloadとかで実行させれば良い。ちなみに上記の例だとidがmapである要素に地図が表示されるはずだが、cssでmapに高さを持たせてあげないと何も表示されないので注意。
白黒にしてマーカーをオリジナル画像に変えてみる
前述のinitMapという関数に色々オプションを記述してあげれば好みにカスタマイズした地図が作れるわけだ。実際に白黒にしてマーカーをオリジナル画像に変更するようにオプション指定して書いてみると
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
function initMap() { const latlng = new google.maps.LatLng(35.689407, 139.700306); const mapOptions = { zoom: 15, center: latlng, mapTypeControlOptions: { mapTypeIds: ['Custom', google.maps.MapTypeId.ROADMAP] } }; const map = new google.maps.Map(document.getElementById('map'), mapOptions); const icon = new google.maps.MarkerImage('marker.png'); const markerOptions = { position: latlng, map: map, icon: icon, }; const marker = new google.maps.Marker(markerOptions); const styleOptions = [{ "stylers": [{ "saturation": -100 }, { "visibility": "simplified" }, { "lightness": 0 } ] }]; const styledMapOptions = { name: 'Custom' } const customType = new google.maps.StyledMapType(styleOptions, styledMapOptions); map.mapTypes.set('Custom', customType); map.setMapTypeId('Custom'); } |
結構長い。1ページに複数の地図を埋め込むとなるとこれをずらずら並べるはめになる。それはあまりに美しくないので自分用に整理しておこうと思ったわけ。
ES6でClass化してみる
先に出来上がったコードを記載する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
class GoogleMap { constructor(options = {}) { /** * @param {string} styledMapName カスタムマップ名 * @param {string} targetId ターゲットID * @param {string} lat 緯度 * @param {string} lng 経度 * @param {number} zoom ズームレベル * @param {string} iconImg マーカー画像 * @param {number} saturation 彩度 * @param {string} visibility マップ上での要素の表示有無 * @param {number} lightness 明度 * @param {string} hue 基本色 * @param {number} gamma ガンマ補正 * @param {boolean} invertLightness 明度の反転 */ this.styledMapName = options.styledMapName || 'CustomStyle'; this.targetId = options.targetId || 'map'; this.lat = options.lat || '35.689407'; this.lng = options.lng || '139.700306'; this.zoom = options.zoom || 15; this.iconImg = options.iconImg || 'marker.png'; this.saturation = options.saturation || 0; this.visibility = options.visibility || 'on'; this.lightness = options.lightness || 0; this.hue = options.hue || ''; this.gamma = options.gamma || 1; this.invertLightness = options.invertLightness || false; this.init(); } init() { const latLng = new google.maps.LatLng(this.lat, this.lng); const customMapTypeId = 'Custom'; const mapOptions = { zoom: this.zoom, center: latLng, mapTypeControlOptions: { mapTypeIds: [customMapTypeId, google.maps.MapTypeId.ROADMAP] } }; const map = new google.maps.Map(document.getElementById(this.targetId), mapOptions); const icon = new google.maps.MarkerImage(this.iconImg); const markerOptions = { position: latLng, map: map, icon: icon, }; const marker = new google.maps.Marker(markerOptions); const styleOptions = [{ 'stylers': [{ 'saturation': this.saturation }, { 'visibility': this.visibility }, { 'lightness': this.lightness }, { 'hue': this.hue }, { 'gamma': this.gamma }, { 'invert_lightness': this.invertLightness }, ] }]; const styledMapOptions = { name: this.styledMapName }; const customType = new google.maps.StyledMapType(styleOptions, styledMapOptions); map.mapTypes.set(customMapTypeId, customType); map.setMapTypeId(customMapTypeId); } } |
こんな感じ。なんのことはない、いつもコピペしているものをまとめただけである。上記クラスを外部ファイルにして読み込み、html内で
1 2 3 |
<script> var shinjuku = new GoogleMap(); </script> |
とかってインスタンスを生成すれば良い。
コンストラクタに色々引数を渡せるようにしておいたが、特に指定する必要がない場合に対応するためor演算子で初期値を設定しておいた。引数を指定しなければidがmapである要素にマーカーにmarker.pngが使われている新宿駅を中心とした地図が表示されるはずだ。ちょっと指定してみると
1 2 3 4 5 6 7 8 |
<script> var fukuoka = new GoogleMap({ targetId: 'fukuoka',//ターゲットID lat: '36.708556', //緯度 lng: '136.932030', //経度 saturation: 1,//彩度 }) </script> |
とかってやればidがfukuokaである要素に福岡駅の地図が白黒で表示されるはずだ。
IE対策
またお前かという感じだが、上記コードはES6構文を用いているためInternet Explorerでは動かない。いい加減にして欲しい。その他ブラウザでもES6構文では動かない場合もあるので対応表をチェックしておこう。
ECMAScript 6 compatibility table
じゃあどうするかっていうとES5にトランスパイルしてあげれば良い。Babelを使っておけば大体のブラウザには対応できるはず、多分。
Babel · The compiler for writing next generation JavaScript
一応トランスパイル後のコードも記載しておく。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
'use strict'; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var GoogleMap = function () { function GoogleMap() { var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; _classCallCheck(this, GoogleMap); /** * @param {string} styledMapName カスタムマップ名 * @param {string} targetId ターゲットID * @param {string} lat 緯度 * @param {string} lng 経度 * @param {number} zoom ズームレベル * @param {string} iconImg マーカー画像 * @param {number} saturation 彩度 * @param {string} visibility マップ上での要素の表示有無 * @param {number} lightness 明度 * @param {string} hue 基本色 * @param {number} gamma ガンマ補正 * @param {boolean} invertLightness 明度の反転 */ this.styledMapName = options.styledMapName || 'CustomStyle'; this.targetId = options.targetId || 'map'; this.lat = options.lat || '35.689407'; this.lng = options.lng || '139.700306'; this.zoom = options.zoom || 15; this.iconImg = options.iconImg || 'marker.png'; this.saturation = options.saturation || 0; this.visibility = options.visibility || 'on'; this.lightness = options.lightness || 0; this.hue = options.hue || ''; this.gamma = options.gamma || 1; this.invertLightness = options.invertLightness || false; this.init(); } _createClass(GoogleMap, [{ key: 'init', value: function init() { var latLng = new google.maps.LatLng(this.lat, this.lng); var customMapTypeId = 'Custom'; var mapOptions = { zoom: this.zoom, center: latLng, mapTypeControlOptions: { mapTypeIds: [customMapTypeId, google.maps.MapTypeId.ROADMAP] } }; var map = new google.maps.Map(document.getElementById(this.targetId), mapOptions); var icon = new google.maps.MarkerImage(this.iconImg); var markerOptions = { position: latLng, map: map, icon: icon }; var marker = new google.maps.Marker(markerOptions); var styleOptions = [{ 'stylers': [{ 'saturation': this.saturation }, { 'visibility': this.visibility }, { 'lightness': this.lightness }, { 'hue': this.hue }, { 'gamma': this.gamma }, { 'invert_lightness': this.invertLightness }] }]; var styledMapOptions = { name: this.styledMapName }; var customType = new google.maps.StyledMapType(styleOptions, styledMapOptions); map.mapTypes.set(customMapTypeId, customType); map.setMapTypeId(customMapTypeId); } }]); return GoogleMap; }(); |
IEが対応ブラウザに含まれている限りはES6でjsを書く場合はgulp-babelなどを使って自動でトランスパイルする環境を構築する必要があるだろう。IEなんて滅びればいいのに。
なんというか別にクラス化する必要もなかったのでは?という感じもするが今後もES6に慣れる意味でも積極的に使っていきたい。