This commit is contained in:
Andrew W Hill 2012-05-16 16:33:56 -07:00
commit 1d377496e0
35 changed files with 12868 additions and 0 deletions

BIN
data/forma_08-05-12.zip Normal file

Binary file not shown.

53
index.html Normal file
View File

@ -0,0 +1,53 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<title>Layer exploration app for GFW</title>
<link rel="shortcut icon" href="http://cartodb.com/favicon/favicon_32x32.ico" />
<link href="http://code.google.com/apis/maps/documentation/javascript/examples/default.css" rel="stylesheet" type="text/css" />
<link rel="stylesheet" href="lib/cartodb.css">
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false"></script>
<script type="text/javascript" src="lib/wax.g.js"></script>
<script type="text/javascript" src="lib/cartodb-gmapsv3.js"></script>
<script type="text/javascript" src="lib/dat.gui.min.js"></script>
<script type="text/javascript" src="lib/underscore-min.js"></script>
<script type="text/javascript" src="lib/backbone.js"></script>
<script type="text/javascript" src="lib/class.js"></script>
<script type="text/javascript" src="lib/backbone.cartodb.js"></script>
<script type="text/javascript" src="src/wri.js"></script>
<script type="text/javascript">
var gui;
function initialize() {
// initialise the google map
var map = new google.maps.Map(document.getElementById('map_canvas'), {
center: new google.maps.LatLng(0,110),
zoom: 5,
mapTypeId: google.maps.MapTypeId.SATELLITE,
mapTypeControl: false
});
dat.GUI.DEFAULT_WIDTH = 300;
gui = new dat.GUI();
WRI(function(env) {
WRI.app = new env.app.Instance(map, {
user : 'wri-01',
layerTable : 'layerinfo',
logging : true
});
WRI.app.run();
WRI.env = env;
});
}
</script>
</head>
<body onload="initialize()">
<div id="map_canvas"></div>
<a class="cartodb_logo" href="http://www.cartodb.com" target="_blank">CartoDB</a>
</body>
</html>

210
lib/backbone.cartodb.js Normal file
View File

