Torque: beyond points

JSGeo

Stuart Lynn / @stuart_lynn

Stuart Lynn

Map Scientist

World is

Space

+

Time

Maps online are a data compresion problem

Chris Whong's Taxi Data: 11Gb of taxi pick up and drop off's

11 Million taxi drop offs and pick ups

Torque

URL Schema


http://host.com/{z}/{x}/{y}.torque.[json|bin]
					

Tile Format


[
	{
		x: 25,
		y: 77,
		values: [ 1, 10 ],
		steps: [214, 215]
	},
	...
]
					

Metadata


{
	start: 0,
	end: 100,
	resolution: 2
	# scale: 1/resolution,
	data_steps: 365,
	column_type: "number"
	"minzoom": 0,
	"maxzoom": 11,
	"tiles": [
		'http://a.host.com/{z}/{x}/{y}.torque.json',
		'http://b.host.com/{z}/{x}/{y}.torque.json',
		'http://c.host.com/{z}/{x}/{y}.torque.json',
		'http://d.host.com/{z}/{x}/{y}.torque.json'
	],
	"bounds": [ -180, -85.05112877980659, 180, 85.0511287798066 ]
}
					
Full Torque Spec
Torque.js

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);

// initialize a torque layer that uses the CartoDB account details and SQL API to pull in data
var torqueLayer = new L.TorqueLayer({
	user: 'viz2',
	table: 'ow',
	cartocss: CARTOCSS
});

torqueLayer.addTo(map);
torqueLayer.play();
					

Map {
    -torque-time-attribute: "date";
    -torque-aggregation-function: "count(cartodb_id)";
    -torque-frame-count: 760;
    -torque-animation-duration: 15;
    -torque-resolution: 2;
}
#layer {
    marker-width: 3;
    marker-fill-opacity: 0.8;
    marker-fill: #FEE391;
    comp-op: "lighten";
    [value > 2] { marker-fill: #FEC44F; }
    [value > 3] { marker-fill: #FE9929; }
    [value > 4] { marker-fill: #EC7014; }
    [value > 5] { marker-fill: #CC4C02; }
    [value > 6] { marker-fill: #993404; }
    [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;}
}

					

Three limiting problems

  • No ability to style according to value appart from selectors
  • Only one variable to style on
  • Only one tied to CartoDB as a data source.
https://github.com/CartoDB/torque/tree/stuart_experimental/examples

[value > 2] { marker-fill: #FEC44F; }
[value > 3] { marker-fill: #FE9929; }
[value > 4] { marker-fill: #EC7014; }
[value > 5] { marker-fill: #CC4C02; }
[value > 6] { marker-fill: #993404; }
[value > 7] { marker-fill: #662506; }
					

Ugly, verbose and not very easy to write

Really want functons that scale properties with data


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)";
					

-torque-aggregation-function: "count(cartodb_id)";
					

Only have 1 dimension to style with :-(


-torque-aggregation-function: "avg(abs(depth));avg(mag)";
					

Renderers

Actually stored in cartocss_render.js

Text renderer


text-name: "{{value}}c";
					


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 )
}

					

Text renderer

Vector Renderer


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 = 1

    // 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, -max_line_length/2.0)
    ctx.rotate(angle)
    ctx.strokeStyle = color
    ctx.moveTo(0,-max_line_length*2)
    ctx.lineTo(0,max_line_length*2)
    ctx.stroke();

  }
					

Providers

Allow you to map a data source to CartoDB

Redshift example

See Chris Whong for info about a spark provider

Go hack on Torque.

d3.cartodb


var map = new L.Map("map", { center: [0, 0], zoom: 3 })
var layer = new d3.cartodb.Layer({
	user: 'rambo',
	sql_api_template: 'http://rambo.cartodb.com/'
});
layer.addTo(map);
layer.setSQL('select * form table');
layer.setCartoCSS('#test { marker-fill: red; }');
				

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {
        dates: [1,2,3...],
        values:[{pop: 10, crashes:2}, {pop:8, crashes:0}, {pop:8, crashes:1}...]
      },
      "geometry": {
        "type": "Polygon",
        "coordinates": ...
      }

    }
    }
  ]
}
				

polygon-fill: lin_scale([0,100], ["red","blue"]);
				

    this.renderer.addFunction('lin_scale', function (args, done) {
      var domain    = args[0].value[0].value
      var range     = args[1].value[0].value
      done(function(v) {
      return {
        is: 'custom',
        toString: function() {
          return "(function() { if(!data.dates__uint16){return 'transparent';} var index; var prevDates = data.dates__uint16.map(function(d){return data['global']['step'] >= d }); var index = prevDates.indexOf(false) - 1; var val= data.vals__uint8[index]; var col =  d3.scale.linear().domain(["+domain+"]).range(["+range+"])(val); return col;  })();";
        }
      }
    });
  });

				
nyc buildings