/** * * Torque library * * A tool for mapping temporal data from CartoDB * Still in development and being finalized for * CartoDB 2.0 * * Authors: Andrew Hill, Simon Tokumine, Javier Santana * */ // iOS fix if (Function.prototype.bind == undefined) { Function.prototype.bind = function (bind) { var self = this; return function () { var args = Array.prototype.slice.call(arguments); return self.apply(bind || null, args); }; }; } function Torque() { var args = Array.prototype.slice.call(arguments), callback = args.pop(), modules = (args[0] && typeof args[0] === "string") ? args : args[0], config, i; if (!(this instanceof Torque)) { return new Torque(modules, callback); } if (!modules || modules === '*') { modules = []; for (i in Torque.modules) { if (Torque.modules.hasOwnProperty(i)) { modules.push(i); } } } for (i = 0; i < modules.length; i += 1) { Torque.modules[modules[i]](this); } callback(this); return this; } ; Torque.modules = {}; Torque.modules.app = function (torque) { torque.app = {}; torque.app.Instance = Class.extend( { init:function (logging) { this.layers = {}; torque.log.enabled = logging ? logging : false; }, addLayer:function (map, options) { var layer = new torque.layer.Engine(map, options); return layer } } ); }; Torque.modules.layer = function (torque) { torque.layer = {}; torque.layer.Engine = Class.extend({ init:function (map, options) { this._defaults = { user:'viz2', table:'ny_bus', column:'timestamp', istime: true, steps:250, resolution:3, cumulative:false, fps:24, autoplay:true, clock:false, zindex:0, fitbounds:false, countby:'count(i.cartodb_id)', blendmode:'source-over', trails:false, point_type:'square', subtitles:false, scrub:false } this.options = _.defaults(options, this._defaults); this._map = map; this._index = this.options.zindex; while (this._map.overlayMapTypes.length < this.options.zindex) { this._map.overlayMapTypes.push(null); } this._cartodb = new Backbone.CartoDB({user:this.options.user}); this.bounds = new google.maps.LatLngBounds(); torque.clock.enabled = this.options.clock ? this.options.clock : false; torque.clock.set('loading...'); this.getDeltas(); }, pause:function () { if (this.running == true) { this.running = false; } else { this.running = true; this.play(); } }, setOptions:function (new_options) { this.running = false; this.options = _.defaults(new_options, this._defaults); torque.clock.enabled = this.options.clock ? this.options.clock : false; torque.clock.set('loading...'); this._cartodb = new Backbone.CartoDB({user:this.options.user}); this.bounds = new google.maps.LatLngBounds(); this._map.overlayMapTypes.setAt(this._index, null); this.getDeltas(); }, run:function () { this.start = new Date(this.options.start).getTime(); this.end = new Date(this.options.end).getTime(); this._current = this.start; this._step = Math.floor((this.end - this.start) / this.options.steps); this._setupListeners(); this._display = new TimePlayer(this.start, (this.start - this.end), this._step, this.options); this._map.overlayMapTypes.setAt(this._index, this._display); this.fitBounds(this.options.fitbounds); this.running = false; torque.clock.clear(); // If scrubbable, override other options that may have been set if (this.options.scrub){ this.options.scrub(this); // this.options.autoplay = false; // this.options.trails = false; } if (this.options.autoplay) { this.running = true; this.play(); } torque.log.info('Layer is now running!'); }, _setupListeners:function () { var that = this; google.maps.event.addListener(this._map, 'zoom_changed', function () { that._display.reset_max_value(); }); }, getBounds:function () { return this.bounds; }, fitBounds:function (f) { if (f !== false) { this._map.fitBounds(this.bounds); if (typeof f == 'number') { this._map.setZoom(this._map.getZoom() + f); } else { this._map.setZoom(this._map.getZoom()); } } }, getDeltas:function (options) { var that = this; if (this.options.istime == true){ var max_col = "date_part('epoch',max({0}))".format(this.options.column); var min_col = "date_part('epoch',min({0}))".format(this.options.column); } else { var max_col = "max({0})".format(this.options.column); var min_col = "min({0})".format(this.options.column); } var sql = "SELECT st_xmax(st_envelope(st_collect(the_geom))) xmax,st_ymax(st_envelope(st_collect(the_geom))) ymax, st_xmin(st_envelope(st_collect(the_geom))) xmin, st_ymin(st_envelope(st_collect(the_geom))) ymin, {0} max, {1} min FROM {2}".format(max_col, min_col, this.options.table); var timeExtents = this._cartodb.CartoDBCollection.extend({ sql:sql }); var times = new timeExtents(); times.fetch(); times.bind('reset', function () { times.each(function (p) { that.options.start = p.get('min'); that.options.end = p.get('max'); that.bounds.extend(new google.maps.LatLng(p.get('ymin'), p.get('xmax'))); that.bounds.extend(new google.maps.LatLng(p.get('ymax'), p.get('xmin'))); that.bounds.extend(new google.maps.LatLng((p.get('ymax') + p.get('ymin')) / 2, (p.get('xmax') + p.get('xmin')) / 2)); }); that.run(); }); }, advance:function () { if (this._current + this._step < this.end) { this._current = this._current + this._step; // } else if (this._current < this.end) { // this._current = this.end; } else { this._current = this.start; } this._display.set_time((this._current - this.start) / this._step); }, play:function () { var pause = 0; if (this._current < this.end) { this._current = this._current + this._step if (this.end < this._current) { pause = 500; } } else { this._current = this.start; } var date = new Date(this._current * 1000); var date_arry = date.toString().substr(4).split(' '); torque.clock.set('' + date_arry[0] + ' ' + date_arry[2] + ''); if (this.options.subtitles) { torque.subtitles.set(date); } this._display.set_time((this._current - this.start) / this._step); if (this.options.scrub_move){ this.options.scrub_move(this) } if (this.running) { if ( this.options.fps ) { var ms = pause + 1000 * 1 / this.options.fps; //console.log("play timeout: " + ms + " ms (pause:" + pause + ", fps:" + this.options.fps + ")"); setTimeout(function () { this.play() }.bind(this), ms); } } } }); } Torque.modules.clock = function (torque) { torque.clock = {}; torque.clock.clear = function () { $('.torque_time').html(''); }; torque.clock.set = function (msg) { torque.clock._hand(msg); }; torque.clock._hand = function (msg) { var clockger = window.console; if (torque.clock.enabled) { $('.torque_time').html(msg); } }; }; Torque.modules.subtitles = function (torque) { torque.subtitles = { subs:[ { from:new Date("March 01, 1913 00:00:00"), to:new Date("July 01, 1914 00:00:00"), sub:"Pre war" }, { from:new Date("August 01, 1914 00:00:00"), to:new Date("February 01, 1915 00:00:00"), sub:"War begins with Germany" }, { from:new Date("February 02, 1915 00:00:00"), to:new Date("October 01, 1916 00:00:00"), sub:"North Sea naval blockade" }, { from:new Date("October 02, 1917 00:00:00"), to:new Date("April 01, 1917 00:00:00"), sub:"Atlantic U-boat warfare" }, { from:new Date("April 02, 1917 00:00:00"), to:new Date("September 01, 1917 00:00:00"), sub:"USA enters war" }, { from:new Date("September 02, 1917 00:00:00"), to:new Date("November 01, 1918 00:00:00"), sub:"Destroyers begin to escort convoys in Atlantic" }, { from:new Date("November 02, 1918 00:00:00"), to:new Date("August 01, 1920 00:00:00"), sub:"End of WWI" }, { from:new Date("August 02, 1920 00:00:00"), to:new Date("August 01, 1925 00:00:00"), sub:"Trade routes resume" } ] }; torque.subtitles.clear = function () { $('.torque_subs').html(''); }; torque.subtitles.set = function (date) { $.each(this.subs, function () { if (this.from < date && this.to > date) { torque.subtitles._update(this.sub); } }); }; torque.subtitles._update = function (msg) { $('.torque_subs').html(msg); }; }; /** * Logging module that torquetes log messages to the console and to the Speed * Tracer API. It contains convenience methods for info(), warn(), error(), * and todo(). * */ Torque.modules.log = function (torque) { torque.log = {}; torque.log.info = function (msg) { torque.log._torquete('INFO: ' + msg); }; torque.log.warn = function (msg) { torque.log._torquete('WARN: ' + msg); }; torque.log.error = function (msg) { torque.log._torquete('ERROR: ' + msg); }; torque.log.todo = function (msg) { torque.log._torquete('TODO: ' + msg); }; torque.log._torquete = function (msg) { var logger = window.console; if (torque.log.enabled) { if (logger && logger.markTimeline) { logger.markTimeline(msg); } console.log(msg); } }; }; var originShift = 2 * Math.PI * 6378137 / 2.0; var initialResolution = 2 * Math.PI * 6378137 / 256.0; function meterToPixels(mx, my, zoom) { var res = initialResolution / (1 << zoom); var px = (mx + originShift) / res; var py = (my + originShift) / res; return [px, py]; }