@ -0,0 +1,210 @@
/**
*
* 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/v1/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
};
};

1159
lib/backbone.js Normal file

File diff suppressed because it is too large Load Diff

BIN
lib/blank_tile.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 B

2799
lib/carto.js Normal file

File diff suppressed because it is too large Load Diff

542
lib/cartodb-gmapsv3.js Normal file
View File

@ -0,0 +1,542 @@
/**
* @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/v1/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/v1/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);

25
lib/cartodb.css Normal file
View File

@ -0,0 +1,25 @@
/*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;}

66
lib/class.js Normal file
View File

@ -0,0 +1,66 @@
/* 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 Normal file

File diff suppressed because one or more lines are too long

2945
lib/modestmaps.js Executable file

File diff suppressed because it is too large Load Diff

BIN
lib/sprite.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

31
lib/underscore-min.js vendored Normal file
View File

@ -0,0 +1,31 @@
// 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);

2766
lib/wax.g.js Normal file

File diff suppressed because it is too large Load Diff

53
sql/down_sample.sql Normal file
View File

@ -0,0 +1,53 @@
--Run loop for each zoom 16-7, 15-6 respectively
INSERT INTO forma_data (x,y,date_array,z) (
SELECT x, y, array_agg(undate) as date_array, 15 as z FROM (
SELECT floor(x/2) as x, floor(y/2) as y, unnest(date_array) AS undate FROM forma_data WHERE z=16
) foo GROUP BY x,y
);
INSERT INTO forma_data (x,y,date_array,z) (
SELECT x, y, array_agg(undate) as date_array, 14 as z FROM (
SELECT floor(x/2) as x, floor(y/2) as y, unnest(date_array) AS undate FROM forma_data WHERE z=15
) foo GROUP BY x,y
);
INSERT INTO forma_data (x,y,date_array,z) (
SELECT x, y, array_agg(undate) as date_array, 13 as z FROM (
SELECT floor(x/2) as x, floor(y/2) as y, unnest(date_array) AS undate FROM forma_data WHERE z=14
) foo GROUP BY x,y
);
INSERT INTO forma_data (x,y,date_array,z) (
SELECT x, y, array_agg(undate) as date_array, 12 as z FROM (
SELECT floor(x/2) as x, floor(y/2) as y, unnest(date_array) AS undate FROM forma_data WHERE z=13
) foo GROUP BY x,y
);
INSERT INTO forma_data (x,y,date_array,z) (
SELECT x, y, array_agg(undate) as date_array, 11 as z FROM (
SELECT floor(x/2) as x, floor(y/2) as y, unnest(date_array) AS undate FROM forma_data WHERE z=12
) foo GROUP BY x,y
);
INSERT INTO forma_data (x,y,date_array,z) (
SELECT x, y, array_agg(undate) as date_array, 10 as z FROM (
SELECT floor(x/2) as x, floor(y/2) as y, unnest(date_array) AS undate FROM forma_data WHERE z=11
) foo GROUP BY x,y
);
INSERT INTO forma_data (x,y,date_array,z) (
SELECT x, y, array_agg(undate) as date_array, 9 as z FROM (
SELECT floor(x/2) as x, floor(y/2) as y, unnest(date_array) AS undate FROM forma_data WHERE z=10
) foo GROUP BY x,y
);
INSERT INTO forma_data (x,y,date_array,z) (
SELECT x, y, array_agg(undate) as date_array, 8 as z FROM (
SELECT floor(x/2) as x, floor(y/2) as y, unnest(date_array) AS undate FROM forma_data WHERE z=9
) foo GROUP BY x,y
);
INSERT INTO forma_data (x,y,date_array,z) (
SELECT x, y, array_agg(undate) as date_array, 7 as z FROM (
SELECT floor(x/2) as x, floor(y/2) as y, unnest(date_array) AS undate FROM forma_data WHERE z=8
) foo GROUP BY x,y
);
INSERT INTO forma_data (x,y,date_array,z) (
SELECT x, y, array_agg(undate) as date_array, 6 as z FROM (
SELECT floor(x/2) as x, floor(y/2) as y, unnest(date_array) AS undate FROM forma_data WHERE z=7
) foo GROUP BY x,y
);

72
sql/forma.carto Normal file
View File

@ -0,0 +1,72 @@
#forma_zoom_polys{
line-color:#FFFFFF;
line-width:0;
line-opacity:1;
polygon-opacity:0;
[zoom>=10][z=16]{
line-width:0.1;
polygon-opacity:0.8;
}
[zoom=9][z=15]{
line-width:0.1;
polygon-opacity:0.8;
}
[zoom=8][z=14]{
line-width:0.1;
polygon-opacity:0.8;
}
[zoom=7][z=13]{
line-width:0.1;
polygon-opacity:0.8;
}
[zoom=6][z=12]{
line-width:0.1;
polygon-opacity:0.8;
}
[zoom=5][z=11]{
line-width:0.1;
polygon-opacity:0.8;
}
[zoom=4][z=10]{
line-width:0.1;
polygon-opacity:0.8;
}
[zoom=3][z=9]{
line-width:0.1;
polygon-opacity:0.8;
}
[zoom=2][z=8]{
line-width:0.1;
polygon-opacity:0.8;
}
[zoom=1][z=7]{
line-width:0.1;
polygon-opacity:0.8;
}
[zoom=0][z=6]{
line-width:0.1;
polygon-opacity:0.8;
}
}
#forma_zoom_polys[alerts<=54860] {
polygon-fill:#B10026
}
#forma_zoom_polys[alerts<=5000] {
polygon-fill:#E31A1C
}
#forma_zoom_polys[alerts<=500] {
polygon-fill:#FC4E2A
}
#forma_zoom_polys[alerts<=50] {
polygon-fill:#FD8D3C
}
#forma_zoom_polys[alerts<=5] {
polygon-fill:#FEB24C
}
#forma_zoom_polys[alerts<=2] {
polygon-fill:#FED976
}
#forma_zoom_polys[alerts<=1] {
polygon-fill:#FFFFB2
}

72
sql/forma2.carto Normal file
View File

@ -0,0 +1,72 @@
#forma_zoom_polys{
line-color:#FFFFFF;
line-width:0;
line-opacity:1;
polygon-opacity:0;
[zoom>=10][z=16]{
line-width:0.1;
polygon-opacity:0.8;
}
[zoom=9][z=15]{
line-width:0.1;
polygon-opacity:0.8;
}
[zoom=8][z=14]{
line-width:0.1;
polygon-opacity:0.8;
}
[zoom=7][z=13]{
line-width:0.1;
polygon-opacity:0.8;
}
[zoom=6][z=12]{
line-width:0.1;
polygon-opacity:0.8;
}
[zoom=5][z=11]{
line-width:0.1;
polygon-opacity:0.8;
}
[zoom=4][z=10]{
line-width:0.1;
polygon-opacity:0.8;
}
[zoom=3][z=9]{
line-width:0.1;
polygon-opacity:0.8;
}
[zoom=2][z=8]{
line-width:0.1;
polygon-opacity:0.8;
}
[zoom=1][z=7]{
line-width:0.1;
polygon-opacity:0.8;
}
[zoom=0][z=6]{
line-width:0.1;
polygon-opacity:0.8;
}
}
#forma_zoom_polys[alerts>alerts] {
polygon-fill:#662506
}
#forma_zoom_polys[alerts<=5000] {
polygon-fill:#993404
}
#forma_zoom_polys[alerts<=500] {
polygon-fill:#EC7014
}
#forma_zoom_polys[alerts<=50] {
polygon-fill:#FE9929
}
#forma_zoom_polys[alerts<=5] {
polygon-fill:#FEC44F
}
#forma_zoom_polys[alerts<=2] {
polygon-fill:#FEE391
}
#forma_zoom_polys[alerts<=1] {
polygon-fill:#FFF7BC
}

1
sql/junk.sql Normal file
View File

@ -0,0 +1 @@
select cartodb_id,ST_Intersection(CASE WHEN ST_Dimension(ST_Snap(ST_CollectionExtract(ST_SnapToGrid(ST_CollectionExtract(ST_Simplify("the_geom", 0.0006866455078125), ST_Dimension( "the_geom") + 1 ), 0.001373291015625), ST_Dimension( "the_geom") + 1 ), ST_SnapToGrid(ST_Expand(ST_MakeEnvelope(0.3515625,51.17934297928927,0.703125,51.39920565355378, 4326), 0.00274658203125),0.001373291015625), 0.001373291015625)) = 0 OR GeometryType(ST_Snap(ST_CollectionExtract(ST_SnapToGrid(ST_CollectionExtract(ST_Simplify("the_geom", 0.0006866455078125), ST_Dimension( "the_geom") + 1 ), 0.001373291015625), ST_Dimension( "the_geom") + 1 ), ST_SnapToGrid(ST_Expand(ST_MakeEnvelope(0.3515625,51.17934297928927,0.703125,51.39920565355378, 4326), 0.00274658203125),0.001373291015625), 0.001373291015625)) = 'GEOMETRYCOLLECTION' THEN ST_Snap(ST_CollectionExtract(ST_SnapToGrid(ST_CollectionExtract(ST_Simplify("the_geom", 0.0006866455078125), ST_Dimension( "the_geom") + 1 ), 0.001373291015625), ST_Dimension( "the_geom") + 1 ), ST_SnapToGrid(ST_Expand(ST_MakeEnvelope(0.3515625,51.17934297928927,0.703125,51.39920565355378, 4326), 0.00274658203125),0.001373291015625), 0.001373291015625) ELSE ST_CollectionExtract(ST_MakeValid(ST_Snap(ST_CollectionExtract(ST_SnapToGrid(ST_CollectionExtract(ST_Simplify("the_geom", 0.0006866455078125), ST_Dimension( "the_geom") + 1 ), 0.001373291015625), ST_Dimension( "the_geom") + 1 ), ST_SnapToGrid(ST_Expand(ST_MakeEnvelope(0.3515625,51.17934297928927,0.703125,51.39920565355378, 4326), 0.00274658203125),0.001373291015625), 0.001373291015625)), ST_Dimension("the_geom") + 1 ) END, ST_SnapToGrid(ST_Expand(ST_MakeEnvelope(0.3515625,51.17934297928927,0.703125,51.39920565355378, 4326), 0.00274658203125),0.001373291015625)) as the_geom,alerts from forma_zoom_polys WHERE the_geom && ST_MakeEnvelope(0.3515625,51.17934297928927,0.703125,51.39920565355378, 4326)&format=geojson&dp=6

40
sql/pixels_to_polys.sql Normal file
View File

@ -0,0 +1,40 @@
ogr2ogr -f "PostgreSQL" PG:"user=cartodb_user_2 dbname=cartodb_user_2_db" forma_120120424-24445-iu0g79.csv -nln forma_months
53182,32275,16,
--Run once to merge Robins data where he has multiple events for the same polygon
INSERT INTO forma_data (x,y,date_array,z) (
SELECT x, y, array_agg(undate) as date_array, 16 as z FROM (
SELECT x, y, period AS undate FROM forma_input WHERE z=16
) foo GROUP BY x,y
)
## SEE DOWNSAMPLE
--Run loop for each zoom 16-7, 15-6 respectively
INSERT INTO forma_data (x,y,date_array,z) (
SELECT x, y, array_agg(undate) as date_array, 15 as z FROM (
SELECT floor(x/2) as x, floor(y/2) as y, unnest(date_array) AS undate FROM forma_data WHERE z=16
) foo GROUP BY x,y
)
--update the_geom for points
SET statement_timeout TO 0; UPDATE forma_data SET the_geom = ST_Transform(ST_SetSRId(ST_Point( ((x*256) * (156543.03392804062 / (2^z)) - 20037508.342789244), -(((y)*256) * (156543.03392804062 / (2^z)) - 20037508.342789244)), 3857), 4326) WHERE the_geom IS NULL;
--create polygons
SET statement_timeout TO 0; DELETE FROM forma_zoom_polys; INSERT INTO forma_zoom_polys (the_geom,z,alerts) (SELECT st_multi(st_transform(ST_Envelope(ST_SetSRId(ST_Collect( ST_Point( ((x*256.0) * (156543.03392804062 / (2^z)) - 20037508.342789244), -(((y)*256.0) * (156543.03392804062 / (2^z)) - 20037508.342789244)), ST_Point( (((x+1.0)*256.0) * (156543.03392804062 / (2^z)) - 20037508.342789244), -(((y-1.0)*256.0) * (156543.03392804062 / (2^z)) - 20037508.342789244)) ), 3857)),4326)) as the_geom,z,array_length(date_array,1) FROM forma_data);
--create arrays of distinct dates
INSERT INTO forma_data (x,y,date_array,z) (
SELECT x, y, array_agg(undate) as date_array, 15 as z FROM (
SELECT floor(x/2) as x, floor(y/2) as y, unnest(date_array) AS undate FROM forma_data WHERE z=16
) foo GROUP BY x,y,undate
)
--create arrays of all dates
INSERT INTO forma_data (x,y,date_array,z) (
SELECT x, y, array_agg(undate) as date_array, 13 as z FROM (
SELECT floor(x/2) as x, floor(y/2) as y, unnest(date_array) AS undate FROM forma_data WHERE z=14
) foo GROUP BY x,y
)
[zoom>=10][z=16]{marker-width:1;}
[zoom=9][z=15]{marker-width:1;}

128
src/mapdig.js Normal file
View File

@ -0,0 +1,128 @@
var MAPDIG = {};
MAPDIG.create = function(carto_user, table_name, ignore, default_width) {
var me = {
generated_sql: 'SELECT * FROM ' + table_name,
SQL: 'SELECT * FROM ' + table_name,
table: table_name,
carto_user: carto_user,
ignore: ['cartodb_id', 'the_geom', 'the_geom_webmercator', 'updated_at', 'created_at'].concat(ignore),
numbers: {},
strings: {},
origin_numbers: {},
default_width: default_width
};
me.toSQL = function(){
var sql = this.SQL;
var filter_sql = [];
_.each(this.numbers, function(val,key){
if (this.origin_numbers[key] != val){
var root = key.split(' ')[0]
var cap = key.split(' ')[1]
if (cap == 'min'){
filter_sql.push(root + '>=' + val);
} else {
filter_sql.push(root + '<=' + val)
}
}
}, this);
_.each(this.strings, function(val,key){
if (val != '*'){
filter_sql.push(key + ' LIKE \'%25' + val + '%25\'');
}
}, this);
return ((filter_sql.length < 1) ? sql : sql + ' WHERE ' + filter_sql.join(' AND '));
};
me.init = function(cartodb_layer){
var c, n1, s1, f1, tmp_dat; // holders for event handlers and reflected columns
var that = this;
that.layer = cartodb_layer;
dat.GUI.DEFAULT_WIDTH = that.default_width;
that.gui = new dat.GUI();
// Direct SQL input
c = that.gui.add(that, 'generated_sql').name('Map SQL');
c.onFinishChange(function(value) { that.executeSQL(value); });
// Attempt to reflect table structure and attach controls and events for all columns found
// Currently does this long hand as cannot access schema. dumb, but works
$.getJSON('http://'+that.carto_user+'.cartodb.com/api/v2/sql/?q=select%20*%20from%20' + that.table +'%20limit%201', function(data){
// grab the top value in each col, strip ignores
var cols = [];
_.each(data.rows[0],function(val,key){
if (!_.include(that.ignore, key)){
cols.push('max("'+key+'") as ' + key);
}
});
// sample a value from each column to determine GUI element type
$.getJSON('http://'+that.carto_user+'.cartodb.com/api/v2/sql/?q=select%20'+cols.join(',') +'%20from%20' + that.table +'%20limit%201', function(data){
n1 = that.gui.addFolder('Numeric filter');
s1 = that.gui.addFolder('Text filter');
_.each(data.rows[0], function(val,key){
if(_.isNumber(val)){
$.getJSON('http://'+that.carto_user+'.cartodb.com/api/v2/sql/?q=select min("'+key+'"), max("'+key+'") from ' + that.table, function(data){
if (data.rows[0].max > data.rows[0].min){
//f1 = n1.addFolder(key);
that.numbers[key+' min'] = data.rows[0].min;
that.numbers[key+' max'] = data.rows[0].max;
that.origin_numbers[key+' min'] = data.rows[0].min;
that.origin_numbers[key+' max'] = data.rows[0].max;
c = n1.add(that.numbers, key+' min', data.rows[0].min, data.rows[0].max);
c.onFinishChange(function(value) { that.renderSQL(); });
c = n1.add(that.numbers, key+' max', data.rows[0].min, data.rows[0].max);
c.onFinishChange(function(value) { that.renderSQL(); });
}
});
}
// test if it's massive - if so, text box, else dropdown.
if(_.isString(val)){
$.getJSON('http://'+that.carto_user+'.cartodb.com/api/v2/sql/?q=select count(distinct("'+key+'")) from ' + that.table, function(data){
if (data.rows[0].count <= 1000 && data.rows[0].count > 0){
$.getJSON('http://'+that.carto_user+'.cartodb.com/api/v2/sql/?q=select distinct("'+key+'") as ele from ' + that.table + ' ORDER BY ELE ASC', function(data){
tmp_dat = _.map(data.rows, function(r){ return r.ele; });
tmp_dat.unshift('*');
that.strings[key] = '*';
c = s1.add(that.strings, key, tmp_dat );
c.onFinishChange(function(value) { that.renderSQL(); });
});
} else {
that.strings[key] = '*';
c = s1.add(that.strings,key);
c.onFinishChange(function(value) { that.renderSQL(); });
}
});
}
});
});
});
// disabled as buggy
//this.gui.remember(this);
};
me.executeSQL = function(sql){
this.layer.update(sql);
};
me.renderSQL = function(){
this.generated_sql = this.toSQL();
for (var i in this.gui.__controllers) {
this.gui.__controllers[i].updateDisplay();
}
this.layer.update(this.generated_sql);
};
return me;
};

376
src/wri.js Normal file
View File

@ -0,0 +1,376 @@
function WRI() {
var args = Array.prototype.slice.call(arguments),
callback = args.pop(),
modules = (args[0] && typeof args[0] === "string") ? args : args[0],
config,
i;
if (!(this instanceof WRI)) {
return new WRI(modules, callback);
}
if (!modules || modules === '*') {
modules = [];
for (i in WRI.modules) {
if (WRI.modules.hasOwnProperty(i)) {
modules.push(i);
}
}
}
for (i = 0; i < modules.length; i += 1) {
WRI.modules[modules[i]](this);
}
callback(this);
return this;
};
WRI.modules = {};
WRI.modules.app = function(wri) {
wri.app = {};
wri.app.Instance = Class.extend(
{
init: function(map, options) {
this.options = _.defaults(options, {
user : 'wri-01',
layerTable : 'layerinfo',
});
this._precision = 2;
wri.log.enabled = options ? options.logging: false;
this._map = map;
this._map.overlayMapTypes.push(null);
this.lastHash = null;
this._cartodb = Backbone.CartoDB({user: this.options.user});
this.datalayers = new wri.datalayers.Engine(this._cartodb, options.layerTable, this._map);
this._setHash();
},
run: function() {
this._setupListeners();
this.update();
wri.log.info('App is now running!');
},
_setHash: function(){
if (location.hash.split("/").length != 3){
var hash = "#5/0/110"
console.log("HASH RESET");
console.log(location.hash);
location.replace(hash);
this.lastHash = hash;
}
},
_setupListeners: function(){
var that = this;
//setup zoom listener
google.maps.event.addListener(this._map, 'zoom_changed', function() {
var hash = "#" + this.getZoom() + "/" + this.getCenter().lat().toFixed(that._precision) +"/" + this.getCenter().lng().toFixed(that._precision);
if (that.lastHash != hash) {
location.replace(hash);
that.lastHash = hash;
}
});
google.maps.event.addListener(this._map, 'center_changed', function() {
var hash = "#" + this.getZoom() + "/" + this.getCenter().lat().toFixed(that._precision) +"/" + this.getCenter().lng().toFixed(that._precision);
if (that.lastHash != hash) {
location.replace(hash);
that.lastHash = hash;
}
});
},
parseHash: function(hash) {
var args = hash.split("/");
if (args.length == 3) {
var zoom = parseInt(args[0], 10),
lat = parseFloat(args[1]),
lon = parseFloat(args[2]);
if (isNaN(zoom) || isNaN(lat) || isNaN(lon)) {
return false;
} else {
return {
center: new google.maps.LatLng(lat, lon),
zoom: zoom
}
}
} else {
return false;
}
},
update: function() {
var hash = location.hash;
if (hash === this.lastHash) {
// console.info("(no change)");
return;
}
var sansHash = hash.substr(1),
parsed = this.parseHash(sansHash);
if (parsed) {
this._map.setZoom(parsed.zoom);
this._map.setCenter(parsed.center);
}
}
}
);
};
WRI.modules.maplayer = function(wri) {
wri.maplayer = {};
wri.maplayer.Engine = Class.extend(
{
init: function(layer, map) {
this.layer = layer;
this._map = map;
this._bindDisplay(new wri.maplayer.Display());
this._options = this._display.getOptions(this.layer.get('tileurl'), this.layer.get('ispng'));
// this._boundingbox = this.layer.get('the_geom');
var sw = new google.maps.LatLng(this.layer.get('ymin'), this.layer.get('xmin'));
var ne = new google.maps.LatLng(this.layer.get('ymax'),this.layer.get('xmax'));
this._bounds = new google.maps.LatLngBounds(sw, ne);
wri.log.info(this._options.getTileUrl({x: 3, y: 4},3));
this._displayed = false;
this._addControll();
this._maptype = new google.maps.ImageMapType(this._options);
this._tileindex = this._map.overlayMapTypes.length;
this._map.overlayMapTypes.setAt(this._tileindex, null);
this._setupListeners();
this._handleLayer();
},
_setupListeners: function(){
var that = this;
//setup zoom listener
google.maps.event.addListener(this._map, 'zoom_changed', function() {
that._inZoom(true);
that._handleLayer();
});
google.maps.event.addListener(this._map, 'center_changed', function() {
that._inBounds(true);
that._handleLayer();
});
this._inZoom(true);
this._inBounds(true);
},
_inZoom: function(reset){
if (this._inZoomVal==null){
this._inZoomVal = true;
}
if(reset){
if (this.layer.get('zmin')<=this._map.getZoom() && this._map.getZoom()<=this.layer.get('zmax')) {
this._inZoomVal = true;
} else {
this._inZoomVal = false;
}
}
return this._inZoomVal;
},
_inBounds: function(reset){
if (this._inBoundsVal==null){
this._inBoundsVal = true;
}
if(reset){
var bounds = this._map.getBounds();
var ne = bounds.getNorthEast();
var sw = bounds.getSouthWest();
if (this._bounds.intersects(bounds)){
this._inBoundsVal = true;
} else {
this._inBoundsVal = false;
}
}
return this._inBoundsVal;
},
_inView: function(){
if (this._inZoom(false) && this._inBounds(false)) {
return true;
} else {
return false
}
},
_handleLayer: function(){
if(this.layer.get('visible') && !this._displayed && this._inView()){
this._displayed = true;
this._map.overlayMapTypes.setAt(this._tileindex, this._maptype);
wri.log.info(this.layer.get('title')+ " added at "+this._tileindex)
} else if (this._displayed && !this._inView()){
this._displayed = false;
this._map.overlayMapTypes.setAt(this._tileindex, null);
wri.log.info(this.layer.get('title')+ " removed at "+this._tileindex)
}
},
_addControll: function(){
var that = this;
this._opacity = {alpha: 100};
this.toggle = gui.addFolder(this.layer.get('title'));;
this.toggle
.add(this.layer.attributes, 'visible')
.onChange(function(value) {
wri.log.info(value);
that._toggleLayer();
});
this.toggle
.add(this._opacity,'alpha').min(0).max(100).step(5)
.name('transparency')
.onChange(function(value) {
that._maptype.setOpacity(value/100);
});
var zoomTo = function(){
var self = that;
this.zoomExtents = function(){
self._map.fitBounds(self._bounds);
}
}
this.toggle
.add(new zoomTo(), 'zoomExtents')
},
_bindDisplay: function(display) {
var that = this;
this._display = display;
display.setEngine(this);
},
_toggleLayer: function(){
var that = this;
if (this.layer.get('visible') == false){
wri.log.info('LAYER OFF');
this._map.overlayMapTypes.setAt(this._tileindex, null);
//this._map.overlayMapTypes.setAt(this._tileindex, null);
} else {
wri.log.info('LAYER ON');
if(this._inView()){
this._displayed = true;
this._map.overlayMapTypes.setAt(this._tileindex, this._maptype);
}
}
}
});
wri.maplayer.Display = Class.extend(
{
/**
* Constructs a new Display with the given DOM element.
*/
init: function() {
wri.log.info('displayed');
},
/**
* Sets the engine for this display.
*
* @param engine a mol.ui.Engine subclass
*/
setEngine: function(engine) {
this._engine = engine;
},
getTileUrl: function(tile, zoom) {
var that = this;
var url = that.tileurl.replace(RegExp('\\{Z}', 'g'), zoom);
url = url.replace(RegExp('\\{X}', 'g'), tile.x);
url = url.replace(RegExp('\\{Y}', 'g'), tile.y);
return url;
},
getOptions: function(tileurl, ispng){
var that = this;
var options = {
alt: "MapServer Layer",
getTileUrl: this.getTileUrl,
tileurl: tileurl,
isPng: ispng,
maxZoom: 17,
minZoom: 1,
name: "MapServer Layer",
tileSize: new google.maps.Size(256, 256)
};
return options;
}
}
);
}
WRI.modules.datalayers = function(wri) {
wri.datalayers = {};
wri.datalayers.Engine = Class.extend(
{
init: function(CartoDB, layerTable, map) {
this._map = map;
this._bycartodbid = {};
this._bytitle = {};
this._dataarray = [];
this._cartodb = CartoDB;
var LayersColl = this._cartodb.CartoDBCollection.extend({
sql: function(){
return "SELECT title, zmin, zmax, ST_XMAX(the_geom) as xmax,ST_XMIN(the_geom) as xmin,ST_YMAX(the_geom) as ymax,ST_YMIN(the_geom) as ymin, tileurl, true as visible FROM " + layerTable + " WHERE display = True ORDER BY displaylayer ASC"
}
});
this.LayersObj = new LayersColl();
this.LayersObj.fetch();
this._loadLayers();
},
_loadLayers: function(){
var that = this;
this.LayersObj.bind('reset', function() {
that.LayersObj.each(function(p){that._addLayer(p)});
});
},
_addLayer: function(p){
wri.log.warn('only showing baselayers for now');
//if (p.get('category')=='baselayer'){
var layer = new wri.maplayer.Engine(p, this._map);
this._dataarray.push(layer);
this._bycartodbid[p.get('cartodb_id')] = layer;
this._bytitle[p.get('title')] = layer;
//}
}
});
};
/**
* Logging module that writes log messages to the console and to the Speed
* Tracer API. It contains convenience methods for info(), warn(), error(),
* and todo().
*
*/
WRI.modules.log = function(wri) {
wri.log = {};
wri.log.info = function(msg) {
wri.log._write('INFO: ' + msg);
};
wri.log.warn = function(msg) {
wri.log._write('WARN: ' + msg);
};
wri.log.error = function(msg) {
wri.log._write('ERROR: ' + msg);
};
wri.log.todo = function(msg) {
wri.log._write('TODO: '+ msg);
};
wri.log._write = function(msg) {
var logger = window.console;
if (wri.log.enabled) {
if (logger && logger.markTimeline) {
logger.markTimeline(msg);
}
console.log(msg);
}
};
};

