Google.v3.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. /**
  2. * @requires SuperMap/Layer/Google.js
  3. */
  4. /**
  5. * Constant: SuperMap.Layer.Google.v3
  6. *
  7. * Mixin providing functionality specific to the Google Maps API v3.
  8. *
  9. * To use this layer, you must include the GMaps v3 API in your html.
  10. *
  11. * Because SuperMap needs to control mouse events, it isolates the GMaps mapObject
  12. * (the DOM elements provided by Google) using the EventPane.
  13. * However, because the Terms of Use require some of those elements,
  14. * such as the links to Google's terms, to be clickable, these elements have
  15. * to be moved up to SuperMap' container div. There is however no easy way
  16. * to identify these, and the logic (see the repositionMapElements function
  17. * in the source) may need to be changed if Google changes them.
  18. * These elements are not part of the published API and can be changed at any time,
  19. * so a given SuperMap release can only guarantee support for the 'frozen'
  20. * Google release at the time of the SuperMap release. See
  21. * https://developers.google.com/maps/documentation/javascript/basics#Versioning
  22. * for Google's current release cycle.
  23. *
  24. * For this reason, it's recommended that production code specifically loads
  25. * the current frozen version, for example:
  26. *
  27. * (code)
  28. * <script src="http://maps.google.com/maps/api/js?v=3.7&amp;sensor=false"></script>
  29. * (end)
  30. *
  31. * but that development code should use the latest 'nightly' version, so that any
  32. * problems can be dealt with as soon as they arise, and before they affect the production, 'frozen', code.
  33. *
  34. * Note, however, that frozen versions are retired as part of Google's release
  35. * cycle, and once this happens, you will get the next version, in the example above, 3.8 once 3.7 is retired.
  36. *
  37. * This version supports 3.7.
  38. *
  39. *
  40. * Note that this layer configures the google.maps.map object with the
  41. * "disableDefaultUI" option set to true. Using UI controls that the Google
  42. * Maps API provides is not supported by the SuperMap API.
  43. */
  44. SuperMap.Layer.Google.v3 = {
  45. /**
  46. * Constant: DEFAULTS
  47. * {Object} It is not recommended to change the properties set here. Note
  48. * that Google.v3 layers only work when sphericalMercator is set to true.
  49. *
  50. * (code)
  51. * {
  52. * sphericalMercator: true,
  53. * projection: "EPSG:900913"
  54. * }
  55. * (end)
  56. */
  57. DEFAULTS: {
  58. sphericalMercator: true,
  59. projection: "EPSG:900913"
  60. },
  61. /**
  62. * APIProperty: animationEnabled
  63. * {Boolean} If set to true, the transition between zoom levels will be
  64. * animated (if supported by the GMaps API for the device used). Set to
  65. * false to match the zooming experience of other layer types. Default
  66. * is true. Note that the GMaps API does not give us control over zoom
  67. * animation, so if set to false, when zooming, this will make the
  68. * layer temporarily invisible, wait until GMaps reports the map being
  69. * idle, and make it visible again. The result will be a blank layer
  70. * for a few moments while zooming.
  71. */
  72. animationEnabled: true,
  73. /**
  74. * Method: loadMapObject
  75. * Load the GMap and register appropriate event listeners. If we can't
  76. * load GMap2, then display a warning message.
  77. */
  78. loadMapObject:function() {
  79. if (!this.type) {
  80. this.type = google.maps.MapTypeId.ROADMAP;
  81. }
  82. var mapObject;
  83. var cache = SuperMap.Layer.Google.cache[this.map.id];
  84. if (cache) {
  85. // there are already Google layers added to this map
  86. mapObject = cache.mapObject;
  87. // increment the layer count
  88. ++cache.count;
  89. } else {
  90. // this is the first Google layer for this map
  91. var container = this.map.viewPortDiv;
  92. var div = document.createElement("div");
  93. div.id = this.map.id + "_GMapContainer";
  94. div.style.position = "absolute";
  95. div.style.width = "100%";
  96. div.style.height = "100%";
  97. container.appendChild(div);
  98. // create GMap and shuffle elements
  99. var center = this.map.getCenter();
  100. mapObject = new google.maps.Map(div, {
  101. center: center ?
  102. new google.maps.LatLng(center.lat, center.lon) :
  103. new google.maps.LatLng(0, 0),
  104. zoom: this.map.getZoom() || 0,
  105. mapTypeId: this.type,
  106. disableDefaultUI: true,
  107. keyboardShortcuts: false,
  108. draggable: false,
  109. disableDoubleClickZoom: true,
  110. scrollwheel: false,
  111. streetViewControl: false
  112. });
  113. // cache elements for use by any other google layers added to
  114. // this same map
  115. cache = {
  116. mapObject: mapObject,
  117. count: 1
  118. };
  119. SuperMap.Layer.Google.cache[this.map.id] = cache;
  120. this.repositionListener = google.maps.event.addListenerOnce(
  121. mapObject,
  122. "center_changed",
  123. SuperMap.Function.bind(this.repositionMapElements, this)
  124. );
  125. }
  126. this.mapObject = mapObject;
  127. this.setGMapVisibility(this.visibility);
  128. },
  129. /**
  130. * Method: repositionMapElements
  131. *
  132. * Waits until powered by and terms of use elements are available and then
  133. * moves them so they are clickable.
  134. */
  135. repositionMapElements: function() {
  136. // This is the first time any Google layer in this mapObject has been
  137. // made visible. The mapObject needs to know the container size.
  138. google.maps.event.trigger(this.mapObject, "resize");
  139. var div = this.mapObject.getDiv().firstChild;
  140. if (!div || div.childNodes.length < 3) {
  141. this.repositionTimer = window.setTimeout(
  142. SuperMap.Function.bind(this.repositionMapElements, this),
  143. 250
  144. );
  145. return false;
  146. }
  147. var cache = SuperMap.Layer.Google.cache[this.map.id];
  148. var container = this.map.viewPortDiv;
  149. // move the ToS and branding stuff up to the container div
  150. // depends on value of zIndex, which is not robust
  151. for (var i=div.children.length-1; i>=0; --i) {
  152. if (div.children[i].style.zIndex == 1000001) {
  153. var termsOfUse = div.children[i];
  154. container.appendChild(termsOfUse);
  155. termsOfUse.style.zIndex = "1100";
  156. termsOfUse.style.bottom = "";
  157. termsOfUse.className = "olLayerGoogleCopyright olLayerGoogleV3";
  158. termsOfUse.style.display = "";
  159. cache.termsOfUse = termsOfUse;
  160. }
  161. if (div.children[i].style.zIndex == 1000000) {
  162. var poweredBy = div.children[i];
  163. container.appendChild(poweredBy);
  164. poweredBy.style.zIndex = "1100";
  165. poweredBy.style.bottom = "";
  166. poweredBy.className = "olLayerGooglePoweredBy olLayerGoogleV3 gmnoprint";
  167. poweredBy.style.display = "";
  168. cache.poweredBy = poweredBy;
  169. }
  170. if (div.children[i].style.zIndex == 10000002) {
  171. container.appendChild(div.children[i]);
  172. }
  173. }
  174. this.setGMapVisibility(this.visibility);
  175. },
  176. /**
  177. * APIMethod: onMapResize
  178. */
  179. onMapResize: function() {
  180. if (this.visibility) {
  181. google.maps.event.trigger(this.mapObject, "resize");
  182. } else {
  183. var cache = SuperMap.Layer.Google.cache[this.map.id];
  184. if (!cache.resized) {
  185. var layer = this;
  186. google.maps.event.addListenerOnce(this.mapObject, "tilesloaded", function() {
  187. google.maps.event.trigger(layer.mapObject, "resize");
  188. layer.moveTo(layer.map.getCenter(), layer.map.getZoom());
  189. delete cache.resized;
  190. });
  191. }
  192. cache.resized = true;
  193. }
  194. },
  195. /**
  196. * Method: setGMapVisibility
  197. * Display the GMap container and associated elements.
  198. *
  199. * Parameters:
  200. * visible - {Boolean} Display the GMap elements.
  201. */
  202. setGMapVisibility: function(visible) {
  203. var cache = SuperMap.Layer.Google.cache[this.map.id];
  204. if (cache) {
  205. var type = this.type;
  206. var layers = this.map.layers;
  207. var layer;
  208. for (var i=layers.length-1; i>=0; --i) {
  209. layer = layers[i];
  210. if (layer instanceof SuperMap.Layer.Google &&
  211. layer.visibility === true && layer.inRange === true) {
  212. type = layer.type;
  213. visible = true;
  214. break;
  215. }
  216. }
  217. var container = this.mapObject.getDiv();
  218. if (visible === true) {
  219. this.mapObject.setMapTypeId(type);
  220. container.style.left = "";
  221. if (cache.termsOfUse && cache.termsOfUse.style) {
  222. cache.termsOfUse.style.left = "";
  223. cache.termsOfUse.style.display = "";
  224. cache.poweredBy.style.display = "";
  225. }
  226. cache.displayed = this.id;
  227. } else {
  228. delete cache.displayed;
  229. container.style.left = "-9999px";
  230. if (cache.termsOfUse && cache.termsOfUse.style) {
  231. cache.termsOfUse.style.display = "none";
  232. // move ToU far to the left in addition to setting
  233. // display to "none", because at the end of the GMap
  234. // load sequence, display: none will be unset and ToU
  235. // would be visible after loading a map with a google
  236. // layer that is initially hidden.
  237. cache.termsOfUse.style.left = "-9999px";
  238. cache.poweredBy.style.display = "none";
  239. }
  240. }
  241. }
  242. },
  243. /**
  244. * Method: getMapContainer
  245. *
  246. * Returns:
  247. * {DOMElement} the GMap container's div
  248. */
  249. getMapContainer: function() {
  250. return this.mapObject.getDiv();
  251. },
  252. //
  253. // TRANSLATION: MapObject Bounds <-> SuperMap.Bounds
  254. //
  255. /**
  256. * APIMethod: getMapObjectBoundsFromOLBounds
  257. *
  258. * Parameters:
  259. * olBounds - {<SuperMap.Bounds>}
  260. *
  261. * Returns:
  262. * {Object} A MapObject Bounds, translated from olBounds
  263. * Returns null if null value is passed in
  264. */
  265. getMapObjectBoundsFromOLBounds: function(olBounds) {
  266. var moBounds = null;
  267. if (olBounds != null) {
  268. var sw = this.sphericalMercator ?
  269. this.inverseMercator(olBounds.bottom, olBounds.left) :
  270. new SuperMap.LonLat(olBounds.bottom, olBounds.left);
  271. var ne = this.sphericalMercator ?
  272. this.inverseMercator(olBounds.top, olBounds.right) :
  273. new SuperMap.LonLat(olBounds.top, olBounds.right);
  274. moBounds = new google.maps.LatLngBounds(
  275. new google.maps.LatLng(sw.lat, sw.lon),
  276. new google.maps.LatLng(ne.lat, ne.lon)
  277. );
  278. }
  279. return moBounds;
  280. },
  281. /************************************
  282. * *
  283. * MapObject Interface Controls *
  284. * *
  285. ************************************/
  286. // LonLat - Pixel Translation
  287. /**
  288. * APIMethod: getMapObjectLonLatFromMapObjectPixel
  289. *
  290. * Parameters:
  291. * moPixel - {Object} MapObject Pixel format
  292. *
  293. * Returns:
  294. * {Object} MapObject LonLat translated from MapObject Pixel
  295. */
  296. getMapObjectLonLatFromMapObjectPixel: function(moPixel) {
  297. var size = this.map.getSize();
  298. var lon = this.getLongitudeFromMapObjectLonLat(this.mapObject.center);
  299. var lat = this.getLatitudeFromMapObjectLonLat(this.mapObject.center);
  300. var res = this.map.getResolution();
  301. var delta_x = moPixel.x - (size.w / 2);
  302. var delta_y = moPixel.y - (size.h / 2);
  303. var lonlat = new SuperMap.LonLat(
  304. lon + delta_x * res,
  305. lat - delta_y * res
  306. );
  307. if (this.wrapDateLine) {
  308. lonlat = lonlat.wrapDateLine(this.maxExtent);
  309. }
  310. return this.getMapObjectLonLatFromLonLat(lonlat.lon, lonlat.lat);
  311. },
  312. /**
  313. * APIMethod: getMapObjectPixelFromMapObjectLonLat
  314. *
  315. * Parameters:
  316. * moLonLat - {Object} MapObject LonLat format
  317. *
  318. * Returns:
  319. * {Object} MapObject Pixel transtlated from MapObject LonLat
  320. */
  321. getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {
  322. var lon = this.getLongitudeFromMapObjectLonLat(moLonLat);
  323. var lat = this.getLatitudeFromMapObjectLonLat(moLonLat);
  324. var res = this.map.getResolution();
  325. var extent = this.map.getExtent();
  326. return this.getMapObjectPixelFromXY((1/res * (lon - extent.left)),
  327. (1/res * (extent.top - lat)));
  328. },
  329. /**
  330. * APIMethod: setMapObjectCenter
  331. * Set the mapObject to the specified center and zoom
  332. *
  333. * Parameters:
  334. * center - {Object} MapObject LonLat format
  335. * zoom - {int} MapObject zoom format
  336. */
  337. setMapObjectCenter: function(center, zoom) {
  338. if (this.animationEnabled === false && zoom != this.mapObject.zoom) {
  339. var mapContainer = this.getMapContainer();
  340. google.maps.event.addListenerOnce(
  341. this.mapObject,
  342. "idle",
  343. function() {
  344. mapContainer.style.visibility = "";
  345. }
  346. );
  347. mapContainer.style.visibility = "hidden";
  348. }
  349. this.mapObject.setOptions({
  350. center: center,
  351. zoom: zoom
  352. });
  353. },
  354. // Bounds
  355. /**
  356. * APIMethod: getMapObjectZoomFromMapObjectBounds
  357. *
  358. * Parameters:
  359. * moBounds - {Object} MapObject Bounds format
  360. *
  361. * Returns:
  362. * {Object} MapObject Zoom for specified MapObject Bounds
  363. */
  364. getMapObjectZoomFromMapObjectBounds: function(moBounds) {
  365. return this.mapObject.getBoundsZoomLevel(moBounds);
  366. },
  367. /************************************
  368. * *
  369. * MapObject Primitives *
  370. * *
  371. ************************************/
  372. // LonLat
  373. /**
  374. * APIMethod: getMapObjectLonLatFromLonLat
  375. *
  376. * Parameters:
  377. * lon - {Float}
  378. * lat - {Float}
  379. *
  380. * Returns:
  381. * {Object} MapObject LonLat built from lon and lat params
  382. */
  383. getMapObjectLonLatFromLonLat: function(lon, lat) {
  384. var gLatLng;
  385. if(this.sphericalMercator) {
  386. var lonlat = this.inverseMercator(lon, lat);
  387. gLatLng = new google.maps.LatLng(lonlat.lat, lonlat.lon);
  388. } else {
  389. gLatLng = new google.maps.LatLng(lat, lon);
  390. }
  391. return gLatLng;
  392. },
  393. // Pixel
  394. /**
  395. * APIMethod: getMapObjectPixelFromXY
  396. *
  397. * Parameters:
  398. * x - {Integer}
  399. * y - {Integer}
  400. *
  401. * Returns:
  402. * {Object} MapObject Pixel from x and y parameters
  403. */
  404. getMapObjectPixelFromXY: function(x, y) {
  405. return new google.maps.Point(x, y);
  406. },
  407. /**
  408. * APIMethod: destroy
  409. * Clean up this layer.
  410. */
  411. destroy: function() {
  412. if (this.repositionListener) {
  413. google.maps.event.removeListener(this.repositionListener);
  414. }
  415. if (this.repositionTimer) {
  416. window.clearTimeout(this.repositionTimer);
  417. }
  418. SuperMap.Layer.Google.prototype.destroy.apply(this, arguments);
  419. }
  420. };