2012-05-17 07:33:56 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* 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;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2012-10-01 23:20:50 +08:00
|
|
|
var resource_path= options.user + '.cartodb.com/api/v2/sql';
|
2012-05-17 07:33:56 +08:00
|
|
|
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
|
|
|
|
};
|
|
|
|
|
|
|
|
};
|