174
styler/index.html Normal file
View File

@ -0,0 +1,174 @@
<!DOCTYPE html>
<html>
<head>
<title>Styling tool for GFW</title>
<script type="text/javascript" src="src/core.js"></script>
<script type="text/javascript" src="src/settings.js"></script>
<script type="text/javascript" src="src/mercator.js"></script>
<script type="text/javascript" src="src/geometry.js"></script>
<script type="text/javascript" src="src/model.js"></script>
<script type="text/javascript" src="src/renderer.js"></script>
<script type="text/javascript" src="src/shader.js"></script>
<script type="text/javascript" src="src/cartodb.sql.js"></script>
<script type="text/javascript" src="src/cartodb.provider.js"></script>
<!-- carto -->
<script src='../lib/underscore-min.js' type='text/javascript'></script>
<script src='../lib/carto.js' type='text/javascript'></script>
<script type="text/javascript" src="../lib/modestmaps.js"></script>
<script type="text/javascript" src="src/vecnik.modestmaps.js"></script>
<script type="text/javascript" src="src/carto.js"></script>
<script type="text/javascript">
var map;
function initMap() {
VECNIK.Carto.init(function(carto) {
VECNIK.Carto.compile(
"#world { line-width: 2; line-color: #f00; [TYPEY='test']{ line-width: 2; } [ZOOM = 0]{ line-width: 2; } }"
, function() {});
});
var template = 'http://b.tiles.mapbox.com/v3/mapbox.mapbox-streets/{Z}/{X}/{Y}.png';
// template = 'http://b.tiles.mapbox.com/v3/mapbox.mapbox-light/{Z}/{X}/{Y}.png64';
// template = 'http://b.tile.stamen.com/terrain-background/{Z}/{X}/{Y}.jpg';
template = "http://oatile1.mqcdn.com/tiles/1.0.0/sat/{Z}/{X}/{Y}.jpg"
var subdomains = [ 'oatile1', 'oatile2.', 'oatile3.', 'oatile4.' ];
var provider = new MM.TemplatedLayer(template, subdomains);
var dataSource = new VECNIK.CartoDB.API({
user: 'wri-01',
table: 'forma_zoom_polys',
columns: ['alerts'], //do not include the_geom or cartodb_id, are implicit
ENABLE_CLIPPING: true,
ENABLE_SIMPLIFY: true,
ENABLE_FIXING: true,
ENABLE_SNAPPING: true,
debug: true
});
var shader = new VECNIK.CartoShader({
'point-color': '#fff',
'line-color': '#fff',
'line-width': function(data) {
if(data.type === 'primary') {
return '3';
}
return '1';
},
'polygon-fill': function(data) {
return "rgba(200, 200, 200, 0.8)";
}
});
var vector_layer = new VECNIK.MM.CanvasProvider(dataSource, shader);
fg = new MM.Layer(vector_layer);
map = new MM.Map(document.getElementById('map'), [provider, fg])
if(!location.hash) {
map.setCenterZoom(new MM.Location(3.7, 102.8), 4);
}
var hash = new MM.Hash(map);
var code = document.getElementById('code')
var codeOld= '';
var compileShader = _.debounce(function() {
VECNIK.Carto.compile(code.value , function(shaderData) {
if(shaderData) {
//console.log(code.value);
shader.compile(shaderData);
}
});
}, 200);
code.onkeyup = function() {
if(code.value != codeOld) {
compileShader();
codeOld = code.value;
}
}
compileShader(code.value);
}
</script>
<style>
html, body, #map {
width: 100%; height: 100%;
padding: 0;
margin: 0;
}
#livecode {
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 450px;
padding-left: 10px;
box-shadow: 0px 0px 5px 6px #ccc;
}
textarea {
color: rgba(0,0,0,0.9);
background-color:rgba(255,255,255,0.92);
position: absolute;
left: 10px;
top: 0;
bottom: 0;
padding: 0;
margin: 0;
border: none;
font-family: monospace;
font-size: 14px;
width: 350px;
border: 0;
outline: none;
}
/* code */
.highlight {
font-family: monospace;
font-size: 19px;
}
</style>
</head>
<body onload="initMap()">
<div id="map"></div>
<!--<div id="livecode" class="highlight">-->
</div>
<textarea id="code">
#forma_zoom_polys{
line-color:#FFFFFF;
line-width:0;
line-opacity:1;
line-width:0.1;
polygon-opacity:0.9;
polygon-fill:#B10026
[alerts<=5000] {
polygon-fill:#E31A1C
}
[alerts<=500] {
polygon-fill:#FC4E2A
}
[alerts<=50] {
polygon-fill:#FD8D3C
}
[alerts<=5] {
polygon-fill:#FEB24C
}
[alerts<=2] {
polygon-fill:#FED976
}
[alerts<=1] {
polygon-fill:#FFFFB2
}
}
</textarea>
</body>
</html>

