wip torque 1.0

This commit is contained in:
javi 2013-07-23 20:21:10 +02:00
parent bb4a026061
commit a7190a86c8
27 changed files with 11102 additions and 10645 deletions

3
.gitignore vendored
View File

@ -1,2 +1,3 @@
.idea/ .idea/
.DS_Store .DS_Store
*.swp

97
examples/leaflet.html Normal file
View File

@ -0,0 +1,97 @@
<html>
<link rel="stylesheet" href="../vendor/leaflet.css" />
<style>
#map, html, body {
width: 100%; height: 100%; padding: 0; margin: 0;
}
</style>
<body>
<div id="map"></div>
<script src="../vendor/leaflet.js"></script>
<script src="../lib/torque/request.js"></script>
<script src="../lib/torque/leaflet/leaflet_tileloader_mixin.js"></script>
<script src="../lib/torque/leaflet/canvas_layer.js"></script>
<script src="../lib/torque/renderer/point.js"></script>
<script src="../lib/torque/provider.json.js"></script>
<script>
var map = new L.Map('map', {
zoomControl: true,
//center: [43, 0],
center: [60.20639271653636, 25.004882812],
zoom: 13
});
L.TorqueLayer = L.CanvasLayer.extend({
initialize: function(options) {
var self = this;
options.tileLoader = true;
this.key = 0;
L.CanvasLayer.prototype.initialize.call(this, options);
this.provider = new torque.providers.json(options);
this.renderer = new torque.renderer.Point(this.getCanvas(), options);
// for each tile shown on the map request the data
this.on('tileAdded', function(t) {
var tileData = this.provider.getTileData(t, t.zoom, function(tileData) {
self._tileLoaded(t, tileData);
self.redraw();
});
}, this);
},
render: function() {
var canvas = this.getCanvas();
canvas.width = canvas.width;
var ctx = canvas.getContext('2d');
//ctx.clearRect(0, 0, canvas.width, canvas.height);
for(var t in this._tiles) {
var tile = this._tiles[t];
var pos = this.getTilePos(tile.coord);
ctx.setTransform(1, 0, 0, 1, pos.x, pos.y);
this.renderer.renderTile(tile, this.key);
}
},
setKey: function(t) {
this.key = t;
this.redraw();
}
});
L.tileLayer('http://tile.stamen.com/toner/{z}/{x}/{y}.png', {
attribution: 'Stamen'
}).addTo(map);
var torqueLayer = new L.TorqueLayer({
url: 'http://development.localhost.lan:8080/api/v1/sql',
resolution: 1,
start_date: 0,
end_date: 220,
step: 1,
table: 'importing_1369045322_helsinki_manydays_live',
column: 'ac',
countby: 'count(mm)',
pixel_size: 3
});
torqueLayer.addTo(map);
torqueLayer.setKey(1);
var t = 0;
setInterval(function() {
torqueLayer.setKey(t++%200);
}, 16);
</script>
</body>
</html>

View File

@ -1,210 +0,0 @@
/**
*
* backbone cartodb adapter
*
* this is a small library that allows to use Backbone with models
* to work with data stored in CartoDB (a geospatial database on
* the cloud, see more info at http://cartodb.com).
*
* it does NOT overrride Backbone.sync
*
*/
Backbone.CartoDB = function(options, query, cache) {
options = _.defaults(options, {
USE_PROXY: false,
user: ''
});
function _SQL(sql) {
this.sql = sql;
}
function SQL(sql) {
return new _SQL(sql);
}
// SQL("{0} is {1}").format("CartoDB", "epic!");
_SQL.prototype.format = function() {
var str = this.sql,
len = arguments.length+1;
var safe, arg;
for (i=0; i < len; arg = arguments[i++]) {
safe = typeof arg === 'object' ? JSON.stringify(arg) : arg;
str = str.replace(RegExp('\\{'+(i-1)+'\\}', 'g'), safe);
}
return str;
};
var resource_path= options.user + '.cartodb.com/api/v2/sql';
var resource_url = 'https://' + resource_path;
/**
* fetch sql from the server
*
* this function should be changed if you're working on node
*
*/
query = query || function(sql, callback, proxy) {
var url = resource_url;
var crossDomain = true;
if(proxy) {
url = 'api/v0/proxy/' + resource_url;
crossDomain = false;
}
if(sql.length > 1500) {
$.ajax({
url: url,
crossDomain: crossDomain,
type: 'POST',
dataType: 'json',
data: 'q=' + encodeURIComponent(sql),
success: callback,
error: function(){
if(proxy) {
callback();
} else {
//try fallback
if(USE_PROXY) {
query(sql, callback, true);
}
}
}
});
} else {
// TODO: add timeout
$.getJSON(resource_url + '?q=' + encodeURIComponent(sql) + '&callback=?')
.success(callback)
.fail(function(){
callback();
}).complete(function() {
});
}
};
var dummy_cache = {
setItem: function(key, value) { },
getItem: function(key) { return null; },
removeItem: function(key) { }
};
cache = cache && dummy_cache;
var CartoDBModel = Backbone.Model.extend({
_create_sql: function() {
var where = SQL(" where {0} = '{1}'").format(
this.columns[this.what],
this.get(this.what).replace("'", "''")
);
var select = this._sql_select();
var sql = 'select ' + select.join(',') + ' from ' + this.table + where;
return sql;
},
_sql_select: function() {
var select = [];
for(var k in this.columns) {
var w = this.columns[k];
if(w.indexOf('ST_') !== -1 || w === "the_geom") {
select.push(SQL('ST_AsGeoJSON({1}) as {0}').format(k,w));
} else {
select.push(SQL('{1} as {0}').format(k, w));
}
}
return select;
},
_parse_columns: function(row) {
var parsed = {};
for(var k in row) {
var v = row[k];
var c = this.columns[k];
if (c.indexOf('ST_') !== -1 || c === "the_geom") {
parsed[k] = JSON.parse(v);
} else {
parsed[k] = row[k];
}
}
return parsed;
},
fetch: function() {
var self = this;
query(this._create_sql(), function(data) {
self.set(self._parse_columns(data.rows[0]));
});
}
});
/**
* cartodb collection created from a sql composed using 'columns' and
* 'table' attributes defined in a child class
*
* var C = CartoDBCollection.extend({
* table: 'table',
* columns: ['c1', 'c2']
* });
* var c = new C();
* c.fetch();
*/
var CartoDBCollection = Backbone.Collection.extend({
_create_sql: function() {
var tables = this.table;
if(!_.isArray(this.table)) {
tables = [this.table];
}
tables = tables.join(',');
var select = CartoDBModel.prototype._sql_select.call(this);
var sql = 'select ' + select.join(',') + ' from ' + this.table;
if (this.where) {
sql += " WHERE " + this.where;
}
return sql;
},
fetch: function() {
var self = this;
var sql = this.sql || this._create_sql();
if(typeof(sql) === "function") {
sql = sql.call(this);
}
var item = this.cache ? cache.getItem(sql): false;
if(!item) {
query(sql, function(data) {
if(this.cache) {
try {
cache.setItem(sql, JSON.stringify(data.rows));
} catch(e) {}
}
var rows;
if(!self.sql) {
rows = _.map(data.rows, function(r) {
return CartoDBModel.prototype._parse_columns.call(self, r);
});
} else {
rows = data.rows;
}
self.reset(rows);
});
} else {
self.reset(JSON.parse(item));
}
}
});
return {
query: query,
CartoDBCollection: CartoDBCollection,
CartoDBModel: CartoDBModel,
SQL: SQL
};
};

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 B

File diff suppressed because it is too large Load Diff

View File

