diff --git a/code/boot.js b/code/boot.js index d038bc81..850787fb 100644 --- a/code/boot.js +++ b/code/boot.js @@ -188,8 +188,8 @@ window.setupMap = function() { // map update status handling & update map hooks // ensures order of calls - map.on('movestart zoomstart', function() { window.mapRunsUserAction = true; window.requests.abort(); window.startRefreshTimeout(-1); }); - map.on('moveend zoomend', function() { window.mapRunsUserAction = false; window.startRefreshTimeout(ON_MOVE_REFRESH*1000); }); + map.on('movestart', function() { window.mapRunsUserAction = true; window.requests.abort(); window.startRefreshTimeout(-1); }); + map.on('moveend', function() { window.mapRunsUserAction = false; window.startRefreshTimeout(ON_MOVE_REFRESH*1000); }); window.addResumeFunction(function() { window.startRefreshTimeout(ON_MOVE_REFRESH*1000); }); diff --git a/code/map_data_request.js b/code/map_data_request.js index d1ae7440..86068a0e 100644 --- a/code/map_data_request.js +++ b/code/map_data_request.js @@ -12,12 +12,13 @@ window.MapDataRequest = function() { this.activeRequestCount = 0; this.requestedTiles = {}; + this.moving = false; + // no more than this many requests in parallel this.MAX_REQUESTS = 4; // no more than this many tiles in one request - this.MAX_TILES_PER_REQUEST = 16; - // but don't create more requests if it would make less than this per request - this.MIN_TILES_PER_REQUEST = 4; + // (the stock site seems to have no limit - i've seen ~100 for L3+ portals and a maximised browser window) + this.MAX_TILES_PER_REQUEST = 32; // number of times to retty a tile after a 'bad' error (i.e. not a timeout) this.MAX_TILE_RETRIES = 3; @@ -38,13 +39,12 @@ window.MapDataRequest.prototype.start = function() { var savedContext = this; // setup idle resume function - window.addResumeFunction ( function() { savedContext.setStatus('refreshing'); savedContext.refreshOnTimeout(savedContext.IDLE_RESUME_REFRESH); } ); + window.addResumeFunction ( function() { console.log('refresh map idle resume'); savedContext.setStatus('refreshing'); savedContext.refreshOnTimeout(savedContext.IDLE_RESUME_REFRESH); } ); - // and map move callback - window.map.on('moveend', function() { savedContext.setStatus('refreshing'); savedContext.refreshOnTimeout(savedContext.MOVE_REFRESH); } ); + // and map move start/end callbacks + window.map.on('movestart', this.mapMoveStart, this); + window.map.on('moveend', this.mapMoveEnd, this); - // and on movestart, we clear the request queue - window.map.on('movestart', function() { savedContext.setStatus('paused'); savedContext.clearQueue(); } ); // then set a timeout to start the first refresh this.refreshOnTimeout (this.STARTUP_REFRESH); @@ -52,14 +52,40 @@ window.MapDataRequest.prototype.start = function() { } -window.MapDataRequest.prototype.refreshOnTimeout = function(seconds) { + +window.MapDataRequest.prototype.mapMoveStart = function() { + console.log('refresh map movestart'); + + this.moving=true; + + this.clearQueue(); + + this.setStatus('paused'); + this.clearTimeout(); + +} + +window.MapDataRequest.prototype.mapMoveEnd = function() { + console.log('refresh map moveend'); + + this.moving=false; + + this.setStatus('refreshing'); + this.refreshOnTimeout(this.MOVE_REFRESH); +} + + +window.MapDataRequest.prototype.clearTimeout = function() { if (this.timer) { console.log("cancelling existing map refresh timer"); clearTimeout(this.timer); this.timer = undefined; } +} +window.MapDataRequest.prototype.refreshOnTimeout = function(seconds) { + this.clearTimeout(); console.log("starting map refresh in "+seconds+" seconds"); @@ -169,6 +195,11 @@ window.MapDataRequest.prototype.processRequestQueue = function(isFirstPass) { // if nothing left in the queue, end the render. otherwise, send network requests if (Object.keys(this.tileBounds).length == 0) { + + // if map is being dragged, just return without any end of map processing + if (this.moving) + return; + this.render.endRenderPass(); var endTime = new Date().getTime(); @@ -178,7 +209,6 @@ window.MapDataRequest.prototype.processRequestQueue = function(isFirstPass) { window.runHooks ('mapDataRefreshEnd', {}); - if (!window.isIdle()) { // refresh timer based on time to run this pass, with a minimum of REFRESH seconds var refreshTimer = Math.max(this.REFRESH, duration*this.FETCH_TO_REFRESH_FACTOR); @@ -192,38 +222,37 @@ window.MapDataRequest.prototype.processRequestQueue = function(isFirstPass) { } // create a list of tiles that aren't requested over the network - var pendingTiles = {}; + var pendingTiles = []; for (var id in this.tileBounds) { if (!(id in this.requestedTiles) ) { - pendingTiles[id] = true; + pendingTiles.push(id); } } - console.log("- request state: "+Object.keys(this.requestedTiles).length+" tiles in "+this.activeRequestCount+" active requests, "+Object.keys(pendingTiles).length+" tiles queued"); + console.log("- request state: "+Object.keys(this.requestedTiles).length+" tiles in "+this.activeRequestCount+" active requests, "+pendingTiles.length+" tiles queued"); - var requestTileCount = Math.min(this.MAX_TILES_PER_REQUEST,Math.max(this.MIN_TILES_PER_REQUEST, Object.keys(pendingTiles).length/this.MAX_REQUESTS)); + var requestBuckets = this.MAX_REQUESTS - this.activeRequestCount; + if (pendingTiles.length > 0 && requestBuckets > 0) { - while (this.activeRequestCount < this.MAX_REQUESTS && Object.keys(pendingTiles).length > 0) { - // let's distribute the requests evenly throughout the pending list. + var lastTileIndex = Math.min(requestBuckets*this.MAX_TILES_PER_REQUEST, pendingTiles.length); - var pendingTilesArray = Object.keys(pendingTiles); + for (var bucket=0; bucket 0) { + console.log("-- new request: "+tiles.length+" tiles"); + this.sendTileRequest(tiles); } } - - console.log("-- asking for "+tiles.length+" tiles in one request"); - this.sendTileRequest(tiles); } + // update status var pendingTileCount = this.requestedTileCount - (this.successTileCount+this.failedTileCount+this.staleTileCount); var longText = 'Tiles: ' + this.cachedTileCount + ' cached, ' + @@ -262,10 +291,11 @@ window.MapDataRequest.prototype.sendTileRequest = function(tiles) { var savedThis = this; - window.requests.add (window.postAjax('getThinnedEntitiesV4', data, + // NOTE: don't add the request with window.request.add, as we don't want the abort handling to apply to map data any more + window.postAjax('getThinnedEntitiesV4', data, function(data, textStatus, jqXHR) { savedThis.handleResponse (data, tiles, true); }, // request successful callback function() { savedThis.handleResponse (undefined, tiles, false); } // request failed callback - )); + ); } window.MapDataRequest.prototype.requeueTile = function(id, error) { @@ -300,6 +330,15 @@ window.MapDataRequest.prototype.requeueTile = function(id, error) { } else { // if false, was a 'timeout', so unlimited retries (as the stock site does) this.debugTiles.setState (id, 'retrying'); + + // FIXME? it's nice to move retried tiles to the end of the request queue. however, we don't actually have a + // proper queue, just an object with guid as properties. Javascript standards don't guarantee the order of properties + // within an object. however, all current browsers do keep property order, and new properties are added at the end. + // therefore, delete and re-add the requeued tile and it will be added to the end of the queue + var boundsData = this.tileBounds[id]; + delete this.tileBounds[id]; + this.tileBounds[id] = boundsData; + } } // else the tile wasn't currently wanted (an old non-cancelled request) - ignore } diff --git a/code/status_bar.js b/code/status_bar.js index 6e007dfa..cd034767 100644 --- a/code/status_bar.js +++ b/code/status_bar.js @@ -32,7 +32,7 @@ window.renderUpdateStatus = function() { t += ''+status.short+''; if (status.progress !== undefined) - t += ' '+Math.round(status.progress*100)+'%'; + t += ' '+Math.floor(status.progress*100)+'%'; } else { // no mapDataRequest object - no status known t += '...unknown...';