PageRenderTime 57ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/js/vendor/openlayers/src/openlayers/lib/OpenLayers/TileManager.js

http://github.com/phpmyadmin/phpmyadmin
JavaScript | 462 lines | 233 code | 31 blank | 198 comment | 44 complexity | 3b4ddd1a586af8f3f01b33a11f169887 MD5 | raw file
Possible License(s): GPL-2.0, MIT, LGPL-3.0
  1. /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
  2. * full list of contributors). Published under the 2-clause BSD license.
  3. * See license.txt in the OpenLayers distribution or repository for the
  4. * full text of the license. */
  5. /**
  6. * @requires OpenLayers/Util.js
  7. * @requires OpenLayers/BaseTypes.js
  8. * @requires OpenLayers/BaseTypes/Element.js
  9. * @requires OpenLayers/Layer/Grid.js
  10. * @requires OpenLayers/Tile/Image.js
  11. */
  12. /**
  13. * Class: OpenLayers.TileManager
  14. * Provides queueing of image requests and caching of image elements.
  15. *
  16. * Queueing avoids unnecessary image requests while changing zoom levels
  17. * quickly, and helps improve dragging performance on mobile devices that show
  18. * a lag in dragging when loading of new images starts. <zoomDelay> and
  19. * <moveDelay> are the configuration options to control this behavior.
  20. *
  21. * Caching avoids setting the src on image elements for images that have already
  22. * been used. Several maps can share a TileManager instance, in which case each
  23. * map gets its own tile queue, but all maps share the same tile cache.
  24. */
  25. OpenLayers.TileManager = OpenLayers.Class({
  26. /**
  27. * APIProperty: cacheSize
  28. * {Number} Number of image elements to keep referenced in this instance's
  29. * cache for fast reuse. Default is 256.
  30. */
  31. cacheSize: 256,
  32. /**
  33. * APIProperty: tilesPerFrame
  34. * {Number} Number of queued tiles to load per frame (see <frameDelay>).
  35. * Default is 2.
  36. */
  37. tilesPerFrame: 2,
  38. /**
  39. * APIProperty: frameDelay
  40. * {Number} Delay between tile loading frames (see <tilesPerFrame>) in
  41. * milliseconds. Default is 16.
  42. */
  43. frameDelay: 16,
  44. /**
  45. * APIProperty: moveDelay
  46. * {Number} Delay in milliseconds after a map's move event before loading
  47. * tiles. Default is 100.
  48. */
  49. moveDelay: 100,
  50. /**
  51. * APIProperty: zoomDelay
  52. * {Number} Delay in milliseconds after a map's zoomend event before loading
  53. * tiles. Default is 200.
  54. */
  55. zoomDelay: 200,
  56. /**
  57. * Property: maps
  58. * {Array(<OpenLayers.Map>)} The maps to manage tiles on.
  59. */
  60. maps: null,
  61. /**
  62. * Property: tileQueueId
  63. * {Object} The ids of the <drawTilesFromQueue> loop, keyed by map id.
  64. */
  65. tileQueueId: null,
  66. /**
  67. * Property: tileQueue
  68. * {Object(Array(<OpenLayers.Tile>))} Tiles queued for drawing, keyed by
  69. * map id.
  70. */
  71. tileQueue: null,
  72. /**
  73. * Property: tileCache
  74. * {Object} Cached image elements, keyed by URL.
  75. */
  76. tileCache: null,
  77. /**
  78. * Property: tileCacheIndex
  79. * {Array(String)} URLs of cached tiles. First entry is the least recently
  80. * used.
  81. */
  82. tileCacheIndex: null,
  83. /**
  84. * Constructor: OpenLayers.TileManager
  85. * Constructor for a new <OpenLayers.TileManager> instance.
  86. *
  87. * Parameters:
  88. * options - {Object} Configuration for this instance.
  89. */
  90. initialize: function(options) {
  91. OpenLayers.Util.extend(this, options);
  92. this.maps = [];
  93. this.tileQueueId = {};
  94. this.tileQueue = {};
  95. this.tileCache = {};
  96. this.tileCacheIndex = [];
  97. },
  98. /**
  99. * Method: addMap
  100. * Binds this instance to a map
  101. *
  102. * Parameters:
  103. * map - {<OpenLayers.Map>}
  104. */
  105. addMap: function(map) {
  106. if (this._destroyed || !OpenLayers.Layer.Grid) {
  107. return;
  108. }
  109. this.maps.push(map);
  110. this.tileQueue[map.id] = [];
  111. for (var i=0, ii=map.layers.length; i<ii; ++i) {
  112. this.addLayer({layer: map.layers[i]});
  113. }
  114. map.events.on({
  115. move: this.move,
  116. zoomend: this.zoomEnd,
  117. changelayer: this.changeLayer,
  118. addlayer: this.addLayer,
  119. preremovelayer: this.removeLayer,
  120. scope: this
  121. });
  122. },
  123. /**
  124. * Method: removeMap
  125. * Unbinds this instance from a map
  126. *
  127. * Parameters:
  128. * map - {<OpenLayers.Map>}
  129. */
  130. removeMap: function(map) {
  131. if (this._destroyed || !OpenLayers.Layer.Grid) {
  132. return;
  133. }
  134. window.clearTimeout(this.tileQueueId[map.id]);
  135. if (map.layers) {
  136. for (var i=0, ii=map.layers.length; i<ii; ++i) {
  137. this.removeLayer({layer: map.layers[i]});
  138. }
  139. }
  140. if (map.events) {
  141. map.events.un({
  142. move: this.move,
  143. zoomend: this.zoomEnd,
  144. changelayer: this.changeLayer,
  145. addlayer: this.addLayer,
  146. preremovelayer: this.removeLayer,
  147. scope: this
  148. });
  149. }
  150. delete this.tileQueue[map.id];
  151. delete this.tileQueueId[map.id];
  152. OpenLayers.Util.removeItem(this.maps, map);
  153. },
  154. /**
  155. * Method: move
  156. * Handles the map's move event
  157. *
  158. * Parameters:
  159. * evt - {Object} Listener argument
  160. */
  161. move: function(evt) {
  162. this.updateTimeout(evt.object, this.moveDelay, true);
  163. },
  164. /**
  165. * Method: zoomEnd
  166. * Handles the map's zoomEnd event
  167. *
  168. * Parameters:
  169. * evt - {Object} Listener argument
  170. */
  171. zoomEnd: function(evt) {
  172. this.updateTimeout(evt.object, this.zoomDelay);
  173. },
  174. /**
  175. * Method: changeLayer
  176. * Handles the map's changeLayer event
  177. *
  178. * Parameters:
  179. * evt - {Object} Listener argument
  180. */
  181. changeLayer: function(evt) {
  182. if (evt.property === 'visibility' || evt.property === 'params') {
  183. this.updateTimeout(evt.object, 0);
  184. }
  185. },
  186. /**
  187. * Method: addLayer
  188. * Handles the map's addlayer event
  189. *
  190. * Parameters:
  191. * evt - {Object} The listener argument
  192. */
  193. addLayer: function(evt) {
  194. var layer = evt.layer;
  195. if (layer instanceof OpenLayers.Layer.Grid) {
  196. layer.events.on({
  197. addtile: this.addTile,
  198. retile: this.clearTileQueue,
  199. scope: this
  200. });
  201. var i, j, tile;
  202. for (i=layer.grid.length-1; i>=0; --i) {
  203. for (j=layer.grid[i].length-1; j>=0; --j) {
  204. tile = layer.grid[i][j];
  205. this.addTile({tile: tile});
  206. if (tile.url && !tile.imgDiv) {
  207. this.manageTileCache({object: tile});
  208. }
  209. }
  210. }
  211. }
  212. },
  213. /**
  214. * Method: removeLayer
  215. * Handles the map's preremovelayer event
  216. *
  217. * Parameters:
  218. * evt - {Object} The listener argument
  219. */
  220. removeLayer: function(evt) {
  221. var layer = evt.layer;
  222. if (layer instanceof OpenLayers.Layer.Grid) {
  223. this.clearTileQueue({object: layer});
  224. if (layer.events) {
  225. layer.events.un({
  226. addtile: this.addTile,
  227. retile: this.clearTileQueue,
  228. scope: this
  229. });
  230. }
  231. if (layer.grid) {
  232. var i, j, tile;
  233. for (i=layer.grid.length-1; i>=0; --i) {
  234. for (j=layer.grid[i].length-1; j>=0; --j) {
  235. tile = layer.grid[i][j];
  236. this.unloadTile({object: tile});
  237. }
  238. }
  239. }
  240. }
  241. },
  242. /**
  243. * Method: updateTimeout
  244. * Applies the <moveDelay> or <zoomDelay> to the <drawTilesFromQueue> loop,
  245. * and schedules more queue processing after <frameDelay> if there are still
  246. * tiles in the queue.
  247. *
  248. * Parameters:
  249. * map - {<OpenLayers.Map>} The map to update the timeout for
  250. * delay - {Number} The delay to apply
  251. * nice - {Boolean} If true, the timeout function will only be created if
  252. * the tilequeue is not empty. This is used by the move handler to
  253. * avoid impacts on dragging performance. For other events, the tile
  254. * queue may not be populated yet, so we need to set the timer
  255. * regardless of the queue size.
  256. */
  257. updateTimeout: function(map, delay, nice) {
  258. window.clearTimeout(this.tileQueueId[map.id]);
  259. var tileQueue = this.tileQueue[map.id];
  260. if (!nice || tileQueue.length) {
  261. this.tileQueueId[map.id] = window.setTimeout(
  262. OpenLayers.Function.bind(function() {
  263. this.drawTilesFromQueue(map);
  264. if (tileQueue.length) {
  265. this.updateTimeout(map, this.frameDelay);
  266. }
  267. }, this), delay
  268. );
  269. }
  270. },
  271. /**
  272. * Method: addTile
  273. * Listener for the layer's addtile event
  274. *
  275. * Parameters:
  276. * evt - {Object} The listener argument
  277. */
  278. addTile: function(evt) {
  279. if (evt.tile instanceof OpenLayers.Tile.Image) {
  280. evt.tile.events.on({
  281. beforedraw: this.queueTileDraw,
  282. beforeload: this.manageTileCache,
  283. loadend: this.addToCache,
  284. unload: this.unloadTile,
  285. scope: this
  286. });
  287. } else {
  288. // Layer has the wrong tile type, so don't handle it any longer
  289. this.removeLayer({layer: evt.tile.layer});
  290. }
  291. },
  292. /**
  293. * Method: unloadTile
  294. * Listener for the tile's unload event
  295. *
  296. * Parameters:
  297. * evt - {Object} The listener argument
  298. */
  299. unloadTile: function(evt) {
  300. var tile = evt.object;
  301. tile.events.un({
  302. beforedraw: this.queueTileDraw,
  303. beforeload: this.manageTileCache,
  304. loadend: this.addToCache,
  305. unload: this.unloadTile,
  306. scope: this
  307. });
  308. OpenLayers.Util.removeItem(this.tileQueue[tile.layer.map.id], tile);
  309. },
  310. /**
  311. * Method: queueTileDraw
  312. * Adds a tile to the queue that will draw it.
  313. *
  314. * Parameters:
  315. * evt - {Object} Listener argument of the tile's beforedraw event
  316. */
  317. queueTileDraw: function(evt) {
  318. var tile = evt.object;
  319. var queued = false;
  320. var layer = tile.layer;
  321. var url = layer.getURL(tile.bounds);
  322. var img = this.tileCache[url];
  323. if (img && img.className !== 'olTileImage') {
  324. // cached image no longer valid, e.g. because we're olTileReplacing
  325. delete this.tileCache[url];
  326. OpenLayers.Util.removeItem(this.tileCacheIndex, url);
  327. img = null;
  328. }
  329. // queue only if image with same url not cached already
  330. if (layer.url && (layer.async || !img)) {
  331. // add to queue only if not in queue already
  332. var tileQueue = this.tileQueue[layer.map.id];
  333. if (!~OpenLayers.Util.indexOf(tileQueue, tile)) {
  334. tileQueue.push(tile);
  335. }
  336. queued = true;
  337. }
  338. return !queued;
  339. },
  340. /**
  341. * Method: drawTilesFromQueue
  342. * Draws tiles from the tileQueue, and unqueues the tiles
  343. */
  344. drawTilesFromQueue: function(map) {
  345. var tileQueue = this.tileQueue[map.id];
  346. var limit = this.tilesPerFrame;
  347. var animating = map.zoomTween && map.zoomTween.playing;
  348. while (!animating && tileQueue.length && limit) {
  349. tileQueue.shift().draw(true);
  350. --limit;
  351. }
  352. },
  353. /**
  354. * Method: manageTileCache
  355. * Adds, updates, removes and fetches cache entries.
  356. *
  357. * Parameters:
  358. * evt - {Object} Listener argument of the tile's beforeload event
  359. */
  360. manageTileCache: function(evt) {
  361. var tile = evt.object;
  362. var img = this.tileCache[tile.url];
  363. if (img) {
  364. // if image is on its layer's backbuffer, remove it from backbuffer
  365. if (img.parentNode &&
  366. OpenLayers.Element.hasClass(img.parentNode, 'olBackBuffer')) {
  367. img.parentNode.removeChild(img);
  368. img.id = null;
  369. }
  370. // only use image from cache if it is not on a layer already
  371. if (!img.parentNode) {
  372. img.style.visibility = 'hidden';
  373. img.style.opacity = 0;
  374. tile.setImage(img);
  375. // LRU - move tile to the end of the array to mark it as the most
  376. // recently used
  377. OpenLayers.Util.removeItem(this.tileCacheIndex, tile.url);
  378. this.tileCacheIndex.push(tile.url);
  379. }
  380. }
  381. },
  382. /**
  383. * Method: addToCache
  384. *
  385. * Parameters:
  386. * evt - {Object} Listener argument for the tile's loadend event
  387. */
  388. addToCache: function(evt) {
  389. var tile = evt.object;
  390. if (!this.tileCache[tile.url]) {
  391. if (!OpenLayers.Element.hasClass(tile.imgDiv, 'olImageLoadError')) {
  392. if (this.tileCacheIndex.length >= this.cacheSize) {
  393. delete this.tileCache[this.tileCacheIndex[0]];
  394. this.tileCacheIndex.shift();
  395. }
  396. this.tileCache[tile.url] = tile.imgDiv;
  397. this.tileCacheIndex.push(tile.url);
  398. }
  399. }
  400. },
  401. /**
  402. * Method: clearTileQueue
  403. * Clears the tile queue from tiles of a specific layer
  404. *
  405. * Parameters:
  406. * evt - {Object} Listener argument of the layer's retile event
  407. */
  408. clearTileQueue: function(evt) {
  409. var layer = evt.object;
  410. var tileQueue = this.tileQueue[layer.map.id];
  411. for (var i=tileQueue.length-1; i>=0; --i) {
  412. if (tileQueue[i].layer === layer) {
  413. tileQueue.splice(i, 1);
  414. }
  415. }
  416. },
  417. /**
  418. * Method: destroy
  419. */
  420. destroy: function() {
  421. for (var i=this.maps.length-1; i>=0; --i) {
  422. this.removeMap(this.maps[i]);
  423. }
  424. this.maps = null;
  425. this.tileQueue = null;
  426. this.tileQueueId = null;
  427. this.tileCache = null;
  428. this.tileCacheIndex = null;
  429. this._destroyed = true;
  430. }
  431. });