cartodb/lib/assets/javascripts/deep-insights/widgets/time-series/torque-histogram-view.js
2020-06-15 10:58:47 +08:00

171 lines
5.7 KiB
JavaScript

var _ = require('underscore');
var HistogramView = require('./histogram-view');
var TorqueTimeSliderView = require('./torque-time-slider-view');
var TorqueControlsView = require('./torque-controls-view');
/**
* Torque time-series histogram view.
* Extends the common histogram chart view with time-control
* this.dataviewModel is a histogram model
*/
module.exports = HistogramView.extend({
defaults: _.extend({}, HistogramView.prototype.defaults, {offsetLeft: 34}),
className: function () {
return HistogramView.prototype.className + ' CDB-Widget-content CDB-Widget-content--torqueTimeSeries u-flex';
},
initialize: function () {
if (!this.options.torqueLayerModel) throw new Error('torqeLayerModel is required');
if (!this.options.rangeFilter) throw new Error('rangeFilter is required');
if (!this.options.dataviewModel) throw new Error('dataviewModel is required');
if (!this.options.timeSeriesModel) throw new Error('timeSeriesModel is required');
this._torqueLayerModel = this.options.torqueLayerModel;
this._dataviewModel = this.options.dataviewModel;
this._timeSeriesModel = this.options.timeSeriesModel;
HistogramView.prototype.initialize.call(this);
},
_initBinds: function () {
HistogramView.prototype._initBinds.call(this);
this.listenTo(this._torqueLayerModel, 'change:renderRange', this._onRenderRangeChanged);
this.listenTo(this._torqueLayerModel, 'change:steps change:start change:end', this._reSelectRange);
this.listenTo(this._torqueLayerModel, 'change:cartocss', this._onUpdateCartocss);
},
_createHistogramView: function () {
this._chartType = this._torqueLayerModel.get('column_type') === 'date' ? 'time' : 'number';
HistogramView.prototype._createHistogramView.call(this);
this._torqueControls = new TorqueControlsView({
torqueLayerModel: this._torqueLayerModel,
rangeFilter: this._rangeFilter
});
this.addView(this._torqueControls);
this.$el.prepend(this._torqueControls.render().el);
this._chartView.setAnimated();
this._chartView.bind('on_brush_click', this._onBrushClick, this);
this._timeSliderView = new TorqueTimeSliderView({
dataviewModel: this._dataviewModel, // a histogram model
chartView: this._chartView,
torqueLayerModel: this._torqueLayerModel,
timeSeriesModel: this._timeSeriesModel,
rangeFilter: this._rangeFilter
});
this.addView(this._timeSliderView);
this._timeSliderView.render();
},
_onChangeData: function () {
HistogramView.prototype._onChangeData.call(this);
if (this._chartView) {
this._reSelectRange();
}
},
_onRenderRangeChanged: function (_model, range) {
if (range.start === undefined && range.end === undefined) {
this._chartView.removeSelection();
this._rangeFilter.unsetRange();
}
},
_onBrushClick: function (indexPct) {
var steps = this._torqueLayerModel.get('steps');
var step = Math.round(steps * indexPct);
// Going to the last step causes a jump to the beginning immediately
if (step === steps) step -= 1;
HistogramView.prototype.resetFilter.apply(this);
this._torqueLayerModel.set({ step: step });
},
_onBrushEnd: function () {
HistogramView.prototype._onBrushEnd.apply(this, arguments);
this._reSelectRange();
},
_timeToStep: function (timestamp, place) {
var steps = this._torqueLayerModel.get('steps');
var start = this._torqueLayerModel.get('start');
var end = this._torqueLayerModel.get('end');
var step = (end - start) === 0
? place === 'max'
? steps
: 0
: (steps * (1000 * timestamp - start)) / (end - start);
step = Number(step.toFixed(2));
return step;
},
_reSelectRange: function (model) {
if (!this._rangeFilter.isEmpty()) {
this._torqueLayerModel.pause();
var min = this._rangeFilter.get('min');
var max = this._rangeFilter.get('max');
var loStep = this._timeToStep(min, 'min');
var hiStep = this._timeToStep(max, 'max');
// -- HACK: Reset filter if the min/max values are out of the scope
var data = this._dataviewModel.get('data');
var loBar = _.findWhere(data, { start: min });
var hiBar = _.findWhere(data, { end: max });
if (!loBar || !hiBar) {
return this._torqueLayerModel.resetRenderRange();
}
// clamp values since the range can be outside of the current torque thing
var steps = this._torqueLayerModel.get('steps');
var ratio = this._chartView.getSelectionExtent() / 100;
this._updateDuration(ratio);
this._torqueLayerModel.renderRange(
this._clampRangeVal(0, steps, loStep), // start
this._clampRangeVal(0, steps, hiStep) // end
);
} else {
this._torqueLayerModel.play();
this._updateDuration(1);
}
},
_updateDuration: function (ratio, cartocss) {
if (!this._torqueLayerModel.getAnimationDuration) return;
var duration = this._torqueLayerModel.getAnimationDuration(cartocss || this._torqueLayerModel.get('cartocss'));
this._torqueLayerModel.set('customDuration', Math.round(duration * ratio));
},
_onUpdateCartocss: function (m, cartocss) {
var ratio;
if (!this._rangeFilter.isEmpty()) {
var loStep = this._timeToStep(this._rangeFilter.get('min'));
var hiStep = this._timeToStep(this._rangeFilter.get('max'));
var steps = this._torqueLayerModel.get('steps');
ratio = (hiStep - loStep) / steps;
} else {
ratio = 1;
}
// Update silently, when carto.js updates the cartoCSS for torque, it will apply the new duration.
this._updateDuration(ratio, cartocss, { silent: true });
},
_clampRangeVal: function (a, b, t) {
return Math.max(a, Math.min(b, t));
},
_getMarginLeft: function () {
return 16;
}
});