Allow to render several keys/steps with a given range
- At low level point renderer now allows to receive several keys in renderTile. - leaflet and gmaps layers expose renderRange(start, end) to be able to set and render a range of steps. This closes #246
This commit is contained in:
parent
5413f4cb82
commit
2d5542a28c
@ -9,7 +9,7 @@ function GMapsTorqueLayer(options) {
|
|||||||
if (!torque.isBrowserSupported()) {
|
if (!torque.isBrowserSupported()) {
|
||||||
throw new Error("browser is not supported by torque");
|
throw new Error("browser is not supported by torque");
|
||||||
}
|
}
|
||||||
this.key = 0;
|
this.keys = [0];
|
||||||
this.shader = null;
|
this.shader = null;
|
||||||
this.ready = false;
|
this.ready = false;
|
||||||
this.options = torque.extend({}, options);
|
this.options = torque.extend({}, options);
|
||||||
@ -31,7 +31,7 @@ function GMapsTorqueLayer(options) {
|
|||||||
|
|
||||||
this.animator = new torque.Animator(function(time) {
|
this.animator = new torque.Animator(function(time) {
|
||||||
var k = time | 0;
|
var k = time | 0;
|
||||||
if(self.key !== k) {
|
if(self.getKey() !== k) {
|
||||||
self.setKey(k);
|
self.setKey(k);
|
||||||
}
|
}
|
||||||
}, torque.extend(torque.clone(this.options), {
|
}, torque.extend(torque.clone(this.options), {
|
||||||
@ -102,7 +102,7 @@ GMapsTorqueLayer.prototype = torque.extend({},
|
|||||||
self.fire('change:steps', {
|
self.fire('change:steps', {
|
||||||
steps: self.provider.getSteps()
|
steps: self.provider.getSteps()
|
||||||
});
|
});
|
||||||
self.setKey(self.key);
|
self.setKey(self.getKey());
|
||||||
};
|
};
|
||||||
|
|
||||||
this.provider = new this.providers[this.options.provider](this.options);
|
this.provider = new this.providers[this.options.provider](this.options);
|
||||||
@ -211,7 +211,7 @@ GMapsTorqueLayer.prototype = torque.extend({},
|
|||||||
if (tile) {
|
if (tile) {
|
||||||
pos = this.getTilePos(tile.coord);
|
pos = this.getTilePos(tile.coord);
|
||||||
ctx.setTransform(1, 0, 0, 1, pos.x, pos.y);
|
ctx.setTransform(1, 0, 0, 1, pos.x, pos.y);
|
||||||
this.renderer.renderTile(tile, this.key);
|
this.renderer.renderTile(tile, this.keys);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.renderer.applyFilters();
|
this.renderer.applyFilters();
|
||||||
@ -233,10 +233,18 @@ GMapsTorqueLayer.prototype = torque.extend({},
|
|||||||
* accumulated
|
* accumulated
|
||||||
*/
|
*/
|
||||||
setKey: function(key) {
|
setKey: function(key) {
|
||||||
this.key = key;
|
this.setKeys([key]);
|
||||||
this.animator.step(key);
|
},
|
||||||
|
|
||||||
|
setKeys: function(keys) {
|
||||||
|
this.keys = keys;
|
||||||
|
this.animator.step(this.getKey());
|
||||||
this.redraw();
|
this.redraw();
|
||||||
this.fire('change:time', { time: this.getTime(), step: this.key });
|
this.fire('change:time', { time: this.getTime(), step: this.getKey() });
|
||||||
|
},
|
||||||
|
|
||||||
|
getKey: function() {
|
||||||
|
return this.keys[0];
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -250,6 +258,20 @@ GMapsTorqueLayer.prototype = torque.extend({},
|
|||||||
this.setKey(time);
|
this.setKey(time);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
renderRange: function(start, end) {
|
||||||
|
this.pause();
|
||||||
|
var keys = [];
|
||||||
|
for (var i = start; i <= end; i++) {
|
||||||
|
keys.push(i);
|
||||||
|
}
|
||||||
|
this.setKeys(keys);
|
||||||
|
},
|
||||||
|
|
||||||
|
resetRenderRange: function() {
|
||||||
|
this.stop();
|
||||||
|
this.play();
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* transform from animation step to Date object
|
* transform from animation step to Date object
|
||||||
* that contains the animation time
|
* that contains the animation time
|
||||||
@ -272,7 +294,7 @@ GMapsTorqueLayer.prototype = torque.extend({},
|
|||||||
},
|
},
|
||||||
|
|
||||||
getStep: function() {
|
getStep: function() {
|
||||||
return this.key;
|
return this.getKey();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -280,7 +302,7 @@ GMapsTorqueLayer.prototype = torque.extend({},
|
|||||||
* in the defined column. Date object
|
* in the defined column. Date object
|
||||||
*/
|
*/
|
||||||
getTime: function() {
|
getTime: function() {
|
||||||
return this.stepToTime(this.key);
|
return this.stepToTime(this.getKey());
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -328,7 +350,7 @@ GMapsTorqueLayer.prototype = torque.extend({},
|
|||||||
*/
|
*/
|
||||||
getValues: function(step) {
|
getValues: function(step) {
|
||||||
var values = [];
|
var values = [];
|
||||||
step = step === undefined ? this.key: step;
|
step = step === undefined ? this.getKey(): step;
|
||||||
var t, tile;
|
var t, tile;
|
||||||
for(t in this._tiles) {
|
for(t in this._tiles) {
|
||||||
tile = this._tiles[t];
|
tile = this._tiles[t];
|
||||||
@ -338,7 +360,7 @@ GMapsTorqueLayer.prototype = torque.extend({},
|
|||||||
},
|
},
|
||||||
|
|
||||||
getValueForPos: function(x, y, step) {
|
getValueForPos: function(x, y, step) {
|
||||||
step = step === undefined ? this.key: step;
|
step = step === undefined ? this.getKey(): step;
|
||||||
var t, tile, pos, value = null, xx, yy;
|
var t, tile, pos, value = null, xx, yy;
|
||||||
for(t in this._tiles) {
|
for(t in this._tiles) {
|
||||||
tile = this._tiles[t];
|
tile = this._tiles[t];
|
||||||
@ -402,7 +424,7 @@ GMapsTiledTorqueLayer.prototype = torque.extend({}, CanvasTileLayer.prototype, {
|
|||||||
|
|
||||||
initialize: function(options) {
|
initialize: function(options) {
|
||||||
var self = this;
|
var self = this;
|
||||||
this.key = 0;
|
this.keys = [0];
|
||||||
|
|
||||||
this.options.renderer = this.options.renderer || 'pixel';
|
this.options.renderer = this.options.renderer || 'pixel';
|
||||||
this.options.provider = this.options.provider || 'sql_api';
|
this.options.provider = this.options.provider || 'sql_api';
|
||||||
@ -438,12 +460,12 @@ GMapsTiledTorqueLayer.prototype = torque.extend({}, CanvasTileLayer.prototype, {
|
|||||||
|
|
||||||
this.renderer.setCanvas(canvas);
|
this.renderer.setCanvas(canvas);
|
||||||
|
|
||||||
var accum = this.renderer.accumulate(tile.data, this.key);
|
var accum = this.renderer.accumulate(tile.data, this.getKey());
|
||||||
this.renderer.renderTileAccum(accum, 0, 0);
|
this.renderer.renderTileAccum(accum, 0, 0);
|
||||||
},
|
},
|
||||||
|
|
||||||
setKey: function(key) {
|
setKey: function(key) {
|
||||||
this.key = key;
|
this.keys = [key];
|
||||||
this.redraw();
|
this.redraw();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ L.TorqueLayer = L.CanvasLayer.extend({
|
|||||||
throw new Error("browser is not supported by torque");
|
throw new Error("browser is not supported by torque");
|
||||||
}
|
}
|
||||||
options.tileLoader = true;
|
options.tileLoader = true;
|
||||||
this.key = 0;
|
this.keys = [0];
|
||||||
this.prevRenderedKey = 0;
|
this.prevRenderedKey = 0;
|
||||||
if (options.cartocss) {
|
if (options.cartocss) {
|
||||||
torque.extend(options, torque.common.TorqueLayer.optionsFromCartoCSS(options.cartocss));
|
torque.extend(options, torque.common.TorqueLayer.optionsFromCartoCSS(options.cartocss));
|
||||||
@ -39,7 +39,7 @@ L.TorqueLayer = L.CanvasLayer.extend({
|
|||||||
|
|
||||||
this.animator = new torque.Animator(function(time) {
|
this.animator = new torque.Animator(function(time) {
|
||||||
var k = time | 0;
|
var k = time | 0;
|
||||||
if(self.key !== k) {
|
if(self.getKey() !== k) {
|
||||||
self.setKey(k, { direct: true });
|
self.setKey(k, { direct: true });
|
||||||
}
|
}
|
||||||
}, torque.extend(torque.clone(options), {
|
}, torque.extend(torque.clone(options), {
|
||||||
@ -84,7 +84,7 @@ L.TorqueLayer = L.CanvasLayer.extend({
|
|||||||
self.fire('change:steps', {
|
self.fire('change:steps', {
|
||||||
steps: self.provider.getSteps()
|
steps: self.provider.getSteps()
|
||||||
});
|
});
|
||||||
self.setKey(self.key);
|
self.setKey(self.getKey());
|
||||||
};
|
};
|
||||||
|
|
||||||
this.renderer.on("allIconsLoaded", this.render.bind(this));
|
this.renderer.on("allIconsLoaded", this.render.bind(this));
|
||||||
@ -239,7 +239,7 @@ L.TorqueLayer = L.CanvasLayer.extend({
|
|||||||
// all the points
|
// all the points
|
||||||
this.renderer._ctx.drawImage(tile._tileCache, 0, 0);
|
this.renderer._ctx.drawImage(tile._tileCache, 0, 0);
|
||||||
} else {
|
} else {
|
||||||
this.renderer.renderTile(tile, this.key);
|
this.renderer.renderTile(tile, this.keys);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -248,7 +248,7 @@ L.TorqueLayer = L.CanvasLayer.extend({
|
|||||||
// prepare caches if the animation is not running
|
// prepare caches if the animation is not running
|
||||||
// don't cache if the key has just changed, this avoids to cache
|
// don't cache if the key has just changed, this avoids to cache
|
||||||
// when the user is dragging, it only cache when the map is still
|
// when the user is dragging, it only cache when the map is still
|
||||||
if (!this.animator.isRunning() && this.key === this.prevRenderedKey) {
|
if (!this.animator.isRunning() && this.getKey() === this.prevRenderedKey) {
|
||||||
var tile_size = this.renderer.TILE_SIZE;
|
var tile_size = this.renderer.TILE_SIZE;
|
||||||
for(t in this._tiles) {
|
for(t in this._tiles) {
|
||||||
tile = this._tiles[t];
|
tile = this._tiles[t];
|
||||||
@ -268,7 +268,7 @@ L.TorqueLayer = L.CanvasLayer.extend({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.prevRenderedKey = this.key;
|
this.prevRenderedKey = this.getKey();
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -278,11 +278,28 @@ L.TorqueLayer = L.CanvasLayer.extend({
|
|||||||
* accumulated
|
* accumulated
|
||||||
*/
|
*/
|
||||||
setKey: function(key, options) {
|
setKey: function(key, options) {
|
||||||
this.key = key;
|
this.setKeys([key], options);
|
||||||
this.animator.step(key);
|
},
|
||||||
|
|
||||||
|
setKeys: function(keys, options) {
|
||||||
|
this.keys = keys;
|
||||||
|
this.animator.step(this.getKey());
|
||||||
this._clearTileCaches();
|
this._clearTileCaches();
|
||||||
this.redraw(options && options.direct);
|
this.redraw(options && options.direct);
|
||||||
this.fire('change:time', { time: this.getTime(), step: this.key });
|
this.fire('change:time', {
|
||||||
|
time: this.getTime(),
|
||||||
|
step: this.getKey(),
|
||||||
|
start: this.getKey(),
|
||||||
|
end: this.getLastKey()
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getKey: function() {
|
||||||
|
return this.keys[0];
|
||||||
|
},
|
||||||
|
|
||||||
|
getLastKey: function() {
|
||||||
|
return this.keys[this.keys.length - 1];
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -296,6 +313,20 @@ L.TorqueLayer = L.CanvasLayer.extend({
|
|||||||
this.setKey(time);
|
this.setKey(time);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
renderRange: function(start, end) {
|
||||||
|
this.pause();
|
||||||
|
var keys = [];
|
||||||
|
for (var i = start; i <= end; i++) {
|
||||||
|
keys.push(i);
|
||||||
|
}
|
||||||
|
this.setKeys(keys);
|
||||||
|
},
|
||||||
|
|
||||||
|
resetRenderRange: function() {
|
||||||
|
this.stop();
|
||||||
|
this.play();
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* transform from animation step to Date object
|
* transform from animation step to Date object
|
||||||
* that contains the animation time
|
* that contains the animation time
|
||||||
@ -317,7 +348,7 @@ L.TorqueLayer = L.CanvasLayer.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
getStep: function() {
|
getStep: function() {
|
||||||
return this.key;
|
return this.getKey();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -325,7 +356,7 @@ L.TorqueLayer = L.CanvasLayer.extend({
|
|||||||
* in the defined column. Date object
|
* in the defined column. Date object
|
||||||
*/
|
*/
|
||||||
getTime: function() {
|
getTime: function() {
|
||||||
return this.stepToTime(this.key);
|
return this.stepToTime(this.getKey());
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -381,7 +412,7 @@ L.TorqueLayer = L.CanvasLayer.extend({
|
|||||||
*/
|
*/
|
||||||
getValues: function(step) {
|
getValues: function(step) {
|
||||||
var values = [];
|
var values = [];
|
||||||
step = step === undefined ? this.key: step;
|
step = step === undefined ? this.getKey(): step;
|
||||||
var t, tile;
|
var t, tile;
|
||||||
for(t in this._tiles) {
|
for(t in this._tiles) {
|
||||||
tile = this._tiles[t];
|
tile = this._tiles[t];
|
||||||
@ -394,7 +425,7 @@ L.TorqueLayer = L.CanvasLayer.extend({
|
|||||||
* return the value for position relative to map coordinates. null for no value
|
* return the value for position relative to map coordinates. null for no value
|
||||||
*/
|
*/
|
||||||
getValueForPos: function(x, y, step) {
|
getValueForPos: function(x, y, step) {
|
||||||
step = step === undefined ? this.key: step;
|
step = step === undefined ? this.getKey(): step;
|
||||||
var t, tile, pos, value = null, xx, yy;
|
var t, tile, pos, value = null, xx, yy;
|
||||||
for(t in this._tiles) {
|
for(t in this._tiles) {
|
||||||
tile = this._tiles[t];
|
tile = this._tiles[t];
|
||||||
|
@ -172,13 +172,19 @@ var Filters = require('./torque_filters');
|
|||||||
//
|
//
|
||||||
// renders all the layers (and frames for each layer) from cartocss
|
// renders all the layers (and frames for each layer) from cartocss
|
||||||
//
|
//
|
||||||
renderTile: function(tile, key, callback) {
|
renderTile: function(tile, keys, callback) {
|
||||||
if (this._iconsToLoad > 0) {
|
if (this._iconsToLoad > 0) {
|
||||||
this.on('allIconsLoaded', function() {
|
this.on('allIconsLoaded', function() {
|
||||||
this.renderTile.apply(this, [tile, key, callback]);
|
this.renderTile.apply(this, [tile, keys, callback]);
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// convert scalar key to keys array
|
||||||
|
if (typeof keys.length === 'undefined') {
|
||||||
|
keys = [keys];
|
||||||
|
}
|
||||||
|
|
||||||
var prof = Profiler.metric('torque.renderer.point.renderLayers').start();
|
var prof = Profiler.metric('torque.renderer.point.renderLayers').start();
|
||||||
var layers = this._shader.getLayers();
|
var layers = this._shader.getLayers();
|
||||||
for(var i = 0, n = layers.length; i < n; ++i ) {
|
for(var i = 0, n = layers.length; i < n; ++i ) {
|
||||||
@ -189,7 +195,9 @@ var Filters = require('./torque_filters');
|
|||||||
for(var fr = 0; fr < layer.frames().length; ++fr) {
|
for(var fr = 0; fr < layer.frames().length; ++fr) {
|
||||||
var frame = layer.frames()[fr];
|
var frame = layer.frames()[fr];
|
||||||
var fr_sprites = sprites[frame] || (sprites[frame] = []);
|
var fr_sprites = sprites[frame] || (sprites[frame] = []);
|
||||||
this._renderTile(tile, key - frame, frame, fr_sprites, layer);
|
for (var k = 0, len = keys.length; k < len; k++) {
|
||||||
|
this._renderTile(tile, keys[k] - frame, frame, fr_sprites, layer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,11 @@ QUnit.module('renderer/point');
|
|||||||
|
|
||||||
var IMAGE_DIFF_TOLERANCE = 4 / 100;
|
var IMAGE_DIFF_TOLERANCE = 4 / 100;
|
||||||
|
|
||||||
|
// HOW TO debug image output
|
||||||
|
// -------------------------
|
||||||
|
// Once you have a valid canvas and no errors, it's possible to write to disk the canvas buffer as a png image with:
|
||||||
|
// require('fs').writeFileSync('/tmp/torque-acceptance-test-tile.png', canvas.toBuffer(), {encoding: null});
|
||||||
|
|
||||||
asyncTest('navy example', function(assert) {
|
asyncTest('navy example', function(assert) {
|
||||||
var cartocss = [
|
var cartocss = [
|
||||||
'Map {',
|
'Map {',
|
||||||
@ -64,3 +69,43 @@ asyncTest('basic heatmap', function(assert) {
|
|||||||
QUnit.start();
|
QUnit.start();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
asyncTest('render multiple steps', function(assert) {
|
||||||
|
var CARTOCSS = [
|
||||||
|
'Map {',
|
||||||
|
' -torque-frame-count: 360;',
|
||||||
|
' -torque-animation-duration: 30;',
|
||||||
|
' -torque-time-attribute: "cartodb_id";',
|
||||||
|
' -torque-aggregation-function: "count(cartodb_id)";',
|
||||||
|
' -torque-resolution: 1;',
|
||||||
|
' -torque-data-aggregation: linear;',
|
||||||
|
'}',
|
||||||
|
'#generate_series {',
|
||||||
|
' comp-op: lighter;',
|
||||||
|
' marker-fill-opacity: 0.9;',
|
||||||
|
' marker-line-color: #FFF;',
|
||||||
|
' marker-line-width: 0;',
|
||||||
|
' marker-line-opacity: 1;',
|
||||||
|
' marker-type: rectable;',
|
||||||
|
' marker-width: 6;',
|
||||||
|
' marker-fill: #0F3B82;',
|
||||||
|
'}'
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
var steps = [];
|
||||||
|
for (var i = 20; i <= 50; i++) {
|
||||||
|
steps.push(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dataset can be regenerated with:
|
||||||
|
// SELECT
|
||||||
|
// s + 181 as cartodb_id,
|
||||||
|
// st_transform(ST_SetSRID (st_makepoint(s, 20 + 10*sin(s)), 4326), 3857) as the_geom_webmercator
|
||||||
|
// FROM generate_series(-180, 180, 1) as s
|
||||||
|
pointRenderer.getTile('generate_series_sin-2-0-1.torque.json', CARTOCSS, 2, 0, 1, steps, function(err, canvas) {
|
||||||
|
assert.ok(!err, 'no error while getting tile');
|
||||||
|
var imageDiff = image.compare(canvas.toBuffer(), 'generate_series_sin-2-0-1.png');
|
||||||
|
assert.ok(imageDiff < IMAGE_DIFF_TOLERANCE, 'image not matching, probably not rendering several steps');
|
||||||
|
QUnit.start();
|
||||||
|
});
|
||||||
|
});
|
BIN
test/fixtures/image/generate_series_sin-2-0-1.png
vendored
Normal file
BIN
test/fixtures/image/generate_series_sin-2-0-1.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.1 KiB |
1
test/fixtures/json/generate_series_sin-2-0-1.torque.json
vendored
Normal file
1
test/fixtures/json/generate_series_sin-2-0-1.torque.json
vendored
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user