Compare commits

...

2 Commits

Author SHA1 Message Date
javi
fe27c0b515 added play, pause and stop events, closes #53 2014-11-01 17:25:08 +01:00
javi
589a29acfb added templated provider WIP 2014-11-01 17:24:31 +01:00
3 changed files with 300 additions and 1 deletions

View File

@ -37,6 +37,7 @@
start: function() {
this.running = true;
requestAnimationFrame(this._tick);
this.options.onStart && this.options.onStart();
},
isRunning: function() {
@ -46,6 +47,7 @@
stop: function() {
this.pause();
this.time(0);
this.options.onStop && this.options.onStop();
},
// real animation time
@ -96,6 +98,7 @@
pause: function() {
this.running = false;
cancelAnimationFrame(this._tick);
this.options.onPause && this.options.onPause();
},
_tick: function() {

View File

@ -37,7 +37,17 @@ L.TorqueLayer = L.CanvasLayer.extend({
if(self.key !== k) {
self.setKey(k, { direct: true });
}
}, torque.clone(options));
}, torque.extend(torque.clone(options), {
onPause: function() {
self.fire('pause');
},
onStop: function() {
self.fire('stop');
},
onStart: function() {
self.fire('play');
}
}));
this.play = this.animator.start.bind(this.animator);
this.stop = this.animator.stop.bind(this.animator);

View File

