245 lines
8.0 KiB
JavaScript
245 lines
8.0 KiB
JavaScript
// MAP DATA //////////////////////////////////////////////////////////
|
||
// these functions handle how and which entities are displayed on the
|
||
// map. They also keep them up to date, unless interrupted by user
|
||
// action.
|
||
|
||
// cache for data tiles. indexed by the query key (qk)
|
||
window.cache = undefined;
|
||
window.render = undefined;
|
||
window.debugTiles = undefined;
|
||
|
||
|
||
// due to the cache (and race conditions in the server) - and now also oddities in the returned data
|
||
// we need to remember the deleted entities list across multiple requests
|
||
window._deletedEntityGuid = {}
|
||
|
||
// requests map data for current viewport. For details on how this
|
||
// works, refer to the description in “MAP DATA REQUEST CALCULATORS”
|
||
window.requestData = function() {
|
||
if (window.cache === undefined) window.cache = new DataCache();
|
||
if (window.render === undefined) window.render = new Render();
|
||
|
||
|
||
console.log('refreshing data');
|
||
requests.abort();
|
||
window.statusTotalMapTiles = 0;
|
||
window.statusCachedMapTiles = 0;
|
||
window.statusSuccessMapTiles = 0;
|
||
window.statusStaleMapTiles = 0;
|
||
window.statusErrorMapTiles = 0;
|
||
|
||
// clear the list of returned deleted entities
|
||
window._deletedEntityGuid = {}
|
||
|
||
if (!debugTiles) debugTiles = new RenderDebugTiles();
|
||
debugTiles.reset();
|
||
|
||
cache.expire();
|
||
|
||
|
||
render.startRenderPass();
|
||
|
||
|
||
//a limit on the number of map tiles to be pulled in a single request
|
||
var MAX_TILES_PER_BUCKET = 18;
|
||
// the number of separate buckets. more can be created if the size exceeds MAX_TILES_PER_BUCKET
|
||
var BUCKET_COUNT = 4;
|
||
|
||
var bounds = clampLatLngBounds(map.getBounds());
|
||
|
||
//we query the server as if the zoom level was this. it may not match the actual map zoom level
|
||
var z = getPortalDataZoom();
|
||
console.log('requesting data tiles at zoom '+z+' (L'+getMinPortalLevelForZoom(z)+'+ portals), map zoom is '+map.getZoom());
|
||
|
||
var x1 = lngToTile(bounds.getWest(), z);
|
||
var x2 = lngToTile(bounds.getEast(), z);
|
||
var y1 = latToTile(bounds.getNorth(), z);
|
||
var y2 = latToTile(bounds.getSouth(), z);
|
||
|
||
// will group requests by second-last quad-key quadrant
|
||
tiles = {};
|
||
fullBucketCount = 0;
|
||
|
||
var requestTileCount = 0;
|
||
|
||
// y goes from left to right
|
||
for (var y = y1; y <= y2; y++) {
|
||
// x goes from bottom to top(?)
|
||
for (var x = x1; x <= x2; x++) {
|
||
var tile_id = pointToTileId(z, x, y);
|
||
var latNorth = tileToLat(y,z);
|
||
var latSouth = tileToLat(y+1,z);
|
||
var lngWest = tileToLng(x,z);
|
||
var lngEast = tileToLng(x+1,z);
|
||
|
||
debugTiles.create(tile_id,[[latSouth,lngWest],[latNorth,lngEast]]);
|
||
window.statusTotalMapTiles++;
|
||
|
||
// TODO?: if the selected portal is in this tile, always fetch the data
|
||
if (cache.isFresh(tile_id)) {
|
||
var tiledata = cache.get(tile_id);
|
||
|
||
render.processTileData (tiledata);
|
||
|
||
debugTiles.setColour(tile_id,'#0f0','#ff0');
|
||
window.statusCachedMapTiles++;
|
||
} else {
|
||
// group requests into buckets based on the tile count retrieved via the network.
|
||
var bucket = requestTileCount % BUCKET_COUNT;
|
||
|
||
if (!tiles[bucket]) {
|
||
//create empty bucket
|
||
tiles[bucket] = [];
|
||
}
|
||
else if(tiles[bucket].length >= MAX_TILES_PER_BUCKET) {
|
||
//too many items in bucket. rename it, and create a new empty one
|
||
tiles[bucket+'_'+fullBucketCount] = tiles[bucket];
|
||
fullBucketCount++;
|
||
tiles[bucket] = [];
|
||
}
|
||
|
||
requestTileCount++;
|
||
|
||
var boundsParam = generateBoundsParams(
|
||
tile_id,
|
||
latSouth,
|
||
lngWest,
|
||
latNorth,
|
||
lngEast
|
||
);
|
||
|
||
tiles[bucket].push(boundsParam);
|
||
|
||
debugTiles.setColour(tile_id,'#00f','#000');
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
|
||
// send ajax requests
|
||
console.log('requesting '+requestTileCount+' tiles in '+Object.keys(tiles).length+' requests');
|
||
$.each(tiles, function(ind, tls) {
|
||
// sort the tiles by the cache age - oldest/missing first. the server often times out requests and the first listed
|
||
// are more likely to succeed. this will ensure we're more likely to have fresh data
|
||
tls.sort(function(a,b) {
|
||
var timea = cache.getTime(a.qk);
|
||
var timeb = cache.getTime(b.qk);
|
||
if (timea < timeb) return -1;
|
||
if (timea > timeb) return 1;
|
||
return 0;
|
||
});
|
||
|
||
data = { };
|
||
data.boundsParamsList = tls;
|
||
// keep a list of tile_ids with each request. in the case of a server error, we can try and use cached tiles if available
|
||
var tile_ids = []
|
||
$.each(tls,function(i,req) { tile_ids.push(req.qk); });
|
||
window.requests.add(window.postAjax('getThinnedEntitiesV4', data, function(data, textStatus, jqXHR) { window.handleDataResponse(data,false,tile_ids); }, function() { window.handleFailedRequest(tile_ids); }));
|
||
});
|
||
|
||
if(tiles.length == 0) {
|
||
// if everything was cached, we immediately end the render pass
|
||
// otherwise, the render pass will be ended in the callbacks
|
||
render.endRenderPass();
|
||
}
|
||
}
|
||
|
||
// Handle failed map data request
|
||
window.handleFailedRequest = function(tile_ids) {
|
||
console.log('request failed: tiles '+tile_ids.join(','));
|
||
|
||
var cachedData = { result: { map: {} } };
|
||
$.each(tile_ids, function(ind,tile_id) {
|
||
var cached = cache.get(tile_id);
|
||
if (cached) {
|
||
// we have stale cached data - use it
|
||
cachedData.result.map[tile_id] = cached;
|
||
debugTiles.setColour(tile_id,'#800','#ff0');
|
||
console.log('(using stale cache entry for map tile '+tile_id+')');
|
||
window.statusStaleMapTiles++;
|
||
} else {
|
||
// no cached data
|
||
debugTiles.setColour(tile_id,'#800','#f00');
|
||
window.statusErrorMapTiles++;
|
||
}
|
||
});
|
||
if(Object.keys(cachedData.result.map).length > 0) {
|
||
handleDataResponse(cachedData, true);
|
||
}
|
||
|
||
if(requests.isLastRequest('getThinnedEntitiesV4')) {
|
||
render.endRenderPass();
|
||
// var leftOverPortals = portalRenderLimit.mergeLowLevelPortals(null);
|
||
// handlePortalsRender(leftOverPortals);
|
||
}
|
||
runHooks('requestFinished', {success: false});
|
||
}
|
||
|
||
// works on map data response and ensures entities are drawn/updated.
|
||
window.handleDataResponse = function(data, fromCache, tile_ids) {
|
||
// remove from active ajax queries list
|
||
if(!data || !data.result) {
|
||
window.failedRequestCount++;
|
||
console.warn(data);
|
||
handleFailedRequest(tile_ids);
|
||
return;
|
||
}
|
||
|
||
var m = data.result.map;
|
||
// defer rendering of portals because there is no z-index in SVG.
|
||
// this means that what’s rendered last ends up on top. While the
|
||
// portals can be brought to front, this costs extra time. They need
|
||
// to be in the foreground, or they cannot be clicked. See
|
||
// https://github.com/Leaflet/Leaflet/issues/185
|
||
var ppp = {};
|
||
var p2f = {};
|
||
$.each(m, function(qk, val) {
|
||
|
||
// if this request wasn't from the cache, check it's status. store in the cache if good
|
||
// for debugging, we set the debug tile colours. cached tiles have colours set elsewhere so are not set here
|
||
if (!fromCache) {
|
||
|
||
if('error' in val) {
|
||
console.log('map data tile '+qk+' response error: '+val.error);
|
||
|
||
// try to use data in the cache, even if it's stale
|
||
var cacheVal = cache.get(qk);
|
||
|
||
if (!cacheVal) {
|
||
debugTiles.setColour(qk, '#f00','#f00');
|
||
// no data in cache for this tile. continue processing - it's possible it also has some valid data
|
||
window.statusErrorMapTiles++;
|
||
} else {
|
||
// stale cache entry exists - use it
|
||
val = cacheVal;
|
||
debugTiles.setColour(qk, '#f00','#ff0');
|
||
console.log('(using stale cache entry for map tile '+qk+')');
|
||
window.statusStaleMapTiles++;
|
||
}
|
||
} else {
|
||
// not an error - store this tile into the cache
|
||
cache.store(qk,val);
|
||
debugTiles.setColour(qk, '#0f0','#0f0');
|
||
window.statusSuccessMapTiles++;
|
||
}
|
||
|
||
}
|
||
|
||
render.processTileData(val);
|
||
|
||
});
|
||
|
||
|
||
resolvePlayerNames();
|
||
renderUpdateStatus();
|
||
|
||
if(requests.isLastRequest('getThinnedEntitiesV4')) {
|
||
render.endRenderPass();
|
||
// var leftOverPortals = portalRenderLimit.mergeLowLevelPortals(null);
|
||
// handlePortalsRender(leftOverPortals);
|
||
}
|
||
runHooks('requestFinished', {success: true});
|
||
}
|
||
|