Files
eacp_webapp/htdocs/js/dynamic_aircraftlist.js
T
2026-06-16 11:24:50 +08:00

506 lines
19 KiB
JavaScript

/********************************************************************
* Dynamic Aircraftlist for Radarcape
********************************************************************
* Guenter Koellner Embedded Development GmbH
*
* Am Rain 24
* 85256 Vierkirchen
* Geschaeftsfuehrer Guenter Koellner
*
* http://www.modesbeast.com
********************************************************************/
var refresh_time = 1;
var aircraftlist_body;
var aircraftlist_header;
var aircraftlist_row2clone;
var cookie_sort = "aclist_sort";
var sort = {
"key" : "hex",
"asc" : true,
"empty_on_top" : true
};
var cookie_colselect = "aclist_colselect";
const category = {
"A0":"A0: Unspecified powered aircraft",
"A1":"A1: Light (< 15500 lbs)",
"A2":"A2: Small (15500 to 75000 lbs)",
"A3":"A3: Large (75000 to 300000 lbs)",
"A4":"A4: High Vortex Large",
"A5":"A5: Heavy (> 300000 lbs)",
"A6":"A6: High Performance ( > 5 g acceleration and > 400 kts)",
"A7":"A7: Rotorcraft",
"B0":"B0: Unspecified unpowered aircraft or UAV or spacecraft",
"B1":"B1: Glider/Sailplane",
"B2":"B2: Lighter-than-Air",
"B3":"B3: Parachutist/Skydiver",
"B4":"B4: Ultralight/Hang Glider/Paraglider",
"B5":"B5: Reserved",
"B6":"B6: Unmanned Aerial Vehicle",
"B7":"B7: Space/Trans-atmospheric Vehicle",
"C0":"C0: Unspecified ground installation or vehicle",
"C1":"C1: Surface Vehicle - Emergency Vehicle",
"C2":"C2: Surface Vehicle - Service Vehicle",
"C3":"C3: Fixed Ground or Tethered Obstruction",
"F0":"F00: Flarm: Unknown",
"F1":"F01: Flarm: Glider",
"F2":"F02: Flarm: Tow Plane",
"F3":"F03: Flarm: Helicopter,Rotorcraft",
"F4":"F04: Flarm: Parachute",
"F5":"F05: Flarm: Drop Plane",
"F6":"F06: Flarm: Hang Glider",
"F7":"F07: Flarm: Para Glider",
"F8":"F08: Flarm: Powered Aircraft",
"F9":"F09: Flarm: Jet Aircraft",
"F10":"F10: Flarm: UFO",
"F11":"F11: Flarm: Balloon",
"F12":"F12: Flarm: Airship",
"F13":"F13: Flarm: UAV",
"F14":"F14: Flarm: Unused",
"F15":"F15: Flarm: Static Object"
}
var category_title = '';
for( var key in category ) {
category_title += category[key] + '\r\n';
}
var aclist = {
"reg": { "visible":true, "span":"Click on the link to show aircraft on planespotters.net", "title":"Registration", "align":"center"},
"hex": { "visible":true, "span":null, "title":"Mode-S", "align":"center"},
"fli": { "visible":true, "span":null, "title":"Flight", "align":"center"},
"orgh": { "visible":true, "span":null, "title":"Origin", "align":"center"},
"org": { "visible":true, "span":null, "title":"Origin<br/>Name", "align":"center"},
"dsth": { "visible":true, "span":null, "title":"Destination", "align":"center"},
"dst": { "visible":true, "span":null, "title":"Destination<br/>Name","align":"center"},
"trk": { "visible":true, "span":"True Track", "title":"Track", "align":"center"},
"hdgm": { "visible":true, "span":null, "title":"Magn.<br/>Heading","align":"center"},
"hdgt": { "visible":true, "span":null, "title":"True<br/>Heading", "align":"center"},
"alt": { "visible":true, "span":"Barometric Altitude [feet]", "title":"Altitude", "align":"center"},
"altg": { "visible":true, "span":"GNSS Elipsoidal Altitude [feet]", "title":"GNSS<br/>Altitude","align":"center"},
"spd": { "visible":true, "span":"Ground Speed [knots]", "title":"Speed", "align":"center"},
"ias": { "visible":true, "span":"Indicated Air Speed", "title":"&nbsp;IAS&nbsp;", "align":"center"},
"tas": { "visible":true, "span":"True Air Speed", "title":"&nbsp;TAS&nbsp;", "align":"center"},
"vrt": { "visible":true, "span":"Vertical Rate [feet per minute]", "title":"Vertical<br/>Rate","align":"center"},
"gda": { "visible":true, "span":"Aircraft on Ground or Airborne", "title":"GND/<br/>AIR", "align":"center"},
"typ": { "visible":true, "span":"Aircraft Type", "title":"Type", "align":"center"},
"cat": { "visible":true, "span":category_title, "title":"Category", "align":"center"},
"opr": { "visible":true, "span":null, "title":"Operator", "align":"center"},
"cou": { "visible":true, "span":null, "title":"Country", "align":"center"},
"src": { "visible":true, "span":"Source of Position Information", "title":"Source", "align":"center"},
"squ": { "visible":true, "span":null, "title":"Squawk", "align":"center"},
"lat": { "visible":true, "span":"Click on Latitude to show aircraft in Openlayers map", "title":"Latitude", "align":"center"},
"lon": { "visible":true, "span":"Click on Longitude to show aircraft in Gmap", "title":"Longitude", "align":"center"},
"dis": { "visible":true, "span":"Distance from Receiver", "title":"Distance", "align":"center"},
"spi": { "visible":true, "span":null, "title":"Indicator", "align":"center"},
"alr": { "visible":true, "span":"0=no alert, 1=temporary (Squawk changed previous 18s) 2=Mode-S alert 3=permanent (Squawk 7500,7600,7700)","title":"Alert", "align":"center"},
"ape": { "visible":true, "span":null, "title":"Autopilot<br/>engaged","align":"center"},
"tcm": { "visible":true, "span":"TCAS 0=not operational, 1: operational, 2: alert", "title":"TCAS", "align":"center"},
"qnhs": { "visible":true, "span":null, "title":"QNH<br/>sel.", "align":"center"},
"alts": { "visible":true, "span":null, "title":"Altitude<br/>sel.","align":"center"},
"hdgs": { "visible":true, "span":null, "title":"Heading<br/>sel.", "align":"center"},
"dbm": { "visible":true, "span":"Signal Level [dBm]", "title":"Signal<br/>Level", "align":"center"},
"tmp": { "visible":true, "span":null, "title":"Temp.<br/>°C", "align":"center"},
"wsp": { "visible":true, "span":null, "title":"Wind<br/>Speed", "align":"center"},
"wdi": { "visible":true, "span":null, "title":"Wind<br/>Direction","align":"center"},
"tim": { "visible":true, "span":null, "title":"Last Signal", "align":"center"},
"mop": { "visible":true, "span":"null=no ADS-B received, 0=default for unconfirmed version","title":"MOPS<br/>version","align":"center"},
"tru": { "visible":true, "span":"Number of Trustable DF-11/17/18 Messages", "title":"Trust", "align":"center"},
"pic": { "visible":true, "span":null, "title":"ASTERIX<br/>PIC", "align":"center"}
}
function aircraftlistSetVisibleColumns() {
var cells = aircraftlist_header.cells;
for (var key in aclist) {
cells.namedItem(key).style.display = aclist[key].visible ? 'table-cell' : 'none';
}
}
function createAircraftlist() {
// create table
//
var table = document.createElement("TABLE");
table.setAttribute("class", "table table-striped table-condensed table-more-condensed table-bordered table-hover");
table.style.whiteSpace = "pre-line";
var div = document.getElementById("div_aircraftlist");
div.appendChild(table);
// create table header
//
var header = table.createTHead();
aircraftlist_header = header.insertRow(-1);
for (var key in aclist) {
var cell = aircraftlist_header.insertCell(-1);
var contents = '<b>' + aclist[key].title + '</b></span><br/>';
//contents += '<div class="arrow_box" id="' + key + '"><div class="arrow_up"></div><div class="arrow_down"></div></div>';
contents += '<div class="arrow-box" id="' + key + '"><span class="arrow-up glyphicon glyphicon-triangle-top" aria-hidden="true"></span><span class="arrow-down glyphicon glyphicon-triangle-bottom" aria-hidden="true"></span></div>';
if( aclist[key].span!=null ) {
contents = '<span title="' + aclist[key].span + '">' + contents;
}
cell.innerHTML=contents;
cell.id = key;
cell.align = aclist[key].align;
cell.style.display = aclist[key].visible ? 'table-cell' : 'none';
}
// create table body and insert invisible row to clone
// https://stackoverflow.com/questions/6481948/how-do-i-create-the-tbody-tag-in-a-table-with-pure-javascript
aircraftlist_body = table.appendChild(document.createElement('tbody'));
aircraftlist_row2clone = aircraftlist_body.insertRow(-1);
for (var key in aclist) {
var cell = aircraftlist_row2clone.insertCell(-1);
cell.id = key;
cell.style.display = 'none';
cell.align = aclist[key].align;
}
}
function getCategoryLongtext(cat) {
var retval = cat;
if (category[cat])
retval = category[cat];
return retval;
}
function gda_longtext (gda) {
switch (gda.toUpperCase())
{
case 'A': return "Air";
case 'G': return "Ground";
case '?': return "Unk.";
default: return '?'+gda+'?';
}
}
function src_longtext (src) {
switch (src.toUpperCase())
{
case 'A': return "ADS-B";
case 'M': return "MLAT";
case 'L': return "FLARM";
case 'O': return "OGN";
case 'F': return "FlAwa";
case '?': return null;
case null: return null;
default: return '?'+src+'?';
}
}
function get_date_time_string(unix_timestamp, nanoseconds) {
// Create a new JavaScript Date object based on the timestamp
// multiplied by 1000 so that the argument is in milliseconds, not seconds.
var date = new Date(unix_timestamp * 1000);
// Hours part from the timestamp
var hours = date.getHours();
var minutes = "0" + date.getMinutes();
var seconds = "0" + date.getSeconds();
var formattedTime = date.toISOString();
formattedTime = formattedTime.replace("T", " ");
var ns = "000000000" + nanoseconds;
formattedTime = formattedTime.substr(0, 20) + ns.substr(-9); // formattedTime.substr(20)
return formattedTime;
}
function updateStatistics() {
getJSON(rcd_globals.fcgipath+"mapinfo.json", function(stat_data) {
refresh_time = ( typeof stat_data["refreshaclist"]!=='undefined' ) ? stat_data["refreshaclist"] : refresh_time;
var table = document.getElementById("statistic").tBodies[0];
var cells = table.rows[0].cells;
var txt = "";
txt += '<b>Total Aircraft:</b> ' + stat_data["dbsize"];
txt += ' ADS-B: ' + stat_data["num_adsb"];
txt += ' MLAT: ' + stat_data["num_mlat"];
txt += ' Flarm: ' + stat_data["num_flarm"];
txt += ' <b>Active:</b> ' + stat_data["watched"];
txt += ' ADS-B: ' + stat_data["num_active_adsb"];
txt += ' MLAT: ' + stat_data["num_active_mlat"];
txt += ' Flarm: ' + stat_data["num_active_flarm"];
cells.namedItem("statistic_text").innerHTML = txt;
});
}
function updateTable() {
getJSON(rcd_globals.fcgipath+"aircraftlist.json", function(unsorted_data) {
for ( var i = 0; i < unsorted_data.length; i++) {
unsorted_data[i].tim = get_date_time_string(unsorted_data[i].uti, unsorted_data[i].ns);
unsorted_data[i].orgh = resolveAirportById(unsorted_data[i].org).label;
unsorted_data[i].dsth = resolveAirportById(unsorted_data[i].dst).label;
unsorted_data[i].src = src_longtext(unsorted_data[i].src);
var optional_field;
for( optional_field of ['altg','spi','alr','ape','tcm','ias','tas','hdgm','hdgt','qnhs','alts','hdgs','altg','pic','mop'] ) {
if( unsorted_data[i][optional_field]==null ) { unsorted_data[i][optional_field] = null; }
}
}
var data = unsorted_data.sort(function(a, b) {
var x = a[sort.key];
var y = b[sort.key];
if( x==y ) {
return 0;
}
if( x==null || x==="" ) {
return sort.empty_on_top==true ? -1 : 1;
}
if( y==null || y==="" ) {
return sort.empty_on_top==false ? -1 : 1;
}
if( sort.asc ) {
return (x < y) ? -1 : 1;
} else {
return (x > y) ? -1 : 1;
}
});
var rows_len = aircraftlist_body.rows.length - 1; // first is row to clone
while( rows_len>data.length ) {
aircraftlist_body.removeChild(aircraftlist_body.rows[rows_len--]);
}
for ( var i=0; i<data.length; ++i) {
if (i >= rows_len) {
var line = aircraftlist_body.rows[0].cloneNode(true);
line.style = ""; // remove style="display:none;"
aircraftlist_body.appendChild(line);
} else {
var line = aircraftlist_body.rows[1 + i];
}
var cells = line.cells;
// manipulate registration: link to planespotters
if( data[i].reg==null ) {
data[i].reg="";
}
else if( data[i].reg.includes("state") ) {
}
else if( data[i].reg!="" ) {
data[i].reg = "<a href=\"https://planespotters.net/search?q=" + data[i].reg + "\" target=\"_blank\">" + data[i].reg + "</a>";
}
// manipulate lat and lon to links into internal maps
if (data[i].lat == null || data[i].lon == null) {
data[i].lat = "";
data[i].lon = "";
} else {
// fragment identifier / anchor
data[i].lat = "<a href=\"map.html#icao=" + data[i].hex + "\">" + data[i].lat.toFixed(5) + "</a>";
data[i].lon = "<a href=\"gmap.html#icao=" + data[i].hex + "\">" + data[i].lon.toFixed(5) + "</a>";
}
if( data[i].org ) { data[i].org = '<span title="' + resolveAirportById(data[i].org).text + '">' + data[i].org; }
if( data[i].dst ) { data[i].dst = '<span title="' + resolveAirportById(data[i].dst).text + '">' + data[i].dst; }
// manipulate Ground/Air, categorie and data source, distance
data[i].gda = data[i].gda==null ? "" : gda_longtext(data[i].gda);
data[i].cat = data[i].cat==null ? "" : '<span title="' + getCategoryLongtext(data[i].cat) + '">' + data[i].cat + '</span>';
data[i].dis = data[i].dis==null ? "" : data[i].dis.toFixed(1);
['gda','cat','src','reg','fli','hex','org','orgh','dst','dsth','trk','hdgm','hdgt','alt','altg','spd','ias','vrt','typ','opr','cou','squ','spi','alr','ape','tcm','lat','lon','tas','qnhs','alts','hdgs','dbm','tmp','tim','tru','mop','dis','wsp','wdi','pic'].forEach(
function(key) {
if( aclist[key].visible ) {
cells.namedItem(key).style.display = 'table-cell';
cells.namedItem(key).innerHTML = data[i][key];
} else {
cells.namedItem(key).style.display = 'none';
}
}
);
}
});
}
function FilterControl(controlDiv) {
var controlUI = document.createElement('div');
controlUI.className = 'colselect-icon';
controlUI.title = 'Click to modify the column select';
controlDiv.appendChild(controlUI);
// create a string that contains all IDs of the checkboxes
var all_checkbox_ids = ''
var first = true;
for (var key in aclist) {
all_checkbox_ids += (first? '' : ', ') + '#enable_' + key;
first = false;
}
all_checkbox_ids += ', #empty_on_top, #empty_on_bottom';
controlUI.addEventListener( 'click', function() {
if (document.getElementById('colselect-dialog') === null) {
var colselectDialog = document.createElement('div');
colselectDialog.id = 'colselect-dialog';
function update_checkboxes() {
for (var key in aclist) {
var input_field_id = '#enable_' + key;
$(input_field_id).prop('checked', aclist[key].visible);
}
}
// create a table which contains checkboxes for all keys
var select_table = '<div class="close" title="Close"></div>'
+ '<table>'
+ '<tr><td><input type="radio" name="modify_all" id="clear_all" checked></td><td><b>Clear All</b></td></tr>'
+ '<tr><td><input type="radio" name="modify_all" id="set_all"></td><td><b>Select All</b></td></tr>';
for (var key in aclist) {
var non_breaked_title = aclist[key].title.replace("<br/>", " ");
select_table += '<tr><td><input type="checkbox" id="enable_' + key + '"' + '></td><td>' + non_breaked_title + "</td></tr>";
}
select_table +='<tr><td><input type="radio" name="sort_empty" id="empty_on_top" checked></td><td><b>Sort empty fields to top</b></td></tr>'
+ '<tr><td><input type="radio" name="sort_empty" id="empty_on_bottom"></td><td><b>Sort empty fields to bottom</b></td></tr>'
+ '<tr><td></td><td><button id="colselect-apply">Apply &amp; Close</button></td></tr>'
+ '</table>';
colselectDialog.innerHTML = select_table;
controlDiv.appendChild(colselectDialog);
update_checkboxes();
function handleChange() {
for (var key in aclist) {
var input_field_id = "#enable_" + key;
aclist[key].visible = $(input_field_id).prop("checked") || false;
}
sort.empty_on_top = $("#empty_on_top").prop("checked") || false;
setCookie( cookie_sort, JSON.stringify(sort), 9999 );
colselect = {};
for (var key in aclist) { colselect[key] = aclist[key].visible; }
setCookie(cookie_colselect, JSON.stringify(colselect), 9999);
aircraftlistSetVisibleColumns();
}
$(all_checkbox_ids).change(handleChange);
function setAll() {
for (var key in aclist) { aclist[key].visible = true; }
update_checkboxes();
}
function clearAll() {
for (var key in aclist) { aclist[key].visible = false; }
update_checkboxes();
}
$("#set_all").change(setAll);
$("#clear_all").change(clearAll);
$("#colselect-dialog .close").click(function() {
handleChange();
$("#colselect-dialog").hide();
});
$("#colselect-dialog .clear").click(function() {
$(all_checkbox_ids).val(true);
handleChange();
});
$("#colselect-apply").click(function() {
handleChange();
$("#colselect-dialog").hide();
});
} else {
$("#colselect-dialog").show();
}
});
}
$(document).ready(function() {
console.log("dynamic_aircraftlist.js-->document ready");
// read cookies
// ------------
var tmp = [];
var cookie = getCookie(cookie_sort);
if( cookie ) {
$.extend( tmp, JSON.parse(cookie) );
}
for (var key in tmp) {
if( key in sort ) {
sort[key] = tmp[key];
}
}
tmp = [];
var cookie = getCookie(cookie_colselect);
if( cookie ) {
$.extend( tmp, JSON.parse(cookie) );
}
for (var key in tmp) {
if( key in aclist ) {
aclist[key].visible = tmp[key];
}
}
loadAirports();
createAircraftlist();
// create filter box
// -----------------
// https://stackoverflow.com/questions/18266231/display-a-small-image-at-the-top-right-corner
var filterControlDiv = document.getElementById("colselect-button");
var filterControl = new FilterControl(filterControlDiv);
// create event listeners
// ----------------------
$(".arrow-box .arrow-down").click(function(evt) {
sort.key = $(this).parent().attr("id");
sort.asc = false;
updateTable();
setCookie( cookie_sort, JSON.stringify(sort), 9999 );
});
$(".arrow-box .arrow-up").click(function(evt) {
sort.key = $(this).parent().attr("id");
sort.asc = true;
updateTable();
setCookie( cookie_sort, JSON.stringify(sort), 9999 );
});
$('body').on('click', '.hasicon > .icon', function() {
// console.log("Icon Clicked");
});
// execute
// -------
function loop() {
if( rcd_globals.initialized ) {
updateTable();
updateStatistics();
}
//else
// console.log(rcd_globals);
var delay = Math.max(1000, refresh_time*1000);
setTimeout( loop, delay );
}
loop();
});