From 2d5542a28c9b1cb0e972e785146955ad03fce0ce Mon Sep 17 00:00:00 2001 From: Raul Ochoa Date: Mon, 14 Dec 2015 10:56:07 +0100 Subject: [PATCH] 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 --- lib/torque/gmaps/torque.js | 50 ++++++++++----- lib/torque/leaflet/torque.js | 57 ++++++++++++++---- lib/torque/renderer/point.js | 14 ++++- test/acceptance/renderer/point.js | 45 ++++++++++++++ .../image/generate_series_sin-2-0-1.png | Bin 0 -> 5220 bytes .../generate_series_sin-2-0-1.torque.json | 1 + 6 files changed, 137 insertions(+), 30 deletions(-) create mode 100644 test/fixtures/image/generate_series_sin-2-0-1.png create mode 100644 test/fixtures/json/generate_series_sin-2-0-1.torque.json diff --git a/lib/torque/gmaps/torque.js b/lib/torque/gmaps/torque.js index 814ee0a..3600f4c 100644 --- a/lib/torque/gmaps/torque.js +++ b/lib/torque/gmaps/torque.js @@ -9,7 +9,7 @@ function GMapsTorqueLayer(options) { if (!torque.isBrowserSupported()) { throw new Error("browser is not supported by torque"); } - this.key = 0; + this.keys = [0]; this.shader = null; this.ready = false; this.options = torque.extend({}, options); @@ -31,7 +31,7 @@ function GMapsTorqueLayer(options) { this.animator = new torque.Animator(function(time) { var k = time | 0; - if(self.key !== k) { + if(self.getKey() !== k) { self.setKey(k); } }, torque.extend(torque.clone(this.options), { @@ -102,7 +102,7 @@ GMapsTorqueLayer.prototype = torque.extend({}, self.fire('change:steps', { steps: self.provider.getSteps() }); - self.setKey(self.key); + self.setKey(self.getKey()); }; this.provider = new this.providers[this.options.provider](this.options); @@ -211,7 +211,7 @@ GMapsTorqueLayer.prototype = torque.extend({}, if (tile) { pos = this.getTilePos(tile.coord); 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(); @@ -233,10 +233,18 @@ GMapsTorqueLayer.prototype = torque.extend({}, * accumulated */ setKey: function(key) { - this.key = key; - this.animator.step(key); + this.setKeys([key]); + }, + + setKeys: function(keys) { + this.keys = keys; + this.animator.step(this.getKey()); 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); }, + 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 * that contains the animation time @@ -272,7 +294,7 @@ GMapsTorqueLayer.prototype = torque.extend({}, }, getStep: function() { - return this.key; + return this.getKey(); }, /** @@ -280,7 +302,7 @@ GMapsTorqueLayer.prototype = torque.extend({}, * in the defined column. Date object */ getTime: function() { - return this.stepToTime(this.key); + return this.stepToTime(this.getKey()); }, /** @@ -328,7 +350,7 @@ GMapsTorqueLayer.prototype = torque.extend({}, */ getValues: function(step) { var values = []; - step = step === undefined ? this.key: step; + step = step === undefined ? this.getKey(): step; var t, tile; for(t in this._tiles) { tile = this._tiles[t]; @@ -338,7 +360,7 @@ GMapsTorqueLayer.prototype = torque.extend({}, }, 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; for(t in this._tiles) { tile = this._tiles[t]; @@ -402,7 +424,7 @@ GMapsTiledTorqueLayer.prototype = torque.extend({}, CanvasTileLayer.prototype, { initialize: function(options) { var self = this; - this.key = 0; + this.keys = [0]; this.options.renderer = this.options.renderer || 'pixel'; this.options.provider = this.options.provider || 'sql_api'; @@ -438,12 +460,12 @@ GMapsTiledTorqueLayer.prototype = torque.extend({}, CanvasTileLayer.prototype, { 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); }, setKey: function(key) { - this.key = key; + this.keys = [key]; this.redraw(); }, diff --git a/lib/torque/leaflet/torque.js b/lib/torque/leaflet/torque.js index 70f2c68..7c1b2ad 100644 --- a/lib/torque/leaflet/torque.js +++ b/lib/torque/leaflet/torque.js @@ -26,7 +26,7 @@ L.TorqueLayer = L.CanvasLayer.extend({ throw new Error("browser is not supported by torque"); } options.tileLoader = true; - this.key = 0; + this.keys = [0]; this.prevRenderedKey = 0; if (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) { var k = time | 0; - if(self.key !== k) { + if(self.getKey() !== k) { self.setKey(k, { direct: true }); } }, torque.extend(torque.clone(options), { @@ -84,7 +84,7 @@ L.TorqueLayer = L.CanvasLayer.extend({ self.fire('change:steps', { steps: self.provider.getSteps() }); - self.setKey(self.key); + self.setKey(self.getKey()); }; this.renderer.on("allIconsLoaded", this.render.bind(this)); @@ -239,7 +239,7 @@ L.TorqueLayer = L.CanvasLayer.extend({ // all the points this.renderer._ctx.drawImage(tile._tileCache, 0, 0); } 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 // 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 - if (!this.animator.isRunning() && this.key === this.prevRenderedKey) { + if (!this.animator.isRunning() && this.getKey() === this.prevRenderedKey) { var tile_size = this.renderer.TILE_SIZE; for(t in this._tiles) { 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 */ setKey: function(key, options) { - this.key = key; - this.animator.step(key); + this.setKeys([key], options); + }, + + setKeys: function(keys, options) { + this.keys = keys; + this.animator.step(this.getKey()); this._clearTileCaches(); 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); }, + 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 * that contains the animation time @@ -317,7 +348,7 @@ L.TorqueLayer = L.CanvasLayer.extend({ }, getStep: function() { - return this.key; + return this.getKey(); }, /** @@ -325,7 +356,7 @@ L.TorqueLayer = L.CanvasLayer.extend({ * in the defined column. Date object */ getTime: function() { - return this.stepToTime(this.key); + return this.stepToTime(this.getKey()); }, /** @@ -381,7 +412,7 @@ L.TorqueLayer = L.CanvasLayer.extend({ */ getValues: function(step) { var values = []; - step = step === undefined ? this.key: step; + step = step === undefined ? this.getKey(): step; var t, tile; for(t in this._tiles) { 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 */ 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; for(t in this._tiles) { tile = this._tiles[t]; diff --git a/lib/torque/renderer/point.js b/lib/torque/renderer/point.js index 4b07d14..53ed888 100644 --- a/lib/torque/renderer/point.js +++ b/lib/torque/renderer/point.js @@ -172,13 +172,19 @@ var Filters = require('./torque_filters'); // // 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) { this.on('allIconsLoaded', function() { - this.renderTile.apply(this, [tile, key, callback]); + this.renderTile.apply(this, [tile, keys, callback]); }); 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 layers = this._shader.getLayers(); 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) { var frame = layer.frames()[fr]; 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); + } } } } diff --git a/test/acceptance/renderer/point.js b/test/acceptance/renderer/point.js index e37f576..ece8287 100644 --- a/test/acceptance/renderer/point.js +++ b/test/acceptance/renderer/point.js @@ -5,6 +5,11 @@ QUnit.module('renderer/point'); 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) { var cartocss = [ 'Map {', @@ -63,4 +68,44 @@ asyncTest('basic heatmap', function(assert) { assert.ok(imageDiff < IMAGE_DIFF_TOLERANCE, 'heatmap tile is ok'); 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(); + }); }); \ No newline at end of file diff --git a/test/fixtures/image/generate_series_sin-2-0-1.png b/test/fixtures/image/generate_series_sin-2-0-1.png new file mode 100644 index 0000000000000000000000000000000000000000..184300a3a91d0f5d0b06960606234a11645fdaef GIT binary patch literal 5220 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 literal 0 HcmV?d00001 diff --git a/test/fixtures/json/generate_series_sin-2-0-1.torque.json b/test/fixtures/json/generate_series_sin-2-0-1.torque.json new file mode 100644 index 0000000..57483d2 --- /dev/null +++ b/test/fixtures/json/generate_series_sin-2-0-1.torque.json @@ -0,0 +1 @@ +[{"x__uint8":225,"y__uint8":45,"vals__uint8":[1],"dates__uint16":[78]},{"x__uint8":205,"y__uint8":31,"vals__uint8":[1],"dates__uint16":[71]},{"x__uint8":162,"y__uint8":72,"vals__uint8":[1],"dates__uint16":[56]},{"x__uint8":51,"y__uint8":89,"vals__uint8":[1],"dates__uint16":[17]},{"x__uint8":176,"y__uint8":89,"vals__uint8":[1],"dates__uint16":[61]},{"x__uint8":91,"y__uint8":68,"vals__uint8":[1],"dates__uint16":[31]},{"x__uint8":54,"y__uint8":80,"vals__uint8":[1],"dates__uint16":[18]},{"x__uint8":196,"y__uint8":85,"vals__uint8":[1],"dates__uint16":[68]},{"x__uint8":94,"y__uint8":40,"vals__uint8":[1],"dates__uint16":[32]},{"x__uint8":108,"y__uint8":76,"vals__uint8":[1],"dates__uint16":[37]},{"x__uint8":63,"y__uint8":35,"vals__uint8":[1],"dates__uint16":[21]},{"x__uint8":28,"y__uint8":48,"vals__uint8":[1],"dates__uint16":[9]},{"x__uint8":40,"y__uint8":44,"vals__uint8":[1],"dates__uint16":[13]},{"x__uint8":151,"y__uint8":29,"vals__uint8":[1],"dates__uint16":[52]},{"x__uint8":80,"y__uint8":31,"vals__uint8":[1],"dates__uint16":[27]},{"x__uint8":233,"y__uint8":76,"vals__uint8":[1],"dates__uint16":[81]},{"x__uint8":253,"y__uint8":55,"vals__uint8":[1],"dates__uint16":[88]},{"x__uint8":199,"y__uint8":59,"vals__uint8":[1],"dates__uint16":[69]},{"x__uint8":236,"y__uint8":47,"vals__uint8":[1],"dates__uint16":[82]},{"x__uint8":165,"y__uint8":43,"vals__uint8":[1],"dates__uint16":[57]},{"x__uint8":242,"y__uint8":38,"vals__uint8":[1],"dates__uint16":[84]},{"x__uint8":193,"y__uint8":86,"vals__uint8":[1],"dates__uint16":[67]},{"x__uint8":245,"y__uint8":66,"vals__uint8":[1],"dates__uint16":[85]},{"x__uint8":82,"y__uint8":52,"vals__uint8":[1],"dates__uint16":[28]},{"x__uint8":111,"y__uint8":47,"vals__uint8":[1],"dates__uint16":[38]},{"x__uint8":6,"y__uint8":32,"vals__uint8":[1],"dates__uint16":[1]},{"x__uint8":74,"y__uint8":60,"vals__uint8":[1],"dates__uint16":[25]},{"x__uint8":179,"y__uint8":79,"vals__uint8":[1],"dates__uint16":[62]},{"x__uint8":210,"y__uint8":81,"vals__uint8":[1],"dates__uint16":[73]},{"x__uint8":191,"y__uint8":61,"vals__uint8":[1],"dates__uint16":[66]},{"x__uint8":230,"y__uint8":89,"vals__uint8":[1],"dates__uint16":[80]},{"x__uint8":26,"y__uint8":29,"vals__uint8":[1],"dates__uint16":[8]},{"x__uint8":216,"y__uint8":68,"vals__uint8":[1],"dates__uint16":[75]},{"x__uint8":134,"y__uint8":32,"vals__uint8":[1],"dates__uint16":[46]},{"x__uint8":68,"y__uint8":86,"vals__uint8":[1],"dates__uint16":[23]},{"x__uint8":148,"y__uint8":37,"vals__uint8":[1],"dates__uint16":[51]},{"x__uint8":3,"y__uint8":56,"vals__uint8":[1],"dates__uint16":[0]},{"x__uint8":239,"y__uint8":29,"vals__uint8":[1],"dates__uint16":[83]},{"x__uint8":20,"y__uint8":65,"vals__uint8":[1],"dates__uint16":[6]},{"x__uint8":182,"y__uint8":51,"vals__uint8":[1],"dates__uint16":[63]},{"x__uint8":255,"y__uint8":32,"vals__uint8":[1],"dates__uint16":[89]},{"x__uint8":60,"y__uint8":30,"vals__uint8":[1],"dates__uint16":[20]},{"x__uint8":11,"y__uint8":56,"vals__uint8":[1],"dates__uint16":[3]},{"x__uint8":97,"y__uint8":29,"vals__uint8":[1],"dates__uint16":[33]},{"x__uint8":105,"y__uint8":89,"vals__uint8":[1],"dates__uint16":[36]},{"x__uint8":117,"y__uint8":37,"vals__uint8":[1],"dates__uint16":[40]},{"x__uint8":34,"y__uint8":89,"vals__uint8":[1],"dates__uint16":[11]},{"x__uint8":23,"y__uint8":37,"vals__uint8":[1],"dates__uint16":[7]},{"x__uint8":71,"y__uint8":85,"vals__uint8":[1],"dates__uint16":[24]},{"x__uint8":31,"y__uint8":77,"vals__uint8":[1],"dates__uint16":[10]},{"x__uint8":222,"y__uint8":29,"vals__uint8":[1],"dates__uint16":[77]},{"x__uint8":185,"y__uint8":30,"vals__uint8":[1],"dates__uint16":[64]},{"x__uint8":9,"y__uint8":32,"vals__uint8":[1],"dates__uint16":[2]},{"x__uint8":37,"y__uint8":73,"vals__uint8":[1],"dates__uint16":[12]},{"x__uint8":145,"y__uint8":64,"vals__uint8":[1],"dates__uint16":[50]},{"x__uint8":128,"y__uint8":55,"vals__uint8":[1],"dates__uint16":[44]},{"x__uint8":154,"y__uint8":48,"vals__uint8":[1],"dates__uint16":[53]},{"x__uint8":102,"y__uint8":73,"vals__uint8":[1],"dates__uint16":[35]},{"x__uint8":228,"y__uint8":74,"vals__uint8":[1],"dates__uint16":[79]},{"x__uint8":202,"y__uint8":34,"vals__uint8":[1],"dates__uint16":[70]},{"x__uint8":14,"y__uint8":83,"vals__uint8":[1],"dates__uint16":[4]},{"x__uint8":171,"y__uint8":41,"vals__uint8":[1],"dates__uint16":[59]},{"x__uint8":17,"y__uint8":87,"vals__uint8":[1],"dates__uint16":[5]},{"x__uint8":77,"y__uint8":34,"vals__uint8":[1],"dates__uint16":[26]},{"x__uint8":100,"y__uint8":44,"vals__uint8":[1],"dates__uint16":[34]},{"x__uint8":48,"y__uint8":69,"vals__uint8":[1],"dates__uint16":[16]},{"x__uint8":142,"y__uint8":87,"vals__uint8":[1],"dates__uint16":[49]},{"x__uint8":174,"y__uint8":69,"vals__uint8":[1],"dates__uint16":[60]},{"x__uint8":46,"y__uint8":40,"vals__uint8":[1],"dates__uint16":[15]},{"x__uint8":168,"y__uint8":29,"vals__uint8":[1],"dates__uint16":[58]},{"x__uint8":156,"y__uint8":77,"vals__uint8":[1],"dates__uint16":[54]},{"x__uint8":125,"y__uint8":83,"vals__uint8":[1],"dates__uint16":[43]},{"x__uint8":114,"y__uint8":29,"vals__uint8":[1],"dates__uint16":[39]},{"x__uint8":139,"y__uint8":83,"vals__uint8":[1],"dates__uint16":[48]},{"x__uint8":250,"y__uint8":82,"vals__uint8":[1],"dates__uint16":[87]},{"x__uint8":159,"y__uint8":89,"vals__uint8":[1],"dates__uint16":[55]},{"x__uint8":188,"y__uint8":35,"vals__uint8":[1],"dates__uint16":[65]},{"x__uint8":131,"y__uint8":32,"vals__uint8":[1],"dates__uint16":[45]},{"x__uint8":119,"y__uint8":65,"vals__uint8":[1],"dates__uint16":[41]},{"x__uint8":0,"y__uint8":83,"vals__uint8":[1],"dates__uint16":[0]},{"x__uint8":208,"y__uint8":53,"vals__uint8":[1],"dates__uint16":[72]},{"x__uint8":85,"y__uint8":80,"vals__uint8":[1],"dates__uint16":[29]},{"x__uint8":219,"y__uint8":40,"vals__uint8":[1],"dates__uint16":[76]},{"x__uint8":137,"y__uint8":56,"vals__uint8":[1],"dates__uint16":[47]},{"x__uint8":247,"y__uint8":88,"vals__uint8":[1],"dates__uint16":[86]},{"x__uint8":43,"y__uint8":29,"vals__uint8":[1],"dates__uint16":[14]},{"x__uint8":57,"y__uint8":51,"vals__uint8":[1],"dates__uint16":[19]},{"x__uint8":65,"y__uint8":60,"vals__uint8":[1],"dates__uint16":[22]},{"x__uint8":122,"y__uint8":88,"vals__uint8":[1],"dates__uint16":[42]},{"x__uint8":213,"y__uint8":89,"vals__uint8":[1],"dates__uint16":[74]},{"x__uint8":88,"y__uint8":89,"vals__uint8":[1],"dates__uint16":[30]}] \ No newline at end of file