2 * Google layer using Google Maps API
5 /* global google: true */
7 L
.Google
= L
.Class
.extend({
8 includes
: L
.Mixin
.Events
,
18 continuousWorld
: false,
21 backgroundColor
: '#dddddd'
25 // Possible types: SATELLITE, ROADMAP, HYBRID, TERRAIN
26 initialize: function (type
, options
) {
29 this._ready
= L
.Google
.isGoogleMapsReady();
31 L
.Util
.setOptions(this, options
);
33 this._googleApiPromise
= this._ready
? Promise
.resolve(window
.google
) : L
.Google
.createGoogleApiPromise();
35 this._googleApiPromise
38 _this
._initMapObject();
42 this._type
= type
|| 'SATELLITE';
45 onAdd: function (map
, insertAtTheBottom
) {
47 this._googleApiPromise
50 _this
._insertAtTheBottom
= insertAtTheBottom
;
52 // create a container div for tiles
53 _this
._initContainer();
54 _this
._initMapObject();
57 map
.on('viewreset', _this
._reset
, _this
);
59 _this
._limitedUpdate
= L
.Util
.limitExecByInterval(_this
._update
, 150, _this
);
60 map
.on('move', _this
._update
, _this
);
62 map
.on('zoomanim', _this
._handleZoomAnim
, _this
);
64 //20px instead of 1em to avoid a slight overlap with google's attribution
65 map
._controlCorners
.bottomright
.style
.marginBottom
= '20px';
72 onRemove: function (map
) {
73 map
._container
.removeChild(this._container
);
75 map
.off('viewreset', this._reset
, this);
77 map
.off('move', this._update
, this);
79 map
.off('zoomanim', this._handleZoomAnim
, this);
81 map
._controlCorners
.bottomright
.style
.marginBottom
= '0em';
84 getAttribution: function () {
85 return this.options
.attribution
;
88 setOpacity: function (opacity
) {
89 this.options
.opacity
= opacity
;
91 L
.DomUtil
.setOpacity(this._container
, opacity
);
95 setElementSize: function (e
, size
) {
96 e
.style
.width
= size
.x
+ 'px';
97 e
.style
.height
= size
.y
+ 'px';
100 _initContainer: function () {
101 var tilePane
= this._map
._container
,
102 first
= tilePane
.firstChild
;
104 if (!this._container
) {
105 this._container
= L
.DomUtil
.create('div', 'leaflet-google-layer leaflet-top leaflet-left');
106 this._container
.id
= '_GMapContainer_' + L
.Util
.stamp(this);
107 this._container
.style
.zIndex
= 'auto';
110 tilePane
.insertBefore(this._container
, first
);
112 this.setOpacity(this.options
.opacity
);
113 this.setElementSize(this._container
, this._map
.getSize());
116 _initMapObject: function () {
117 if (!this._ready
|| !this._container
) return;
118 this._google_center
= new google
.maps
.LatLng(0, 0);
119 var map
= new google
.maps
.Map(this._container
, {
120 center
: this._google_center
,
123 mapTypeId
: google
.maps
.MapTypeId
[this._type
],
124 disableDefaultUI
: true,
125 keyboardShortcuts
: false,
127 disableDoubleClickZoom
: true,
129 streetViewControl
: false,
130 styles
: this.options
.mapOptions
.styles
,
131 backgroundColor
: this.options
.mapOptions
.backgroundColor
135 this._reposition
= google
.maps
.event
.addListenerOnce(map
, 'center_changed',
136 function () { _this
.onReposition(); });
139 google
.maps
.event
.addListenerOnce(map
, 'idle',
140 function () { _this
._checkZoomLevels(); });
141 google
.maps
.event
.addListenerOnce(map
, 'tilesloaded',
142 function () { _this
.fire('load'); });
143 //Reporting that map-object was initialized.
144 this.fire('MapObjectInitialized', {mapObject
: map
});
147 _checkZoomLevels: function () {
148 //setting the zoom level on the Google map may result in a different zoom level than the one requested
149 //(it won't go beyond the level for which they have data).
150 // verify and make sure the zoom levels on both Leaflet and Google maps are consistent
151 if ((this._map
.getZoom() !== undefined) && (this._google
.getZoom() !== this._map
.getZoom())) {
152 //zoom levels are out of sync. Set the leaflet zoom level to match the google one
153 this._map
.setZoom(this._google
.getZoom());
157 _reset: function () {
158 this._initContainer();
161 _update: function () {
162 if (!this._google
) return;
165 var center
= this._map
.getCenter();
166 var _center
= new google
.maps
.LatLng(center
.lat
, center
.lng
);
168 this._google
.setCenter(_center
);
169 if (this._map
.getZoom() !== undefined)
170 this._google
.setZoom(Math
.round(this._map
.getZoom()));
172 this._checkZoomLevels();
175 _resize: function () {
176 var size
= this._map
.getSize();
177 if (this._container
.style
.width
=== size
.x
&&
178 this._container
.style
.height
=== size
.y
)
180 this.setElementSize(this._container
, size
);
185 _handleZoomAnim: function (e
) {
186 var center
= e
.center
;
187 var _center
= new google
.maps
.LatLng(center
.lat
, center
.lng
);
189 this._google
.setCenter(_center
);
190 this._google
.setZoom(Math
.round(e
.zoom
));
194 onReposition: function () {
195 if (!this._google
) return;
196 google
.maps
.event
.trigger(this._google
, 'resize');
200 L
.Google
.isGoogleMapsReady = function () {
201 return !!window
.google
&& !!window
.google
.maps
&& !!window
.google
.maps
.Map
;
205 L
.Google
.asyncInitialize
= L
.Google
.isGoogleMapsReady
;
207 L
.Google
.maxApiChecks
= 10;
209 L
.Google
.apiCheckIntervalMilliSecs
= 500;
211 L
.Google
.createGoogleApiPromise = function () {
212 var checkCounter
= 0;
213 var intervalId
= null;
215 return new Promise(function (resolve
, reject
) {
216 intervalId
= setInterval(function () {
217 if (checkCounter
>= L
.Google
.maxApiChecks
&& !L
.Google
.isGoogleMapsReady()) {
218 clearInterval(intervalId
);
219 return reject(new Error('window.google not found after max attempts'));
221 if (L
.Google
.isGoogleMapsReady()) {
222 clearInterval(intervalId
);
223 return resolve(window
.google
);
226 }, L
.Google
.apiCheckIntervalMilliSecs
);