Merge pull request #261 from CartoDB/turbocarto

Turbo-carto support
This commit is contained in:
Francisco Dans 2016-07-06 22:24:26 +02:00 committed by GitHub
commit b5a2e88f1c
5 changed files with 150 additions and 58 deletions

View File

@ -1,7 +1,6 @@
L.Mixin.TileLoader = {
_initTileLoader: function() {
this._tiles = {}
this._tilesLoading = {};
this._tilesToLoad = 0;
this._map.on({

View File

@ -27,6 +27,7 @@ L.TorqueLayer = L.CanvasLayer.extend({
}
options.tileLoader = true;
this.keys = [0];
this._tiles = {};
Object.defineProperty(this, 'key', {
get: function() {
return this.getKey();
@ -34,7 +35,9 @@ L.TorqueLayer = L.CanvasLayer.extend({
});
this.prevRenderedKey = 0;
if (options.cartocss) {
torque.extend(options, torque.common.TorqueLayer.optionsFromCartoCSS(options.cartocss));
// We're only passing the Map header to the global options because the parser won't like turbocarto expressions
var headerCartoCSS = options.cartocss.replace(/\n/g,'').match(/Map\s*?\{.*?}/g)[0];
torque.extend(options, torque.common.TorqueLayer.optionsFromCartoCSS(headerCartoCSS));
}
options.resolution = options.resolution || 2;
@ -78,8 +81,10 @@ L.TorqueLayer = L.CanvasLayer.extend({
if (this.options.tileJSON) this.options.provider = 'tileJSON';
this.provider = new this.providers[this.options.provider](options);
options.layer = this;
this.renderer = new this.renderers[this.options.renderer](this.getCanvas(), options);
options.ready = function() {
self.fire("change:bounds", {
bounds: self.provider.getBounds()
@ -92,6 +97,10 @@ L.TorqueLayer = L.CanvasLayer.extend({
self.setKeys(self.getKeys());
};
this.on('tileLoaded', function () {
self.renderer.setCartoCSS(self.renderer.style);
})
this.renderer.on("allIconsLoaded", this.render.bind(this));
@ -384,25 +393,25 @@ L.TorqueLayer = L.CanvasLayer.extend({
setCartoCSS: function(cartocss) {
if (this.provider.options.named_map) throw new Error("CartoCSS style on named maps is read-only");
if (!this.renderer) throw new Error('renderer is not valid');
var shader = new carto.RendererJS().render(cartocss);
this.renderer.setShader(shader);
this.renderer.setCartoCSS(cartocss, function () {
// provider options
var options = torque.common.TorqueLayer.optionsFromLayer(this.renderer._shader.findLayer({ name: 'Map' }));
this.provider.setCartoCSS && this.provider.setCartoCSS(cartocss);
if(this.provider.setOptions(options)) {
this._reloadTiles();
}
// provider options
var options = torque.common.TorqueLayer.optionsFromLayer(shader.findLayer({ name: 'Map' }));
this.provider.setCartoCSS && this.provider.setCartoCSS(cartocss);
if(this.provider.setOptions(options)) {
this._reloadTiles();
}
torque.extend(this.options, options);
torque.extend(this.options, options);
// animator options
if (options.animationDuration) {
this.animator.duration(options.animationDuration);
}
this._clearCaches();
this.redraw();
return this;
}.bind(this));
// animator options
if (options.animationDuration) {
this.animator.duration(options.animationDuration);
}
this._clearCaches();
this.redraw();
return this;
},
/**

View File

@ -0,0 +1,52 @@
var d3 = require('d3');
var jenks = require('turf-jenks');
function TorqueDataSource (tiles) {
this.tiles = tiles
}
module.exports = TorqueDataSource
TorqueDataSource.prototype.getName = function () {
return 'TorqueDataSource'
}
TorqueDataSource.prototype.getRamp = function (column, bins, method, callback) {
var ramp = []
var error = null
var values = Object.keys(this.tiles).map(function (t) {
return this.tiles[t].renderData;
}.bind(this)).reduce(function (p,c,i) {
for(var i = 0; i<c.length; i++) {
p.push(c[i]);
}
return p;
},[]);
var extent = d3.extent(values);
if (!method || method === 'equal' || method === 'jenks') {
var scale = d3.scale.linear().domain([0, bins]).range(extent)
ramp = d3.range(bins).map(scale)
} else if (method === 'quantiles') {
ramp = d3.scale.quantile().range(d3.range(bins)).domain(values).quantiles()
} else if (method === 'headstails') {
var sortedValues = values.sort(function(a, b) {
return a - b;
});
if (sortedValues.length < bins) {
error = 'Number of bins should be lower than total number of rows'
} else if (sortedValues.length === bins) {
ramp = sortedValues;
} else {
var mean = d3.mean(sortedValues);
ramp.push(mean);
for (var i = 1; i < bins; i++) {
ramp.push(d3.mean(sortedValues.filter(function (v) {
return v > ramp[length - 1];
})));
}
}
} else {
error = new Error('Quantification method ' + method + ' is not supported')
}
callback(error, ramp)
}

View File

@ -3,6 +3,8 @@ var cartocss = require('./cartocss_render');
var Profiler = require('../profiler');
var carto = global.carto || require('carto');
var Filters = require('./torque_filters');
var turbocarto = require('turbo-carto');
var CartoDatasource = require('./datasource');
var TAU = Math.PI * 2;
var DEFAULT_CARTOCSS = [
@ -62,6 +64,7 @@ var Filters = require('./torque_filters');
throw new Error("canvas can't be undefined");
}
this.options = options;
this.layer = options.layer;
this._canvas = canvas;
this._ctx = canvas.getContext('2d');
this._sprites = []; // sprites per layer
@ -69,7 +72,8 @@ var Filters = require('./torque_filters');
this._icons = {};
this._iconsToLoad = 0;
this._filters = new Filters(this._canvas, {canvasClass: options.canvasClass});
this.setCartoCSS(this.options.cartocss || DEFAULT_CARTOCSS);
this.style = this.options.cartocss || DEFAULT_CARTOCSS;
this.setCartoCSS(this.style);
this.TILE_SIZE = 256;
this._style = null;
this._gradients = {};
@ -80,18 +84,20 @@ var Filters = require('./torque_filters');
torque.extend(PointRenderer.prototype, torque.Event, {
clearCanvas: function() {
var canvas = this._canvas;
var color = this._Map['-torque-clear-color']
// shortcut for the default value
if (color === "rgba(255, 255, 255, 0)" || !color) {
this._canvas.width = this._canvas.width;
} else {
var ctx = this._ctx;
ctx.setTransform(1, 0, 0, 1, 0, 0);
var compop = this._Map['comp-op']
ctx.globalCompositeOperation = compop2canvas(compop) || compop;
ctx.fillStyle = color;
ctx.fillRect(0, 0, canvas.width, canvas.height);
if (this._Map) {
var canvas = this._canvas;
var color = this._Map['-torque-clear-color']
// shortcut for the default value
if (color === "rgba(255, 255, 255, 0)" || !color) {
this._canvas.width = this._canvas.width;
} else {
var ctx = this._ctx;
ctx.setTransform(1, 0, 0, 1, 0, 0);
var compop = this._Map['comp-op']
ctx.globalCompositeOperation = compop2canvas(compop) || compop;
ctx.fillStyle = color;
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
}
},
@ -103,9 +109,20 @@ var Filters = require('./torque_filters');
//
// sets the cartocss style to render stuff
//
setCartoCSS: function(cartocss) {
// clean sprites
this.setShader(new carto.RendererJS().render(cartocss));
setCartoCSS: function(cartocss, callback) {
var self = this;
if (PointRenderer.isTurboCarto(cartocss)) {
var datasource = new CartoDatasource(self.layer._tiles);
turbocarto(cartocss, datasource, function (err, parsedCartoCSS) {
self.setShader(new carto.RendererJS().render(parsedCartoCSS));
self.layer.redraw();
self.layer.animator.start();
callback && callback();
});
} else {
self.setShader(new carto.RendererJS().render(cartocss));
callback && callback();
}
},
setShader: function(shader) {
@ -483,6 +500,17 @@ var Filters = require('./torque_filters');
}
});
PointRenderer.isTurboCarto = function (cartocss) {
var reservedWords = ['ramp', 'colorbrewer', 'buckets']
var isTurbo = reservedWords
.map(function (w) {
return w + '('
})
.map(String.prototype.indexOf.bind(cartocss))
.every(function (f) { return f === -1 })
return !isTurbo
}
// exports public api
module.exports = PointRenderer;

View File

@ -1,32 +1,36 @@
{
"name": "torque.js",
"version": "2.15.1",
"description": "Temporal mapping for CartoDB",
"repository": {
"type": "git",
"url": "git://github.com/CartoDB/torque.git"
},
"author": {
"name": "CartoDB",
"url": "http://cartodb.com/",
"email": "wadus@cartodb.com"
},
"contributors": [
"Andrew Hill <andrew@vizzuality.com>",
"Simon Tokumine <tokumine@google.com>",
"Javier Alvarez <jmedina@vizzuality.com>",
"Javier Arce <javierarce@vizzuality.com>",
"Javier Santana <jsantana@vizzuality.com>",
"Raúl Ochoa <rochoa@cartodb.com>",
"Nicklas Gummesson <nicklas@cartodb.com>",
"Francisco Dans <francisco@cartodb.com>"
],
"licenses": [{
"name": "torque.js",
"version": "2.15.1",
"description": "Temporal mapping for CartoDB",
"repository": {
"type": "git",
"url": "git://github.com/CartoDB/torque.git"
},
"author": {
"name": "CartoDB",
"url": "http://cartodb.com/",
"email": "wadus@cartodb.com"
},
"contributors": [
"Andrew Hill <andrew@vizzuality.com>",
"Simon Tokumine <tokumine@google.com>",
"Javier Alvarez <jmedina@vizzuality.com>",
"Javier Arce <javierarce@vizzuality.com>",
"Javier Santana <jsantana@vizzuality.com>",
"Raúl Ochoa <rochoa@cartodb.com>",
"Nicklas Gummesson <nicklas@cartodb.com>",
"Francisco Dans <francisco@cartodb.com>"
],
"licenses": [
{
"type": "BSD"
}
],
"dependencies": {
"carto": "https://github.com/CartoDB/carto/archive/master.tar.gz"
"carto": "https://github.com/CartoDB/carto/archive/master.tar.gz",
"d3": "^3.5.6",
"turbo-carto": "^0.12.1",
"turf-jenks": "^1.0.1"
},
"devDependencies": {
"browserify": "~7.0.0",