This commit is contained in:
zhongjin
2020-06-15 12:07:54 +08:00
parent 610ed21a90
commit a96ef233c9
444 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,152 @@
/**
* set of functions from other libraries used to pack the library
* as much as posible
*/
;(function() {
var slice = Array.prototype.slice;
var splice = Array.prototype.splice;
var eventSplitter = /\s+/;
// A module that can be mixed in to *any object* in order to provide it with
// custom events. You may bind with `on` or remove with `off` callback functions
// to an event; trigger`-ing an event fires all callbacks in succession.
//
// var object = {};
// _.extend(object, Backbone.Events);
// object.on('expand', function(){ alert('expanded'); });
// object.trigger('expand');
//
var Events = Backbone.Events = {
// Bind one or more space separated events, `events`, to a `callback`
// function. Passing `"all"` will bind the callback to all events fired.
on: function(events, callback, context) {
var calls, event, node, tail, list;
if (!callback) return this;
events = events.split(eventSplitter);
calls = this._callbacks || (this._callbacks = {});
// Create an immutable callback list, allowing traversal during
// modification. The tail is an empty object that will always be used
// as the next node.
while (event = events.shift()) {
list = calls[event];
node = list ? list.tail : {};
node.next = tail = {};
node.context = context;
node.callback = callback;
calls[event] = {tail: tail, next: list ? list.next : node};
}
return this;
},
// Remove one or many callbacks. If `context` is null, removes all callbacks
// with that function. If `callback` is null, removes all callbacks for the
// event. If `events` is null, removes all bound callbacks for all events.
off: function(events, callback, context) {
var event, calls, node, tail, cb, ctx;
// No events, or removing *all* events.
if (!(calls = this._callbacks)) return;
if (!(events || callback || context)) {
delete this._callbacks;
return this;
}
// Loop through the listed events and contexts, splicing them out of the
// linked list of callbacks if appropriate.
events = events ? events.split(eventSplitter) : _.keys(calls);
while (event = events.shift()) {
node = calls[event];
delete calls[event];
if (!node || !(callback || context)) continue;
// Create a new list, omitting the indicated callbacks.
tail = node.tail;
while ((node = node.next) !== tail) {
cb = node.callback;
ctx = node.context;
if ((callback && cb !== callback) || (context && ctx !== context)) {
this.on(event, cb, ctx);
}
}
}
return this;
},
// Trigger one or many events, firing all bound callbacks. Callbacks are
// passed the same arguments as `trigger` is, apart from the event name
// (unless you're listening on `"all"`, which will cause your callback to
// receive the true name of the event as the first argument).
trigger: function(events) {
var event, node, calls, tail, args, all, rest;
if (!(calls = this._callbacks)) return this;
all = calls.all;
events = events.split(eventSplitter);
rest = slice.call(arguments, 1);
// For each event, walk through the linked list of callbacks twice,
// first to trigger the event, then to trigger any `"all"` callbacks.
while (event = events.shift()) {
if (node = calls[event]) {
tail = node.tail;
while ((node = node.next) !== tail) {
node.callback.apply(node.context || this, rest);
}
}
if (node = all) {
tail = node.tail;
args = [event].concat(rest);
while ((node = node.next) !== tail) {
node.callback.apply(node.context || this, args);
}
}
}
return this;
}
};
// Aliases for backwards compatibility.
Events.bind = Events.on;
Events.unbind = Events.off;
if(cartodb._Promise === undefined) {
function _Promise() { }
_Promise.prototype = Events;
_Promise.prototype.done = function(fn) {
return this.on('done', fn);
}
_Promise.prototype.error = function(fn) {
return this.on('error', fn);
}
cartodb._Promise = _Promise;
}
if(typeof(_) === 'undefined') {
var _ = {
extend: function(obj, prop) {
for(var p in prop) { obj[p] = prop[p]; }
return obj;
},
defaults: function(obj, def) {
for(var p in def) {
if(obj[p] == undefined) {
obj[p] = def[p];
}
}
return obj;
},
isFunction: function(fn) {
return typeof(fn) === 'function';
}
}
}
})();

View File

