Compare commits
51 Commits
master
...
ae-workers
Author | SHA1 | Date | |
---|---|---|---|
|
1895d0551d | ||
|
50fa5c356e | ||
|
5d143d4270 | ||
|
5e2bf59c4e | ||
|
e6e6664e28 | ||
|
b7f1253c95 | ||
|
4e6744abe9 | ||
|
6d16120336 | ||
|
ee332d0762 | ||
|
bd479fc507 | ||
|
f3e9e2e3f2 | ||
|
ac5b2aaf7b | ||
|
52dbe5fd33 | ||
|
edd49ec100 | ||
|
4b6c680ae9 | ||
|
f283167c41 | ||
|
32d2334c18 | ||
|
944b5c500d | ||
|
285260b8b0 | ||
|
62b9de79aa | ||
|
884144a442 | ||
|
c524df2f01 | ||
|
6ded5dafc9 | ||
|
aa79eeda20 | ||
|
dc7516e9e6 | ||
|
3f9a8f4a18 | ||
|
deacb1ae00 | ||
|
83b6b3d058 | ||
|
a8a8d79d86 | ||
|
204df7829d | ||
|
98a37771ca | ||
|
6a0fe4beb7 | ||
|
68ff448f78 | ||
|
171a5b6e0c | ||
|
7f72ae01af | ||
|
8e8ae30dc8 | ||
|
3fe1c60d61 | ||
|
51fc104793 | ||
|
d048652ba9 | ||
|
9479b214fa | ||
|
43e6cb7a37 | ||
|
4f41136d24 | ||
|
a36fd0936a | ||
|
bc37a754a8 | ||
|
7ad4f21cc7 | ||
|
ea7f4efb23 | ||
|
169de36bcf | ||
|
4b240a4485 | ||
|
d7d7396ba8 | ||
|
cd396b19c3 | ||
|
82c5ef2b96 |
10
Makefile
10
Makefile
@ -9,20 +9,10 @@ JS_CLIENT_FILES= lib/torque/*.js \
|
|||||||
lib/torque/leaflet/canvas_layer.js \
|
lib/torque/leaflet/canvas_layer.js \
|
||||||
lib/torque/leaflet/torque.js
|
lib/torque/leaflet/torque.js
|
||||||
|
|
||||||
all: dist/torque.js dist/torque.full.js
|
|
||||||
|
|
||||||
dist/torque.full.uncompressed.js: dist_folder dist/torque.uncompressed.js
|
dist/torque.full.uncompressed.js: dist_folder dist/torque.uncompressed.js
|
||||||
$(BROWSERIFY) lib/torque/index.js --standalone torque > dist/torque.full.uncompressed.js
|
$(BROWSERIFY) lib/torque/index.js --standalone torque > dist/torque.full.uncompressed.js
|
||||||
|
|
||||||
dist/torque.full.js: dist_folder dist/torque.full.uncompressed.js
|
|
||||||
$(UGLIFYJS) dist/torque.full.uncompressed.js > dist/torque.full.js
|
|
||||||
|
|
||||||
dist/torque.uncompressed.js: dist_folder $(JS_CLIENT_FILES)
|
|
||||||
$(BROWSERIFY) lib/torque/index.js --no-bundle-external --standalone torque > dist/torque.uncompressed.js
|
|
||||||
|
|
||||||
dist/torque.js: dist_folder dist/torque.uncompressed.js
|
|
||||||
$(UGLIFYJS) dist/torque.uncompressed.js > dist/torque.js
|
|
||||||
|
|
||||||
dist_folder:
|
dist_folder:
|
||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
|
|
||||||
|
@ -16,11 +16,12 @@
|
|||||||
// define the torque layer style using cartocss
|
// define the torque layer style using cartocss
|
||||||
var CARTOCSS = [
|
var CARTOCSS = [
|
||||||
'Map {',
|
'Map {',
|
||||||
'-torque-time-attribute: "date";',
|
'-torque-time-attribute: "postedtime";',
|
||||||
'-torque-aggregation-function: "count(cartodb_id)";',
|
'-torque-aggregation-function: "count(cartodb_id)";',
|
||||||
'-torque-frame-count: 760;',
|
'-torque-frame-count: 1024;',
|
||||||
'-torque-animation-duration: 15;',
|
'-torque-animation-duration: 30;',
|
||||||
'-torque-resolution: 2',
|
'-torque-resolution: 2',
|
||||||
|
//'-torque-data-aggregation:cumulative;',
|
||||||
'}',
|
'}',
|
||||||
'#layer {',
|
'#layer {',
|
||||||
' marker-width: 3;',
|
' marker-width: 3;',
|
||||||
@ -33,16 +34,14 @@
|
|||||||
' [value > 5] { marker-fill: #CC4C02; }',
|
' [value > 5] { marker-fill: #CC4C02; }',
|
||||||
' [value > 6] { marker-fill: #993404; }',
|
' [value > 6] { marker-fill: #993404; }',
|
||||||
' [value > 7] { marker-fill: #662506; }',
|
' [value > 7] { marker-fill: #662506; }',
|
||||||
' [frame-offset = 1] { marker-width: 10; marker-fill-opacity: 0.05;}',
|
|
||||||
' [frame-offset = 2] { marker-width: 15; marker-fill-opacity: 0.02;}',
|
|
||||||
'}'
|
'}'
|
||||||
].join('\n');
|
].join('\n');
|
||||||
|
|
||||||
|
|
||||||
var map = new L.Map('map', {
|
var map = new L.Map('map', {
|
||||||
zoomControl: true,
|
zoomControl: true,
|
||||||
center: [40, 0],
|
center: [54.1109429427243, -2.724609375],
|
||||||
zoom: 3
|
zoom: 6
|
||||||
});
|
});
|
||||||
|
|
||||||
L.tileLayer('http://{s}.api.cartocdn.com/base-dark/{z}/{x}/{y}.png', {
|
L.tileLayer('http://{s}.api.cartocdn.com/base-dark/{z}/{x}/{y}.png', {
|
||||||
@ -50,8 +49,8 @@
|
|||||||
}).addTo(map);
|
}).addTo(map);
|
||||||
|
|
||||||
var torqueLayer = new L.TorqueLayer({
|
var torqueLayer = new L.TorqueLayer({
|
||||||
user : 'viz2',
|
user : 'fdansv',
|
||||||
table : 'ow',
|
table : 'twitter_jeremyclarkson_clarksonsacked_',
|
||||||
cartocss: CARTOCSS
|
cartocss: CARTOCSS
|
||||||
});
|
});
|
||||||
torqueLayer.error(function(err){
|
torqueLayer.error(function(err){
|
||||||
@ -60,7 +59,7 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
torqueLayer.addTo(map);
|
torqueLayer.addTo(map);
|
||||||
torqueLayer.play()
|
torqueLayer.play();
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
// types
|
// types
|
||||||
var types = {
|
var types = {
|
||||||
Uint8Array: typeof(global['Uint8Array']) !== 'undefined' ? global.Uint8Array : Array,
|
Uint8Array: typeof(global['Uint8Array']) !== 'undefined' ? global.Uint8Array : Array,
|
||||||
|
Uint8ClampedArray: typeof(global['Uint8ClampedArray']) !== 'undefined' ? global.Uint8ClampedArray: Array,
|
||||||
Uint32Array: typeof(global['Uint32Array']) !== 'undefined' ? global.Uint32Array : Array,
|
Uint32Array: typeof(global['Uint32Array']) !== 'undefined' ? global.Uint32Array : Array,
|
||||||
Int16Array: typeof(global['Int16Array']) !== 'undefined' ? global.Int16Array : Array,
|
Int16Array: typeof(global['Int16Array']) !== 'undefined' ? global.Int16Array : Array,
|
||||||
Int32Array: typeof(global['Int32Array']) !== 'undefined' ? global.Int32Array: Array
|
Int32Array: typeof(global['Int32Array']) !== 'undefined' ? global.Int32Array: Array
|
||||||
|
@ -60,6 +60,8 @@ L.TorqueLayer = L.CanvasLayer.extend({
|
|||||||
this.setDuration = this.animator.duration.bind(this.animator);
|
this.setDuration = this.animator.duration.bind(this.animator);
|
||||||
this.isRunning = this.animator.isRunning.bind(this.animator);
|
this.isRunning = this.animator.isRunning.bind(this.animator);
|
||||||
|
|
||||||
|
this.lastStep = 0;
|
||||||
|
|
||||||
|
|
||||||
L.CanvasLayer.prototype.initialize.call(this, options);
|
L.CanvasLayer.prototype.initialize.call(this, options);
|
||||||
|
|
||||||
@ -212,7 +214,9 @@ L.TorqueLayer = L.CanvasLayer.extend({
|
|||||||
var canvas = this.getCanvas();
|
var canvas = this.getCanvas();
|
||||||
this.renderer.clearCanvas();
|
this.renderer.clearCanvas();
|
||||||
var ctx = canvas.getContext('2d');
|
var ctx = canvas.getContext('2d');
|
||||||
|
var brrr = false;
|
||||||
|
|
||||||
|
|
||||||
for(t in this._tiles) {
|
for(t in this._tiles) {
|
||||||
tile = this._tiles[t];
|
tile = this._tiles[t];
|
||||||
if (tile) {
|
if (tile) {
|
||||||
@ -228,12 +232,17 @@ L.TorqueLayer = L.CanvasLayer.extend({
|
|||||||
// when the tile has a cached image just render it and avoid to render
|
// when the tile has a cached image just render it and avoid to render
|
||||||
// all the points
|
// all the points
|
||||||
this.renderer._ctx.drawImage(tile._tileCache, 0, 0);
|
this.renderer._ctx.drawImage(tile._tileCache, 0, 0);
|
||||||
|
brrr = true;
|
||||||
} else {
|
} else {
|
||||||
this.renderer.renderTile(tile, this.key);
|
this.renderer.renderTile(tile, this.key, pos);
|
||||||
|
brrr = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.renderer.applyFilters();
|
var full = this.getStep() < this.lastStep;
|
||||||
|
this.lastStep = this.getStep();
|
||||||
|
if(!brrr) this.renderer.flush(full);
|
||||||
|
//this.renderer.applyFilters();
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
var Uint8Array = torque.types.Uint8Array;
|
var Uint8Array = torque.types.Uint8Array;
|
||||||
var Int32Array = torque.types.Int32Array;
|
var Int32Array = torque.types.Int32Array;
|
||||||
var Uint32Array = torque.types.Uint32Array;
|
var Uint32Array = torque.types.Uint32Array;
|
||||||
|
var Uint8ClampedArray = torque.types.Uint8ClampedArray;
|
||||||
|
|
||||||
// format('hello, {0}', 'rambo') -> "hello, rambo"
|
// format('hello, {0}', 'rambo') -> "hello, rambo"
|
||||||
function format(str) {
|
function format(str) {
|
||||||
@ -82,7 +83,7 @@
|
|||||||
dates = (1 + maxDateSlots) * rows.length;
|
dates = (1 + maxDateSlots) * rows.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
var type = this.options.cumulative ? Uint32Array: Uint8Array;
|
var type = this.options.cumulative ? Uint32Array: Uint8ClampedArray;
|
||||||
|
|
||||||
// reserve memory for all the dates
|
// reserve memory for all the dates
|
||||||
var timeIndex = new Int32Array(maxDateSlots + 1); //index-size
|
var timeIndex = new Int32Array(maxDateSlots + 1); //index-size
|
||||||
|
321
lib/torque/renderer/ball.js
Normal file
321
lib/torque/renderer/ball.js
Normal file
@ -0,0 +1,321 @@
|
|||||||
|
////////////////////////////////
|
||||||
|
// Torque BallRenderer
|
||||||
|
// CartoDB, 2015
|
||||||
|
// developed by Francisco Dans
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
var work = require('webworkify');
|
||||||
|
|
||||||
|
function BallRenderer(thecanvas){
|
||||||
|
this.canvas = thecanvas;
|
||||||
|
this.ctx = this.canvas.getContext("2d");
|
||||||
|
this.width = this.canvas.width;
|
||||||
|
this.height = this.canvas.height;
|
||||||
|
this.size = this.width * this.height;
|
||||||
|
this.pointLayer = new Uint8ClampedArray(this.size * 4);
|
||||||
|
this.radius = 15;
|
||||||
|
this.drawnTemp = {};
|
||||||
|
this.prof = 0;
|
||||||
|
this.gradient = {};
|
||||||
|
this.ballsSoFar = 0;
|
||||||
|
this.balls = 0;
|
||||||
|
this.cumulative = true;
|
||||||
|
this.RW4 = this.radius * this.width *4;
|
||||||
|
this.availableWorkers = 10;
|
||||||
|
}
|
||||||
|
BallRenderer.prototype = {
|
||||||
|
getBallIndices: function(x, y){
|
||||||
|
var indices = new Int8Array(2*r+1);
|
||||||
|
|
||||||
|
return indices;
|
||||||
|
},
|
||||||
|
addBall: function(x0, y0){
|
||||||
|
if(this.cachedBall){
|
||||||
|
var startingPoint = this.getRIndexPos(x0, y0) - this.RW4 - this.radius*4;
|
||||||
|
var i = 0,
|
||||||
|
pointer = startingPoint,
|
||||||
|
ballWidth = (this.radius*2)*4
|
||||||
|
linemax = startingPoint + 2 * this.radius*4,
|
||||||
|
endPoint = this.getRIndexPos(x0, y0) + this.RW4 + this.radius;
|
||||||
|
while (pointer <= endPoint){
|
||||||
|
while (pointer <= linemax){
|
||||||
|
this.pointLayer[pointer+3] += this.cachedBall[i+3];
|
||||||
|
i+=4;
|
||||||
|
pointer+=4;
|
||||||
|
}
|
||||||
|
linemax += this.width * 4;
|
||||||
|
pointer += this.width * 4 - ballWidth -4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
this.precacheBall(x0,y0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
precacheBall: function(x0, y0){
|
||||||
|
this.cachedBall = new Uint8ClampedArray(Math.pow(2 * this.radius + 1, 2)*4);
|
||||||
|
x0 = this.radius;
|
||||||
|
y0 = this.radius;
|
||||||
|
var orad = this.radius;
|
||||||
|
var x = this.radius;
|
||||||
|
var y = 0;
|
||||||
|
var radiusError = 1 - x;
|
||||||
|
while (x >= y){
|
||||||
|
// Try not to touch the following, it's a pain in the ass to write
|
||||||
|
this.horizontalLine(-x + x0, x + x0, y + y0, x0, y0);
|
||||||
|
this.horizontalLine(-y + x0, y + x0, -x + y0, x0, y0);
|
||||||
|
this.horizontalLine(-x + x0, x + x0, -y + y0, x0, y0);
|
||||||
|
this.horizontalLine(-y + x0, y + x0, x + y0, x0, y0);
|
||||||
|
++y;
|
||||||
|
if (radiusError<0){
|
||||||
|
radiusError += 2 * y + 1;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
--x;
|
||||||
|
radiusError += 2 * (y - x) + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.drawnTemp = {};
|
||||||
|
|
||||||
|
},
|
||||||
|
horizontalLine: function(xi, xf, yi, x0, y0){
|
||||||
|
// Assumes xi is on the left and xf is on the right
|
||||||
|
if(typeof this.drawnTemp[yi] === "undefined"){
|
||||||
|
while (xi <= xf){
|
||||||
|
this.addPoint(xi, yi, 20 - ((20 * this.lineDistance(xi, yi,x0,y0)) / this.radius));
|
||||||
|
++xi;
|
||||||
|
}
|
||||||
|
this.drawnTemp[yi]=true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
addPoint: function(x, y, alpha){
|
||||||
|
var indexPos = (y*(this.radius*2+1)+x)*4;
|
||||||
|
this.cachedBall[indexPos + 3] += alpha;
|
||||||
|
},
|
||||||
|
map_range: function(value, low1, high1, low2, high2) {
|
||||||
|
return low2 + (high2 - low2) * (value - low1) / (high1 - low1);
|
||||||
|
},
|
||||||
|
lineDistance: function(x,y,x0,y0){
|
||||||
|
var xs = Math.pow(x - x0, 2);
|
||||||
|
var ys = Math.pow(y - y0, 2);
|
||||||
|
return Math.sqrt( xs + ys );
|
||||||
|
},
|
||||||
|
draw: function(dataArray){
|
||||||
|
if (!dataArray){
|
||||||
|
if (this.isoplethLayer) dataArray = this.isoplethLayer;
|
||||||
|
else if (this.contourLayer) dataArray = this.contourLayer;
|
||||||
|
else if (this.heatmapLayer) dataArray = this.heatmapLayer;
|
||||||
|
else dataArray = this.pointLayer;
|
||||||
|
}
|
||||||
|
if(!this.imageData) this.imageData = this.ctx.createImageData(this.width, this.height);
|
||||||
|
this.imageData.data.set(dataArray);
|
||||||
|
this.ctx.putImageData(this.imageData, 0, 0);
|
||||||
|
},
|
||||||
|
mergeLayers: function(from, to){
|
||||||
|
if (from.length !== to.length) throw("layers aren't of the same size"); return;
|
||||||
|
for (var i = 0; i<to.length; i++){
|
||||||
|
if(from[i+3]>0){
|
||||||
|
// There's a better way of doing this but I was lazy.
|
||||||
|
to[i] = from[i];
|
||||||
|
to[i+1] = from[i+1];
|
||||||
|
to[i+2] = from[i+2];
|
||||||
|
to[i+3] = from[i+3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
reduceArray: function(){
|
||||||
|
|
||||||
|
},
|
||||||
|
expandArray: function(){
|
||||||
|
|
||||||
|
},
|
||||||
|
invalidate: function(full){
|
||||||
|
// if(!transit){
|
||||||
|
for (var i = 0, len = this.pointLayer.length; i< len; i+=4){
|
||||||
|
this.pointLayer[i + 3] -= 10;
|
||||||
|
}
|
||||||
|
// }
|
||||||
|
// else{
|
||||||
|
// this.pointLayer = new Uint8ClampedArray(this.size * 4);
|
||||||
|
// }
|
||||||
|
|
||||||
|
if(this.heatmapLayer){
|
||||||
|
this.heatmapLayer = new Uint8ClampedArray(this.size * 4);
|
||||||
|
}
|
||||||
|
else if(this.contourLayer){
|
||||||
|
this.contourLayer = new Uint8ClampedArray(this.size * 4);
|
||||||
|
if(this.isoplethLayer){
|
||||||
|
this.isoplethLayer = new Uint8ClampedArray(this.size * 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getRIndexPos: function(x,y){
|
||||||
|
var rIndexPos = (y*this.width+x)*4;
|
||||||
|
return rIndexPos;
|
||||||
|
},
|
||||||
|
getXYFromRIndex: function(index){
|
||||||
|
var x = (index % (this.width*4))/4;
|
||||||
|
var y = (index - 4 * x) / (4 * this.width);
|
||||||
|
return [x,y];
|
||||||
|
},
|
||||||
|
// Clockwise. Again, there definitely is a better way.
|
||||||
|
getNeighbors: function(index){
|
||||||
|
var tw = this.width*4;
|
||||||
|
var n = index - tw;
|
||||||
|
var s = index + tw;
|
||||||
|
return [n, n + 4, index + 4, s + 4, s, s - 4, index - 4, n -4];
|
||||||
|
},
|
||||||
|
isEmpty: function(layer) {
|
||||||
|
for (var i = 0; i<layer.length; i+=4){
|
||||||
|
if(layer[i+3] > 0) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
isInvalid: function(layer) {
|
||||||
|
for (var i = 0; i<layer.length; i+=4){
|
||||||
|
if(layer[i+3]!==0 && !layer[i+3]) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
createArray: function(){
|
||||||
|
return new Uint8ClampedArray(this.size * 4);
|
||||||
|
},
|
||||||
|
contour: function(granularity){
|
||||||
|
if(!this.contourGradient){
|
||||||
|
var step = 255/granularity;
|
||||||
|
var i = 0, a = new Uint8ClampedArray(granularity+1), c=0;
|
||||||
|
while (i<255){
|
||||||
|
a[c] = i;
|
||||||
|
i += step;
|
||||||
|
c++;
|
||||||
|
}
|
||||||
|
a[a.length-1] = 255;
|
||||||
|
var l = -step/2;
|
||||||
|
this.contourGradient = new Uint8ClampedArray(1024);
|
||||||
|
for(var i = 0; i<a.length; i++){
|
||||||
|
var y = Math.round(i*step);
|
||||||
|
var thisAlpha = a[i];
|
||||||
|
while(y<step*(i+1)){
|
||||||
|
this.contourGradient[y*4+3] = thisAlpha;
|
||||||
|
y++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var gradient = this.contourGradient;
|
||||||
|
if(!this.contourLayer) this.contourLayer = new Uint8ClampedArray(this.size * 4);
|
||||||
|
for (var i = this.pointLayer.length-4, alpha; i>=0; i-=4){
|
||||||
|
if(this.pointLayer[i+3] > 0){
|
||||||
|
var currentAlpha = this.pointLayer[i+3];
|
||||||
|
this.contourLayer[i+0] = 255;
|
||||||
|
this.contourLayer[i+1] = 105;
|
||||||
|
this.contourLayer[i+2] = 180;
|
||||||
|
this.contourLayer[i+3] = gradient[currentAlpha*4+3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isopleth: function(){
|
||||||
|
var iso = this.createArray();
|
||||||
|
var contour = this.contourLayer;
|
||||||
|
var eq = false;
|
||||||
|
for (var i = 0, len = contour.length; i<len; i+=4){
|
||||||
|
if(!eq){
|
||||||
|
var alpha = contour[i + 3];
|
||||||
|
if (alpha > 0 && alpha < 255){
|
||||||
|
var neighbors = this.getNeighbors(i);
|
||||||
|
var refCol = contour[neighbors[0]+3];
|
||||||
|
for (var n = 1, ln = neighbors.length; n<ln; ++n){
|
||||||
|
if (contour[neighbors[n]+3] !== refCol) break;
|
||||||
|
if (n === ln-1) eq = true;
|
||||||
|
}
|
||||||
|
if(!eq){
|
||||||
|
for (var n = 0, ln = neighbors.length; n<ln; ++n){
|
||||||
|
var index = neighbors[n];
|
||||||
|
if(index>0 && (contour[index+3] === 0 || contour[index+3] !== contour[i+3])){
|
||||||
|
iso[index + 3] = contour[i+3];
|
||||||
|
iso[index] = 153;
|
||||||
|
iso[index + 1] = 60;
|
||||||
|
iso[index + 2] = 243;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
eq = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.isoplethLayer = iso;
|
||||||
|
},
|
||||||
|
|
||||||
|
heatmap: function (gradient){
|
||||||
|
if(!gradient) gradient = {
|
||||||
|
0.4: 'blue',
|
||||||
|
0.6: 'cyan',
|
||||||
|
0.7: 'lightgreen',
|
||||||
|
0.9: 'yellow',
|
||||||
|
1.0: 'red'
|
||||||
|
};
|
||||||
|
if(JSON.stringify(this.gradient) !== JSON.stringify(gradient)){
|
||||||
|
this.gradient = gradient;
|
||||||
|
// create a 256x1 gradient that we'll use to turn a grayscale heatmap into a colored one
|
||||||
|
var canvas = document.createElement('canvas'),
|
||||||
|
ctx = canvas.getContext('2d'),
|
||||||
|
gradientColours = ctx.createLinearGradient(0, 0, 0, 256);
|
||||||
|
|
||||||
|
canvas.width = 1;
|
||||||
|
canvas.height = 256;
|
||||||
|
|
||||||
|
for (var i in gradient) {
|
||||||
|
gradientColours.addColorStop(+i, gradient[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.fillStyle = gradientColours;
|
||||||
|
ctx.fillRect(0, 0, 1, 256);
|
||||||
|
this.gradientData = ctx.getImageData(0, 0, 1, 256).data;
|
||||||
|
}
|
||||||
|
this.colorize();
|
||||||
|
},
|
||||||
|
|
||||||
|
colorize: function () {
|
||||||
|
if (!this.colorWorkers){
|
||||||
|
this.initColorWorkers();
|
||||||
|
}
|
||||||
|
for(k in this.colorWorkers){
|
||||||
|
this.colorWorkers[k].postMessage([this.pointLayer,k, 3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
var grad = this.gradientData;
|
||||||
|
this.heatmapLayer = new Uint8ClampedArray(this.size * 4);
|
||||||
|
// while(Object.keys(this.loadedColorizedChunks).length < this.colorWorkers.length){
|
||||||
|
// console.log(Object.keys(this.loadedColorizedChunks).length)
|
||||||
|
// }
|
||||||
|
// var i = 0;
|
||||||
|
// while(i<=this.colorWorkers.length){
|
||||||
|
// this.heatmapLayer += this.loadedColorizedChunks[i];
|
||||||
|
// }
|
||||||
|
// for (var i = this.pointLayer.length-4, alpha; i>=0; i-=4){
|
||||||
|
// alpha = this.pointLayer[i+3] * 4; // get gradient color from opacity value
|
||||||
|
// if (alpha>0) {
|
||||||
|
// this.heatmapLayer[i] = grad[alpha]; // R
|
||||||
|
// this.heatmapLayer[i + 1] = grad[alpha + 1]; // G
|
||||||
|
// this.heatmapLayer[i + 2] = grad[alpha + 2]; // B
|
||||||
|
// this.heatmapLayer[i + 3] = alpha; // A
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
|
||||||
|
initColorWorkers: function(){
|
||||||
|
var workerCount = 3
|
||||||
|
this.colorWorkers = [];
|
||||||
|
if(!this.loadedColorizedChunks) this.loadedColorizedChunks = {};
|
||||||
|
for (var i = 0; i < workerCount; i++) {
|
||||||
|
var worker = work(require('./colorWorker.js'));
|
||||||
|
worker.grad = this.gradientData;
|
||||||
|
worker.onmessage = function(data){
|
||||||
|
this.loadedColorizedChunks[data.data[0]] = data.data[1];
|
||||||
|
}
|
||||||
|
this.colorWorkers.push(worker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = BallRenderer;
|
0
lib/torque/renderer/ballWorker.js
Normal file
0
lib/torque/renderer/ballWorker.js
Normal file
19
lib/torque/renderer/colorWorker.js
Normal file
19
lib/torque/renderer/colorWorker.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
onmessage = function(e){
|
||||||
|
var index = e.data[1];
|
||||||
|
var pointList = e.data[0];
|
||||||
|
var n = e.data[2]
|
||||||
|
var cs = Math.round(pointList.length/4/n);
|
||||||
|
var si = cs * index * 4;
|
||||||
|
var ei = (si + cs) * 4;
|
||||||
|
var subset = pointList.subarray(si,ei);
|
||||||
|
for (var i = subset.length-4, alpha; i>=0; i-=4){
|
||||||
|
alpha = subset[i+3] * 4; // get gradient color from opacity value
|
||||||
|
if (alpha>0) {
|
||||||
|
subset[i] = this.gradientData[alpha]; // R
|
||||||
|
subset[i + 1] = this.gradientData[alpha + 1]; // G
|
||||||
|
subset[i + 2] = this.gradientData[alpha + 2]; // B
|
||||||
|
subset[i + 3] = alpha; // A
|
||||||
|
}
|
||||||
|
}
|
||||||
|
postMessage([index, subset]);
|
||||||
|
}
|
@ -2,7 +2,8 @@ var torque = require('../');
|
|||||||
var cartocss = require('./cartocss_render');
|
var cartocss = require('./cartocss_render');
|
||||||
var Profiler = require('../profiler');
|
var Profiler = require('../profiler');
|
||||||
var carto = global.carto || require('carto');
|
var carto = global.carto || require('carto');
|
||||||
var Filters = require('./torque_filters');
|
var filters = require('./torque_filters');
|
||||||
|
var ballRenderer = require('./ball.js');
|
||||||
|
|
||||||
var TAU = Math.PI * 2;
|
var TAU = Math.PI * 2;
|
||||||
var DEFAULT_CARTOCSS = [
|
var DEFAULT_CARTOCSS = [
|
||||||
@ -51,8 +52,7 @@ var Filters = require('./torque_filters');
|
|||||||
this._sprites = []; // sprites per layer
|
this._sprites = []; // sprites per layer
|
||||||
this._shader = null;
|
this._shader = null;
|
||||||
this._icons = {};
|
this._icons = {};
|
||||||
this._iconsToLoad = 0;
|
this._filters = filters(this._canvas);
|
||||||
this._filters = new Filters(this._canvas, {canvasClass: options.canvasClass});
|
|
||||||
this.setCartoCSS(this.options.cartocss || DEFAULT_CARTOCSS);
|
this.setCartoCSS(this.options.cartocss || DEFAULT_CARTOCSS);
|
||||||
this.TILE_SIZE = 256;
|
this.TILE_SIZE = 256;
|
||||||
this._style = null;
|
this._style = null;
|
||||||
@ -129,29 +129,20 @@ var Filters = require('./torque_filters');
|
|||||||
}
|
}
|
||||||
|
|
||||||
var canvas = this._createCanvas();
|
var canvas = this._createCanvas();
|
||||||
|
// take into account the exterior ring to calculate the size
|
||||||
|
var canvasSize = (st['marker-line-width'] || 0) + pointSize*2;
|
||||||
var ctx = canvas.getContext('2d');
|
var ctx = canvas.getContext('2d');
|
||||||
|
var w = ctx.width = canvas.width = ctx.height = canvas.height = Math.ceil(canvasSize);
|
||||||
|
ctx.translate(w/2, w/2);
|
||||||
|
|
||||||
var markerFile = st["marker-file"] || st["point-file"];
|
var img_name = this._qualifyURL(st["marker-file"] || st["point-file"]);
|
||||||
var qualifiedUrl = markerFile && this._qualifyURL(markerFile);
|
if (img_name && this._icons.itemsToLoad <= 0) {
|
||||||
|
var img = this._icons[img_name];
|
||||||
if (qualifiedUrl && this._iconsToLoad <= 0 && this._icons[qualifiedUrl]) {
|
img.w = st['marker-width'] || img.width;
|
||||||
var img = this._icons[qualifiedUrl];
|
img.h = st['marker-width'] || st['marker-height'];
|
||||||
|
cartocss.renderSprite(ctx, img, st);
|
||||||
var dWidth = st['marker-width'] * 2 || img.width;
|
}
|
||||||
var dHeight = (st['marker-height'] || dWidth) * (img.width / img.height);
|
else {
|
||||||
|
|
||||||
canvas.width = ctx.width = dWidth;
|
|
||||||
canvas.height = ctx.height = dHeight;
|
|
||||||
|
|
||||||
ctx.scale(dWidth/img.width, dHeight/img.height);
|
|
||||||
|
|
||||||
cartocss.renderSprite(ctx, img, st);
|
|
||||||
} else {
|
|
||||||
// take into account the exterior ring to calculate the size
|
|
||||||
var canvasSize = (st['marker-line-width'] || 0) + pointSize*2;
|
|
||||||
var w = ctx.width = canvas.width = ctx.height = canvas.height = Math.ceil(canvasSize);
|
|
||||||
ctx.translate(w/2, w/2);
|
|
||||||
|
|
||||||
var mt = st['marker-type'];
|
var mt = st['marker-type'];
|
||||||
if (mt && mt === 'rectangle') {
|
if (mt && mt === 'rectangle') {
|
||||||
cartocss.renderRectangle(ctx, st);
|
cartocss.renderRectangle(ctx, st);
|
||||||
@ -172,31 +163,27 @@ 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, key,pos) {
|
||||||
if (this._iconsToLoad > 0) {
|
if(typeof this.ballRenderer === "undefined"){
|
||||||
this.on('allIconsLoaded', function() {
|
this.ballRenderer = new ballRenderer(this._canvas);
|
||||||
this.renderTile.apply(this, [tile, key, callback]);
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
var prof = Profiler.metric('torque.renderer.point.renderLayers').start();
|
|
||||||
var layers = this._shader.getLayers();
|
|
||||||
for(var i = 0, n = layers.length; i < n; ++i ) {
|
|
||||||
var layer = layers[i];
|
|
||||||
if (layer.name() !== "Map") {
|
|
||||||
var sprites = this._sprites[i] || (this._sprites[i] = {});
|
|
||||||
// frames for each layer
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
this._renderTile(tile, key, 0, null, null, null, pos);
|
||||||
|
// var prof = Profiler.metric('torque.renderer.point.renderLayers').start();
|
||||||
|
// var layers = this._shader.getLayers();
|
||||||
|
// for(var i = 0, n = layers.length; i < n; ++i ) {
|
||||||
|
// var layer = layers[i];
|
||||||
|
// if (layer.name() !== "Map") {
|
||||||
|
// var sprites = this._sprites[i] || (this._sprites[i] = {});
|
||||||
|
// // frames for each layer
|
||||||
|
// 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);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
prof.end(true);
|
// prof.end(true);
|
||||||
|
|
||||||
return callback && callback(null);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_createCanvas: function() {
|
_createCanvas: function() {
|
||||||
@ -210,21 +197,6 @@ var Filters = require('./torque_filters');
|
|||||||
? new this.options.imageClass()
|
? new this.options.imageClass()
|
||||||
: new Image();
|
: new Image();
|
||||||
},
|
},
|
||||||
|
|
||||||
_setImageSrc: function(img, url, callback) {
|
|
||||||
if (this.options.setImageSrc) {
|
|
||||||
this.options.setImageSrc(img, url, callback);
|
|
||||||
} else {
|
|
||||||
img.onload = function(){
|
|
||||||
callback(null);
|
|
||||||
};
|
|
||||||
img.onerror = function(){
|
|
||||||
callback(new Error('Could not load image'));
|
|
||||||
};
|
|
||||||
img.src = url;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_qualifyURL: function(url) {
|
_qualifyURL: function(url) {
|
||||||
if (typeof this.options.qualifyURL !== "undefined"){
|
if (typeof this.options.qualifyURL !== "undefined"){
|
||||||
return this.options.qualifyURL(url);
|
return this.options.qualifyURL(url);
|
||||||
@ -240,42 +212,43 @@ var Filters = require('./torque_filters');
|
|||||||
// renders a tile in the canvas for key defined in
|
// renders a tile in the canvas for key defined in
|
||||||
// the torque tile
|
// the torque tile
|
||||||
//
|
//
|
||||||
_renderTile: function(tile, key, frame_offset, sprites, shader, shaderVars) {
|
_renderTile: function(tile, key, frame_offset, sprites, shader, shaderVars, pos) {
|
||||||
if (!this._canvas) return;
|
if (!this._canvas) return;
|
||||||
|
|
||||||
var prof = Profiler.metric('torque.renderer.point.renderTile').start();
|
var prof = Profiler.metric('torque.renderer.point.renderTile').start();
|
||||||
var ctx = this._ctx;
|
var ctx = this._ctx;
|
||||||
var blendMode = compop2canvas(shader.eval('comp-op')) || this.options.blendmode;
|
// var blendMode = compop2canvas(shader.eval('comp-op')) || this.options.blendmode;
|
||||||
if (blendMode) {
|
// if (blendMode) {
|
||||||
ctx.globalCompositeOperation = blendMode;
|
// ctx.globalCompositeOperation = blendMode;
|
||||||
}
|
// }
|
||||||
if (this.options.cumulative && key > tile.maxDate) {
|
if (this.options.cumulative && key > tile.maxDate) {
|
||||||
//TODO: precache because this tile is not going to change
|
//TODO: precache because this tile is not going to change
|
||||||
key = tile.maxDate;
|
key = tile.maxDate;
|
||||||
}
|
}
|
||||||
var tileMax = this.options.resolution * (this.TILE_SIZE/this.options.resolution - 1)
|
var tileMax = this.options.resolution * (this.TILE_SIZE/this.options.resolution - 1)
|
||||||
var activePixels = tile.timeCount[key];
|
var activePixels = tile.timeCount[key];
|
||||||
var anchor = this.options.resolution/2;
|
|
||||||
if (activePixels) {
|
if (activePixels) {
|
||||||
var pixelIndex = tile.timeIndex[key];
|
var pixelIndex = tile.timeIndex[key];
|
||||||
for(var p = 0; p < activePixels; ++p) {
|
for(var p = 0; p < activePixels; ++p) {
|
||||||
var posIdx = tile.renderDataPos[pixelIndex + p];
|
var posIdx = tile.renderDataPos[pixelIndex + p];
|
||||||
var c = tile.renderData[pixelIndex + p];
|
var c = tile.renderData[pixelIndex + p];
|
||||||
if (c) {
|
if (c) {
|
||||||
var sp = sprites[c];
|
var x = tile.x[posIdx];
|
||||||
if (sp === undefined) {
|
var y = tileMax - tile.y[posIdx]; // flip mercator
|
||||||
sp = sprites[c] = this.generateSprite(shader, c, torque.extend({ zoom: tile.z, 'frame-offset': frame_offset }, shaderVars));
|
this.ballRenderer.addBall(pos.x + x, pos.y + y);
|
||||||
}
|
// var sp = sprites[c];
|
||||||
if (sp) {
|
// if (sp === undefined) {
|
||||||
var x = tile.x[posIdx]- (sp.width >> 1) + anchor;
|
// sp = sprites[c] = this.generateSprite(shader, c, torque.extend({ zoom: tile.z, 'frame-offset': frame_offset }, shaderVars));
|
||||||
var y = tileMax - tile.y[posIdx] + anchor; // flip mercator
|
// }
|
||||||
ctx.drawImage(sp, x, y - (sp.height >> 1));
|
// if (sp) {
|
||||||
}
|
// var x = tile.x[posIdx]- (sp.width >> 1);
|
||||||
|
// var y = tileMax - tile.y[posIdx]; // flip mercator
|
||||||
|
// ctx.drawImage(sp, x, y - (sp.height >> 1));
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
prof.end(true);
|
prof.end(true);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -347,61 +320,44 @@ var Filters = require('./torque_filters');
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
_preloadIcons: function(img_names){
|
||||||
_preloadIcons: function(img_names) {
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
this._icons = {};
|
||||||
if (img_names.length > 0 && !this._forcePoints) {
|
if (img_names.length > 0 && !this._forcePoints){
|
||||||
|
for (var i = 0; i<img_names.length; i++){
|
||||||
var qualifiedImageUrlSet = Object.keys(img_names.reduce(function(imgNamesMap, imgName) {
|
var new_img = this._createImage();
|
||||||
var qualifiedUrl = self._qualifyURL(imgName);
|
this._icons[this._qualifyURL(img_names[i])] = null;
|
||||||
if (!self._icons[qualifiedUrl]) {
|
if (typeof self._icons.itemsToLoad === 'undefined'){
|
||||||
imgNamesMap[qualifiedUrl] = true;
|
this._icons.itemsToLoad = img_names.length;
|
||||||
}
|
|
||||||
return imgNamesMap;
|
|
||||||
}, {}));
|
|
||||||
|
|
||||||
var filtered = self._shader.getLayers().some(function(layer) {
|
|
||||||
return typeof layer.shader["image-filters"] !== "undefined";
|
|
||||||
});
|
|
||||||
|
|
||||||
this._iconsToLoad += qualifiedImageUrlSet.length;
|
|
||||||
|
|
||||||
qualifiedImageUrlSet.forEach(function(qualifiedImageUrl) {
|
|
||||||
self._icons[qualifiedImageUrl] = null;
|
|
||||||
|
|
||||||
var img = self._createImage();
|
|
||||||
|
|
||||||
if (filtered) {
|
|
||||||
img.crossOrigin = 'Anonymous';
|
|
||||||
}
|
}
|
||||||
|
var filtered = self._shader.getLayers().some(function(layer){return typeof layer.shader["image-filters"] !== "undefined"});
|
||||||
self._setImageSrc(img, qualifiedImageUrl, function(err) {
|
if (filtered){
|
||||||
if (err) {
|
new_img.crossOrigin = 'Anonymous';
|
||||||
self._forcePoints = true;
|
}
|
||||||
self.clearSpriteCache();
|
new_img.onload = function(e){
|
||||||
self._iconsToLoad = 0;
|
self._icons[this.src] = this;
|
||||||
self.fire("allIconsLoaded");
|
if (Object.keys(self._icons).length === img_names.length + 1){
|
||||||
if(filtered) {
|
self._icons.itemsToLoad--;
|
||||||
console.info("Only CORS-enabled, or same domain image-files can be used in combination with image-filters");
|
if (self._icons.itemsToLoad <= 0){
|
||||||
}
|
|
||||||
console.error("Couldn't get marker-file " + qualifiedImageUrl);
|
|
||||||
} else {
|
|
||||||
self._icons[qualifiedImageUrl] = img;
|
|
||||||
self._iconsToLoad--;
|
|
||||||
|
|
||||||
if (self._iconsToLoad <= 0){
|
|
||||||
self.clearSpriteCache();
|
self.clearSpriteCache();
|
||||||
self.fire("allIconsLoaded");
|
self.fire("allIconsLoaded");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
});
|
new_img.onerror = function(){
|
||||||
} else {
|
self._forcePoints = true;
|
||||||
this.fire("allIconsLoaded");
|
self.clearSpriteCache();
|
||||||
|
if(filtered){
|
||||||
|
console.info("Only CORS-enabled, or same domain image-files can be used in combination with image-filters");
|
||||||
|
}
|
||||||
|
console.error("Couldn't get marker-file " + this.src);
|
||||||
|
};
|
||||||
|
this.itemsToLoad++;
|
||||||
|
new_img.src = img_names[i];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
|
},
|
||||||
applyFilters: function(){
|
applyFilters: function(){
|
||||||
if(this._style){
|
if(this._style){
|
||||||
if(this._style['image-filters']){
|
if(this._style['image-filters']){
|
||||||
@ -439,6 +395,15 @@ var Filters = require('./torque_filters');
|
|||||||
this._filters.draw();
|
this._filters.draw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
flush: function(full){
|
||||||
|
this._ctx.setTransform(1, 0, 0, 1, 0, 0);
|
||||||
|
if(!this.ballRenderer) return;
|
||||||
|
// this.ballRenderer.contour(7);
|
||||||
|
// this.ballRenderer.isopleth();
|
||||||
|
this.ballRenderer.heatmap();
|
||||||
|
this.ballRenderer.draw();
|
||||||
|
this.ballRenderer.invalidate();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
89
package.json
89
package.json
@ -1,46 +1,49 @@
|
|||||||
{
|
{
|
||||||
"name": "torque.js",
|
"name": "torque.js",
|
||||||
"version": "2.11.1",
|
"version": "2.11.1",
|
||||||
"description": "Torque javascript library",
|
"description": "Torque javascript library",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git://github.com/CartoDB/torque.git"
|
"url": "git://github.com/CartoDB/torque.git"
|
||||||
},
|
},
|
||||||
"author": {
|
"author": {
|
||||||
"name": "CartoDB",
|
"name": "CartoDB",
|
||||||
"url": "http://cartodb.com/",
|
"url": "http://cartodb.com/",
|
||||||
"email": "wadus@cartodb.com"
|
"email": "wadus@cartodb.com"
|
||||||
},
|
},
|
||||||
"contributors": [
|
"contributors": [
|
||||||
"Andrew Hill <andrew@vizzuality.com>",
|
"Andrew Hill <andrew@vizzuality.com>",
|
||||||
"Simon Tokumine <tokumine@google.com>",
|
"Simon Tokumine <tokumine@google.com>",
|
||||||
"Javier Alvarez <jmedina@vizzuality.com>",
|
"Javier Alvarez <jmedina@vizzuality.com>",
|
||||||
"Javier Arce <javierarce@vizzuality.com>",
|
"Javier Arce <javierarce@vizzuality.com>",
|
||||||
"Javier Santana <jsantana@vizzuality.com>",
|
"Javier Santana <jsantana@vizzuality.com>",
|
||||||
"Raúl Ochoa <rochoa@cartodb.com>",
|
"Raúl Ochoa <rochoa@cartodb.com>",
|
||||||
"Nicklas Gummesson <nicklas@cartodb.com>",
|
"Nicklas Gummesson <nicklas@cartodb.com>",
|
||||||
"Francisco Dans <francisco@cartodb.com>"
|
"Francisco Dans <francisco@cartodb.com>"
|
||||||
],
|
],
|
||||||
"licenses": [{
|
"licenses": [
|
||||||
|
{
|
||||||
"type": "BSD"
|
"type": "BSD"
|
||||||
}],
|
}
|
||||||
"dependencies": {
|
],
|
||||||
"carto": "https://github.com/CartoDB/carto/archive/master.tar.gz"
|
"dependencies": {
|
||||||
},
|
"carto": "https://github.com/CartoDB/carto/archive/master.tar.gz",
|
||||||
"devDependencies": {
|
"webworkify": "^1.0.2"
|
||||||
"leaflet": "0.7.3",
|
},
|
||||||
"underscore": "^1.6.0",
|
"devDependencies": {
|
||||||
"node-qunit-phantomjs": "^1.0.0",
|
"leaflet": "0.7.3",
|
||||||
"browserify": "^7.0.0",
|
"underscore": "^1.6.0",
|
||||||
"mapnik": "https://github.com/CartoDB/node-mapnik/tarball/1.4.15-cdb1",
|
"node-qunit-phantomjs": "^1.0.0",
|
||||||
"canvas": "~1.2.1",
|
"browserify": "^7.0.0",
|
||||||
"request": "^2.53.0",
|
"mapnik": "https://github.com/CartoDB/node-mapnik/tarball/1.4.15-cdb1",
|
||||||
"qunit": "~0.7.5",
|
"canvas": "~1.2.1",
|
||||||
"qunitjs": "1.x",
|
"request": "^2.53.0",
|
||||||
"uglify-js": "1.3.3"
|
"qunit": "~0.7.5",
|
||||||
},
|
"qunitjs": "1.x",
|
||||||
"scripts": {
|
"uglify-js": "1.3.3"
|
||||||
"test": "make test-all"
|
},
|
||||||
},
|
"scripts": {
|
||||||
"main": "./lib/torque/index.js"
|
"test": "make test-all"
|
||||||
|
},
|
||||||
|
"main": "./lib/torque/index.js"
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user