132
styler/src/carto.js Normal file
View File

@ -0,0 +1,132 @@
//========================================
// Carto stylesheets support
//
// this is basically a hack on top of branch browser of carto
// repository: Compiles carto to javascript shader
//========================================
(function(VECNIK) {
// 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 + ";"
}
tree.Selector.prototype.toJS = function() {
var self = this;
var opMap = {
'=': '==='
};
var zoom = "(" + self.zoom + " & (1 << ctx.zoom))";
return [zoom].concat(
_.map(this.filters, 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.Ruleset.prototype.toJS = function() {
var shaderAttrs = {};
var _if = this.selectors[0].toJS();
_.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 createFn(ops) {
var body = ops.join('\n');
return Function("data","ctx", "var _value = null; " + body + "; return _value; ");
}
function toCartoShader(ruleset) {
var shaderAttrs = {};
shaderAttrs = ruleset.rules[0].toJS();
try {
for(var attr in shaderAttrs) {
shaderAttrs[attr] = createFn(shaderAttrs[attr]);
}
}
catch(e) {
console.log("error creating shader");
console.log(e);
return null;
}
return shaderAttrs;
}
/**
* compile from Carto style to javascript shader
*/
var compile = function(style, callback) {
var parse_env = {
error: function(obj) {
console.log("ERROR");
}
};
var parser = new carto.Parser(parse_env);
parser.parse(style, function(err, ruleset) {
if(!err) {
var shader = toCartoShader(ruleset);
callback(shader);
} else {
callback(null);
}
});
}
var init = function(callback) {
carto_initialize(carto, './reference.json', function(carto) {
VECNIK.Carto._carto = carto;
if(callback) callback(carto);
});
}
VECNIK.Carto = {
init: init,
compile: compile
};
})(VECNIK);
if (typeof module !== 'undefined' && module.exports) {
}

View File

@ -0,0 +1,49 @@
//========================================
// CartoDB data provider
//========================================
(function(VECNIK) {
function CartoDBSQLAPI(opts) {
this.projection = new VECNIK.MercatorProjection();
this.opts = opts;
this.base_url = 'http://' + opts.user + ".cartodb.com/api/v2/sql";
//set defaults
this.opts.ENABLE_SIMPLIFY = VECNIK.settings.get('ENABLE_SIMPLIFY');
this.opts.ENABLE_SNAPPING = VECNIK.settings.get('ENABLE_SNAPPING');
this.opts.ENABLE_CLIPPING = VECNIK.settings.get('ENABLE_CLIPPING');
this.opts.ENABLE_FIXING = VECNIK.settings.get('ENABLE_FIXING');
}
CartoDBSQLAPI.prototype.debug = function(w) {
if(this.opts.debug) {
//console.log(w);
}
}
CartoDBSQLAPI.prototype._sql_url = function(sql) {
var self = this;
this.debug(sql);
return this.base_url + "?q=" + encodeURIComponent(sql) + "&format=geojson&dp=6";
}
CartoDBSQLAPI.prototype.get_tile_data_sql = function(projection, table, x, y, zoom) {
return VECNIK.CartoDB.SQL(projection, table, x, y, zoom, this.opts);
};
CartoDBSQLAPI.prototype.url = function(coordinates) {
var projection = this.projection;
var opts = this.opts;
var table = opts.table;
var prj = this.projection;
var sql = this.get_tile_data_sql(prj, table, coordinates.column, coordinates.row, coordinates.zoom);
var sql_url = this._sql_url(sql);
return sql_url;
}
VECNIK.CartoDB = VECNIK.CartoDB || {};
VECNIK.CartoDB.API = CartoDBSQLAPI;
})(VECNIK);

135
styler/src/cartodb.sql.js Normal file
View File

@ -0,0 +1,135 @@
//========================================
// sql generator for cartodb
//========================================
var VECNIK = VECNIK || {};
(function(VECNIK) {
var sql = function(projection, table, x, y, zoom, opts) {
opts = opts || {
ENABLE_CLIPPING: false,
ENABLE_SIMPLIFY: false,
ENABLE_FIXING: false,
ENABLE_SNAPPING: false
};
var bbox = projection.tileBBox(x, y, zoom);
var geom_column = '"the_geom"';
var geom_column_orig = '"the_geom"';
var id_column = 'cartodb_id';
var TILE_SIZE = 256;
var tile_pixel_width = TILE_SIZE;
var tile_pixel_height = TILE_SIZE;
//console.log('-- ZOOM: ' + zoom);
var tile_geo_width = bbox[1].lng() - bbox[0].lng();
var tile_geo_height = bbox[1].lat() - bbox[0].lat();
var pixel_geo_width = tile_geo_width / tile_pixel_width;
var pixel_geo_height = tile_geo_height / tile_pixel_height;
//console.log('-- PIXEL_GEO_SIZE: '
// + pixel_geo_width + ' x ' + pixel_geo_height);
var pixel_geo_maxsize = Math.max(pixel_geo_width, pixel_geo_height);
//console.log('-- MAX_SIZE: ' + pixel_geo_maxsize);
var tolerance = pixel_geo_maxsize / 2;
//console.log('-- TOLERANCE: ' + tolerance);
// simplify
var ENABLE_SIMPLIFY = opts.ENABLE_SIMPLIFY;
if ( ENABLE_SIMPLIFY ) {
geom_column = 'ST_Simplify(' + geom_column + ', ' + tolerance + ')';
// may change type
geom_column = 'ST_CollectionExtract(' + geom_column + ', ST_Dimension( '
+ geom_column_orig + ') + 1 )';
}
// snap to a pixel grid
var ENABLE_SNAPPING = opts.ENABLE_SNAPPING;
if ( ENABLE_SNAPPING ) {
geom_column = 'ST_SnapToGrid(' + geom_column + ', '
+ pixel_geo_maxsize + ')';
// may change type
geom_column = 'ST_CollectionExtract(' + geom_column + ', ST_Dimension( '
+ geom_column_orig + ') + 1 )';
}
// This is the query bounding box
var sql_env = "ST_MakeEnvelope("
+ bbox[0].lng() + "," + bbox[0].lat() + ","
+ bbox[1].lng() + "," + bbox[1].lat() + ", 4326)";
// clip
var ENABLE_CLIPPING = opts.ENABLE_CLIPPING;
if ( ENABLE_CLIPPING ) {
// This is a slightly enlarged version of the query bounding box
var sql_env_exp = 'ST_Expand(' + sql_env + ', '
+ ( pixel_geo_maxsize * 2 ) + ')';
// Also must be snapped to the grid ...
sql_env_exp = 'ST_SnapToGrid(' + sql_env_exp + ','
+ pixel_geo_maxsize + ')';
// snap to box
geom_column = 'ST_Snap(' + geom_column + ', ' + sql_env_exp
+ ', ' + pixel_geo_maxsize + ')';
// Make valid (both ST_Snap and ST_SnapToGrid and ST_Expand
var ENABLE_FIXING = opts.ENABLE_FIXING;
if ( ENABLE_FIXING ) {
// NOTE: up to PostGIS-2.0.0 beta5 ST_MakeValid did not accept
// points nor GeometryCollection objects
geom_column = 'CASE WHEN ST_Dimension('
+ geom_column + ') = 0 OR GeometryType('
+ geom_column + ") = 'GEOMETRYCOLLECTION' THEN "
+ geom_column + ' ELSE ST_CollectionExtract(ST_MakeValid('
+ geom_column + '), ST_Dimension(' + geom_column_orig
+ ') + 1 ) END';
}
// clip by box
geom_column = 'ST_Intersection(' + geom_column
+ ', ' + sql_env_exp + ')';
}
var columns = id_column + ',' + geom_column + ' as the_geom';
if(opts.columns) {
columns += ',';
columns += opts.columns.join(',')
columns += ' ';
}
// profiling only
var COUNT_ONLY = opts.COUNT_ONLY || false;
if ( COUNT_ONLY ) {
columns = x + ' as x, ' + y + ' as y, sum(st_npoints('
+ geom_column + ')) as the_geom';
}
var sql = "select " + columns +" from " + table;
sql += " WHERE the_geom && " + sql_env;
if (parseInt(zoom) < 11){
sql += " AND z = " + zoom + "+6 "
} else {
sql += " AND z = 16 "
}
//sql += " LIMIT 100";
//console.log('-- SQL: ' + sql);
return sql;
};
VECNIK.CartoDB = VECNIK.CartoDB || {};
VECNIK.CartoDB.SQL = sql;
})(VECNIK);
if (typeof module !== 'undefined' && module.exports) {
module.exports.CartoDBSQL = VECNIK.CartoDB.SQL;
}

110
styler/src/core.js Normal file
View File

@ -0,0 +1,110 @@
//========================================
// Core
//
// base classes
//========================================
// create root scope if not exists
var VECNIK = VECNIK || {};
(function(VECNIK) {
//========================================
// Events
//
// event management
//========================================
function Event() {}
Event.prototype.on = function(evt, callback) {
var cb = this.callbacks = this.callbacks || {};
var l = cb[evt] || (cb[evt] = []);
l.push(callback);
};
Event.prototype.emit = function(evt) {
var c = this.callbacks && this.callbacks[evt];
for(var i = 0; c && i < c.length; ++i) {
c[i].apply(this, Array.prototype.slice.call(arguments, 1));
}
};
// http get
// should be improved
function get(url, callback) {
var mygetrequest= new XMLHttpRequest();
mygetrequest.onreadystatechange=function() {
if (mygetrequest.readyState == 4){
if (mygetrequest.status == 200){
callback(JSON.parse(mygetrequest.responseText));
}
else {
//error
}
}
};
mygetrequest.open("GET", url, true)
mygetrequest.send(null)
}
//========================================
// model
//
// pretty basic model funcionallity
//========================================
function Model() {
//this.data = {}; // serializable data
}
Model.prototype = new Event();
Model.prototype.set = function(data, silent) {
this.data = this.data || {};
for(var v in data) {
if(data.hasOwnProperty(v)) {
this.data[v] = data[v];
}
}
if(!silent) {
this.emit('change', this.data);
}
};
Model.prototype.get = function(attr, def) {
if(this.data) {
if(attr in this.data) {
return this.data[attr];
}
return def;
}
return def;
};
/**
* delete the attribute
*/
Model.prototype.unset = function(attr, silent) {
delete this.data[attr];
if(!silent) {
this.emit('change', this.data);
}
};
Model.prototype.destroy = function() {
this.emit('destroy');
delete this.data;
};
VECNIK.Event = Event;
VECNIK.Model = Model;
VECNIK.get = get;
})(VECNIK);
if (typeof module !== 'undefined' && module.exports) {
module.exports.Event = VECNIK.Event;
module.exports.Model = VECNIK.Model;
}

95
styler/src/geometry.js Normal file
View File

@ -0,0 +1,95 @@
//========================================
// geometry conversion
//========================================
var VECNIK = VECNIK || {};
(function(VECNIK) {
var LatLng = VECNIK.LatLng;
var Point = VECNIK.Point;
//stats
var stats = {
vertices: 0
};
var latlng = new LatLng(0, 0);
var prj = new VECNIK.MercatorProjection();
function map_latlon(ll, x, y, zoom) {
latlng.latitude = ll[1];
latlng.longitude = ll[0];
stats.vertices++;
var point = prj.latLngToTilePoint(latlng, x, y, zoom);
//point.x = point.x >> 0;
//point.y = point.y >> 0;
return point;
}
var primitive_conversion = {
'LineString': function(x, y, zoom, coordinates) {
var converted = [];
var pc = primitive_conversion['Point'];
for(var i=0; i < coordinates.length; ++i) {
converted.push(pc(x, y, zoom, coordinates[i]));
}
return converted;
},
'Point': function(x, y, zoom, coordinates) {
return map_latlon(coordinates, x, y, zoom);
},
'MultiPoint': function(x, y, zoom, coordinates) {
var converted = [];
var pc = primitive_conversion['Point'];
for(var i=0; i < coordinates.length; ++i) {
converted.push(pc(x, y, zoom, coordinates[i]));
}
return converted;
},
//do not manage inner polygons!
'Polygon': function(x, y, zoom, coordinates) {
if(coordinates[0]) {
var coords = [];
for(var i=0; i < coordinates[0].length; ++i) {
coords.push(map_latlon(coordinates[0][i], x, y, zoom));
}
return [coords];
}
return null;
},
'MultiPolygon': function(x, y, zoom, coordinates) {
var polys = [];
var poly;
var pc = primitive_conversion['Polygon'];
for(var i=0; i < coordinates.length; ++i) {
poly = pc(x, y, zoom, coordinates[i]);
if(poly)
polys.push(poly);
}
return polys;
}
};
var project_geometry = function(geometry, zoom, x, y) {
var conversor = primitive_conversion[geometry.type];
if(conversor) {
return conversor(x, y , zoom, geometry.coordinates);
}
};
VECNIK.project_geometry = project_geometry;
VECNIK.geometry_stats = stats;
})(VECNIK);
if (typeof module !== 'undefined' && module.exports) {
module.exports.project_geometry = VECNIK.project_geometry;
}
if (typeof self !== 'undefined') {
self.VECNIK = VECNIK;
}

135
styler/src/mercator.js Normal file
View File

@ -0,0 +1,135 @@
//========================================
// Mercator projection
//========================================
//
var VECNIK = VECNIK || {};
(function(VECNIK) {
var TILE_SIZE = 256;
// todo: move outside
function Point(x, y) {
this.x = x || 0;
this.y = y || 0;
}
function LatLng(lat, lon) {
this.latitude = lat || 0;
this.longitude = lon || 0;
}
LatLng.prototype.lat = function() {
return this.latitude;
}
LatLng.prototype.lng = function() {
return this.longitude;
}
function bound(value, opt_min, opt_max) {
if (opt_min != null) value = Math.max(value, opt_min);
if (opt_max != null) value = Math.min(value, opt_max);
return value;
}
function degreesToRadians(deg) {
return deg * (Math.PI / 180);
}
function radiansToDegrees(rad) {
return rad / (Math.PI / 180);
}
function MercatorProjection() {
this.pixelOrigin_ = new Point(TILE_SIZE / 2, TILE_SIZE / 2);
this.pixelsPerLonDegree_ = TILE_SIZE / 360;
this.pixelsPerLonRadian_ = TILE_SIZE / (2 * Math.PI);
}
MercatorProjection.prototype.fromLatLngToPoint = function (latLng, opt_point) {
var me = this;
var point = opt_point || new Point(0, 0);
var origin = me.pixelOrigin_;
point.x = origin.x + latLng.lng() * me.pixelsPerLonDegree_;
// NOTE(appleton): Truncating to 0.9999 effectively limits latitude to
// 89.189. This is about a third of a tile past the edge of the world
// tile.
var siny = bound(Math.sin(degreesToRadians(latLng.lat())), -0.9999,
0.9999);
point.y = origin.y + 0.5 * Math.log((1 + siny) / (1 - siny)) *
-me.pixelsPerLonRadian_;
return point;
};
MercatorProjection.prototype.fromPointToLatLng = function (point) {
var me = this;
var origin = me.pixelOrigin_;
var lng = (point.x - origin.x) / me.pixelsPerLonDegree_;
var latRadians = (point.y - origin.y) / -me.pixelsPerLonRadian_;
var lat = radiansToDegrees(2 * Math.atan(Math.exp(latRadians)) -
Math.PI / 2);
return new LatLng(lat, lng);
};
MercatorProjection.prototype.tileBBox = function(x, y, zoom) {
var numTiles = 1 << zoom;
var inc = TILE_SIZE/numTiles;
var px = x*TILE_SIZE/numTiles;
var py = y*TILE_SIZE/numTiles;
return [
this.fromPointToLatLng(new Point(px, py + inc)),
this.fromPointToLatLng(new Point(px + inc, py))
];
};
MercatorProjection.prototype.tilePoint = function(x, y, zoom) {
var numTiles = 1 << zoom;
var px = x*TILE_SIZE;
var py = y*TILE_SIZE;
return [px, py];
};
MercatorProjection.prototype.latLngToTilePoint = function(latLng, x, y, zoom) {
var numTiles = 1 << zoom;
var projection = this;
var worldCoordinate = projection.fromLatLngToPoint(latLng);
var pixelCoordinate = new Point(
worldCoordinate.x * numTiles,
worldCoordinate.y * numTiles);
var tp = this.tilePoint(x, y, zoom);
return new Point(
Math.floor(pixelCoordinate.x - tp[0]),
Math.floor(pixelCoordinate.y - tp[1]));
};
MercatorProjection.prototype.latLngToTile = function(latLng, zoom) {
var numTiles = 1 << zoom;
var projection = this;
var worldCoordinate = projection.fromLatLngToPoint(latLng);
var pixelCoordinate = new Point(
worldCoordinate.x * numTiles,
worldCoordinate.y * numTiles);
return new Point(
Math.floor(pixelCoordinate.x / TILE_SIZE),
Math.floor(pixelCoordinate.y / TILE_SIZE));
};
VECNIK.LatLng = LatLng;
VECNIK.Point = Point;
VECNIK.MercatorProjection = MercatorProjection;
})(VECNIK);
if (typeof module !== 'undefined' && module.exports) {
module.exports.MercatorProjection = VECNIK.MercatorProjection;
module.exports.LatLng = VECNIK.LatLng;
module.exports.Point = VECNIK.Point;
}
if (typeof self !== 'undefined') {
self.VECNIK = VECNIK;
}

145
styler/src/model.js Normal file
View File

@ -0,0 +1,145 @@
//========================================
// vecnik models
//========================================
(function(VECNIK) {
// utility
function Profiler(name){
this.t0 = 0;
this.unit = '';
}
Profiler.prototype.start = function(unit) {
this.t0 = new Date().getTime();
this.unit = unit || '';
}
Profiler.prototype.end= function() {
var t = new Date().getTime() - this.t0;
//console.log("PROFILE - " + this.unit + ":" + t);
return t;
}
//========================================
// tile model
//========================================
function Tile(x, y, zoom) {
this.x = x;
this.y = y;
this.zoom = zoom;
this.on('change', this.precache.bind(this))
this.stats = {
conversion_time: 0,
vertices: 0,
primitive_count: 0
};
this.profiler = new Profiler('tile');
}
Tile.prototype = new VECNIK.Model();
Tile.prototype.key = function() {
return [this.x, this.y, this.zoom].join('-');
}
Tile.prototype.geometry = function() {
return this.get('geometry');
}
Tile.prototype.precache = function() {
var self = this;
var geometry = [];
this.profiler.start('conversion_time');
var primitives = this.data.features;
var vertex_count = VECNIK.geometry_stats.vertices;
if(VECNIK.settings.get('WEBWORKERS') && typeof Worker !== undefined) {
var worker = new Worker('../js/projector.worker.js');
worker.onmessage = function(ev) {
self.set({geometry: ev.data.geometry}, true);
self.unset('features', true);
self.emit('geometry_ready');
};
worker.postMessage({
primitives: primitives,
zoom: this.zoom,
x: this.x,
y: this.y
});
} else {
for(var i = 0; i < primitives.length; ++i) {
var p = primitives[i];
if(p.geometry) {
var converted = VECNIK.project_geometry(p.geometry, this.zoom, this.x, this.y);
if(converted && converted.length !== 0) {
geometry.push({
vertexBuffer: converted,
type: p.geometry.type,
metadata: p.properties
});
} else {
delete p.geometry.coordinates;
}
}
}
this.set({geometry: geometry}, true);
this.unset('features', true);
this.emit('geometry_ready');
}
this.stats.vertices = VECNIK.geometry_stats.vertices - vertex_count;
this.stats.primitive_count = primitives.length;
this.stats.conversion_time = this.profiler.end();
}
//========================================
// tile manager
//========================================
function TileManager(dataProvider) {
this.tiles = {};
this.dataProvider = dataProvider;
}
TileManager.prototype.tileIndex= function(coordinates) {
return coordinates.toKey();
}
TileManager.prototype.get = function(coordinates) {
return this.tiles[this.tileIndex(coordinates)];
}
TileManager.prototype.destroy= function(coordinates) {
var tile = this.tiles[this.tileIndex(coordinates)];
if(tile) {
tile.destroy();
//console.log("removing " + this.tileIndex(coordinates));
delete this.tiles[this.tileIndex(coordinates)];
}
}
TileManager.prototype.add = function(coordinates) {
//console.log("adding" + this.tileIndex(coordinates));
var tile = this.tiles[this.tileIndex(coordinates)] = new Tile(
coordinates.column,
coordinates.row,
coordinates.zoom
);
VECNIK.get(this.dataProvider.url(coordinates), function(data) {
tile.set(data);
});
return tile;
}
VECNIK.Tile = Tile;
VECNIK.TileManager = TileManager;
VECNIK.Profiler = Profiler;
})(VECNIK);
if (typeof module !== 'undefined' && module.exports) {
module.exports.Tile = VECNIK.Tile;
module.exports.TileManager = VECNIK.TileManager;
}

View File

@ -0,0 +1,23 @@
importScripts('../js/mercator.js');
importScripts('../js/geometry.js');
self.onmessage = function(event) {
var data = event.data;
var primitives = data.primitives;
var geometry = [];
for(var i = 0; i < primitives.length; ++i) {
var p = primitives[i];
if(p.geometry) {
var converted = VECNIK.project_geometry(p.geometry,
data.zoom, data.x, data.y);
if(converted && converted.length !== 0) {
geometry.push({
vertexBuffer: converted,
type: p.geometry.type,
metadata: p.properties
});
}
}
}
self.postMessage({geometry: geometry});
};

175
styler/src/renderer.js Normal file
View File

@ -0,0 +1,175 @@
//========================================
// vecnik views
//========================================
(function(VECNIK) {
function Renderer() {
var self = this;
var primitive_render = this.primitive_render = {
'Point': function(ctx, coordinates) {
ctx.save();
var radius = 2;
var p = coordinates;
ctx.translate(p.x, p.y);
ctx.beginPath();
ctx.arc(radius, radius, radius, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fill();
ctx.stroke();
ctx.restore();
},
'MultiPoint': function(ctx, coordinates) {
var prender = primitive_render['Point'];
for(var i=0; i < coordinates.length; ++i) {
prender(ctx, coordinates[i]);
}
},
'Polygon': function(ctx, coordinates) {
ctx.beginPath();
var p = coordinates[0][0];
ctx.moveTo(p.x, p.y);
for(var i=0; i < coordinates[0].length; ++i) {
p = coordinates[0][i];
ctx.lineTo(p.x, p.y);
}
ctx.closePath();
ctx.fill();
//ctx.stroke();
},
'MultiPolygon': function(ctx, coordinates) {
var prender = primitive_render['Polygon'];
for(var i=0; i < coordinates.length; ++i) {
prender(ctx, coordinates[i]);
}
},
'LineString': function(ctx, coordinates) {
ctx.beginPath();
var p = coordinates[0];
ctx.moveTo(p.x, p.y);
for(var i=0; i < coordinates.length; ++i) {
p = coordinates[i];
ctx.lineTo(p.x, p.y);
}
ctx.stroke();
}
};
}
Renderer.prototype.render = function(ctx, geometry, zoom, shader) {
var primitive_render = this.primitive_render;
ctx.canvas.width = ctx.canvas.width;
var primitive_type;
if(geometry && geometry.length) {
for(var i = 0; i < geometry.length; ++i) {
var geo = geometry[i];
var primitive_type = geo.type;
var renderer = primitive_render[primitive_type];
if(renderer) {
// render visible tile
var render_context = {
zoom: zoom,
id: i
};
var is_active = true;
if(shader) {
is_active = shader.needs_render(geo.metadata, render_context, primitive_type);
if(is_active) {
shader.reset(ctx, primitive_type);
shader.apply(ctx, geo.metadata, render_context);
}
}
if (is_active) {
renderer(ctx, geo.vertexBuffer);
}
}
}
}
};
//========================================
// Canvas tile view
//========================================
function CanvasTileView(tile, shader, renderer) {
this.tileSize = new VECNIK.Point(256, 256);
var canvas = document.createElement('canvas');
canvas.width = this.tileSize.x;
canvas.height = this.tileSize.y;
this.ctx = canvas.getContext('2d');
this.canvas = canvas;
var backCanvas = document.createElement('canvas');
backCanvas.width = this.tileSize.x;
backCanvas.height = this.tileSize.y;
this.backCtx = backCanvas.getContext('2d');
this.backCanvas = backCanvas;
this.el = canvas;
this.id = tile.key();
this.el.setAttribute('id', tile.key());
var self = this;
this.tile = tile;
var render = function(){self.render();};
tile.on('geometry_ready', render);
// shader
this.shader = shader;
if(shader) {
shader.on('change', render);
}
this.renderer = renderer || new Renderer();
this.profiler = new VECNIK.Profiler('tile_render');
this.stats = {
rendering_time: 0
}
}
CanvasTileView.prototype.remove = function() {
}
CanvasTileView.prototype.render = function() {
var ctx = this.ctx;
this.profiler.start('render');
var BACKBUFFER = true;
if(BACKBUFFER) {
this.backCanvas.width = this.backCanvas.width;
this.renderer.render(this.backCtx, this.tile.geometry(), this.tile.zoom, this.shader);
this.canvas.width = this.canvas.width;
this.ctx.drawImage(this.backCanvas, 0, 0);
} else {
this.renderer.render(ctx, this.tile.geometry(), this.tile.zoom, this.shader);
}
this.stats.rendering_time = this.profiler.end();
}
//========================================
// Map view
// manages the list of tiles
//========================================
function CanvasMapView() {
this.tile_views = {};
}
CanvasMapView.prototype.add = function(canvasview) {
this.tile_views[canvasview.id] = canvasview;
}
CanvasMapView.prototype.getByElement = function(el) {
return this.tile_views[el.getAttribute('id')];
}
VECNIK.Renderer = Renderer;
VECNIK.CanvasTileView = CanvasTileView;
VECNIK.CanvasMapView = CanvasMapView;
})(VECNIK);
if (typeof module !== 'undefined' && module.exports) {
}

33
styler/src/settings.js Normal file
View File

@ -0,0 +1,33 @@
//========================================
// Global settings
//========================================
var VECNIK = VECNIK || {};
(function(VECNIK) {
function Settings(defaults) {
this.set(defaults);
}
Settings.prototype = new VECNIK.Model();
// default settings
VECNIK.settings = new Settings({
WEBWORKERS: false,
BACKBUFFER: true,
ENABLE_SIMPLIFY: true,
ENABLE_SNAPPING: true,
ENABLE_CLIPPING: true,
ENABLE_FIXING: true
});
})(VECNIK);
if (typeof module !== 'undefined' && module.exports) {
module.exports.settings = VECNIK.settings;
}
if (typeof self !== 'undefined') {
self.VECNIK = VECNIK;
}

120
styler/src/shader.js Normal file
View File

@ -0,0 +1,120 @@
//========================================
// shader
//========================================
(function(VECNIK) {
var mapper = {
'point-color': 'fillStyle',
'line-color': 'strokeStyle',
'line-width': 'lineWidth',
'line-opacity': 'globalAlpha',
'polygon-fill': 'fillStyle',
'polygon-opacity': 'globalAlpha'
};
function CartoShader(shader) {
this.compiled = {};
this.shader_src = null;
this.compile(shader)
}
CartoShader.prototype = new VECNIK.Event();
CartoShader.prototype.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]; })();");
}
}
this.emit('change');
};
var needed_settings = {
'LineString': [
'line-color',
'line-width',
'line-opacity'
],
'Polygon': [
'polygon-fill'
],
'MultiPolygon': [
'polygon-fill'
]
};
var defaults = {
'LineString': {
'strokeStyle': '#000',
'lineWidth': 1,
'globalAlpha': 1.0,
'lineCap': 'round'
},
'Polygon': {
'strokeStyle': '#000',
'lineWidth': 1,
'globalAlpha': 1.0
},
'MultiPolygon': {
'strokeStyle': '#000',
'lineWidth': 1,
'globalAlpha': 1.0
}
};
CartoShader.prototype.needs_render = function(data, render_context, primitive_type) {
var variables = needed_settings[primitive_type];
var shader = this.compiled;
for(var attr in variables) {
var style_attr = variables[attr];
var attr_present = this.shader_src[style_attr];
if(attr_present !== undefined) {
var fn = shader[mapper[style_attr]];
if(typeof fn === 'function') {
fn = fn(data, render_context);
}
if(fn !== null && fn !== undefined) {
return true;
}
}
}
return false;
}
CartoShader.prototype.reset = function(ctx, primitive_type) {
var def = defaults[primitive_type];
for(var attr in def) {
ctx[attr] = def[attr];
}
}
CartoShader.prototype.apply = function(canvas_ctx, data, render_context) {
var shader = this.compiled;
for(var attr in shader) {
var fn = shader[attr];
if(typeof fn === 'function') {
fn = fn(data, render_context);
}
if(fn !== null && canvas_ctx[attr] != fn) {
canvas_ctx[attr] = fn;
}
}
};
VECNIK.CartoShader = CartoShader;
})(VECNIK);
if (typeof module !== 'undefined' && module.exports) {
module.exports.CartoShader = CartoShader;
}

