From bff8a2bb1e5bca0de4ed0d1de43406060b655af5 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Fri, 18 Jan 2013 01:16:14 +0100 Subject: [PATCH 01/60] Add DebugTileSource exports --- src/objectliterals.exports | 5 +++++ src/ol/source/debugtilesource.exports | 1 + src/ol/source/debugtilesource.js | 9 --------- 3 files changed, 6 insertions(+), 9 deletions(-) create mode 100644 src/ol/source/debugtilesource.exports diff --git a/src/objectliterals.exports b/src/objectliterals.exports index 171672ef67..7e1f62a30d 100644 --- a/src/objectliterals.exports +++ b/src/objectliterals.exports @@ -51,6 +51,11 @@ @exportObjectLiteralProperty ol.source.BingMapsOptions.key string @exportObjectLiteralProperty ol.source.BingMapsOptions.style ol.BingMapsStyle +@exportObjectLiteral ol.source.DebugTileSourceOptions +@exportObjectLiteralProperty ol.source.DebugTileSourceOptions.extent ol.Extent|undefined +@exportObjectLiteralProperty ol.source.DebugTileSourceOptions.projection ol.Projection|undefined +@exportObjectLiteralProperty ol.source.DebugTileSourceOptions.tileGrid ol.tilegrid.TileGrid|undefined + @exportObjectLiteral ol.source.StamenOptions @exportObjectLiteralProperty ol.source.StamenOptions.flavor string|undefined @exportObjectLiteralProperty ol.source.StamenOptions.provider string diff --git a/src/ol/source/debugtilesource.exports b/src/ol/source/debugtilesource.exports new file mode 100644 index 0000000000..2c74b974f3 --- /dev/null +++ b/src/ol/source/debugtilesource.exports @@ -0,0 +1 @@ +@exportClass ol.source.DebugTileSource ol.source.DebugTileSourceOptions diff --git a/src/ol/source/debugtilesource.js b/src/ol/source/debugtilesource.js index cfe149bb20..452cf5085a 100644 --- a/src/ol/source/debugtilesource.js +++ b/src/ol/source/debugtilesource.js @@ -1,5 +1,4 @@ goog.provide('ol.source.DebugTileSource'); -goog.provide('ol.source.DebugTileSourceOptions'); goog.require('ol.Size'); goog.require('ol.Tile'); @@ -79,14 +78,6 @@ ol.DebugTile_.prototype.getImage = function(opt_context) { }; -/** - * @typedef {{extent: (ol.Extent|undefined), - * projection: (ol.Projection|undefined), - * tileGrid: (ol.tilegrid.TileGrid|undefined)}} - */ -ol.source.DebugTileSourceOptions; - - /** * @constructor From 1c2a1a2a2f85126a158647ffd6e7b4241631bb6f Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Fri, 18 Jan 2013 01:19:51 +0100 Subject: [PATCH 02/60] Sort object literal exports alphabetically --- src/objectliterals.exports | 56 +++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/objectliterals.exports b/src/objectliterals.exports index 7e1f62a30d..f22ef20690 100644 --- a/src/objectliterals.exports +++ b/src/objectliterals.exports @@ -1,3 +1,31 @@ +@exportObjectLiteral ol.MapOptions +@exportObjectLiteralProperty ol.MapOptions.controls ol.Collection|undefined +@exportObjectLiteralProperty ol.MapOptions.doubleClickZoom boolean|undefined +@exportObjectLiteralProperty ol.MapOptions.dragPan boolean|undefined +@exportObjectLiteralProperty ol.MapOptions.interactions ol.Collection|undefined +@exportObjectLiteralProperty ol.MapOptions.keyboard boolean|undefined +@exportObjectLiteralProperty ol.MapOptions.keyboardPanOffset number|undefined +@exportObjectLiteralProperty ol.MapOptions.layers ol.Collection|undefined +@exportObjectLiteralProperty ol.MapOptions.mouseWheelZoom boolean|undefined +@exportObjectLiteralProperty ol.MapOptions.mouseWheelZoomDelta number|undefined +@exportObjectLiteralProperty ol.MapOptions.renderer ol.RendererHint|undefined +@exportObjectLiteralProperty ol.MapOptions.renderers Array.|undefined +@exportObjectLiteralProperty ol.MapOptions.shiftDragZoom boolean|undefined +@exportObjectLiteralProperty ol.MapOptions.target Element|string +@exportObjectLiteralProperty ol.MapOptions.view ol.IView|undefined +@exportObjectLiteralProperty ol.MapOptions.zoomDelta number|undefined + +@exportObjectLiteral ol.View2DOptions +@exportObjectLiteralProperty ol.View2DOptions.center ol.Coordinate|undefined +@exportObjectLiteralProperty ol.View2DOptions.maxResolution number|undefined +@exportObjectLiteralProperty ol.View2DOptions.numZoomLevels number|undefined +@exportObjectLiteralProperty ol.View2DOptions.projection ol.Projection|string|undefined +@exportObjectLiteralProperty ol.View2DOptions.resolution number|undefined +@exportObjectLiteralProperty ol.View2DOptions.resolutions Array.|undefined +@exportObjectLiteralProperty ol.View2DOptions.rotation number|undefined +@exportObjectLiteralProperty ol.View2DOptions.zoom number|undefined +@exportObjectLiteralProperty ol.View2DOptions.zoomFactor number|undefined + @exportObjectLiteral ol.control.AttributionOptions @exportObjectLiteralProperty ol.control.AttributionOptions.map ol.Map|undefined @exportObjectLiteralProperty ol.control.AttributionOptions.target Element|undefined @@ -23,23 +51,6 @@ @exportObjectLiteralProperty ol.layer.LayerOptions.source ol.source.Source @exportObjectLiteralProperty ol.layer.LayerOptions.visible boolean|undefined -@exportObjectLiteral ol.MapOptions -@exportObjectLiteralProperty ol.MapOptions.controls ol.Collection|undefined -@exportObjectLiteralProperty ol.MapOptions.doubleClickZoom boolean|undefined -@exportObjectLiteralProperty ol.MapOptions.dragPan boolean|undefined -@exportObjectLiteralProperty ol.MapOptions.interactions ol.Collection|undefined -@exportObjectLiteralProperty ol.MapOptions.keyboard boolean|undefined -@exportObjectLiteralProperty ol.MapOptions.keyboardPanOffset number|undefined -@exportObjectLiteralProperty ol.MapOptions.layers ol.Collection|undefined -@exportObjectLiteralProperty ol.MapOptions.mouseWheelZoom boolean|undefined -@exportObjectLiteralProperty ol.MapOptions.mouseWheelZoomDelta number|undefined -@exportObjectLiteralProperty ol.MapOptions.renderer ol.RendererHint|undefined -@exportObjectLiteralProperty ol.MapOptions.renderers Array.|undefined -@exportObjectLiteralProperty ol.MapOptions.shiftDragZoom boolean|undefined -@exportObjectLiteralProperty ol.MapOptions.target Element|string -@exportObjectLiteralProperty ol.MapOptions.view ol.IView|undefined -@exportObjectLiteralProperty ol.MapOptions.zoomDelta number|undefined - @exportObjectLiteral ol.overlay.OverlayOptions @exportObjectLiteralProperty ol.overlay.OverlayOptions.coordinate ol.Coordinate|undefined @exportObjectLiteralProperty ol.overlay.OverlayOptions.element Element|undefined @@ -81,14 +92,3 @@ @exportObjectLiteral ol.tilegrid.XYZOptions @exportObjectLiteralProperty ol.tilegrid.XYZOptions.maxZoom number - -@exportObjectLiteral ol.View2DOptions -@exportObjectLiteralProperty ol.View2DOptions.center ol.Coordinate|undefined -@exportObjectLiteralProperty ol.View2DOptions.maxResolution number|undefined -@exportObjectLiteralProperty ol.View2DOptions.numZoomLevels number|undefined -@exportObjectLiteralProperty ol.View2DOptions.projection ol.Projection|string|undefined -@exportObjectLiteralProperty ol.View2DOptions.resolution number|undefined -@exportObjectLiteralProperty ol.View2DOptions.resolutions Array.|undefined -@exportObjectLiteralProperty ol.View2DOptions.rotation number|undefined -@exportObjectLiteralProperty ol.View2DOptions.zoom number|undefined -@exportObjectLiteralProperty ol.View2DOptions.zoomFactor number|undefined From f377a70dc505c18ac4df0158809515226b584900 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Fri, 18 Jan 2013 01:59:59 +0100 Subject: [PATCH 03/60] Export ol.Map.addPreRenderFunction* --- src/ol/map.exports | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ol/map.exports b/src/ol/map.exports index b8cb394c18..8c4285f1c3 100644 --- a/src/ol/map.exports +++ b/src/ol/map.exports @@ -1,4 +1,6 @@ @exportClass ol.Map ol.MapOptions +@exportProperty ol.Map.prototype.addPreRenderFunction +@exportProperty ol.Map.prototype.addPreRenderFunctions @exportProperty ol.Map.prototype.getControls @exportProperty ol.Map.prototype.getInteractions From 11938d226474d69248248213547d0d44b88fe8e3 Mon Sep 17 00:00:00 2001 From: Peter Robins Date: Fri, 18 Jan 2013 13:01:10 +0100 Subject: [PATCH 04/60] Add missing dependencies, refs #116 --- src/ol/renderer/webgl/webglmaprenderer.js | 1 + src/ol/view.js | 1 + 2 files changed, 2 insertions(+) diff --git a/src/ol/renderer/webgl/webglmaprenderer.js b/src/ol/renderer/webgl/webglmaprenderer.js index 6e2e6c3990..c334e59b80 100644 --- a/src/ol/renderer/webgl/webglmaprenderer.js +++ b/src/ol/renderer/webgl/webglmaprenderer.js @@ -17,6 +17,7 @@ goog.require('goog.webgl'); goog.require('ol.Tile'); goog.require('ol.layer.Layer'); goog.require('ol.layer.TileLayer'); +goog.require('ol.renderer.Map'); goog.require('ol.renderer.webgl.FragmentShader'); goog.require('ol.renderer.webgl.TileLayer'); goog.require('ol.renderer.webgl.VertexShader'); diff --git a/src/ol/view.js b/src/ol/view.js index 27e0c9db1f..f69302fc98 100644 --- a/src/ol/view.js +++ b/src/ol/view.js @@ -5,6 +5,7 @@ goog.require('goog.array'); goog.require('ol.IView'); goog.require('ol.IView2D'); goog.require('ol.IView3D'); +goog.require('ol.Object'); /** From 2b8aefbabfc1677c0025a5d634428fc72afcca54 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Fri, 18 Jan 2013 14:03:37 +0100 Subject: [PATCH 05/60] Add ol.condition.altShiftKeysOnly --- src/ol/interaction/condition.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/ol/interaction/condition.js b/src/ol/interaction/condition.js index 81a34c4f63..3644843ddc 100644 --- a/src/ol/interaction/condition.js +++ b/src/ol/interaction/condition.js @@ -20,6 +20,18 @@ ol.interaction.condition.altKeyOnly = function(browserEvent) { }; +/** + * @param {goog.events.BrowserEvent} browserEvent Browser event. + * @return {boolean} True if only the alt and shift keys are pressed. + */ +ol.interaction.condition.altShiftKeysOnly = function(browserEvent) { + return ( + browserEvent.altKey && + !browserEvent.platformModifierKey && + browserEvent.shiftKey); +}; + + /** * @param {goog.events.BrowserEvent} browserEvent Browser event. * @return {boolean} True if only the no modifier keys are pressed. From f3978d200f90a8452ecdbbe641ad321d6362ec31 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Fri, 18 Jan 2013 14:04:33 +0100 Subject: [PATCH 06/60] Use Alt+Shift to rotate by default, refs #21 --- examples/side-by-side.html | 2 +- src/ol/map.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/side-by-side.html b/examples/side-by-side.html index ffc817397b..d113c5ea8d 100644 --- a/examples/side-by-side.html +++ b/examples/side-by-side.html @@ -45,7 +45,7 @@ Rotate: - Alt+drag, r to reset + Alt+Shift+drag, r to reset Brightness/contrast: diff --git a/src/ol/map.js b/src/ol/map.js index a0400327e3..85635f5615 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -893,8 +893,8 @@ ol.Map.createInteractions_ = function(mapOptions) { var rotate = goog.isDef(mapOptions.rotate) ? mapOptions.rotate : true; if (rotate) { - interactions.push( - new ol.interaction.DragRotate(ol.interaction.condition.altKeyOnly)); + interactions.push(new ol.interaction.DragRotate( + ol.interaction.condition.altShiftKeysOnly)); } var doubleClickZoom = goog.isDef(mapOptions.doubleClickZoom) ? From c0c9cdef15045ef4f9a741ed758f9d57066af858 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Thu, 17 Jan 2013 16:15:03 +0100 Subject: [PATCH 07/60] New tile queue implementation (heapify-powered) --- src/ol/tilequeue.js | 178 +++++++++++++++++++++++++++++---- test/jasmine-extensions.js | 6 ++ test/ol.html | 1 + test/spec/ol/tilequeue.test.js | 68 +++++++++++++ 4 files changed, 235 insertions(+), 18 deletions(-) create mode 100644 test/spec/ol/tilequeue.test.js diff --git a/src/ol/tilequeue.js b/src/ol/tilequeue.js index d73f7c0901..9fea413f33 100644 --- a/src/ol/tilequeue.js +++ b/src/ol/tilequeue.js @@ -1,14 +1,25 @@ goog.provide('ol.TilePriorityFunction'); goog.provide('ol.TileQueue'); +goog.require('goog.array'); goog.require('goog.events'); goog.require('goog.events.EventType'); -goog.require('goog.structs.PriorityQueue'); goog.require('ol.Coordinate'); goog.require('ol.Tile'); goog.require('ol.TileState'); +/** + * Tile Queue. + * + * The implementation is inspired from the Closure Library's Heap + * class and Python's heapq module. + * + * http://closure-library.googlecode.com/svn/docs/closure_goog_structs_heap.js.source.html + * http://hg.python.org/cpython/file/2.7/Lib/heapq.py + */ + + /** * @typedef {function(ol.Tile, ol.Coordinate, number): (number|undefined)} */ @@ -43,9 +54,9 @@ ol.TileQueue = function(tilePriorityFunction) { /** * @private - * @type {goog.structs.PriorityQueue} + * @type {Array.>} */ - this.queue_ = new goog.structs.PriorityQueue(); + this.heap_ = []; /** * @private @@ -57,12 +68,44 @@ ol.TileQueue = function(tilePriorityFunction) { /** + * FIXME empty description for jsdoc + */ +ol.TileQueue.prototype.clear = function() { + goog.array.clear(this.heap_); +}; + + +/** + * Remove and return the highest-priority tile. O(logn). + * @private + * @return {ol.Tile|undefined} Tile. + */ +ol.TileQueue.prototype.dequeue_ = function() { + var heap = this.heap_; + var count = heap.length; + if (count <= 0) { + return undefined; + } + var tile = /** @type {ol.Tile} */ (heap[0][1]); + if (count == 1) { + goog.array.clear(heap); + } else { + heap[0] = heap.pop(); + this.siftUp_(0); + } + var tileKey = tile.getKey(); + delete this.queuedTileKeys_[tileKey]; + return tile; +}; + + +/** + * Enqueue a tile. O(logn). * @param {ol.Tile} tile Tile. * @param {ol.Coordinate} tileCenter Tile center. * @param {number} tileResolution Tile resolution. */ -ol.TileQueue.prototype.enqueue = - function(tile, tileCenter, tileResolution) { +ol.TileQueue.prototype.enqueue = function(tile, tileCenter, tileResolution) { if (tile.getState() != ol.TileState.IDLE) { return; } @@ -70,8 +113,9 @@ ol.TileQueue.prototype.enqueue = if (!(tileKey in this.queuedTileKeys_)) { var priority = this.tilePriorityFunction_(tile, tileCenter, tileResolution); if (goog.isDef(priority)) { - this.queue_.enqueue(priority, arguments); + this.heap_.push([priority, tile, tileCenter, tileResolution]); this.queuedTileKeys_[tileKey] = true; + this.siftDown_(0, this.heap_.length - 1); } else { // FIXME fire drop event? } @@ -87,15 +131,57 @@ ol.TileQueue.prototype.handleTileChange = function() { }; +/** + * Gets the index of the left child of the node at the given index. + * @param {number} index The index of the node to get the left child for. + * @return {number} The index of the left child. + * @private + */ +ol.TileQueue.prototype.getLeftChildIndex_ = function(index) { + return index * 2 + 1; +}; + + +/** + * Gets the index of the right child of the node at the given index. + * @param {number} index The index of the node to get the right child for. + * @return {number} The index of the right child. + * @private + */ +ol.TileQueue.prototype.getRightChildIndex_ = function(index) { + return index * 2 + 2; +}; + + +/** + * Gets the index of the parent of the node at the given index. + * @param {number} index The index of the node to get the parent for. + * @return {number} The index of the parent. + * @private + */ +ol.TileQueue.prototype.getParentIndex_ = function(index) { + return (index - 1) >> 1; +}; + + +/** + * Make _heap a heap. O(n). + * @private + */ +ol.TileQueue.prototype.heapify_ = function() { + for (var i = (this.heap_.length >> 1) - 1; i >= 0; i--) { + this.siftUp_(i); + } +}; + + /** * FIXME empty description for jsdoc */ ol.TileQueue.prototype.loadMoreTiles = function() { - var tile, tileKey; - while (!this.queue_.isEmpty() && this.tilesLoading_ < this.maxTilesLoading_) { - tile = (/** @type {Array} */ (this.queue_.dequeue()))[0]; - tileKey = tile.getKey(); - delete this.queuedTileKeys_[tileKey]; + var tile; + while (this.heap_.length > 0 && this.tilesLoading_ < this.maxTilesLoading_) { + tile = /** @type {ol.Tile} */ (this.dequeue_()); goog.events.listen(tile, goog.events.EventType.CHANGE, this.handleTileChange, false, this); tile.load(); @@ -104,17 +190,73 @@ ol.TileQueue.prototype.loadMoreTiles = function() { }; +/** + * @param {number} index The index of the node to move down. + * @private + */ +ol.TileQueue.prototype.siftUp_ = function(index) { + var heap = this.heap_; + var count = heap.length; + var node = heap[index]; + var startIndex = index; + + while (index < (count >> 1)) { + var lIndex = this.getLeftChildIndex_(index); + var rIndex = this.getRightChildIndex_(index); + + var smallerChildIndex = rIndex < count && + heap[rIndex][0] < heap[lIndex][0] ? + rIndex : lIndex; + + heap[index] = heap[smallerChildIndex]; + index = smallerChildIndex; + } + + heap[index] = node; + this.siftDown_(startIndex, index); +}; + + +/** + * @param {number} startIndex The index of the root. + * @param {number} index The index of the node to move up. + * @private + */ +ol.TileQueue.prototype.siftDown_ = function(startIndex, index) { + var heap = this.heap_; + var node = heap[index]; + + while (index > startIndex) { + var parentIndex = this.getParentIndex_(index); + if (heap[parentIndex][0] > node[0]) { + heap[index] = heap[parentIndex]; + index = parentIndex; + } else { + break; + } + } + heap[index] = node; +}; + + /** * FIXME empty description for jsdoc */ ol.TileQueue.prototype.reprioritize = function() { - if (!this.queue_.isEmpty()) { - var values = /** @type {Array.} */ (this.queue_.getValues()); - this.queue_.clear(); - this.queuedTileKeys_ = {}; - var i; - for (i = 0; i < values.length; ++i) { - this.enqueue.apply(this, values[i]); + var heap = this.heap_; + var count = heap.length; + var i, priority, node, tile, tileCenter, tileResolution; + for (i = count - 1; i >= 0; i--) { + node = heap[i]; + tile = /** @type {ol.Tile} */ (node[1]); + tileCenter = /** @type {ol.Coordinate} */ (node[2]); + tileResolution = /** @type {number} */ (node[3]); + priority = this.tilePriorityFunction_(tile, tileCenter, tileResolution); + if (goog.isDef(priority)) { + node[0] = priority; + } else { + goog.array.removeAt(heap, i); } } + this.heapify_(); }; diff --git a/test/jasmine-extensions.js b/test/jasmine-extensions.js index 4649130908..46e24cb7ae 100644 --- a/test/jasmine-extensions.js +++ b/test/jasmine-extensions.js @@ -4,6 +4,12 @@ beforeEach(function() { toBeA: function(type) { return this.actual instanceof type; }, + toBeGreaterThanOrEqualTo: function(other) { + return this.actual >= other; + }, + toBeLessThanOrEqualTo: function(other) { + return this.actual <= other; + }, toRoughlyEqual: function(other, tol) { return Math.abs(this.actual - other) <= tol; } diff --git a/test/ol.html b/test/ol.html index b0e05e2f6d..c490d01fd1 100644 --- a/test/ol.html +++ b/test/ol.html @@ -84,6 +84,7 @@ + diff --git a/test/spec/ol/tilequeue.test.js b/test/spec/ol/tilequeue.test.js new file mode 100644 index 0000000000..3c5bc8a055 --- /dev/null +++ b/test/spec/ol/tilequeue.test.js @@ -0,0 +1,68 @@ +describe('ol.TileQueue', function() { + + // is the tile queue's array a heap? + function isHeap(tq) { + var heap = tq.heap_; + var i; + var key; + var leftKey; + var rightKey; + for (i = 0; i < (heap.length >> 1) - 1; i++) { + key = heap[i][0]; + leftKey = heap[tq.getLeftChildIndex_(i)][0]; + rightKey = heap[tq.getRightChildIndex_(i)][0]; + if (leftKey < key || rightKey < key) { + return false; + } + } + return true; + } + + function addRandomPriorityTiles(tq, num) { + var i, tile, priority; + for (i = 0; i < num; i++) { + tile = new ol.Tile(); + priority = Math.floor(Math.random() * 100); + tq.heap_.push([priority, tile, new ol.Coordinate(0, 0), 1]); + tq.queuedTileKeys_[tile.getKey()] = true; + } + } + + describe('heapify', function() { + it('does convert an arbitrary array into a heap', function() { + + var tq = new ol.TileQueue(function() {}); + addRandomPriorityTiles(tq, 100); + + tq.heapify_(); + expect(isHeap(tq)).toBeTruthy(); + }); + }); + + describe('reprioritize', function() { + it('does reprioritize the array', function() { + + var tq = new ol.TileQueue(function() {}); + addRandomPriorityTiles(tq, 100); + + tq.heapify_(); + + // now reprioritize, changing the priority of 50 tiles and removing the + // rest + + var i = 0; + tq.tilePriorityFunction_ = function() { + if ((i++) % 2 === 0) { + return undefined; + } + return Math.floor(Math.random() * 100); + }; + + tq.reprioritize(); + expect(tq.heap_.length).toEqual(50); + expect(isHeap(tq)).toBeTruthy(); + + }); + }); +}); + From 9cf314fd64af34e15a0f31840988b62f4b05da12 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Fri, 18 Jan 2013 16:04:16 +0100 Subject: [PATCH 08/60] Sort object properties --- src/ol/map.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ol/map.js b/src/ol/map.js index a0400327e3..6d5caa091f 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -628,9 +628,9 @@ ol.Map.prototype.renderFrame_ = function(time) { size: size, tileQueue: this.tileQueue_, tileUsage: {}, + time: time, view2DState: view2DState, - viewHints: viewHints, - time: time + viewHints: viewHints }; } From 7babfebef96fcc2ae9dab4eedcbf21334c2c1b53 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Fri, 18 Jan 2013 16:35:23 +0100 Subject: [PATCH 09/60] Set frame state immediately after rendering --- src/ol/map.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ol/map.js b/src/ol/map.js index 6d5caa091f..2609269db2 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -664,6 +664,8 @@ ol.Map.prototype.renderFrame_ = function(time) { } this.renderer_.renderFrame(frameState); + this.frameState_ = frameState; + this.dirty_ = false; if (!goog.isNull(frameState)) { if (frameState.animate) { @@ -672,8 +674,6 @@ ol.Map.prototype.renderFrame_ = function(time) { Array.prototype.push.apply( this.postRenderFunctions_, frameState.postRenderFunctions); } - this.frameState_ = frameState; - this.dirty_ = false; this.dispatchEvent( new ol.MapEvent(ol.MapEventType.POSTRENDER, this, frameState)); From 111b4585a8f8cc400fcbc4cc6d523114ae5d9390 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Fri, 18 Jan 2013 16:36:41 +0100 Subject: [PATCH 10/60] Fix key usage --- src/ol/renderer/layerrenderer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ol/renderer/layerrenderer.js b/src/ol/renderer/layerrenderer.js index b2003467ab..6e1a4fd729 100644 --- a/src/ol/renderer/layerrenderer.js +++ b/src/ol/renderer/layerrenderer.js @@ -141,7 +141,7 @@ ol.renderer.Layer.prototype.updateTileUsage = var sourceKey = goog.getUid(source).toString(); var zKey = z.toString(); if (sourceKey in tileUsage) { - if (z in tileUsage[sourceKey]) { + if (zKey in tileUsage[sourceKey]) { tileUsage[sourceKey][zKey].extend(tileRange); } else { tileUsage[sourceKey][zKey] = tileRange; From 1c524caae610554a112b1295d2560f121edea51a Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Fri, 18 Jan 2013 16:36:26 +0100 Subject: [PATCH 11/60] Discard unwanted tiles from tile queue --- src/ol/framestate.js | 5 +++- src/ol/map.js | 24 +++++++++++------- .../canvas/canvastilelayerrenderer.js | 4 ++- src/ol/renderer/dom/domtilelayerrenderer.js | 4 ++- src/ol/renderer/layerrenderer.js | 25 +++++++++++++++++++ .../renderer/webgl/webgltilelayerrenderer.js | 4 ++- src/ol/tilequeue.js | 18 ++++++------- test/spec/ol/tilequeue.test.js | 2 +- 8 files changed, 63 insertions(+), 23 deletions(-) diff --git a/src/ol/framestate.js b/src/ol/framestate.js index 574fefe20f..d0b7ec4570 100644 --- a/src/ol/framestate.js +++ b/src/ol/framestate.js @@ -1,4 +1,6 @@ // FIXME add view3DState +// FIXME rename tileUsage to usedTiles +// FIXME factor out common code between tileUsage and wantedTiles goog.provide('ol.FrameState'); goog.provide('ol.PostRenderFunction'); @@ -29,7 +31,8 @@ goog.require('ol.layer.LayerState'); * tileUsage: Object.>, * time: number, * view2DState: ol.View2DState, - * viewHints: Array.}} + * viewHints: Array., + * wantedTiles: Object.>}} */ ol.FrameState; diff --git a/src/ol/map.js b/src/ol/map.js index 2609269db2..33fdaed885 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -453,19 +453,24 @@ ol.Map.prototype.getOverlayContainer = function() { /** * @param {ol.Tile} tile Tile. + * @param {string} tileSourceKey Tile source key. * @param {ol.Coordinate} tileCenter Tile center. - * @param {number} tileResolution Tile resolution. * @return {number|undefined} Tile priority. */ -ol.Map.prototype.getTilePriority = function(tile, tileCenter, tileResolution) { - if (goog.isNull(this.frameState_)) { +ol.Map.prototype.getTilePriority = function(tile, tileSourceKey, tileCenter) { + var frameState = this.frameState_; + if (goog.isNull(frameState) || !(tileSourceKey in frameState.wantedTiles)) { return undefined; - } else { - var center = this.frameState_.view2DState.center; - var deltaX = tileCenter.x - center.x; - var deltaY = tileCenter.y - center.y; - return Math.sqrt(deltaX * deltaX + deltaY * deltaY) / tileResolution; } + var zKey = tile.tileCoord.z.toString(); + if (!(zKey in frameState.wantedTiles[tileSourceKey]) || + !frameState.wantedTiles[tileSourceKey][zKey].contains(tile.tileCoord)) { + return undefined; + } + var center = frameState.view2DState.center; + var deltaX = tileCenter.x - center.x; + var deltaY = tileCenter.y - center.y; + return deltaX * deltaX + deltaY * deltaY; }; @@ -630,7 +635,8 @@ ol.Map.prototype.renderFrame_ = function(time) { tileUsage: {}, time: time, view2DState: view2DState, - viewHints: viewHints + viewHints: viewHints, + wantedTiles: {} }; } diff --git a/src/ol/renderer/canvas/canvastilelayerrenderer.js b/src/ol/renderer/canvas/canvastilelayerrenderer.js index 4c17795648..66c9cf6e25 100644 --- a/src/ol/renderer/canvas/canvastilelayerrenderer.js +++ b/src/ol/renderer/canvas/canvastilelayerrenderer.js @@ -87,6 +87,7 @@ ol.renderer.canvas.TileLayer.prototype.renderFrame = var tileLayer = this.getTileLayer(); var tileSource = tileLayer.getTileSource(); + var tileSourceKey = goog.getUid(tileSource).toString(); var tileGrid = tileSource.getTileGrid(); var tileSize = tileGrid.getTileSize(); var z = tileGrid.getZForResolution(view2DState.resolution); @@ -165,7 +166,7 @@ ol.renderer.canvas.TileLayer.prototype.renderFrame = tileState = tile.getState(); if (tileState == ol.TileState.IDLE) { tileCenter = tileGrid.getTileCoordCenter(tileCoord); - frameState.tileQueue.enqueue(tile, tileCenter, tileResolution); + frameState.tileQueue.enqueue(tile, tileSourceKey, tileCenter); } else if (tileState == ol.TileState.LOADED) { tilesToDrawByZ[z][tileCoord.toString()] = tile; continue; @@ -216,6 +217,7 @@ ol.renderer.canvas.TileLayer.prototype.renderFrame = } this.updateTileUsage(frameState.tileUsage, tileSource, z, tileRange); + this.updateWantedTiles(frameState.wantedTiles, tileSource, z, tileRange); var transform = this.transform_; goog.vec.Mat4.makeIdentity(transform); diff --git a/src/ol/renderer/dom/domtilelayerrenderer.js b/src/ol/renderer/dom/domtilelayerrenderer.js index 58a55295f7..fa9a86b1ae 100644 --- a/src/ol/renderer/dom/domtilelayerrenderer.js +++ b/src/ol/renderer/dom/domtilelayerrenderer.js @@ -83,6 +83,7 @@ ol.renderer.dom.TileLayer.prototype.renderFrame = var tileLayer = this.getTileLayer(); var tileSource = tileLayer.getTileSource(); + var tileSourceKey = goog.getUid(tileSource).toString(); var tileGrid = tileSource.getTileGrid(); var z = tileGrid.getZForResolution(view2DState.resolution); var tileResolution = tileGrid.getResolution(z); @@ -132,7 +133,7 @@ ol.renderer.dom.TileLayer.prototype.renderFrame = tileState = tile.getState(); if (tileState == ol.TileState.IDLE) { tileCenter = tileGrid.getTileCoordCenter(tileCoord); - frameState.tileQueue.enqueue(tile, tileCenter, tileResolution); + frameState.tileQueue.enqueue(tile, tileSourceKey, tileCenter); } else if (tileState == ol.TileState.LOADED) { tilesToDrawByZ[z][tileCoord.toString()] = tile; continue; @@ -237,6 +238,7 @@ ol.renderer.dom.TileLayer.prototype.renderFrame = } this.updateTileUsage(frameState.tileUsage, tileSource, z, tileRange); + this.updateWantedTiles(frameState.wantedTiles, tileSource, z, tileRange); }; diff --git a/src/ol/renderer/layerrenderer.js b/src/ol/renderer/layerrenderer.js index 6e1a4fd729..c019c12276 100644 --- a/src/ol/renderer/layerrenderer.js +++ b/src/ol/renderer/layerrenderer.js @@ -151,3 +151,28 @@ ol.renderer.Layer.prototype.updateTileUsage = tileUsage[sourceKey][zKey] = tileRange; } }; + + +/** + * @protected + * @param {Object.>} wantedTiles Wanted + * tile ranges. + * @param {ol.source.Source} source Source. + * @param {number} z Z. + * @param {ol.TileRange} tileRange Tile range. + */ +ol.renderer.Layer.prototype.updateWantedTiles = + function(wantedTiles, source, z, tileRange) { + var sourceKey = goog.getUid(source).toString(); + var zKey = z.toString(); + if (sourceKey in wantedTiles) { + if (zKey in wantedTiles[sourceKey]) { + wantedTiles[sourceKey][zKey].extend(tileRange); + } else { + wantedTiles[sourceKey][zKey] = tileRange; + } + } else { + wantedTiles[sourceKey] = {}; + wantedTiles[sourceKey][zKey] = tileRange; + } +}; diff --git a/src/ol/renderer/webgl/webgltilelayerrenderer.js b/src/ol/renderer/webgl/webgltilelayerrenderer.js index de1f18e1e2..93654d0a98 100644 --- a/src/ol/renderer/webgl/webgltilelayerrenderer.js +++ b/src/ol/renderer/webgl/webgltilelayerrenderer.js @@ -270,6 +270,7 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = var tileLayer = this.getTileLayer(); var tileSource = tileLayer.getTileSource(); + var tileSourceKey = goog.getUid(tileSource).toString(); var tileGrid = tileSource.getTileGrid(); var z = tileGrid.getZForResolution(view2DState.resolution); var tileResolution = tileGrid.getResolution(z); @@ -392,7 +393,7 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = tileState = tile.getState(); if (tileState == ol.TileState.IDLE) { tileCenter = tileGrid.getTileCoordCenter(tileCoord); - frameState.tileQueue.enqueue(tile, tileCenter, tileResolution); + frameState.tileQueue.enqueue(tile, tileSourceKey, tileCenter); } else if (tileState == ol.TileState.LOADED) { if (mapRenderer.isTileTextureLoaded(tile)) { tilesToDrawByZ[z][tileCoord.toString()] = tile; @@ -460,6 +461,7 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = } this.updateTileUsage(frameState.tileUsage, tileSource, z, tileRange); + this.updateWantedTiles(frameState.wantedTiles, tileSource, z, tileRange); goog.vec.Mat4.makeIdentity(this.matrix_); goog.vec.Mat4.translate(this.matrix_, diff --git a/src/ol/tilequeue.js b/src/ol/tilequeue.js index 9fea413f33..d914257beb 100644 --- a/src/ol/tilequeue.js +++ b/src/ol/tilequeue.js @@ -21,7 +21,7 @@ goog.require('ol.TileState'); /** - * @typedef {function(ol.Tile, ol.Coordinate, number): (number|undefined)} + * @typedef {function(ol.Tile, string, ol.Coordinate): (number|undefined)} */ ol.TilePriorityFunction; @@ -102,18 +102,18 @@ ol.TileQueue.prototype.dequeue_ = function() { /** * Enqueue a tile. O(logn). * @param {ol.Tile} tile Tile. + * @param {string} tileSourceKey Tile source key. * @param {ol.Coordinate} tileCenter Tile center. - * @param {number} tileResolution Tile resolution. */ -ol.TileQueue.prototype.enqueue = function(tile, tileCenter, tileResolution) { +ol.TileQueue.prototype.enqueue = function(tile, tileSourceKey, tileCenter) { if (tile.getState() != ol.TileState.IDLE) { return; } var tileKey = tile.getKey(); if (!(tileKey in this.queuedTileKeys_)) { - var priority = this.tilePriorityFunction_(tile, tileCenter, tileResolution); + var priority = this.tilePriorityFunction_(tile, tileSourceKey, tileCenter); if (goog.isDef(priority)) { - this.heap_.push([priority, tile, tileCenter, tileResolution]); + this.heap_.push([priority, tile, tileSourceKey, tileCenter]); this.queuedTileKeys_[tileKey] = true; this.siftDown_(0, this.heap_.length - 1); } else { @@ -245,13 +245,13 @@ ol.TileQueue.prototype.siftDown_ = function(startIndex, index) { ol.TileQueue.prototype.reprioritize = function() { var heap = this.heap_; var count = heap.length; - var i, priority, node, tile, tileCenter, tileResolution; + var i, priority, node, tile, tileCenter, tileSourceKey; for (i = count - 1; i >= 0; i--) { node = heap[i]; tile = /** @type {ol.Tile} */ (node[1]); - tileCenter = /** @type {ol.Coordinate} */ (node[2]); - tileResolution = /** @type {number} */ (node[3]); - priority = this.tilePriorityFunction_(tile, tileCenter, tileResolution); + tileSourceKey = /** @type {string} */ (node[2]); + tileCenter = /** @type {ol.Coordinate} */ (node[3]); + priority = this.tilePriorityFunction_(tile, tileSourceKey, tileCenter); if (goog.isDef(priority)) { node[0] = priority; } else { diff --git a/test/spec/ol/tilequeue.test.js b/test/spec/ol/tilequeue.test.js index 3c5bc8a055..f4fa134a1c 100644 --- a/test/spec/ol/tilequeue.test.js +++ b/test/spec/ol/tilequeue.test.js @@ -23,7 +23,7 @@ describe('ol.TileQueue', function() { for (i = 0; i < num; i++) { tile = new ol.Tile(); priority = Math.floor(Math.random() * 100); - tq.heap_.push([priority, tile, new ol.Coordinate(0, 0), 1]); + tq.heap_.push([priority, tile, '', new ol.Coordinate(0, 0)]); tq.queuedTileKeys_[tile.getKey()] = true; } } From 3ec2014ad1d4e97df330d9e7110a9e650c4886ff Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Fri, 18 Jan 2013 16:46:32 +0100 Subject: [PATCH 12/60] Rename tileUsage to usedTiles --- src/ol/control/attributioncontrol.js | 14 +++++++------- src/ol/framestate.js | 5 ++--- src/ol/map.js | 2 +- .../renderer/canvas/canvastilelayerrenderer.js | 2 +- src/ol/renderer/dom/domtilelayerrenderer.js | 2 +- src/ol/renderer/layerrenderer.js | 18 +++++++++--------- .../renderer/webgl/webgltilelayerrenderer.js | 2 +- 7 files changed, 22 insertions(+), 23 deletions(-) diff --git a/src/ol/control/attributioncontrol.js b/src/ol/control/attributioncontrol.js index f7148722e8..52e33effba 100644 --- a/src/ol/control/attributioncontrol.js +++ b/src/ol/control/attributioncontrol.js @@ -69,7 +69,7 @@ ol.control.Attribution.prototype.handleMapPostrender = function(mapEvent) { if (goog.isNull(frameState)) { this.updateElement_(null); } else { - this.updateElement_(frameState.tileUsage); + this.updateElement_(frameState.usedTiles); } }; @@ -94,12 +94,12 @@ ol.control.Attribution.prototype.setMap = function(map) { /** * @private - * @param {?Object.>} tileUsage Tile - * usage. + * @param {?Object.>} usedTiles Used + * tiles. */ -ol.control.Attribution.prototype.updateElement_ = function(tileUsage) { +ol.control.Attribution.prototype.updateElement_ = function(usedTiles) { - if (goog.isNull(tileUsage)) { + if (goog.isNull(usedTiles)) { if (this.renderedVisible_) { goog.style.showElement(this.element, false); this.renderedVisible_ = false; @@ -136,14 +136,14 @@ ol.control.Attribution.prototype.updateElement_ = function(tileUsage) { var attributions = {}; var i, tileRanges, tileSource, tileSourceAttribution, tileSourceAttributionKey, tileSourceAttributions, tileSourceKey, z; - for (tileSourceKey in tileUsage) { + for (tileSourceKey in usedTiles) { goog.asserts.assert(tileSourceKey in tileSources); tileSource = tileSources[tileSourceKey]; tileSourceAttributions = tileSource.getAttributions(); if (goog.isNull(tileSourceAttributions)) { continue; } - tileRanges = tileUsage[tileSourceKey]; + tileRanges = usedTiles[tileSourceKey]; for (i = 0; i < tileSourceAttributions.length; ++i) { tileSourceAttribution = tileSourceAttributions[i]; tileSourceAttributionKey = goog.getUid(tileSourceAttribution).toString(); diff --git a/src/ol/framestate.js b/src/ol/framestate.js index d0b7ec4570..6f06b5fff6 100644 --- a/src/ol/framestate.js +++ b/src/ol/framestate.js @@ -1,6 +1,5 @@ // FIXME add view3DState -// FIXME rename tileUsage to usedTiles -// FIXME factor out common code between tileUsage and wantedTiles +// FIXME factor out common code between usedTiles and wantedTiles goog.provide('ol.FrameState'); goog.provide('ol.PostRenderFunction'); @@ -28,8 +27,8 @@ goog.require('ol.layer.LayerState'); * postRenderFunctions: Array., * size: ol.Size, * tileQueue: ol.TileQueue, - * tileUsage: Object.>, * time: number, + * usedTiles: Object.>, * view2DState: ol.View2DState, * viewHints: Array., * wantedTiles: Object.>}} diff --git a/src/ol/map.js b/src/ol/map.js index 33fdaed885..c31e7d88b0 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -632,8 +632,8 @@ ol.Map.prototype.renderFrame_ = function(time) { postRenderFunctions: [], size: size, tileQueue: this.tileQueue_, - tileUsage: {}, time: time, + usedTiles: {}, view2DState: view2DState, viewHints: viewHints, wantedTiles: {} diff --git a/src/ol/renderer/canvas/canvastilelayerrenderer.js b/src/ol/renderer/canvas/canvastilelayerrenderer.js index 66c9cf6e25..8b6a3d0a1d 100644 --- a/src/ol/renderer/canvas/canvastilelayerrenderer.js +++ b/src/ol/renderer/canvas/canvastilelayerrenderer.js @@ -216,7 +216,7 @@ ol.renderer.canvas.TileLayer.prototype.renderFrame = frameState.animate = true; } - this.updateTileUsage(frameState.tileUsage, tileSource, z, tileRange); + this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange); this.updateWantedTiles(frameState.wantedTiles, tileSource, z, tileRange); var transform = this.transform_; diff --git a/src/ol/renderer/dom/domtilelayerrenderer.js b/src/ol/renderer/dom/domtilelayerrenderer.js index fa9a86b1ae..afabed997d 100644 --- a/src/ol/renderer/dom/domtilelayerrenderer.js +++ b/src/ol/renderer/dom/domtilelayerrenderer.js @@ -237,7 +237,7 @@ ol.renderer.dom.TileLayer.prototype.renderFrame = frameState.animate = true; } - this.updateTileUsage(frameState.tileUsage, tileSource, z, tileRange); + this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange); this.updateWantedTiles(frameState.wantedTiles, tileSource, z, tileRange); }; diff --git a/src/ol/renderer/layerrenderer.js b/src/ol/renderer/layerrenderer.js index c019c12276..c460fafb3b 100644 --- a/src/ol/renderer/layerrenderer.js +++ b/src/ol/renderer/layerrenderer.js @@ -130,25 +130,25 @@ ol.renderer.Layer.prototype.handleLayerVisibleChange = goog.nullFunction; /** * @protected - * @param {Object.>} tileUsage Tile usage. + * @param {Object.>} usedTiles Used tiles. * @param {ol.source.Source} source Source. * @param {number} z Z. * @param {ol.TileRange} tileRange Tile range. */ -ol.renderer.Layer.prototype.updateTileUsage = - function(tileUsage, source, z, tileRange) { +ol.renderer.Layer.prototype.updateUsedTiles = + function(usedTiles, source, z, tileRange) { // FIXME should we use tilesToDrawByZ instead? var sourceKey = goog.getUid(source).toString(); var zKey = z.toString(); - if (sourceKey in tileUsage) { - if (zKey in tileUsage[sourceKey]) { - tileUsage[sourceKey][zKey].extend(tileRange); + if (sourceKey in usedTiles) { + if (zKey in usedTiles[sourceKey]) { + usedTiles[sourceKey][zKey].extend(tileRange); } else { - tileUsage[sourceKey][zKey] = tileRange; + usedTiles[sourceKey][zKey] = tileRange; } } else { - tileUsage[sourceKey] = {}; - tileUsage[sourceKey][zKey] = tileRange; + usedTiles[sourceKey] = {}; + usedTiles[sourceKey][zKey] = tileRange; } }; diff --git a/src/ol/renderer/webgl/webgltilelayerrenderer.js b/src/ol/renderer/webgl/webgltilelayerrenderer.js index 93654d0a98..63e3e86238 100644 --- a/src/ol/renderer/webgl/webgltilelayerrenderer.js +++ b/src/ol/renderer/webgl/webgltilelayerrenderer.js @@ -460,7 +460,7 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = } - this.updateTileUsage(frameState.tileUsage, tileSource, z, tileRange); + this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange); this.updateWantedTiles(frameState.wantedTiles, tileSource, z, tileRange); goog.vec.Mat4.makeIdentity(this.matrix_); From 4846a6a7b3003b60351d4e5e183da3cba0dddf07 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Fri, 18 Jan 2013 16:51:56 +0100 Subject: [PATCH 13/60] Only update wanted tiles if not all tiles are loaded --- src/ol/renderer/canvas/canvastilelayerrenderer.js | 2 +- src/ol/renderer/dom/domtilelayerrenderer.js | 2 +- src/ol/renderer/webgl/webgltilelayerrenderer.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ol/renderer/canvas/canvastilelayerrenderer.js b/src/ol/renderer/canvas/canvastilelayerrenderer.js index 8b6a3d0a1d..c79251e6f9 100644 --- a/src/ol/renderer/canvas/canvastilelayerrenderer.js +++ b/src/ol/renderer/canvas/canvastilelayerrenderer.js @@ -214,10 +214,10 @@ ol.renderer.canvas.TileLayer.prototype.renderFrame = if (!allTilesLoaded) { frameState.animate = true; + this.updateWantedTiles(frameState.wantedTiles, tileSource, z, tileRange); } this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange); - this.updateWantedTiles(frameState.wantedTiles, tileSource, z, tileRange); var transform = this.transform_; goog.vec.Mat4.makeIdentity(transform); diff --git a/src/ol/renderer/dom/domtilelayerrenderer.js b/src/ol/renderer/dom/domtilelayerrenderer.js index afabed997d..9cf215a98e 100644 --- a/src/ol/renderer/dom/domtilelayerrenderer.js +++ b/src/ol/renderer/dom/domtilelayerrenderer.js @@ -235,10 +235,10 @@ ol.renderer.dom.TileLayer.prototype.renderFrame = if (!allTilesLoaded) { frameState.animate = true; + this.updateWantedTiles(frameState.wantedTiles, tileSource, z, tileRange); } this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange); - this.updateWantedTiles(frameState.wantedTiles, tileSource, z, tileRange); }; diff --git a/src/ol/renderer/webgl/webgltilelayerrenderer.js b/src/ol/renderer/webgl/webgltilelayerrenderer.js index 63e3e86238..621883f635 100644 --- a/src/ol/renderer/webgl/webgltilelayerrenderer.js +++ b/src/ol/renderer/webgl/webgltilelayerrenderer.js @@ -456,12 +456,12 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = this.renderedTileRange_ = null; this.renderedFramebufferExtent_ = null; frameState.animate = true; + this.updateWantedTiles(frameState.wantedTiles, tileSource, z, tileRange); } } this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange); - this.updateWantedTiles(frameState.wantedTiles, tileSource, z, tileRange); goog.vec.Mat4.makeIdentity(this.matrix_); goog.vec.Mat4.translate(this.matrix_, From a8dc8106960ec3598dcf6360e0be3fbb076b22bf Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Sat, 19 Jan 2013 15:10:22 +0100 Subject: [PATCH 14/60] Use Infinity rather than undefined to drop tiles --- src/ol/map.js | 6 +++--- src/ol/tilequeue.js | 12 +++++++++--- test/spec/ol/tilequeue.test.js | 2 +- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/ol/map.js b/src/ol/map.js index c31e7d88b0..a580e5b315 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -455,17 +455,17 @@ ol.Map.prototype.getOverlayContainer = function() { * @param {ol.Tile} tile Tile. * @param {string} tileSourceKey Tile source key. * @param {ol.Coordinate} tileCenter Tile center. - * @return {number|undefined} Tile priority. + * @return {number} Tile priority. */ ol.Map.prototype.getTilePriority = function(tile, tileSourceKey, tileCenter) { var frameState = this.frameState_; if (goog.isNull(frameState) || !(tileSourceKey in frameState.wantedTiles)) { - return undefined; + return ol.TileQueue.DROP; } var zKey = tile.tileCoord.z.toString(); if (!(zKey in frameState.wantedTiles[tileSourceKey]) || !frameState.wantedTiles[tileSourceKey][zKey].contains(tile.tileCoord)) { - return undefined; + return ol.TileQueue.DROP; } var center = frameState.view2DState.center; var deltaX = tileCenter.x - center.x; diff --git a/src/ol/tilequeue.js b/src/ol/tilequeue.js index d914257beb..1df40e0f01 100644 --- a/src/ol/tilequeue.js +++ b/src/ol/tilequeue.js @@ -21,7 +21,7 @@ goog.require('ol.TileState'); /** - * @typedef {function(ol.Tile, string, ol.Coordinate): (number|undefined)} + * @typedef {function(ol.Tile, string, ol.Coordinate): number} */ ol.TilePriorityFunction; @@ -67,6 +67,12 @@ ol.TileQueue = function(tilePriorityFunction) { }; +/** + * @const {number} + */ +ol.TileQueue.DROP = Infinity; + + /** * FIXME empty description for jsdoc */ @@ -112,7 +118,7 @@ ol.TileQueue.prototype.enqueue = function(tile, tileSourceKey, tileCenter) { var tileKey = tile.getKey(); if (!(tileKey in this.queuedTileKeys_)) { var priority = this.tilePriorityFunction_(tile, tileSourceKey, tileCenter); - if (goog.isDef(priority)) { + if (priority != ol.TileQueue.DROP) { this.heap_.push([priority, tile, tileSourceKey, tileCenter]); this.queuedTileKeys_[tileKey] = true; this.siftDown_(0, this.heap_.length - 1); @@ -252,7 +258,7 @@ ol.TileQueue.prototype.reprioritize = function() { tileSourceKey = /** @type {string} */ (node[2]); tileCenter = /** @type {ol.Coordinate} */ (node[3]); priority = this.tilePriorityFunction_(tile, tileSourceKey, tileCenter); - if (goog.isDef(priority)) { + if (priority != ol.TileQueue.DROP) { node[0] = priority; } else { goog.array.removeAt(heap, i); diff --git a/test/spec/ol/tilequeue.test.js b/test/spec/ol/tilequeue.test.js index f4fa134a1c..37b8d609e0 100644 --- a/test/spec/ol/tilequeue.test.js +++ b/test/spec/ol/tilequeue.test.js @@ -53,7 +53,7 @@ describe('ol.TileQueue', function() { var i = 0; tq.tilePriorityFunction_ = function() { if ((i++) % 2 === 0) { - return undefined; + return ol.TileQueue.DROP; } return Math.floor(Math.random() * 100); }; From 4b971b5f32d85af40b75039e8285ffd19d7242f8 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Sat, 19 Jan 2013 15:24:50 +0100 Subject: [PATCH 15/60] Optimize tile dropping in reprioritize --- src/ol/tilequeue.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/ol/tilequeue.js b/src/ol/tilequeue.js index 1df40e0f01..8a599d5439 100644 --- a/src/ol/tilequeue.js +++ b/src/ol/tilequeue.js @@ -250,9 +250,8 @@ ol.TileQueue.prototype.siftDown_ = function(startIndex, index) { */ ol.TileQueue.prototype.reprioritize = function() { var heap = this.heap_; - var count = heap.length; - var i, priority, node, tile, tileCenter, tileSourceKey; - for (i = count - 1; i >= 0; i--) { + var i, n = 0, node, priority, tile, tileCenter, tileSourceKey; + for (i = 0; i < heap.length; ++i) { node = heap[i]; tile = /** @type {ol.Tile} */ (node[1]); tileSourceKey = /** @type {string} */ (node[2]); @@ -260,9 +259,9 @@ ol.TileQueue.prototype.reprioritize = function() { priority = this.tilePriorityFunction_(tile, tileSourceKey, tileCenter); if (priority != ol.TileQueue.DROP) { node[0] = priority; - } else { - goog.array.removeAt(heap, i); + heap[n++] = node; } } + heap.length = n; this.heapify_(); }; From 2bdfd3765ba10309d3f0852672edadc439a3bfb5 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Sat, 19 Jan 2013 15:38:15 +0100 Subject: [PATCH 16/60] Remove stale FIXME --- src/ol/tilequeue.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ol/tilequeue.js b/src/ol/tilequeue.js index 8a599d5439..caedf4b1a2 100644 --- a/src/ol/tilequeue.js +++ b/src/ol/tilequeue.js @@ -122,8 +122,6 @@ ol.TileQueue.prototype.enqueue = function(tile, tileSourceKey, tileCenter) { this.heap_.push([priority, tile, tileSourceKey, tileCenter]); this.queuedTileKeys_[tileKey] = true; this.siftDown_(0, this.heap_.length - 1); - } else { - // FIXME fire drop event? } } }; From 6507e71dd785e661dc3d549666c56e8b9e3db713 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Sat, 19 Jan 2013 15:44:01 +0100 Subject: [PATCH 17/60] Clear queued tile keys when queue is cleared --- src/ol/tilequeue.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ol/tilequeue.js b/src/ol/tilequeue.js index caedf4b1a2..18cf82e453 100644 --- a/src/ol/tilequeue.js +++ b/src/ol/tilequeue.js @@ -78,6 +78,7 @@ ol.TileQueue.DROP = Infinity; */ ol.TileQueue.prototype.clear = function() { goog.array.clear(this.heap_); + this.queuedTileKeys_ = {}; }; From d43abba154776adda130f79c35a0e204eb7e8c44 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Sat, 19 Jan 2013 15:44:58 +0100 Subject: [PATCH 18/60] Use stricter types in private methods, operate on array directly --- src/ol/tilequeue.js | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/ol/tilequeue.js b/src/ol/tilequeue.js index 18cf82e453..baf61b1e06 100644 --- a/src/ol/tilequeue.js +++ b/src/ol/tilequeue.js @@ -1,7 +1,6 @@ goog.provide('ol.TilePriorityFunction'); goog.provide('ol.TileQueue'); -goog.require('goog.array'); goog.require('goog.events'); goog.require('goog.events.EventType'); goog.require('ol.Coordinate'); @@ -77,7 +76,7 @@ ol.TileQueue.DROP = Infinity; * FIXME empty description for jsdoc */ ol.TileQueue.prototype.clear = function() { - goog.array.clear(this.heap_); + this.heap_.length = 0; this.queuedTileKeys_ = {}; }; @@ -85,17 +84,14 @@ ol.TileQueue.prototype.clear = function() { /** * Remove and return the highest-priority tile. O(logn). * @private - * @return {ol.Tile|undefined} Tile. + * @return {ol.Tile} Tile. */ ol.TileQueue.prototype.dequeue_ = function() { var heap = this.heap_; - var count = heap.length; - if (count <= 0) { - return undefined; - } + goog.asserts.assert(heap.length > 0); var tile = /** @type {ol.Tile} */ (heap[0][1]); - if (count == 1) { - goog.array.clear(heap); + if (heap.length == 1) { + heap.length = 0; } else { heap[0] = heap.pop(); this.siftUp_(0); From 3383c426b36419082a02ae404940f2f0c66f8db5 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Sat, 19 Jan 2013 18:35:24 +0100 Subject: [PATCH 19/60] Use shifts rather than floor --- src/ol/tilegrid/xyztilegrid.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ol/tilegrid/xyztilegrid.js b/src/ol/tilegrid/xyztilegrid.js index b7b16faf60..c948010ba7 100644 --- a/src/ol/tilegrid/xyztilegrid.js +++ b/src/ol/tilegrid/xyztilegrid.js @@ -48,8 +48,8 @@ ol.tilegrid.XYZ.prototype.forEachTileCoordParentTileRange = if (z < 0) { break; } - x = Math.floor(x / 2); - y = Math.floor(y / 2); + x >>= 1; + y >>= 1; tileRange = new ol.TileRange(x, y, x, y); if (callback.call(opt_obj, z, tileRange)) { break; From 9ef5f0b060bf233bb2e10915103401a3098b750f Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Sat, 19 Jan 2013 21:58:01 +0100 Subject: [PATCH 20/60] Ensure dropped tiles are properly removed from tile queue --- src/ol/tilequeue.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ol/tilequeue.js b/src/ol/tilequeue.js index baf61b1e06..c1f3554f14 100644 --- a/src/ol/tilequeue.js +++ b/src/ol/tilequeue.js @@ -245,14 +245,17 @@ ol.TileQueue.prototype.siftDown_ = function(startIndex, index) { */ ol.TileQueue.prototype.reprioritize = function() { var heap = this.heap_; - var i, n = 0, node, priority, tile, tileCenter, tileSourceKey; + var i, n = 0, node, priority, tile, tileCenter, tileKey, tileSourceKey; for (i = 0; i < heap.length; ++i) { node = heap[i]; tile = /** @type {ol.Tile} */ (node[1]); tileSourceKey = /** @type {string} */ (node[2]); tileCenter = /** @type {ol.Coordinate} */ (node[3]); priority = this.tilePriorityFunction_(tile, tileSourceKey, tileCenter); - if (priority != ol.TileQueue.DROP) { + if (priority == ol.TileQueue.DROP) { + tileKey = tile.getKey(); + delete this.queuedTileKeys_[tileKey]; + } else { node[0] = priority; heap[n++] = node; } From 7f38c1683ed16c0adfb8475ab488bcee0d228178 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Sun, 20 Jan 2013 15:15:13 +0100 Subject: [PATCH 21/60] Remove unused TileQueue clear method --- src/ol/tilequeue.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/ol/tilequeue.js b/src/ol/tilequeue.js index c1f3554f14..64984ba1a8 100644 --- a/src/ol/tilequeue.js +++ b/src/ol/tilequeue.js @@ -72,15 +72,6 @@ ol.TileQueue = function(tilePriorityFunction) { ol.TileQueue.DROP = Infinity; -/** - * FIXME empty description for jsdoc - */ -ol.TileQueue.prototype.clear = function() { - this.heap_.length = 0; - this.queuedTileKeys_ = {}; -}; - - /** * Remove and return the highest-priority tile. O(logn). * @private From 8825251c42d06326cb83aab4a9320df769babb19 Mon Sep 17 00:00:00 2001 From: Frederic Junod Date: Thu, 17 Jan 2013 17:03:53 +0100 Subject: [PATCH 22/60] Add new ol.easing.elastic and ol.easing.bounce easing functions. --- examples/side-by-side.js | 6 ++++-- src/ol/easing.js | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/examples/side-by-side.js b/examples/side-by-side.js index 1c32f475e5..411c0d0df3 100644 --- a/examples/side-by-side.js +++ b/examples/side-by-side.js @@ -113,7 +113,8 @@ keyboardInteraction.addCallback('j', function() { canvasMap.addPreRenderFunction(bounce); }); keyboardInteraction.addCallback('l', function() { - var panFrom = ol.animation.createPanFrom(view.getCenter()); + var panFrom = ol.animation.createPanFrom( + view.getCenter(), undefined, undefined, ol.easing.elastic); domMap.addPreRenderFunction(panFrom); webglMap.addPreRenderFunction(panFrom); canvasMap.addPreRenderFunction(panFrom); @@ -133,7 +134,8 @@ keyboardInteraction.addCallback('L', function() { view.setCenter(LONDON); }); keyboardInteraction.addCallback('m', function() { - var panFrom = ol.animation.createPanFrom(view.getCenter(), 1000); + var panFrom = ol.animation.createPanFrom( + view.getCenter(), 1000, undefined, ol.easing.bounce); domMap.addPreRenderFunction(panFrom); webglMap.addPreRenderFunction(panFrom); canvasMap.addPreRenderFunction(panFrom); diff --git a/src/ol/easing.js b/src/ol/easing.js index 7700c7fee6..944279cfcc 100644 --- a/src/ol/easing.js +++ b/src/ol/easing.js @@ -21,3 +21,40 @@ ol.easing.upAndDown = function(t) { return 1 - goog.fx.easing.inAndOut(2 * (t - 0.5)); } }; + + +/** + * from https://raw.github.com/DmitryBaranovskiy/raphael/master/raphael.js + * @param {number} t Input between 0 and 1. + * @return {number} Output between 0 and 1. + */ +ol.easing.elastic = function(t) { + return Math.pow(2, -10 * t) * Math.sin((t - 0.075) * (2 * Math.PI) / 0.3) + 1; +}; + + +/** + * from https://raw.github.com/DmitryBaranovskiy/raphael/master/raphael.js + * @param {number} t Input between 0 and 1. + * @return {number} Output between 0 and 1. + */ +ol.easing.bounce = function(t) { + var s = 7.5625, p = 2.75, l; + if (t < (1 / p)) { + l = s * t * t; + } else { + if (t < (2 / p)) { + t -= (1.5 / p); + l = s * t * t + 0.75; + } else { + if (t < (2.5 / p)) { + t -= (2.25 / p); + l = s * t * t + 0.9375; + } else { + t -= (2.625 / p); + l = s * t * t + 0.984375; + } + } + } + return l; +}; From c6a8e5ef7202777bdbb2baa8e07f9757906732b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Mon, 21 Jan 2013 12:29:31 +0100 Subject: [PATCH 23/60] Remove unused Jasmine matchers --- test/jasmine-extensions.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/test/jasmine-extensions.js b/test/jasmine-extensions.js index 46e24cb7ae..4649130908 100644 --- a/test/jasmine-extensions.js +++ b/test/jasmine-extensions.js @@ -4,12 +4,6 @@ beforeEach(function() { toBeA: function(type) { return this.actual instanceof type; }, - toBeGreaterThanOrEqualTo: function(other) { - return this.actual >= other; - }, - toBeLessThanOrEqualTo: function(other) { - return this.actual <= other; - }, toRoughlyEqual: function(other, tol) { return Math.abs(this.actual - other) <= tol; } From 386bb636bb872d5a000b5df60dbaf84efe3a902f Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Sun, 20 Jan 2013 22:25:28 +0100 Subject: [PATCH 24/60] Add missing dependencies on goog.array --- src/ol/control/mousepositioncontrol.js | 1 + src/ol/mapbrowserevent.js | 1 + src/ol/overlay/overlay.js | 1 + src/ol/renderer/canvas/canvasmaprenderer.js | 1 + src/ol/renderer/canvas/canvastilelayerrenderer.js | 1 + src/ol/renderer/dom/dommaprenderer.js | 1 + src/ol/renderer/maprenderer.js | 1 + src/ol/renderer/webgl/webglmaprenderer.js | 1 + src/ol/source/bingmapssource.js | 1 + src/ol/source/tiledwmssource.js | 1 + src/ol/tileurlfunction.js | 1 + 11 files changed, 11 insertions(+) diff --git a/src/ol/control/mousepositioncontrol.js b/src/ol/control/mousepositioncontrol.js index ec45c3cabf..a20c25a1cd 100644 --- a/src/ol/control/mousepositioncontrol.js +++ b/src/ol/control/mousepositioncontrol.js @@ -3,6 +3,7 @@ goog.provide('ol.control.MousePosition'); +goog.require('goog.array'); goog.require('goog.dom'); goog.require('goog.events'); goog.require('goog.events.EventType'); diff --git a/src/ol/mapbrowserevent.js b/src/ol/mapbrowserevent.js index c39f838928..17defe5f20 100644 --- a/src/ol/mapbrowserevent.js +++ b/src/ol/mapbrowserevent.js @@ -2,6 +2,7 @@ goog.provide('ol.MapBrowserEvent'); goog.provide('ol.MapBrowserEvent.EventType'); goog.provide('ol.MapBrowserEventHandler'); +goog.require('goog.array'); goog.require('goog.asserts'); goog.require('goog.events.BrowserEvent'); goog.require('goog.events.EventTarget'); diff --git a/src/ol/overlay/overlay.js b/src/ol/overlay/overlay.js index 1c96bb3d5c..8c878fb3e2 100644 --- a/src/ol/overlay/overlay.js +++ b/src/ol/overlay/overlay.js @@ -1,6 +1,7 @@ goog.provide('ol.overlay.Overlay'); goog.provide('ol.overlay.OverlayPositioning'); +goog.require('goog.array'); goog.require('goog.events'); goog.require('goog.style'); diff --git a/src/ol/renderer/canvas/canvasmaprenderer.js b/src/ol/renderer/canvas/canvasmaprenderer.js index b664dc977a..37cc2f483f 100644 --- a/src/ol/renderer/canvas/canvasmaprenderer.js +++ b/src/ol/renderer/canvas/canvasmaprenderer.js @@ -2,6 +2,7 @@ goog.provide('ol.renderer.canvas.Map'); +goog.require('goog.array'); goog.require('goog.dom'); goog.require('goog.style'); goog.require('goog.vec.Mat4'); diff --git a/src/ol/renderer/canvas/canvastilelayerrenderer.js b/src/ol/renderer/canvas/canvastilelayerrenderer.js index c79251e6f9..6cdcb478db 100644 --- a/src/ol/renderer/canvas/canvastilelayerrenderer.js +++ b/src/ol/renderer/canvas/canvastilelayerrenderer.js @@ -4,6 +4,7 @@ goog.provide('ol.renderer.canvas.TileLayer'); +goog.require('goog.array'); goog.require('goog.dom'); goog.require('goog.style'); goog.require('goog.vec.Mat4'); diff --git a/src/ol/renderer/dom/dommaprenderer.js b/src/ol/renderer/dom/dommaprenderer.js index 79853a8d6c..6f9724a746 100644 --- a/src/ol/renderer/dom/dommaprenderer.js +++ b/src/ol/renderer/dom/dommaprenderer.js @@ -1,5 +1,6 @@ goog.provide('ol.renderer.dom.Map'); +goog.require('goog.array'); goog.require('goog.asserts'); goog.require('goog.dom'); goog.require('goog.dom.TagName'); diff --git a/src/ol/renderer/maprenderer.js b/src/ol/renderer/maprenderer.js index 14b3db6d9f..1e185e0db2 100644 --- a/src/ol/renderer/maprenderer.js +++ b/src/ol/renderer/maprenderer.js @@ -1,6 +1,7 @@ goog.provide('ol.renderer.Map'); goog.require('goog.Disposable'); +goog.require('goog.array'); goog.require('goog.asserts'); goog.require('goog.events'); goog.require('goog.functions'); diff --git a/src/ol/renderer/webgl/webglmaprenderer.js b/src/ol/renderer/webgl/webglmaprenderer.js index c334e59b80..89f7f032c6 100644 --- a/src/ol/renderer/webgl/webglmaprenderer.js +++ b/src/ol/renderer/webgl/webglmaprenderer.js @@ -5,6 +5,7 @@ goog.provide('ol.renderer.webgl.Map'); goog.provide('ol.renderer.webgl.map.shader'); +goog.require('goog.array'); goog.require('goog.debug.Logger'); goog.require('goog.dispose'); goog.require('goog.dom'); diff --git a/src/ol/source/bingmapssource.js b/src/ol/source/bingmapssource.js index a700ba09d2..ed89f2363c 100644 --- a/src/ol/source/bingmapssource.js +++ b/src/ol/source/bingmapssource.js @@ -2,6 +2,7 @@ goog.provide('ol.BingMapsStyle'); goog.provide('ol.source.BingMaps'); goog.require('goog.Uri'); +goog.require('goog.array'); goog.require('goog.events'); goog.require('goog.events.EventType'); goog.require('goog.net.Jsonp'); diff --git a/src/ol/source/tiledwmssource.js b/src/ol/source/tiledwmssource.js index 40b0c699ff..41c5024327 100644 --- a/src/ol/source/tiledwmssource.js +++ b/src/ol/source/tiledwmssource.js @@ -3,6 +3,7 @@ goog.provide('ol.source.TiledWMS'); +goog.require('goog.array'); goog.require('goog.asserts'); goog.require('goog.object'); goog.require('goog.uri.utils'); diff --git a/src/ol/tileurlfunction.js b/src/ol/tileurlfunction.js index ce63f6c4cb..ce7168fb24 100644 --- a/src/ol/tileurlfunction.js +++ b/src/ol/tileurlfunction.js @@ -1,6 +1,7 @@ goog.provide('ol.TileUrlFunction'); goog.provide('ol.TileUrlFunctionType'); +goog.require('goog.array'); goog.require('goog.math'); goog.require('goog.uri.utils'); goog.require('ol.TileCoord'); From 7229428741ba069d45acb10818f5fff257ec2f51 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Sun, 20 Jan 2013 21:55:08 +0100 Subject: [PATCH 25/60] Use in-place filtering for post-render functions --- src/ol/map.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/ol/map.js b/src/ol/map.js index 6ab92eb8b8..eeac9aee97 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -530,13 +530,12 @@ ol.Map.prototype.handleMapBrowserEvent = function(mapBrowserEvent) { ol.Map.prototype.handlePostRender = function() { this.tileQueue_.reprioritize(); // FIXME only call if needed this.tileQueue_.loadMoreTiles(); - goog.array.forEach( - this.postRenderFunctions_, - function(postRenderFunction) { - postRenderFunction(this, this.frameState_); - }, - this); - this.postRenderFunctions_.length = 0; + var postRenderFunctions = this.postRenderFunctions_; + var i; + for (i = 0; i < postRenderFunctions.length; ++i) { + postRenderFunctions[i](this, this.frameState_); + } + postRenderFunctions.length = 0; }; From d76db2005fe80dde9c064534c571ffb2824b8c24 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Sun, 20 Jan 2013 21:56:08 +0100 Subject: [PATCH 26/60] Use for loop for iterating over layers --- src/ol/map.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ol/map.js b/src/ol/map.js index eeac9aee97..e143d0cf7b 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -615,9 +615,11 @@ ol.Map.prototype.renderFrame_ = function(time) { var backgroundColor = this.getBackgroundColor(); var viewHints = view.getHints(); var layerStates = {}; - goog.array.forEach(layersArray, function(layer) { + var layer; + for (i = 0; i < layersArray.length; ++i) { + layer = layersArray[i]; layerStates[goog.getUid(layer)] = layer.getLayerState(); - }); + } var view2DState = view2D.getView2DState(); frameState = { animate: false, From 3967e173668885b685838965ce347686a9120a21 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Sun, 20 Jan 2013 21:56:27 +0100 Subject: [PATCH 27/60] Use in-place filtering for pre-render functions --- src/ol/map.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/ol/map.js b/src/ol/map.js index e143d0cf7b..dcf7cc0f4d 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -641,12 +641,15 @@ ol.Map.prototype.renderFrame_ = function(time) { }; } - this.preRenderFunctions_ = goog.array.filter( - this.preRenderFunctions_, - function(preRenderFunction) { - return preRenderFunction(this, frameState); - }, - this); + var preRenderFunctions = this.preRenderFunctions_; + var n = 0, preRenderFunction; + for (i = 0; i < preRenderFunctions.length; ++i) { + preRenderFunction = preRenderFunctions[i]; + if (preRenderFunction(this, frameState)) { + preRenderFunctions[n++] = preRenderFunction; + } + } + preRenderFunctions.length = n; if (!goog.isNull(frameState)) { // FIXME works for View2D only From f1b11866fde8ccd3a4945e426f3b790e2e681e01 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Sun, 20 Jan 2013 21:56:48 +0100 Subject: [PATCH 28/60] Remove dependency on goog.array --- src/ol/map.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ol/map.js b/src/ol/map.js index dcf7cc0f4d..a4ae6c28bc 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -8,7 +8,6 @@ goog.provide('ol.RendererHint'); goog.provide('ol.RendererHints'); goog.require('goog.Uri.QueryData'); -goog.require('goog.array'); goog.require('goog.async.AnimationDelay'); goog.require('goog.debug.Logger'); goog.require('goog.dispose'); From c836c8d9c4b32cdb2ff18d211414331e2bed5cc7 Mon Sep 17 00:00:00 2001 From: Frederic Junod Date: Mon, 21 Jan 2013 15:08:15 +0100 Subject: [PATCH 29/60] Add ol.Map.getRenderer function --- src/ol/map.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/ol/map.js b/src/ol/map.js index 6ab92eb8b8..8c222f40df 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -345,6 +345,14 @@ goog.exportProperty( ol.Map.prototype.getBackgroundColor); +/** + * @return {ol.renderer.Map} Renderer. + */ +ol.Map.prototype.getRenderer = function() { + return this.renderer_; +}; + + /** * @return {Element} Container. */ From 2f2ea373ca0624c978223b17f04c434f02ab79a3 Mon Sep 17 00:00:00 2001 From: Frederic Junod Date: Mon, 21 Jan 2013 15:18:18 +0100 Subject: [PATCH 30/60] Add ol.renderer.Map.getCanvas function. Only valid with the canvas and webgl renderers --- src/ol/renderer/canvas/canvasmaprenderer.js | 8 ++++++++ src/ol/renderer/maprenderer.js | 6 ++++++ src/ol/renderer/webgl/webglmaprenderer.js | 8 ++++++++ 3 files changed, 22 insertions(+) diff --git a/src/ol/renderer/canvas/canvasmaprenderer.js b/src/ol/renderer/canvas/canvasmaprenderer.js index b664dc977a..4b253c41d4 100644 --- a/src/ol/renderer/canvas/canvasmaprenderer.js +++ b/src/ol/renderer/canvas/canvasmaprenderer.js @@ -67,6 +67,14 @@ ol.renderer.canvas.Map.prototype.createLayerRenderer = function(layer) { }; +/** + * @inheritDoc + */ +ol.renderer.canvas.Map.prototype.getCanvas = function() { + return this.canvas_; +}; + + /** * @inheritDoc */ diff --git a/src/ol/renderer/maprenderer.js b/src/ol/renderer/maprenderer.js index 14b3db6d9f..0e8f876bb9 100644 --- a/src/ol/renderer/maprenderer.js +++ b/src/ol/renderer/maprenderer.js @@ -148,6 +148,12 @@ ol.renderer.Map.prototype.disposeInternal = function() { }; +/** + * @return {Element} Canvas. + */ +ol.renderer.Map.prototype.getCanvas = goog.functions.NULL; + + /** * @param {ol.layer.Layer} layer Layer. * @protected diff --git a/src/ol/renderer/webgl/webglmaprenderer.js b/src/ol/renderer/webgl/webglmaprenderer.js index c334e59b80..211c6bb25c 100644 --- a/src/ol/renderer/webgl/webglmaprenderer.js +++ b/src/ol/renderer/webgl/webglmaprenderer.js @@ -294,6 +294,14 @@ ol.renderer.webgl.Map.prototype.disposeInternal = function() { }; +/** + * @inheritDoc + */ +ol.renderer.webgl.Map.prototype.getCanvas = function() { + return this.canvas_; +}; + + /** * @return {WebGLRenderingContext} GL. */ From c0ef84fd5dafd332a2ce35803a6b05e13cedf3b3 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Mon, 21 Jan 2013 16:07:06 +0100 Subject: [PATCH 31/60] Remove stale dependencies --- src/ol/renderer/maprenderer.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ol/renderer/maprenderer.js b/src/ol/renderer/maprenderer.js index 14b3db6d9f..9553523759 100644 --- a/src/ol/renderer/maprenderer.js +++ b/src/ol/renderer/maprenderer.js @@ -4,8 +4,6 @@ goog.require('goog.Disposable'); goog.require('goog.asserts'); goog.require('goog.events'); goog.require('goog.functions'); -goog.require('goog.fx.anim'); -goog.require('goog.fx.anim.Animated'); goog.require('goog.vec.Mat4'); goog.require('ol.FrameState'); goog.require('ol.View2D'); From 8cbe0fbbbb4ed9c21e6c87ccd9ad1325e75ec6e0 Mon Sep 17 00:00:00 2001 From: Frederic Junod Date: Mon, 21 Jan 2013 15:24:45 +0100 Subject: [PATCH 32/60] Export canvas map as jpeg image --- examples/two-layers.html | 2 +- examples/two-layers.js | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/two-layers.html b/examples/two-layers.html index 4d7ac617ce..3e23234af5 100644 --- a/examples/two-layers.html +++ b/examples/two-layers.html @@ -21,7 +21,7 @@ DOM WebGL - Canvas + Canvas export map as png
diff --git a/examples/two-layers.js b/examples/two-layers.js index 4817a59a15..9d8b66ec71 100644 --- a/examples/two-layers.js +++ b/examples/two-layers.js @@ -48,3 +48,7 @@ var canvasMap = new ol.Map({ }); canvasMap.bindTo('layers', webglMap); canvasMap.bindTo('view', webglMap); + +goog.events.listen(goog.dom.getElement('canvas-export'), 'click', function(e) { + e.target.href = canvasMap.getRenderer().getCanvas().toDataURL(); +}); From be644087285d4ed9f4e383498419cc141121e8d4 Mon Sep 17 00:00:00 2001 From: Frederic Junod Date: Mon, 21 Jan 2013 16:13:19 +0100 Subject: [PATCH 33/60] Export map as jpeg instead of png --- examples/two-layers.html | 2 +- examples/two-layers.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/two-layers.html b/examples/two-layers.html index 3e23234af5..2ef70164e2 100644 --- a/examples/two-layers.html +++ b/examples/two-layers.html @@ -21,7 +21,7 @@ DOM WebGL - Canvas export map as png + Canvas export map as jpeg
diff --git a/examples/two-layers.js b/examples/two-layers.js index 9d8b66ec71..884f8db98f 100644 --- a/examples/two-layers.js +++ b/examples/two-layers.js @@ -50,5 +50,5 @@ canvasMap.bindTo('layers', webglMap); canvasMap.bindTo('view', webglMap); goog.events.listen(goog.dom.getElement('canvas-export'), 'click', function(e) { - e.target.href = canvasMap.getRenderer().getCanvas().toDataURL(); + e.target.href = canvasMap.getRenderer().getCanvas().toDataURL('image/jpeg'); }); From 8d56b5f26162960822add4edad5b04ddfc9f5701 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Tue, 22 Jan 2013 08:39:37 +0100 Subject: [PATCH 34/60] Update readme.md --- readme.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index e065102702..ee031aafbc 100644 --- a/readme.md +++ b/readme.md @@ -2,13 +2,18 @@ [![Travis CI Status](https://secure.travis-ci.org/openlayers/ol3.png)](http://travis-ci.org/#!/openlayers/ol3) -## Build it -Run make: +## Hosted Examples + +The examples are hosted on GitHub (as GitHub pages). See http://openlayers.github.com/ol3/master/examples/. + +## Build OpenLayers 3 + +Run build.py: $ ./build.py -## Run the examples in debug mode +## Run examples locally Run the [Plovr](http://plovr.com/) web server with: From 8d979127fc455913535045b759adea7d981f8b99 Mon Sep 17 00:00:00 2001 From: Marc Jansen Date: Tue, 22 Jan 2013 12:01:44 +0100 Subject: [PATCH 35/60] Use goog.now() instead of Date.now(). Date.now() is not supported in Internet Explorer up to IE 9. The goog.now() method can be used as a replacement. --- src/ol/animation.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ol/animation.js b/src/ol/animation.js index 4049186bb8..73dfebbe57 100644 --- a/src/ol/animation.js +++ b/src/ol/animation.js @@ -17,7 +17,7 @@ goog.require('ol.easing'); */ ol.animation.createBounce = function(resolution, opt_duration, opt_start, opt_easingFunction) { - var start = goog.isDef(opt_start) ? opt_start : Date.now(); + var start = goog.isDef(opt_start) ? opt_start : goog.now(); var duration = goog.isDef(opt_duration) ? opt_duration : 1000; var easingFunction = goog.isDef(opt_easingFunction) ? opt_easingFunction : ol.easing.upAndDown; @@ -49,7 +49,7 @@ ol.animation.createBounce = */ ol.animation.createPanFrom = function(source, opt_duration, opt_start, opt_easingFunction) { - var start = goog.isDef(opt_start) ? opt_start : Date.now(); + var start = goog.isDef(opt_start) ? opt_start : goog.now(); var sourceX = source.x; var sourceY = source.y; var duration = goog.isDef(opt_duration) ? opt_duration : 1000; @@ -85,7 +85,7 @@ ol.animation.createPanFrom = */ ol.animation.createSpin = function(opt_duration, opt_turns, opt_start, opt_easingFunction) { - var start = goog.isDef(opt_start) ? opt_start : Date.now(); + var start = goog.isDef(opt_start) ? opt_start : goog.now(); var duration = goog.isDef(opt_duration) ? opt_duration : 1000; var turns = goog.isDef(opt_turns) ? opt_turns : 1; var deltaTheta = 2 * turns * Math.PI; @@ -118,7 +118,7 @@ ol.animation.createSpin = */ ol.animation.createZoomFrom = function(sourceResolution, opt_duration, opt_start, opt_easingFunction) { - var start = goog.isDef(opt_start) ? opt_start : Date.now(); + var start = goog.isDef(opt_start) ? opt_start : goog.now(); var duration = goog.isDef(opt_duration) ? opt_duration : 1000; var easingFunction = goog.isDef(opt_easingFunction) ? opt_easingFunction : ol.easing.linear; From f2164e5028e164b86926ae4d911f5bf94da3f834 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Tue, 22 Jan 2013 13:06:53 +0100 Subject: [PATCH 36/60] Export map.getLayers --- src/ol/map.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ol/map.js b/src/ol/map.js index 8c222f40df..d8a9952ad4 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -399,6 +399,10 @@ ol.Map.prototype.getInteractions = function() { ol.Map.prototype.getLayers = function() { return /** @type {ol.Collection} */ (this.get(ol.MapProperty.LAYERS)); }; +goog.exportProperty( + ol.Map.prototype, + 'getLayers', + ol.Map.prototype.getLayers); /** From dda51ecec27bc57fc14ec2f6446ad0033f302fc1 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Tue, 22 Jan 2013 14:37:13 +0100 Subject: [PATCH 37/60] Replace ol.overlay.Overlay with ol.AnchoredElement This commit adds several features: - rename of class to better reflect its behaviour - full ol.Object support - integration with postrender event for integration with animations and render loop - minimised DOM modifications - more precise positioning - element placed in DIV to avoid modifications to user-supplied element - correctly handle changes to the positioning property --- ...-and-popup.html => anchored-elements.html} | 12 +- ...rlay-and-popup.js => anchored-elements.js} | 10 +- src/objectliterals.exports | 10 +- src/ol/anchoredelement.exports | 7 + src/ol/anchoredelement.js | 318 ++++++++++++++++++ src/ol/overlay/overlay.exports | 5 - src/ol/overlay/overlay.js | 211 ------------ 7 files changed, 341 insertions(+), 232 deletions(-) rename examples/{overlay-and-popup.html => anchored-elements.html} (86%) rename examples/{overlay-and-popup.js => anchored-elements.js} (86%) create mode 100644 src/ol/anchoredelement.exports create mode 100644 src/ol/anchoredelement.js delete mode 100644 src/ol/overlay/overlay.exports delete mode 100644 src/ol/overlay/overlay.js diff --git a/examples/overlay-and-popup.html b/examples/anchored-elements.html similarity index 86% rename from examples/overlay-and-popup.html rename to examples/anchored-elements.html index 067adbf8b4..5d7b25b747 100644 --- a/examples/overlay-and-popup.html +++ b/examples/anchored-elements.html @@ -80,7 +80,7 @@ margin-left: -13px; } - Overlay example + Anchored elements example
@@ -89,16 +89,16 @@
-

Overlay example

-
Demonstrates Overlays.
+

Anchored elements example

+
Demonstrates anchored elements.

See the - overlay-and-popup.js source + anchored-elements.js source to see how this is done.

-
overlay, popup, mapquest, openaerial
- +
anchored elements, overlay, popup, mapquest, openaerial
+ diff --git a/examples/overlay-and-popup.js b/examples/anchored-elements.js similarity index 86% rename from examples/overlay-and-popup.js rename to examples/anchored-elements.js index 1e19bb425b..80fac6d6bf 100644 --- a/examples/overlay-and-popup.js +++ b/examples/anchored-elements.js @@ -1,12 +1,12 @@ goog.require('goog.debug.Console'); goog.require('goog.debug.Logger'); goog.require('goog.debug.Logger.Level'); +goog.require('ol.AnchoredElement'); goog.require('ol.Collection'); goog.require('ol.Coordinate'); goog.require('ol.Map'); goog.require('ol.RendererHints'); goog.require('ol.View2D'); -goog.require('ol.overlay.Overlay'); goog.require('ol.source.MapQuestOpenAerial'); @@ -31,15 +31,15 @@ var map = new ol.Map({ }); // Vienna label -var vienna = new ol.overlay.Overlay({ +var vienna = new ol.AnchoredElement({ map: map, - coordinate: ol.Projection.transformWithCodes( + position: ol.Projection.transformWithCodes( new ol.Coordinate(16.3725, 48.208889), 'EPSG:4326', 'EPSG:3857'), element: document.getElementById('vienna') }); // Popup showing the position the user clicked -var popup = new ol.overlay.Overlay({ +var popup = new ol.AnchoredElement({ map: map, element: document.getElementById('popup') }); @@ -49,5 +49,5 @@ map.addEventListener('click', function(evt) { 'Welcome to ol3. The location you clicked was
' + ol.Coordinate.toStringHDMS(ol.Projection.transformWithCodes( coordinate, 'EPSG:3857', 'EPSG:4326')); - popup.setCoordinate(coordinate); + popup.setPosition(coordinate); }); diff --git a/src/objectliterals.exports b/src/objectliterals.exports index f22ef20690..4c16ae6b90 100644 --- a/src/objectliterals.exports +++ b/src/objectliterals.exports @@ -51,11 +51,11 @@ @exportObjectLiteralProperty ol.layer.LayerOptions.source ol.source.Source @exportObjectLiteralProperty ol.layer.LayerOptions.visible boolean|undefined -@exportObjectLiteral ol.overlay.OverlayOptions -@exportObjectLiteralProperty ol.overlay.OverlayOptions.coordinate ol.Coordinate|undefined -@exportObjectLiteralProperty ol.overlay.OverlayOptions.element Element|undefined -@exportObjectLiteralProperty ol.overlay.OverlayOptions.map ol.Map|undefined -@exportObjectLiteralProperty ol.overlay.OverlayOptions.positioning Array.|undefined +@exportObjectLiteral ol.AnchoredElementOptions +@exportObjectLiteralProperty ol.AnchoredElementOptions.element Element|undefined +@exportObjectLiteralProperty ol.AnchoredElementOptions.map ol.Map|undefined +@exportObjectLiteralProperty ol.AnchoredElementOptions.position ol.Coordinate|undefined +@exportObjectLiteralProperty ol.AnchoredElementOptions.positioning ol.AnchoredElementPositioning|undefined @exportObjectLiteral ol.source.BingMapsOptions @exportObjectLiteralProperty ol.source.BingMapsOptions.culture string|undefined diff --git a/src/ol/anchoredelement.exports b/src/ol/anchoredelement.exports new file mode 100644 index 0000000000..29fbb3bfc8 --- /dev/null +++ b/src/ol/anchoredelement.exports @@ -0,0 +1,7 @@ +@exportClass ol.AnchoredElement ol.AnchoredElementOptions + +@exportSymbol ol.AnchoredElementPositioning +@exportProperty ol.AnchoredElementPositioning.BOTTOM_LEFT +@exportProperty ol.AnchoredElementPositioning.BOTTOM_RIGHT +@exportProperty ol.AnchoredElementPositioning.TOP_LEFT +@exportProperty ol.AnchoredElementPositioning.TOP_RIGHT diff --git a/src/ol/anchoredelement.js b/src/ol/anchoredelement.js new file mode 100644 index 0000000000..14c60cd0fe --- /dev/null +++ b/src/ol/anchoredelement.js @@ -0,0 +1,318 @@ +goog.provide('ol.AnchoredElement'); +goog.provide('ol.AnchoredElementPositioning'); +goog.provide('ol.AnchoredElementProperty'); + +goog.require('goog.dom'); +goog.require('goog.events'); +goog.require('goog.style'); +goog.require('ol.Coordinate'); +goog.require('ol.Map'); +goog.require('ol.MapEventType'); +goog.require('ol.Object'); + + +/** + * @enum {string} + */ +ol.AnchoredElementProperty = { + ELEMENT: 'element', + MAP: 'map', + POSITION: 'position', + POSITIONING: 'positioning' +}; + + +/** + * @enum {string} + */ +ol.AnchoredElementPositioning = { + BOTTOM_LEFT: 'bottom-left', + BOTTOM_RIGHT: 'bottom-right', + TOP_LEFT: 'top-left', + TOP_RIGHT: 'top-right' +}; + + + +/** + * @constructor + * @extends {ol.Object} + * @param {ol.AnchoredElementOptions} anchoredElementOptions Anchored element + * options. + */ +ol.AnchoredElement = function(anchoredElementOptions) { + + goog.base(this); + + /** + * @private + * @type {Element} + */ + this.element_ = goog.dom.createElement(goog.dom.TagName.DIV); + this.element_.style.position = 'absolute'; + + /** + * @private + * @type {{bottom_: string, + * left_: string, + * right_: string, + * top_: string, + * visible: boolean}} + */ + this.rendered_ = { + bottom_: '', + left_: '', + right_: '', + top_: '', + visible: true + }; + + goog.events.listen( + this, ol.Object.getChangedEventType(ol.AnchoredElementProperty.ELEMENT), + this.handleElementChanged, false, this); + + goog.events.listen( + this, ol.Object.getChangedEventType(ol.AnchoredElementProperty.MAP), + this.handleMapChanged, false, this); + + goog.events.listen( + this, ol.Object.getChangedEventType(ol.AnchoredElementProperty.POSITION), + this.handlePositionChanged, false, this); + + goog.events.listen( + this, + ol.Object.getChangedEventType(ol.AnchoredElementProperty.POSITIONING), + this.handlePositioningChanged, false, this); + + if (goog.isDef(anchoredElementOptions.element)) { + this.setElement(anchoredElementOptions.element); + } + if (goog.isDef(anchoredElementOptions.position)) { + this.setPosition(anchoredElementOptions.position); + } + if (goog.isDef(anchoredElementOptions.positioning)) { + this.setPositioning(anchoredElementOptions.positioning); + } + if (goog.isDef(anchoredElementOptions.map)) { + this.setMap(anchoredElementOptions.map); + } + +}; +goog.inherits(ol.AnchoredElement, ol.Object); + + +/** + * @return {Element|undefined} Element. + */ +ol.AnchoredElement.prototype.getElement = function() { + return /** @type {Element|undefined} */ ( + this.get(ol.AnchoredElementProperty.ELEMENT)); +}; +goog.exportProperty( + ol.AnchoredElement.prototype, + 'getElement', + ol.AnchoredElement.prototype.getElement); + + +/** + * @return {ol.Map|undefined} Map. + */ +ol.AnchoredElement.prototype.getMap = function() { + return /** @type {ol.Map|undefined} */ ( + this.get(ol.AnchoredElementProperty.MAP)); +}; +goog.exportProperty( + ol.AnchoredElement.prototype, + 'getMap', + ol.AnchoredElement.prototype.getMap); + + +/** + * @return {ol.Coordinate|undefined} Position. + */ +ol.AnchoredElement.prototype.getPosition = function() { + return /** @type {ol.Coordinate|undefined} */ ( + this.get(ol.AnchoredElementProperty.POSITION)); +}; +goog.exportProperty( + ol.AnchoredElement.prototype, + 'getPosition', + ol.AnchoredElement.prototype.getPosition); + + +/** + * @return {ol.AnchoredElementPositioning|undefined} Positioning. + */ +ol.AnchoredElement.prototype.getPositioning = function() { + return /** @type {ol.AnchoredElementPositioning|undefined} */ ( + this.get(ol.AnchoredElementProperty.POSITIONING)); +}; +goog.exportProperty( + ol.AnchoredElement.prototype, + 'getPositioning', + ol.AnchoredElement.prototype.getPositioning); + + +/** + * @protected + */ +ol.AnchoredElement.prototype.handleElementChanged = function() { + goog.dom.removeChildren(this.element_); + var element = this.getElement(); + if (goog.isDefAndNotNull(element)) { + goog.dom.append(/** @type {!Node} */ (this.element_), element); + } +}; + + +/** + * @protected + */ +ol.AnchoredElement.prototype.handleMapChanged = function() { + if (!goog.isNull(this.mapPostrenderListenerKey_)) { + goog.dom.removeNode(this.element_); + goog.events.unlistenByKey(this.mapPostrenderListenerKey_); + this.mapPostrenderListenerKey_ = null; + } + var map = this.getMap(); + if (goog.isDefAndNotNull(map)) { + this.mapPostrenderListenerKey_ = goog.events.listen(map, + ol.MapEventType.POSTRENDER, this.handleMapPostrender, false, this); + this.updatePixelPosition_(); + goog.dom.append( + /** @type {!Node} */ (map.getOverlayContainer()), this.element_); + } +}; + + +/** + * @protected + */ +ol.AnchoredElement.prototype.handleMapPostrender = function() { + this.updatePixelPosition_(); +}; + + +/** + * @protected + */ +ol.AnchoredElement.prototype.handlePositionChanged = function() { + this.updatePixelPosition_(); +}; + + +/** + * @protected + */ +ol.AnchoredElement.prototype.handlePositioningChanged = function() { + this.updatePixelPosition_(); +}; + + +/** + * @param {Element|undefined} element Element. + */ +ol.AnchoredElement.prototype.setElement = function(element) { + this.set(ol.AnchoredElementProperty.ELEMENT, element); +}; +goog.exportProperty( + ol.AnchoredElement.prototype, + 'setElement', + ol.AnchoredElement.prototype.setElement); + + +/** + * @param {ol.Map|undefined} map Map. + */ +ol.AnchoredElement.prototype.setMap = function(map) { + this.set(ol.AnchoredElementProperty.MAP, map); +}; +goog.exportProperty( + ol.AnchoredElement.prototype, + 'setMap', + ol.AnchoredElement.prototype.setMap); + + +/** + * @param {ol.Coordinate|undefined} position Position. + */ +ol.AnchoredElement.prototype.setPosition = function(position) { + this.set(ol.AnchoredElementProperty.POSITION, position); +}; +goog.exportProperty( + ol.AnchoredElement.prototype, + 'setPosition', + ol.AnchoredElement.prototype.setPosition); + + +/** + * @param {ol.AnchoredElementPositioning|undefined} positioning Positioning. + */ +ol.AnchoredElement.prototype.setPositioning = function(positioning) { + this.set(ol.AnchoredElementProperty.POSITIONING, positioning); +}; + + +/** + * @private + */ +ol.AnchoredElement.prototype.updatePixelPosition_ = function() { + + var map = this.getMap(); + var position = this.getPosition(); + if (!goog.isDef(map) || !map.isDef() || !goog.isDef(position)) { + if (this.rendered_.visible) { + goog.style.showElement(this.element_, false); + this.rendered_.visible = false; + } + return; + } + + var pixel = map.getPixelFromCoordinate(position); + var mapSize = map.getSize(); + goog.asserts.assert(goog.isDef(mapSize)); + var style = this.element_.style; + var positioning = this.getPositioning(); + if (positioning == ol.AnchoredElementPositioning.BOTTOM_RIGHT || + positioning == ol.AnchoredElementPositioning.TOP_RIGHT) { + if (this.rendered_.left_ !== '') { + this.rendered_.left_ = style.left = ''; + } + var right = Math.round(mapSize.width - pixel.x) + 'px'; + if (this.rendered_.right_ != right) { + this.rendered_.right_ = style.right = right; + } + } else { + if (this.rendered_.right_ !== '') { + this.rendered_.right_ = style.right = ''; + } + var left = Math.round(pixel.x) + 'px'; + if (this.rendered_.left_ != left) { + this.rendered_.left_ = style.left = left; + } + } + if (positioning == ol.AnchoredElementPositioning.TOP_LEFT || + positioning == ol.AnchoredElementPositioning.TOP_RIGHT) { + if (this.rendered_.bottom_ !== '') { + this.rendered_.bottom_ = style.bottom = ''; + } + var top = Math.round(pixel.y) + 'px'; + if (this.rendered_.top_ != top) { + this.rendered_.top_ = style.top = top; + } + } else { + if (this.rendered_.top_ !== '') { + this.rendered_.top_ = style.top = ''; + } + var bottom = Math.round(mapSize.height - pixel.y) + 'px'; + if (this.rendered_.bottom_ != bottom) { + this.rendered_.bottom_ = style.bottom = bottom; + } + } + + if (!this.rendered_.visible) { + goog.style.showElement(this.element_, true); + this.rendered_.visible = true; + } + +}; diff --git a/src/ol/overlay/overlay.exports b/src/ol/overlay/overlay.exports deleted file mode 100644 index b60b8a16a0..0000000000 --- a/src/ol/overlay/overlay.exports +++ /dev/null @@ -1,5 +0,0 @@ -@exportClass ol.overlay.Overlay ol.overlay.OverlayOptions -@exportProperty ol.overlay.Overlay.prototype.getElement -@exportProperty ol.overlay.Overlay.prototype.setCoordinate -@exportProperty ol.overlay.Overlay.prototype.setMap - diff --git a/src/ol/overlay/overlay.js b/src/ol/overlay/overlay.js deleted file mode 100644 index 8c878fb3e2..0000000000 --- a/src/ol/overlay/overlay.js +++ /dev/null @@ -1,211 +0,0 @@ -goog.provide('ol.overlay.Overlay'); -goog.provide('ol.overlay.OverlayPositioning'); - -goog.require('goog.array'); -goog.require('goog.events'); -goog.require('goog.style'); - - - -/** - * @constructor - * @param {ol.overlay.OverlayOptions} overlayOptions Overlay options. - */ -ol.overlay.Overlay = function(overlayOptions) { - - /** - * @type {ol.Coordinate} - * @private - */ - this.coordinate_ = null; - - /** - * @type {Element} - * @private - */ - this.element_ = null; - - /** - * @private - * @type {ol.Map} - */ - this.map_ = null; - - /** - * @type {Array.} - * @private - */ - this.positioning_ = [ - ol.overlay.OverlayPositioning.LEFT, - ol.overlay.OverlayPositioning.BOTTOM - ]; - - /** - * @private - * @type {Array.} - */ - this.mapListenerKeys_ = null; - - /** - * @private - * @type {Array.} - */ - this.viewListenerKeys_ = null; - - if (goog.isDef(overlayOptions.coordinate)) { - this.setCoordinate(overlayOptions.coordinate); - } - if (goog.isDef(overlayOptions.element)) { - this.setElement(overlayOptions.element); - } - if (goog.isDef(overlayOptions.map)) { - this.setMap(overlayOptions.map); - } - if (goog.isDef(overlayOptions.positioning)) { - this.setPositioning(overlayOptions.positioning); - } -}; - - -/** - * @private - */ -ol.overlay.Overlay.prototype.handleViewChanged_ = function() { - goog.asserts.assert(!goog.isNull(this.map_)); - if (!goog.isNull(this.viewListenerKeys_)) { - goog.array.forEach(this.viewListenerKeys_, goog.events.unlistenByKey); - this.viewListenerKeys_ = null; - } - var view = this.map_.getView(); - if (goog.isDefAndNotNull(view)) { - // FIXME works for View2D only - goog.asserts.assert(view instanceof ol.View2D); - this.viewListenerKeys_ = [ - goog.events.listen( - view, ol.Object.getChangedEventType(ol.View2DProperty.CENTER), - this.updatePixelPosition_, false, this), - - goog.events.listen( - view, ol.Object.getChangedEventType(ol.View2DProperty.RESOLUTION), - this.updatePixelPosition_, false, this), - - goog.events.listen( - view, ol.Object.getChangedEventType(ol.View2DProperty.ROTATION), - this.updatePixelPosition_, false, this) - ]; - this.updatePixelPosition_(); - } -}; - - -/** - * @param {ol.Coordinate} coordinate Coordinate for the overlay's position on - * the map. - */ -ol.overlay.Overlay.prototype.setCoordinate = function(coordinate) { - this.coordinate_ = coordinate; - this.updatePixelPosition_(); -}; - - -/** - * @param {Element} element The DOM element for the overlay. - */ -ol.overlay.Overlay.prototype.setElement = function(element) { - if (this.element_) { - goog.dom.removeNode(this.element_); - } - this.element_ = element; - if (this.map_) { - goog.style.setStyle(this.element_, 'position', 'absolute'); - goog.dom.append(/** @type {!Node} */ (this.map_.getOverlayContainer()), - this.element_); - } - this.updatePixelPosition_(); -}; - - -/** - * @return {Element} The DOM element for the overlay. - */ -ol.overlay.Overlay.prototype.getElement = function() { - return this.element_; -}; - - -/** - * @param {ol.Map} map Map. - */ -ol.overlay.Overlay.prototype.setMap = function(map) { - this.map_ = map; - if (!goog.isNull(this.mapListenerKeys_)) { - goog.array.forEach(this.mapListenerKeys_, goog.events.unlistenByKey); - this.mapListenerKeys_ = null; - } - if (this.element_) { - this.setElement(this.element_); - } - if (goog.isDefAndNotNull(map)) { - this.mapListenerKeys_ = [ - goog.events.listen( - map, ol.Object.getChangedEventType(ol.MapProperty.SIZE), - this.updatePixelPosition_, false, this), - goog.events.listen( - map, ol.Object.getChangedEventType(ol.MapProperty.VIEW), - this.handleViewChanged_, false, this) - ]; - this.handleViewChanged_(); - } -}; - - -/** - * @return {ol.Map} Map. - */ -ol.overlay.Overlay.prototype.getMap = function() { - return this.map_; -}; - - -/** - * Set the CSS properties to use for x- and y-positioning of the element. If - * not set, the default is {@code ['left', 'bottom']}. - * @param {Array.} positioning The positioning. - */ -ol.overlay.Overlay.prototype.setPositioning = function(positioning) { - this.positioning_ = positioning; - this.updatePixelPosition_(); -}; - - -/** - * @private - */ -ol.overlay.Overlay.prototype.updatePixelPosition_ = function() { - if (!goog.isNull(this.map_) && !goog.isNull(this.coordinate_) && - !goog.isNull(this.element_)) { - var pixel = this.map_.getPixelFromCoordinate(this.coordinate_); - var mapSize = this.map_.get(ol.MapProperty.SIZE); - var x = Math.round(pixel.x); - if (this.positioning_[0] === ol.overlay.OverlayPositioning.RIGHT) { - x = mapSize.width - x; - } - var y = Math.round(pixel.y); - if (this.positioning_[1] === ol.overlay.OverlayPositioning.BOTTOM) { - y = mapSize.height - y; - } - goog.style.setStyle(this.element_, this.positioning_[0], x + 'px'); - goog.style.setStyle(this.element_, this.positioning_[1], y + 'px'); - } -}; - - -/** - * @enum {string} - */ -ol.overlay.OverlayPositioning = { - LEFT: 'left', - RIGHT: 'right', - TOP: 'top', - BOTTOM: 'bottom' -}; From 9af78dd2fe38fbc279e7b761e0256e56814ba82d Mon Sep 17 00:00:00 2001 From: Marc Jansen Date: Wed, 23 Jan 2013 08:14:55 +0100 Subject: [PATCH 38/60] Adjust border-radius of zoom buttons. --- css/ol.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/css/ol.css b/css/ol.css index 435276a222..5e679f5ddc 100644 --- a/css/ol.css +++ b/css/ol.css @@ -78,8 +78,8 @@ } } .ol-zoom-in { - border-radius: 4px 4px 0 0; + border-radius: 2px 2px 0 0; } .ol-zoom-out { - border-radius: 0 0 4px 4px; + border-radius: 0 0 2px 2px; } From b770aaa2f5d2b6cfbd6a259a6ed238e6ae9e769b Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Wed, 23 Jan 2013 14:22:25 +0100 Subject: [PATCH 39/60] Add tile cache expiry infrastructure --- .../canvas/canvastilelayerrenderer.js | 1 + src/ol/renderer/dom/domtilelayerrenderer.js | 1 + src/ol/renderer/layerrenderer.js | 19 +++++++++++++++++++ .../renderer/webgl/webgltilelayerrenderer.js | 1 + src/ol/source/tilesource.js | 13 +++++++++++++ 5 files changed, 35 insertions(+) diff --git a/src/ol/renderer/canvas/canvastilelayerrenderer.js b/src/ol/renderer/canvas/canvastilelayerrenderer.js index 6cdcb478db..de1008c364 100644 --- a/src/ol/renderer/canvas/canvastilelayerrenderer.js +++ b/src/ol/renderer/canvas/canvastilelayerrenderer.js @@ -219,6 +219,7 @@ ol.renderer.canvas.TileLayer.prototype.renderFrame = } this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange); + this.scheduleExpireCache(frameState, tileSource); var transform = this.transform_; goog.vec.Mat4.makeIdentity(transform); diff --git a/src/ol/renderer/dom/domtilelayerrenderer.js b/src/ol/renderer/dom/domtilelayerrenderer.js index 9cf215a98e..27b2b6a804 100644 --- a/src/ol/renderer/dom/domtilelayerrenderer.js +++ b/src/ol/renderer/dom/domtilelayerrenderer.js @@ -239,6 +239,7 @@ ol.renderer.dom.TileLayer.prototype.renderFrame = } this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange); + this.scheduleExpireCache(frameState, tileSource); }; diff --git a/src/ol/renderer/layerrenderer.js b/src/ol/renderer/layerrenderer.js index c460fafb3b..b3243519e9 100644 --- a/src/ol/renderer/layerrenderer.js +++ b/src/ol/renderer/layerrenderer.js @@ -2,10 +2,12 @@ goog.provide('ol.renderer.Layer'); goog.require('goog.events'); goog.require('goog.events.EventType'); +goog.require('ol.FrameState'); goog.require('ol.Object'); goog.require('ol.TileRange'); goog.require('ol.layer.Layer'); goog.require('ol.layer.LayerProperty'); +goog.require('ol.source.TileSource'); @@ -128,6 +130,23 @@ ol.renderer.Layer.prototype.handleLayerSaturationChange = goog.nullFunction; ol.renderer.Layer.prototype.handleLayerVisibleChange = goog.nullFunction; +/** + * @protected + * @param {ol.FrameState} frameState Frame state. + * @param {ol.source.TileSource} tileSource Tile source. + */ +ol.renderer.Layer.prototype.scheduleExpireCache = + function(frameState, tileSource) { + if (tileSource.canExpireCache()) { + frameState.postRenderFunctions.push( + goog.partial(function(tileSource, map, frameState) { + var tileSourceKey = goog.getUid(tileSource).toString(); + tileSource.expireCache(frameState.usedTiles[tileSourceKey]); + }, tileSource)); + } +}; + + /** * @protected * @param {Object.>} usedTiles Used tiles. diff --git a/src/ol/renderer/webgl/webgltilelayerrenderer.js b/src/ol/renderer/webgl/webgltilelayerrenderer.js index 621883f635..fd4e0842e2 100644 --- a/src/ol/renderer/webgl/webgltilelayerrenderer.js +++ b/src/ol/renderer/webgl/webgltilelayerrenderer.js @@ -462,6 +462,7 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = } this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange); + this.scheduleExpireCache(frameState, tileSource); goog.vec.Mat4.makeIdentity(this.matrix_); goog.vec.Mat4.translate(this.matrix_, diff --git a/src/ol/source/tilesource.js b/src/ol/source/tilesource.js index 6b82b8f3b8..cff0556921 100644 --- a/src/ol/source/tilesource.js +++ b/src/ol/source/tilesource.js @@ -1,6 +1,7 @@ goog.provide('ol.source.TileSource'); goog.provide('ol.source.TileSourceOptions'); +goog.require('goog.functions'); goog.require('ol.Attribution'); goog.require('ol.Extent'); goog.require('ol.Projection'); @@ -46,6 +47,18 @@ ol.source.TileSource = function(tileSourceOptions) { goog.inherits(ol.source.TileSource, ol.source.Source); +/** + * @return {boolean} Can expire cache. + */ +ol.source.TileSource.prototype.canExpireCache = goog.functions.FALSE; + + +/** + * @param {Object.} usedTiles Used tiles. + */ +ol.source.TileSource.prototype.expireCache = goog.abstractMethod; + + /** * @inheritDoc */ From 756868830106d9f26a026158424f9339b2d4ce05 Mon Sep 17 00:00:00 2001 From: Frederic Junod Date: Wed, 23 Jan 2013 14:35:14 +0100 Subject: [PATCH 40/60] Use option object rather than positional arguments for ol.animation.create* functions --- examples/side-by-side.js | 69 +++++++++++++++++++++++++-------- src/objectliterals.exports | 24 ++++++++++++ src/ol/animation.js | 79 ++++++++++++++++---------------------- src/ol/view2d.js | 6 ++- 4 files changed, 113 insertions(+), 65 deletions(-) diff --git a/examples/side-by-side.js b/examples/side-by-side.js index 411c0d0df3..017b072750 100644 --- a/examples/side-by-side.js +++ b/examples/side-by-side.js @@ -107,26 +107,41 @@ keyboardInteraction.addCallback('H', function() { layer.setHue(layer.getHue() + (Math.PI / 5)); }); keyboardInteraction.addCallback('j', function() { - var bounce = ol.animation.createBounce(2 * view.getResolution()); + var bounce = ol.animation.createBounce({ + resolution: 2 * view.getResolution() + }); domMap.addPreRenderFunction(bounce); webglMap.addPreRenderFunction(bounce); canvasMap.addPreRenderFunction(bounce); }); keyboardInteraction.addCallback('l', function() { - var panFrom = ol.animation.createPanFrom( - view.getCenter(), undefined, undefined, ol.easing.elastic); + var panFrom = ol.animation.createPanFrom({ + source: view.getCenter(), + easing: ol.easing.elastic + }); domMap.addPreRenderFunction(panFrom); webglMap.addPreRenderFunction(panFrom); canvasMap.addPreRenderFunction(panFrom); view.setCenter(LONDON); }); keyboardInteraction.addCallback('L', function() { - var start = Date.now(); + var start = goog.now(); var duration = 5000; - var bounce = ol.animation.createBounce( - 2 * view.getResolution(), duration, start); - var panFrom = ol.animation.createPanFrom(view.getCenter(), duration, start); - var spin = ol.animation.createSpin(duration, 2, start); + var bounce = ol.animation.createBounce({ + resolution: 2 * view.getResolution(), + start: start, + duration: duration + }); + var panFrom = ol.animation.createPanFrom({ + source: view.getCenter(), + start: start, + duration: duration + }); + var spin = ol.animation.createSpin({ + turns: 2, + start: start, + duration: duration + }); var preRenderFunctions = [bounce, panFrom, spin]; domMap.addPreRenderFunctions(preRenderFunctions); webglMap.addPreRenderFunctions(preRenderFunctions); @@ -134,20 +149,34 @@ keyboardInteraction.addCallback('L', function() { view.setCenter(LONDON); }); keyboardInteraction.addCallback('m', function() { - var panFrom = ol.animation.createPanFrom( - view.getCenter(), 1000, undefined, ol.easing.bounce); + var panFrom = ol.animation.createPanFrom({ + source: view.getCenter(), + duration: 1000, + easing: ol.easing.bounce + }); domMap.addPreRenderFunction(panFrom); webglMap.addPreRenderFunction(panFrom); canvasMap.addPreRenderFunction(panFrom); view.setCenter(MOSCOW); }); keyboardInteraction.addCallback('M', function() { - var start = Date.now(); + var start = goog.now(); var duration = 5000; - var bounce = ol.animation.createBounce( - 2 * view.getResolution(), duration, start); - var panFrom = ol.animation.createPanFrom(view.getCenter(), duration, start); - var spin = ol.animation.createSpin(duration, -2, start); + var bounce = ol.animation.createBounce({ + resolution: 2 * view.getResolution(), + start: start, + duration: duration + }); + var panFrom = ol.animation.createPanFrom({ + source: view.getCenter(), + start: start, + duration: duration + }); + var spin = ol.animation.createSpin({ + turns: -2, + start: start, + duration: duration + }); var preRenderFunctions = [bounce, panFrom, spin]; domMap.addPreRenderFunctions(preRenderFunctions); webglMap.addPreRenderFunctions(preRenderFunctions); @@ -174,13 +203,19 @@ keyboardInteraction.addCallback('vV', function() { layer.setVisible(!layer.getVisible()); }); keyboardInteraction.addCallback('x', function() { - var spin = ol.animation.createSpin(2000, 2); + var spin = ol.animation.createSpin({ + turns: 2, + duration: 2000 + }); domMap.addPreRenderFunction(spin); webglMap.addPreRenderFunction(spin); canvasMap.addPreRenderFunction(spin); }); keyboardInteraction.addCallback('X', function() { - var spin = ol.animation.createSpin(2000, -2); + var spin = ol.animation.createSpin({ + turns: -2, + duration: 2000 + }); domMap.addPreRenderFunction(spin); webglMap.addPreRenderFunction(spin); canvasMap.addPreRenderFunction(spin); diff --git a/src/objectliterals.exports b/src/objectliterals.exports index f22ef20690..5dddcaba8b 100644 --- a/src/objectliterals.exports +++ b/src/objectliterals.exports @@ -26,6 +26,30 @@ @exportObjectLiteralProperty ol.View2DOptions.zoom number|undefined @exportObjectLiteralProperty ol.View2DOptions.zoomFactor number|undefined +@exportObjectLiteral ol.animation.BounceOptions +@exportObjectLiteralProperty ol.animation.BounceOptions.resolution number +@exportObjectLiteralProperty ol.animation.BounceOptions.start number|undefined +@exportObjectLiteralProperty ol.animation.BounceOptions.duration number|undefined +@exportObjectLiteralProperty ol.animation.BounceOptions.easing function(number):number|undefined + +@exportObjectLiteral ol.animation.PanFromOptions +@exportObjectLiteralProperty ol.animation.PanFromOptions.source ol.Coordinate +@exportObjectLiteralProperty ol.animation.PanFromOptions.start number|undefined +@exportObjectLiteralProperty ol.animation.PanFromOptions.duration number|undefined +@exportObjectLiteralProperty ol.animation.PanFromOptions.easing function(number):number|undefined + +@exportObjectLiteral ol.animation.SpinOptions +@exportObjectLiteralProperty ol.animation.SpinOptions.turns number +@exportObjectLiteralProperty ol.animation.SpinOptions.start number|undefined +@exportObjectLiteralProperty ol.animation.SpinOptions.duration number|undefined +@exportObjectLiteralProperty ol.animation.SpinOptions.easing function(number):number|undefined + +@exportObjectLiteral ol.animation.ZoomFromOptions +@exportObjectLiteralProperty ol.animation.ZoomFromOptions.resolution number +@exportObjectLiteralProperty ol.animation.ZoomFromOptions.start number|undefined +@exportObjectLiteralProperty ol.animation.ZoomFromOptions.duration number|undefined +@exportObjectLiteralProperty ol.animation.ZoomFromOptions.easing function(number):number|undefined + @exportObjectLiteral ol.control.AttributionOptions @exportObjectLiteralProperty ol.control.AttributionOptions.map ol.Map|undefined @exportObjectLiteralProperty ol.control.AttributionOptions.target Element|undefined diff --git a/src/ol/animation.js b/src/ol/animation.js index 73dfebbe57..958817aa2e 100644 --- a/src/ol/animation.js +++ b/src/ol/animation.js @@ -9,25 +9,22 @@ goog.require('ol.easing'); /** - * @param {number} resolution Resolution. - * @param {number=} opt_duration Duration. - * @param {number=} opt_start Start. - * @param {function(number): number=} opt_easingFunction Easing function. + * @param {ol.animation.BounceOptions} options Options. * @return {ol.PreRenderFunction} Pre-render function. */ -ol.animation.createBounce = - function(resolution, opt_duration, opt_start, opt_easingFunction) { - var start = goog.isDef(opt_start) ? opt_start : goog.now(); - var duration = goog.isDef(opt_duration) ? opt_duration : 1000; - var easingFunction = goog.isDef(opt_easingFunction) ? - opt_easingFunction : ol.easing.upAndDown; +ol.animation.createBounce = function(options) { + var resolution = options.resolution; + var start = goog.isDef(options.start) ? options.start : goog.now(); + var duration = goog.isDef(options.duration) ? options.duration : 1000; + var easing = goog.isDef(options.easing) ? + options.easing : ol.easing.upAndDown; return function(map, frameState) { if (frameState.time < start) { frameState.animate = true; frameState.viewHints[ol.ViewHint.ANIMATING] += 1; return true; } else if (frameState.time < start + duration) { - var delta = easingFunction((frameState.time - start) / duration); + var delta = easing((frameState.time - start) / duration); var deltaResolution = resolution - frameState.view2DState.resolution; frameState.animate = true; frameState.view2DState.resolution += delta * deltaResolution; @@ -41,27 +38,24 @@ ol.animation.createBounce = /** - * @param {ol.Coordinate} source Source. - * @param {number=} opt_duration Duration. - * @param {number=} opt_start Start. - * @param {function(number): number=} opt_easingFunction Easing function. + * @param {ol.animation.PanFromOptions} options Options. * @return {ol.PreRenderFunction} Pre-render function. */ -ol.animation.createPanFrom = - function(source, opt_duration, opt_start, opt_easingFunction) { - var start = goog.isDef(opt_start) ? opt_start : goog.now(); +ol.animation.createPanFrom = function(options) { + var source = options.source; + var start = goog.isDef(options.start) ? options.start : goog.now(); var sourceX = source.x; var sourceY = source.y; - var duration = goog.isDef(opt_duration) ? opt_duration : 1000; - var easingFunction = goog.isDef(opt_easingFunction) ? - opt_easingFunction : goog.fx.easing.inAndOut; + var duration = goog.isDef(options.duration) ? options.duration : 1000; + var easing = goog.isDef(options.easing) ? + options.easing : goog.fx.easing.inAndOut; return function(map, frameState) { if (frameState.time < start) { frameState.animate = true; frameState.viewHints[ol.ViewHint.ANIMATING] += 1; return true; } else if (frameState.time < start + duration) { - var delta = 1 - easingFunction((frameState.time - start) / duration); + var delta = 1 - easing((frameState.time - start) / duration); var deltaX = sourceX - frameState.view2DState.center.x; var deltaY = sourceY - frameState.view2DState.center.y; frameState.animate = true; @@ -77,27 +71,23 @@ ol.animation.createPanFrom = /** - * @param {number=} opt_duration Duration. - * @param {number=} opt_turns Turns. - * @param {number=} opt_start Start. - * @param {function(number): number=} opt_easingFunction Easing function. + * @param {ol.animation.SpinOptions} options Options. * @return {ol.PreRenderFunction} Pre-render function. */ -ol.animation.createSpin = - function(opt_duration, opt_turns, opt_start, opt_easingFunction) { - var start = goog.isDef(opt_start) ? opt_start : goog.now(); - var duration = goog.isDef(opt_duration) ? opt_duration : 1000; - var turns = goog.isDef(opt_turns) ? opt_turns : 1; - var deltaTheta = 2 * turns * Math.PI; - var easingFunction = goog.isDef(opt_easingFunction) ? - opt_easingFunction : goog.fx.easing.inAndOut; +ol.animation.createSpin = function(options) { + var start = goog.isDef(options.start) ? options.start : goog.now(); + var duration = goog.isDef(options.duration) ? options.duration : 1000; + var easing = goog.isDef(options.easing) ? + options.easing : goog.fx.easing.inAndOut; + var deltaTheta = 2 * options.turns * Math.PI; + return function(map, frameState) { if (frameState.time < start) { frameState.animate = true; frameState.viewHints[ol.ViewHint.ANIMATING] += 1; return true; } else if (frameState.time < start + duration) { - var delta = easingFunction((frameState.time - start) / duration); + var delta = easing((frameState.time - start) / duration); frameState.animate = true; frameState.view2DState.rotation += delta * deltaTheta; frameState.viewHints[ol.ViewHint.ANIMATING] += 1; @@ -110,25 +100,22 @@ ol.animation.createSpin = /** - * @param {number} sourceResolution Source resolution. - * @param {number=} opt_duration Duration. - * @param {number=} opt_start Start. - * @param {function(number): number=} opt_easingFunction Easing function. + * @param {ol.animation.ZoomFromOptions} options Options. * @return {ol.PreRenderFunction} Pre-render function. */ -ol.animation.createZoomFrom = - function(sourceResolution, opt_duration, opt_start, opt_easingFunction) { - var start = goog.isDef(opt_start) ? opt_start : goog.now(); - var duration = goog.isDef(opt_duration) ? opt_duration : 1000; - var easingFunction = goog.isDef(opt_easingFunction) ? - opt_easingFunction : ol.easing.linear; +ol.animation.createZoomFrom = function(options) { + var sourceResolution = options.resolution; + var start = goog.isDef(options.start) ? options.start : goog.now(); + var duration = goog.isDef(options.duration) ? options.duration : 1000; + var easing = goog.isDef(options.easing) ? + options.easing : ol.easing.linear; return function(map, frameState) { if (frameState.time < start) { frameState.animate = true; frameState.viewHints[ol.ViewHint.ANIMATING] += 1; return true; } else if (frameState.time < start + duration) { - var delta = 1 - easingFunction((frameState.time - start) / duration); + var delta = 1 - easing((frameState.time - start) / duration); var deltaResolution = sourceResolution - frameState.view2DState.resolution; frameState.animate = true; diff --git a/src/ol/view2d.js b/src/ol/view2d.js index 0ea5977c43..ad4af94932 100644 --- a/src/ol/view2d.js +++ b/src/ol/view2d.js @@ -296,8 +296,10 @@ ol.View2D.prototype.zoom = function(map, delta, opt_anchor, opt_duration) { var currentResolution = this.getResolution(); if (goog.isDef(currentResolution) && goog.isDef(opt_duration)) { map.requestRenderFrame(); - map.addPreRenderFunction(ol.animation.createZoomFrom( - currentResolution, opt_duration)); + map.addPreRenderFunction(ol.animation.createZoomFrom({ + resolution: currentResolution, + duration: opt_duration + })); } var resolution = this.constraints_.resolution(currentResolution, delta); this.zoom_(map, resolution, opt_anchor); From b03429915875556aab1e1641cbaf41d03450b685 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Wed, 23 Jan 2013 14:21:41 +0100 Subject: [PATCH 41/60] Add ol.TileCache --- src/ol/tilecache.js | 58 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 src/ol/tilecache.js diff --git a/src/ol/tilecache.js b/src/ol/tilecache.js new file mode 100644 index 0000000000..e37dd93d00 --- /dev/null +++ b/src/ol/tilecache.js @@ -0,0 +1,58 @@ +goog.provide('ol.TileCache'); + +goog.require('goog.dispose'); +goog.require('goog.structs.LinkedMap'); +goog.require('ol.Tile'); +goog.require('ol.TileRange'); + + +/** + * @define {number} Default high water mark. + */ +ol.DEFAULT_TILE_CACHE_HIGH_WATER_MARK = 512; + + + +/** + * @constructor + * @extends {goog.structs.LinkedMap} + * @param {number=} opt_highWaterMark High water mark. + */ +ol.TileCache = function(opt_highWaterMark) { + + goog.base(this, undefined, true); + + /** + * @private + * @type {number} + */ + this.highWaterMark_ = goog.isDef(opt_highWaterMark) ? + opt_highWaterMark : ol.DEFAULT_TILE_CACHE_HIGH_WATER_MARK; + +}; +goog.inherits(ol.TileCache, goog.structs.LinkedMap); + + +/** + * @return {boolean} Can expire cache. + */ +ol.TileCache.prototype.canExpireCache = function() { + return this.getCount() > this.highWaterMark_; +}; + + +/** + * @param {Object.} usedTiles Used tiles. + */ +ol.TileCache.prototype.expireCache = function(usedTiles) { + var tile, zKey; + while (this.canExpireCache()) { + tile = /** @type {ol.Tile} */ (this.peekLast()); + zKey = tile.tileCoord.z.toString(); + if (zKey in usedTiles && usedTiles[zKey].contains(tile.tileCoord)) { + break; + } else { + this.pop(); + } + } +}; From 09ec4449587c2294181bc27ab45929518e3687eb Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Wed, 23 Jan 2013 14:28:49 +0100 Subject: [PATCH 42/60] Use ol.TileCache in ol.source.ImageTileSource --- src/ol/source/imagetilesource.js | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/ol/source/imagetilesource.js b/src/ol/source/imagetilesource.js index 5baea4539f..b025985f00 100644 --- a/src/ol/source/imagetilesource.js +++ b/src/ol/source/imagetilesource.js @@ -5,6 +5,7 @@ goog.require('ol.Attribution'); goog.require('ol.Extent'); goog.require('ol.ImageTile'); goog.require('ol.Projection'); +goog.require('ol.TileCache'); goog.require('ol.TileCoord'); goog.require('ol.TileUrlFunction'); goog.require('ol.TileUrlFunctionType'); @@ -55,32 +56,46 @@ ol.source.ImageTileSource = function(options) { /** * @private - * @type {Object.} - * FIXME will need to expire elements from this cache - * FIXME see elemoine's work with goog.structs.LinkedMap + * @type {ol.TileCache} */ - this.tileCache_ = {}; + this.tileCache_ = new ol.TileCache(); }; goog.inherits(ol.source.ImageTileSource, ol.source.TileSource); +/** + * @inheritDoc + */ +ol.source.ImageTileSource.prototype.canExpireCache = function() { + return this.tileCache_.canExpireCache(); +}; + + +/** + * @inheritDoc + */ +ol.source.ImageTileSource.prototype.expireCache = function(usedTiles) { + this.tileCache_.expireCache(usedTiles); +}; + + /** * @inheritDoc */ ol.source.ImageTileSource.prototype.getTile = function(tileCoord) { var key = tileCoord.toString(); - if (goog.object.containsKey(this.tileCache_, key)) { - return this.tileCache_[key]; + if (this.tileCache_.containsKey(key)) { + return /** @type {ol.Tile} */ (this.tileCache_.get(key)); } else { var tileUrl = this.getTileCoordUrl(tileCoord); var tile; if (goog.isDef(tileUrl)) { tile = new ol.ImageTile(tileCoord, tileUrl, this.crossOrigin_); + this.tileCache_.set(key, tile); } else { tile = null; } - this.tileCache_[key] = tile; return tile; } }; From 0f3d708525ae0429364a320ff2a02f705e812629 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Wed, 23 Jan 2013 14:28:58 +0100 Subject: [PATCH 43/60] Use ol.TileCache in ol.source.DebugTileSource --- src/ol/source/debugtilesource.js | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/ol/source/debugtilesource.js b/src/ol/source/debugtilesource.js index 452cf5085a..1cb3e059e7 100644 --- a/src/ol/source/debugtilesource.js +++ b/src/ol/source/debugtilesource.js @@ -2,6 +2,7 @@ goog.provide('ol.source.DebugTileSource'); goog.require('ol.Size'); goog.require('ol.Tile'); +goog.require('ol.TileCache'); goog.require('ol.TileCoord'); goog.require('ol.source.TileSource'); goog.require('ol.tilegrid.TileGrid'); @@ -94,26 +95,40 @@ ol.source.DebugTileSource = function(options) { /** * @private - * @type {Object.} - * FIXME will need to expire elements from this cache - * FIXME see elemoine's work with goog.structs.LinkedMap + * @type {ol.TileCache} */ - this.tileCache_ = {}; + this.tileCache_ = new ol.TileCache(); }; goog.inherits(ol.source.DebugTileSource, ol.source.TileSource); +/** + * @inheritDoc + */ +ol.source.DebugTileSource.prototype.canExpireCache = function() { + return this.tileCache_.canExpireCache(); +}; + + +/** + * @inheritDoc + */ +ol.source.DebugTileSource.prototype.expireCache = function(usedTiles) { + this.tileCache_.expireCache(usedTiles); +}; + + /** * @inheritDoc */ ol.source.DebugTileSource.prototype.getTile = function(tileCoord) { var key = tileCoord.toString(); - if (goog.object.containsKey(this.tileCache_, key)) { - return this.tileCache_[key]; + if (this.tileCache_.containsKey(key)) { + return /** @type {ol.DebugTile_} */ (this.tileCache_.get(key)); } else { var tile = new ol.DebugTile_(tileCoord, this.tileGrid); - this.tileCache_[key] = tile; + this.tileCache_.set(key, tile); return tile; } }; From 140f8c86830905fff87d4bf1cefd67387036dd1e Mon Sep 17 00:00:00 2001 From: Shawn Brenneman Date: Wed, 23 Jan 2013 17:40:55 +0100 Subject: [PATCH 44/60] Add goog.structs.LinkedMap from Closure Library --- src/ol/structs/linkedmap.js | 473 ++++++++++++++++++++++++++++++++++++ 1 file changed, 473 insertions(+) create mode 100644 src/ol/structs/linkedmap.js diff --git a/src/ol/structs/linkedmap.js b/src/ol/structs/linkedmap.js new file mode 100644 index 0000000000..1995349a73 --- /dev/null +++ b/src/ol/structs/linkedmap.js @@ -0,0 +1,473 @@ +// Copyright 2007 The Closure Library Authors. 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 A LinkedMap data structure that is accessed using key/value + * pairs like an ordinary Map, but which guarantees a consistent iteration + * order over its entries. The iteration order is either insertion order (the + * default) or ordered from most recent to least recent use. By setting a fixed + * size, the LRU version of the LinkedMap makes an effective object cache. This + * data structure is similar to Java's LinkedHashMap. + * + * @author brenneman@google.com (Shawn Brenneman) + */ + + +goog.provide('goog.structs.LinkedMap'); + +goog.require('goog.structs.Map'); + + + +/** + * Class for a LinkedMap datastructure, which combines O(1) map access for + * key/value pairs with a linked list for a consistent iteration order. Sample + * usage: + * + *
+ * var m = new LinkedMap();
+ * m.set('param1', 'A');
+ * m.set('param2', 'B');
+ * m.set('param3', 'C');
+ * alert(m.getKeys()); // param1, param2, param3
+ *
+ * var c = new LinkedMap(5, true);
+ * for (var i = 0; i < 10; i++) {
+ *   c.set('entry' + i, false);
+ * }
+ * alert(c.getKeys()); // entry9, entry8, entry7, entry6, entry5
+ *
+ * c.set('entry5', true);
+ * c.set('entry1', false);
+ * alert(c.getKeys()); // entry1, entry5, entry9, entry8, entry7
+ * 
+ * + * @param {number=} opt_maxCount The maximum number of objects to store in the + * LinkedMap. If unspecified or 0, there is no maximum. + * @param {boolean=} opt_cache When set, the LinkedMap stores items in order + * from most recently used to least recently used, instead of insertion + * order. + * @constructor + */ +goog.structs.LinkedMap = function(opt_maxCount, opt_cache) { + /** + * The maximum number of entries to allow, or null if there is no limit. + * @type {?number} + * @private + */ + this.maxCount_ = opt_maxCount || null; + + /** + * @type {boolean} + * @private + */ + this.cache_ = !!opt_cache; + + this.map_ = new goog.structs.Map(); + + this.head_ = new goog.structs.LinkedMap.Node_('', undefined); + this.head_.next = this.head_.prev = this.head_; +}; + + +/** + * Finds a node and updates it to be the most recently used. + * @param {string} key The key of the node. + * @return {goog.structs.LinkedMap.Node_} The node or null if not found. + * @private + */ +goog.structs.LinkedMap.prototype.findAndMoveToTop_ = function(key) { + var node = /** @type {goog.structs.LinkedMap.Node_} */ (this.map_.get(key)); + if (node) { + if (this.cache_) { + node.remove(); + this.insert_(node); + } + } + return node; +}; + + +/** + * Retrieves the value for a given key. If this is a caching LinkedMap, the + * entry will become the most recently used. + * @param {string} key The key to retrieve the value for. + * @param {*=} opt_val A default value that will be returned if the key is + * not found, defaults to undefined. + * @return {*} The retrieved value. + */ +goog.structs.LinkedMap.prototype.get = function(key, opt_val) { + var node = this.findAndMoveToTop_(key); + return node ? node.value : opt_val; +}; + + +/** + * Retrieves the value for a given key without updating the entry to be the + * most recently used. + * @param {string} key The key to retrieve the value for. + * @param {*=} opt_val A default value that will be returned if the key is + * not found. + * @return {*} The retrieved value. + */ +goog.structs.LinkedMap.prototype.peekValue = function(key, opt_val) { + var node = this.map_.get(key); + return node ? node.value : opt_val; +}; + + +/** + * Sets a value for a given key. If this is a caching LinkedMap, this entry + * will become the most recently used. + * @param {string} key The key to retrieve the value for. + * @param {*} value A default value that will be returned if the key is + * not found. + */ +goog.structs.LinkedMap.prototype.set = function(key, value) { + var node = this.findAndMoveToTop_(key); + if (node) { + node.value = value; + } else { + node = new goog.structs.LinkedMap.Node_(key, value); + this.map_.set(key, node); + this.insert_(node); + } +}; + + +/** + * Returns the value of the first node without making any modifications. + * @return {*} The value of the first node or undefined if the map is empty. + */ +goog.structs.LinkedMap.prototype.peek = function() { + return this.head_.next.value; +}; + + +/** + * Returns the value of the last node without making any modifications. + * @return {*} The value of the last node or undefined if the map is empty. + */ +goog.structs.LinkedMap.prototype.peekLast = function() { + return this.head_.prev.value; +}; + + +/** + * Removes the first node from the list and returns its value. + * @return {*} The value of the popped node, or undefined if the map was empty. + */ +goog.structs.LinkedMap.prototype.shift = function() { + return this.popNode_(this.head_.next); +}; + + +/** + * Removes the last node from the list and returns its value. + * @return {*} The value of the popped node, or undefined if the map was empty. + */ +goog.structs.LinkedMap.prototype.pop = function() { + return this.popNode_(this.head_.prev); +}; + + +/** + * Removes a value from the LinkedMap based on its key. + * @param {string} key The key to remove. + * @return {boolean} True if the entry was removed, false if the key was not + * found. + */ +goog.structs.LinkedMap.prototype.remove = function(key) { + var node = /** @type {goog.structs.LinkedMap.Node_} */ (this.map_.get(key)); + if (node) { + this.removeNode(node); + return true; + } + return false; +}; + + +/** + * Removes a node from the {@code LinkedMap}. It can be overridden to do + * further cleanup such as disposing of the node value. + * @param {!goog.structs.LinkedMap.Node_} node The node to remove. + * @protected + */ +goog.structs.LinkedMap.prototype.removeNode = function(node) { + node.remove(); + this.map_.remove(node.key); +}; + + +/** + * @return {number} The number of items currently in the LinkedMap. + */ +goog.structs.LinkedMap.prototype.getCount = function() { + return this.map_.getCount(); +}; + + +/** + * @return {boolean} True if the cache is empty, false if it contains any items. + */ +goog.structs.LinkedMap.prototype.isEmpty = function() { + return this.map_.isEmpty(); +}; + + +/** + * Sets the maximum number of entries allowed in this object, truncating any + * excess objects if necessary. + * @param {number} maxCount The new maximum number of entries to allow. + */ +goog.structs.LinkedMap.prototype.setMaxCount = function(maxCount) { + this.maxCount_ = maxCount || null; + if (this.maxCount_ != null) { + this.truncate_(this.maxCount_); + } +}; + + +/** + * @return {!Array.} The list of the keys in the appropriate order for + * this LinkedMap. + */ +goog.structs.LinkedMap.prototype.getKeys = function() { + return this.map(function(val, key) { + return key; + }); +}; + + +/** + * @return {!Array} The list of the values in the appropriate order for + * this LinkedMap. + */ +goog.structs.LinkedMap.prototype.getValues = function() { + return this.map(function(val, key) { + return val; + }); +}; + + +/** + * Tests whether a provided value is currently in the LinkedMap. This does not + * affect item ordering in cache-style LinkedMaps. + * @param {Object} value The value to check for. + * @return {boolean} Whether the value is in the LinkedMap. + */ +goog.structs.LinkedMap.prototype.contains = function(value) { + return this.some(function(el) { + return el == value; + }); +}; + + +/** + * Tests whether a provided key is currently in the LinkedMap. This does not + * affect item ordering in cache-style LinkedMaps. + * @param {string} key The key to check for. + * @return {boolean} Whether the key is in the LinkedMap. + */ +goog.structs.LinkedMap.prototype.containsKey = function(key) { + return this.map_.containsKey(key); +}; + + +/** + * Removes all entries in this object. + */ +goog.structs.LinkedMap.prototype.clear = function() { + this.truncate_(0); +}; + + +/** + * Calls a function on each item in the LinkedMap. + * + * @see goog.structs.forEach + * @param {Function} f The function to call for each item. The function takes + * three arguments: the value, the key, and the LinkedMap. + * @param {Object=} opt_obj The object context to use as "this" for the + * function. + */ +goog.structs.LinkedMap.prototype.forEach = function(f, opt_obj) { + for (var n = this.head_.next; n != this.head_; n = n.next) { + f.call(opt_obj, n.value, n.key, this); + } +}; + + +/** + * Calls a function on each item in the LinkedMap and returns the results of + * those calls in an array. + * + * @see goog.structs.map + * @param {!Function} f The function to call for each item. The function takes + * three arguments: the value, the key, and the LinkedMap. + * @param {Object=} opt_obj The object context to use as "this" for the + * function. + * @return {!Array} The results of the function calls for each item in the + * LinkedMap. + */ +goog.structs.LinkedMap.prototype.map = function(f, opt_obj) { + var rv = []; + for (var n = this.head_.next; n != this.head_; n = n.next) { + rv.push(f.call(opt_obj, n.value, n.key, this)); + } + return rv; +}; + + +/** + * Calls a function on each item in the LinkedMap and returns true if any of + * those function calls returns a true-like value. + * + * @see goog.structs.some + * @param {Function} f The function to call for each item. The function takes + * three arguments: the value, the key, and the LinkedMap, and returns a + * boolean. + * @param {Object=} opt_obj The object context to use as "this" for the + * function. + * @return {boolean} Whether f evaluates to true for at least one item in the + * LinkedMap. + */ +goog.structs.LinkedMap.prototype.some = function(f, opt_obj) { + for (var n = this.head_.next; n != this.head_; n = n.next) { + if (f.call(opt_obj, n.value, n.key, this)) { + return true; + } + } + return false; +}; + + +/** + * Calls a function on each item in the LinkedMap and returns true only if every + * function call returns a true-like value. + * + * @see goog.structs.some + * @param {Function} f The function to call for each item. The function takes + * three arguments: the value, the key, and the Cache, and returns a + * boolean. + * @param {Object=} opt_obj The object context to use as "this" for the + * function. + * @return {boolean} Whether f evaluates to true for every item in the Cache. + */ +goog.structs.LinkedMap.prototype.every = function(f, opt_obj) { + for (var n = this.head_.next; n != this.head_; n = n.next) { + if (!f.call(opt_obj, n.value, n.key, this)) { + return false; + } + } + return true; +}; + + +/** + * Appends a node to the list. LinkedMap in cache mode adds new nodes to + * the head of the list, otherwise they are appended to the tail. If there is a + * maximum size, the list will be truncated if necessary. + * + * @param {goog.structs.LinkedMap.Node_} node The item to insert. + * @private + */ +goog.structs.LinkedMap.prototype.insert_ = function(node) { + if (this.cache_) { + node.next = this.head_.next; + node.prev = this.head_; + + this.head_.next = node; + node.next.prev = node; + } else { + node.prev = this.head_.prev; + node.next = this.head_; + + this.head_.prev = node; + node.prev.next = node; + } + + if (this.maxCount_ != null) { + this.truncate_(this.maxCount_); + } +}; + + +/** + * Removes elements from the LinkedMap if the given count has been exceeded. + * In cache mode removes nodes from the tail of the list. Otherwise removes + * nodes from the head. + * @param {number} count Number of elements to keep. + * @private + */ +goog.structs.LinkedMap.prototype.truncate_ = function(count) { + for (var i = this.map_.getCount(); i > count; i--) { + this.removeNode(this.cache_ ? this.head_.prev : this.head_.next); + } +}; + + +/** + * Removes the node from the LinkedMap if it is not the head, and returns + * the node's value. + * @param {!goog.structs.LinkedMap.Node_} node The item to remove. + * @return {*} The value of the popped node. + * @private + */ +goog.structs.LinkedMap.prototype.popNode_ = function(node) { + if (this.head_ != node) { + this.removeNode(node); + } + return node.value; +}; + + + +/** + * Internal class for a doubly-linked list node containing a key/value pair. + * @param {string} key The key. + * @param {*} value The value. + * @constructor + * @private + */ +goog.structs.LinkedMap.Node_ = function(key, value) { + this.key = key; + this.value = value; +}; + + +/** + * The next node in the list. + * @type {!goog.structs.LinkedMap.Node_} + */ +goog.structs.LinkedMap.Node_.prototype.next; + + +/** + * The previous node in the list. + * @type {!goog.structs.LinkedMap.Node_} + */ +goog.structs.LinkedMap.Node_.prototype.prev; + + +/** + * Causes this node to remove itself from the list. + */ +goog.structs.LinkedMap.Node_.prototype.remove = function() { + this.prev.next = this.next; + this.next.prev = this.prev; + + delete this.prev; + delete this.next; +}; From c7d07124808b461bce71bb74ea209f05f315d4e2 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Wed, 23 Jan 2013 17:41:43 +0100 Subject: [PATCH 45/60] Rename goog.structs.LinkedMap to ol.structs.LinkedMap --- src/ol/structs/linkedmap.js | 82 ++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/src/ol/structs/linkedmap.js b/src/ol/structs/linkedmap.js index 1995349a73..3a9810d8a0 100644 --- a/src/ol/structs/linkedmap.js +++ b/src/ol/structs/linkedmap.js @@ -24,7 +24,7 @@ */ -goog.provide('goog.structs.LinkedMap'); +goog.provide('ol.structs.LinkedMap'); goog.require('goog.structs.Map'); @@ -60,7 +60,7 @@ goog.require('goog.structs.Map'); * order. * @constructor */ -goog.structs.LinkedMap = function(opt_maxCount, opt_cache) { +ol.structs.LinkedMap = function(opt_maxCount, opt_cache) { /** * The maximum number of entries to allow, or null if there is no limit. * @type {?number} @@ -76,7 +76,7 @@ goog.structs.LinkedMap = function(opt_maxCount, opt_cache) { this.map_ = new goog.structs.Map(); - this.head_ = new goog.structs.LinkedMap.Node_('', undefined); + this.head_ = new ol.structs.LinkedMap.Node_('', undefined); this.head_.next = this.head_.prev = this.head_; }; @@ -84,11 +84,11 @@ goog.structs.LinkedMap = function(opt_maxCount, opt_cache) { /** * Finds a node and updates it to be the most recently used. * @param {string} key The key of the node. - * @return {goog.structs.LinkedMap.Node_} The node or null if not found. + * @return {ol.structs.LinkedMap.Node_} The node or null if not found. * @private */ -goog.structs.LinkedMap.prototype.findAndMoveToTop_ = function(key) { - var node = /** @type {goog.structs.LinkedMap.Node_} */ (this.map_.get(key)); +ol.structs.LinkedMap.prototype.findAndMoveToTop_ = function(key) { + var node = /** @type {ol.structs.LinkedMap.Node_} */ (this.map_.get(key)); if (node) { if (this.cache_) { node.remove(); @@ -107,7 +107,7 @@ goog.structs.LinkedMap.prototype.findAndMoveToTop_ = function(key) { * not found, defaults to undefined. * @return {*} The retrieved value. */ -goog.structs.LinkedMap.prototype.get = function(key, opt_val) { +ol.structs.LinkedMap.prototype.get = function(key, opt_val) { var node = this.findAndMoveToTop_(key); return node ? node.value : opt_val; }; @@ -121,7 +121,7 @@ goog.structs.LinkedMap.prototype.get = function(key, opt_val) { * not found. * @return {*} The retrieved value. */ -goog.structs.LinkedMap.prototype.peekValue = function(key, opt_val) { +ol.structs.LinkedMap.prototype.peekValue = function(key, opt_val) { var node = this.map_.get(key); return node ? node.value : opt_val; }; @@ -134,12 +134,12 @@ goog.structs.LinkedMap.prototype.peekValue = function(key, opt_val) { * @param {*} value A default value that will be returned if the key is * not found. */ -goog.structs.LinkedMap.prototype.set = function(key, value) { +ol.structs.LinkedMap.prototype.set = function(key, value) { var node = this.findAndMoveToTop_(key); if (node) { node.value = value; } else { - node = new goog.structs.LinkedMap.Node_(key, value); + node = new ol.structs.LinkedMap.Node_(key, value); this.map_.set(key, node); this.insert_(node); } @@ -150,7 +150,7 @@ goog.structs.LinkedMap.prototype.set = function(key, value) { * Returns the value of the first node without making any modifications. * @return {*} The value of the first node or undefined if the map is empty. */ -goog.structs.LinkedMap.prototype.peek = function() { +ol.structs.LinkedMap.prototype.peek = function() { return this.head_.next.value; }; @@ -159,7 +159,7 @@ goog.structs.LinkedMap.prototype.peek = function() { * Returns the value of the last node without making any modifications. * @return {*} The value of the last node or undefined if the map is empty. */ -goog.structs.LinkedMap.prototype.peekLast = function() { +ol.structs.LinkedMap.prototype.peekLast = function() { return this.head_.prev.value; }; @@ -168,7 +168,7 @@ goog.structs.LinkedMap.prototype.peekLast = function() { * Removes the first node from the list and returns its value. * @return {*} The value of the popped node, or undefined if the map was empty. */ -goog.structs.LinkedMap.prototype.shift = function() { +ol.structs.LinkedMap.prototype.shift = function() { return this.popNode_(this.head_.next); }; @@ -177,7 +177,7 @@ goog.structs.LinkedMap.prototype.shift = function() { * Removes the last node from the list and returns its value. * @return {*} The value of the popped node, or undefined if the map was empty. */ -goog.structs.LinkedMap.prototype.pop = function() { +ol.structs.LinkedMap.prototype.pop = function() { return this.popNode_(this.head_.prev); }; @@ -188,8 +188,8 @@ goog.structs.LinkedMap.prototype.pop = function() { * @return {boolean} True if the entry was removed, false if the key was not * found. */ -goog.structs.LinkedMap.prototype.remove = function(key) { - var node = /** @type {goog.structs.LinkedMap.Node_} */ (this.map_.get(key)); +ol.structs.LinkedMap.prototype.remove = function(key) { + var node = /** @type {ol.structs.LinkedMap.Node_} */ (this.map_.get(key)); if (node) { this.removeNode(node); return true; @@ -201,10 +201,10 @@ goog.structs.LinkedMap.prototype.remove = function(key) { /** * Removes a node from the {@code LinkedMap}. It can be overridden to do * further cleanup such as disposing of the node value. - * @param {!goog.structs.LinkedMap.Node_} node The node to remove. + * @param {!ol.structs.LinkedMap.Node_} node The node to remove. * @protected */ -goog.structs.LinkedMap.prototype.removeNode = function(node) { +ol.structs.LinkedMap.prototype.removeNode = function(node) { node.remove(); this.map_.remove(node.key); }; @@ -213,7 +213,7 @@ goog.structs.LinkedMap.prototype.removeNode = function(node) { /** * @return {number} The number of items currently in the LinkedMap. */ -goog.structs.LinkedMap.prototype.getCount = function() { +ol.structs.LinkedMap.prototype.getCount = function() { return this.map_.getCount(); }; @@ -221,7 +221,7 @@ goog.structs.LinkedMap.prototype.getCount = function() { /** * @return {boolean} True if the cache is empty, false if it contains any items. */ -goog.structs.LinkedMap.prototype.isEmpty = function() { +ol.structs.LinkedMap.prototype.isEmpty = function() { return this.map_.isEmpty(); }; @@ -231,7 +231,7 @@ goog.structs.LinkedMap.prototype.isEmpty = function() { * excess objects if necessary. * @param {number} maxCount The new maximum number of entries to allow. */ -goog.structs.LinkedMap.prototype.setMaxCount = function(maxCount) { +ol.structs.LinkedMap.prototype.setMaxCount = function(maxCount) { this.maxCount_ = maxCount || null; if (this.maxCount_ != null) { this.truncate_(this.maxCount_); @@ -243,7 +243,7 @@ goog.structs.LinkedMap.prototype.setMaxCount = function(maxCount) { * @return {!Array.} The list of the keys in the appropriate order for * this LinkedMap. */ -goog.structs.LinkedMap.prototype.getKeys = function() { +ol.structs.LinkedMap.prototype.getKeys = function() { return this.map(function(val, key) { return key; }); @@ -254,7 +254,7 @@ goog.structs.LinkedMap.prototype.getKeys = function() { * @return {!Array} The list of the values in the appropriate order for * this LinkedMap. */ -goog.structs.LinkedMap.prototype.getValues = function() { +ol.structs.LinkedMap.prototype.getValues = function() { return this.map(function(val, key) { return val; }); @@ -267,7 +267,7 @@ goog.structs.LinkedMap.prototype.getValues = function() { * @param {Object} value The value to check for. * @return {boolean} Whether the value is in the LinkedMap. */ -goog.structs.LinkedMap.prototype.contains = function(value) { +ol.structs.LinkedMap.prototype.contains = function(value) { return this.some(function(el) { return el == value; }); @@ -280,7 +280,7 @@ goog.structs.LinkedMap.prototype.contains = function(value) { * @param {string} key The key to check for. * @return {boolean} Whether the key is in the LinkedMap. */ -goog.structs.LinkedMap.prototype.containsKey = function(key) { +ol.structs.LinkedMap.prototype.containsKey = function(key) { return this.map_.containsKey(key); }; @@ -288,7 +288,7 @@ goog.structs.LinkedMap.prototype.containsKey = function(key) { /** * Removes all entries in this object. */ -goog.structs.LinkedMap.prototype.clear = function() { +ol.structs.LinkedMap.prototype.clear = function() { this.truncate_(0); }; @@ -302,7 +302,7 @@ goog.structs.LinkedMap.prototype.clear = function() { * @param {Object=} opt_obj The object context to use as "this" for the * function. */ -goog.structs.LinkedMap.prototype.forEach = function(f, opt_obj) { +ol.structs.LinkedMap.prototype.forEach = function(f, opt_obj) { for (var n = this.head_.next; n != this.head_; n = n.next) { f.call(opt_obj, n.value, n.key, this); } @@ -321,7 +321,7 @@ goog.structs.LinkedMap.prototype.forEach = function(f, opt_obj) { * @return {!Array} The results of the function calls for each item in the * LinkedMap. */ -goog.structs.LinkedMap.prototype.map = function(f, opt_obj) { +ol.structs.LinkedMap.prototype.map = function(f, opt_obj) { var rv = []; for (var n = this.head_.next; n != this.head_; n = n.next) { rv.push(f.call(opt_obj, n.value, n.key, this)); @@ -343,7 +343,7 @@ goog.structs.LinkedMap.prototype.map = function(f, opt_obj) { * @return {boolean} Whether f evaluates to true for at least one item in the * LinkedMap. */ -goog.structs.LinkedMap.prototype.some = function(f, opt_obj) { +ol.structs.LinkedMap.prototype.some = function(f, opt_obj) { for (var n = this.head_.next; n != this.head_; n = n.next) { if (f.call(opt_obj, n.value, n.key, this)) { return true; @@ -365,7 +365,7 @@ goog.structs.LinkedMap.prototype.some = function(f, opt_obj) { * function. * @return {boolean} Whether f evaluates to true for every item in the Cache. */ -goog.structs.LinkedMap.prototype.every = function(f, opt_obj) { +ol.structs.LinkedMap.prototype.every = function(f, opt_obj) { for (var n = this.head_.next; n != this.head_; n = n.next) { if (!f.call(opt_obj, n.value, n.key, this)) { return false; @@ -380,10 +380,10 @@ goog.structs.LinkedMap.prototype.every = function(f, opt_obj) { * the head of the list, otherwise they are appended to the tail. If there is a * maximum size, the list will be truncated if necessary. * - * @param {goog.structs.LinkedMap.Node_} node The item to insert. + * @param {ol.structs.LinkedMap.Node_} node The item to insert. * @private */ -goog.structs.LinkedMap.prototype.insert_ = function(node) { +ol.structs.LinkedMap.prototype.insert_ = function(node) { if (this.cache_) { node.next = this.head_.next; node.prev = this.head_; @@ -411,7 +411,7 @@ goog.structs.LinkedMap.prototype.insert_ = function(node) { * @param {number} count Number of elements to keep. * @private */ -goog.structs.LinkedMap.prototype.truncate_ = function(count) { +ol.structs.LinkedMap.prototype.truncate_ = function(count) { for (var i = this.map_.getCount(); i > count; i--) { this.removeNode(this.cache_ ? this.head_.prev : this.head_.next); } @@ -421,11 +421,11 @@ goog.structs.LinkedMap.prototype.truncate_ = function(count) { /** * Removes the node from the LinkedMap if it is not the head, and returns * the node's value. - * @param {!goog.structs.LinkedMap.Node_} node The item to remove. + * @param {!ol.structs.LinkedMap.Node_} node The item to remove. * @return {*} The value of the popped node. * @private */ -goog.structs.LinkedMap.prototype.popNode_ = function(node) { +ol.structs.LinkedMap.prototype.popNode_ = function(node) { if (this.head_ != node) { this.removeNode(node); } @@ -441,7 +441,7 @@ goog.structs.LinkedMap.prototype.popNode_ = function(node) { * @constructor * @private */ -goog.structs.LinkedMap.Node_ = function(key, value) { +ol.structs.LinkedMap.Node_ = function(key, value) { this.key = key; this.value = value; }; @@ -449,22 +449,22 @@ goog.structs.LinkedMap.Node_ = function(key, value) { /** * The next node in the list. - * @type {!goog.structs.LinkedMap.Node_} + * @type {!ol.structs.LinkedMap.Node_} */ -goog.structs.LinkedMap.Node_.prototype.next; +ol.structs.LinkedMap.Node_.prototype.next; /** * The previous node in the list. - * @type {!goog.structs.LinkedMap.Node_} + * @type {!ol.structs.LinkedMap.Node_} */ -goog.structs.LinkedMap.Node_.prototype.prev; +ol.structs.LinkedMap.Node_.prototype.prev; /** * Causes this node to remove itself from the list. */ -goog.structs.LinkedMap.Node_.prototype.remove = function() { +ol.structs.LinkedMap.Node_.prototype.remove = function() { this.prev.next = this.next; this.next.prev = this.prev; From 3f6ef77a5a72157bb9d5cde5deb2431e7cb5589c Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Wed, 23 Jan 2013 17:43:41 +0100 Subject: [PATCH 46/60] Add ol.structs.LinkedMap.prototype.peekLastKey --- src/ol/structs/linkedmap.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/ol/structs/linkedmap.js b/src/ol/structs/linkedmap.js index 3a9810d8a0..63971748c5 100644 --- a/src/ol/structs/linkedmap.js +++ b/src/ol/structs/linkedmap.js @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +// FIXME replace this with goog.structs.LinkedMap when goog.structs.LinkedMap +// adds peekLastKey() + /** * @fileoverview A LinkedMap data structure that is accessed using key/value * pairs like an ordinary Map, but which guarantees a consistent iteration @@ -164,6 +167,16 @@ ol.structs.LinkedMap.prototype.peekLast = function() { }; +/** + * Returns the key of the last node without making any modifications. + * @return {string|undefined} The key of the last node or undefined if the map + * is empty. + */ +ol.structs.LinkedMap.prototype.peekLastKey = function() { + return this.head_.prev.key; +}; + + /** * Removes the first node from the list and returns its value. * @return {*} The value of the popped node, or undefined if the map was empty. From 0d7196c09848589578d9e044bd6ffe8bc6d05b48 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Wed, 23 Jan 2013 19:46:03 +0100 Subject: [PATCH 47/60] Expire old textures from texture cache --- src/ol/renderer/webgl/webglmaprenderer.js | 72 +++++++++++++++++++---- 1 file changed, 60 insertions(+), 12 deletions(-) diff --git a/src/ol/renderer/webgl/webglmaprenderer.js b/src/ol/renderer/webgl/webglmaprenderer.js index dfd7d823b1..926a581c0b 100644 --- a/src/ol/renderer/webgl/webglmaprenderer.js +++ b/src/ol/renderer/webgl/webglmaprenderer.js @@ -1,5 +1,3 @@ -// FIXME clear textureCache -// FIXME generational tile texture garbage collector newFrame/get // FIXME check against gl.getParameter(webgl.MAX_TEXTURE_SIZE) goog.provide('ol.renderer.webgl.Map'); @@ -22,10 +20,17 @@ goog.require('ol.renderer.Map'); goog.require('ol.renderer.webgl.FragmentShader'); goog.require('ol.renderer.webgl.TileLayer'); goog.require('ol.renderer.webgl.VertexShader'); +goog.require('ol.structs.LinkedMap'); goog.require('ol.webgl'); goog.require('ol.webgl.WebGLContextEventType'); +/** + * @define {number} Texture cache high water mark. + */ +ol.WEBGL_TEXTURE_CACHE_HIGH_WATER_MARK = 1024; + + /** * @typedef {{magFilter: number, minFilter: number, texture: WebGLTexture}} */ @@ -179,9 +184,15 @@ ol.renderer.webgl.Map = function(container, map) { /** * @private - * @type {Object.} + * @type {ol.structs.LinkedMap} */ - this.textureCache_ = {}; + this.textureCache_ = new ol.structs.LinkedMap(undefined, true); + + /** + * @private + * @type {number} + */ + this.textureCacheFrameMarkerCount_ = 0; /** * @private @@ -227,8 +238,8 @@ ol.renderer.webgl.Map.prototype.bindTileTexture = function(tile, magFilter, minFilter) { var gl = this.getGL(); var tileKey = tile.getKey(); - var textureCacheEntry = this.textureCache_[tileKey]; - if (goog.isDef(textureCacheEntry)) { + if (this.textureCache_.containsKey(tileKey)) { + var textureCacheEntry = this.textureCache_.get(tileKey); gl.bindTexture(goog.webgl.TEXTURE_2D, textureCacheEntry.texture); if (textureCacheEntry.magFilter != magFilter) { gl.texParameteri( @@ -253,11 +264,11 @@ ol.renderer.webgl.Map.prototype.bindTileTexture = goog.webgl.CLAMP_TO_EDGE); gl.texParameteri(goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_WRAP_T, goog.webgl.CLAMP_TO_EDGE); - this.textureCache_[tileKey] = { + this.textureCache_.set(tileKey, { texture: texture, magFilter: magFilter, minFilter: minFilter - }; + }); } }; @@ -287,14 +298,42 @@ ol.renderer.webgl.Map.prototype.disposeInternal = function() { goog.object.forEach(this.shaderCache_, function(shader) { gl.deleteShader(shader); }); - goog.object.forEach(this.textureCache_, function(textureCacheEntry) { - gl.deleteTexture(textureCacheEntry.texture); + this.textureCache_.forEach(function(textureCacheEntry) { + if (!goog.isNull(textureCacheEntry)) { + gl.deleteTexture(textureCacheEntry.texture); + } }); } goog.base(this, 'disposeInternal'); }; +/** + * @param {ol.Map} map Map. + * @param {ol.FrameState} frameState Frame state. + * @private + */ +ol.renderer.webgl.Map.prototype.expireCache_ = function(map, frameState) { + var gl = this.getGL(); + var key, textureCacheEntry; + while (this.textureCache_.getCount() - this.textureCacheFrameMarkerCount_ > + ol.WEBGL_TEXTURE_CACHE_HIGH_WATER_MARK) { + textureCacheEntry = /** @type {?ol.renderer.webgl.TextureCacheEntry} */ + (this.textureCache_.peekLast()); + if (goog.isNull(textureCacheEntry)) { + if (Number(this.textureCache_.peekLastKey()) == frameState.time) { + break; + } else { + --this.textureCacheFrameMarkerCount_; + } + } else { + gl.deleteTexture(textureCacheEntry.texture); + } + this.textureCache_.pop(); + } +}; + + /** * @inheritDoc */ @@ -400,7 +439,8 @@ ol.renderer.webgl.Map.prototype.handleWebGLContextLost = function(event) { this.arrayBuffer_ = null; this.shaderCache_ = {}; this.programCache_ = {}; - this.textureCache_ = {}; + this.textureCache_.clear(); + this.textureCacheFrameMarkerCount_ = 0; goog.object.forEach(this.layerRenderers, function(layerRenderer) { layerRenderer.handleWebGLContextLost(); }); @@ -437,7 +477,7 @@ ol.renderer.webgl.Map.prototype.initializeGL_ = function() { * @return {boolean} Is tile texture loaded. */ ol.renderer.webgl.Map.prototype.isTileTextureLoaded = function(tile) { - return tile.getKey() in this.textureCache_; + return this.textureCache_.containsKey(tile.getKey()); }; @@ -481,6 +521,9 @@ ol.renderer.webgl.Map.prototype.renderFrame = function(frameState) { return false; } + this.textureCache_.set(frameState.time.toString(), null); + ++this.textureCacheFrameMarkerCount_; + goog.array.forEach(frameState.layersArray, function(layer) { var layerState = frameState.layerStates[goog.getUid(layer)]; if (!layerState.visible || !layerState.ready) { @@ -563,6 +606,11 @@ ol.renderer.webgl.Map.prototype.renderFrame = function(frameState) { this.calculateMatrices2D(frameState); + if (this.textureCache_.getCount() - this.textureCacheFrameMarkerCount_ > + ol.WEBGL_TEXTURE_CACHE_HIGH_WATER_MARK) { + frameState.postRenderFunctions.push(goog.bind(this.expireCache_, this)); + } + }; From b0eb7a4b9bba31eaa514ecc4de6d9483038dc370 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Wed, 23 Jan 2013 19:49:22 +0100 Subject: [PATCH 48/60] Port ol.TileCache to ol.structs.LinkedMap --- src/ol/tilecache.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ol/tilecache.js b/src/ol/tilecache.js index e37dd93d00..9645fb9513 100644 --- a/src/ol/tilecache.js +++ b/src/ol/tilecache.js @@ -1,9 +1,9 @@ goog.provide('ol.TileCache'); goog.require('goog.dispose'); -goog.require('goog.structs.LinkedMap'); goog.require('ol.Tile'); goog.require('ol.TileRange'); +goog.require('ol.structs.LinkedMap'); /** @@ -15,7 +15,7 @@ ol.DEFAULT_TILE_CACHE_HIGH_WATER_MARK = 512; /** * @constructor - * @extends {goog.structs.LinkedMap} + * @extends {ol.structs.LinkedMap} * @param {number=} opt_highWaterMark High water mark. */ ol.TileCache = function(opt_highWaterMark) { @@ -30,7 +30,7 @@ ol.TileCache = function(opt_highWaterMark) { opt_highWaterMark : ol.DEFAULT_TILE_CACHE_HIGH_WATER_MARK; }; -goog.inherits(ol.TileCache, goog.structs.LinkedMap); +goog.inherits(ol.TileCache, ol.structs.LinkedMap); /** From 0845dea366a7ee38e2a454a37706fb6f96405e2e Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 23 Jan 2013 22:03:08 -0700 Subject: [PATCH 49/60] Care with transform Since the transform method takes an arbitrary transform function, new coordinates may not be ordered in the same way as the originals. --- src/ol/extent.js | 7 ++++--- test/spec/ol/extent.test.js | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/ol/extent.js b/src/ol/extent.js index 1cbeb217ea..3d0ab215d6 100644 --- a/src/ol/extent.js +++ b/src/ol/extent.js @@ -94,7 +94,8 @@ ol.Extent.prototype.getTopRight = function() { * @return {ol.Extent} Extent. */ ol.Extent.prototype.transform = function(transformFn) { - var min = transformFn(new ol.Coordinate(this.minX, this.minY)); - var max = transformFn(new ol.Coordinate(this.maxX, this.maxY)); - return new ol.Extent(min.x, min.y, max.x, max.y); + var a = transformFn(new ol.Coordinate(this.minX, this.minY)); + var b = transformFn(new ol.Coordinate(this.maxX, this.maxY)); + return new ol.Extent(Math.min(a.x, b.x), Math.min(a.y, b.y), + Math.max(a.x, b.x), Math.max(a.y, b.y)); }; diff --git a/test/spec/ol/extent.test.js b/test/spec/ol/extent.test.js index 8763aaf0d3..4d80e64f03 100644 --- a/test/spec/ol/extent.test.js +++ b/test/spec/ol/extent.test.js @@ -55,6 +55,23 @@ describe('ol.Extent', function() { expect(destinationExtent.maxX).toRoughlyEqual(5009377.085697311, 1e-9); expect(destinationExtent.maxY).toRoughlyEqual(8399737.889818361, 1e-9); }); + + it('takes arbitrary function', function() { + var transformFn = function(coordinate) { + return new ol.Coordinate(-coordinate.x, -coordinate.y); + } + var sourceExtent = new ol.Extent(-15, -30, 45, 60); + var destinationExtent = sourceExtent.transform(transformFn); + expect(destinationExtent).not.toBeUndefined(); + expect(destinationExtent).not.toBeNull(); + // FIXME check values with third-party tool + expect(destinationExtent.minX).toBe(-45); + expect(destinationExtent.minY).toBe(-60); + expect(destinationExtent.maxX).toBe(15); + expect(destinationExtent.maxY).toBe(30); + }); + + }); }); From afb9add911a590672717e823f5f432de978f0fbe Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 23 Jan 2013 22:12:17 -0700 Subject: [PATCH 50/60] Checked with third-party tool --- test/spec/ol/extent.test.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/spec/ol/extent.test.js b/test/spec/ol/extent.test.js index 4d80e64f03..83e20e15e6 100644 --- a/test/spec/ol/extent.test.js +++ b/test/spec/ol/extent.test.js @@ -64,14 +64,12 @@ describe('ol.Extent', function() { var destinationExtent = sourceExtent.transform(transformFn); expect(destinationExtent).not.toBeUndefined(); expect(destinationExtent).not.toBeNull(); - // FIXME check values with third-party tool expect(destinationExtent.minX).toBe(-45); expect(destinationExtent.minY).toBe(-60); expect(destinationExtent.maxX).toBe(15); expect(destinationExtent.maxY).toBe(30); }); - }); }); From 3ccfaebcfc7ded68f3a9ba96db36de95af24e87d Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 23 Jan 2013 22:18:00 -0700 Subject: [PATCH 51/60] Punctuated --- test/spec/ol/extent.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/spec/ol/extent.test.js b/test/spec/ol/extent.test.js index 83e20e15e6..d1312fad3c 100644 --- a/test/spec/ol/extent.test.js +++ b/test/spec/ol/extent.test.js @@ -59,7 +59,7 @@ describe('ol.Extent', function() { it('takes arbitrary function', function() { var transformFn = function(coordinate) { return new ol.Coordinate(-coordinate.x, -coordinate.y); - } + }; var sourceExtent = new ol.Extent(-15, -30, 45, 60); var destinationExtent = sourceExtent.transform(transformFn); expect(destinationExtent).not.toBeUndefined(); From d04b2bb2bdada321df994eafa52b052913481f14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Thu, 24 Jan 2013 10:28:28 +0100 Subject: [PATCH 52/60] Fix typo --- src/ol/source/source.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ol/source/source.js b/src/ol/source/source.js index f2a95444fa..727c732789 100644 --- a/src/ol/source/source.js +++ b/src/ol/source/source.js @@ -10,7 +10,7 @@ goog.require('ol.TileUrlFunction'); /** - * @typedef {{attribtions: (Array.|undefined), + * @typedef {{attributions: (Array.|undefined), * extent: (ol.Extent|undefined), * projection: (ol.Projection|undefined)}} */ From dc9fb0618510819b97089829524cd72d0aab4dec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Thu, 24 Jan 2013 10:32:32 +0100 Subject: [PATCH 53/60] Remove ref to userProjection The wms-custom-proj example sets a userProjection in the map while userProjection is gone for now. It was removed in 927cffb2. --- examples/wms-custom-proj.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/examples/wms-custom-proj.js b/examples/wms-custom-proj.js index 8052557a0e..ae6dea5cc9 100644 --- a/examples/wms-custom-proj.js +++ b/examples/wms-custom-proj.js @@ -52,9 +52,6 @@ var layers = new ol.Collection([ ]); var map = new ol.Map({ - // By setting userProjection to the same as projection, we do not need - // proj4js because we do not need any transforms. - userProjection: epsg21781, layers: layers, renderers: ol.RendererHints.createFromQueryData(), target: 'map', From 8cac270234a3d2b014fdd701593beb9c72f61595 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Thu, 24 Jan 2013 16:31:56 +0100 Subject: [PATCH 54/60] Use + rather than Number for string to number conversion --- src/ol/renderer/webgl/webglmaprenderer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ol/renderer/webgl/webglmaprenderer.js b/src/ol/renderer/webgl/webglmaprenderer.js index 926a581c0b..a1eefb7326 100644 --- a/src/ol/renderer/webgl/webglmaprenderer.js +++ b/src/ol/renderer/webgl/webglmaprenderer.js @@ -321,7 +321,7 @@ ol.renderer.webgl.Map.prototype.expireCache_ = function(map, frameState) { textureCacheEntry = /** @type {?ol.renderer.webgl.TextureCacheEntry} */ (this.textureCache_.peekLast()); if (goog.isNull(textureCacheEntry)) { - if (Number(this.textureCache_.peekLastKey()) == frameState.time) { + if (+this.textureCache_.peekLastKey() == frameState.time) { break; } else { --this.textureCacheFrameMarkerCount_; From 71a462cd54d30c748d69766644e4f7450aad3168 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Thu, 24 Jan 2013 21:40:26 +0100 Subject: [PATCH 55/60] Make map handle view changes With the frameState stuff the map renderer need not listen to view changes. The map renderer receives orders from the map. These orders are renderFrame_ calls with frameState objects representing the current state. --- src/ol/map.js | 34 +++++++++++++++++ src/ol/renderer/canvas/canvasmaprenderer.js | 18 --------- src/ol/renderer/maprenderer.js | 41 +-------------------- 3 files changed, 35 insertions(+), 58 deletions(-) diff --git a/src/ol/map.js b/src/ol/map.js index 9f8e99574b..9dffa68f9d 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -174,6 +174,12 @@ ol.Map = function(mapOptions) { */ this.target_ = mapOptionsInternal.target; + /** + * @private + * @type {?number} + */ + this.viewPropertyListenerKey_ = null; + /** * @private * @type {Element} @@ -277,6 +283,8 @@ ol.Map = function(mapOptions) { */ this.tileQueue_ = new ol.TileQueue(goog.bind(this.getTilePriority, this)); + goog.events.listen(this, ol.Object.getChangedEventType(ol.MapProperty.VIEW), + this.handleViewChanged_, false, this); this.setValues(mapOptionsInternal.values); this.handleBrowserWindowResize(); @@ -559,6 +567,32 @@ ol.Map.prototype.handleBrowserWindowResize = function() { }; +/** + * @private + */ +ol.Map.prototype.handleViewPropertyChanged_ = function() { + this.render(); +}; + + +/** + * @private + */ +ol.Map.prototype.handleViewChanged_ = function() { + if (!goog.isNull(this.viewPropertyListenerKey_)) { + goog.events.unlistenByKey(this.viewPropertyListenerKey_); + this.viewPropertyListenerKey_ = null; + } + var view = this.getView(); + if (goog.isDefAndNotNull(view)) { + this.viewPropertyListenerKey_ = goog.events.listen( + view, ol.ObjectEventType.CHANGED, + this.handleViewPropertyChanged_, false, this); + } + this.render(); +}; + + /** * @return {boolean} Is defined. */ diff --git a/src/ol/renderer/canvas/canvasmaprenderer.js b/src/ol/renderer/canvas/canvasmaprenderer.js index 24d21410db..c107c50c9f 100644 --- a/src/ol/renderer/canvas/canvasmaprenderer.js +++ b/src/ol/renderer/canvas/canvasmaprenderer.js @@ -84,15 +84,6 @@ ol.renderer.canvas.Map.prototype.handleBackgroundColorChanged = function() { }; -/** - * @inheritDoc - */ -ol.renderer.canvas.Map.prototype.handleViewPropertyChanged = function() { - goog.base(this, 'handleViewPropertyChanged'); - this.getMap().render(); -}; - - /** * @param {goog.events.Event} event Event. * @protected @@ -111,15 +102,6 @@ ol.renderer.canvas.Map.prototype.handleSizeChanged = function() { }; -/** - * @inheritDoc - */ -ol.renderer.canvas.Map.prototype.handleViewChanged = function() { - goog.base(this, 'handleViewChanged'); - this.getMap().render(); -}; - - /** * @inheritDoc */ diff --git a/src/ol/renderer/maprenderer.js b/src/ol/renderer/maprenderer.js index 3587628852..1ebf22042f 100644 --- a/src/ol/renderer/maprenderer.js +++ b/src/ol/renderer/maprenderer.js @@ -47,12 +47,6 @@ ol.renderer.Map = function(container, map) { this.layersListenerKeys_ = null; - /** - * @private - * @type {?number} - */ - this.viewPropertyListenerKey_ = null; - /** * @private * @type {Array.} @@ -68,11 +62,7 @@ ol.renderer.Map = function(container, map) { goog.events.listen( map, ol.Object.getChangedEventType(ol.MapProperty.SIZE), - this.handleSizeChanged, false, this), - - goog.events.listen( - map, ol.Object.getChangedEventType(ol.MapProperty.VIEW), - this.handleViewChanged, false, this) + this.handleSizeChanged, false, this) ]; }; @@ -137,9 +127,6 @@ ol.renderer.Map.prototype.disposeInternal = function() { goog.dispose(layerRenderer); }); goog.array.forEach(this.mapListenerKeys_, goog.events.unlistenByKey); - if (!goog.isNull(this.viewPropertyListenerKey_)) { - goog.events.unlistenByKey(this.viewPropertyListenerKey_); - } if (!goog.isNull(this.layersListenerKeys_)) { goog.array.forEach(this.layersListenerKeys_, goog.events.unlistenByKey); } @@ -223,14 +210,6 @@ ol.renderer.Map.prototype.handleLayersRemove = function(collectionEvent) { }; -/** - * @protected - */ -ol.renderer.Map.prototype.handleViewPropertyChanged = function() { - this.getMap().render(); -}; - - /** * @protected */ @@ -239,24 +218,6 @@ ol.renderer.Map.prototype.handleSizeChanged = function() { }; -/** - * @protected - */ -ol.renderer.Map.prototype.handleViewChanged = function() { - if (!goog.isNull(this.viewPropertyListenerKey_)) { - goog.events.unlistenByKey(this.viewPropertyListenerKey_); - this.viewPropertyListenerKey_ = null; - } - var view = this.getMap().getView(); - if (goog.isDefAndNotNull(view)) { - this.viewPropertyListenerKey_ = goog.events.listen( - view, ol.ObjectEventType.CHANGED, - this.handleViewPropertyChanged, false, this); - } - this.getMap().render(); -}; - - /** * @param {ol.layer.Layer} layer Layer. * @protected From 15898b559bbfb6477fa32c6b6694617cc6078a19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Thu, 24 Jan 2013 22:06:47 +0100 Subject: [PATCH 56/60] Make map handle size changes --- src/ol/map.js | 10 ++++++++++ src/ol/renderer/canvas/canvasmaprenderer.js | 9 --------- src/ol/renderer/maprenderer.js | 14 +------------- 3 files changed, 11 insertions(+), 22 deletions(-) diff --git a/src/ol/map.js b/src/ol/map.js index 9dffa68f9d..0202d0d2af 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -285,6 +285,8 @@ ol.Map = function(mapOptions) { goog.events.listen(this, ol.Object.getChangedEventType(ol.MapProperty.VIEW), this.handleViewChanged_, false, this); + goog.events.listen(this, ol.Object.getChangedEventType(ol.MapProperty.SIZE), + this.handleSizeChanged_, false, this); this.setValues(mapOptionsInternal.values); this.handleBrowserWindowResize(); @@ -567,6 +569,14 @@ ol.Map.prototype.handleBrowserWindowResize = function() { }; +/** + * @private + */ +ol.Map.prototype.handleSizeChanged_ = function() { + this.render(); +}; + + /** * @private */ diff --git a/src/ol/renderer/canvas/canvasmaprenderer.js b/src/ol/renderer/canvas/canvasmaprenderer.js index c107c50c9f..d06eeda438 100644 --- a/src/ol/renderer/canvas/canvasmaprenderer.js +++ b/src/ol/renderer/canvas/canvasmaprenderer.js @@ -93,15 +93,6 @@ ol.renderer.canvas.Map.prototype.handleLayerRendererChange = function(event) { }; -/** - * @inheritDoc - */ -ol.renderer.canvas.Map.prototype.handleSizeChanged = function() { - goog.base(this, 'handleSizeChanged'); - this.getMap().render(); -}; - - /** * @inheritDoc */ diff --git a/src/ol/renderer/maprenderer.js b/src/ol/renderer/maprenderer.js index 1ebf22042f..5eb3c507bb 100644 --- a/src/ol/renderer/maprenderer.js +++ b/src/ol/renderer/maprenderer.js @@ -58,11 +58,7 @@ ol.renderer.Map = function(container, map) { goog.events.listen( map, ol.Object.getChangedEventType(ol.MapProperty.LAYERS), - this.handleLayersChanged, false, this), - - goog.events.listen( - map, ol.Object.getChangedEventType(ol.MapProperty.SIZE), - this.handleSizeChanged, false, this) + this.handleLayersChanged, false, this) ]; }; @@ -210,14 +206,6 @@ ol.renderer.Map.prototype.handleLayersRemove = function(collectionEvent) { }; -/** - * @protected - */ -ol.renderer.Map.prototype.handleSizeChanged = function() { - this.getMap().render(); -}; - - /** * @param {ol.layer.Layer} layer Layer. * @protected From 2a28b7c699656a19862ccef7dd53a98f73dcd0e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Thu, 24 Jan 2013 22:07:04 +0100 Subject: [PATCH 57/60] Add comment --- src/ol/map.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ol/map.js b/src/ol/map.js index 0202d0d2af..a5ebaae2aa 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -289,6 +289,7 @@ ol.Map = function(mapOptions) { this.handleSizeChanged_, false, this); this.setValues(mapOptionsInternal.values); + // this gives the map an initial size this.handleBrowserWindowResize(); this.controls_.forEach( From 4a64837ba1acdd26772e73e40b6e38185d214f2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Thu, 24 Jan 2013 22:12:10 +0100 Subject: [PATCH 58/60] Make map handle background color changes --- src/ol/map.js | 11 +++++++++++ src/ol/renderer/canvas/canvasmaprenderer.js | 8 -------- src/ol/renderer/maprenderer.js | 10 ---------- src/ol/renderer/webgl/webglmaprenderer.js | 8 -------- 4 files changed, 11 insertions(+), 26 deletions(-) diff --git a/src/ol/map.js b/src/ol/map.js index a5ebaae2aa..277ef4a172 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -287,6 +287,9 @@ ol.Map = function(mapOptions) { this.handleViewChanged_, false, this); goog.events.listen(this, ol.Object.getChangedEventType(ol.MapProperty.SIZE), this.handleSizeChanged_, false, this); + goog.events.listen( + this, ol.Object.getChangedEventType(ol.MapProperty.BACKGROUND_COLOR), + this.handleBackgroundColorChanged_, false, this), this.setValues(mapOptionsInternal.values); // this gives the map an initial size @@ -561,6 +564,14 @@ ol.Map.prototype.handlePostRender = function() { }; +/** + * @private + */ +ol.Map.prototype.handleBackgroundColorChanged_ = function() { + this.render(); +}; + + /** * @protected */ diff --git a/src/ol/renderer/canvas/canvasmaprenderer.js b/src/ol/renderer/canvas/canvasmaprenderer.js index d06eeda438..e740898e42 100644 --- a/src/ol/renderer/canvas/canvasmaprenderer.js +++ b/src/ol/renderer/canvas/canvasmaprenderer.js @@ -76,14 +76,6 @@ ol.renderer.canvas.Map.prototype.getCanvas = function() { }; -/** - * @inheritDoc - */ -ol.renderer.canvas.Map.prototype.handleBackgroundColorChanged = function() { - this.getMap().render(); -}; - - /** * @param {goog.events.Event} event Event. * @protected diff --git a/src/ol/renderer/maprenderer.js b/src/ol/renderer/maprenderer.js index 5eb3c507bb..1e953dfc24 100644 --- a/src/ol/renderer/maprenderer.js +++ b/src/ol/renderer/maprenderer.js @@ -52,10 +52,6 @@ ol.renderer.Map = function(container, map) { * @type {Array.} */ this.mapListenerKeys_ = [ - goog.events.listen( - map, ol.Object.getChangedEventType(ol.MapProperty.BACKGROUND_COLOR), - this.handleBackgroundColorChanged, false, this), - goog.events.listen( map, ol.Object.getChangedEventType(ol.MapProperty.LAYERS), this.handleLayersChanged, false, this) @@ -157,12 +153,6 @@ ol.renderer.Map.prototype.getMap = function() { }; -/** - * Handle background color changed. - */ -ol.renderer.Map.prototype.handleBackgroundColorChanged = goog.nullFunction; - - /** * @param {ol.CollectionEvent} collectionEvent Collection event. * @protected diff --git a/src/ol/renderer/webgl/webglmaprenderer.js b/src/ol/renderer/webgl/webglmaprenderer.js index a1eefb7326..6a62129e67 100644 --- a/src/ol/renderer/webgl/webglmaprenderer.js +++ b/src/ol/renderer/webgl/webglmaprenderer.js @@ -409,14 +409,6 @@ ol.renderer.webgl.Map.prototype.getShader = function(shaderObject) { }; -/** - * @inheritDoc - */ -ol.renderer.webgl.Map.prototype.handleBackgroundColorChanged = function() { - this.getMap().render(); -}; - - /** * @param {goog.events.Event} event Event. * @protected From 80b38e608bf95cc2862534424cde5924a5cbe788 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Thu, 24 Jan 2013 22:24:51 +0100 Subject: [PATCH 59/60] No more need for listener keys array --- src/ol/renderer/canvas/canvasmaprenderer.js | 9 --------- src/ol/renderer/maprenderer.js | 16 +++++++--------- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/src/ol/renderer/canvas/canvasmaprenderer.js b/src/ol/renderer/canvas/canvasmaprenderer.js index e740898e42..5d9fa3741e 100644 --- a/src/ol/renderer/canvas/canvasmaprenderer.js +++ b/src/ol/renderer/canvas/canvasmaprenderer.js @@ -76,15 +76,6 @@ ol.renderer.canvas.Map.prototype.getCanvas = function() { }; -/** - * @param {goog.events.Event} event Event. - * @protected - */ -ol.renderer.canvas.Map.prototype.handleLayerRendererChange = function(event) { - this.getMap().render(); -}; - - /** * @inheritDoc */ diff --git a/src/ol/renderer/maprenderer.js b/src/ol/renderer/maprenderer.js index 1e953dfc24..06bf5cb930 100644 --- a/src/ol/renderer/maprenderer.js +++ b/src/ol/renderer/maprenderer.js @@ -42,20 +42,18 @@ ol.renderer.Map = function(container, map) { /** * @private - * @type {Array.} + * @type {?number} */ - this.layersListenerKeys_ = null; - + this.mapLayersChangedListenerKey_ = + goog.events.listen( + map, ol.Object.getChangedEventType(ol.MapProperty.LAYERS), + this.handleLayersChanged, false, this); /** * @private * @type {Array.} */ - this.mapListenerKeys_ = [ - goog.events.listen( - map, ol.Object.getChangedEventType(ol.MapProperty.LAYERS), - this.handleLayersChanged, false, this) - ]; + this.layersListenerKeys_ = null; }; goog.inherits(ol.renderer.Map, goog.Disposable); @@ -118,7 +116,7 @@ ol.renderer.Map.prototype.disposeInternal = function() { goog.object.forEach(this.layerRenderers, function(layerRenderer) { goog.dispose(layerRenderer); }); - goog.array.forEach(this.mapListenerKeys_, goog.events.unlistenByKey); + goog.events.unlistenByKey(this.mapLayersChangedListenerKey_); if (!goog.isNull(this.layersListenerKeys_)) { goog.array.forEach(this.layersListenerKeys_, goog.events.unlistenByKey); } From 98b264e8ce5bb990db212e155b28de5bc63b9dd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Thu, 24 Jan 2013 22:25:00 +0100 Subject: [PATCH 60/60] Add comment --- src/ol/renderer/maprenderer.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ol/renderer/maprenderer.js b/src/ol/renderer/maprenderer.js index 06bf5cb930..01313a98b3 100644 --- a/src/ol/renderer/maprenderer.js +++ b/src/ol/renderer/maprenderer.js @@ -40,6 +40,10 @@ ol.renderer.Map = function(container, map) { */ this.layerRenderers = {}; + // + // We listen to layer add/remove to add/remove layer renderers. + // + /** * @private * @type {?number}