Compare commits

...

33 Commits

Author SHA1 Message Date
Stuart Lynn
dc389b7f1b build 2015-09-28 13:15:56 -04:00
Stuart Lynn
69b3f97a4d build 2015-09-14 11:28:22 -04:00
Stuart Lynn
7292104895 use sql_api 2015-09-14 11:28:05 -04:00
Stuart Lynn
a27e8f88ff allow any values to be used in the text renderer 2015-09-14 11:27:51 -04:00
Stuart Lynn
a47279e11a Merge branch 'point_labels' into multi_variable_with_scaling 2015-09-11 13:07:18 -04:00
Stuart Lynn
120e383630 build 2015-09-11 13:05:36 -04:00
Stuart Lynn
f41d79a045 correcting typo 2015-09-11 13:00:59 -04:00
Stuart Lynn
29220a16ec examples updates 2015-09-11 13:00:41 -04:00
Stuart Lynn
dfc8345f35 build 2015-09-09 18:30:26 -04:00
Stuart Lynn
ed585820ac tweak example 2015-09-09 18:30:19 -04:00
Stuart Lynn
3964b32b2e add properties to scale 2015-09-09 18:30:05 -04:00
Stuart Lynn
05aa3db45c Merge branch 'vector_marker' into multi_variable_with_scaling 2015-09-09 14:47:38 -04:00
Stuart Lynn
1b93a6c67e example file for vector field 2015-09-09 14:46:12 -04:00
Stuart Lynn
70c34d2d30 build 2015-09-09 14:45:46 -04:00
Stuart Lynn
3a9f47b9f2 adding vector renderer for torque 2015-09-09 14:45:36 -04:00
Stuart Lynn
25d0fb77a5 build 2015-09-09 11:14:06 -04:00
Stuart Lynn
73ed8bdfd7 mutli variable scaling function example 2015-09-09 11:06:26 -04:00
Stuart Lynn
cd7bb694bf Merge branch 'multi_variable_json_torque_test' into multi_variable_with_scaling 2015-09-09 10:49:35 -04:00
Stuart Lynn
73e2a3736c fix problem when there is only one variable requested. Default that having index 0 in the renderData array 2015-09-09 10:48:53 -04:00
Stuart Lynn
5763feb0bd Merge branch 'scale_functions_for_variables' into multi_variable_with_scaling 2015-09-09 10:41:12 -04:00
Stuart Lynn
a2f7ce04ea build 2015-09-09 10:38:37 -04:00
Stuart Lynn
b24b4b0db9 Apply the scaling functions to different variables 2015-09-09 10:38:17 -04:00
Stuart Lynn
266919ad1b updating example 2015-09-09 10:37:27 -04:00
Stuart Lynn
c75271dacf Merge branch 'scale_functions_for_variables' into multi_variable_with_scaling 2015-09-09 10:12:59 -04:00
Stuart Lynn
e9bde54840 build 2015-09-09 09:58:08 -04:00
Stuart Lynn
b9bcb1b33f more sensible mapping for the multiple values 2015-09-09 09:57:50 -04:00
Stuart Lynn
35e1044f1a adding in example 2015-09-08 18:08:05 -04:00
Stuart Lynn
17f2c2aee0 build 2015-09-08 18:07:54 -04:00
Stuart Lynn
6b0e946e41 Adding some support for multiple variables in Torque. This is a test for just now 2015-09-08 18:07:45 -04:00
Stuart Lynn
e2bcae0c84 adding text render example 2015-09-08 12:00:21 -04:00
Stuart Lynn
a03f9ca4e2 compile 2015-09-08 12:00:05 -04:00
Stuart Lynn
2a47541cc5 adding redner text function to cartocss_render 2015-09-08 11:58:55 -04:00
Stuart Lynn
85f11a73c8 Proof of concept of how CartoCSS could be extended to allow properties to be set using scaling functions that take value, zoom or frame-offset as variables. 2015-09-04 15:58:59 -04:00
11 changed files with 979 additions and 97 deletions

12
dist/torque.full.js vendored

File diff suppressed because one or more lines are too long

View File

