Skip to content

Commit

Permalink
feat: Allow sharing queues between tile layers.
Browse files Browse the repository at this point in the history
Although you could sort of share the fetch queue between tile layers, it
wouldn't really share properly without a custom needed function.  This
explicitly supports sharing a fetch queue between tile layers.

This allows, for instance, multiple layers that access the same server
to have a single fetch queue so that the number of requests against the
server is limited (usually to 6) rather than that number multiplied by
the number of layers.
  • Loading branch information
manthey committed Jul 8, 2021
1 parent a43e385 commit 0845217
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 15 deletions.
15 changes: 8 additions & 7 deletions src/tile.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ var $ = require('jquery');

/**
* This class defines the raw interface for a "tile" on a map. A tile is
* defined as a rectangular section of a map. The base implementation
* is independent of the actual content of the tile, but assumes that
* the content is loaded asynchronously via a url. The tile object
* has a promise-like interface.
* defined as a quadrilateral section of a map. The base implementation is
* independent of the actual content of the tile, but assumes that the content
* is loaded asynchronously via a url. The tile object has a promise-like
* interface.
* @example
* tile.then(function (data) {...}).catch(function (data) {...});
*
Expand Down Expand Up @@ -48,6 +48,8 @@ var tile = function (spec) {
* @property {object} index The tile index.
* @property {number} index.x The tile x index.
* @property {number} index.y The tile y index.
* @property {number} [index.level] The tile level index.
* @property {number} [index.reference] The tile reference index.
* @name geo.tile#index
*/
Object.defineProperty(this, 'index', {
Expand Down Expand Up @@ -143,13 +145,12 @@ var tile = function (spec) {

/**
* Return a unique string representation of the given tile useble as a hash
* key. Possibly extend later to include url information to make caches
* aware of the tile source.
* key.
*
* @returns {string}
*/
this.toString = function () {
return [this._index.level || 0, this._index.y, this._index.x].join('_');
return [this._index.level || 0, this._index.y, this._index.x, this._index.reference || 0].join('_');
};

/**
Expand Down
57 changes: 51 additions & 6 deletions src/tileLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ var featureLayer = require('./featureLayer');
* zoom level.
* @property {number} [cacheSize=400] The maximum number of tiles to cache.
* The default is 200 if keepLower is false.
* @property {geo.fetchQueue} [queue] A fetch queue to use. If unspecified, a
* new queue is created.
* @property {number} [queueSize=6] The queue size. Most browsers make at most
* 6 requests to any domain, so this should be no more than 6 times the
* number of subdomains used.
* @property {number} [initialQueueSize=0] The initial queue size. `0` to use
* the queue size. When querying a tile server that needs to load
* information before serving the first time, having an initial queue size of
* information before serving the first tile, having an initial queue size of
* 1 can reduce the load on the tile server. After the initial queue of
* tiles are loaded, the `queueSize` is used for all additional queries
* unless the `initialQueueSize` is set again or the tile cache is reset.
Expand Down Expand Up @@ -209,6 +211,7 @@ var tileLayer = function (arg) {
m_initialQueueSize = arg.initialQueueSize || 0,
m_lastTileSet = [],
m_maxBounds = [],
m_reference,
m_exited,
m_this = this;

Expand All @@ -230,7 +233,7 @@ var tileLayer = function (arg) {
this._cache = tileCache({size: arg.cacheSize});

// initialize the tile fetch queue
this._queue = fetchQueue({
this._queue = arg.queue || fetchQueue({
// this should probably be 6 * subdomains.length if subdomains are used
size: m_queueSize,
initialSize: m_initialQueueSize,
Expand All @@ -239,9 +242,14 @@ var tileLayer = function (arg) {
// smaller values will do needless computations.
track: arg.cacheSize,
needed: function (tile) {
if (this._tileLayers && this._tileLayers.length) {
return this._tileLayers.some((tl) => tile === tl.cache.get(tile.toString(), true));
}
return tile === m_this.cache.get(tile.toString(), true);
}
});
this._queue._tileLayers = this._queue._tileLayers || [];
this._queue._tileLayers.push(m_this);

var m_tileOffsetValues = {};

Expand Down Expand Up @@ -275,6 +283,25 @@ var tileLayer = function (arg) {
return $.extend({}, m_this._activeTiles); // copy on output
}});

/**
* Get/set the queue object.
* @property {geo.fetchQueue} queue The current queue.
* @name geo.tileLayer#queue
*/
Object.defineProperty(this, 'queue', {
get: function () { return m_this._queue; },
set: function (queue) {
if (m_this._queue !== queue) {
if (this._queue && this._queue._tileLayers && this._queue._tileLayers.indexOf(m_this) >= 0) {
this._queue._tileLayers.splice(this._queue._tileLayers.indexOf(m_this), 1);
}
m_this._queue = queue;
m_this._queue._tileLayers = m_this._queue._tileLayers || [];
m_this._queue._tileLayers.push(m_this);
}
}
});

/**
* Get/set the queue size.
* @property {number} size The queue size.
Expand Down Expand Up @@ -302,6 +329,20 @@ var tileLayer = function (arg) {
}
});

/**
* Get/set the tile reference value.
* @property {string} reference A reference value to distinguish tiles on
* this layer.
* @name geo.tileLayer#reference
*/
Object.defineProperty(this, 'reference', {
get: function () { return '' + m_this.id() + '_' + (m_reference || 0); },
set: function (reference) {
m_reference = reference;
},
configurable: true
});

/**
* The number of tiles at the given zoom level. The default implementation
* just returns `Math.pow(2, z)`.
Expand Down Expand Up @@ -566,11 +607,12 @@ var tileLayer = function (arg) {
* @param {object} index The tile index
* @param {number} index.x
* @param {number} index.y
* @param {number} index.level
* @param {number} [index.level]
* @param {number} [index.reference]
* @returns {string}
*/
this._tileHash = function (index) {
return [index.level || 0, index.y, index.x].join('_');
return [index.level || 0, index.y, index.x, index.reference || 0].join('_');
};

/**
Expand Down Expand Up @@ -663,8 +705,8 @@ var tileLayer = function (arg) {
// loop over the tile range
for (i = start.x; i <= end.x; i += 1) {
for (j = start.y; j <= end.y; j += 1) {
index = {level: level, x: i, y: j};
source = {level: level, x: i, y: j};
index = {level: level, x: i, y: j, reference: m_this.reference};
source = {level: level, x: i, y: j, reference: m_this.reference};
if (m_this._options.wrapX) {
source.x = modulo(source.x, nTilesLevel.x);
}
Expand Down Expand Up @@ -1622,6 +1664,9 @@ var tileLayer = function (arg) {
// call super method
s_exit.apply(m_this, arguments);
m_exited = true;
if (this._queue && this._queue._tileLayers && this._queue._tileLayers.indexOf(m_this) >= 0) {
this._queue._tileLayers.splice(this._queue._tileLayers.indexOf(m_this), 1);
}
return m_this;
};

Expand Down
4 changes: 2 additions & 2 deletions tests/cases/osmLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ describe('geo.core.osmLayer', function () {
it('check null tiles and switch to svg', function () {
positions = {};
$.each($('[tile-reference]'), function () {
var ref = $(this).attr('tile-reference');
var ref = $(this).attr('tile-reference').split('_').slice(0, 3).join('_');
positions[ref] = $(this)[0].getBoundingClientRect();
});
map.deleteLayer(layer);
Expand All @@ -268,7 +268,7 @@ describe('geo.core.osmLayer', function () {
});
it('compare tile offsets at angle ' + angle, function () {
$.each($('image[reference]'), function () {
var ref = $(this).attr('reference');
var ref = $(this).attr('reference').split('_').slice(0, 3).join('_');
/* Only check the top level */
if (ref.indexOf('4_') === 0) {
var offset = $(this)[0].getBoundingClientRect();
Expand Down
20 changes: 20 additions & 0 deletions tests/cases/tileLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,26 @@ describe('geo.tileLayer', function () {
expect(layer.initialQueueSize).toBe(1);
expect(layer._queue.initialSize).toBe(1);
});
it('queue', function () {
var m = map(), layer;
opts.map = m;
layer = geo.tileLayer(opts);
expect(layer.queue._tileLayers.length).toBe(1);
var layer2 = geo.tileLayer(opts);
var origQueue = layer2.queue;
layer2.queue = layer.queue;
expect(layer.queue._tileLayers.length).toBe(2);
layer2.queue = origQueue;
expect(layer.queue._tileLayers.length).toBe(1);
});
it('reference', function () {
var m = map(), layer;
opts.map = m;
layer = geo.tileLayer(opts);
expect(layer.reference).toBe(layer.id() + '_0');
layer.reference = 'A';
expect(layer.reference).toBe(layer.id() + '_A');
});
});
describe('Public utility methods', function () {
describe('isValid', function () {
Expand Down
3 changes: 3 additions & 0 deletions tutorials/multiframe/index.pug
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,9 @@ block mainTutorial
layerB = map.createLayer('osm', params.layer);
// create a foreground layer for the current frame
params.layer.url = `${baseUrl}0`;
// have the layers share a fetch queue. Since both tile layers are from
// the same server, this prevents requests from getting backlogged.
params.layer.queue = layerB.queue;
layerA = map.createLayer('osm', params.layer);
layerA._frame = 0;
// adjust the frame slider and listen to changes
Expand Down

0 comments on commit 0845217

Please sign in to comment.