Files
cartodb/carto.js/src/analysis/analysis-service.js
T
2020-06-15 10:58:47 +08:00

156 lines
5.2 KiB
JavaScript

var _ = require('underscore');
var Backbone = require('backbone');
var Analysis = require('./analysis-model');
var camshaftReference = require('./camshaft-reference');
var LayerTypes = require('../geo/map/layer-types.js');
var AnalysisService = function (opts) {
opts = opts || {};
if (!opts.engine) {
throw new Error('engine is required');
}
this._engine = opts.engine;
this._apiKey = opts.apiKey;
this._authToken = opts.authToken;
this._camshaftReference = opts.camshaftReference || camshaftReference; // For testing purposes
this._analysisNodes = new Backbone.Collection();
};
/**
* Recursively generates a graph of analyses and returns the "root" node.
* For each node definition in the analysisDefinition:
* - If a node had been already created this method updates the attributes of the existing node.
* - Otherwise create a new node and index it by id into the `_analysisNodes` object.
*/
AnalysisService.prototype.analyse = function (analysisDefinition) {
analysisDefinition = _.clone(analysisDefinition);
var analysis = this.findNodeById(analysisDefinition.id);
var analysisAttrs = this._getAnalysisAttributesFromAnalysisDefinition(analysisDefinition);
if (analysis) {
analysis.set(analysisAttrs);
} else {
if (this._apiKey) {
analysisAttrs.apiKey = this._apiKey;
}
if (this._authToken) {
analysisAttrs.authToken = this._authToken;
}
analysis = new Analysis(analysisAttrs, {
camshaftReference: this._camshaftReference,
engine: this._engine
});
this._analysisNodes.add(analysis);
analysis.bind('destroy', this._onAnalysisRemoved, this);
}
return analysis;
};
/**
* This function is used to iterate over the analysis graph.
* It uses the camshaft reference to extract those parameters which are analysis nodes. And call analyse on them.
*
* This function wont be needed if we split the analysis definition in `params` and `inputs`. Where all analysis
* are garanted to be in the inputs object.
*/
AnalysisService.prototype._getAnalysisAttributesFromAnalysisDefinition = function (analysisDefinition) {
var analysisNodes = {};
var analysisType = analysisDefinition.type;
var sourceNamesForAnalysisType = this._camshaftReference.getSourceNamesForAnalysisType(analysisType);
_.each(sourceNamesForAnalysisType, function (sourceName) {
var sourceParams = analysisDefinition.params[sourceName];
if (sourceParams) {
analysisNodes[sourceName] = this.analyse(sourceParams);
}
}, this);
return _.omit(_.extend(analysisDefinition, analysisDefinition.params, analysisNodes), 'params');
};
/**
* Create a source analysis
* This function is used because some legacy viz.json files contains layers without `source` and have a `query` field instead.
* This query is translated into a analysis of type `source`.
*/
AnalysisService.prototype.createAnalysisForLayer = function (layerId, layerQuery) {
return this.analyse({
id: layerId,
type: 'source',
params: {
query: layerQuery
}
});
};
AnalysisService.prototype.findNodeById = function (id) {
return this._analysisNodes.get(id);
};
AnalysisService.prototype._onAnalysisRemoved = function (analysis) {
this._analysisNodes.remove(analysis);
analysis.unbind('destroy', this._onAnalysisRemoved);
};
/**
* Return all the analysis nodes without duplicates.
* The analyses are obtained from the layers and dataviews collections.
* @example
* We have the following analyses: (a0->a1->a2), (b0->a2)
* This method will give us: (a0->a1->a2), (a1->a2), (a2), (b0->a2)
*/
AnalysisService.getUniqueAnalysisNodes = function (layersCollection, dataviewsCollection) {
var uniqueAnalysisNodes = {};
var analysisList = AnalysisService.getAnalysisList(layersCollection, dataviewsCollection);
_.each(analysisList, function (analysis) {
analysis.getNodesCollection().each(function (analysisNode) {
uniqueAnalysisNodes[analysisNode.get('id')] = analysisNode;
});
});
return _.map(uniqueAnalysisNodes, function (analisis) { return analisis; }, this);
};
/**
* Return a list with all the analyses contained in the given collections.
* @example
* We have the following analyses: (a0->a1->a2), (b0->a2)
* This method will give us: (a0->a1->a2), (b0->a2)
*/
AnalysisService.getAnalysisList = function (layersCollection, dataviewsCollection) {
var layerAnalyses = _getAnalysesFromLayers(layersCollection);
var dataviewsAnalyses = _getAnalysesFromDataviews(dataviewsCollection);
return layerAnalyses.concat(dataviewsAnalyses);
};
function _getAnalysesFromLayers (layersCollection) {
var layers = _getCartoDBAndTorqueLayers(layersCollection);
return _.chain(layers)
.map(function (layer) {
return layer.getSource();
})
.compact()
.value();
}
function _getAnalysesFromDataviews (dataviewsCollection) {
return dataviewsCollection.chain()
.map(function (dataview) {
return dataview.getSource();
})
.compact()
.value();
}
function _getCartoDBAndTorqueLayers (layersCollection) {
return layersCollection.filter(function (layer) {
// Carto and torque layers are supposed to have a source
return LayerTypes.isCartoDBLayer(layer) || LayerTypes.isTorqueLayer(layer);
});
}
module.exports = AnalysisService;