A tile queue that can be aborted.

This saves server requests, and because we use OpenLayers.Animation, setting img.src on a tile should not freeze iOS any more, so we can hopefully get rid of scheduleMoveGriddedTiles.
This commit is contained in:
ahocevar
2012-01-26 01:24:11 +01:00
parent 9aa1c4450e
commit ef85f43d21
9 changed files with 165 additions and 158 deletions
+2 -1
View File
@@ -6,8 +6,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta name="apple-mobile-web-app-status-bar-style" content="black">
<link rel="stylesheet" href="../theme/default/style.css" type="text/css">
<link rel="stylesheet" href="mobile-wmts-vienna.css" type="text/css"> <link rel="stylesheet" href="mobile-wmts-vienna.css" type="text/css">
<script src="../lib/OpenLayers.js"></script>
</head> </head>
<body> <body>
<h1 id="title">City of Vienna WMTS for Desktop and Mobile Devices</h1> <h1 id="title">City of Vienna WMTS for Desktop and Mobile Devices</h1>
@@ -22,6 +22,7 @@
functionality and uses the Geolocate control. functionality and uses the Geolocate control.
</p> </p>
<div id="map"></div> <div id="map"></div>
<script src="../lib/OpenLayers.js"></script>
<script src="mobile-wmts-vienna.js"></script> <script src="mobile-wmts-vienna.js"></script>
</body> </body>
</html> </html>
-2
View File
@@ -152,7 +152,6 @@ var map;
var defaults = { var defaults = {
requestEncoding: "REST", requestEncoding: "REST",
matrixSet: "google3857", matrixSet: "google3857",
buffer: 4,
attribution: 'Datenquelle: Stadt Wien - <a href="http://data.wien.gv.at">data.wien.gv.at</a>' attribution: 'Datenquelle: Stadt Wien - <a href="http://data.wien.gv.at">data.wien.gv.at</a>'
}; };
var doc = request.responseText, var doc = request.responseText,
@@ -182,7 +181,6 @@ var map;
requestEncoding: "REST", requestEncoding: "REST",
matrixSet: "google3857", matrixSet: "google3857",
tileFullExtent: extent, tileFullExtent: extent,
buffer: 4,
attribution: 'Datenquelle: Stadt Wien - <a href="http://data.wien.gv.at">data.wien.gv.at</a>' attribution: 'Datenquelle: Stadt Wien - <a href="http://data.wien.gv.at">data.wien.gv.at</a>'
}; };
fmzk = new OpenLayers.Layer.WMTS(OpenLayers.Util.applyDefaults({ fmzk = new OpenLayers.Layer.WMTS(OpenLayers.Util.applyDefaults({
+127 -147
View File
@@ -97,13 +97,6 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
*/ */
numLoadingTiles: 0, numLoadingTiles: 0,
/**
* APIProperty: tileLoadingDelay
* {Integer} - Number of milliseconds before we shift and load
* tiles. Default is 100.
*/
tileLoadingDelay: 100,
/** /**
* Property: serverResolutions * Property: serverResolutions
* {Array(Number}} This property is documented in subclasses as * {Array(Number}} This property is documented in subclasses as
@@ -112,10 +105,16 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
serverResolutions: null, serverResolutions: null,
/** /**
* Property: timerId * Property: tileLoopId
* {Number} - The id of the tileLoadingDelay timer. * {Number} - The id of the <doTileOperation> animation.
*/ */
timerId: null, tileLoopId: null,
/**
* Property: tileOperations
* {Array(Object)} Pending tile operations.
*/
tileOperations: null,
/** /**
* Property: backBuffer * Property: backBuffer
@@ -152,7 +151,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
* flash effects caused by tile animation. * flash effects caused by tile animation.
*/ */
backBufferTimerId: null, backBufferTimerId: null,
/** /**
* Register a listener for a particular event with the following syntax: * Register a listener for a particular event with the following syntax:
* (code) * (code)
@@ -187,10 +186,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this, OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this,
arguments); arguments);
this.grid = []; this.grid = [];
this.tileOperations = [];
this._moveGriddedTiles = OpenLayers.Function.bind(
this.moveGriddedTiles, this
);
}, },
/** /**
@@ -201,10 +197,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
* map - {<OpenLayers.Map>} The map. * map - {<OpenLayers.Map>} The map.
*/ */
removeMap: function(map) { removeMap: function(map) {
if(this.timerId != null) { this.abortTileOperations();
window.clearTimeout(this.timerId);
this.timerId = null;
}
if(this.backBufferTimerId !== null) { if(this.backBufferTimerId !== null) {
window.clearTimeout(this.backBufferTimerId); window.clearTimeout(this.backBufferTimerId);
this.backBufferTimerId = null; this.backBufferTimerId = null;
@@ -230,20 +223,20 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
* destroy() on each of them to kill circular references * destroy() on each of them to kill circular references
*/ */
clearGrid:function() { clearGrid:function() {
this.abortTileOperations();
if (this.grid) { if (this.grid) {
for(var iRow=0, len=this.grid.length; iRow<len; iRow++) { for(var iRow=0, len=this.grid.length; iRow<len; iRow++) {
var row = this.grid[iRow]; var row = this.grid[iRow];
for(var iCol=0, clen=row.length; iCol<clen; iCol++) { for(var iCol=0, clen=row.length; iCol<clen; iCol++) {
var tile = row[iCol]; var tile = row[iCol];
this.removeTileMonitoringHooks(tile); this.destroyTile(tile);
tile.destroy();
} }
} }
this.grid = []; this.grid = [];
this.gridResolution = null; this.gridResolution = null;
} }
}, },
/** /**
* APIMethod: clone * APIMethod: clone
* Create a clone of this layer * Create a clone of this layer
@@ -299,6 +292,10 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
// if grid is empty or zoom has changed, we *must* re-tile // if grid is empty or zoom has changed, we *must* re-tile
var forceReTile = !this.grid.length || zoomChanged; var forceReTile = !this.grid.length || zoomChanged;
if (forceReTile) {
this.abortTileOperations();
}
// total bounds of the tiles // total bounds of the tiles
var tilesBounds = this.getTilesBounds(); var tilesBounds = this.getTilesBounds();
@@ -371,11 +368,68 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
} }
this.initGriddedTiles(bounds); this.initGriddedTiles(bounds);
} else { } else {
this.scheduleMoveGriddedTiles(); this.moveGriddedTiles();
} }
} }
} }
}, },
/**
* Method: addTileOperation
* Adds a tile operation to the animation queue.
*
* Parameters:
* fn - {Function} The function to execute
* scope - {Object} The execution scope for the function
*/
addTileOperation: function(fn, scope) {
this.tileOperations.push({fn: fn, scope: scope});
if (!this.tileLoopId) {
this.tileLoopId = OpenLayers.Animation.start(
OpenLayers.Function.bind(this.doTileOperation, this),
null, this.div
);
}
},
/**
* Method: doTileOperation
* Executes the first tile operation from the animation queue.
*/
doTileOperation: function() {
if (this.tileOperations.length === 0) {
this.abortTileOperations();
} else {
var operation = this.tileOperations.shift();
operation.fn.call(operation.scope);
}
},
/**
* Method: abortTileOperations
* Stops the animation queue and removes all pending operations
*/
abortTileOperations: function() {
OpenLayers.Animation.stop(this.tileLoopId);
this.tileLoopId = null;
this.tileOperations = [];
},
/**
* Method: destroyTile
*
* Parameters:
* tile - {<OpenLayers.Tile>}
*/
destroyTile: function(tile) {
for (var i=this.tileOperations.length-1; i>=0; --i) {
if (this.tileOperations[i].scope === tile) {
this.tileOperations.splice(i, 1);
}
}
this.removeTileMonitoringHooks(tile);
tile.destroy();
},
/** /**
* Method: getServerResolution * Method: getServerResolution
@@ -565,24 +619,10 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
*/ */
moveByPx: function(dx, dy) { moveByPx: function(dx, dy) {
if (!this.singleTile) { if (!this.singleTile) {
this.scheduleMoveGriddedTiles(); this.moveGriddedTiles();
} }
}, },
/**
* Method: scheduleMoveGriddedTiles
* Schedule the move of tiles.
*/
scheduleMoveGriddedTiles: function() {
if (this.timerId != null) {
window.clearTimeout(this.timerId);
}
this.timerId = window.setTimeout(
this._moveGriddedTiles,
this.tileLoadingDelay
);
},
/** /**
* APIMethod: setTileSize * APIMethod: setTileSize
* Check if we are in singleTile mode and if so, set the size as a ratio * Check if we are in singleTile mode and if so, set the size as a ratio
@@ -776,7 +816,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
var layerContainerDivLeft = parseInt(this.map.layerContainerDiv.style.left); var layerContainerDivLeft = parseInt(this.map.layerContainerDiv.style.left);
var layerContainerDivTop = parseInt(this.map.layerContainerDiv.style.top); var layerContainerDivTop = parseInt(this.map.layerContainerDiv.style.top);
var tileData = [], center = this.map.getCenter();
do { do {
var row = this.grid[rowidx++]; var row = this.grid[rowidx++];
if (!row) { if (!row) {
@@ -810,6 +850,12 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
} else { } else {
tile.moveTo(tileBounds, px, false); tile.moveTo(tileBounds, px, false);
} }
var tileCenter = tileBounds.getCenterLonLat();
tileData.push({
tile: tile,
distance: Math.pow(tileCenter.lon - center.lon, 2) +
Math.pow(tileCenter.lat - center.lat, 2)
});
tileoffsetlon += tilelon; tileoffsetlon += tilelon;
tileoffsetx += this.tileSize.w; tileoffsetx += this.tileSize.w;
@@ -828,7 +874,12 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
this.gridResolution = this.getServerResolution(); this.gridResolution = this.getServerResolution();
//now actually draw the tiles //now actually draw the tiles
this.spiralTileLoad(); tileData.sort(function(a, b) {
return a.distance - b.distance;
});
for (var i=0, ii=tileData.length; i<ii; ++i) {
tileData[i].tile.draw();
}
}, },
/** /**
@@ -843,79 +894,6 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
return this.maxExtent; return this.maxExtent;
}, },
/**
* Method: spiralTileLoad
* Starts at the top right corner of the grid and proceeds in a spiral
* towards the center, adding tiles one at a time to the beginning of a
* queue.
*
* Once all the grid's tiles have been added to the queue, we go back
* and iterate through the queue (thus reversing the spiral order from
* outside-in to inside-out), calling draw() on each tile.
*/
spiralTileLoad: function() {
var tileQueue = [];
var directions = ["right", "down", "left", "up"];
var iRow = 0;
var iCell = -1;
var direction = OpenLayers.Util.indexOf(directions, "right");
var directionsTried = 0;
while( directionsTried < directions.length) {
var testRow = iRow;
var testCell = iCell;
switch (directions[direction]) {
case "right":
testCell++;
break;
case "down":
testRow++;
break;
case "left":
testCell--;
break;
case "up":
testRow--;
break;
}
// if the test grid coordinates are within the bounds of the
// grid, get a reference to the tile.
var tile = null;
if ((testRow < this.grid.length) && (testRow >= 0) &&
(testCell < this.grid[0].length) && (testCell >= 0)) {
tile = this.grid[testRow][testCell];
}
if ((tile != null) && (!tile.queued)) {
//add tile to beginning of queue, mark it as queued.
tileQueue.unshift(tile);
tile.queued = true;
//restart the directions counter and take on the new coords
directionsTried = 0;
iRow = testRow;
iCell = testCell;
} else {
//need to try to load a tile in a different direction
direction = (direction + 1) % 4;
directionsTried++;
}
}
// now we go through and draw the tiles in forward order
for(var i=0, len=tileQueue.length; i<len; i++) {
var tile = tileQueue[i];
tile.draw();
//mark tile as unqueued for the next time (since tiles are reused)
tile.queued = false;
}
},
/** /**
* APIMethod: addTile * APIMethod: addTile
* Create a tile, initialize it, and add it to the layer div. * Create a tile, initialize it, and add it to the layer div.
@@ -928,8 +906,17 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
* {<OpenLayers.Tile>} The added OpenLayers.Tile * {<OpenLayers.Tile>} The added OpenLayers.Tile
*/ */
addTile: function(bounds, position) { addTile: function(bounds, position) {
var that = this;
var options = OpenLayers.Util.extend({
draw: function() {
// clear immediately
this.clear();
// draw in an animation frame - can be aborted
that.addTileOperation(that.tileClass.prototype.draw, this);
}
}, this.tileOptions);
return new this.tileClass(this, position, bounds, null, return new this.tileClass(this, position, bounds, null,
this.tileSize, this.tileOptions); this.tileSize, options);
}, },
/** /**
@@ -993,37 +980,32 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
* Method: moveGriddedTiles * Method: moveGriddedTiles
*/ */
moveGriddedTiles: function() { moveGriddedTiles: function() {
var shifted = true;
var buffer = this.buffer || 1; var buffer = this.buffer || 1;
var scale = this.getResolutionScale(); var scale = this.getResolutionScale();
var tlLayer = this.grid[0][0].position.clone(); while(true) {
tlLayer.x *= scale; var tlLayer = this.grid[0][0].position.clone();
tlLayer.y *= scale; tlLayer.x *= scale;
tlLayer = tlLayer.add(parseInt(this.div.style.left, 10), tlLayer.y *= scale;
parseInt(this.div.style.top, 10)); tlLayer = tlLayer.add(parseInt(this.div.style.left, 10),
var offsetX = parseInt(this.map.layerContainerDiv.style.left); parseInt(this.div.style.top, 10));
var offsetY = parseInt(this.map.layerContainerDiv.style.top); var offsetX = parseInt(this.map.layerContainerDiv.style.left);
var tlViewPort = tlLayer.add(offsetX, offsetY); var offsetY = parseInt(this.map.layerContainerDiv.style.top);
var tileSize = { var tlViewPort = tlLayer.add(offsetX, offsetY);
w: this.tileSize.w * scale, var tileSize = {
h: this.tileSize.h * scale w: this.tileSize.w * scale,
}; h: this.tileSize.h * scale
if (tlViewPort.x > -tileSize.w * (buffer - 1)) { };
this.shiftColumn(true); if (tlViewPort.x > -tileSize.w * (buffer - 1)) {
} else if (tlViewPort.x < -tileSize.w * buffer) { this.shiftColumn(true);
this.shiftColumn(false); } else if (tlViewPort.x < -tileSize.w * buffer) {
} else if (tlViewPort.y > -tileSize.h * (buffer - 1)) { this.shiftColumn(false);
this.shiftRow(true); } else if (tlViewPort.y > -tileSize.h * (buffer - 1)) {
} else if (tlViewPort.y < -tileSize.h * buffer) { this.shiftRow(true);
this.shiftRow(false); } else if (tlViewPort.y < -tileSize.h * buffer) {
} else { this.shiftRow(false);
shifted = false; } else {
} break;
if (shifted) { }
// we may have other row or columns to shift, schedule it
// with a setTimeout, to give the user a chance to sneak
// in moveTo's
this.timerId = window.setTimeout(this._moveGriddedTiles, 0);
} }
}, },
@@ -1113,8 +1095,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
var row = this.grid.pop(); var row = this.grid.pop();
for (var i=0, l=row.length; i<l; i++) { for (var i=0, l=row.length; i<l; i++) {
var tile = row[i]; var tile = row[i];
this.removeTileMonitoringHooks(tile); this.destroyTile(tile);
tile.destroy();
} }
} }
@@ -1123,8 +1104,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
for (var i=0, l=this.grid.length; i<l; i++) { for (var i=0, l=this.grid.length; i<l; i++) {
var row = this.grid[i]; var row = this.grid[i];
var tile = row.pop(); var tile = row.pop();
this.removeTileMonitoringHooks(tile); this.destroyTile(tile);
tile.destroy();
} }
} }
}, },
+5
View File
@@ -4,6 +4,11 @@
<script type="text/javascript">window.alert = oldAlert;</script> <script type="text/javascript">window.alert = oldAlert;</script>
<script src="../OLLoader.js"></script> <script src="../OLLoader.js"></script>
<script type="text/javascript"> <script type="text/javascript">
// turn off animation frame handling, so we can check img urls in tests
OpenLayers.Layer.Grid.prototype.addTileOperation = function(fn, scope) {
fn.call(scope);
};
var isMozilla = (navigator.userAgent.indexOf("compatible") == -1); var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
var layer; var layer;
+11 -8
View File
@@ -2,6 +2,12 @@
<head> <head>
<script src="../OLLoader.js"></script> <script src="../OLLoader.js"></script>
<script type="text/javascript"> <script type="text/javascript">
// turn off animation frame handling, so we can check img urls in tests
var origAddTileOperation = OpenLayers.Layer.Grid.prototype.addTileOperation;
OpenLayers.Layer.Grid.prototype.addTileOperation = function(fn, scope) {
fn.call(scope);
};
var isMozilla = (navigator.userAgent.indexOf("compatible") == -1); var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
var layer; var layer;
@@ -189,7 +195,7 @@
function test_Layer_Grid_moveTo(t) { function test_Layer_Grid_moveTo(t) {
t.plan(14); t.plan(13);
var map = new OpenLayers.Map('map'); var map = new OpenLayers.Map('map');
layer = new OpenLayers.Layer.WMS(name, url, params); layer = new OpenLayers.Layer.WMS(name, url, params);
@@ -214,7 +220,7 @@
g_WhichFunc = "InitGridded"; g_WhichFunc = "InitGridded";
g_Bounds = bounds; g_Bounds = bounds;
}; };
layer._moveGriddedTiles = function() { layer.moveGriddedTiles = function() {
g_WhichFunc = "MoveGridded"; g_WhichFunc = "MoveGridded";
g_Bounds = layer.map.getExtent(); g_Bounds = layer.map.getExtent();
}; };
@@ -307,7 +313,7 @@
//gridded //gridded
layer.grid = [ [ {} ] ]; layer.grid = [ [ {position: new OpenLayers.Pixel(0,0)} ] ];
layer.singleTile = false; layer.singleTile = false;
//regular move //regular move
@@ -315,11 +321,8 @@
tilesBounds = new OpenLayers.Bounds(10,10,120,120); tilesBounds = new OpenLayers.Bounds(10,10,120,120);
g_WhichFunc = null; g_WhichFunc = null;
layer.moveTo(null, zoomChanged); layer.moveTo(null, zoomChanged);
t.eq(g_WhichFunc, null, "moveGriddedTiles is delayed - not called yet"); t.eq(g_WhichFunc, "MoveGridded", "if tiles not drastically out of bounds, we call moveGriddedTile()");
t.delay_call(0.2, function() { t.ok(g_Bounds.equals(b), "if tiles not drastically out of bounds, we call moveGriddedTile() with correct bounds");
t.ok(g_WhichFunc == "MoveGridded", "if tiles not drastically out of bounds, we call moveGriddedTile()");
t.ok(g_Bounds.equals(b), "if tiles not drastically out of bounds, we call moveGriddedTile() with correct bounds");
});
// drastic pan // drastic pan
clearTestBounds(); clearTestBounds();
+5
View File
@@ -6,6 +6,11 @@
<script src="../OLLoader.js"></script> <script src="../OLLoader.js"></script>
<script type="text/javascript"> <script type="text/javascript">
// turn off animation frame handling, so we can check img urls in tests
OpenLayers.Layer.Grid.prototype.addTileOperation = function(fn, scope) {
fn.call(scope);
};
var isMozilla = (navigator.userAgent.indexOf("compatible") == -1); var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
var layer; var layer;
+5
View File
@@ -5,6 +5,11 @@
<script type="text/javascript">window.alert = oldAlert;</script> <script type="text/javascript">window.alert = oldAlert;</script>
<script src="../OLLoader.js"></script> <script src="../OLLoader.js"></script>
<script type="text/javascript"> <script type="text/javascript">
// turn off animation frame handling, so we can check img urls in tests
OpenLayers.Layer.Grid.prototype.addTileOperation = function(fn, scope) {
fn.call(scope);
};
var isMozilla = (navigator.userAgent.indexOf("compatible") == -1); var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
var layer; var layer;
+5
View File
@@ -2,6 +2,11 @@
<head> <head>
<script src="../OLLoader.js"></script> <script src="../OLLoader.js"></script>
<script type="text/javascript"> <script type="text/javascript">
// turn off animation frame handling, so we can check img urls in tests
OpenLayers.Layer.Grid.prototype.addTileOperation = function(fn, scope) {
fn.call(scope);
};
var isMozilla = (navigator.userAgent.indexOf("compatible") == -1); var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
var layer; var layer;
@@ -5,6 +5,11 @@
<script src="../../../../lib/deprecated.js"></script> <script src="../../../../lib/deprecated.js"></script>
<script type="text/javascript"> <script type="text/javascript">
// turn off animation frame handling, so we can check img urls in tests
OpenLayers.Layer.Grid.prototype.addTileOperation = function(fn, scope) {
fn.call(scope);
};
var isMozilla = (navigator.userAgent.indexOf("compatible") == -1); var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
var layer; var layer;