1
0
Fork 0
satnogs-network/network/static/js/observation_new.js

662 lines
30 KiB
JavaScript

/* global moment, d3, Slider, calcPolarPlotSVG */
$(document).ready( function(){
$('#advanced-options').click(function(){
if($('button span').hasClass('glyphicon-chevron-down')) {
$(this).html('<span class="glyphicon glyphicon-chevron-up"></span> Hide Advanced Options');
} else {
$(this).html('<span class="glyphicon glyphicon-chevron-down"></span> Show Advanced Options');
}
});
$('#default-horizon').click(function() {
$('#min-horizon').slider('destroy');
$('#min-horizon').remove();
});
$('#custom-horizon').click(function() {
$('#horizon-status').append('<input type="hidden" name="min-horizon" id="min-horizon"/>');
$('#min-horizon').slider({
id: 'min-horizon-slider',
min: 0,
max: 90,
step: 1,
value: 0,
ticks: [0, 30, 60, 90],
ticks_labels: ['0', '30', '60', '90']
});
});
function create_station_option(station){
return `
<option value="` + station.id + `"
data-content='<div class="station-option">
<span class="label label-` + station.status_display.toLowerCase() +`">
` + station.id +
`</span>
<span class="station-description">
` + station.name +
`</span>
</div>'>
</option>
`;
}
function select_proper_stations(filters, callback){
var url = '/scheduling_stations/';
var data = {'transmitter': filters.transmitter};
if (filters.station) {
data.station_id = filters.station;
}
$.ajax({
type: 'POST',
url: url,
data: data,
dataType: 'json',
beforeSend: function(xhr) {
xhr.setRequestHeader('X-CSRFToken', $('[name="csrfmiddlewaretoken"]').val());
$('#station-field').hide();
$('#station-field-loading').show();
}
}).done(function(data) {
if (data.length == 1 && data[0].error) {
$('#station-selection').html(`<option id="no-station"
value="" selected>
No station available
</option>`).prop('disabled', true);
$('#station-selection').selectpicker('refresh');
} else if (data.stations.length > 0) {
var stations_options = '';
if (filters.station && data.stations.findIndex(st => st.id == filters.station) > -1) {
var station = data.stations.find(st => st.id == filters.station);
stations_options = create_station_option(station);
$('#station-selection').html(stations_options);
$('#station-selection').selectpicker('val', filters.station);
$('#station-selection').selectpicker('refresh');
} else {
$.each(data.stations, function (i, station) {
stations_options += create_station_option(station);
});
$('#station-selection').html(stations_options).prop('disabled', false);
$('#station-selection').selectpicker('refresh');
$('#station-selection').selectpicker('selectAll');
}
} else {
$('#station-selection').html(`<option id="no-station"
value="" selected>
No station available
</option>`).prop('disabled', true);
$('#station-selection').selectpicker('refresh');
}
if (callback) {
callback();
}
$('#station-field-loading').hide();
$('#station-field').show();
});
}
function create_transmitter_option(satellite, transmitter){
return `
<option data-satellite="` + satellite + `"
value="` + transmitter.uuid + `"
data-success-rate="` + transmitter.success_rate + `"
data-content='<div class="transmitter-option">
<div class="transmitter-description">
` + transmitter.description + ' - ' + (transmitter.downlink_low/1e6).toFixed(3) + ' MHz - ' + transmitter.mode +
`</div>
<div class="progress">
<div class="progress-bar progress-bar-success transmitter-good"
data-toggle="tooltip" data-placement="bottom"
title="` + transmitter.success_rate + '% (' + transmitter.good_count + `) Good"
style="width:` + transmitter.success_rate + `%"></div>
<div class="progress-bar progress-bar-warning transmitter-unvetted"
data-toggle="tooltip" data-placement="bottom"
title="` + transmitter.unvetted_rate + '% (' + transmitter.unvetted_count + `) Unvetted"
style="width:` + transmitter.unvetted_rate + `%"></div>
<div class="progress-bar progress-bar-danger transmitter-bad"
data-toggle="tooltip" data-placement="bottom"
title="` + transmitter.bad_rate + '% (' + transmitter.bad_count + `) Bad"
style="width:` + transmitter.bad_rate + `%"></div>
<div class="progress-bar progress-bar-info transmitter-future"
data-toggle="tooltip" data-placement="bottom"
title="` + transmitter.future_rate + '% (' + transmitter.future_count + `) Future"
style="width:` + transmitter.future_rate + `%"></div>
</div>
</div>'>
</option>
`;
}
function select_proper_transmitters(filters, callback){
var url = '/transmitters/';
var data = {'satellite': filters.satellite};
if (filters.station) {
data.station_id = filters.station;
}
$.ajax({
type: 'POST',
url: url,
data: data,
dataType: 'json',
beforeSend: function(xhr) {
xhr.setRequestHeader('X-CSRFToken', $('[name="csrfmiddlewaretoken"]').val());
$('#transmitter-field').hide();
$('#transmitter-field-loading').show();
$('#station-field').hide();
$('#station-field-loading').show();
}
}).done(function(data) {
if (data.length == 1 && data[0].error) {
$('#transmitter-selection').html(`<option id="no-transmitter"
value="" selected>
No transmitter available
</option>`).prop('disabled', true);
$('#transmitter-selection').selectpicker('refresh');
} else if (data.transmitters.length > 0) {
var transmitters_options = '';
if (filters.transmitter){
var is_transmitter_available = (data.transmitters.findIndex(tr => tr.uuid == filters.transmitter) > -1);
if(is_transmitter_available) {
var transmitter = data.transmitters.find(tr => tr.uuid == filters.transmitter);
transmitters_options = create_transmitter_option(filters.satellite, transmitter);
$('#transmitter-selection').html(transmitters_options);
$('#transmitter-selection').selectpicker('val', filters.transmitter);
var hidden_input='<input type="hidden" name="transmitter" value="'+ filters.transmitter + '">';
$('#transmitter-selection').after(hidden_input);
filters.transmitter = transmitter.uuid;
} else {
$('#transmitter-selection').html(`<option id="no-transmitter" value="" selected>
No transmitter available
</option>`).prop('disabled', true);
delete filters.transmitter;
}
$('#transmitter-selection').selectpicker('refresh');
} else {
var max_good_count = 0;
var max_good_val = '';
$.each(data.transmitters, function (i, transmitter) {
if (max_good_count <= transmitter.good_count) {
max_good_count = transmitter.good_count;
max_good_val = transmitter.uuid;
}
transmitters_options += create_transmitter_option(filters.satellite, transmitter);
});
$('#transmitter-selection').html(transmitters_options).prop('disabled', false);
$('#transmitter-selection').selectpicker('refresh');
$('#transmitter-selection').selectpicker('val', max_good_val);
filters.transmitter = max_good_val;
}
$('.tle').hide();
$('.tle[data-norad="' + filters.satellite + '"]').show();
} else {
$('#transmitter-selection').html(`<option id="no-transmitter"
value="" selected>
No transmitter available
</option>`).prop('disabled', true);
$('#transmitter-selection').selectpicker('refresh');
}
if (callback) {
select_proper_stations(filters,callback);
} else {
select_proper_stations(filters);
}
$('#transmitter-field-loading').hide();
$('#transmitter-field').show();
});
}
var suggested_data = [];
var elevation_slider = new Slider('#elevation-filter', { id: 'elevation-filter', min: 0, max: 90, step: 1, range: true, value: [0, 90] });
function update_schedule_button_status(){
if($('#timeline rect').not('.unselected-obs').length != 0){
$('#schedule-observation').prop('disabled', false);
} else {
$('#schedule-observation').prop('disabled', true);
}
}
function filter_observations() {
var elmin = elevation_slider.getValue()[0];
var elmax = elevation_slider.getValue()[1];
$.each(suggested_data, function(i, station){
$.each(station.times, function(j, observation){
var obs_rect = $('#' + observation.id);
if(observation.elev_max > elmax || observation.elev_max < elmin){
observation.selected = false;
obs_rect.toggleClass('unselected-obs', true);
obs_rect.toggleClass('filtered-out', true);
obs_rect.css('cursor', 'default');
} else {
obs_rect.toggleClass('filtered-out', false);
obs_rect.css('cursor', 'pointer');
}
if(!obs_rect.hasClass('filtered-out') && !observation.selected){
station.selectedAll = false;
}
});
});
update_schedule_button_status();
}
elevation_slider.on('slideStop', function() {
filter_observations();
update_schedule_button_status();
});
$('#select-all-observations').on('click', function(){
$.each(suggested_data, function(i, station){
$.each(station.times, function(j, observation){
if(!$('#' + observation.id).hasClass('filtered-out')){
observation.selected = true;
$('#' + observation.id).toggleClass('unselected-obs', false);
}
});
station.selectedAll = true;
});
update_schedule_button_status();
});
$('#select-none-observations').on('click', function(){
$.each(suggested_data, function(i, station){
$.each(station.times, function(j, observation){
observation.selected = false;
$('#' + observation.id).toggleClass('unselected-obs', true);
});
station.selectedAll = false;
});
update_schedule_button_status();
});
$('#modal-schedule-observation').on('click', function() {
$(this).prop('disabled', true);
$('#schedule-observation').prop('disabled', true);
$('#calculate-observation').prop('disabled', true);
});
$('#schedule-observation').on('click', function() {
$('#windows-data').empty();
var obs_counter = 0;
var station_counter = 0;
var warn_min_obs = parseInt(this.dataset.warnMinObs);
var transmitter_uuid = $('#transmitter-selection').find(':selected').val();
$.each(suggested_data, function(i, station){
let obs_counted = obs_counter;
$.each(station.times, function(j, observation){
if(observation.selected){
var start = moment.utc(observation.starting_time).format('YYYY-MM-DD HH:mm:ss.SSS');
var end = moment.utc(observation.ending_time).format('YYYY-MM-DD HH:mm:ss.SSS');
$('#windows-data').append('<input type="hidden" name="obs-' + obs_counter + '-start" value="' + start + '">');
$('#windows-data').append('<input type="hidden" name="obs-' + obs_counter + '-end" value="' + end + '">');
$('#windows-data').append('<input type="hidden" name="obs-' + obs_counter + '-ground_station" value="' + station.id + '">');
$('#windows-data').append('<input type="hidden" name="obs-' + obs_counter + '-transmitter_uuid" value="' + transmitter_uuid + '">');
obs_counter += 1;
}
});
if(obs_counted < obs_counter){
station_counter += 1;
}
});
$('#windows-data').append('<input type="hidden" name="obs-TOTAL_FORMS" value="' + obs_counter + '">');
$('#windows-data').append('<input type="hidden" name="obs-INITIAL_FORMS" value="0">');
if(obs_counter > warn_min_obs){
$('#confirm-modal .counted-obs').text(obs_counter);
$('#confirm-modal .counted-stations').text(station_counter);
$('#confirm-modal').modal('show');
} else if (obs_counter != 0){
$(this).prop('disabled', true);
$('#calculate-observation').prop('disabled', true);
$('#form-obs').submit();
}
});
var obs_filter = $('#form-obs').data('obs-filter');
var obs_filter_dates = $('#form-obs').data('obs-filter-dates');
var obs_filter_station = $('#form-obs').data('obs-filter-station');
var obs_filter_satellite = $('#form-obs').data('obs-filter-satellite');
var obs_filter_transmitter = $('#form-obs').data('obs-filter-transmitter');
if (!obs_filter_dates) {
var minStart = $('#datetimepicker-start').data('date-minstart');
var minEnd = $('#datetimepicker-end').data('date-minend');
var maxRange = $('#datetimepicker-end').data('date-maxrange');
var minRange = minEnd - minStart;
var minStartDate = moment().utc().add(minStart, 'm').format('YYYY-MM-DD HH:mm');
var maxStartDate = moment().utc().add(minStart + maxRange - minRange, 'm').format('YYYY-MM-DD HH:mm');
var minEndDate = moment().utc().add(minEnd, 'm').format('YYYY-MM-DD HH:mm');
var maxEndDate = moment().utc().add(minStart + maxRange, 'm').format('YYYY-MM-DD HH:mm');
$('#datetimepicker-start').datetimepicker({
useCurrent: false //https://github.com/Eonasdan/bootstrap-datetimepicker/issues/1075
});
$('#datetimepicker-start').data('DateTimePicker').date(minStartDate);
$('#datetimepicker-start').data('DateTimePicker').minDate(minStartDate);
$('#datetimepicker-start').data('DateTimePicker').maxDate(maxStartDate);
$('#datetimepicker-end').datetimepicker({
useCurrent: false //https://github.com/Eonasdan/bootstrap-datetimepicker/issues/1075
});
$('#datetimepicker-end').data('DateTimePicker').date(minEndDate);
$('#datetimepicker-end').data('DateTimePicker').minDate(minEndDate);
$('#datetimepicker-end').data('DateTimePicker').maxDate(maxEndDate);
$('#datetimepicker-start').on('dp.change',function (e) {
var newMinEndDate = e.date.clone().add(minRange, 'm');
if ($('#datetimepicker-end').data('DateTimePicker').date() < newMinEndDate) {
$('#datetimepicker-end').data('DateTimePicker').date(newMinEndDate);
}
$('#datetimepicker-end').data('DateTimePicker').minDate(newMinEndDate);
});
}
function initiliaze_calculation(show_results){
if(show_results){
$('.calculation-result').show();
} else {
$('.calculation-result').hide();
}
$('#obs-selection-tools').hide();
$('#timeline').empty();
$('#hover-obs').hide();
$('#windows-data').empty();
$('#schedule-observation').prop('disabled', true);
$('#calculate-observation').prop('disabled', false);
}
$('#satellite-selection').on('changed.bs.select', function() {
var satellite = $(this).find(':selected').data('norad');
var station = $('#form-obs').data('obs-filter-station');
select_proper_transmitters({
satellite: satellite,
station: station
});
initiliaze_calculation(false);
});
$('#transmitter-selection').on('changed.bs.select', function() {
var transmitter = $(this).find(':selected').val();
var station = $('#form-obs').data('obs-filter-station');
select_proper_stations({
transmitter: transmitter,
station: station
});
initiliaze_calculation(false);
});
function sort_stations(a, b){
if( a.status > b.status){
return -1;
} else {
return a.id - b.id;
}
}
function calculate_observation(){
initiliaze_calculation(true);
var url = '/prediction_windows/';
var data = {};
// Add 5min for giving time to user schedule and avoid frequent start time errors.
// More on issue #686: https://gitlab.com/librespacefoundation/satnogs/satnogs-network/issues/686
if (!obs_filter_dates){
data.start = moment($('#datetimepicker-start input').val()).add(5,'minute').format('YYYY-MM-DD HH:mm');
} else {
data.start = $('#datetimepicker-start input').val();
}
data.end = $('#datetimepicker-end input').val();
data.transmitter = $('#transmitter-selection').find(':selected').val();
data.satellite = $('#satellite-selection').val();
data.stations = $('#station-selection').val();
if (data.satellite.length == 0) {
$('#windows-data').html('<span class="text-danger">You should select a Satellite first.</span>');
return;
} else if (data.transmitter.length == 0) {
$('#windows-data').html('<span class="text-danger">You should select a Transmitter first.</span>');
return;
} else if (data.stations.length == 0 || (data.stations.length == 1 && data.stations[0] == '')) {
$('#windows-data').html('<span class="text-danger">You should select a Station first.</span>');
return;
} else if (data.start.length == 0) {
$('#windows-data').html('<span class="text-danger">You should select a Start Time first.</span>');
return;
} else if (data.end.length == 0) {
$('#windows-data').html('<span class="text-danger">You should select an End Time first.</span>');
return;
}
var is_custom_horizon = $('#horizon-status input[type=radio]').filter(':checked').val() == 'custom';
if(is_custom_horizon) {
data.min_horizon = $('#min-horizon').val();
}
var trancate_overlapped = $('#overlapped input[type=radio]').filter(':checked').val() == 'truncate-overlapped';
if(trancate_overlapped) {
data.overlapped = 1;
}
$.ajax({
type: 'POST',
url: url,
data: data,
dataType: 'json',
beforeSend: function(xhr) {
xhr.setRequestHeader('X-CSRFToken', $('[name="csrfmiddlewaretoken"]').val());
$('#loading').show();
}
}).done(function(results) {
$('#loading').hide();
if (results.length == 1 && results[0].error) {
var error_msg = results[0].error;
$('#windows-data').html('<span class="text-danger">' + error_msg + '</span>');
} else {
suggested_data = [];
var dc = 0; // Data counter
$('#windows-data').empty();
results.sort(sort_stations);
$.each(results, function(i, k){
var label = k.id + ' - ' + k.name;
var times = [];
var selectedAll = true;
$.each(k.window, function(m, n){
var starting_time = moment.utc(n.start).valueOf();
var ending_time = moment.utc(n.end).valueOf();
var selected = false;
if(k.status !== 1 || obs_filter_station){
selected = true;
}
selectedAll = selectedAll && selected;
times.push({
starting_time: starting_time,
ending_time: ending_time,
az_start: n.az_start,
az_end: n.az_end,
elev_max: n.elev_max,
tle0: n.tle0,
tle1: n.tle1,
tle2: n.tle2,
selected: selected,
overlapped: n.overlapped,
id: k.id + '_' + times.length
});
dc = dc + 1;
});
if(times.length > 0){
suggested_data.push({
label: label,
id: k.id,
lat: k.lat,
lon: k.lng,
alt: k.alt,
selectedAll: selectedAll,
times: times
});
}
});
if (dc > 0) {
timeline_init(data.start, data.end, suggested_data);
} else {
var empty_msg = 'No Ground Station available for this observation window';
$('#windows-data').html('<span class="text-danger">' + empty_msg + '</span>');
}
}
});
}
function timeline_init(start, end, payload){
var start_timeline = moment.utc(start).valueOf();
var end_timeline = moment.utc(end).valueOf();
var period = end_timeline - start_timeline;
var tick_interval = 15;
var tick_time = d3.time.minutes;
if(period >= 86400000){
tick_interval = 2;
tick_time = d3.time.hours;
} else if(period >= 43200000){
tick_interval = 1;
tick_time = d3.time.hours;
} else if(period >= 21600000){
tick_interval = 30;
}
$('#hover-obs').hide();
$('#timeline').empty();
var chart = d3.timeline()
.beginning(start_timeline)
.ending(end_timeline)
.mouseout(function () {
$('#hover-obs').fadeOut(100);
})
.hover(function (d, i, datum) {
if(!$('#' + d.id).hasClass('filtered-out')){
var div = $('#hover-obs');
div.fadeIn(300);
var colors = chart.colors();
div.find('.coloredDiv').css('background-color', colors(i));
div.find('#name').text(datum.label);
div.find('#start').text(moment.utc(d.starting_time).format('YYYY-MM-DD HH:mm:ss'));
div.find('#end').text(moment.utc(d.ending_time).format('YYYY-MM-DD HH:mm:ss'));
div.find('#details').text('⤉ ' + d.az_start + '° ⇴ ' + d.elev_max + '° ⤈ ' + d.az_end + '°');
const groundstation = {
lat: datum.lat,
lon: datum.lon,
alt: datum.alt
};
const timeframe = {
start: new Date(d.starting_time),
end: new Date(d.ending_time)
};
const polarPlotSVG = calcPolarPlotSVG(timeframe,
groundstation,
d.tle1,
d.tle2);
const polarPlotAxes = `
<path fill="none" stroke="black" stroke-width="1" d="M 0 -95 v 190 M -95 0 h 190"/>
<circle fill="none" stroke="black" cx="0" cy="0" r="30"/>
<circle fill="none" stroke="black" cx="0" cy="0" r="60"/>
<circle fill="none" stroke="black" cx="0" cy="0" r="90"/>
<text x="-4" y="-96">N</text>
<text x="-4" y="105">S</text>
<text x="96" y="4">E</text>
<text x="-106" y="4">W</text>
`;
$('#polar-plot').html(polarPlotAxes);
$('#polar-plot').append(polarPlotSVG);
}
})
.click(function(d, i, datum){
if ($('rect').length == 1 && obs_filter_station) {
return;
}
if(Array.isArray(d)){
$.each(datum.times, function(i, observation){
if(!$('#' + observation.id).hasClass('filtered-out')){
observation.selected = !datum.selectedAll;
$('#' + observation.id).toggleClass('unselected-obs', !observation.selected);
}
});
datum.selectedAll = !datum.selectedAll;
} else {
var obs = $('#' + d.id);
if(!obs.hasClass('filtered-out')){
d.selected = !d.selected;
obs.toggleClass('unselected-obs', !d.selected);
if(!d.selected){
datum.selectedAll = false;
} else {
datum.selectedAll = true;
for(var j in datum.times){
if(!datum.times[j].selected){
datum.selectedAll = false;
break;
}
}
}
}
}
update_schedule_button_status();
})
.margin({left:140, right:10, top:0, bottom:50})
.tickFormat({format: d3.time.format.utc('%H:%M'), tickTime: tick_time, tickInterval: tick_interval, tickSize: 6})
.stack();
var svg_width = 1140;
if (screen.width < 1200) { svg_width = 940; }
if (screen.width < 992) { svg_width = 720; }
if (screen.width < 768) { svg_width = screen.width - 30; }
d3.select('#timeline').append('svg').attr('width', svg_width)
.datum(payload).call(chart);
$('g').find('rect').css({'stroke': 'black', 'cursor': 'pointer'});
$.each(suggested_data, function(i, station){
$.each(station.times, function(j, obs){
if(!obs.selected){
$('#' + obs.id).addClass('unselected-obs');
}
if(obs.overlapped){
$('#' + obs.id).css({'stroke': 'red'});
}
});
});
update_schedule_button_status();
if ($('rect').length > 1) {
$('#obs-selection-tools').show();
}
}
$('#calculate-observation').click( function(){
calculate_observation();
});
if (obs_filter && obs_filter_satellite) {
select_proper_transmitters({
satellite: obs_filter_satellite,
transmitter: obs_filter_transmitter,
station: obs_filter_station
}, function(){
if (obs_filter_dates && obs_filter_station && obs_filter_satellite){
$('#obs-selection-tools').hide();
$('#truncate-overlapped').click();
calculate_observation();
}
});
} else {
// Focus on satellite field
$('#satellite-selection').selectpicker('refresh');
$('#satellite-selection').selectpicker('toggle');
}
// Hotkeys bindings
$(document).bind('keyup', function(event){
if(document.activeElement.tagName != 'INPUT'){
if (event.which == 67) {
calculate_observation();
} else if (event.which == 83) {
var link_schedule = $('#schedule-observation');
link_schedule[0].click();
}
}
});
});