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

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="apple-mobile-web-app-capable" content="yes">
<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">
<script src="../lib/OpenLayers.js"></script>
</head>
<body>
<h1 id="title">City of Vienna WMTS for Desktop and Mobile Devices</h1>
@@ -22,6 +22,7 @@
functionality and uses the Geolocate control.
</p>
<div id="map"></div>
<script src="../lib/OpenLayers.js"></script>
<script src="mobile-wmts-vienna.js"></script>
</body>
</html>

View File

@@ -152,7 +152,6 @@ var map;
var defaults = {
requestEncoding: "REST",
matrixSet: "google3857",
buffer: 4,
attribution: 'Datenquelle: Stadt Wien - <a href="http://data.wien.gv.at">data.wien.gv.at</a>'
};
var doc = request.responseText,
@@ -182,7 +181,6 @@ var map;
requestEncoding: "REST",
matrixSet: "google3857",
tileFullExtent: extent,
buffer: 4,
attribution: 'Datenquelle: Stadt Wien - <a href="http://data.wien.gv.at">data.wien.gv.at</a>'
};
fmzk = new OpenLayers.Layer.WMTS(OpenLayers.Util.applyDefaults({

View File

@@ -97,13 +97,6 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
*/
numLoadingTiles: 0,
/**
* APIProperty: tileLoadingDelay
* {Integer} - Number of milliseconds before we shift and load
* tiles. Default is 100.
*/
tileLoadingDelay: 100,
/**
* Property: serverResolutions
* {Array(Number}} This property is documented in subclasses as
@@ -112,10 +105,16 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
serverResolutions: null,
/**
* Property: timerId
* {Number} - The id of the tileLoadingDelay timer.
* Property: tileLoopId
* {Number} - The id of the <doTileOperation> animation.
*/
timerId: null,
tileLoopId: null,
/**
* Property: tileOperations
* {Array(Object)} Pending tile operations.
*/
tileOperations: null,
/**
* Property: backBuffer
@@ -187,10 +186,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this,
arguments);
this.grid = [];
this._moveGriddedTiles = OpenLayers.Function.bind(
this.moveGriddedTiles, this
);
this.tileOperations = [];
},
/**
@@ -201,10 +197,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
* map - {<OpenLayers.Map>} The map.
*/
removeMap: function(map) {
if(this.timerId != null) {
window.clearTimeout(this.timerId);
this.timerId = null;
}
this.abortTileOperations();
if(this.backBufferTimerId !== null) {
window.clearTimeout(this.backBufferTimerId);
this.backBufferTimerId = null;
@@ -230,13 +223,13 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
* destroy() on each of them to kill circular references
*/
clearGrid:function() {
this.abortTileOperations();
if (this.grid) {
for(var iRow=0, len=this.grid.length; iRow<len; iRow++) {
var row = this.grid[iRow];
for(var iCol=0, clen=row.length; iCol<clen; iCol++) {
var tile = row[iCol];
this.removeTileMonitoringHooks(tile);
tile.destroy();
this.destroyTile(tile);
}
}
this.grid = [];
@@ -300,6 +293,10 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
// if grid is empty or zoom has changed, we *must* re-tile
var forceReTile = !this.grid.length || zoomChanged;
if (forceReTile) {
this.abortTileOperations();
}
// total bounds of the tiles
var tilesBounds = this.getTilesBounds();
@@ -371,12 +368,69 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
}
this.initGriddedTiles(bounds);
} 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
* Return the closest highest server-supported resolution. Throw an
@@ -565,24 +619,10 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
*/
moveByPx: function(dx, dy) {
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
* 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 layerContainerDivTop = parseInt(this.map.layerContainerDiv.style.top);
var tileData = [], center = this.map.getCenter();
do {
var row = this.grid[rowidx++];
if (!row) {
@@ -810,6 +850,12 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
} else {
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;
tileoffsetx += this.tileSize.w;
@@ -828,7 +874,12 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
this.gridResolution = this.getServerResolution();
//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;
},
/**
* 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
* 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
*/
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,
this.tileSize, this.tileOptions);
this.tileSize, options);
},
/**
@@ -993,37 +980,32 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
* Method: moveGriddedTiles
*/
moveGriddedTiles: function() {
var shifted = true;
var buffer = this.buffer || 1;
var scale = this.getResolutionScale();
var tlLayer = this.grid[0][0].position.clone();
tlLayer.x *= scale;
tlLayer.y *= scale;
tlLayer = tlLayer.add(parseInt(this.div.style.left, 10),
parseInt(this.div.style.top, 10));
var offsetX = parseInt(this.map.layerContainerDiv.style.left);
var offsetY = parseInt(this.map.layerContainerDiv.style.top);
var tlViewPort = tlLayer.add(offsetX, offsetY);
var tileSize = {
w: this.tileSize.w * scale,
h: this.tileSize.h * scale
};
if (tlViewPort.x > -tileSize.w * (buffer - 1)) {
this.shiftColumn(true);
} else if (tlViewPort.x < -tileSize.w * buffer) {
this.shiftColumn(false);
} else if (tlViewPort.y > -tileSize.h * (buffer - 1)) {
this.shiftRow(true);
} else if (tlViewPort.y < -tileSize.h * buffer) {
this.shiftRow(false);
} else {
shifted = false;
}
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);
while(true) {
var tlLayer = this.grid[0][0].position.clone();
tlLayer.x *= scale;
tlLayer.y *= scale;
tlLayer = tlLayer.add(parseInt(this.div.style.left, 10),
parseInt(this.div.style.top, 10));
var offsetX = parseInt(this.map.layerContainerDiv.style.left);
var offsetY = parseInt(this.map.layerContainerDiv.style.top);
var tlViewPort = tlLayer.add(offsetX, offsetY);
var tileSize = {
w: this.tileSize.w * scale,
h: this.tileSize.h * scale
};
if (tlViewPort.x > -tileSize.w * (buffer - 1)) {
this.shiftColumn(true);
} else if (tlViewPort.x < -tileSize.w * buffer) {
this.shiftColumn(false);
} else if (tlViewPort.y > -tileSize.h * (buffer - 1)) {
this.shiftRow(true);
} else if (tlViewPort.y < -tileSize.h * buffer) {
this.shiftRow(false);
} else {
break;
}
}
},
@@ -1113,8 +1095,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
var row = this.grid.pop();
for (var i=0, l=row.length; i<l; i++) {
var tile = row[i];
this.removeTileMonitoringHooks(tile);
tile.destroy();
this.destroyTile(tile);
}
}
@@ -1123,8 +1104,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
for (var i=0, l=this.grid.length; i<l; i++) {
var row = this.grid[i];
var tile = row.pop();
this.removeTileMonitoringHooks(tile);
tile.destroy();
this.destroyTile(tile);
}
}
},