View File

@ -0,0 +1,55 @@
L.TileLayer.Canvas = L.TileLayer.extend({
options: {
async: false
},
initialize: function (options) {
this.tileSize = tileSize || new MM.Point(256, 256)
this.tiles = new CartoDBSQLAPI({
user: 'vizzuality',
table: 'countries_final',
columns: ['admin'],
});
this.views = new CanvasMapView();
},
redraw: function () {
},
_createTileProto: function () {
var proto = this._canvasProto = L.DomUtil.create('canvas', 'leaflet-tile');
var tileSize = this.options.tileSize;
proto.width = tileSize;
proto.height = tileSize;
},
_createTile: function () {
var tile = this._canvasProto.cloneNode(false);
tile.onselectstart = tile.onmousemove = L.Util.falseFn;
return tile;
},
_loadTile: function (tile, tilePoint, zoom) {
tile._layer = this;
tile._tilePoint = tilePoint;
tile._zoom = zoom;
this.drawTile(tile, tilePoint, zoom);
if (!this.options.async) {
this.tileDrawn(tile);
}
},
_resetTile: function (tile) {
},
drawTile: function (tile, tilePoint, zoom) {
// override with rendering code
},
tileDrawn: function (tile) {
this._tileOnLoad.call(tile);
}
});

View File