@@ -0,0 +1,229 @@
/**
* public api for cartodb
*/
(function() {
function _Promise() {
}
_.extend(_Promise.prototype, Backbone.Events, {
done: function(fn) {
return this.bind('done', fn);
},
error: function(fn) {
return this.bind('error', fn);
}
});
cdb._Promise = _Promise;
var _requestCache = {};
/**
* compose cartodb url
*/
function cartodbUrl(opts) {
var host = opts.host || 'carto.com';
var protocol = opts.protocol || 'https';
return protocol + '://' + opts.user + '.' + host + '/api/v1/viz/' + opts.table + '/viz.json';
}
/**
* given layer params fetchs the layer json
*/
function _getLayerJson(layer, callback) {
var url = null;
if(layer.layers !== undefined || ((layer.kind || layer.type) !== undefined)) {
// layer object contains the layer data
_.defer(function() { callback(layer); });
return;
} else if(layer.table !== undefined && layer.user !== undefined) {
// layer object points to cartodbjson
url = cartodbUrl(layer);
} else if(layer.indexOf) {
// fetch from url
url = layer;
}
if(url) {
cdb.core.Loader.get(url, callback);
} else {
_.defer(function() { callback(null); });
}
}
/**
* create a layer for the specified map
*
* @param map should be a L.Map object, or equivalent depending on what provider you have.
* @param layer should be an url or a javascript object with the data to create the layer
* @param options layer options
*
*/
cartodb.createLayer = function(map, layer, options, callback) {
if(map === undefined) {
throw new TypeError("map should be provided");
}
if(layer === undefined) {
throw new TypeError("layer should be provided");
}
var layerView, MapType;
var options = options || {};
var args = arguments;
var fn = args[args.length -1];
if(_.isFunction(fn)) {
callback = fn;
}
var promise = new _Promise();
promise.addTo = function(map, position) {
promise.on('done', function() {
MapType.addLayerToMap(layerView, map, position);
});
return promise;
};
_getLayerJson(layer, function(visData) {
var layerData;
if(!visData) {
promise.trigger('error');
return;
}
// extract layer data from visualization data
if(visData.layers) {
if(visData.layers.length < 2) {
promise.trigger('error', "visualization file does not contain layer info");
}
var index = options.layerIndex;
if (index !== undefined) {
if(visData.layers.length <= index) {
promise.trigger('error', 'layerIndex out of bounds');
return;
}
layerData = visData.layers[index];
} else {
var DATA_LAYER_TYPES = ['namedmap', 'layergroup', 'torque'];
// Select the first data layer (namedmap or layergroup)
layerData = _.find(visData.layers, function(layer){
return DATA_LAYER_TYPES.indexOf(layer.type) !== -1;
});
}
} else {
layerData = visData;
}
if(!layerData) {
promise.trigger('error');
return;
}
// update options
if(options && !_.isFunction(options)) {
layerData.options = layerData.options || {};
_.extend(layerData.options, options);
}
options = _.defaults(options, {
infowindow: true,
https: false,
legends: true,
time_slider: true,
tooltip: true
});
// check map type
// TODO: improve checking
if(typeof(map.overlayMapTypes) !== "undefined") {
MapType = cdb.geo.GoogleMapsMapView;
// check if leaflet is loaded globally
} else if(map instanceof L.Map || (window.L && map instanceof window.L.Map)) {
MapType = cdb.geo.LeafletMapView;
} else {
promise.trigger('error', "cartodb.js can't guess the map type");
return promise;
}
// create a dummy viz
var viz = map.viz;
if(!viz) {
var mapView = new MapType({
map_object: map,
map: new cdb.geo.Map()
});
map.viz = viz = new cdb.vis.Vis({
mapView: mapView
});
viz.updated_at = visData.updated_at;
viz.https = options.https;
}
function createLayer() {
layerView = viz.createLayer(layerData, { no_base_layer: true });
var torqueLayer;
var mobileEnabled = /Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
var addMobileLayout = (options.mobile_layout && mobileEnabled) || options.force_mobile;
if(!layerView) {
promise.trigger('error', "layer not supported");
return promise;
}
if(options.infowindow) {
viz.addInfowindow(layerView);
}
if(options.tooltip) {
viz.addTooltip(layerView);
}
if(options.legends) {
var layerModel = cdb.vis.Layers.create(layerData.type || layerData.kind, viz, layerData);
viz._addLegends(viz._createLayerLegendView(layerModel.attributes, layerView))
}
if(options.time_slider && layerView.model.get('type') === 'torque') {
if (!addMobileLayout) { // don't add the overlay if we are in mobile
viz.addTimeSlider(layerView);
}
torqueLayer = layerView;
}
if (addMobileLayout) {
options.mapView = map.viz.mapView;
viz.addOverlay({
type: 'mobile',
layerView: layerView,
overlays: [],
torqueLayer: torqueLayer,
options: options
});
}
callback && callback(layerView);
promise.trigger('done', layerView);
}
// load needed modules
if(!viz.checkModules([layerData])) {
viz.loadModules([layerData], function() {
createLayer();
});
} else {
createLayer();
}
});
return promise;
};
})();