@ -1,542 +0,0 @@
/**
* @name cartodb-gmapsv3 for Google Maps V3 API
* @version 0.2 [December 14, 2011]
* @author: xavijam@gmail.com
* @fileoverview <b>Author:</b> xavijam@gmail.com<br/> <b>Licence:</b>
* Licensed under <a
* href="http://opensource.org/licenses/mit-license.php">MIT</a>
* license.<br/> This library lets you use CartoDB with google
* maps v3.
*
*/
/**
* @name google
* @class The fundamental namespace for Google APIs
*/
/**
* @name google.maps
* @class The fundamental namespace for Google Maps V3 API
*/
/*
* - Map style of cartodb
* - Infowindow of cartodb
* - Tiles style of cartodb
*/
// Namespace
var CartoDB = CartoDB || {};
(function($) {
if (typeof(google.maps.CartoDBLayer) === "undefined") {
/**
* @params {}
* map_canvas - Gmapsv3 canvas id (necesary for showing the infowindow)
* map - Your gmapsv3 map
* user_name - CartoDB user name
* table_name CartoDB table name
* query If you want to apply any sql sentence to the table...
* tile_style - If you want to add other style to the layer
* map_style - If you want to see the map styles created on cartodb (opcional - default = false)
* infowindow - If you want to see infowindows when click in a geometry (opcional - default = false)
* auto_bound - Let cartodb auto-bound-zoom in the map (opcional - default = false)
* debug - Do you want to debug the library? Set to true
*/
google.maps.CartoDBLayer = function (params) {
this.params = params;
this.params.feature = params.infowindow;
if (this.params.map_style) setCartoDBMapStyle(this.params); // Map style? ok, let's style.
if (this.params.auto_bound) autoBound(this.params); // Bounds? CartoDB does it.
if (this.params.infowindow) {
addWaxCartoDBTiles(this.params);
} else {
addSimpleCartoDBTiles(this.params); // Always add cartodb tiles, simple or with wax.
}
this.params.visible = true;
this.params.active = true;
// Zoom to cartodb geometries
function autoBound(params) {
// Zoom to your geometries
// If the table is private you can't auto zoom without being authenticated
if (!params.map_key) {
$.ajax({
url:'http://'+params.user_name+'.cartodb.com/api/v2/sql/?q='+escape('select ST_Extent(the_geom) from '+ params.table_name),
dataType: 'jsonp',
timeout: 2000,
callbackParameter: 'callback',
success: function(result) {
if (result.rows[0].st_extent!=null) {
var coordinates = result.rows[0].st_extent.replace('BOX(','').replace(')','').split(',');
var coor1 = coordinates[0].split(' ');
var coor2 = coordinates[1].split(' ');
var bounds = new google.maps.LatLngBounds();
// Check bounds
if (coor1[0] > 180 || coor1[0] < -180 || coor1[1] > 90 || coor1[1] < -90
|| coor2[0] > 180 || coor2[0] < -180 || coor2[1] > 90 || coor2[1] < -90) {
coor1[0] = '-30';
coor1[1] = '-50';
coor2[0] = '110';
coor2[1] = '80';
}
bounds.extend(new google.maps.LatLng(coor1[1],coor1[0]));
bounds.extend(new google.maps.LatLng(coor2[1],coor2[0]));
params.map.fitBounds(bounds);
}
},
error: function(e,msg) {
params.debug && console.debug('Error setting table bounds: ' + msg);
}
});
}
}
// Set the map styles of your cartodb table/map
function setCartoDBMapStyle(params) {
$.ajax({
url: 'http://' + params.user_name + '.cartodb.com/tiles/' + params.table_name + '/map_metadata?'+ 'map_key=' + (params.map_key || ''),
dataType: 'jsonp',
timeout: 2000,
callbackParameter: 'callback',
success: function(result) {
var map_style = $.parseJSON(result.map_metadata);
if (!map_style || map_style.google_maps_base_type=="roadmap") {
params.map.setOptions({mapTypeId: google.maps.MapTypeId.ROADMAP});
} else if (map_style.google_maps_base_type=="satellite") {
params.map.setOptions({mapTypeId: google.maps.MapTypeId.SATELLITE});
} else if (map_style.google_maps_base_type=="terrain") {
params.map.setOptions({mapTypeId: google.maps.MapTypeId.TERRAIN});
} else {
var mapStyles = [ { stylers: [ { saturation: -65 }, { gamma: 1.52 } ] },{ featureType: "administrative", stylers: [ { saturation: -95 }, { gamma: 2.26 } ] },{ featureType: "water", elementType: "labels", stylers: [ { visibility: "off" } ] },{ featureType: "administrative.locality", stylers: [ { visibility: "off" } ] },{ featureType: "road", stylers: [ { visibility: "simplified" }, { saturation: -99 }, { gamma: 2.22 } ] },{ featureType: "poi", elementType: "labels", stylers: [ { visibility: "off" } ] },{ featureType: "road.arterial", stylers: [ { visibility: "off" } ] },{ featureType: "road.local", elementType: "labels", stylers: [ { visibility: "off" } ] },{ featureType: "transit", stylers: [ { visibility: "off" } ] },{ featureType: "road", elementType: "labels", stylers: [ { visibility: "off" } ] },{ featureType: "poi", stylers: [ { saturation: -55 } ] } ];
map_style.google_maps_customization_style = mapStyles;
params.map.setOptions({mapTypeId: google.maps.MapTypeId.ROADMAP});
}
// Custom tiles
if (!map_style) {
map_style = {google_maps_customization_style: []};
}
params.map.setOptions({styles: map_style.google_maps_customization_style});
},
error: function(e, msg) {
params.debug && console.debug('Error setting map style: ' + msg);
}
});
}
// Add cartodb tiles to the map
function addSimpleCartoDBTiles(params) {
// Add the cartodb tiles
var cartodb_layer = {
getTileUrl: function(coord, zoom) {
return 'http://' + params.user_name + '.cartodb.com/tiles/' + params.table_name + '/'+zoom+'/'+coord.x+'/'+coord.y+'.png?sql='+params.query;
},
tileSize: new google.maps.Size(256, 256),
name: params.query,
description: false
};
params.layer = new google.maps.ImageMapType(cartodb_layer);
params.map.overlayMapTypes.insertAt(0,params.layer);
}
// Add cartodb tiles to the map
function addWaxCartoDBTiles(params) {
// interaction placeholder
var currentCartoDbId;
params.tilejson = generateTileJson(params);
params.infowindow = new CartoDB.Infowindow(params);
params.cache_buster = 0;
params.waxOptions = {
callbacks: {
out: function(){
params.map.setOptions({draggableCursor: 'default'});
},
over: function(feature, div, opt3, evt){
params.map.setOptions({draggableCursor: 'pointer'});
},
click: function(feature, div, opt3, evt){
// If there are more than one cartodb layer, close all possible infowindows
params.infowindow.hideAll();
params.infowindow.open(feature,evt.latLng);
}
},
clickAction: 'full'
};
params.layer = new wax.g.connector(params.tilejson);
params.map.overlayMapTypes.insertAt(0,params.layer);
params.interaction = wax.g.interaction(params.map, params.tilejson, params.waxOptions);
}
// Refresh wax interaction
function refreshWax(params,sql) {
if (params.infowindow) {
params.cache_buster++;
params.query = sql;
params.tilejson = generateTileJson(params);
// Remove old wax
removeOldLayer(params.map,params.layer);
// Setup new wax
params.tilejson.grids = wax.util.addUrlData(params.tilejson.grids_base, 'cache_buster=' + params.cache_buster);
// Add map tiles
params.layer = new wax.g.connector(params.tilejson);
params.map.overlayMapTypes.insertAt(0,params.layer);
// Add interaction
params.interaction.remove();
params.interaction = wax.g.interaction(params.map, params.tilejson, params.waxOptions);
}
}
// Refresh tiles
function refreshTiles(params,sql) {
// If you are not using interaction on the tiles... let's update your tiles
if (!params.infowindow) {
// First remove previous cartodb - tiles.
removeOldLayer(params.map,params.layer);
// Then add the cartodb tiles
params.query = sql;
var cartodb_layer = {
getTileUrl: function(coord, zoom) {
return 'http://' + params.user_name + '.cartodb.com/tiles/' + params.table_name + '/'+zoom+'/'+coord.x+'/'+coord.y+'.png?sql='+params.query;
},
tileSize: new google.maps.Size(256, 256),
name: params.query,
description: false
};
params.layer = new google.maps.ImageMapType(cartodb_layer);
params.map.overlayMapTypes.insertAt(0,params.layer);
}
}
function generateTileJson(params) {
var core_url = 'http://' + params.user_name + '.cartodb.com';
var base_url = core_url + '/tiles/' + params.table_name + '/{z}/{x}/{y}';
var tile_url = base_url + '.png?cache_buster=0';
var grid_url = base_url + '.grid.json';
// SQL?
if (params.query) {
var query = 'sql=' + params.query;
tile_url = wax.util.addUrlData(tile_url, query);
grid_url = wax.util.addUrlData(grid_url, query);
}
// Map key ?
if (params.map_key) {
var map_key = 'map_key=' + params.map_key;
tile_url = wax.util.addUrlData(tile_url,map_key);
grid_url = wax.util.addUrlData(grid_url,map_key);
}
// Tiles style ?
if (params.tile_style) {
var style = 'style=' + encodeURIComponent(params.tile_style);
tile_url = wax.util.addUrlData(tile_url,style);
grid_url = wax.util.addUrlData(grid_url,style);
}
// Build up the tileJSON
// TODO: make a blankImage a real 'empty tile' image
return {
blankImage: 'blank_tile.png',
tilejson: '1.0.0',
scheme: 'xyz',
tiles: [tile_url],
grids: [grid_url],
tiles_base: tile_url,
grids_base: grid_url,
name: params.query,
description: true,
formatter: function(options, data) {
currentCartoDbId = data.cartodb_id;
return data.cartodb_id;
},
cache_buster: function(){
return params.cache_buster;
}
};
}
// Remove old cartodb layer added (wax or imagemaptype)
function removeOldLayer(map,layer) {
if (layer) {
var pos = -1;
map.overlayMapTypes.forEach(function(map_type,i){
if (layer == map_type && map_type.name == layer.name && map_type.description == layer.description) {
pos = i;
}
});
if (pos!=-1)
map.overlayMapTypes.removeAt(pos);
layer = null;
}
}
// Update tiles & interactivity layer;
google.maps.CartoDBLayer.prototype.update = function(sql) {
// Hide the infowindow
if (this.params.infowindow)
this.params.infowindow.hide();
// Refresh wax
refreshWax(this.params,sql);
// Refresh tiles
refreshTiles(this.params,sql);
this.params.active = true;
this.params.visible = true;
};
// Destroy layers from the map
google.maps.CartoDBLayer.prototype.destroy = function() {
// First remove previous cartodb - tiles.
removeOldLayer(this.params.map,this.params.layer);
if (this.params.infowindow) {
// Remove wax interaction
this.params.interaction.remove();
this.params.infowindow.hide();
}
this.params.active = false;
};
// Hide layers from the map
google.maps.CartoDBLayer.prototype.hide = function() {
this.destroy();
this.params.visible = false;
};
// Show layers from the map
google.maps.CartoDBLayer.prototype.show = function() {
if (!this.params.visible || !this.params.active) {
this.update(this.params.query);
this.params.visible = true;
}
};
// CartoDB layer visible?
google.maps.CartoDBLayer.prototype.isVisible = function() {
return this.params.visible;
};
};
}
/**
* CartoDB.Infowindow
* @xavijam
**/
CartoDB.Infowindow = function (params) {
this.latlng_ = new google.maps.LatLng(0,0);
this.feature_;
this.map_ = params.map;
this.columns_;
this.offsetHorizontal_ = -107;
this.width_ = 214;
this.setMap(params.map);
this.params_ = params;
};
CartoDB.Infowindow.prototype = new google.maps.OverlayView();
CartoDB.Infowindow.prototype.draw = function() {
var me = this;
var div = this.div_;
if (!div) {
div = this.div_ = document.createElement('DIV');
div.className = "cartodb_infowindow";
div.innerHTML = '<a href="#close" class="close">x</a>'+
'<div class="outer_top">'+
'<div class="top">'+
'</div>'+
'</div>'+
'<div class="bottom">'+
'<label>id:1</label>'+
'</div>';
$(div).find('a.close').click(function(ev){
ev.preventDefault();
ev.stopPropagation();
me.hide();
});
google.maps.event.addDomListener(div,'click',function(ev){ev.preventDefault ? ev.preventDefault() : ev.returnValue = false;});
google.maps.event.addDomListener(div,'dblclick',function(ev){ev.preventDefault ? ev.preventDefault() : ev.returnValue = false;});
google.maps.event.addDomListener(div,'mousedown',function(ev){ev.preventDefault ? ev.preventDefault() : ev.returnValue = false;});
google.maps.event.addDomListener(div,'mouseup',function(ev){ev.preventDefault ? ev.preventDefault() : ev.returnValue = false;});
google.maps.event.addDomListener(div,'mousewheel',function(ev){ev.stopPropagation();});
google.maps.event.addDomListener(div,'DOMMouseScroll',function(ev){ev.stopPropagation();});
var panes = this.getPanes();
panes.floatPane.appendChild(div);
div.style.opacity = 0;
}
var pixPosition = this.getProjection().fromLatLngToDivPixel(this.latlng_);
if (pixPosition) {
div.style.width = this.width_ + 'px';
div.style.left = (pixPosition.x - 49) + 'px';
var actual_height = - $(div).height();
div.style.top = (pixPosition.y + actual_height + 5) + 'px';
}
};
CartoDB.Infowindow.prototype.setPosition = function() {
if (this.div_) {
var div = this.div_;
var pixPosition = this.getProjection().fromLatLngToDivPixel(this.latlng_);
if (pixPosition) {
div.style.width = this.width_ + 'px';
div.style.left = (pixPosition.x - 49) + 'px';
var actual_height = - $(div).height();
div.style.top = (pixPosition.y + actual_height + 10) + 'px';
}
this.show();
}
};
CartoDB.Infowindow.prototype.open = function(feature,latlng){
var that = this
, infowindow_sql = 'SELECT * FROM ' + this.params_.table_name + ' WHERE cartodb_id=' + feature;
that.feature_ = feature;
// If the table is private, you can't run any api methods
if (this.params_.feature!=true) {
infowindow_sql = encodeURIComponent(this.params_.feature.replace('{{feature}}',feature));
}
$.ajax({
url:'http://'+ this.params_.user_name +'.cartodb.com/api/v2/sql/?q='+infowindow_sql,
dataType: 'jsonp',
timeout: 2000,
callbackParameter: 'callback',
success: function(result) {
positionateInfowindow(result.rows[0],latlng);
},
error: function(e,msg) {
that.params_.debug && console.debug('Error retrieving infowindow variables: ' + msg);
}
});
function positionateInfowindow(variables,center) {
if (that.div_) {
var div = that.div_;
// Get latlng position
that.latlng_ = latlng;
// Remove the unnecessary html
$('div.cartodb_infowindow div.outer_top div.top').html('');
$('div.cartodb_infowindow div.outer_top div.bottom label').html('');
// List all the new variables
for (p in variables) {
if (p!='cartodb_id' && p!='cdb_centre' && p!='the_geom_webmercator') {
$('div.cartodb_infowindow div.outer_top div.top').append('<label>'+p+'</label><p class="'+((variables[p]!=null && variables[p]!='')?'':'empty')+'">'+(variables[p] || 'empty')+'</p>');
}
}
// Show cartodb_id?
if (variables['cartodb_id']) {
$('div.cartodb_infowindow div.bottom label').html('id: <strong>'+feature+'</strong>');
}
that.moveMaptoOpen();
that.setPosition();
}
}
};
CartoDB.Infowindow.prototype.hide = function() {
if (this.div_) {
var div = this.div_;
$(div).animate({
top: '+=' + 10 + 'px',
opacity: 0},
100, 'swing',
function () {
div.style.visibility = "hidden";
}
);
}
};
CartoDB.Infowindow.prototype.show = function() {
if (this.div_) {
var div = this.div_;
div.style.opacity = 0;
div.style.visibility = "visible";
$(div).animate({
top: '-=' + 10 + 'px',
opacity: 1},
250
);
}
};
CartoDB.Infowindow.prototype.hideAll = function() {
$('div.cartodb_infowindow').css('visibility','hidden');
};
CartoDB.Infowindow.prototype.isVisible = function(marker_id) {
if (this.div_) {
var div = this.div_;
if (div.style.visibility == 'visible' && this.feature_!=null) {
return true;
} else {
return false;
}
} else {
return false;
}
};
CartoDB.Infowindow.prototype.transformGeoJSON = function(str) {
var json = $.parseJSON(str);
return new google.maps.LatLng(json.coordinates[1],json.coordinates[0]);
};
CartoDB.Infowindow.prototype.moveMaptoOpen = function() {
var left = 0;
var top = 0;
var div = this.div_;
var pixPosition = this.getProjection().fromLatLngToContainerPixel(this.latlng_);
if ((pixPosition.x + this.offsetHorizontal_) < 0) {
left = (pixPosition.x + this.offsetHorizontal_ - 20);
}
if ((pixPosition.x + 180) >= ($('#'+this.params_.map_canvas).width())) {
left = (pixPosition.x + 180 - $('#'+this.params_.map_canvas).width());
}
if ((pixPosition.y - $(div).height()) < 0) {
top = (pixPosition.y - $(div).height() - 30);
}
this.map_.panBy(left,top);
};
})(jQuery);

View File