@ -0,0 +1,55 @@
//========================================
// Core
//
// base classes
//========================================
// create root scope if not exists
var VECNIK = VECNIK || {};
(function(VECNIK) {
var MM = com.modestmaps;
//========================================
// testing provider with mapbox tile layer
//========================================
function TileManagerMapBox() {
}
TileManagerMapBox.prototype = new VECNIK.TileManager();
TileManagerMapBox.prototype.url = function(coordinates) {
return 'http://b.tiles.mapbox.com/v3/mapbox.mapbox-streets/' + coordinates.zoom + '/' + coordinates.row + '/' + coordinates.column + ".png";
}
//========================================
// Canvas provider
//========================================
function CanvasProvider(dataSource, shader, renderer, tileSize) {
this.tileSize = tileSize || new MM.Point(256, 256)
this.renderer = renderer;
this.tiles = new VECNIK.TileManager(dataSource)
this.views = new VECNIK.CanvasMapView(shader);
this.shader = shader;
}
CanvasProvider.prototype.getTile = function(coord) {
var tile = this.tiles.add(coord);
var canvas = new VECNIK.CanvasTileView(tile, this.shader, this.renderer);
this.views.add(canvas);
return canvas.el;
}
CanvasProvider.prototype.releaseTile = function(coordinates) {
this.tiles.destroy(coordinates);
};
MM.extend(CanvasProvider, MM.MapProvider);
VECNIK.MM = {
CanvasProvider: CanvasProvider,
TileManagerMapBox: TileManagerMapBox
};
})(VECNIK);