View File

@@ -0,0 +1,691 @@
;(function() {
var root = this;
root.cartodb = root.cartodb || {};
function SQL(options) {
if(cartodb === this || window === this) {
return new SQL(options);
}
if(!options.user) {
throw new Error("user should be provided");
}
var loc = new String(window.location.protocol);
loc = loc.slice(0, loc.length - 1);
if(loc == 'file') {
loc = 'https';
}
this.ajax = options.ajax || (typeof(jQuery) !== 'undefined' ? jQuery.ajax: reqwest);
if(!this.ajax) {
throw new Error("jQuery or reqwest should be loaded");
}
this.options = _.defaults(options, {
version: 'v2',
protocol: loc,
jsonp: typeof(jQuery) !== 'undefined' ? !jQuery.support.cors: false
})
if (!this.options.sql_api_template) {
var opts = this.options;
var template = null;
if(opts && opts.completeDomain) {
template = opts.completeDomain;
} else {
var host = opts.host || 'carto.com';
var protocol = opts.protocol || 'https';
template = protocol + '://{user}.' + host;
}
this.options.sql_api_template = template;
}
}
SQL.prototype._host = function() {
var opts = this.options;
return opts.sql_api_template.replace('{user}', opts.user) + '/api/' + opts.version + '/sql';
},
/**
* var sql = new SQL('cartodb_username');
* sql.execute("select * from {{ table }} where id = {{ id }}", {
* table: 'test',
* id: '1'
* })
*/
SQL.prototype.execute = function(sql, vars, options, callback) {
//Variable that defines if a query should be using get method or post method
var MAX_LENGTH_GET_QUERY = 1024;
var promise = new cartodb._Promise();
if(!sql) {
throw new TypeError("sql should not be null");
}
// setup arguments
var args = arguments,
fn = args[args.length -1];
if(_.isFunction(fn)) {
callback = fn;
}
options = _.defaults(options || {}, this.options);
var params = {
type: 'get',
dataType: 'json',
crossDomain: true
};
if(options.cache !== undefined) {
params.cache = options.cache;
}
if(options.jsonp) {
delete params.crossDomain;
if (options.jsonpCallback) {
params.jsonpCallback = options.jsonpCallback;
}
params.dataType = 'jsonp';
}
// Substitute mapnik tokens
// resolution at zoom level 0
var res = '156543.03515625';
// full webmercator extent
var ext = 'ST_MakeEnvelope(-20037508.5,-20037508.5,20037508.5,20037508.5,3857)';
sql = sql.replace('!bbox!', ext)
.replace('!pixel_width!', res)
.replace('!pixel_height!', res);
// create query
var query = Mustache.render(sql, vars);
// check method: if we are going to send by get or by post
var isGetRequest = query.length < MAX_LENGTH_GET_QUERY;
// generate url depending on the http method
var reqParams = ['format', 'dp', 'api_key'];
// request params
if (options.extra_params) {
reqParams = reqParams.concat(options.extra_params);
}
params.url = this._host() ;
if (isGetRequest) {
var q = 'q=' + encodeURIComponent(query);
for(var i in reqParams) {
var r = reqParams[i];
var v = options[r];
if(v) {
q += '&' + r + "=" + v;
}
}
params.url += '?' + q;
} else {
var objPost = {'q': query};
for(var i in reqParams) {
var r = reqParams[i];
var v = options[r];
if (v) {
objPost[r] = v;
}
}
params.data = objPost;
//Check if we are using jQuery(uncompressed) or reqwest (core)
if ((typeof(jQuery) !== 'undefined')) {
params.type = 'post';
} else {
params.method = 'post';
}
}
// wrap success and error functions
var success = options.success;
var error = options.error;
if(success) delete options.success;
if(error) delete error.success;
params.error = function(resp) {
var res = resp.responseText || resp.response;
var errors = res && JSON.parse(res);
promise.trigger('error', errors && errors.error, resp)
if(error) error(resp);
}
params.success = function(resp, status, xhr) {
// manage rewest
if(status == undefined) {
status = resp.status;
xhr = resp;
resp = JSON.parse(resp.response);
}
//Timeout explanation. CartoDB.js ticket #336
//From St.Ov.: "what setTimeout does is add a new event to the browser event queue
//and the rendering engine is already in that queue (not entirely true, but close enough)
//so it gets executed before the setTimeout event."
setTimeout(function() {
promise.trigger('done', resp, status, xhr);
if(success) success(resp, status, xhr);
if(callback) callback(resp);
}, 0);
}
// call ajax
delete options.jsonp;
this.ajax(_.extend(params, options));
return promise;
}
SQL.prototype.getBounds = function(sql, vars, options, callback) {
var promise = new cartodb._Promise();
var args = arguments,
fn = args[args.length -1];
if(_.isFunction(fn)) {
callback = fn;
}
var s = 'SELECT ST_XMin(ST_Extent(the_geom)) as minx,' +
' ST_YMin(ST_Extent(the_geom)) as miny,'+
' ST_XMax(ST_Extent(the_geom)) as maxx,' +
' ST_YMax(ST_Extent(the_geom)) as maxy' +
' from ({{{ sql }}}) as subq';
sql = Mustache.render(sql, vars);
this.execute(s, { sql: sql }, options)
.done(function(result) {
if (result.rows && result.rows.length > 0 && result.rows[0].maxx != null) {
var c = result.rows[0];
var minlat = -85.0511;
var maxlat = 85.0511;
var minlon = -179;
var maxlon = 179;
var clamp = function(x, min, max) {
return x < min ? min : x > max ? max : x;
}
var lon0 = clamp(c.maxx, minlon, maxlon);
var lon1 = clamp(c.minx, minlon, maxlon);
var lat0 = clamp(c.maxy, minlat, maxlat);
var lat1 = clamp(c.miny, minlat, maxlat);
var bounds = [[lat0, lon0], [lat1, lon1]];
promise.trigger('done', bounds);
callback && callback(bounds);
}
})
.error(function(err) {
promise.trigger('error', err);
})
return promise;
}
/**
* var people_under_10 = sql
* .table('test')
* .columns(['age', 'column2'])
* .filter('age < 10')
* .limit(15)
* .order_by('age')
*
* people_under_10(function(results) {
* })
*/
SQL.prototype.table = function(name) {
var _name = name;
var _filters;
var _columns = [];
var _limit;
var _order;
var _orderDir;
var _sql = this;
function _table() {
_table.fetch.apply(_table, arguments);
}
_table.fetch = function(vars) {
vars = vars || {}
var args = arguments,
fn = args[args.length -1];
if(_.isFunction(fn)) {
callback = fn;
if(args.length === 1) vars = {};
}
_sql.execute(_table.sql(), vars, callback);
}
_table.sql = function() {
var s = "select"
if(_columns.length) {
s += ' ' + _columns.join(',') + ' '
} else {
s += ' * '
}
s += "from " + _name;
if(_filters) {
s += " where " + _filters;
}
if(_limit) {
s += " limit " + _limit;
}
if(_order) {
s += " order by " + _order;
}
if(_orderDir) {
s += ' ' + _orderDir;
}
return s;
}
_table.filter = function(f) {
_filters = f;
return _table;
}
_table.order_by= function(o) {
_order = o;
return _table;
}
_table.asc = function() {
_orderDir = 'asc'
return _table;
}
_table.desc = function() {
_orderDir = 'desc'
return _table;
}
_table.columns = function(c) {
_columns = c;
return _table;
}
_table.limit = function(l) {
_limit = l;
return _table;
}
return _table;
}
/*
* sql.filter(sql.f().distance('< 10km')
*/
/*cartodb.SQL.geoFilter = function() {
var _sql;
function f() {}
f.distance = function(qty) {
qty.replace('km', '*1000')
_sql += 'st_distance(the_geom) ' + qty
}
f.or = function() {
}
f.and = function() {
}
return f;
}
*/
function array_agg(s) {
return JSON.parse(s.replace(/^{/, '[').replace(/}$/,']'));
}
SQL.prototype.describeString = function(sql, column, callback) {
var s = [
'WITH t as (',
' SELECT count(*) as total,',
' count(DISTINCT {{column}}) as ndist',
' FROM ({{sql}}) _wrap',
' ), a as (',
' SELECT ',
' count(*) cnt, ',
' {{column}}',
' FROM ',
' ({{sql}}) _wrap ',
' GROUP BY ',
' {{column}} ',
' ORDER BY ',
' cnt DESC',
' ), b As (',
' SELECT',
' row_number() OVER (ORDER BY cnt DESC) rn,',
' cnt',
' FROM a',
' ), c As (',
' SELECT ',
' sum(cnt) OVER (ORDER BY rn ASC) / t.total cumperc,',
' rn,',
' cnt ',
' FROM b, t',
' LIMIT 10',
' ),',
'stats as (',
'select count(distinct({{column}})) as uniq, ',
' count(*) as cnt, ',
' sum(case when COALESCE(NULLIF({{column}},\'\')) is null then 1 else 0 end)::numeric as null_count, ',
' sum(case when COALESCE(NULLIF({{column}},\'\')) is null then 1 else 0 end)::numeric / count(*)::numeric as null_ratio, ',
// ' CDB_DistinctMeasure(array_agg({{column}}::text)) as cat_weight ',
' (SELECT max(cumperc) weight FROM c) As skew ',
'from ({{sql}}) __wrap',
'),',
'hist as (',
'select array_agg(row(d, c)) array_agg from (select distinct({{column}}) d, count(*) as c from ({{sql}}) __wrap, stats group by 1 limit 100) _a',
')',
'select * from stats, hist'
];
var query = Mustache.render(s.join('\n'), {
column: column,
sql: sql
});
var normalizeName = function(str) {
var normalizedStr = str.replace(/^"(.+(?="$))?"$/, '$1'); // removes surrounding quotes
return normalizedStr.replace(/""/g, '"'); // removes duplicated quotes
}
this.execute(query, function(data) {
var row = data.rows[0];
var weight = 0;
var histogram = [];
try {
var s = array_agg(row.array_agg);
var histogram = _(s).map(function(row) {
var r = row.match(/\((.*),(\d+)/);
var name = normalizeName(r[1]);
return [name, +r[2]];
});
weight = row.skew * (1 - row.null_ratio) * (1 - row.uniq / row.cnt) * ( row.uniq > 1 ? 1 : 0);
} catch(e) {
}
callback({
type: 'string',
hist: histogram,
distinct: row.uniq,
count: row.cnt,
null_count: row.null_count,
null_ratio: row.null_ratio,
skew: row.skew,
weight: weight
});
});
}
SQL.prototype.describeDate = function(sql, column, callback) {
var s = [
'with minimum as (',
'SELECT min({{column}}) as start_time FROM ({{sql}}) _wrap), ',
'maximum as (SELECT max({{column}}) as end_time FROM ({{sql}}) _wrap), ',
'null_ratio as (SELECT sum(case when {{column}} is null then 1 else 0 end)::numeric / count(*)::numeric as null_ratio FROM ({{sql}}) _wrap), ',
'moments as (SELECT count(DISTINCT {{column}}) as moments FROM ({{sql}}) _wrap)',
'SELECT * FROM minimum, maximum, moments, null_ratio'
];
var query = Mustache.render(s.join('\n'), {
column: column,
sql: sql
});
this.execute(query, function(data) {
var row = data.rows[0];
var e = new Date(row.end_time);
var s = new Date(row.start_time);
var moments = row.moments;
var steps = Math.min(row.moments, 1024);
callback({
type: 'date',
start_time: s,
end_time: e,
range: e - s,
steps: steps,
null_ratio: row.null_ratio
});
});
}
SQL.prototype.describeBoolean = function(sql, column, callback){
var s = [
'with stats as (',
'select count(distinct({{column}})) as uniq,',
'count(*) as cnt',
'from ({{sql}}) _wrap ',
'),',
'null_ratio as (',
'SELECT sum(case when {{column}} is null then 1 else 0 end)::numeric / count(*)::numeric as null_ratio FROM ({{sql}}) _wrap), ',
'true_ratio as (',
'SELECT sum(case when {{column}} is true then 1 else 0 end)::numeric / count(*)::numeric as true_ratio FROM ({{sql}}) _wrap) ',
'SELECT * FROM true_ratio, null_ratio, stats'
];
var query = Mustache.render(s.join('\n'), {
column: column,
sql: sql
});
this.execute(query, function(data) {
var row = data.rows[0];
callback({
type: 'boolean',
null_ratio: row.null_ratio,
true_ratio: row.true_ratio,
distinct: row.uniq,
count: row.cnt
});
});
}
SQL.prototype.describeGeom = function(sql, column, callback) {
var s = [
'with stats as (',
'select st_asgeojson(st_extent({{column}})) as bbox',
'from ({{sql}}) _wrap',
'),',
'geotype as (',
'select st_geometrytype({{column}}) as geometry_type from ({{sql}}) _w where {{column}} is not null limit 1',
'),',
'clusters as (',
'with clus as (',
'SELECT distinct(ST_snaptogrid(the_geom, 10)) as cluster, count(*) as clustercount FROM ({{sql}}) _wrap group by 1 order by 2 desc limit 3),',
'total as (',
'SELECT count(*) FROM ({{sql}}) _wrap)',
'SELECT sum(clus.clustercount)/sum(total.count) AS clusterrate FROM clus, total',
'),',
'density as (',
'SELECT count(*) / st_area(st_extent(the_geom)) as density FROM ({{sql}}) _wrap',
')',
'select * from stats, geotype, clusters, density'
];
var query = Mustache.render(s.join('\n'), {
column: column,
sql: sql
});
function simplifyType(g) {
return {
'st_multipolygon': 'polygon',
'st_polygon': 'polygon',
'st_multilinestring': 'line',
'st_linestring': 'line',
'st_multipoint': 'point',
'st_point': 'point'
}[g.toLowerCase()]
};
this.execute(query, function(data) {
var row = data.rows[0];
var bbox = JSON.parse(row.bbox).coordinates[0]
callback({
type: 'geom',
//lon,lat -> lat, lon
bbox: [[bbox[0][0],bbox[0][1]], [bbox[2][0], bbox[2][1]]],
geometry_type: row.geometry_type,
simplified_geometry_type: simplifyType(row.geometry_type),
cluster_rate: row.clusterrate,
density: row.density
});
});
}
SQL.prototype.columns = function(sql, options, callback) {
var args = arguments,
fn = args[args.length -1];
if(_.isFunction(fn)) {
callback = fn;
}
var s = "select * from (" + sql + ") __wrap limit 0";
var exclude = ['cartodb_id','latitude','longitude','created_at','updated_at','lat','lon','the_geom_webmercator'];
this.execute(s, function(data) {
var t = {}
for (var i in data.fields) {
if (exclude.indexOf(i) === -1) {
t[i] = data.fields[i].type;
}
}
callback(t);
});
};
SQL.prototype.describeFloat = function(sql, column, callback) {
var s = [
'with stats as (',
'select min({{column}}) as min,',
'max({{column}}) as max,',
'avg({{column}}) as avg,',
'count(DISTINCT {{column}}) as cnt,',
'count(distinct({{column}})) as uniq,',
'count(*) as cnt,',
'sum(case when {{column}} is null then 1 else 0 end)::numeric / count(*)::numeric as null_ratio,',
'stddev_pop({{column}}) / count({{column}}) as stddev,',
'CASE WHEN abs(avg({{column}})) > 1e-7 THEN stddev({{column}}) / abs(avg({{column}})) ELSE 1e12 END as stddevmean,',
'CDB_DistType(array_agg("{{column}}"::numeric)) as dist_type ',
'from ({{sql}}) _wrap ',
'),',
'params as (select min(a) as min, (max(a) - min(a)) / 7 as diff from ( select {{column}} as a from ({{sql}}) _table_sql where {{column}} is not null ) as foo ),',
'histogram as (',
'select array_agg(row(bucket, range, freq)) as hist from (',
'select CASE WHEN uniq > 1 then width_bucket({{column}}, min-0.01*abs(min), max+0.01*abs(max), 100) ELSE 1 END as bucket,',
'numrange(min({{column}})::numeric, max({{column}})::numeric) as range,',
'count(*) as freq',
'from ({{sql}}) _w, stats',
'group by 1',
'order by 1',
') __wrap',
'),',
'hist as (',
'select array_agg(row(d, c)) cat_hist from (select distinct({{column}}) d, count(*) as c from ({{sql}}) __wrap, stats group by 1 limit 100) _a',
'),',
'buckets as (',
'select CDB_QuantileBins(array_agg(distinct({{column}}::numeric)), 7) as quantiles, ',
' (select array_agg(x::numeric) FROM (SELECT (min + n * diff)::numeric as x FROM generate_series(1,7) n, params) p) as equalint,',
// ' CDB_EqualIntervalBins(array_agg({{column}}::numeric), 7) as equalint, ',
' CDB_JenksBins(array_agg(distinct({{column}}::numeric)), 7) as jenks, ',
' CDB_HeadsTailsBins(array_agg(distinct({{column}}::numeric)), 7) as headtails ',
'from ({{sql}}) _table_sql where {{column}} is not null',
')',
'select * from histogram, stats, buckets, hist'
];
var query = Mustache.render(s.join('\n'), {
column: column,
sql: sql
});
this.execute(query, function(data) {
var row = data.rows[0];
var s = array_agg(row.hist);
var h = array_agg(row.cat_hist);
callback({
type: 'number',
cat_hist:
_(h).map(function(row) {
var r = row.match(/\((.*),(\d+)/);
return [+r[1], +r[2]];
}),
hist: _(s).map(function(row) {
if(row.indexOf("empty") > -1) return;
var els = row.split('"');
return { index: els[0].replace(/\D/g,''),
range: els[1].split(",").map(function(d){return d.replace(/\D/g,'')}),
freq: els[2].replace(/\D/g,'') };
}),
stddev: row.stddev,
null_ratio: row.null_ratio,
count: row.cnt,
distinct: row.uniq,
//lstddev: row.lstddev,
avg: row.avg,
max: row.max,
min: row.min,
stddevmean: row.stddevmean,
weight: (row.uniq > 1 ? 1 : 0) * (1 - row.null_ratio) * (row.stddev < -1 ? 1 : (row.stddev < 1 ? 0.5 : (row.stddev < 3 ? 0.25 : 0.1))),
quantiles: row.quantiles,
equalint: row.equalint,
jenks: row.jenks,
headtails: row.headtails,
dist_type: row.dist_type
});
});
}
// describe a column
SQL.prototype.describe = function(sql, column, options) {
var self = this;
var args = arguments,
fn = args[args.length -1];
if(_.isFunction(fn)) {
var _callback = fn;
}
var callback = function(data) {
data.column = column;
_callback(data);
}
var s = "select * from (" + sql + ") __wrap limit 0";
this.execute(s, function(data) {
var type = (options && options.type) ? options.type : data.fields[column].type;
if (!type) {
callback(new Error("column does not exist"));
return;
}
else if (type === 'string') {
self.describeString(sql, column, callback);
} else if (type === 'number') {
self.describeFloat(sql, column, callback);
} else if (type === 'geometry') {
self.describeGeom(sql, column, callback);
} else if (type === 'date') {
self.describeDate(sql, column, callback);
} else if (type === 'boolean') {
self.describeBoolean(sql, column, callback);
} else {
callback(new Error("column type is not supported"));
}
});
}
root.cartodb.SQL = SQL;
})();

View File

@@ -0,0 +1,49 @@
;(function() {
var root = this;
root.cartodb = root.cartodb || {};
var defaults = {
tiler_domain: "carto.com",
tiler_port: "80",
tiler_protocol: "http",
subdomains: ['{s}'],
extra_params: {
cache_policy: 'persist'
}
};
var Tiles = function(options) {
_.defaults(options, defaults);
if(!options.sublayers) {
throw new Error("sublayers should be passed");
}
if(!options.user_name) {
throw new Error("username should be passed");
}
options.layer_definition = LayerDefinition.layerDefFromSubLayers(options.sublayers);
options.ajax = reqwest.compat;
LayerDefinition.call(this, options.layer_definition, options);
};
_.extend(Tiles.prototype, LayerDefinition.prototype);
root.cartodb.Tiles = Tiles;
/**
* return the tile url template for the layer requested
*/
Tiles.getTiles = function(options, callback) {
var t = new Tiles(options);
t.getTiles(callback);
return t;
};
})();

View File

@@ -0,0 +1,35 @@
(function() {
cartodb.createVis = function(el, vizjson, options, callback) {
if (!el) {
throw new TypeError("a DOM element should be provided");
}
var
args = arguments,
fn = args[args.length -1];
if (_.isFunction(fn)) {
callback = fn;
}
el = (typeof el === 'string' ? document.getElementById(el) : el);
var vis = new cartodb.vis.Vis({ el: el });
if (vizjson) {
vis.load(vizjson, options);
if (callback) {
vis.done(callback);
}
}
return vis;
};
})();