UNPKG

googlemaps-v3-utility-library

Version:
993 lines (866 loc) 27.2 kB
/** * @license * Copyright 2013 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @fileoverview Earth API library for Maps v3. * usage: var ge = new GoogleEarth(map);. * @author jlivni@google.com (Josh Livni). */ /** * @constructor * @param {google.maps.Map} map the Map associated with this Earth instance. */ function GoogleEarth(map) { if (!google || !google.earth) { throw 'google.earth not loaded'; } if (!google.earth.isSupported()) { throw 'Google Earth API is not supported on this system'; } if (!google.earth.isInstalled()) { throw 'Google Earth API is not installed on this system'; } /** * @const * @private * @type {string} */ this.RED_ICON_ = 'http://maps.google.com/mapfiles/kml/paddle/red-circle.png'; /** * @private * @type {google.maps.Map} */ this.map_ = map; /** * @private * @type {Node} */ this.mapDiv_ = map.getDiv(); /** * @private * @type {boolean} */ this.earthVisible_ = false; /** * @private * @type {string} */ this.earthTitle_ = 'Earth'; /** * @private * @type {Object} */ this.moveEvents_ = []; /** * @private * @type {Object} */ this.overlays_ = {}; /** * @private * @type {?Object} */ this.lastClickedPlacemark_ = null; /** * Keep track of each time the 3D view is reloaded/refreshed * @private * @type {number} */ this.displayCounter_ = 0; this.addEarthMapType_(); this.addEarthControl_(); } window['GoogleEarth'] = GoogleEarth; /** * @const * @type {string} */ GoogleEarth.MAP_TYPE_ID = 'GoogleEarthAPI'; GoogleEarth['MAP_TYPE_ID'] = GoogleEarth.MAP_TYPE_ID; /** * @const * @private * @type {string} */ GoogleEarth.INFO_WINDOW_OPENED_EVENT_ = 'GEInfoWindowOpened'; /** * @const * @private * @type {number} */ GoogleEarth.MAX_EARTH_ZOOM_ = 27; /** * @return {?google.earth.GEPlugin} The Earth API Instance. */ GoogleEarth.prototype.getInstance = function() { return this.instance_; }; GoogleEarth.prototype['getInstance'] = GoogleEarth.prototype.getInstance; /** * @private */ GoogleEarth.prototype.addEarthMapType_ = function() { var map = this.map_; var earthMapType = /** @type {google.maps.MapType} */({ tileSize: new google.maps.Size(256, 256), maxZoom: 19, name: this.earthTitle_, // The alt helps the findMapTypeControlDiv work. alt: this.earthTitle_, getTile: /** * @param {google.maps.Point} tileCoord the tile coordinate. * @param {number} zoom the zoom level. * @param {Node} ownerDocument n/a. * @return {Node} the overlay. */ function(tileCoord, zoom, ownerDocument) { var div = ownerDocument.createElement('DIV'); return div; } }); map.mapTypes.set(GoogleEarth.MAP_TYPE_ID, earthMapType); var options = /** @type {google.maps.MapTypeControlOptions} */({ mapTypeControlOptions: { mapTypeIds: [google.maps.MapTypeId.ROADMAP, google.maps.MapTypeId.SATELLITE, GoogleEarth.MAP_TYPE_ID] } }); map.setOptions(options); var that = this; google.maps.event.addListener(map, 'maptypeid_changed', function() { that.mapTypeChanged_(); }); }; /** * @private */ GoogleEarth.prototype.mapTypeChanged_ = function() { if (this.map_.getMapTypeId() == GoogleEarth.MAP_TYPE_ID) { this.showEarth_(); } else { this.switchToMapView_(); } }; /** * @private */ GoogleEarth.prototype.showEarth_ = function() { var mapTypeControlDiv = this.findMapTypeControlDiv_(); this.setZIndexes_(mapTypeControlDiv); this.addShim_(mapTypeControlDiv); this.controlDiv_.style.display = ''; this.earthVisible_ = true; if (!this.instance_) { this.initializeEarth_(); return; } this.refresh_(); }; /** * @private */ GoogleEarth.prototype.refresh_ = function() { this.overlays_ = {}; this.flyToMapView_(true); this.clearPlacemarks_(); this.displayCounter_++; this.clearMoveEvents_(); this.addMapOverlays_(); }; /** * Clear all marker position_changed events * @private */ GoogleEarth.prototype.clearMoveEvents_ = function() { for (var i = 0, evnt; evnt = this.moveEvents_[i]; i++) { google.maps.event.removeListener(evnt); } }; /** * Clear all features on this instance. * @private */ GoogleEarth.prototype.clearPlacemarks_ = function() { var features = this.instance_.getFeatures(); while (features.getFirstChild()) { features.removeChild(features.getFirstChild()); } }; /** * Fly to the current map zoom, add slight tilt. * @private * @param {boolean} tilt whether to teleport and tilt, or just flyto. */ GoogleEarth.prototype.flyToMapView_ = function(tilt) { var center = this.map_.getCenter(); var lookAt = this.instance_.createLookAt(''); var range = Math.pow(2, GoogleEarth.MAX_EARTH_ZOOM_ - this.map_.getZoom()); lookAt.setRange(range); lookAt.setLatitude(center.lat()); lookAt.setLongitude(center.lng()); lookAt.setHeading(0); lookAt.setAltitude(0); var ge = this.instance_; if (tilt) { // Teleport to the pre-tilt view immediately. ge.getOptions().setFlyToSpeed(5); ge.getView().setAbstractView(lookAt); lookAt.setTilt(15); // Fly to the tilt at regular speed in 200ms ge.getOptions().setFlyToSpeed(0.75); window.setTimeout(function() { ge.getView().setAbstractView(lookAt); }, 200); // Set the flyto speed back to default after the animation starts. window.setTimeout(function() { ge.getOptions().setFlyToSpeed(1); }, 250); } else { // Fly to the approximate map view at regular speed. ge.getView().setAbstractView(lookAt); } }; /** * @param {string|*} hex color value in rgb. * @param {string|*=} opacity -- in percentage. * @return {string} abgr KML style color. * @private */ GoogleEarth.getKMLColor_ = function(hex, opacity) { if (hex[0] == '#') { hex = hex.substring(1, 9); } if (typeof opacity == 'undefined') { opacity = 'FF'; } else { opacity = parseInt(parseFloat(opacity) * 255, 10).toString(16); if (opacity.length == 1) { opacity = '0' + opacity; } } var R = hex.substring(0, 2); var G = hex.substring(2, 4); var B = hex.substring(4, 6); var abgr = [opacity, B, G, R].join(''); return abgr; }; /** * @param {google.maps.MVCObject} overlay the map overlay. * @return {String} ID for the Placemark. * @private */ GoogleEarth.prototype.generatePlacemarkId_ = function(overlay) { var placemarkId = this.displayCounter_ + 'GEV3_' + overlay['__gme_id']; return placemarkId; }; /** * @param {google.maps.MVCObject} overlay the map overlay. * @return {google.earth.KmlPlacemark} placemark the placemark. * @private */ GoogleEarth.prototype.createPlacemark_ = function(overlay) { var placemarkId = this.generatePlacemarkId_(overlay); this.overlays_[placemarkId] = overlay; return this.instance_.createPlacemark(placemarkId); }; /** * @param {google.maps.Rectangle} rectangle the rectangle overlay. * @private */ GoogleEarth.prototype.createRectangle_ = function(rectangle) { var ge = this.instance_; var bounds = rectangle.getBounds(); var ne = bounds.getNorthEast(); var sw = bounds.getSouthWest(); var placemark = this.createPlacemark_(rectangle); placemark.setGeometry(ge.createPolygon('')); var ring = ge.createLinearRing(''); //set the style var style = this.createStyle_(rectangle); placemark.setStyleSelector(style); //create the rectangle var coords = ring.getCoordinates(); coords.pushLatLngAlt(ne.lat(), ne.lng(), 0); coords.pushLatLngAlt(ne.lat(), sw.lng(), 0); coords.pushLatLngAlt(sw.lat(), sw.lng(), 0); coords.pushLatLngAlt(sw.lat(), ne.lng(), 0); coords.pushLatLngAlt(ne.lat(), ne.lng(), 0); placemark.getGeometry().setOuterBoundary(ring); placemark.setName('placemark'); ge.getFeatures().appendChild(placemark); }; /** * @param {google.maps.GroundOverlay} groundOverlay the GroundOverlay. * @private */ GoogleEarth.prototype.addGroundOverlay_ = function(groundOverlay) { var bounds = groundOverlay.getBounds(); var ne = bounds.getNorthEast(); var sw = bounds.getSouthWest(); var ge = this.instance_; var overlay = ge.createGroundOverlay(''); overlay.setLatLonBox(ge.createLatLonBox('')); var latLonBox = overlay.getLatLonBox(); latLonBox.setBox(ne.lat(), sw.lat(), ne.lng(), sw.lng(), 0); overlay.setIcon(ge.createIcon('')); overlay.getIcon().setHref(groundOverlay.getUrl()); ge.getFeatures().appendChild(overlay); }; /** * @param {string} url for kml. * @private */ GoogleEarth.prototype.addKML_ = function(url) { var ge = this.instance_; google.earth.fetchKml(ge, url, function(kml) { if (!kml) { // wrap alerts in API callbacks and event handlers // in a window.setTimeout to prevent deadlock in some browsers window.setTimeout(function() { alert('Bad or null KML.'); }, 0); return; } ge.getFeatures().appendChild(kml); }); }; /** * @param {String} placemarkId the id of the placemark. * @private */ GoogleEarth.prototype.updatePlacemark_ = function(placemarkId) { //TODO(jlivni) generalize to work with more than just Markers/Points var marker = this.overlays_[placemarkId]; var placemark = this.instance_.getElementById(placemarkId); var geom = placemark.getGeometry(); var position = marker.getPosition(); geom.setLatitude(position.lat()); geom.setLongitude(position.lng()); }; /** * @param {google.maps.Marker} marker The map marker. * @private */ GoogleEarth.prototype.createPoint_ = function(marker) { if (!marker.getPosition()) { return; } var ge = this.instance_; var placemark = this.createPlacemark_(marker); if (marker.getTitle()) { placemark.setName(marker.getTitle()); } // Create style map for placemark var icon = ge.createIcon(''); if (marker.getIcon()) { //TODO(jlivni) fix for if it's a markerImage icon.setHref(marker.getIcon()); } else { icon.setHref(this.RED_ICON_); } var style = ge.createStyle(''); style.getIconStyle().setIcon(icon); placemark.setStyleSelector(style); // Create point var point = ge.createPoint(''); point.setLatitude(marker.getPosition().lat()); point.setLongitude(marker.getPosition().lng()); placemark.setGeometry(point); ge.getFeatures().appendChild(placemark); //add listener for marker move on Map var that = this; var moveEvent = google.maps.event.addListener(marker, 'position_changed', function() { var placemarkId = that.generatePlacemarkId_(marker); that.updatePlacemark_(placemarkId); }); this.moveEvents_.push(moveEvent); }; /** * @param {google.maps.Polygon} polygon the polygon overlay. * @private */ GoogleEarth.prototype.createPolygon_ = function(polygon) { var ge = this.instance_; var placemark = this.createPlacemark_(polygon); // Create polygon var poly = ge.createPolygon(''); placemark.setGeometry(poly); //set the style var style = this.createStyle_(polygon); placemark.setStyleSelector(style); //assume single linearRing var outer = ge.createLinearRing(''); poly.setOuterBoundary(outer); var coords = outer.getCoordinates(); var path = polygon.getPath().getArray(); //TODO(jlivni) use getPaths and multiple rings for (var i = 0, latLng; latLng = path[i]; i++) { coords.pushLatLngAlt(latLng.lat(), latLng.lng(), 0); } ge.getFeatures().appendChild(placemark); }; /** * Computes the LatLng produced by starting from a given LatLng and heading a * given distance. * @see http://williams.best.vwh.net/avform.htm#LL * @param {google.maps.LatLng} from The LatLng from which to start. * @param {number} distance The distance to travel. * @param {number} heading The heading in degrees clockwise from north. * @return {google.maps.LatLng} The result. * @private */ GoogleEarth.computeOffset_ = function(from, distance, heading) { var radius = 6378137; distance /= radius; heading = heading * (Math.PI / 180); //convert to radians var fromLat = from.lat() * (Math.PI / 180); var fromLng = from.lng() * (Math.PI / 180); var cosDistance = Math.cos(distance); var sinDistance = Math.sin(distance); var sinFromLat = Math.sin(fromLat); var cosFromLat = Math.cos(fromLat); var sinLat = cosDistance * sinFromLat + sinDistance * cosFromLat * Math.cos(heading); var dLng = Math.atan2( sinDistance * cosFromLat * Math.sin(heading), cosDistance - sinFromLat * sinLat); return new google.maps.LatLng((Math.asin(sinLat) / (Math.PI / 180)), (fromLng + dLng) / (Math.PI / 180)); }; /** * @param {google.maps.Circle} circle The circle overlay. * @private */ GoogleEarth.prototype.createCircle_ = function(circle) { var ge = this.instance_; var center = circle.getCenter(); var radius = circle.getRadius(); var placemark = this.createPlacemark_(circle); placemark.setGeometry(ge.createPolygon('')); var ring = ge.createLinearRing(''); //set the style var style = this.createStyle_(circle); placemark.setStyleSelector(style); //create a circle var vertices = 25; for (var i = 0; i < vertices; i++) { var heading = 360 / vertices * i; var offset = GoogleEarth.computeOffset_(center, radius, heading); ring.getCoordinates().pushLatLngAlt(offset.lat(), offset.lng(), 0); } placemark.getGeometry().setOuterBoundary(ring); placemark.setName('placemark'); ge.getFeatures().appendChild(placemark); }; /** * @param {google.maps.Polyline} polyline The map polyline overlay. * @private */ GoogleEarth.prototype.createPolyline_ = function(polyline) { var ge = this.instance_; var placemark = this.createPlacemark_(polyline); // Create linestring var lineString = ge.createLineString(''); lineString.setTessellate(true); placemark.setGeometry(lineString); //set the style var style = this.createStyle_(polyline); placemark.setStyleSelector(style); var coords = lineString.getCoordinates(); var path = polyline.getPath().getArray(); //TODO(jlivni) use getPaths for case of multiple rings for (var i = 0, latLng; latLng = path[i]; i++) { coords.pushLatLngAlt(latLng.lat(), latLng.lng(), 0); } ge.getFeatures().appendChild(placemark); }; /** * @param {google.maps.MVCObject} overlay the map overlay. * @return {google.earth.KmlStyle} the style. * @private */ GoogleEarth.prototype.createStyle_ = function(overlay) { var style = this.instance_.createStyle(''); var polyStyle = style.getPolyStyle(); var lineStyle = style.getLineStyle(); lineStyle.setWidth(this.getMVCVal_(overlay, 'strokeWeight', 3)); var strokeOpacity = this.getMVCVal_(overlay, 'strokeOpacity', 1); var fillOpacity = this.getMVCVal_(overlay, 'fillOpacity', 0.3); var strokeColor = this.getMVCVal_(overlay, 'strokeColor', '#000000'); var fillColor = this.getMVCVal_(overlay, 'fillColor', '#000000'); lineStyle.getColor().set(GoogleEarth.getKMLColor_(strokeColor, strokeOpacity)); polyStyle.getColor().set(GoogleEarth.getKMLColor_(fillColor, fillOpacity)); return style; }; /** * Gets the property value from an mvc object. * @param {google.maps.MVCObject} mvcObject The object. * @param {string} property The property. * @param {string|number} def The default. * @return {string|number} The property, or default if property undefined. * @private */ GoogleEarth.prototype.getMVCVal_ = function(mvcObject, property, def) { var val = mvcObject.get(property); if (typeof val == 'undefined') { return def; } else { return /** @type {string|number} */(val); } }; /** * Add map overlays to Earth. * @private */ GoogleEarth.prototype.addMapOverlays_ = function() { var overlays = this.getOverlays_(); for (var i = 0, marker; marker = overlays['Marker'][i]; i++) { this.createPoint_(marker); } for (var i = 0, polygon; polygon = overlays['Polygon'][i]; i++) { this.createPolygon_(polygon); } for (var i = 0, polyline; polyline = overlays['Polyline'][i]; i++) { this.createPolyline_(polyline); } for (var i = 0, rectangle; rectangle = overlays['Rectangle'][i]; i++) { this.createRectangle_(rectangle); } for (var i = 0, circle; circle = overlays['Circle'][i]; i++) { this.createCircle_(circle); } for (var i = 0, kml; kml = overlays['KmlLayer'][i]; i++) { this.addKML_(kml.getUrl()); } for (var i = 0, overlay; overlay = overlays['GroundOverlay'][i]; i++) { this.addGroundOverlay_(overlay); } }; /** * @private */ GoogleEarth.prototype.initializeEarth_ = function() { var that = this; google.earth.createInstance(this.earthDiv_, function(instance) { that.instance_ = /** @type {google.earth.GEPlugin} */(instance); that.addEarthEvents_(); that.refresh_(); }, function(e) { that.hideEarth_(); //TODO(jlivni) record previous maptype that.map_.setMapTypeId(google.maps.MapTypeId.ROADMAP); throw 'Google Earth API failed to initialize: ' + e; }); }; /** * @private */ GoogleEarth.prototype.addEarthEvents_ = function() { var ge = this.instance_; ge.getWindow().setVisibility(true); // add a navigation control var navControl = ge.getNavigationControl(); navControl.setVisibility(ge.VISIBILITY_AUTO); var screen = navControl.getScreenXY(); screen.setYUnits(ge.UNITS_INSET_PIXELS); screen.setXUnits(ge.UNITS_PIXELS); // add some layers var layerRoot = ge.getLayerRoot(); layerRoot.enableLayerById(ge.LAYER_BORDERS, true); layerRoot.enableLayerById(ge.LAYER_ROADS, true); var that = this; google.maps.event.addListener(this.map_, GoogleEarth.INFO_WINDOW_OPENED_EVENT_, function(infowindow) { //If Earth is open, create balloon if (!that.earthVisible_) { return; } var balloon = that.instance_.createHtmlStringBalloon(''); //TODO assuming anchor == marker == lastclicked var placemark = that.lastClickedPlacemark_; balloon.setFeature(placemark); balloon.setContentString(infowindow.getContent()); that.instance_.setBalloon(balloon); }); // On click of a placemark we want to trigger the map click event. google.earth.addEventListener(ge.getGlobe(), 'click', function(event) { var target = event.getTarget(); var overlay = that.overlays_[target.getId()]; if (overlay) { event.preventDefault(); // Close any currently opened map info windows. var infoWindows = that.getOverlaysForType_('InfoWindow'); for (var i = 0, infoWindow; infoWindow = infoWindows[i]; i++) { infoWindow.close(); } that.lastClickedPlacemark_ = target; google.maps.event.trigger(overlay, 'click'); } }); }; /** * Set the Map view to match Earth. * @private */ GoogleEarth.prototype.matchMapToEarth_ = function() { var lookAt = this.instance_.getView().copyAsLookAt( this.instance_.ALTITUDE_RELATIVE_TO_GROUND); var range = lookAt.getRange(); var zoom = Math.round( GoogleEarth.MAX_EARTH_ZOOM_ - (Math.log(range) / Math.log(2))); if (!this.map_.getZoom() == zoom) { this.map_.setZoom(zoom - 1); } else { this.map_.setZoom(zoom); } var center = new google.maps.LatLng(lookAt.getLatitude(), lookAt.getLongitude()); this.map_.panTo(center); }; /** * Animate from Earth to Maps view. * @private */ GoogleEarth.prototype.switchToMapView_ = function() { if (!this.earthVisible_) { return; } // First, set map to match current earth view. this.matchMapToEarth_(); // Now fly Earth to match the map view. var that = this; window.setTimeout(function() { // Sometimes it takes a few ms before the map zoom is ready. that.flyToMapView_(); }, 50); window.setTimeout(function() { // And switch back to maps once we've flown. that.hideEarth_(); }, 2200); }; /** * Hide the Earth div. * @private */ GoogleEarth.prototype.hideEarth_ = function() { this.unsetZIndexes_(); this.removeShim_(); this.controlDiv_.style.display = 'none'; this.earthVisible_ = false; }; /** * Sets the z-index of all controls except for the map type control so that * they appear behind Earth. * @param {Node} mapTypeControlDiv the control div. * @private */ GoogleEarth.prototype.setZIndexes_ = function(mapTypeControlDiv) { var oldIndex = mapTypeControlDiv.style.zIndex; var siblings = this.controlDiv_.parentNode.childNodes; for (var i = 0, sibling; sibling = siblings[i]; i++) { sibling['__gme_ozi'] = sibling.style.zIndex; // Sets the zIndex of all controls to be behind Earth. sibling.style.zIndex = -1; } mapTypeControlDiv['__gme_ozi'] = oldIndex; this.controlDiv_.style.zIndex = mapTypeControlDiv.style.zIndex = 0; }; /** * @private */ GoogleEarth.prototype.unsetZIndexes_ = function() { var siblings = this.controlDiv_.parentNode.childNodes; for (var i = 0, sibling; sibling = siblings[i]; i++) { // Set the old zIndex back sibling.style.zIndex = sibling['__gme_ozi']; } }; /** * @param {Node} mapTypeControlDiv the control div. * @private */ GoogleEarth.prototype.addShim_ = function(mapTypeControlDiv) { var iframeShim = this.iframeShim_ = document.createElement('IFRAME'); iframeShim.src = 'javascript:false;'; iframeShim.scrolling = 'no'; iframeShim.frameBorder = '0'; var style = iframeShim.style; style.zIndex = -100000; style.width = style.height = '100%'; style.position = 'absolute'; style.left = style.top = 0; // Appends the iframe to the map type control's DIV so that its width and // height will be 100% and if the control changes from a bar to a drop down, // it flows nicely. mapTypeControlDiv.appendChild(iframeShim); }; /** * Remove the shim containing the earth div. * @private */ GoogleEarth.prototype.removeShim_ = function() { this.iframeShim_.parentNode.removeChild(this.iframeShim_); this.iframeShim_ = null; }; /** * @private * @return {Node} the map type control div. */ GoogleEarth.prototype.findMapTypeControlDiv_ = function() { var title = 'title=[\'\"]?' + this.earthTitle_ + '[\"\']?'; var regex = new RegExp(title); var siblings = this.controlDiv_.parentNode.childNodes; for (var i = 0, sibling; sibling = siblings[i]; i++) { if (regex.test(sibling.innerHTML)) { return sibling; } } }; /** * @private */ GoogleEarth.prototype.addEarthControl_ = function() { var mapDiv = this.mapDiv_; var control = this.controlDiv_ = document.createElement('DIV'); control.style.position = 'absolute'; control.style.width = 0; control.style.height = 0; control.index = 0; control.style.display = 'none'; var inner = this.innerDiv_ = document.createElement('DIV'); inner.style.width = mapDiv.clientWidth + 'px'; inner.style.height = mapDiv.clientHeight + 'px'; inner.style.position = 'absolute'; control.appendChild(inner); var earthDiv = this.earthDiv_ = document.createElement('DIV'); earthDiv.style.position = 'absolute'; earthDiv.style.width = '100%'; earthDiv.style.height = '100%'; inner.appendChild(earthDiv); this.map_.controls[google.maps.ControlPosition.TOP_LEFT].push(control); var that = this; google.maps.event.addListener(this.map_, 'resize', function() { that.resizeEarth_(); }); }; /** * @private */ GoogleEarth.prototype.resizeEarth_ = function() { var innerStyle = this.innerDiv_.style; var mapDiv = this.mapDiv_; innerStyle.width = mapDiv.clientWidth + 'px'; innerStyle.height = mapDiv.clientHeight + 'px'; }; /** * @param {string} type type of overlay (Polygon, etc). * @return {Array.<Object>} list of overlays of given type currently on map. * @private */ GoogleEarth.prototype.getOverlaysForType_ = function(type) { var tmp = []; var overlays = GoogleEarth.overlays_[type]; for (var i in overlays) { if (overlays.hasOwnProperty(i)) { var overlay = overlays[i]; if (overlay.get('map') == this.map_) { tmp.push(overlay); } } } return tmp; }; /** * @return {Object} dictionary of lists for all map overlays. * @private */ GoogleEarth.prototype.getOverlays_ = function() { var overlays = {}; var overlayClasses = GoogleEarth.OVERLAY_CLASSES; for (var i = 0, overlayClass; overlayClass = overlayClasses[i]; i++) { overlays[overlayClass] = this.getOverlaysForType_(overlayClass); } return overlays; }; /** * @private */ GoogleEarth.overlays_ = {}; /** * override the open property for infowindow * @private */ GoogleEarth.modifyOpen_ = function() { google.maps.InfoWindow.prototype.openOriginal_ = google.maps.InfoWindow.prototype['open']; GoogleEarth.overlays_['InfoWindow'] = {}; google.maps.InfoWindow.prototype['open'] = function(map, anchor) { if (map) { if (!this['__gme_id']) { this['__gme_id'] = GoogleEarth.counter_++; GoogleEarth.overlays_['InfoWindow'][this['__gme_id']] = this; } } else { delete GoogleEarth.overlays_['InfoWindow'][this['__gme_id']]; this['__gme_id'] = undefined; } google.maps.event.trigger(map, GoogleEarth.INFO_WINDOW_OPENED_EVENT_, this); this.openOriginal_(map, anchor); }; }; /** * @param {string} overlayClass overlay type, such as Marker, Polygon, etc. * @private */ GoogleEarth.modifySetMap_ = function(overlayClass) { var proto = google.maps[overlayClass].prototype; proto['__gme_setMapOriginal'] = proto.setMap; GoogleEarth.overlays_[overlayClass] = {}; google.maps[overlayClass].prototype['setMap'] = function(map) { if (map) { if (!this['__gme_id']) { this['__gme_id'] = GoogleEarth.counter_++; GoogleEarth.overlays_[overlayClass][this['__gme_id']] = this; } } else { delete GoogleEarth.overlays_[overlayClass][this['__gme_id']]; this['__gme_id'] = undefined; } this['__gme_setMapOriginal'](map); }; }; /** * @const * @type {Array.<string>} */ GoogleEarth.OVERLAY_CLASSES = ['Marker', 'Polyline', 'Polygon', 'Rectangle', 'Circle', 'KmlLayer', 'GroundOverlay', 'InfoWindow']; /** * Keep track of total number of placemarks added. * @type {number} * @private */ GoogleEarth.counter_ = 0; /** * Wrapper to call appropriate prototype override methods for all overlays * @private */ GoogleEarth.trackOverlays_ = function() { var overlayClasses = GoogleEarth.OVERLAY_CLASSES; for (var i = 0, overlayClass; overlayClass = overlayClasses[i]; i++) { GoogleEarth.modifySetMap_(overlayClass); if (overlayClass == 'InfoWindow') { GoogleEarth.modifyOpen_(); } } }; GoogleEarth.trackOverlays_();