@ -0,0 +1,286 @@
(function(exports) {
var torque = exports.torque = exports.torque || {};
var providers = exports.torque.providers = exports.torque.providers || {};
var Uint8Array = torque.types.Uint8Array;
var Int32Array = torque.types.Int32Array;
var Uint32Array = torque.types.Uint32Array;
// format('hello, {0}', 'rambo') -> "hello, rambo"
function format(str) {
for(var i = 1; i < arguments.length; ++i) {
var attrs = arguments[i];
for(var attr in attrs) {
str = str.replace(RegExp('\\{' + attr + '\\}', 'g'), attrs[attr]);
}
}
return str;
}
var json = function (options) {
this.options = options;
this.options.tiler_protocol = options.tiler_protocol || 'http';
this.options.tiler_domain = options.tiler_domain || 'cartodb.com';
this.options.tiler_port = options.tiler_port || 80;
// check options
if (options.resolution === undefined ) throw new Error("resolution should be provided");
if (options.steps === undefined ) throw new Error("steps should be provided");
};
json.prototype = {
/**
* return the torque tile encoded in an efficient javascript
* structure:
* {
* x:Uint8Array x coordinates in tile reference system, normally from 0-255
* y:Uint8Array y coordinates in tile reference system
* Index: Array index to the properties
* }
*/
proccessTile: function(rows, coord, zoom) {
var r;
var x = new Uint8Array(rows.length);
var y = new Uint8Array(rows.length);
var prof_mem = Profiler.metric('ProviderJSON:mem');
var prof_point_count = Profiler.metric('ProviderJSON:point_count');
var prof_process_time = Profiler.metric('ProviderJSON:process_time').start()
// count number of dates
var dates = 0;
var maxDateSlots = -1;
for (r = 0; r < rows.length; ++r) {
var row = rows[r];
dates += row.dates__uint16.length;
for(var d = 0; d < row.dates__uint16.length; ++d) {
maxDateSlots = Math.max(maxDateSlots, row.dates__uint16[d]);
}
}
if(this.options.cumulative) {
dates = (1 + maxDateSlots) * rows.length;
}
var type = this.options.cumulative ? Uint32Array: Uint8Array;
// reserve memory for all the dates
var timeIndex = new Int32Array(maxDateSlots + 1); //index-size
var timeCount = new Int32Array(maxDateSlots + 1);
var renderData = new (this.options.valueDataType || type)(dates);
var renderDataPos = new Uint32Array(dates);
prof_mem.inc(
4 * maxDateSlots + // timeIndex
4 * maxDateSlots + // timeCount
dates + //renderData
dates * 4
); //renderDataPos
prof_point_count.inc(rows.length);
var rowsPerSlot = {};
// precache pixel positions
for (var r = 0; r < rows.length; ++r) {
var row = rows[r];
x[r] = row.x__uint8 * this.options.resolution;
// fix value when it's in the tile EDGE
// TODO: this should be fixed in SQL query
if (row.y__uint8 === -1) {
y[r] = 0;
} else {
y[r] = row.y__uint8 * this.options.resolution;
}
var dates = row.dates__uint16;
var vals = row.vals__uint8;
if (!this.options.cumulative) {
for (var j = 0, len = dates.length; j < len; ++j) {
var rr = rowsPerSlot[dates[j]] || (rowsPerSlot[dates[j]] = []);
if(this.options.cumulative) {
vals[j] += prev_val;
}
prev_val = vals[j];
rr.push([r, vals[j]]);
}
} else {
var valByDate = {}
for (var j = 0, len = dates.length; j < len; ++j) {
valByDate[dates[j]] = vals[j];
}
var accum = 0;
// extend the latest to the end
for (var j = dates[0]; j <= maxDateSlots; ++j) {
var rr = rowsPerSlot[j] || (rowsPerSlot[j] = []);
var v = valByDate[j];
if (v) {
accum += v;
}
rr.push([r, accum]);
}
/*var lastDateSlot = dates[dates.length - 1];
for (var j = lastDateSlot + 1; j <= maxDateSlots; ++j) {
var rr = rowsPerSlot[j] || (rowsPerSlot[j] = []);
rr.push([r, prev_val]);
}
*/
}
}
// for each timeslot search active buckets
var renderDataIndex = 0;
var timeSlotIndex = 0;
var i = 0;
for(var i = 0; i <= maxDateSlots; ++i) {
var c = 0;
var slotRows = rowsPerSlot[i]
if(slotRows) {
for (var r = 0; r < slotRows.length; ++r) {
var rr = slotRows[r];
++c;
renderDataPos[renderDataIndex] = rr[0]
renderData[renderDataIndex] = rr[1];
++renderDataIndex;
}
}
timeIndex[i] = timeSlotIndex;
timeCount[i] = c;
timeSlotIndex += c;
}
prof_process_time.end();
return {
x: x,
y: y,
z: zoom,
coord: {
x: coord.x,
y: coord.y,
z: zoom
},
timeCount: timeCount,
timeIndex: timeIndex,
renderDataPos: renderDataPos,
renderData: renderData,
maxDate: maxDateSlots
};
},
_extraParams: function() {
if (this.options.extra_params) {
var p = [];
for(var k in this.options.extra_params) {
var v = this.options.extra_params[k];
if (v) {
p.push(k + "=" + encodeURIComponent(v));
}
}
return p.join('&');
}
return null;
},
isHttps: function() {
return this.options.sql_api_protocol && this.options.sql_api_protocol === 'https';
},
// execute actual query
getTileData: function(coord, zoom, callback) {
var template = this.tileUrl(coord, zoom);
var self = this;
var fetchTime = Profiler.metric('jsonarray:fetch time');
fetchTime.start();
torque.net.get(template, function (data) {
fetchTime.end();
if (data) {
data = self.proccessTile(JSON.parse(data.responseText).tile, coord, zoom);
}
callback(data);
});
},
url: function() {
return this.options.url;
},
tileUrl: function(coord, zoom) {
var template = this.url();
var s = (this.options.subdomains || 'abcd')[(coord.x + coord.y + zoom) % 4];
return template
.replace('{x}', coord.x)
.replace('{y}', coord.y)
.replace('{z}', zoom)
.replace('{s}', s);
},
getKeySpan: function() {
return {
start: this.options.start * 1000,
end: this.options.end * 1000,
step: this.options.step,
steps: this.options.steps,
};
},
setColumn: function(column, isTime) {
this.options.column = column;
this.reload();
},
setResolution: function(res) {
this.options.resolution = res;
},
// return true if tiles has been changed
setOptions: function(opt) {
var refresh = false;
if(opt.resolution !== undefined && opt.resolution !== this.options.resolution) {
this.options.resolution = opt.resolution;
refresh = true;
}
if(opt.steps !== undefined && opt.steps !== this.options.steps) {
this.setSteps(opt.steps, { silent: true });
refresh = true;
}
if(opt.column !== undefined && opt.column !== this.options.column) {
this.options.column = opt.column;
refresh = true;
}
if(opt.countby !== undefined && opt.countby !== this.options.countby) {
this.options.countby = opt.countby;
refresh = true;
}
return refresh;
},
getSteps: function() {
return Math.min(this.options.steps, this.options.data_steps);
},
setSteps: function(steps, opt) {
},
getBounds: function() {
}
};
torque.providers.json = json;
})(typeof exports === "undefined" ? this : exports);