@ -3181,7 +3181,19 @@ var Profiler = require('../profiler');
// 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 val_keys = []
if(rows.length>0){
val_keys = Object.keys(rows[0]).filter(function(k){return (k.indexOf("vals__uint8") > -1) })
}
var renderData = []
val_keys.forEach(function(key,index){
renderData[index] = new (this.options.valueDataType || type)(dates);
}.bind(this))
var renderDataPos = new Uint32Array(dates);
prof_mem.inc(
@ -3208,15 +3220,29 @@ var Profiler = require('../profiler');
}
var dates = row.dates__uint16;
var vals = row.vals__uint8;
var val_keys = Object.keys(row).filter(function(k){return (k.indexOf("vals__uint8") > -1) })
var val_arr = []
val_keys.forEach(function(key){
var i = (key=='vals_uint8' ? 0 : key.match(/vals__uint8_(\d+)/)[1])
val_arr[i] = row[key];
})
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]]);
//Stuart: Not sure I understand why this is here?
// if(this.options.cumulative) {
// vals[j] += prev_val;
// }
// prev_val = vals[j];
var all_vals = []
val_arr.forEach(function(vals){
all_vals.push(vals[j])
})
rr.push([r, all_vals]);
}
} else {
var valByDate = {}
@ -3245,6 +3271,7 @@ var Profiler = require('../profiler');
}
// for each timeslot search active buckets
var renderDataIndex = 0;
var timeSlotIndex = 0;
@ -3254,10 +3281,13 @@ var Profiler = require('../profiler');
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];
rr[1].forEach(function(rrr,index){
renderData[index][renderDataIndex] = rrr;
})
++renderDataIndex;
}
}
@ -3346,7 +3376,6 @@ var Profiler = require('../profiler');
subdomains = [null]; // no subdomain
}
var url;
if (options.no_cdn) {
url = this._host();
@ -3363,6 +3392,7 @@ var Profiler = require('../profiler');
},
getTileData: function(coord, zoom, callback) {
if(!this._ready) {
this._tileQueue.push([coord, zoom, callback]);
} else {
@ -3394,6 +3424,14 @@ var Profiler = require('../profiler');
var column_conv = this.options.column;
var agg_columns;
if(this.options.countby.indexOf(";") > 0){
agg_columns = this.options.countby.split(";")
}
else{
agg_columns = [this.options.countby]
}
if(this.options.is_time) {
column_conv = format("date_part('epoch', {column})", this.options);
}
@ -3406,8 +3444,13 @@ var Profiler = require('../profiler');
", CDB_XYZ_Extent({x}, {y}, {zoom}) as ext " +
")," +
"cte AS ( "+
" SELECT ST_SnapToGrid(i.the_geom_webmercator, p.res) g" +
", {countby} c" +
" SELECT ST_SnapToGrid(i.the_geom_webmercator, p.res) g"
agg_columns.forEach(function(col,index){
sql = sql + ", "+col+" c"+index
}.bind(this))
sql = sql +
", floor(({column_conv} - {start})/{step}) d" +
" FROM ({_sql}) i, par p " +
" WHERE i.the_geom_webmercator && p.ext " +
@ -3415,14 +3458,20 @@ var Profiler = require('../profiler');
") " +
"" +
"SELECT (st_x(g)-st_xmin(p.ext))/p.res x__uint8, " +
" (st_y(g)-st_ymin(p.ext))/p.res y__uint8," +
" array_agg(c) vals__uint8," +
" array_agg(d) dates__uint16" +
" (st_y(g)-st_ymin(p.ext))/p.res y__uint8"
agg_columns.forEach(function(col,index){
sql = sql + ", array_agg(c"+index+") vals__uint8_"+index
}.bind(this))
sql = sql +
", array_agg(d) dates__uint16" +
// the tile_size where are needed because the overlaps query in cte subquery includes the points
// in the left and bottom borders of the tile
" FROM cte, par p where (st_y(g)-st_ymin(p.ext))/p.res < tile_size and (st_x(g)-st_xmin(p.ext))/p.res < tile_size GROUP BY x__uint8, y__uint8";
var query = format(sql, this.options, {
zoom: zoom,
x: coord.x,
@ -3431,6 +3480,7 @@ var Profiler = require('../profiler');
_sql: this.getSQL()
});
var self = this;
this.sql(query, function (data) {
if (data) {
@ -4442,6 +4492,31 @@ var Profiler = require('../profiler');
}
}
function renderVector(ctx, st){
var angle = st['marker-angle']
var color = st['marker-stroke']
var mag = st['marker-mag']
var max_mag = st['marker-max-mag'] ? st['marker-max-mag'] : 10
var max_line_length = st['marker-width']
var scaled_mag = (mag/max_mag)*max_line_length
ctx.lineWidth = 2
// ctx.strokeStyle='green'
// ctx.strokeRect(-max_line_length,-max_line_length,max_line_length*2,max_line_length*2)
// ctx.translate(max_line_length/2.0, 0)
ctx.rotate(angle)
ctx.strokeStyle = color
ctx.moveTo(0,-max_line_length*2)
ctx.lineTo(0,max_line_length*2)
ctx.stroke();
}
function renderRectangle(ctx, st) {
ctx.fillStyle = st['marker-fill'];
var pixel_size = st['marker-width'];
@ -4473,6 +4548,20 @@ var Profiler = require('../profiler');
}
}
function renderText(ctx,st){
var width = st['marker-width']
var text = st['text-name']
var font = st['text-face-name'] ? st['text-face-name'] : 'Droid Sans Regular'
var textSize = st['text-size'] ? st['text-size'] : '10px'
var color = st['text-fill'] ? st['text-fill'] : 'white'
// ctx.font = textSize+"px "+font
ctx.fillStyle = color
ctx.font= textSize + " " + font
ctx.fillText(text, -width/2.0, width/2.0 )
}
function renderSprite(ctx, img, st) {
if(img.complete){
@ -4487,6 +4576,8 @@ module.exports = {
renderPoint: renderPoint,
renderSprite: renderSprite,
renderRectangle: renderRectangle,
renderVector: renderVector,
renderText: renderText,
MAX_SPRITE_RADIUS: MAX_SPRITE_RADIUS
};
@ -4594,6 +4685,7 @@ var Filters = require('./torque_filters');
setShader: function(shader) {
// clean sprites
this._sprites = [];
this._shader = shader;
this._Map = this._shader.getDefault().getStyle({}, { zoom: 0 });
@ -4605,6 +4697,30 @@ var Filters = require('./torque_filters');
this._sprites = [];
},
processScaleFunction:function(input, opts){
if(typeof(input)!='string'){
return input;
}
var matches = input.match(/scale_(.*)\((.*)\)/)
if(matches){
var type = matches[1];
var params = matches[2].split(",");
var variable = params[0]
var domain = params.slice(1,3);
var range = params.slice(3,params.length);
var scale = {lin: d3.scale.linear, log: d3.scale.log, quant: d3.scale.quantize, sqrt: d3.scale.sqrt}[type]
var s = scale().domain(domain).range(range)
return s(opts[variable])
}
else{
return input;
}
},
//
// generate sprite based on cartocss style
@ -4612,9 +4728,31 @@ var Filters = require('./torque_filters');
generateSprite: function(shader, value, shaderVars) {
var self = this;
var prof = Profiler.metric('torque.renderer.point.generateSprite').start();
var st = shader.getStyle({
value: value
}, shaderVars);
var values = {}
if(value.length ==1){
values["value"] = value[0]
}
else{
value.forEach(function(val,index){
values["value"+index] = val;
})
}
var st = shader.getStyle(values, shaderVars);
var processingVars = {}
for(var key in shaderVars){ processingVars[key]= shaderVars[key]}
for(var key in values){processingVars[key] = values[key]}
var varsToProcess = ['marker-width', 'marker-fill-opacity', 'marker-fill', 'marker-stroke', 'marker-angle', 'marker-mag']
varsToProcess.forEach(function(key){
if(st[key]){
st[key] = this.processScaleFunction(st[key], processingVars)
}
}.bind(this))
if(this._style === null || this._style !== st){
this._style = st;
}
@ -4655,10 +4793,23 @@ var Filters = require('./torque_filters');
var mt = st['marker-type'];
if (mt && mt === 'rectangle') {
cartocss.renderRectangle(ctx, st);
} else {
} else if(mt && mt === 'vector') {
cartocss.renderVector(ctx,st);
}
else{
cartocss.renderPoint(ctx, st);
}
}
if(st['text-name']){
var captures = st['text-name'].match(/\{\{(.*)\}\}/)
captures.slice(1,captures.length).forEach(function(rep){
st['text-name'] = st['text-name'].replace("{{"+rep+"}}", values[rep])
})
// st['text-name'] = st['text-name'].replace("{{value}}",value)
cartocss.renderText(ctx,st)
}
prof.end(true);
if (torque.flags.sprites_to_images) {
var i = this._createImage();
@ -4760,7 +4911,7 @@ var Filters = require('./torque_filters');
var pixelIndex = tile.timeIndex[key];
for(var p = 0; p < activePixels; ++p) {
var posIdx = tile.renderDataPos[pixelIndex + p];
var c = tile.renderData[pixelIndex + p];
var c = tile.renderData.map(function(a){return a[pixelIndex + p]});
if (c) {
var sp = sprites[c];
if (sp === undefined) {
@ -4826,7 +4977,9 @@ var Filters = require('./torque_filters');
var pixelIndex = tile.timeIndex[step];
for(var p = 0; p < activePixels; ++p) {
var posIdx = tile.renderDataPos[pixelIndex + p];
var c = tile.renderData[pixelIndex + p];
var c =tile.renderData.map(function(r){ return r[pixelIndex + p]})
if (c) {
var x = tile.x[posIdx];
var y = tileMax - tile.y[posIdx];
@ -13932,7 +14085,7 @@ module.exports={
"url": "https://github.com/cartodb/carto",
"repository": {
"type": "git",
"url": "http://github.com/cartodb/carto.git"
"url": "git+ssh://git@github.com/cartodb/carto.git"
},
"author": {
"name": "CartoDB",
@ -14003,7 +14156,7 @@ module.exports={
"bugs": {
"url": "https://github.com/cartodb/carto/issues"
},
"homepage": "https://github.com/cartodb/carto",
"homepage": "https://github.com/cartodb/carto#readme",
"_id": "carto@0.15.1-cdb1",
"_shasum": "62534c2975cbee073f10c6c14a0c7e889c9469e7",
"_resolved": "https://github.com/CartoDB/carto/archive/master.tar.gz",

4
dist/torque.js vendored

File diff suppressed because one or more lines are too long

View File

@ -3188,7 +3188,19 @@ var Profiler = require('../profiler');
// 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 val_keys = []
if(rows.length>0){
val_keys = Object.keys(rows[0]).filter(function(k){return (k.indexOf("vals__uint8") > -1) })
}
var renderData = []
val_keys.forEach(function(key,index){
renderData[index] = new (this.options.valueDataType || type)(dates);
}.bind(this))
var renderDataPos = new Uint32Array(dates);
prof_mem.inc(
@ -3215,15 +3227,29 @@ var Profiler = require('../profiler');
}
var dates = row.dates__uint16;
var vals = row.vals__uint8;
var val_keys = Object.keys(row).filter(function(k){return (k.indexOf("vals__uint8") > -1) })
var val_arr = []
val_keys.forEach(function(key){
var i = (key=='vals_uint8' ? 0 : key.match(/vals__uint8_(\d+)/)[1])
val_arr[i] = row[key];
})
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]]);
//Stuart: Not sure I understand why this is here?
// if(this.options.cumulative) {
// vals[j] += prev_val;
// }
// prev_val = vals[j];
var all_vals = []
val_arr.forEach(function(vals){
all_vals.push(vals[j])
})
rr.push([r, all_vals]);
}
} else {
var valByDate = {}
@ -3252,6 +3278,7 @@ var Profiler = require('../profiler');
}
// for each timeslot search active buckets
var renderDataIndex = 0;
var timeSlotIndex = 0;
@ -3261,10 +3288,13 @@ var Profiler = require('../profiler');
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];
rr[1].forEach(function(rrr,index){
renderData[index][renderDataIndex] = rrr;
})
++renderDataIndex;
}
}
@ -3353,7 +3383,6 @@ var Profiler = require('../profiler');
subdomains = [null]; // no subdomain
}
var url;
if (options.no_cdn) {
url = this._host();
@ -3370,6 +3399,7 @@ var Profiler = require('../profiler');
},
getTileData: function(coord, zoom, callback) {
if(!this._ready) {
this._tileQueue.push([coord, zoom, callback]);
} else {
@ -3401,6 +3431,14 @@ var Profiler = require('../profiler');
var column_conv = this.options.column;
var agg_columns;
if(this.options.countby.indexOf(";") > 0){
agg_columns = this.options.countby.split(";")
}
else{
agg_columns = [this.options.countby]
}
if(this.options.is_time) {
column_conv = format("date_part('epoch', {column})", this.options);
}
@ -3413,8 +3451,13 @@ var Profiler = require('../profiler');
", CDB_XYZ_Extent({x}, {y}, {zoom}) as ext " +
")," +
"cte AS ( "+
" SELECT ST_SnapToGrid(i.the_geom_webmercator, p.res) g" +
", {countby} c" +
" SELECT ST_SnapToGrid(i.the_geom_webmercator, p.res) g"
agg_columns.forEach(function(col,index){
sql = sql + ", "+col+" c"+index
}.bind(this))
sql = sql +
", floor(({column_conv} - {start})/{step}) d" +
" FROM ({_sql}) i, par p " +
" WHERE i.the_geom_webmercator && p.ext " +
@ -3422,14 +3465,20 @@ var Profiler = require('../profiler');
") " +
"" +
"SELECT (st_x(g)-st_xmin(p.ext))/p.res x__uint8, " +
" (st_y(g)-st_ymin(p.ext))/p.res y__uint8," +
" array_agg(c) vals__uint8," +
" array_agg(d) dates__uint16" +
" (st_y(g)-st_ymin(p.ext))/p.res y__uint8"
agg_columns.forEach(function(col,index){
sql = sql + ", array_agg(c"+index+") vals__uint8_"+index
}.bind(this))
sql = sql +
", array_agg(d) dates__uint16" +
// the tile_size where are needed because the overlaps query in cte subquery includes the points
// in the left and bottom borders of the tile
" FROM cte, par p where (st_y(g)-st_ymin(p.ext))/p.res < tile_size and (st_x(g)-st_xmin(p.ext))/p.res < tile_size GROUP BY x__uint8, y__uint8";
var query = format(sql, this.options, {
zoom: zoom,
x: coord.x,
@ -3438,6 +3487,7 @@ var Profiler = require('../profiler');
_sql: this.getSQL()
});
var self = this;
this.sql(query, function (data) {
if (data) {
@ -4449,6 +4499,31 @@ var Profiler = require('../profiler');
}
}
function renderVector(ctx, st){
var angle = st['marker-angle']
var color = st['marker-stroke']
var mag = st['marker-mag']
var max_mag = st['marker-max-mag'] ? st['marker-max-mag'] : 10
var max_line_length = st['marker-width']
var scaled_mag = (mag/max_mag)*max_line_length
ctx.lineWidth = 2
// ctx.strokeStyle='green'
// ctx.strokeRect(-max_line_length,-max_line_length,max_line_length*2,max_line_length*2)
// ctx.translate(max_line_length/2.0, 0)
ctx.rotate(angle)
ctx.strokeStyle = color
ctx.moveTo(0,-max_line_length*2)
ctx.lineTo(0,max_line_length*2)
ctx.stroke();
}
function renderRectangle(ctx, st) {
ctx.fillStyle = st['marker-fill'];
var pixel_size = st['marker-width'];
@ -4480,6 +4555,20 @@ var Profiler = require('../profiler');
}
}
function renderText(ctx,st){
var width = st['marker-width']
var text = st['text-name']
var font = st['text-face-name'] ? st['text-face-name'] : 'Droid Sans Regular'
var textSize = st['text-size'] ? st['text-size'] : '10px'
var color = st['text-fill'] ? st['text-fill'] : 'white'
// ctx.font = textSize+"px "+font
ctx.fillStyle = color
ctx.font= textSize + " " + font
ctx.fillText(text, -width/2.0, width/2.0 )
}
function renderSprite(ctx, img, st) {
if(img.complete){
@ -4494,6 +4583,8 @@ module.exports = {
renderPoint: renderPoint,
renderSprite: renderSprite,
renderRectangle: renderRectangle,
renderVector: renderVector,
renderText: renderText,
MAX_SPRITE_RADIUS: MAX_SPRITE_RADIUS
};
@ -4601,6 +4692,7 @@ var Filters = require('./torque_filters');
setShader: function(shader) {
// clean sprites
this._sprites = [];
this._shader = shader;
this._Map = this._shader.getDefault().getStyle({}, { zoom: 0 });
@ -4612,6 +4704,30 @@ var Filters = require('./torque_filters');
this._sprites = [];
},
processScaleFunction:function(input, opts){
if(typeof(input)!='string'){
return input;
}
var matches = input.match(/scale_(.*)\((.*)\)/)
if(matches){
var type = matches[1];
var params = matches[2].split(",");
var variable = params[0]
var domain = params.slice(1,3);
var range = params.slice(3,params.length);
var scale = {lin: d3.scale.linear, log: d3.scale.log, quant: d3.scale.quantize, sqrt: d3.scale.sqrt}[type]
var s = scale().domain(domain).range(range)
return s(opts[variable])
}
else{
return input;
}
},
//
// generate sprite based on cartocss style
@ -4619,9 +4735,31 @@ var Filters = require('./torque_filters');
generateSprite: function(shader, value, shaderVars) {
var self = this;
var prof = Profiler.metric('torque.renderer.point.generateSprite').start();
var st = shader.getStyle({
value: value
}, shaderVars);
var values = {}
if(value.length ==1){
values["value"] = value[0]
}
else{
value.forEach(function(val,index){
values["value"+index] = val;
})
}
var st = shader.getStyle(values, shaderVars);
var processingVars = {}
for(var key in shaderVars){ processingVars[key]= shaderVars[key]}
for(var key in values){processingVars[key] = values[key]}
var varsToProcess = ['marker-width', 'marker-fill-opacity', 'marker-fill', 'marker-stroke', 'marker-angle', 'marker-mag']
varsToProcess.forEach(function(key){
if(st[key]){
st[key] = this.processScaleFunction(st[key], processingVars)
}
}.bind(this))
if(this._style === null || this._style !== st){
this._style = st;
}
@ -4662,10 +4800,23 @@ var Filters = require('./torque_filters');
var mt = st['marker-type'];
if (mt && mt === 'rectangle') {
cartocss.renderRectangle(ctx, st);
} else {
} else if(mt && mt === 'vector') {
cartocss.renderVector(ctx,st);
}
else{
cartocss.renderPoint(ctx, st);
}
}
if(st['text-name']){
var captures = st['text-name'].match(/\{\{(.*)\}\}/)
captures.slice(1,captures.length).forEach(function(rep){
st['text-name'] = st['text-name'].replace("{{"+rep+"}}", values[rep])
})
// st['text-name'] = st['text-name'].replace("{{value}}",value)
cartocss.renderText(ctx,st)
}
prof.end(true);
if (torque.flags.sprites_to_images) {
var i = this._createImage();
@ -4767,7 +4918,7 @@ var Filters = require('./torque_filters');
var pixelIndex = tile.timeIndex[key];
for(var p = 0; p < activePixels; ++p) {
var posIdx = tile.renderDataPos[pixelIndex + p];
var c = tile.renderData[pixelIndex + p];
var c = tile.renderData.map(function(a){return a[pixelIndex + p]});
if (c) {
var sp = sprites[c];
if (sp === undefined) {
@ -4833,7 +4984,9 @@ var Filters = require('./torque_filters');
var pixelIndex = tile.timeIndex[step];
for(var p = 0; p < activePixels; ++p) {
var posIdx = tile.renderDataPos[pixelIndex + p];
var c = tile.renderData[pixelIndex + p];
var c =tile.renderData.map(function(r){ return r[pixelIndex + p]})
if (c) {
var x = tile.x[posIdx];
var y = tileMax - tile.y[posIdx];

View File

@ -0,0 +1,80 @@
<html>
<link rel="stylesheet" href="vendor/leaflet.css" />
<style>
#map, html, body {
width: 100%; height: 100%; padding: 0; margin: 0;
}
#title {
position: absolute;
top: 100px;
left: 50px;
color: white;
font-size: 27px;
font-family: Helvetica, sans-serif;
}
</style>
<body>
<div id="map"></div>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script>
<script src="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.js"></script>
<script src="../../dist/torque.full.uncompressed.js"></script>
<script>
// define the torque layer style using cartocss
// this creates a kind of density map
//color scale from http://colorbrewer2.org/
var CARTOCSS = [
'Map {',
'-torque-time-attribute: "time";',
'-torque-aggregation-function: "avg(abs(depth));avg(mag)";',
'-torque-frame-count: 200;',
'-torque-animation-duration: 20;',
'-torque-resolution: 1',
'}',
'#all_month_3 {',
' marker-width: "scale_lin(value0,0.0,10,0,5)";',
' marker-fill-opacity: "scale_sqrt(frame-offset,0,9,1,0)";',
' marker-type: ellipse;',
' marker-fill: "scale_lin(value1,0.1,5,blue,red)";',
'}',
'#all_month_3[frame-offset=1]{}',
'#all_month_3[frame-offset=2]{}',
'#all_month_3[frame-offset=3]{}',
'#all_month_3[frame-offset=4]{}',
'#all_month_3[frame-offset=5]{}',
'#all_month_3[frame-offset=6]{}',
'#all_month_3[frame-offset=7]{}',
'#all_month_3[frame-offset=8]{}',
'#all_month_3[frame-offset=9]{}',
].join('\n');
var map = new L.Map('map', {
zoomControl: true,
center: [40, 0],
zoom: 3
});
L.tileLayer('http://{s}.api.cartocdn.com/base-dark/{z}/{x}/{y}.png', {
attribution: 'CartoDB'
}).addTo(map);
var torqueLayer = new L.TorqueLayer({
user : 'eschbacher',
table : 'all_month_3',
provider : 'sql_api',
cartocss: CARTOCSS
});
torqueLayer.addTo(map);
torqueLayer.play();
</script>
</body>
</html>

View File

@ -0,0 +1,152 @@
<html>
<link rel="stylesheet" href="vendor/leaflet.css" />
<style>
#map, html, body {
width: 100%; height: 100%; padding: 0; margin: 0;
}
#title {
position: absolute;
top: 100px;
left: 50px;
color: white;
font-size: 27px;
font-family: Helvetica, sans-serif;
}
</style>
<body>
<div id="map"></div>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.js"></script>
<script src="../dist/torque.full.uncompressed.js"></script>
<script>
// define the torque layer style using cartocss
// this creates a kind of density map
//color scale from http://colorbrewer2.org/
var CARTOCSS = [
'Map {',
'-torque-time-attribute: "time";',
'-torque-aggregation-function: "avg(mag*10);avg(depth)";',
'-torque-frame-count: 256;',
'-torque-animation-duration: 30;',
'-torque-resolution: 1',
'}',
'#all_day {',
' marker-width: 4;',
' marker-fill-opacity: 0.7;',
' marker-type: ellipse;',
'marker-fill: #FFFFB2;',
'}',
'#all_day [ value1 <= 250] {',
' marker-width: 15.0;',
'}',
'#all_day [ value1 <= 50.15] {',
' marker-width: 14.0;',
'}',
'#all_day [ value1 <= 18.5095] {',
' marker-width: 13.0;',
'}',
'#all_day [ value1 <= 14.5909] {',
' marker-width: 12.0;',
'}',
'#all_day [ value1 <= 11.785] {',
' marker-width: 11.0;',
'}',
'#all_day [ value1 <= 9.30425] {',
' marker-width: 10.0;',
'}',
'#all_day [ value1 <= 7.10635] {',
' marker-width: 9.0;',
'}',
'#all_day [ value1 <= 5.4714] {',
' marker-width: 8.0;',
'}',
'#all_day [ value1 <= 2.825] {',
' marker-width: 7.0;',
'}',
'#all_day [ value1 <= 1.7035] {',
' marker-width: 6.0;',
'}',
'#all_day [ value0 <= 80.2] {',
'marker-fill: #B10026;',
'}',
'#all_day [ value0 <= 60.2] {',
'marker-fill: #E31A1C;',
'}',
'#all_day [ value0 <= 40.58] {',
'marker-fill: #FC4E2A;',
'}',
'#all_day [ value0 <= 30.92] {',
'marker-fill: #FD8D3C;',
'}',
'#all_day [ value0 <= 30.6] {',
'marker-fill: #FEB24C;',
'}',
'#all_day [ value0 <= 30.35] {',
'marker-fill: #FED976;',
'}',
'#all_day [ value0 <= 3.16] {',
'marker-fill: #FFFFB2;',
'}'
].join('\n');
var map = new L.Map('map', {
zoomControl: true,
center: [40, 0],
zoom: 3
});
L.tileLayer('http://{s}.api.cartocdn.com/base-dark/{z}/{x}/{y}.png', {
attribution: 'CartoDB'
}).addTo(map);
var torqueLayer = new L.TorqueLayer({
user : 'stuartlynn',
table : 'quakes_2014',
provider : 'sql_api',
cartocss: CARTOCSS
});
torqueLayer.addTo(map);
torqueLayer.play();
map.on('click', function(e) {
var p = e.containerPoint
var value = torqueLayer.getValueForPos(p.x, p.y);
if (value !== null) {
map.openPopup('average temperature: ' + value.value + "C", e.latlng);
}
});
// show small rectable and change cursor on hover
var hover = null;
map.on('mousemove', function(e) {
var p = e.containerPoint
var value = torqueLayer.getValueForPos(p.x, p.y);
// remove previous hover box
if (hover) {
map.removeLayer(hover);
hover = null;
}
if (value !== null) {
hover = L.rectangle(value.bbox, {
color: '#000',
weight: 1
}).addTo(map);
map._container.style.cursor = 'pointer';
} else {
map._container.style.cursor = 'auto';
}
});
</script>
</body>
</html>

View File

@ -0,0 +1,113 @@
<html>
<link rel="stylesheet" href="vendor/leaflet.css" />
<style>
#map, html, body {
width: 100%; height: 100%; padding: 0; margin: 0;
}
#title {
position: absolute;
top: 100px;
left: 50px;
color: white;
font-size: 27px;
font-family: Helvetica, sans-serif;
}
</style>
<body>
<div id="map"></div>
<div id="title">Average temperature collected by Britain's Royal Navy (1913-1925)</div>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.js"></script>
<script src="../dist/torque.full.uncompressed.js"></script>
<script>
// define the torque layer style using cartocss
// this creates a kind of density map
//color scale from http://colorbrewer2.org/
var CARTOCSS = [
'Map {',
'-torque-time-attribute: "date";',
'-torque-aggregation-function: "avg(temp::float)";',
'-torque-frame-count: 1;',
'-torque-animation-duration: 15;',
'-torque-resolution: 32',
'}',
'#layer {',
' marker-width: 16;',
' marker-fill-opacity: 1.0;',
' marker-fill: #fff5eb; ',
' marker-type: rectangle;',
'text-name: "{{value}}c";',
'text-face-name: "Arial";',
'text-fill: #036;',
'text-size: 100px;',
' [value > 1] { marker-fill: #fee6ce; }',
' [value > 2] { marker-fill: #fdd0a2; }',
' [value > 4] { marker-fill: #fdae6b; }',
' [value > 10] { marker-fill: #fd8d3c; }',
' [value > 15] { marker-fill: #f16913; }',
' [value > 20] { marker-fill: #d94801; }',
' [value > 25] { marker-fill: #8c2d04; }',
'}'
].join('\n');
var map = new L.Map('map', {
zoomControl: true,
center: [40, 0],
zoom: 3
});
L.tileLayer('http://{s}.api.cartocdn.com/base-dark/{z}/{x}/{y}.png', {
attribution: 'CartoDB'
}).addTo(map);
var torqueLayer = new L.TorqueLayer({
user : 'viz2',
table : 'ow',
provider : 'sql_api',
cartocss: CARTOCSS
});
torqueLayer.addTo(map);
map.on('click', function(e) {
var p = e.containerPoint
var value = torqueLayer.getValueForPos(p.x, p.y);
if (value !== null) {
map.openPopup('average temperature: ' + value.value + "C", e.latlng);
}
});
// show small rectable and change cursor on hover
var hover = null;
map.on('mousemove', function(e) {
var p = e.containerPoint
var value = torqueLayer.getValueForPos(p.x, p.y);
// remove previous hover box
if (hover) {
map.removeLayer(hover);
hover = null;
}
if (value !== null) {
hover = L.rectangle(value.bbox, {
color: '#000',
weight: 1
}).addTo(map);
map._container.style.cursor = 'pointer';
} else {
map._container.style.cursor = 'auto';
}
});
</script>
</body>
</html>

78
examples/vector_test.html Normal file
View File

@ -0,0 +1,78 @@
<html>
<link rel="stylesheet" href="vendor/leaflet.css" />
<style>
#map, html, body {
width: 100%; height: 100%; padding: 0; margin: 0;
}
#title {
position: absolute;
top: 100px;
left: 50px;
color: white;
font-size: 27px;
font-family: Helvetica, sans-serif;
}
</style>
<body>
<div id="map"></div>
<div id="title">Example wind map using torque vector</div>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script>
<script src="../dist/torque.full.uncompressed.js"></script>
<script>
// define the torque layer style using cartocss
// this creates a kind of density map
//color scale from http://colorbrewer2.org/
var CARTOCSS = [
'Map {',
' -torque-frame-count:1;',
' -torque-animation-duration:1;',
' -torque-time-attribute:"date";',
' -torque-aggregation-function: "avg(angle*40);avg(mag)";',
' -torque-resolution:4;',
' -torque-data-aggregation:linear;',
'}',
'#wind {',
' marker-width: 5;',
' marker-fill-opacity: 1.0;',
' marker-max-mag: 7; ',
' marker-type: vector;',
' marker-mag: "scale_log(value1, 0,61.76,0,7)";',
' marker-stroke : "scale_log(value1,0.1,61.76,#FFFFB2,#B10026)";',
' marker-angle : "scale_lin(value0,0,255,0,6.283185)";',
'}'
]
CARTOCSS = CARTOCSS.join("\n")
var map = new L.Map('map', {
zoomControl: true,
center: [40, 0],
zoom: 3
});
L.tileLayer('http://{s}.api.cartocdn.com/base-dark/{z}/{x}/{y}.png', {
attribution: 'CartoDB'
}).addTo(map);
var torqueLayer = new L.TorqueLayer({
user : 'stuartlynn',
table : 'wind',
provider : 'sql_api',
cartocss: CARTOCSS
});
torqueLayer.addTo(map);
torqueLayer.play();
</script>
</body>
</html>

View File

@ -80,7 +80,19 @@ var Profiler = require('../profiler');
// 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 val_keys = []
if(rows.length>0){
val_keys = Object.keys(rows[0]).filter(function(k){return (k.indexOf("vals__uint8") > -1) })
}
var renderData = []
val_keys.forEach(function(key,index){
renderData[index] = new (this.options.valueDataType || type)(dates);
}.bind(this))
var renderDataPos = new Uint32Array(dates);
prof_mem.inc(
@ -107,15 +119,29 @@ var Profiler = require('../profiler');
}
var dates = row.dates__uint16;
var vals = row.vals__uint8;
var val_keys = Object.keys(row).filter(function(k){return (k.indexOf("vals__uint8") > -1) })
var val_arr = []
val_keys.forEach(function(key){
var i = (key=='vals_uint8' ? 0 : key.match(/vals__uint8_(\d+)/)[1])
val_arr[i] = row[key];
})
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]]);
//Stuart: Not sure I understand why this is here?
// if(this.options.cumulative) {
// vals[j] += prev_val;
// }
// prev_val = vals[j];
var all_vals = []
val_arr.forEach(function(vals){
all_vals.push(vals[j])
})
rr.push([r, all_vals]);
}
} else {
var valByDate = {}
@ -144,6 +170,7 @@ var Profiler = require('../profiler');
}
// for each timeslot search active buckets
var renderDataIndex = 0;
var timeSlotIndex = 0;
@ -153,10 +180,13 @@ var Profiler = require('../profiler');
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];
rr[1].forEach(function(rrr,index){
renderData[index][renderDataIndex] = rrr;
})
++renderDataIndex;
}
}
@ -245,7 +275,6 @@ var Profiler = require('../profiler');
subdomains = [null]; // no subdomain
}
var url;
if (options.no_cdn) {
url = this._host();
@ -262,6 +291,7 @@ var Profiler = require('../profiler');
},
getTileData: function(coord, zoom, callback) {
if(!this._ready) {
this._tileQueue.push([coord, zoom, callback]);
} else {
@ -293,6 +323,14 @@ var Profiler = require('../profiler');
var column_conv = this.options.column;
var agg_columns;
if(this.options.countby.indexOf(";") > 0){
agg_columns = this.options.countby.split(";")
}
else{
agg_columns = [this.options.countby]
}
if(this.options.is_time) {
column_conv = format("date_part('epoch', {column})", this.options);
}
@ -305,8 +343,13 @@ var Profiler = require('../profiler');
", CDB_XYZ_Extent({x}, {y}, {zoom}) as ext " +
")," +
"cte AS ( "+
" SELECT ST_SnapToGrid(i.the_geom_webmercator, p.res) g" +
", {countby} c" +
" SELECT ST_SnapToGrid(i.the_geom_webmercator, p.res) g"
agg_columns.forEach(function(col,index){
sql = sql + ", "+col+" c"+index
}.bind(this))
sql = sql +
", floor(({column_conv} - {start})/{step}) d" +
" FROM ({_sql}) i, par p " +
" WHERE i.the_geom_webmercator && p.ext " +
@ -314,14 +357,20 @@ var Profiler = require('../profiler');
") " +
"" +
"SELECT (st_x(g)-st_xmin(p.ext))/p.res x__uint8, " +
" (st_y(g)-st_ymin(p.ext))/p.res y__uint8," +
" array_agg(c) vals__uint8," +
" array_agg(d) dates__uint16" +
" (st_y(g)-st_ymin(p.ext))/p.res y__uint8"
agg_columns.forEach(function(col,index){
sql = sql + ", array_agg(c"+index+") vals__uint8_"+index
}.bind(this))
sql = sql +
", array_agg(d) dates__uint16" +
// the tile_size where are needed because the overlaps query in cte subquery includes the points
// in the left and bottom borders of the tile
" FROM cte, par p where (st_y(g)-st_ymin(p.ext))/p.res < tile_size and (st_x(g)-st_xmin(p.ext))/p.res < tile_size GROUP BY x__uint8, y__uint8";
var query = format(sql, this.options, {
zoom: zoom,
x: coord.x,
@ -330,6 +379,7 @@ var Profiler = require('../profiler');
_sql: this.getSQL()
});
var self = this;
this.sql(query, function (data) {
if (data) {

View File

@ -42,6 +42,31 @@
}
}
function renderVector(ctx, st){
var angle = st['marker-angle']
var color = st['marker-stroke']
var mag = st['marker-mag']
var max_mag = st['marker-max-mag'] ? st['marker-max-mag'] : 10
var max_line_length = st['marker-width']
var scaled_mag = (mag/max_mag)*max_line_length
ctx.lineWidth = 2
// ctx.strokeStyle='green'
// ctx.strokeRect(-max_line_length,-max_line_length,max_line_length*2,max_line_length*2)
// ctx.translate(max_line_length/2.0, 0)
ctx.rotate(angle)
ctx.strokeStyle = color
ctx.moveTo(0,-max_line_length*2)
ctx.lineTo(0,max_line_length*2)
ctx.stroke();
}
function renderRectangle(ctx, st) {
ctx.fillStyle = st['marker-fill'];
var pixel_size = st['marker-width'];
@ -73,6 +98,20 @@
}
}
function renderText(ctx,st){
var width = st['marker-width']
var text = st['text-name']
var font = st['text-face-name'] ? st['text-face-name'] : 'Droid Sans Regular'
var textSize = st['text-size'] ? st['text-size'] : '10px'
var color = st['text-fill'] ? st['text-fill'] : 'white'
// ctx.font = textSize+"px "+font
ctx.fillStyle = color
ctx.font= textSize + " " + font
ctx.fillText(text, -width/2.0, width/2.0 )
}
function renderSprite(ctx, img, st) {
if(img.complete){
@ -87,5 +126,7 @@ module.exports = {
renderPoint: renderPoint,
renderSprite: renderSprite,
renderRectangle: renderRectangle,
renderVector: renderVector,
renderText: renderText,
MAX_SPRITE_RADIUS: MAX_SPRITE_RADIUS
};

View File

@ -94,6 +94,7 @@ var Filters = require('./torque_filters');
setShader: function(shader) {
// clean sprites
this._sprites = [];
this._shader = shader;
this._Map = this._shader.getDefault().getStyle({}, { zoom: 0 });
@ -105,6 +106,30 @@ var Filters = require('./torque_filters');
this._sprites = [];
},
processScaleFunction:function(input, opts){
if(typeof(input)!='string'){
return input;
}
var matches = input.match(/scale_(.*)\((.*)\)/)
if(matches){
var type = matches[1];
var params = matches[2].split(",");
var variable = params[0]
var domain = params.slice(1,3);
var range = params.slice(3,params.length);
var scale = {lin: d3.scale.linear, log: d3.scale.log, quant: d3.scale.quantize, sqrt: d3.scale.sqrt}[type]
var s = scale().domain(domain).range(range)
return s(opts[variable])
}
else{
return input;
}
},
//
// generate sprite based on cartocss style
@ -112,9 +137,31 @@ var Filters = require('./torque_filters');
generateSprite: function(shader, value, shaderVars) {
var self = this;
var prof = Profiler.metric('torque.renderer.point.generateSprite').start();
var st = shader.getStyle({
value: value
}, shaderVars);
var values = {}
if(value.length ==1){
values["value"] = value[0]
}
else{
value.forEach(function(val,index){
values["value"+index] = val;
})
}
var st = shader.getStyle(values, shaderVars);
var processingVars = {}
for(var key in shaderVars){ processingVars[key]= shaderVars[key]}
for(var key in values){processingVars[key] = values[key]}
var varsToProcess = ['marker-width', 'marker-fill-opacity', 'marker-fill', 'marker-stroke', 'marker-angle', 'marker-mag']
varsToProcess.forEach(function(key){
if(st[key]){
st[key] = this.processScaleFunction(st[key], processingVars)
}
}.bind(this))
if(this._style === null || this._style !== st){
this._style = st;
}
@ -155,10 +202,23 @@ var Filters = require('./torque_filters');
var mt = st['marker-type'];
if (mt && mt === 'rectangle') {
cartocss.renderRectangle(ctx, st);
} else {
} else if(mt && mt === 'vector') {
cartocss.renderVector(ctx,st);
}
else{
cartocss.renderPoint(ctx, st);
}
}
if(st['text-name']){
var captures = st['text-name'].match(/\{\{(.*)\}\}/)
captures.slice(1,captures.length).forEach(function(rep){
st['text-name'] = st['text-name'].replace("{{"+rep+"}}", values[rep])
})
// st['text-name'] = st['text-name'].replace("{{value}}",value)
cartocss.renderText(ctx,st)
}
prof.end(true);
if (torque.flags.sprites_to_images) {
var i = this._createImage();
@ -260,7 +320,7 @@ var Filters = require('./torque_filters');
var pixelIndex = tile.timeIndex[key];
for(var p = 0; p < activePixels; ++p) {
var posIdx = tile.renderDataPos[pixelIndex + p];
var c = tile.renderData[pixelIndex + p];
var c = tile.renderData.map(function(a){return a[pixelIndex + p]});
if (c) {
var sp = sprites[c];
if (sp === undefined) {
@ -326,7 +386,9 @@ var Filters = require('./torque_filters');
var pixelIndex = tile.timeIndex[step];
for(var p = 0; p < activePixels; ++p) {
var posIdx = tile.renderDataPos[pixelIndex + p];
var c = tile.renderData[pixelIndex + p];
var c =tile.renderData.map(function(r){ return r[pixelIndex + p]})
if (c) {
var x = tile.x[posIdx];
var y = tileMax - tile.y[posIdx];