View File

@@ -4,6 +4,11 @@
<script type="text/javascript">window.alert = oldAlert;</script>
<script src="../OLLoader.js"></script>
<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 layer;

View File

@@ -2,6 +2,12 @@
<head>
<script src="../OLLoader.js"></script>
<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 layer;
@@ -189,7 +195,7 @@
function test_Layer_Grid_moveTo(t) {
t.plan(14);
t.plan(13);
var map = new OpenLayers.Map('map');
layer = new OpenLayers.Layer.WMS(name, url, params);
@@ -214,7 +220,7 @@
g_WhichFunc = "InitGridded";
g_Bounds = bounds;
};
layer._moveGriddedTiles = function() {
layer.moveGriddedTiles = function() {
g_WhichFunc = "MoveGridded";
g_Bounds = layer.map.getExtent();
};
@@ -307,7 +313,7 @@
//gridded
layer.grid = [ [ {} ] ];
layer.grid = [ [ {position: new OpenLayers.Pixel(0,0)} ] ];
layer.singleTile = false;
//regular move
@@ -315,11 +321,8 @@
tilesBounds = new OpenLayers.Bounds(10,10,120,120);
g_WhichFunc = null;
layer.moveTo(null, zoomChanged);
t.eq(g_WhichFunc, null, "moveGriddedTiles is delayed - not called yet");
t.delay_call(0.2, function() {
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");
});
t.eq(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
clearTestBounds();

View File

@@ -6,6 +6,11 @@
<script src="../OLLoader.js"></script>
<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 layer;

View File

@@ -5,6 +5,11 @@
<script type="text/javascript">window.alert = oldAlert;</script>
<script src="../OLLoader.js"></script>
<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 layer;

View File

@@ -2,6 +2,11 @@
<head>
<script src="../OLLoader.js"></script>
<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 layer;

View File

@@ -5,6 +5,11 @@
<script src="../../../../lib/deprecated.js"></script>
<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 layer;