@ -1,26 +0,0 @@
/*other stuff*/
/*Marker infowindow*/
div.cartodb_infowindow {position:absolute; display:block; width:214px; padding:0; visibility:hidden;}
div.cartodb_infowindow a.close {position:absolute; right:3px; top:3px; width:22px; height:15px; padding:4px 0 3px 0; text-align:center; font:bold 15px "Helvetica",Arial; color:#666666; text-decoration:none; line-height:15px}
div.cartodb_infowindow a.close:hover {color:#333333}
div.cartodb_infowindow div.outer_top {width:186px; padding:25px 18px 5px 10px; background:url('sprite.png') 0 top;}
div.cartodb_infowindow div.top {width:186px; max-height:200px; overflow-y:auto; overflow-x:hidden}
div.cartodb_infowindow div.top .jspTrack {background: #dddddd;}
div.cartodb_infowindow div.top .jspDrag {background: #999999;}
div.cartodb_infowindow div.top .jspHover, div.cartodb_infowindow div.top .jspActive {background:#666666}
div.cartodb_infowindow div.top label {display:block; width:auto; padding:0 0 0 5px; font:normal 11px Arial; color:#B3B3B3; text-shadow:0 1px white}
div.cartodb_infowindow div.top p {display:block; width:170px; max-height:20px; padding:2px 4px; margin:2px 0 7px; font:bold 11px 'Helvetica',Arial; color:#666666; border:none; background:none; text-shadow:0 1px white;
text-overflow:ellipsis; overflow:hidden; white-space:nowrap;}
div.cartodb_infowindow div.top p.empty {font-weight:normal; font-style:italic; color:#b7b7b7;}
/*div.cartodb_infowindow div.top p:focus {border:1px solid #666666; border-radius:5px; -webkit-border-radius:5px; -moz-border-radius:5px;}*/
div.cartodb_infowindow div.bottom {width:180px; height:36px; padding:11px 16px 10px 10px; background:url('sprite.png') no-repeat right top;}
div.cartodb_infowindow div.bottom label {float:left; margin:5px 0 0 3px; font:normal 11px Arial; color:#B3B3B3; text-shadow:0 1px white}
div.cartodb_infowindow div.bottom label strong {font:bold 11px 'Helvetica',Arial; color:#666666; text-shadow:0 1px white;}
div.cartodb_infowindow div.bottom a {float:right; height:12px; margin:0 5px 0 0; padding:4px 7px; border:1px solid #999; font:bold 11px "Helvetica",Arial; color:#333333; text-align:center;
text-decoration:none; -webkit-border-radius:3px; -moz-border-radius:3px; border-radius:3px; text-shadow:0 1px white; background:linear-gradient(-90deg, #FFFFFF, #CACBCE); background:-webkit-gradient(linear, 50% 0%, 50% 100%, from(#FFFFFF), to(#CACBCE));
background:-moz-linear-gradient(-90deg, #FFFFFF, #CACBCE); background: -o-linear-gradient(#FFFFFF,#CACBCE); filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#FFFFFF', endColorstr='#CACBCE');}
div.cartodb_infowindow div.bottom a:hover {background:linear-gradient(-90deg, #CACBCE, #FFFFFF); background:-webkit-gradient(linear, 50% 0%, 50% 100%, from(#CACBCE), to(#FFFFFF));
background:-moz-linear-gradient(-90deg, #CACBCE, #FFFFFF); background: -o-linear-gradient(#CACBCE,#FFFFFF); filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#CACBCE', endColorstr='#FFFFFF'); cursor:pointer;}
a.cartodb_logo {position:absolute; bottom:8px; left:75px; display:block; width:69px; height:27px; background:url('http://cartodb.s3.amazonaws.com/embed/embed_sprite.png') no-repeat -61px 0;
text-indent:-9999px; line-height:0; font-size:0;}
div.torque_time {position:absolute; bottom:28px; right:15px; display:block; width:189px; height:27px;color:white;}

View File

@ -1,66 +0,0 @@
/* Simple JavaScript Inheritance
* By John Resig http://ejohn.org/
* MIT Licensed.
*/
// Inspired by base2 and Prototype
(function(){
var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
// The base Class implementation (does nothing)
this.Class = function(){};
// Create a new Class that inherits from this class
Class.extend = Class.implement = function(prop) {
var _super = this.prototype;
// Instantiate a base class (but only create the instance,
// don't run the init constructor)
initializing = true;
var prototype = new this();
initializing = false;
// Copy the properties over onto the new prototype
for (var name in prop) {
// Check if we're overwriting an existing function
prototype[name] = typeof prop[name] == "function" &&
typeof _super[name] == "function" && fnTest.test(prop[name]) ?
(function(name, fn){
return function() {
var tmp = this._super;
// Add a new ._super() method that is the same method
// but on the super-class
this._super = _super[name];
// The method only need to be bound temporarily, so we
// remove it when we're done executing
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, prop[name]) :
prop[name];
}
// The dummy class constructor
function Class() {
// All construction is actually done in the init method
if ( !initializing && this.init )
this.init.apply(this, arguments);
}
// Populate our constructed prototype object
Class.prototype = prototype;
// Enforce the constructor to be what we expect
Class.constructor = Class;
// And make this class extendable
Class.extend = arguments.callee;
Class.implement = arguments.callee;
return Class;
};
})();

94
lib/dat.gui.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

493
lib/torque/gmaps/CanvasLayer.js Executable file
View File

@ -0,0 +1,493 @@
/**
* @license
* Copyright 2013 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview Extends OverlayView to provide a canvas "Layer".
* @author Brendan Kenny
*/
/**
* A map layer that provides a canvas over the slippy map and a callback
* system for efficient animation. Requires canvas and CSS 2D transform
* support.
* @constructor
* @extends google.maps.OverlayView
* @param {CanvasLayerOptions=} opt_options Options to set in this CanvasLayer.
*/
function CanvasLayer(opt_options) {
/**
* If true, canvas is in a map pane and the OverlayView is fully functional.
* See google.maps.OverlayView.onAdd for more information.
* @type {boolean}
* @private
*/
this.isAdded_ = false;
/**
* If true, each update will immediately schedule the next.
* @type {boolean}
* @private
*/
this.isAnimated_ = false;
/**
* The name of the MapPane in which this layer will be displayed.
* @type {string}
* @private
*/
this.paneName_ = CanvasLayer.DEFAULT_PANE_NAME_;
/**
* A user-supplied function called whenever an update is required. Null or
* undefined if a callback is not provided.
* @type {?function=}
* @private
*/
this.updateHandler_ = null;
/**
* A user-supplied function called whenever an update is required and the
* map has been resized since the last update. Null or undefined if a
* callback is not provided.
* @type {?function}
* @private
*/
this.resizeHandler_ = null;
/**
* The LatLng coordinate of the top left of the current view of the map. Will
* be null when this.isAdded_ is false.
* @type {google.maps.LatLng}
* @private
*/
this.topLeft_ = null;
/**
* The map-pan event listener. Will be null when this.isAdded_ is false. Will
* be null when this.isAdded_ is false.
* @type {?function}
* @private
*/
this.centerListener_ = null;
/**
* The map-resize event listener. Will be null when this.isAdded_ is false.
* @type {?function}
* @private
*/
this.resizeListener_ = null;
/**
* If true, the map size has changed and this.resizeHandler_ must be called
* on the next update.
* @type {boolean}
* @private
*/
this.needsResize_ = true;
/**
* A browser-defined id for the currently requested callback. Null when no
* callback is queued.
* @type {?number}
* @private
*/
this.requestAnimationFrameId_ = null;
var canvas = document.createElement('canvas');
canvas.style.position = 'absolute';
canvas.style.top = 0;
canvas.style.left = 0;
canvas.style.pointerEvents = 'none';
/**
* The canvas element.
* @type {!HTMLCanvasElement}
*/
this.canvas = canvas;
/**
* Simple bind for functions with no args for bind-less browsers (Safari).
* @param {Object} thisArg The this value used for the target function.
* @param {function} func The function to be bound.
*/
function simpleBindShim(thisArg, func) {
return function() { func.apply(thisArg); };
}
/**
* A reference to this.repositionCanvas_ with this bound as its this value.
* @type {function}
* @private
*/
this.repositionFunction_ = simpleBindShim(this, this.repositionCanvas_);
/**
* A reference to this.resize_ with this bound as its this value.
* @type {function}
* @private
*/
this.resizeFunction_ = simpleBindShim(this, this.resize_);
/**
* A reference to this.update_ with this bound as its this value.
* @type {function}
* @private
*/
this.requestUpdateFunction_ = simpleBindShim(this, this.update_);
// set provided options, if any
if (opt_options) {
this.setOptions(opt_options);
}
}
CanvasLayer.prototype = new google.maps.OverlayView();
/**
* The default MapPane to contain the canvas.
* @type {string}
* @const
* @private
*/
CanvasLayer.DEFAULT_PANE_NAME_ = 'overlayLayer';
/**
* Transform CSS property name, with vendor prefix if required. If browser
* does not support transforms, property will be ignored.
* @type {string}
* @const
* @private
*/
CanvasLayer.CSS_TRANSFORM_ = (function() {
var div = document.createElement('div');
var transformProps = [
'transform',
'WebkitTransform',
'MozTransform',
'OTransform',
'msTransform'
];
for (var i = 0; i < transformProps.length; i++) {
var prop = transformProps[i];
if (div.style[prop] !== undefined) {
return prop;
}
}
// return unprefixed version by default
return transformProps[0];
})();
/**
* The requestAnimationFrame function, with vendor-prefixed or setTimeout-based
* fallbacks. MUST be called with window as thisArg.
* @type {function}
* @param {function} callback The function to add to the frame request queue.
* @return {number} The browser-defined id for the requested callback.
* @private
*/
CanvasLayer.prototype.requestAnimFrame_ =
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback) {
return window.setTimeout(callback, 1000 / 60);
};
/**
* The cancelAnimationFrame function, with vendor-prefixed fallback. Does not
* fall back to clearTimeout as some platforms implement requestAnimationFrame
* but not cancelAnimationFrame, and the cost is an extra frame on onRemove.
* MUST be called with window as thisArg.
* @type {function}
* @param {number=} requestId The id of the frame request to cancel.
* @private
*/
CanvasLayer.prototype.cancelAnimFrame_ =
window.cancelAnimationFrame ||
window.webkitCancelAnimationFrame ||
window.mozCancelAnimationFrame ||
window.oCancelAnimationFrame ||
window.msCancelAnimationFrame ||
function(requestId) {};
/**
* Sets any options provided. See CanvasLayerOptions for more information.
* @param {CanvasLayerOptions} options The options to set.
*/
CanvasLayer.prototype.setOptions = function(options) {
if (options.animate !== undefined) {
this.setAnimate(options.animate);
}
if (options.paneName !== undefined) {
this.setPane(options.paneName);
}
if (options.updateHandler !== undefined) {
this.setUpdateHandler(options.updateHandler);
}
if (options.resizeHandler !== undefined) {
this.setResizeHandler(options.resizeHandler);
}
if (options.map !== undefined) {
this.setMap(options.map);
}
};
/**
* Set the animated state of the layer. If true, updateHandler will be called
* repeatedly, once per frame. If false, updateHandler will only be called when
* a map property changes that could require the canvas content to be redrawn.
* @param {boolean} animate Whether the canvas is animated.
*/
CanvasLayer.prototype.setAnimate = function(animate) {
this.isAnimated_ = !!animate;
if (this.isAnimated_) {
this.scheduleUpdate();
}
};
/**
* @return {boolean} Whether the canvas is animated.
*/
CanvasLayer.prototype.isAnimated = function() {
return this.isAnimated_;
};
/**
* Set the MapPane in which this layer will be displayed, by name. See
* {@code google.maps.MapPanes} for the panes available.
* @param {string} paneName The name of the desired MapPane.
*/
CanvasLayer.prototype.setPaneName = function(paneName) {
this.paneName_ = paneName;
this.setPane_();
};
/**
* @return {string} The name of the current container pane.
*/
CanvasLayer.prototype.getPaneName = function() {
return this.paneName_;
};
/**
* Adds the canvas to the specified container pane. Since this is guaranteed to
* execute only after onAdd is called, this is when paneName's existence is
* checked (and an error is thrown if it doesn't exist).
* @private
*/
CanvasLayer.prototype.setPane_ = function() {
if (!this.isAdded_) {
return;
}
// onAdd has been called, so panes can be used
var panes = this.getPanes();
if (!panes[this.paneName_]) {
throw new Error('"' + this.paneName_ + '" is not a valid MapPane name.');
}
panes[this.paneName_].appendChild(this.canvas);
};
/**
* Set a function that will be called whenever the parent map and the overlay's
* canvas have been resized. If opt_resizeHandler is null or unspecified, any
* existing callback is removed.
* @param {?function=} opt_resizeHandler The resize callback function.
*/
CanvasLayer.prototype.setResizeHandler = function(opt_resizeHandler) {
this.resizeHandler_ = opt_resizeHandler;
};
/**
* Set a function that will be called when a repaint of the canvas is required.
* If opt_updateHandler is null or unspecified, any existing callback is
* removed.
* @param {?function=} opt_updateHandler The update callback function.
*/
CanvasLayer.prototype.setUpdateHandler = function(opt_updateHandler) {
this.updateHandler_ = opt_updateHandler;
};
/**
* @inheritDoc
*/
CanvasLayer.prototype.onAdd = function() {
if (this.isAdded_) {
return;
}
this.isAdded_ = true;
this.setPane_();
this.resizeListener_ = google.maps.event.addListener(this.getMap(),
'resize', this.resizeFunction_);
this.centerListener_ = google.maps.event.addListener(this.getMap(),
'center_changed', this.repositionFunction_);
this.resize_();
this.repositionCanvas_();
};
/**
* @inheritDoc
*/
CanvasLayer.prototype.onRemove = function() {
if (!this.isAdded_) {
return;
}
this.isAdded_ = false;
this.topLeft_ = null;
// remove canvas and listeners for pan and resize from map
this.canvas.parentElement.removeChild(this.canvas);
if (this.centerListener_) {
google.maps.event.removeListener(this.centerListener_);
this.centerListener_ = null;
}
if (this.resizeListener_) {
google.maps.event.removeListener(this.resizeListener_);
this.resizeListener_ = null;
}
// cease canvas update callbacks
if (this.requestAnimationFrameId_) {
this.cancelAnimFrame_.call(window, this.requestAnimationFrameId_);
this.requestAnimationFrameId_ = null;
}
};
/**
* The internal callback for resize events that resizes the canvas to keep the
* map properly covered.
* @private
*/
CanvasLayer.prototype.resize_ = function() {
// TODO(bckenny): it's common to use a smaller canvas but use CSS to scale
// what is drawn by the browser to save on fill rate. Add an option to do
// this.
if (!this.isAdded_) {
return;
}
var map = this.getMap();
var width = map.getDiv().offsetWidth;
var height = map.getDiv().offsetHeight;
var oldWidth = this.canvas.width;
var oldHeight = this.canvas.height;
// resizing may allocate a new back buffer, so do so conservatively
if (oldWidth !== width || oldHeight !== height) {
this.canvas.width = width;
this.canvas.height = height;
this.canvas.style.width = width + 'px';
this.canvas.style.height = height + 'px';
this.needsResize_ = true;
this.scheduleUpdate();
}
};
/**
* @inheritDoc
*/
CanvasLayer.prototype.draw = function() {
this.repositionCanvas_();
};
/**
* Internal callback for map view changes. Since the Maps API moves the overlay
* along with the map, this function calculates the opposite translation to
* keep the canvas in place.
* @private
*/
CanvasLayer.prototype.repositionCanvas_ = function() {
// TODO(bckenny): *should* only be executed on RAF, but in current browsers
// this causes noticeable hitches in map and overlay relative
// positioning.
var bounds = this.getMap().getBounds();
this.topLeft_ = new google.maps.LatLng(bounds.getNorthEast().lat(),
bounds.getSouthWest().lng());
// canvas position relative to draggable map's conatainer depends on
// overlayView's projection, not the map's
var projection = this.getProjection();
var divTopLeft = projection.fromLatLngToDivPixel(this.topLeft_);
this.canvas.style[CanvasLayer.CSS_TRANSFORM_] = 'translate(' +
Math.round(divTopLeft.x) + 'px,' + Math.round(divTopLeft.y) + 'px)';
this.scheduleUpdate();
};
/**
* Internal callback that serves as main animation scheduler via
* requestAnimationFrame. Calls resize and update callbacks if set, and
* schedules the next frame if overlay is animated.
* @private
*/
CanvasLayer.prototype.update_ = function() {
this.requestAnimationFrameId_ = null;
if (!this.isAdded_) {
return;
}
if (this.isAnimated_) {
this.scheduleUpdate();
}
if (this.needsResize_ && this.resizeHandler_) {
this.needsResize_ = false;
this.resizeHandler_();
}
if (this.updateHandler_) {
this.updateHandler_();
}
};
/**
* A convenience method to get the current LatLng coordinate of the top left of
* the current view of the map.
* @return {google.maps.LatLng} The top left coordinate.
*/
CanvasLayer.prototype.getTopLeft = function() {
return this.topLeft_;
};
/**
* Schedule a requestAnimationFrame callback to updateHandler. If one is
* already scheduled, there is no effect.
*/
CanvasLayer.prototype.scheduleUpdate = function() {
if (this.isAdded_ && !this.requestAnimationFrameId_) {
this.requestAnimationFrameId_ =
this.requestAnimFrame_.call(window, this.requestUpdateFunction_);
}
};

View File

@ -0,0 +1,122 @@
/**
* full canvas layer implementation for Leaflet
*/
L.CanvasLayer = L.Class.extend({
includes: [L.Mixin.Events, L.Mixin.TileLoader],
options: {
minZoom: 0,
maxZoom: 28,
tileSize: 256,
subdomains: 'abc',
errorTileUrl: '',
attribution: '',
zoomOffset: 0,
opacity: 1,
unloadInvisibleTiles: L.Browser.mobile,
updateWhenIdle: L.Browser.mobile,
tileLoader: false // installs tile loading events
},
initialize: function (options) {
var self = this;
//this.project = this._project.bind(this);
this.render = this.render.bind(this);
L.Util.setOptions(this, options);
this._canvas = document.createElement('canvas');
this._ctx = this._canvas.getContext('2d');
var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
this.requestAnimationFrame = requestAnimationFrame;
},
onAdd: function (map) {
this._map = map;
this._staticPane = map._createPane('leaflet-tile-pane', map._container);
this._staticPane.appendChild(this._canvas);
map.on({
'viewreset': this._reset
//'move': this._render
}, this);
map.on('move', this._render, this);//function(){ console.log("a"); }, this);
if(this.options.tileLoader) {
this._initTileLoader();
}
this._reset();
},
getCanvas: function() {
return this._canvas;
},
draw: function() {
return this._reset();
},
onRemove: function (map) {
map._container.removeChild(this._staticPane);
map.off({
'viewreset': this._reset,
'move': this._render
}, this);
},
addTo: function (map) {
map.addLayer(this);
return this;
},
setOpacity: function (opacity) {
this.options.opacity = opacity;
this._updateOpacity();
return this;
},
bringToFront: function () {
return this;
},
bringToBack: function () {
return this;
},
_reset: function () {
var size = this._map.getSize();
this._canvas.width = size.x;
this._canvas.height = size.y;
this.onResize();
this._render();
},
/*
_project: function(x) {
var point = this._map.latLngToLayerPoint(new L.LatLng(x[1], x[0]));
return [point.x, point.y];
},
*/
_updateOpacity: function () { },
_render: function() {
this.requestAnimationFrame.call(window, this.render);
},
redraw: function() {
this._render();
},
onResize: function() {
},
render: function() {
throw new Error('render function should be implemented');
}
});

View File

@ -0,0 +1,451 @@
/*
====================
this class renders tile data in a given time
====================
*/
function TimePlayer(min_date, end, step, options) {
this.time = 0;
this.step = step;
this.CAP_UNIT = end;
this.MIN_DATE = min_date;
this.MAX_UNITS = options.steps + 2;
this.MAX_VALUE = 0;
this.MAX_VALUE_LOG = 0;
this.BASE_UNIT = 0;
this.canvas_setup = this.get_time_data;
this.render = this.render_time;
this.cells = [];
this.table = options.table;
this.user = options.user;
this.t_column = options.column;
this.resolution = options.resolution;
this.countby = options.countby
this.base_url = 'http://a.netdna.cartocdn.com/' + this.user + '/api/v2/sql';
this.options = options;
}
TimePlayer.prototype = new CanvasTileLayer();
/**
* change time, t is the month (integer)
*/
TimePlayer.prototype.set_time = function (t) {
if (this.time != (t >> 0)) {
this.time = t;
this.redraw();
}
};
TimePlayer.prototype.reset_max_value = function () {
this.MAX_VALUE = 0;
this.MAX_VALUE_LOG = 0;
};
/**
* change table where the data is choosen
*/
TimePlayer.prototype.set_table = function (table, size) {
if (this.table === table) {
return; // nothing to do
}
this.table = table;
this.pixel_size = size;
this.recreate();
this.redraw();
};
/**
* private
*/
// get data from cartodb
TimePlayer.prototype.sql = function (sql, callback) {
var self = this;
$.getJSON(this.base_url + "?q=" + encodeURIComponent(sql), function (data) {
callback(data);
});
};
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];
}
// precache data to render fast
TimePlayer.prototype.pre_cache_months = function (rows, coord, zoom) {
var row;
var xcoords;
var ycoords;
var values;
if (typeof(ArrayBuffer) !== undefined) {
xcoords = new Uint8Array(new ArrayBuffer(rows.length));
ycoords = new Uint8Array(new ArrayBuffer(rows.length));
values = new Uint8Array(new ArrayBuffer(rows.length * this.MAX_UNITS));// 256 months
} else {
// fallback
xcoords = [];
ycoords = [];
values = [];
// array buffer set by default to 0
for (var i = 0; i < rows.length * this.MAX_UNITS; ++i) {
values[i] = 0;
}
}
// base tile x, y
var tile_base_x = coord.x * 256;
var tile_base_y = coord.y * 256;
var total_pixels = 256 << zoom;
for (var i in rows) {
row = rows[i];
pixels = meterToPixels(row.x, row.y, zoom);
pixels[1] = total_pixels - pixels[1];
xcoords[i] = pixels[0];
ycoords[i] = pixels[1];
var base_idx = i * this.MAX_UNITS;
//def[row.sd[0]] = row.se[0];
for (var j = 0; j < row.dates.length; ++j) {
values[base_idx + row.dates[j]] = row.vals[j];
if (row.vals[j] > this.MAX_VALUE) {
this.MAX_VALUE = row.vals[j];
this.MAX_VALUE_LOG = Math.log(this.MAX_VALUE);
}
}
;
if (this.options.cumulative) {
for (var j = 1; j < this.MAX_UNITS; ++j) {
values[base_idx + j] += values[base_idx + j - 1];
if (this.options.cumulative_expires) {
for ( var u = 0; u < row.dates_end.length; ++u ) {
if ( row.dates_end[u] != null && row.dates_end[u] < (j+1) ) {
values[base_idx + j - 1 ] -= row.vals[u]
}
}
}
if (values[base_idx + j] > this.MAX_VALUE) {
this.MAX_VALUE = values[base_idx + j];
this.MAX_VALUE_LOG = Math.log(this.MAX_VALUE);
}
}
}
}
return {
length:rows.length,
xcoords:xcoords,
ycoords:ycoords,
values:values,
size:1 << (this.resolution * 2)
};
};
// get time data in json format
TimePlayer.prototype.get_time_data = function (tile, coord, zoom) {
var self = this;
if (!self.table) {
return;
}
// get x, y for cells and sd, se for deforestation changes
// sd contains the months
// se contains the deforestation for each entry in sd
// take se and sd as a matrix [se|sd]
var numTiles = 1 << zoom;
var sql = ""
if ( this.options.cumulative_expires == true) {
sql = "WITH hgrid AS ( " +
" SELECT CDB_RectangleGrid( " +
" CDB_XYZ_Extent({0}, {1}, {2}), ".format(coord.x, coord.y, zoom) +
" CDB_XYZ_Resolution({0}) * {1}, ".format(zoom, this.resolution) +
" CDB_XYZ_Resolution({0}) * {1} ".format(zoom, this.resolution) +
" ) as cell " +
" ) " +
" SELECT " +
" x, y, array_agg(c) vals, array_agg(d) dates , array_agg(de) dates_end" +
" FROM ( " +
" SELECT " +
" round(CAST (st_xmax(hgrid.cell) AS numeric),4) x, " +
" round(CAST (st_ymax(hgrid.cell) AS numeric),4) y, " +
" {0} c, ".format(this.countby) +
" floor((date_part('epoch',{0})- {1})/{2}) d, ".format(this.t_column, this.MIN_DATE, this.step) +
" floor((date_part('epoch',{0})- {1})/{2}) de ".format(this.options.expiration_column, this.MIN_DATE, this.step) +
" FROM " +
" hgrid, {0} i ".format(this.table) +
" WHERE " +
" ST_Intersects(i.the_geom_webmercator, hgrid.cell) " +
" GROUP BY " +
" hgrid.cell, " +
" floor((date_part('epoch',{0})- {1})/{2}), ".format(this.t_column, this.MIN_DATE, this.step) +
" floor((date_part('epoch',{0})- {1})/{2})".format(this.options.expiration_column, this.MIN_DATE, this.step) +
" ) f GROUP BY x, y";
} else {
sql = "WITH hgrid AS ( " +
" SELECT CDB_RectangleGrid( " +
" CDB_XYZ_Extent({0}, {1}, {2}), ".format(coord.x, coord.y, zoom) +
" CDB_XYZ_Resolution({0}) * {1}, ".format(zoom, this.resolution) +
" CDB_XYZ_Resolution({0}) * {1} ".format(zoom, this.resolution) +
" ) as cell " +
" ) " +
" SELECT " +
" x, y, array_agg(c) vals, array_agg(d) dates " +
" FROM ( " +
" SELECT " +
" round(CAST (st_xmax(hgrid.cell) AS numeric),4) x, round(CAST (st_ymax(hgrid.cell) AS numeric),4) y, " +
" {0} c, floor((date_part('epoch',{1})- {2})/{3}) d ".format(this.countby, this.t_column, this.MIN_DATE, this.step) +
" FROM " +
" hgrid, {0} i ".format(this.table) +
" WHERE " +
" ST_Intersects(i.the_geom_webmercator, hgrid.cell) " +
" GROUP BY " +
" hgrid.cell, floor((date_part('epoch',{0})- {1})/{2})".format(this.t_column, this.MIN_DATE, this.step) +
" ) f GROUP BY x, y";
}
var prof = Profiler.get('tile fetch');
prof.start();
this.sql(sql, function (data) {
if (data.rows) {
prof.end();
var p = Profiler.get('tile data cache');
p.start();
tile.cells = self.pre_cache_months(data.rows, coord, zoom);
p.end();
p = Profiler.get('tile render');
p.start();
self.redraw_tile(tile);
p.end();
}
});
};
YO = 1;
TimePlayer.prototype.render_time = function (tile, coord, zoom) {
var self = this;
//var month = -this.BASE_UNIT + 1 + this.time>>0;
//var month = Math.ceil(this.MAX_UNITS * (this.time - this.BASE_UNIT)/(this.CAP_UNIT-this.BASE_UNIT));
var month = this.time;
var w = tile.canvas.width;
var h = tile.canvas.height;
var ctx = tile.ctx;
var i, x, y, cell, cells;
cells = tile.cells;
if (!cells || cells.length === 0) {
return;
}
var colors = [
//"#FFFFE5",
//"#FFF7BC",
"#FEE391",
"#FEC44F",
"#FE9929",
"#EC7014",
"#CC4C02",
"#993404",
"#662506"
];
var fillStyle;
// clear canvas
tile.canvas.width = w;
var ci = 0;
var cu = 0;
ctx.strokeStyle = ctx.fillStyle = colors[cu];
ctx.globalCompositeOperation = this.options.blendmode;
var xc = cells.xcoords;
var yc = cells.ycoords;
var vals = cells.values;
var dz = 256 / Math.pow(2, zoom)
// render cells
var len = cells.length;
var pixel_size = this.resolution//*this.options.cellsize;
var pixel_size_trail_circ = pixel_size * 2;
var pixel_size_trail_squa = pixel_size * 1.5;
var offset = Math.floor((pixel_size - 1) / 2);
var tau = Math.PI * 2;
// memoize sprite canvases
if (self.sprite_1 == undefined) {
self.sprite_1 = [];
$(colors).each(function () {
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
ctx.width = canvas.width = pixel_size * 2;
ctx.height = canvas.height = pixel_size * 2;
ctx.globalAlpha = 1;
ctx.fillStyle = this.toString();
ctx.beginPath();
ctx.arc(pixel_size, pixel_size, pixel_size, 0, tau, true, true);
ctx.closePath();
ctx.fill();
self.sprite_1.push(canvas);
});
}
if (self.sprite_2 == undefined) {
self.sprite_2 = [];
$(colors).each(function () {
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
ctx.width = canvas.width = pixel_size_trail_circ * 2;
ctx.height = canvas.height = pixel_size_trail_circ * 2;
ctx.globalAlpha = 0.3;
ctx.fillStyle = this.toString();
ctx.beginPath();
ctx.arc(pixel_size_trail_circ, pixel_size_trail_circ, pixel_size_trail_circ, 0, tau, true, true);
ctx.closePath();
ctx.fill();
self.sprite_2.push(canvas);
});
}
if (self.sprite_3 == undefined) {
self.sprite_3 = [];
$(colors).each(function () {
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
ctx.width = canvas.width = pixel_size * 2;
ctx.height = canvas.height = pixel_size * 2;
ctx.globalAlpha = 0.3;
ctx.fillStyle = this.toString();
ctx.beginPath();
ctx.arc(pixel_size, pixel_size, pixel_size, 0, tau, true, true);
ctx.closePath();
ctx.fill();
self.sprite_3.push(canvas);
});
}
var numTiles = 1 << zoom;
for (i = 0; i < len; ++i) {
var cell = cells.values[this.MAX_UNITS * i + month];
if (cell) {
ci = cell == 0 ? 0 : Math.floor((colors.length - 1) * (Math.log(cell) / this.MAX_VALUE_LOG));
if (ci != cu) {
cu = ci < colors.length ? ci : cu;
ctx.fillStyle = colors[cu];
}
if (this.options.point_type == 'circle') {
ctx.drawImage(self.sprite_1[cu], xc[i] - pixel_size, yc[i] - pixel_size)
} else if (this.options.point_type == 'square') {
ctx.fillRect(xc[i] - offset, yc[i] - offset, pixel_size, pixel_size);
}
}
if (this.options.trails == true) {
cell = cells.values[this.MAX_UNITS * i + month - 1];
if (cell) {
ci = cell == 0 ? 0 : Math.floor((colors.length - 1) * (Math.log(cell) / this.MAX_VALUE_LOG));
if (ci != cu) {
cu = ci < colors.length ? ci : cu;
ctx.fillStyle = colors[cu];
}
if (this.options.point_type == 'circle') {
//alignment hack - sorry to the gods of graphics
ctx.drawImage(self.sprite_2[cu], xc[i] - pixel_size_trail_squa - 1, yc[i] - pixel_size_trail_squa - 1)
} else if (this.options.point_type == 'square') {
ctx.fillRect(xc[i] - offset, yc[i] - offset, pixel_size_trail_squa, pixel_size_trail_squa);
}
}
cell = cells.values[this.MAX_UNITS * i + month - 2];
if (cell) {
ci = cell == 0 ? 0 : Math.floor((colors.length - 1) * (Math.log(cell) / this.MAX_VALUE_LOG));
if (ci != cu) {
cu = ci < colors.length ? ci : cu;
ctx.fillStyle = colors[cu];
}
if (this.options.point_type == 'circle') {
ctx.drawImage(self.sprite_3[cu], xc[i] - pixel_size, yc[i] - pixel_size)
} else if (this.options.point_type == 'square') {
ctx.fillRect(xc[i] - offset, yc[i] - offset, pixel_size, pixel_size);
}
}
}
}
};
/**
* String formatting for JavaScript.
*
* Usage:
*
* "{0} is {1}".format("CartoDB", "epic!");
* // CartoDB is epic!
*
*/
String.prototype.format = (function (i, safe, arg) {
function format() {
var str = this,
len = arguments.length + 1;
for (i = 0; i < len; arg = arguments[i++]) {
safe = typeof arg === 'object' ? JSON.stringify(arg) : arg;
str = str.replace(RegExp('\\{' + (i - 1) + '\\}', 'g'), safe);
}
return str;
}
//format.native = String.prototype.format;
return format;
})();
// =================
// profiler
// =================
function Profiler() {
}
Profiler.times = {};
Profiler.new_time = function (type, time) {
var t = Profiler.times[type] = Profiler.times[type] || {
max:0,
min:10000000,
avg:0,
total:0,
count:0
};
t.max = Math.max(t.max, time);
t.total += time;
t.min = Math.min(t.min, time);
++t.count;
t.avg = t.total / t.count;
};
Profiler.print_stats = function () {
for (k in Profiler.times) {
var t = Profiler.times[k];
console.log(" === " + k + " === ");
console.log(" max: " + t.max);
console.log(" min: " + t.min);
console.log(" avg: " + t.avg);
console.log(" total: " + t.total);
}
};
Profiler.get = function (type) {
return {
t0:null,
start:function () {
this.t0 = new Date().getTime();
},
end:function () {
if (this.t0 !== null) {
Profiler.new_time(type, this.time = new Date().getTime() - this.t0);
this.t0 = null;
}
}
};
};

View File

@ -0,0 +1,122 @@
L.Mixin.TileLoader = {
_initTileLoader: function() {
this._tiles = {}
this._tilesToLoad = 0;
this._map.on({
'moveend': this._updateTiles
}, this);
this._updateTiles();
},
_removeTileLoader: function() {
map.off({
'moveend': this._updateTiles
}, this);
//TODO: remove tiles
},
_updateTiles: function () {
if (!this._map) { return; }
var bounds = this._map.getPixelBounds(),
zoom = this._map.getZoom(),
tileSize = this.options.tileSize;
if (zoom > this.options.maxZoom || zoom < this.options.minZoom) {
return;
}
var nwTilePoint = new L.Point(
Math.floor(bounds.min.x / tileSize),
Math.floor(bounds.min.y / tileSize)),
seTilePoint = new L.Point(
Math.floor(bounds.max.x / tileSize),
Math.floor(bounds.max.y / tileSize)),
tileBounds = new L.Bounds(nwTilePoint, seTilePoint);
this._addTilesFromCenterOut(tileBounds);
this._removeOtherTiles(tileBounds);
},
_removeOtherTiles: function (bounds) {
var kArr, x, y, key;
for (key in this._tiles) {
if (this._tiles.hasOwnProperty(key)) {
kArr = key.split(':');
x = parseInt(kArr[0], 10);
y = parseInt(kArr[1], 10);
// remove tile if it's out of bounds
if (x < bounds.min.x || x > bounds.max.x || y < bounds.min.y || y > bounds.max.y) {
this._removeTile(key);
}
}
}
},
_removeTile: function (key) {
this.fire('tileRemoved', this._tiles[key]);
delete this._tiles[key];
},
_tileShouldBeLoaded: function (tilePoint) {
return !((tilePoint.x + ':' + tilePoint.y + ':' + tilePoint.zoom) in this._tiles);
},
_tileLoaded: function(tilePoint, tileData) {
this._tilesToLoad--;
this._tiles[tilePoint.x + ':' + tilePoint.y + ':' + tilePoint.zoom] = tileData;
if(this._tilesToLoad === 0) {
this.fire("tilesLoaded");
}
},
getTilePos: function (tilePoint) {
tilePoint = new L.Point(tilePoint.x, tilePoint.y);
var origin = this._map._getNewTopLeftPoint(this._map.getCenter()),
tileSize = this.options.tileSize;
return tilePoint.multiplyBy(tileSize).subtract(origin);
},
_addTilesFromCenterOut: function (bounds) {
var queue = [],
center = bounds.getCenter(),
zoom = this._map.getZoom();
var j, i, point;
for (j = bounds.min.y; j <= bounds.max.y; j++) {
for (i = bounds.min.x; i <= bounds.max.x; i++) {
point = new L.Point(i, j);
point.zoom = zoom;
if (this._tileShouldBeLoaded(point)) {
queue.push(point);
}
}
}
var tilesToLoad = queue.length;
if (tilesToLoad === 0) { return; }
// load tiles in order of their distance to center
queue.sort(function (a, b) {
return a.distanceTo(center) - b.distanceTo(center);
});
this._tilesToLoad += tilesToLoad;
for (i = 0; i < tilesToLoad; i++) {
this.fire('tileAdded', queue[i]);
}
}
}

71
lib/torque/profiler.js Normal file
View File

@ -0,0 +1,71 @@
// =================
// profiler
// =================
//
// Counters
// pendingJobs.inc();
// pendingJobs.dec();
//
// Meters
// A meter measures the rate of events over time
// requests.mark();
//
// Histograms
// responseSizes.update(response.getContent().length);
//
// Timers
// private final Timer responses = metrics.timer(name(RequestHandler.class, "responses"));
//
// final Timer.Context context = responses.time();
//try {
//return "OK";
//} finally {
//context.stop();
//}
// Health Checks
//
function Profiler() {
}
Profiler.times = {};
Profiler.new_time = function (type, time) {
var t = Profiler.times[type] = Profiler.times[type] || {
max:0,
min:10000000,
avg:0,
total:0,
count:0
};
t.max = Math.max(t.max, time);
t.total += time;
t.min = Math.min(t.min, time);
++t.count;
t.avg = t.total / t.count;
};
Profiler.print_stats = function () {
for (k in Profiler.times) {
var t = Profiler.times[k];
console.log(" === " + k + " === ");
console.log(" max: " + t.max);
console.log(" min: " + t.min);
console.log(" avg: " + t.avg);
console.log(" total: " + t.total);
}
};
Profiler.get = function (type) {
return {
t0:null,
start:function () {
this.t0 = new Date().getTime();
},
end:function () {
if (this.t0 !== null) {
Profiler.new_time(type, this.time = new Date().getTime() - this.t0);
this.t0 = null;
}
}
};
};

178
lib/torque/provider.json.js Normal file
View File

@ -0,0 +1,178 @@
(function(exports) {
exports.torque = exports.torque || {};
var providers = exports.torque.providers = exports.torque.providers || {};
// format('hello, {0}', 'rambo') -> "hello, rambo"
function format(str, attrs) {
for(var i = 1; i < arguments.length; ++i) {
var attrs = arguments[i];
for(var attr in attrs) {
str = str.replace(RegExp('\\{' + attr + '\\}', 'g'), attrs[attr]);
}
}
return str;
}
var json = function (options) {
// check options
//if(!options.user) throw new Error("user should be provided");
if (options.resolution === undefined ) throw new Error("resolution should be provided");
this.options = options;
};
json.prototype = {
/**
* return the torque tile encoded in an efficient javascript
* structure:
* {
* x:Uint8Array x coordinates in tile reference system, normally from 0-255
* y:Uint8Array y coordinates in tile reference system
* Index: Array index to the properties
* }
*/
proccessTile: function(rows, coord, zoom) {
var x = new Uint8Array(rows.length);
var y = new Uint8Array(rows.length);
// count number of dates
var dates = 0;
var maxDateSlots = 0;
for (var r = 0; r < rows.length; ++r) {
var row = rows[r];
dates += row['dates__uint16'].length;
maxDateSlots = Math.max(maxDateSlots, row.dates__uint16.length);
}
// reserve memory for all the dates
var timeIndex = new Int32Array(maxDateSlots); //index-size
var timeCount = new Int32Array(maxDateSlots);
var renderData = new Uint8Array(dates);
var renderDataPos = new Uint32Array(dates);
var rowsPerSlot = [];
// precache pixel positions
for (var r = 0; r < rows.length; ++r) {
var row = rows[r];
x[r] = row.x__uint8;
y[r] = 255 - row.y__uint8;
var dates = rows[r]['dates__uint16'];
var vals = rows[r]['vals__uint8'];
for (var j = 0, len = dates.length; j < len; ++j) {
var rr = rowsPerSlot[dates[j]] || (rowsPerSlot[dates[j]] = []);
rr.push([r, vals[j]]);
}
}
// for each timeslot search active buckets
var renderDataIndex = 0;
var timeSlotIndex = 0;
for(var i = 0; i < maxDateSlots; ++i) {
var c = 0;
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];
++renderDataIndex;
}
}
/*
for (var r = 0; r < rows.length; ++r) {
var dates = rows.get('dates__uint16')[r];
var vals = rows.get('vals__uint8')[r];
for (var j = 0, len = dates.length; j < len; ++j) {
if(dates[j] == i) {
++c;
renderData[renderDataIndex] = vals[j];
renderDataPos[renderDataIndex] = r;
++renderDataIndex;
}
}
}
*/
timeIndex[i] = timeSlotIndex;
timeCount[i] = c;
timeSlotIndex += c;
}
return {
x: x,
y: y,
coord: {
x: coord.x,
y: coord.y,
z: zoom,
},
timeCount: timeCount,
timeIndex: timeIndex,
renderDataPos: renderDataPos,
renderData: renderData
};
},
url: function() {
return this.options.url || 'http://' + this.options.user + '.cartodb.com/api/v2/sql';
},
// execute actual query
sql: function(sql, callback) {
torque.net.get(this.url() + "?q=" + encodeURIComponent(sql), function (data) {
callback(data);
});
},
/**
* `coord` object like {x : tilex, y: tiley }
* `zoom` quadtree zoom level
*/
getTileData: function(coord, zoom, callback) {
this.table = this.options.table;
var numTiles = 1 << zoom;
var sql = "" +
"WITH " +
"par AS (" +
" SELECT CDB_XYZ_Resolution({zoom})*{resolution} as res" +
", CDB_XYZ_Extent({x}, {y}, {zoom}) as ext " +
")," +
"cte AS ( "+
" SELECT ST_SnapToGrid(i.the_geom_webmercator, p.res) g" +
", {countby} c" +
", floor(({column} - {start_date})/{step}) d" +
" FROM {table} i, par p " +
" WHERE i.the_geom_webmercator && p.ext " +
" GROUP BY g, d" +
") " +
"" +
"SELECT least((st_x(g)-st_xmin(p.ext))/p.res, 255) x__uint8, " +
" least((st_y(g)-st_ymin(p.ext))/p.res, 255) y__uint8," +
" array_agg(c) vals__uint8," +
" array_agg(d) dates__uint16" +
" FROM cte, par p GROUP BY x__uint8, y__uint8";
var query = format(sql, this.options, {
zoom: zoom,
x: coord.x,
y: coord.y
});
var self = this;
this.sql(query, function (data) {
var rows = JSON.parse(data.responseText).rows;
callback(self.proccessTile(rows, coord, zoom));
});
}
};
torque.providers.json = json
})(typeof exports === "undefined" ? this : exports);

View File

@ -0,0 +1,96 @@
(function(exports) {
exports.torque = exports.torque || {};
exports.torque.renderer = exports.torque.renderer || {};
var TAU = Math.PI * 2;
var DEFAULT_COLORS = [
"#FEE391",
"#FEC44F",
"#FE9929",
"#EC7014",
"#CC4C02",
"#993404",
"#662506"
];
//
// this renderer just render points depending of the value
//
function PointRenderer(canvas, options) {
if (!canvas) {
throw new Error("canvas can't be undefined");
}
this.options = options;
this._canvas = canvas;
this._ctx = canvas.getContext('2d');
this._sprites = [];
this.generateSprites();
}
PointRenderer.prototype = {
//
// pregenerate sprites to improve rendering. There should
// be a sprite for each one of the values of the categories
// if there is no sprite for that the value is not rendered
//
generateSprites: function() {
var pixel_size = this.options.pixel_size;
for(var c = 0; c < DEFAULT_COLORS.length; ++c) {
// create a canvas
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
ctx.width = canvas.width = pixel_size * 2;
ctx.height = canvas.height = pixel_size * 2;
ctx.globalAlpha = 1;
ctx.fillStyle = DEFAULT_COLORS[c];
// render a circle
ctx.beginPath();
ctx.arc(pixel_size, pixel_size, pixel_size, 0, TAU, true, true);
ctx.closePath();
ctx.fill();
this._sprites.push(canvas);
}
},
//
// renders a tile in the canvas for key defined in
// the torque tile
//
renderTile: function(tile, key) {
if(!this._canvas) return;
//var prof = Profiler.get('render').start();
var ctx = this._ctx;
var sprites = this._sprites;
var activePixels = tile.timeCount[key];
if(activePixels) {
var pixelIndex = tile.timeIndex[key];
for(var p = 0; p < activePixels; ++p) {
var posIdx = tile.renderDataPos[pixelIndex + p];
var c = tile.renderData[pixelIndex + p];
if(c) {
var sp = sprites[Math.min(c, sprites.length - 1)];
var x = tile.x[posIdx] - (sp.width >> 1);
var y = tile.y[posIdx] - (sp.height >> 1);
/*ctx.fillColor = '#F00';
ctx.fillRect(x, y, 100, 100);
*/
ctx.drawImage(
sp,
tile.x[posIdx] - (sp.width >> 1),
tile.y[posIdx] - (sp.height >> 1)
);
}
}
}
//prof.end();
}
};
// exports public api
exports.torque.renderer.Point = PointRenderer;
})(typeof exports === "undefined" ? this : exports);

27
lib/torque/request.js Normal file
View File

@ -0,0 +1,27 @@
(function(exports) {
var torque = exports.torque = exports.torque || {};
torque.net = torque.net || {};
function get(url, callback) {
var req = new XMLHttpRequest();
req.onreadystatechange = function() {
if (req.readyState == 4){
if (req.status == 200){
callback(req);
} else {
callback(null);
}
}
};
req.open("GET", url, true);
//req.responseType = 'arraybuffer';
req.send(null)
return req;
}
torque.net = {
get: get
};
})(typeof exports === "undefined" ? this : exports);

403
lib/torque/torque.js Normal file
View File

@ -0,0 +1,403 @@
/*
====================
this class renders tile data in a given time
====================
*/
function TimePlayer(min_date, end, step, options) {
this.time = 0;
this.step = step;
this.CAP_UNIT = end;
this.MIN_DATE = min_date;
this.MAX_UNITS = options.steps + 2;
this.MAX_VALUE = 0;
this.MAX_VALUE_LOG = 0;
this.BASE_UNIT = 0;
this.canvas_setup = this.get_time_data;
this.render = this.render_time;
this.cells = [];
this.table = options.table;
this.user = options.user;
this.t_column = options.column;
this.resolution = options.resolution;
this.countby = options.countby
this.base_url = 'http://a.netdna.cartocdn.com/' + this.user + '/api/v2/sql';
this.options = options;
}
TimePlayer.prototype = new CanvasTileLayer();
/**
* change time, t is the month (integer)
*/
TimePlayer.prototype.set_time = function (t) {
if (this.time != (t >> 0)) {
this.time = t;
this.redraw();
}
};
TimePlayer.prototype.reset_max_value = function () {
this.MAX_VALUE = 0;
this.MAX_VALUE_LOG = 0;
};
/**
* change table where the data is choosen
*/
TimePlayer.prototype.set_table = function (table, size) {
if (this.table === table) {
return; // nothing to do
}
this.table = table;
this.pixel_size = size;
this.recreate();
this.redraw();
};
/**
* private
*/
// get data from cartodb
TimePlayer.prototype.sql = function (sql, callback) {
var self = this;
$.getJSON(this.base_url + "?q=" + encodeURIComponent(sql), function (data) {
callback(data);
});
};
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];
}
// precache data to render fast
TimePlayer.prototype.pre_cache_months = function (rows, coord, zoom) {
var row;
var xcoords;
var ycoords;
var values;
if (typeof(ArrayBuffer) !== undefined) {
xcoords = new Uint8Array(new ArrayBuffer(rows.length));
ycoords = new Uint8Array(new ArrayBuffer(rows.length));
values = new Uint8Array(new ArrayBuffer(rows.length * this.MAX_UNITS));// 256 months
} else {
// fallback
xcoords = [];
ycoords = [];
values = [];
// array buffer set by default to 0
for (var i = 0; i < rows.length * this.MAX_UNITS; ++i) {
values[i] = 0;
}
}
// base tile x, y
var tile_base_x = coord.x * 256;
var tile_base_y = coord.y * 256;
var total_pixels = 256 << zoom;
for (var i in rows) {
row = rows[i];
pixels = meterToPixels(row.x, row.y, zoom);
pixels[1] = total_pixels - pixels[1];
xcoords[i] = pixels[0];
ycoords[i] = pixels[1];
var base_idx = i * this.MAX_UNITS;
//def[row.sd[0]] = row.se[0];
for (var j = 0; j < row.dates.length; ++j) {
values[base_idx + row.dates[j]] = row.vals[j];
if (row.vals[j] > this.MAX_VALUE) {
this.MAX_VALUE = row.vals[j];
this.MAX_VALUE_LOG = Math.log(this.MAX_VALUE);
}
}
;
if (this.options.cumulative) {
for (var j = 1; j < this.MAX_UNITS; ++j) {
values[base_idx + j] += values[base_idx + j - 1];
if (this.options.cumulative_expires) {
for ( var u = 0; u < row.dates_end.length; ++u ) {
if ( row.dates_end[u] != null && row.dates_end[u] < (j+1) ) {
values[base_idx + j - 1 ] -= row.vals[u]
}
}
}
if (values[base_idx + j] > this.MAX_VALUE) {
this.MAX_VALUE = values[base_idx + j];
this.MAX_VALUE_LOG = Math.log(this.MAX_VALUE);
}
}
}
}
return {
length:rows.length,
xcoords:xcoords,
ycoords:ycoords,
values:values,
size:1 << (this.resolution * 2)
};
};
// get time data in json format
TimePlayer.prototype.get_time_data = function (tile, coord, zoom) {
var self = this;
if (!self.table) {
return;
}
// get x, y for cells and sd, se for deforestation changes
// sd contains the months
// se contains the deforestation for each entry in sd
// take se and sd as a matrix [se|sd]
var numTiles = 1 << zoom;
var sql = ""
if ( this.options.cumulative_expires == true) {
sql = "WITH hgrid AS ( " +
" SELECT CDB_RectangleGrid( " +
" CDB_XYZ_Extent({0}, {1}, {2}), ".format(coord.x, coord.y, zoom) +
" CDB_XYZ_Resolution({0}) * {1}, ".format(zoom, this.resolution) +
" CDB_XYZ_Resolution({0}) * {1} ".format(zoom, this.resolution) +
" ) as cell " +
" ) " +
" SELECT " +
" x, y, array_agg(c) vals, array_agg(d) dates , array_agg(de) dates_end" +
" FROM ( " +
" SELECT " +
" round(CAST (st_xmax(hgrid.cell) AS numeric),4) x, " +
" round(CAST (st_ymax(hgrid.cell) AS numeric),4) y, " +
" {0} c, ".format(this.countby) +
" floor((date_part('epoch',{0})- {1})/{2}) d, ".format(this.t_column, this.MIN_DATE, this.step) +
" floor((date_part('epoch',{0})- {1})/{2}) de ".format(this.options.expiration_column, this.MIN_DATE, this.step) +
" FROM " +
" hgrid, {0} i ".format(this.table) +
" WHERE " +
" ST_Intersects(i.the_geom_webmercator, hgrid.cell) " +
" GROUP BY " +
" hgrid.cell, " +
" floor((date_part('epoch',{0})- {1})/{2}), ".format(this.t_column, this.MIN_DATE, this.step) +
" floor((date_part('epoch',{0})- {1})/{2})".format(this.options.expiration_column, this.MIN_DATE, this.step) +
" ) f GROUP BY x, y";
} else {
sql = "WITH hgrid AS ( " +
" SELECT CDB_RectangleGrid( " +
" CDB_XYZ_Extent({0}, {1}, {2}), ".format(coord.x, coord.y, zoom) +
" CDB_XYZ_Resolution({0}) * {1}, ".format(zoom, this.resolution) +
" CDB_XYZ_Resolution({0}) * {1} ".format(zoom, this.resolution) +
" ) as cell " +
" ) " +
" SELECT " +
" x, y, array_agg(c) vals, array_agg(d) dates " +
" FROM ( " +
" SELECT " +
" round(CAST (st_xmax(hgrid.cell) AS numeric),4) x, round(CAST (st_ymax(hgrid.cell) AS numeric),4) y, " +
" {0} c, floor((date_part('epoch',{1})- {2})/{3}) d ".format(this.countby, this.t_column, this.MIN_DATE, this.step) +
" FROM " +
" hgrid, {0} i ".format(this.table) +
" WHERE " +
" ST_Intersects(i.the_geom_webmercator, hgrid.cell) " +
" GROUP BY " +
" hgrid.cell, floor((date_part('epoch',{0})- {1})/{2})".format(this.t_column, this.MIN_DATE, this.step) +
" ) f GROUP BY x, y";
}
var prof = Profiler.get('tile fetch');
prof.start();
this.sql(sql, function (data) {
if (data.rows) {
prof.end();
var p = Profiler.get('tile data cache');
p.start();
tile.cells = self.pre_cache_months(data.rows, coord, zoom);
p.end();
p = Profiler.get('tile render');
p.start();
self.redraw_tile(tile);
p.end();
}
});
};
YO = 1;
TimePlayer.prototype.render_time = function (tile, coord, zoom) {
var self = this;
//var month = -this.BASE_UNIT + 1 + this.time>>0;
//var month = Math.ceil(this.MAX_UNITS * (this.time - this.BASE_UNIT)/(this.CAP_UNIT-this.BASE_UNIT));
var month = this.time;
var w = tile.canvas.width;
var h = tile.canvas.height;
var ctx = tile.ctx;
var i, x, y, cell, cells;
cells = tile.cells;
if (!cells || cells.length === 0) {
return;
}
var colors = [
//"#FFFFE5",
//"#FFF7BC",
"#FEE391",
"#FEC44F",
"#FE9929",
"#EC7014",
"#CC4C02",
"#993404",
"#662506"
];
var fillStyle;
// clear canvas
tile.canvas.width = w;
var ci = 0;
var cu = 0;
ctx.strokeStyle = ctx.fillStyle = colors[cu];
ctx.globalCompositeOperation = this.options.blendmode;
var xc = cells.xcoords;
var yc = cells.ycoords;
var vals = cells.values;
var dz = 256 / Math.pow(2, zoom)
// render cells
var len = cells.length;
var pixel_size = this.resolution//*this.options.cellsize;
var pixel_size_trail_circ = pixel_size * 2;
var pixel_size_trail_squa = pixel_size * 1.5;
var offset = Math.floor((pixel_size - 1) / 2);
var tau = Math.PI * 2;
// memoize sprite canvases
if (self.sprite_1 == undefined) {
self.sprite_1 = [];
$(colors).each(function () {
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
ctx.width = canvas.width = pixel_size * 2;
ctx.height = canvas.height = pixel_size * 2;
ctx.globalAlpha = 1;
ctx.fillStyle = this.toString();
ctx.beginPath();
ctx.arc(pixel_size, pixel_size, pixel_size, 0, tau, true, true);
ctx.closePath();
ctx.fill();
self.sprite_1.push(canvas);
});
}
if (self.sprite_2 == undefined) {
self.sprite_2 = [];
$(colors).each(function () {
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
ctx.width = canvas.width = pixel_size_trail_circ * 2;
ctx.height = canvas.height = pixel_size_trail_circ * 2;
ctx.globalAlpha = 0.3;
ctx.fillStyle = this.toString();
ctx.beginPath();
ctx.arc(pixel_size_trail_circ, pixel_size_trail_circ, pixel_size_trail_circ, 0, tau, true, true);
ctx.closePath();
ctx.fill();
self.sprite_2.push(canvas);
});
}
if (self.sprite_3 == undefined) {
self.sprite_3 = [];
$(colors).each(function () {
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
ctx.width = canvas.width = pixel_size * 2;
ctx.height = canvas.height = pixel_size * 2;
ctx.globalAlpha = 0.3;
ctx.fillStyle = this.toString();
ctx.beginPath();
ctx.arc(pixel_size, pixel_size, pixel_size, 0, tau, true, true);
ctx.closePath();
ctx.fill();
self.sprite_3.push(canvas);
});
}
var numTiles = 1 << zoom;
for (i = 0; i < len; ++i) {
var cell = cells.values[this.MAX_UNITS * i + month];
if (cell) {
ci = cell == 0 ? 0 : Math.floor((colors.length - 1) * (Math.log(cell) / this.MAX_VALUE_LOG));
if (ci != cu) {
cu = ci < colors.length ? ci : cu;
ctx.fillStyle = colors[cu];
}
if (this.options.point_type == 'circle') {
ctx.drawImage(self.sprite_1[cu], xc[i] - pixel_size, yc[i] - pixel_size)
} else if (this.options.point_type == 'square') {
ctx.fillRect(xc[i] - offset, yc[i] - offset, pixel_size, pixel_size);
}
}
if (this.options.trails == true) {
cell = cells.values[this.MAX_UNITS * i + month - 1];
if (cell) {
ci = cell == 0 ? 0 : Math.floor((colors.length - 1) * (Math.log(cell) / this.MAX_VALUE_LOG));
if (ci != cu) {
cu = ci < colors.length ? ci : cu;
ctx.fillStyle = colors[cu];
}
if (this.options.point_type == 'circle') {
//alignment hack - sorry to the gods of graphics
ctx.drawImage(self.sprite_2[cu], xc[i] - pixel_size_trail_squa - 1, yc[i] - pixel_size_trail_squa - 1)
} else if (this.options.point_type == 'square') {
ctx.fillRect(xc[i] - offset, yc[i] - offset, pixel_size_trail_squa, pixel_size_trail_squa);
}
}
cell = cells.values[this.MAX_UNITS * i + month - 2];
if (cell) {
ci = cell == 0 ? 0 : Math.floor((colors.length - 1) * (Math.log(cell) / this.MAX_VALUE_LOG));
if (ci != cu) {
cu = ci < colors.length ? ci : cu;
ctx.fillStyle = colors[cu];
}
if (this.options.point_type == 'circle') {
ctx.drawImage(self.sprite_3[cu], xc[i] - pixel_size, yc[i] - pixel_size)
} else if (this.options.point_type == 'square') {
ctx.fillRect(xc[i] - offset, yc[i] - offset, pixel_size, pixel_size);
}
}
}
}
};
/**
* String formatting for JavaScript.
*
* Usage:
*
* "{0} is {1}".format("CartoDB", "epic!");
* // CartoDB is epic!
*
*/
String.prototype.format = (function (i, safe, arg) {
function format() {
var str = this,
len = arguments.length + 1;
for (i = 0; i < len; arg = arguments[i++]) {
safe = typeof arg === 'object' ? JSON.stringify(arg) : arg;
str = str.replace(RegExp('\\{' + (i - 1) + '\\}', 'g'), safe);
}
return str;
}
//format.native = String.prototype.format;
return format;
})();

31
lib/underscore-min.js vendored
View File

@ -1,31 +0,0 @@
// Underscore.js 1.3.1
// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
// Underscore is freely distributable under the MIT license.
// Portions of Underscore are inspired or borrowed from Prototype,
// Oliver Steele's Functional, and John Resig's Micro-Templating.
// For all details and documentation:
// http://documentcloud.github.com/underscore
(function(){function q(a,c,d){if(a===c)return a!==0||1/a==1/c;if(a==null||c==null)return a===c;if(a._chain)a=a._wrapped;if(c._chain)c=c._wrapped;if(a.isEqual&&b.isFunction(a.isEqual))return a.isEqual(c);if(c.isEqual&&b.isFunction(c.isEqual))return c.isEqual(a);var e=l.call(a);if(e!=l.call(c))return false;switch(e){case "[object String]":return a==String(c);case "[object Number]":return a!=+a?c!=+c:a==0?1/a==1/c:a==+c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source==
c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if(typeof a!="object"||typeof c!="object")return false;for(var f=d.length;f--;)if(d[f]==a)return true;d.push(a);var f=0,g=true;if(e=="[object Array]"){if(f=a.length,g=f==c.length)for(;f--;)if(!(g=f in a==f in c&&q(a[f],c[f],d)))break}else{if("constructor"in a!="constructor"in c||a.constructor!=c.constructor)return false;for(var h in a)if(b.has(a,h)&&(f++,!(g=b.has(c,h)&&q(a[h],c[h],d))))break;if(g){for(h in c)if(b.has(c,
h)&&!f--)break;g=!f}}d.pop();return g}var r=this,G=r._,n={},k=Array.prototype,o=Object.prototype,i=k.slice,H=k.unshift,l=o.toString,I=o.hasOwnProperty,w=k.forEach,x=k.map,y=k.reduce,z=k.reduceRight,A=k.filter,B=k.every,C=k.some,p=k.indexOf,D=k.lastIndexOf,o=Array.isArray,J=Object.keys,s=Function.prototype.bind,b=function(a){return new m(a)};if(typeof exports!=="undefined"){if(typeof module!=="undefined"&&module.exports)exports=module.exports=b;exports._=b}else r._=b;b.VERSION="1.3.1";var j=b.each=
b.forEach=function(a,c,d){if(a!=null)if(w&&a.forEach===w)a.forEach(c,d);else if(a.length===+a.length)for(var e=0,f=a.length;e<f;e++){if(e in a&&c.call(d,a[e],e,a)===n)break}else for(e in a)if(b.has(a,e)&&c.call(d,a[e],e,a)===n)break};b.map=b.collect=function(a,c,b){var e=[];if(a==null)return e;if(x&&a.map===x)return a.map(c,b);j(a,function(a,g,h){e[e.length]=c.call(b,a,g,h)});if(a.length===+a.length)e.length=a.length;return e};b.reduce=b.foldl=b.inject=function(a,c,d,e){var f=arguments.length>2;a==
null&&(a=[]);if(y&&a.reduce===y)return e&&(c=b.bind(c,e)),f?a.reduce(c,d):a.reduce(c);j(a,function(a,b,i){f?d=c.call(e,d,a,b,i):(d=a,f=true)});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(z&&a.reduceRight===z)return e&&(c=b.bind(c,e)),f?a.reduceRight(c,d):a.reduceRight(c);var g=b.toArray(a).reverse();e&&!f&&(c=b.bind(c,e));return f?b.reduce(g,c,d,e):b.reduce(g,c)};b.find=b.detect=
function(a,c,b){var e;E(a,function(a,g,h){if(c.call(b,a,g,h))return e=a,true});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(A&&a.filter===A)return a.filter(c,b);j(a,function(a,g,h){c.call(b,a,g,h)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;j(a,function(a,g,h){c.call(b,a,g,h)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=true;if(a==null)return e;if(B&&a.every===B)return a.every(c,b);j(a,function(a,g,h){if(!(e=
e&&c.call(b,a,g,h)))return n});return e};var E=b.some=b.any=function(a,c,d){c||(c=b.identity);var e=false;if(a==null)return e;if(C&&a.some===C)return a.some(c,d);j(a,function(a,b,h){if(e||(e=c.call(d,a,b,h)))return n});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;return p&&a.indexOf===p?a.indexOf(c)!=-1:b=E(a,function(a){return a===c})};b.invoke=function(a,c){var d=i.call(arguments,2);return b.map(a,function(a){return(b.isFunction(c)?c||a:a[c]).apply(a,d)})};b.pluck=
function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b<e.computed&&(e={value:a,computed:b})});
return e.value};b.shuffle=function(a){var b=[],d;j(a,function(a,f){f==0?b[0]=a:(d=Math.floor(Math.random()*(f+1)),b[f]=b[d],b[d]=a)});return b};b.sortBy=function(a,c,d){return b.pluck(b.map(a,function(a,b,g){return{value:a,criteria:c.call(d,a,b,g)}}).sort(function(a,b){var c=a.criteria,d=b.criteria;return c<d?-1:c>d?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]};j(a,function(a,b){var c=e(a,b);(d[c]||(d[c]=[])).push(a)});return d};b.sortedIndex=function(a,
c,d){d||(d=b.identity);for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?e=g+1:f=g}return e};b.toArray=function(a){return!a?[]:a.toArray?a.toArray():b.isArray(a)?i.call(a):b.isArguments(a)?i.call(a):b.values(a)};b.size=function(a){return b.toArray(a).length};b.first=b.head=function(a,b,d){return b!=null&&!d?i.call(a,0,b):a[0]};b.initial=function(a,b,d){return i.call(a,0,a.length-(b==null||d?1:b))};b.last=function(a,b,d){return b!=null&&!d?i.call(a,Math.max(a.length-b,0)):a[a.length-1]};b.rest=
b.tail=function(a,b,d){return i.call(a,b==null||d?1:b)};b.compact=function(a){return b.filter(a,function(a){return!!a})};b.flatten=function(a,c){return b.reduce(a,function(a,e){if(b.isArray(e))return a.concat(c?e:b.flatten(e));a[a.length]=e;return a},[])};b.without=function(a){return b.difference(a,i.call(arguments,1))};b.uniq=b.unique=function(a,c,d){var d=d?b.map(a,d):a,e=[];b.reduce(d,function(d,g,h){if(0==h||(c===true?b.last(d)!=g:!b.include(d,g)))d[d.length]=g,e[e.length]=a[h];return d},[]);
return e};b.union=function(){return b.uniq(b.flatten(arguments,true))};b.intersection=b.intersect=function(a){var c=i.call(arguments,1);return b.filter(b.uniq(a),function(a){return b.every(c,function(c){return b.indexOf(c,a)>=0})})};b.difference=function(a){var c=b.flatten(i.call(arguments,1));return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e<c;e++)d[e]=b.pluck(a,""+e);return d};b.indexOf=function(a,c,
d){if(a==null)return-1;var e;if(d)return d=b.sortedIndex(a,c),a[d]===c?d:-1;if(p&&a.indexOf===p)return a.indexOf(c);for(d=0,e=a.length;d<e;d++)if(d in a&&a[d]===c)return d;return-1};b.lastIndexOf=function(a,b){if(a==null)return-1;if(D&&a.lastIndexOf===D)return a.lastIndexOf(b);for(var d=a.length;d--;)if(d in a&&a[d]===b)return d;return-1};b.range=function(a,b,d){arguments.length<=1&&(b=a||0,a=0);for(var d=arguments[2]||1,e=Math.max(Math.ceil((b-a)/d),0),f=0,g=Array(e);f<e;)g[f++]=a,a+=d;return g};
var F=function(){};b.bind=function(a,c){var d,e;if(a.bind===s&&s)return s.apply(a,i.call(arguments,1));if(!b.isFunction(a))throw new TypeError;e=i.call(arguments,2);return d=function(){if(!(this instanceof d))return a.apply(c,e.concat(i.call(arguments)));F.prototype=a.prototype;var b=new F,g=a.apply(b,e.concat(i.call(arguments)));return Object(g)===g?g:b}};b.bindAll=function(a){var c=i.call(arguments,1);c.length==0&&(c=b.functions(a));j(c,function(c){a[c]=b.bind(a[c],a)});return a};b.memoize=function(a,
c){var d={};c||(c=b.identity);return function(){var e=c.apply(this,arguments);return b.has(d,e)?d[e]:d[e]=a.apply(this,arguments)}};b.delay=function(a,b){var d=i.call(arguments,2);return setTimeout(function(){return a.apply(a,d)},b)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(i.call(arguments,1)))};b.throttle=function(a,c){var d,e,f,g,h,i=b.debounce(function(){h=g=false},c);return function(){d=this;e=arguments;var b;f||(f=setTimeout(function(){f=null;h&&a.apply(d,e);i()},c));g?h=true:
a.apply(d,e);i();g=true}};b.debounce=function(a,b){var d;return function(){var e=this,f=arguments;clearTimeout(d);d=setTimeout(function(){d=null;a.apply(e,f)},b)}};b.once=function(a){var b=false,d;return function(){if(b)return d;b=true;return d=a.apply(this,arguments)}};b.wrap=function(a,b){return function(){var d=[a].concat(i.call(arguments,0));return b.apply(this,d)}};b.compose=function(){var a=arguments;return function(){for(var b=arguments,d=a.length-1;d>=0;d--)b=[a[d].apply(this,b)];return b[0]}};
b.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=J||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var c=[],d;for(d in a)b.has(a,d)&&(c[c.length]=d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};b.defaults=function(a){j(i.call(arguments,
1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return q(a,b,[])};b.isEmpty=function(a){if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(b.has(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=o||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a===Object(a)};
b.isArguments=function(a){return l.call(a)=="[object Arguments]"};if(!b.isArguments(arguments))b.isArguments=function(a){return!(!a||!b.has(a,"callee"))};b.isFunction=function(a){return l.call(a)=="[object Function]"};b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)=="[object Date]"};
b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.has=function(a,b){return I.call(a,b)};b.noConflict=function(){r._=G;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e<a;e++)b.call(d,e)};b.escape=function(a){return(""+a).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#x27;").replace(/\//g,"&#x2F;")};b.mixin=function(a){j(b.functions(a),
function(c){K(c,b[c]=a[c])})};var L=0;b.uniqueId=function(a){var b=L++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var t=/.^/,u=function(a){return a.replace(/\\\\/g,"\\").replace(/\\'/g,"'")};b.template=function(a,c){var d=b.templateSettings,d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.escape||t,function(a,b){return"',_.escape("+
u(b)+"),'"}).replace(d.interpolate||t,function(a,b){return"',"+u(b)+",'"}).replace(d.evaluate||t,function(a,b){return"');"+u(b).replace(/[\r\n\t]/g," ")+";__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');",e=new Function("obj","_",d);return c?e(c,b):function(a){return e.call(this,a,b)}};b.chain=function(a){return b(a).chain()};var m=function(a){this._wrapped=a};b.prototype=m.prototype;var v=function(a,c){return c?b(a).chain():a},K=function(a,c){m.prototype[a]=
function(){var a=i.call(arguments);H.call(a,this._wrapped);return v(c.apply(b,a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];m.prototype[a]=function(){var d=this._wrapped;b.apply(d,arguments);var e=d.length;(a=="shift"||a=="splice")&&e===0&&delete d[0];return v(d,this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];m.prototype[a]=function(){return v(b.apply(this._wrapped,arguments),this._chain)}});m.prototype.chain=function(){this._chain=
true;return this};m.prototype.value=function(){return this._wrapped}}).call(this);

File diff suppressed because it is too large Load Diff

235
vendor/leaflet.carto.js vendored Normal file
View File

@ -0,0 +1,235 @@
// monkey patch less classes
tree.Value.prototype.toJS = function() {
var v = this.value[0].value[0];
val = v.toString();
if(v.is === "color") {
val = "'" + val + "'";
}
return "_value = " + val + ";";
};
Object.defineProperty(tree.Filterset.prototype, 'toJS', {
enumerable: false,
value: function(env) {
var opMap = {
'=': '==='
};
return _.map(this, function(filter) {
var op = filter.op;
if(op in opMap) {
op = opMap[op];
}
var val = filter.val;
if(filter._val !== undefined) {
val = filter._val.toString(true);
}
var attrs = "data";
return attrs + "." + filter.key + " " + op + " " + val;
}).join(' && ');
}
});
tree.Definition.prototype.toJS = function() {
var shaderAttrs = {};
// merge conditions from filters with zoom condition of the
// definition
var zoom = "(" + this.zoom + " & (1 << ctx.zoom))";
var _if = this.filters.toJS()
if(_if && _if.length > 0) {
_if += " && " + zoom;
} else {
_if = zoom;
}
_.each(this.rules, function(rule) {
if(rule instanceof tree.Rule) {
shaderAttrs[rule.name] = shaderAttrs[rule.name] || [];
if (_if) {
shaderAttrs[rule.name].push(
"if(" + _if + "){" + rule.value.toJS() + "}"
);
} else {
shaderAttrs[rule.name].push(rule.value.toJS());
}
} else {
if (rule instanceof tree.Ruleset) {
var sh = rule.toJS();
for(var v in sh) {
shaderAttrs[v] = shaderAttrs[v] || [];
for(var attr in sh[v]) {
shaderAttrs[v].push(sh[v][attr]);
}
}
}
}
});
return shaderAttrs;
};
function CartoCSS(style) {
if(style) {
this.setStyle(style);
}
}
CartoCSS.Layer = function(shader, options) {
this.options = options;
this.shader = shader;
};
CartoCSS.renderers = {};
CartoCSS.renderers['svg'] = {
maps: {},
transform: function(src) {
var target = {};
for(var i in src) {
var t = this.maps[i];
if(t) {
target[t] = src[i];
} else {
console.log("unknow property: " + i);
}
}
return target;
}
};
(function() {
var renderer = CartoCSS.renderers['svg'];
var ref = window.carto['mapnik-reference'].version.latest;
var s = 'polygon';
for(var i in ref.symbolizers[s]) {
renderer.maps[ref.symbolizers[s][i].css] = i;
}
console.log(renderer.maps);
})();
CartoCSS.Layer.prototype = {
/*
* ``target``: style, 'svg', 'canvas'...
* ``props``: feature properties
* ``context``: rendering properties, i.e zoom
*/
getStyle: function(target, props, context) {
var style = {};
for(var i in this.shader) {
style[i] = this.shader[i](props, context);
}
return CartoCSS.renderers[target].transform(style);
},
/**
* returns true if a feature needs to be rendered
*/
filter: function(featureType, props, context) {
for(var i in this.shader) {
var s = this.shader[i](props, context);
if(s) {
return true;
}
}
return false;
},
transformGeometries: function(geojson) {
return geojson;
}
};
CartoCSS.prototype = {
setStyle: function(style) {
var layers = this.parse(style);
this.layers = layers.map(function(shader) {
return new CartoCSS.Layer(shader);
});
},
getLayers: function() {
return this.layers;
},
_createFn: function(ops) {
var body = ops.join('\n');
return Function("data","ctx", "var _value = null; " + body + "; return _value; ");
},
_compile: function(shader) {
if(typeof shader === 'string') {
shader = eval("(function() { return " + shader +"; })()");
}
this.shader_src = shader;
for(var attr in shader) {
var c = mapper[attr];
if(c) {
this.compiled[c] = eval("(function() { return shader[attr]; })();");
}
}
},
parse: function(cartocss) {
var parse_env = {
frames: [],
errors: [],
error: function(obj) {
this.errors.push(obj);
}
};
var ruleset = null;
try {
ruleset = (new carto.Parser(parse_env)).parse(cartocss);
} catch(e) {
// add the style.mss string to match the response from the server
parse_env.errors.push(e.message);
return;
}
if(ruleset) {
var defs = ruleset.toList(parse_env);
defs.reverse();
// group by elements[0].value::attachment
var layers = {};
for(var i = 0; i < defs.length; ++i) {
var def = defs[i];
var key = def.elements[0] + "::" + def.attachment;
var layer = layers[key] = (layers[key] || {});
var props = def.toJS();
for(var v in props) {
(layer[v] = (layer[v] || [])).push(props[v].join('\n'))
}
}
var ordered_layers = [];
var done = {};
for(var i = 0; i < defs.length; ++i) {
var def = defs[i];
var k = def.elements[0] + "::" + def.attachment;
if(!done[k]) {
var layer = layers[k];
for(var prop in layer) {
layer[prop] = this._createFn(layer[prop]);
}
ordered_layers.push(layer);
done[k] = true;
}
}
return ordered_layers;
}
return null;
}
};

463
vendor/leaflet.css vendored Normal file
View File

@ -0,0 +1,463 @@
/* required styles */
.leaflet-map-pane,
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow,
.leaflet-tile-pane,
.leaflet-tile-container,
.leaflet-overlay-pane,
.leaflet-shadow-pane,
.leaflet-marker-pane,
.leaflet-popup-pane,
.leaflet-overlay-pane svg,
.leaflet-zoom-box,
.leaflet-image-layer,
.leaflet-layer {
position: absolute;
left: 0;
top: 0;
}
.leaflet-container {
overflow: hidden;
-ms-touch-action: none;
}
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow {
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
-webkit-user-drag: none;
}
.leaflet-marker-icon,
.leaflet-marker-shadow {
display: block;
}
/* map is broken in FF if you have max-width: 100% on tiles */
.leaflet-container img {
max-width: none !important;
}
/* stupid Android 2 doesn't understand "max-width: none" properly */
.leaflet-container img.leaflet-image-layer {
max-width: 15000px !important;
}
.leaflet-tile {
filter: inherit;
visibility: hidden;
}
.leaflet-tile-loaded {
visibility: inherit;
}
.leaflet-zoom-box {
width: 0;
height: 0;
}
.leaflet-tile-pane { z-index: 2; }
.leaflet-objects-pane { z-index: 3; }
.leaflet-overlay-pane { z-index: 4; }
.leaflet-shadow-pane { z-index: 5; }
.leaflet-marker-pane { z-index: 6; }
.leaflet-popup-pane { z-index: 7; }
/* control positioning */
.leaflet-control {
position: relative;
z-index: 7;
pointer-events: auto;
}
.leaflet-top,
.leaflet-bottom {
position: absolute;
z-index: 1000;
pointer-events: none;
}
.leaflet-top {
top: 0;
}
.leaflet-right {
right: 0;
}
.leaflet-bottom {
bottom: 0;
}
.leaflet-left {
left: 0;
}
.leaflet-control {
float: left;
clear: both;
}
.leaflet-right .leaflet-control {
float: right;
}
.leaflet-top .leaflet-control {
margin-top: 10px;
}
.leaflet-bottom .leaflet-control {
margin-bottom: 10px;
}
.leaflet-left .leaflet-control {
margin-left: 10px;
}
.leaflet-right .leaflet-control {
margin-right: 10px;
}
/* zoom and fade animations */
.leaflet-fade-anim .leaflet-tile,
.leaflet-fade-anim .leaflet-popup {
opacity: 0;
-webkit-transition: opacity 0.2s linear;
-moz-transition: opacity 0.2s linear;
-o-transition: opacity 0.2s linear;
transition: opacity 0.2s linear;
}
.leaflet-fade-anim .leaflet-tile-loaded,
.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
opacity: 1;
}
.leaflet-zoom-anim .leaflet-zoom-animated {
-webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1);
-moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1);
-o-transition: -o-transform 0.25s cubic-bezier(0,0,0.25,1);
transition: transform 0.25s cubic-bezier(0,0,0.25,1);
}
.leaflet-zoom-anim .leaflet-tile,
.leaflet-pan-anim .leaflet-tile,
.leaflet-touching .leaflet-zoom-animated {
-webkit-transition: none;
-moz-transition: none;
-o-transition: none;
transition: none;
}
.leaflet-zoom-anim .leaflet-zoom-hide {
visibility: hidden;
}
/* cursors */
.leaflet-clickable {
cursor: pointer;
}
.leaflet-container {
cursor: -webkit-grab;
cursor: -moz-grab;
}
.leaflet-popup-pane,
.leaflet-control {
cursor: auto;
}
.leaflet-dragging,
.leaflet-dragging .leaflet-clickable,
.leaflet-dragging .leaflet-container {
cursor: move;
cursor: -webkit-grabbing;
cursor: -moz-grabbing;
}
/* visual tweaks */
.leaflet-container {
background: #ddd;
outline: 0;
}
.leaflet-container a {
color: #0078A8;
}
.leaflet-container a.leaflet-active {
outline: 2px solid orange;
}
.leaflet-zoom-box {
border: 2px dotted #05f;
background: white;
opacity: 0.5;
}
/* general typography */
.leaflet-container {
font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif;
}
/* general toolbar styles */
.leaflet-bar {
box-shadow: 0 1px 7px rgba(0,0,0,0.65);
-webkit-border-radius: 4px;
border-radius: 4px;
}
.leaflet-bar a, .leaflet-bar a:hover {
background-color: #fff;
border-bottom: 1px solid #ccc;
width: 26px;
height: 26px;
line-height: 26px;
display: block;
text-align: center;
text-decoration: none;
color: black;
}
.leaflet-bar a,
.leaflet-control-layers-toggle {
background-position: 50% 50%;
background-repeat: no-repeat;
display: block;
}
.leaflet-bar a:hover {
background-color: #f4f4f4;
}
.leaflet-bar a:first-child {
-webkit-border-top-left-radius: 4px;
border-top-left-radius: 4px;
-webkit-border-top-right-radius: 4px;
border-top-right-radius: 4px;
}
.leaflet-bar a:last-child {
-webkit-border-bottom-left-radius: 4px;
border-bottom-left-radius: 4px;
-webkit-border-bottom-right-radius: 4px;
border-bottom-right-radius: 4px;
border-bottom: none;
}
.leaflet-bar a.leaflet-disabled {
cursor: default;
background-color: #f4f4f4;
color: #bbb;
}
.leaflet-touch .leaflet-bar {
-webkit-border-radius: 10px;
border-radius: 10px;
}
.leaflet-touch .leaflet-bar a {
width: 30px;
height: 30px;
}
.leaflet-touch .leaflet-bar a:first-child {
-webkit-border-top-left-radius: 7px;
border-top-left-radius: 7px;
-webkit-border-top-right-radius: 7px;
border-top-right-radius: 7px;
}
.leaflet-touch .leaflet-bar a:last-child {
-webkit-border-bottom-left-radius: 7px;
border-bottom-left-radius: 7px;
-webkit-border-bottom-right-radius: 7px;
border-bottom-right-radius: 7px;
border-bottom: none;
}
/* zoom control */
.leaflet-control-zoom-in {
font: bold 18px 'Lucida Console', Monaco, monospace;
}
.leaflet-control-zoom-out {
font: bold 22px 'Lucida Console', Monaco, monospace;
}
.leaflet-touch .leaflet-control-zoom-in {
font-size: 22px;
line-height: 30px;
}
.leaflet-touch .leaflet-control-zoom-out {
font-size: 28px;
line-height: 30px;
}
/* layers control */
.leaflet-control-layers {
box-shadow: 0 1px 7px rgba(0,0,0,0.4);
background: #f8f8f9;
-webkit-border-radius: 5px;
border-radius: 5px;
}
.leaflet-control-layers-toggle {
background-image: url(images/layers.png);
width: 36px;
height: 36px;
}
.leaflet-retina .leaflet-control-layers-toggle {
background-image: url(images/layers-2x.png);
background-size: 26px 26px;
}
.leaflet-touch .leaflet-control-layers-toggle {
width: 44px;
height: 44px;
}
.leaflet-control-layers .leaflet-control-layers-list,
.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
display: none;
}
.leaflet-control-layers-expanded .leaflet-control-layers-list {
display: block;
position: relative;
}
.leaflet-control-layers-expanded {
padding: 6px 10px 6px 6px;
color: #333;
background: #fff;
}
.leaflet-control-layers-selector {
margin-top: 2px;
position: relative;
top: 1px;
}
.leaflet-control-layers label {
display: block;
}
.leaflet-control-layers-separator {
height: 0;
border-top: 1px solid #ddd;
margin: 5px -10px 5px -6px;
}
/* attribution and scale controls */
.leaflet-container .leaflet-control-attribution {
background-color: rgba(255, 255, 255, 0.7);
box-shadow: 0 0 5px #bbb;
margin: 0;
}
.leaflet-control-attribution,
.leaflet-control-scale-line {
padding: 0 5px;
color: #333;
}
.leaflet-container .leaflet-control-attribution,
.leaflet-container .leaflet-control-scale {
font-size: 11px;
}
.leaflet-left .leaflet-control-scale {
margin-left: 5px;
}
.leaflet-bottom .leaflet-control-scale {
margin-bottom: 5px;
}
.leaflet-control-scale-line {
border: 2px solid #777;
border-top: none;
color: black;
line-height: 1.1;
padding: 2px 5px 1px;
font-size: 11px;
text-shadow: 1px 1px 1px #fff;
background-color: rgba(255, 255, 255, 0.5);
box-shadow: 0 -1px 5px rgba(0, 0, 0, 0.2);
white-space: nowrap;
overflow: hidden;
}
.leaflet-control-scale-line:not(:first-child) {
border-top: 2px solid #777;
border-bottom: none;
margin-top: -2px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
}
.leaflet-control-scale-line:not(:first-child):not(:last-child) {
border-bottom: 2px solid #777;
}
.leaflet-touch .leaflet-control-attribution,
.leaflet-touch .leaflet-control-layers,
.leaflet-touch .leaflet-control-zoom {
box-shadow: none;
}
.leaflet-touch .leaflet-control-layers,
.leaflet-touch .leaflet-control-zoom {
border: 4px solid rgba(0,0,0,0.3);
}
/* popup */
.leaflet-popup {
position: absolute;
text-align: center;
}
.leaflet-popup-content-wrapper {
padding: 1px;
text-align: left;
-webkit-border-radius: 12px;
border-radius: 12px;
}
.leaflet-popup-content {
margin: 13px 19px;
line-height: 1.4;
}
.leaflet-popup-content p {
margin: 18px 0;
}
.leaflet-popup-tip-container {
margin: 0 auto;
width: 40px;
height: 20px;
position: relative;
overflow: hidden;
}
.leaflet-popup-tip {
width: 17px;
height: 17px;
padding: 1px;
margin: -10px auto 0;
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-ms-transform: rotate(45deg);
-o-transform: rotate(45deg);
transform: rotate(45deg);
}
.leaflet-popup-content-wrapper, .leaflet-popup-tip {
background: white;
box-shadow: 0 3px 14px rgba(0,0,0,0.4);
}
.leaflet-container a.leaflet-popup-close-button {
position: absolute;
top: 0;
right: 0;
padding: 4px 4px 0 0;
text-align: center;
width: 18px;
height: 14px;
font: 16px/14px Tahoma, Verdana, sans-serif;
color: #c3c3c3;
text-decoration: none;
font-weight: bold;
background: transparent;
}
.leaflet-container a.leaflet-popup-close-button:hover {
color: #999;
}
.leaflet-popup-scrolled {
overflow: auto;
border-bottom: 1px solid #ddd;
border-top: 1px solid #ddd;
}
/* div icon */
.leaflet-div-icon {
background: #fff;
border: 1px solid #666;
}
.leaflet-editing-icon {
-webkit-border-radius: 2px;
border-radius: 2px;
}

8342
vendor/leaflet.js vendored Executable file

File diff suppressed because it is too large Load Diff