PageRenderTime 49ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/static/scripts/gis/openlayers/lib/OpenLayers/TileManager.js

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