2017-01-03 10:42:22 -07:00
|
|
|
// D3 visualisation
|
2016-08-11 05:50:52 -06:00
|
|
|
|
2017-01-03 10:42:22 -07:00
|
|
|
d3.lineChart = function(telemetry_key, unit) {
|
2016-08-11 05:50:52 -06:00
|
|
|
var config = {
|
2016-11-20 10:20:23 -07:00
|
|
|
margin: {top: 20, right: 20, bottom: 115, left: 100},
|
2016-08-11 05:50:52 -06:00
|
|
|
width: 700,
|
|
|
|
height: 500
|
|
|
|
};
|
2017-01-03 10:42:22 -07:00
|
|
|
|
2016-08-11 05:50:52 -06:00
|
|
|
var svg;
|
|
|
|
|
2016-11-20 10:13:30 -07:00
|
|
|
// Define the div for the tooltip
|
|
|
|
var div = d3.select("body").append("div")
|
|
|
|
.attr("class", "chart-tooltip")
|
|
|
|
.style("opacity", 0);
|
2016-08-11 05:50:52 -06:00
|
|
|
|
2017-01-03 10:42:22 -07:00
|
|
|
function render(selection) {
|
|
|
|
selection.each(function(_data) {
|
2016-08-11 05:50:52 -06:00
|
|
|
var chartW = config.width - config.margin.left - config.margin.right,
|
|
|
|
chartH = config.height - config.margin.top - config.margin.bottom;
|
|
|
|
|
2016-11-24 07:03:36 -07:00
|
|
|
|
2016-08-11 05:50:52 -06:00
|
|
|
var x1 = d3.scale.ordinal()
|
|
|
|
.domain(_data.map(function(d, i){
|
2016-11-20 10:02:37 -07:00
|
|
|
return parseDate(d.telemetry.observation_datetime);
|
2016-08-11 05:50:52 -06:00
|
|
|
}))
|
2016-11-24 07:03:36 -07:00
|
|
|
.rangePoints([0, chartW]);
|
2016-08-11 05:50:52 -06:00
|
|
|
|
2017-01-04 03:49:43 -07:00
|
|
|
var y1;
|
|
|
|
|
|
|
|
switch(_data.length) {
|
|
|
|
case 1:
|
|
|
|
y1 = d3.scale.linear()
|
|
|
|
.domain([0, d3.max(_data, function(d, i){ return +d.telemetry.damod_data[telemetry_key]; })])
|
|
|
|
.range([chartH, 0])
|
|
|
|
.nice(4);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
y1 = d3.scale.linear()
|
|
|
|
.domain(d3.extent(_data, function(d, i){ return +d.telemetry.damod_data[telemetry_key]; }))
|
|
|
|
.range([chartH, 0])
|
|
|
|
.nice(4);
|
|
|
|
}
|
2016-08-11 05:50:52 -06:00
|
|
|
|
|
|
|
var xAxis = d3.svg.axis()
|
|
|
|
.scale(x1)
|
|
|
|
.orient('bottom');
|
|
|
|
|
|
|
|
var yAxis = d3.svg.axis()
|
|
|
|
.scale(y1)
|
|
|
|
.orient('left');
|
|
|
|
|
|
|
|
if(!svg) {
|
|
|
|
svg = d3.select(this)
|
|
|
|
.append('svg')
|
2017-01-04 03:19:17 -07:00
|
|
|
.classed('svg-chart', true);
|
2016-08-11 05:50:52 -06:00
|
|
|
var container = svg.append('g').classed('container-group', true);
|
|
|
|
container.append('g').classed('chart-group', true);
|
|
|
|
container.append('g').classed('x-axis-group axis', true);
|
|
|
|
container.append('g').classed('y-axis-group axis', true);
|
|
|
|
}
|
|
|
|
|
|
|
|
svg.transition().attr({width: config.width, height: config.height});
|
2016-11-20 10:15:32 -07:00
|
|
|
|
2016-08-11 05:50:52 -06:00
|
|
|
svg.select('.container-group')
|
|
|
|
.attr({transform: 'translate(' + config.margin.left + ',' + config.margin.top + ')'});
|
|
|
|
|
|
|
|
svg.select('.x-axis-group.axis')
|
|
|
|
.attr({transform: 'translate(0,' + (chartH) + ')'})
|
|
|
|
.transition()
|
|
|
|
.call(xAxis);
|
|
|
|
|
|
|
|
svg.select('.y-axis-group.axis')
|
|
|
|
.transition()
|
|
|
|
.call(yAxis);
|
|
|
|
|
2016-11-20 10:20:23 -07:00
|
|
|
svg.selectAll(".x-axis-group.axis text") // select all the text elements for the xaxis
|
|
|
|
.attr("transform", function(d) {
|
|
|
|
return "translate(-50,50)rotate(-45)";
|
|
|
|
});
|
2016-11-20 10:02:37 -07:00
|
|
|
|
|
|
|
// Axis labels
|
2016-08-11 05:50:52 -06:00
|
|
|
svg.append("text")
|
2016-11-20 10:02:37 -07:00
|
|
|
.attr("transform", "translate(" + (chartW + config.margin.right + 18) + " ," + (chartH + 10) + ")")
|
2016-08-11 05:50:52 -06:00
|
|
|
.style("text-anchor", "middle")
|
|
|
|
.text("Observation Datetime");
|
|
|
|
|
|
|
|
svg.append("text")
|
|
|
|
.attr("transform", "rotate(-90)")
|
2016-11-20 10:20:23 -07:00
|
|
|
.attr("y", 40)
|
2016-08-11 05:50:52 -06:00
|
|
|
.attr("x", 0 - (chartH / 2))
|
|
|
|
.attr("dy", "1em")
|
|
|
|
.style("text-anchor", "middle")
|
2016-11-20 10:11:19 -07:00
|
|
|
.text("Value (" + unit + ")");
|
2016-08-11 05:50:52 -06:00
|
|
|
|
2017-01-04 03:49:43 -07:00
|
|
|
switch(_data.length) {
|
|
|
|
case 1:
|
|
|
|
// Add the scatterplot
|
|
|
|
svg.selectAll("dot")
|
|
|
|
.data(_data)
|
|
|
|
.enter().append("circle")
|
|
|
|
.attr("r", 4)
|
|
|
|
.attr("cx", function(d, i) { return chartW / 2 + config.margin.left; })
|
|
|
|
.attr("cy", function(d) { return y1(d.telemetry.damod_data[telemetry_key]) + config.margin.top; })
|
|
|
|
.attr("class", "circle")
|
|
|
|
.on("mouseover", function(d) {
|
|
|
|
div.transition()
|
|
|
|
.duration(200)
|
|
|
|
.style("opacity", 1);
|
|
|
|
div.html(d.telemetry.damod_data[telemetry_key] + ' (' + unit + ')')
|
|
|
|
.style("left", (d3.event.pageX) + "px")
|
|
|
|
.style("top", (d3.event.pageY - 26) + "px");
|
|
|
|
})
|
|
|
|
.on("mouseout", function(d) {
|
|
|
|
div.transition()
|
|
|
|
.duration(500)
|
|
|
|
.style("opacity", 0);
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
var xInterval = chartW / (_data.length - 1);
|
|
|
|
|
|
|
|
// Define the line
|
|
|
|
var valueline = d3.svg.line()
|
|
|
|
.x(function(d,i) { return (xInterval*i + config.margin.left); })
|
|
|
|
.y(function(d) { return y1(d.telemetry.damod_data[telemetry_key]) + config.margin.top; });
|
|
|
|
|
|
|
|
// Add the valueline path
|
|
|
|
svg.append("path")
|
|
|
|
.attr("class", "line")
|
|
|
|
.attr("d", valueline(_data));
|
|
|
|
|
|
|
|
// Add the scatterplot
|
|
|
|
svg.selectAll("dot")
|
|
|
|
.data(_data)
|
|
|
|
.enter().append("circle")
|
|
|
|
.attr("r", 4)
|
|
|
|
.attr("cx", function(d, i) { return xInterval*i + config.margin.left; })
|
|
|
|
.attr("cy", function(d) { return y1(d.telemetry.damod_data[telemetry_key]) + config.margin.top; })
|
|
|
|
.attr("class", "circle")
|
|
|
|
.on("mouseover", function(d) {
|
|
|
|
div.transition()
|
|
|
|
.duration(200)
|
|
|
|
.style("opacity", 1);
|
|
|
|
div.html(d.telemetry.damod_data[telemetry_key] + ' (' + unit + ')')
|
|
|
|
.style("left", (d3.event.pageX) + "px")
|
|
|
|
.style("top", (d3.event.pageY - 26) + "px");
|
|
|
|
})
|
|
|
|
.on("mouseout", function(d) {
|
|
|
|
div.transition()
|
|
|
|
.duration(500)
|
|
|
|
.style("opacity", 0);
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2016-08-11 05:50:52 -06:00
|
|
|
});
|
|
|
|
}
|
2017-01-03 10:42:22 -07:00
|
|
|
return render;
|
2016-08-11 05:50:52 -06:00
|
|
|
};
|
|
|
|
|
2017-01-03 10:42:22 -07:00
|
|
|
// Retreive Satellite Id
|
2016-08-11 05:50:52 -06:00
|
|
|
|
2017-01-03 10:42:22 -07:00
|
|
|
var satelliteId = $('#telemetry-block').data('satid');
|
|
|
|
|
|
|
|
// Backbone Models
|
|
|
|
|
|
|
|
var TelemetryData = Backbone.Model.extend({});
|
|
|
|
|
|
|
|
// Backbone Collections
|
|
|
|
|
|
|
|
var TelemetryCollection = Backbone.Collection.extend({
|
|
|
|
url:"/api/telemetry/?satellite=" + satelliteId
|
|
|
|
});
|
|
|
|
|
|
|
|
var TelemetryDescriptors = TelemetryCollection.extend({
|
|
|
|
parse: function(response){
|
2017-01-04 10:55:03 -07:00
|
|
|
if(response.length !== 0) {
|
|
|
|
return response[0].appendix;
|
|
|
|
}
|
2017-01-03 10:42:22 -07:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
var TelemetryValues = TelemetryCollection.extend({
|
|
|
|
comparator: function(collection){
|
|
|
|
return( collection.get('telemetry').observation_datetime );
|
2017-01-03 11:34:26 -07:00
|
|
|
},
|
|
|
|
byDate: function (start_date, end_date) {
|
|
|
|
filtered = this.filter(function (model) {
|
|
|
|
var date = parseDateFilter(model.get('telemetry').observation_datetime);
|
2017-01-04 03:49:43 -07:00
|
|
|
return ( date >= start_date && date <= end_date );
|
2017-01-03 11:34:26 -07:00
|
|
|
});
|
|
|
|
return new TelemetryValues(filtered);
|
2017-01-03 10:42:22 -07:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Backbone Views
|
|
|
|
|
|
|
|
var TelemetryDescriptorsView = Backbone.View.extend({
|
|
|
|
el: "#telemetry-descriptors",
|
|
|
|
template: _.template($('#telemetryDescriptorsTemplate').html()),
|
|
|
|
initialize: function(){
|
|
|
|
this.listenTo(this.collection, 'add reset change remove', this.renderItem);
|
|
|
|
this.collection.fetch();
|
|
|
|
},
|
|
|
|
renderItem: function (model) {
|
|
|
|
this.$el.append(this.template(model.toJSON()));
|
|
|
|
$('#telemetry-descriptors li:first-child').addClass('active');
|
|
|
|
}
|
|
|
|
});
|
2016-08-11 05:50:52 -06:00
|
|
|
|
2017-01-03 10:42:22 -07:00
|
|
|
var TelemetryChartView = Backbone.View.extend({
|
2016-08-11 05:50:52 -06:00
|
|
|
el: ".chart",
|
|
|
|
chart: null,
|
|
|
|
chartSelection: null,
|
|
|
|
initialize: function() {
|
2017-01-03 10:42:22 -07:00
|
|
|
this.collection.fetch();
|
2017-01-04 10:55:03 -07:00
|
|
|
this.updateDates(moment().subtract(7, 'days').format('YYYY/MM/DD'), moment().format('YYYY/MM/DD'));
|
|
|
|
this.renderPlaceholder();
|
2017-01-04 02:50:49 -07:00
|
|
|
this.collection.on('update filter', this.render, this);
|
2017-01-03 10:42:22 -07:00
|
|
|
chart = d3.lineChart();
|
2016-08-11 05:50:52 -06:00
|
|
|
},
|
|
|
|
events: {
|
2017-01-04 02:50:49 -07:00
|
|
|
"click .telemetry-key": "updateKey",
|
2016-08-11 05:50:52 -06:00
|
|
|
},
|
|
|
|
render: function() {
|
2017-01-04 03:19:17 -07:00
|
|
|
if (this.collection.length > 0) {
|
|
|
|
$('#telemetry-descriptors').show();
|
|
|
|
$('#data-available').empty();
|
|
|
|
d3.select('svg').remove();
|
|
|
|
var data = this.collection.toJSON();
|
|
|
|
this.chartSelection = d3.select(this.el)
|
|
|
|
.datum(data)
|
|
|
|
.call(d3.lineChart(data[0].appendix[0].key, data[0].appendix[0].unit));
|
|
|
|
} else {
|
2017-01-04 10:55:03 -07:00
|
|
|
this.renderPlaceholder();
|
2017-01-04 03:19:17 -07:00
|
|
|
}
|
2016-08-11 05:50:52 -06:00
|
|
|
},
|
2017-01-04 10:55:03 -07:00
|
|
|
renderPlaceholder: function() {
|
|
|
|
$('#telemetry-descriptors').hide();
|
|
|
|
$('#data-available').html("<p>There is no data available for the selected dates.</p>");
|
|
|
|
d3.select('svg').remove();
|
|
|
|
},
|
2017-01-04 02:50:49 -07:00
|
|
|
updateKey: function(e){
|
2016-11-20 10:13:30 -07:00
|
|
|
d3.select('svg').remove();
|
2017-01-03 10:42:22 -07:00
|
|
|
this.chartSelection.call(d3.lineChart($(e.currentTarget).data("key"), $(e.currentTarget).data("unit")));
|
2016-11-20 10:14:31 -07:00
|
|
|
var active = $(e.currentTarget);
|
|
|
|
active.addClass('active');
|
|
|
|
$('li').not(active).removeClass('active');
|
2016-08-11 05:50:52 -06:00
|
|
|
},
|
2017-01-04 02:50:49 -07:00
|
|
|
updateDates: function(start_date, end_date){
|
|
|
|
this.collection = telemetryValues.byDate(start_date, end_date);
|
|
|
|
this.render();
|
|
|
|
},
|
2016-08-11 05:50:52 -06:00
|
|
|
});
|
|
|
|
|
|
|
|
// Fetch data and render views
|
|
|
|
|
|
|
|
var telemetryDescriptorsView = new TelemetryDescriptorsView({ collection: new TelemetryDescriptors() });
|
2017-01-04 02:50:49 -07:00
|
|
|
var telemetryValues = new TelemetryValues();
|
|
|
|
var telemetryChartView = new TelemetryChartView({collection: telemetryValues});
|
2017-01-03 10:42:22 -07:00
|
|
|
|
|
|
|
|
|
|
|
// Parse datetime values
|
|
|
|
|
|
|
|
function parseDate (date) {
|
|
|
|
var res = date.substring(4,6) + '/' + date.substring(6,8) + '/' + date.substring(0,4) + ' ' + date.substring(9 ,11) + ':' + date.substring(11 ,13) + ':' + date.substring(13 ,15);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2017-01-03 11:34:26 -07:00
|
|
|
function parseDateFilter (date) {
|
|
|
|
var res = date.substring(0,8);
|
|
|
|
return res;
|
2017-01-04 02:50:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
$('input[name="daterange"]').daterangepicker(
|
|
|
|
{
|
|
|
|
locale: {
|
|
|
|
format: 'YYYY/MM/DD'
|
|
|
|
},
|
2017-01-04 10:55:03 -07:00
|
|
|
dateLimit: {
|
|
|
|
"days": 60
|
|
|
|
},
|
|
|
|
autoApply: true,
|
|
|
|
startDate: moment().subtract(7, 'days').format('YYYY/MM/DD'),
|
|
|
|
endDate: moment().format('YYYY/MM/DD'),
|
2017-01-04 02:50:49 -07:00
|
|
|
},
|
|
|
|
function(start, end, label) {
|
|
|
|
telemetryChartView.updateDates(start.format('YYYYMMDD'), end.format('YYYYMMDD'));
|
|
|
|
}
|
|
|
|
);
|