// Some global variables are defined in early.js
// early.js takes care of getting some history files while the html page and
// some javascript libraries are still loading, hopefully speeding up loading
"use strict";
// Define our global variables
let webgl = false;
let webglSupported = false;
let webglFeatures = new ol.source.Vector();
let webglLayer;
let OLMap = null;
let OLProj = null;
let StaticFeatures = new ol.source.Vector();
let PlaneIconFeatures = new ol.source.Vector();
let trailGroup = new ol.Collection();
let iconLayer;
let trailLayers;
let heatFeatures = [];
let heatFeaturesSpread = 1024;
let heatLayers = [];
let realHeatFeatures = new ol.source.Vector();
let realHeat;
let iconCache = {};
let addToIconCache = [];
let lineStyleCache = {};
let Planes = {};
let PlanesOrdered = [];
let PlaneFilter = {};
let SelectedPlane = null;
let SelectedAllPlanes = false;
let HighlightedPlane = null;
let FollowSelected = false;
let noPan = false;
let loadFinished = false;
let mapResizeTimeout;
let pointerMoveTimeout;
let scaleFactor;
let debugTracks = false;
let debugAll = false;
let trackLabels = false;
let grouptype_checkbox;
let multiSelect = false;
let uat_data = null;
let enableLabels = false;
let extendedLabels = 0;
let mapIsVisible = true;
let tableInView = false;
let onlyMLAT = false;
let onlyMilitary = false;
let onlyADSB = false;
let onlySelected = false;
let onlyDataSource = null;
let fetchingPf = false;
let debug = false;
let debugJump = false;
let jumpTo = null;
let noMLAT = false;
let noVanish = false;
let filterTracks = false;
let refreshId = 0;
let globeIndexGrid = 0;
let globeIndexNow = {};
let globeIndexSpecialTiles;
let globeSimLoad = 4;
let globeTableLimit = 80;
let showGrid = false;
let lastGlobeExtent;
let PendingFetches = 0;
let debugCounter = 0;
let selectedPhotoCache = null;
let pathName = null;
let icaoFilter = null;
let showTrace = false;
let showTraceExit = false;
let showTraceWasIsolation = false;
let traceDate = null;
let traceDateString = null;
let traceDay = null;
let traceOpts = {};
let icaoParam = null;
let globalScale = 1;
let userScale = 1;
let iconScale = 1;
let labelScale = 1;
let newWidth = lineWidth;
let SiteOverride = false;
let airport = null;
let labelFill = null;
let blackFill = null;
let labelStroke = null;
let labelStrokeNarrow = null;
let bgFill = null;
let legSel = -1;
let geoMag = null;
let globalCompositeTested = false;
let solidT = false;
let lastActive = new Date().getTime();
let firstFetchDone = false;
let overrideMapType = null;
let halloween = false;
let noRegOnly = false;
let triggerMapRefresh = false;
let shareLink = '';
let onMobile = false;
let SpecialSquawks = {
'7500' : { cssClass: 'squawk7500', markerColor: 'rgb(255, 85, 85)', text: 'Aircraft Hijacking' },
'7600' : { cssClass: 'squawk7600', markerColor: 'rgb(0, 255, 255)', text: 'Radio Failure' },
'7700' : { cssClass: 'squawk7700', markerColor: 'rgb(255, 255, 0)', text: 'General Emergency' }
};
// Get current map settings
let CenterLat, CenterLon, ZoomLvl, ZoomLvlCache;
let zoomTimeout;
let noMovement;
let checkMoveZoom;
let checkMoveCenter = [0, 0];
let TrackedAircraft = 0;
let globeTrackedAircraft = 0;
let TrackedAircraftPositions = 0;
let TrackedHistorySize = 0;
let SitePosition = null;
// timestamps
let now = 0;
let last = 0;
let uat_now = 0;
let uat_last = 0;
let today = 0;
let FetchPending = [];
let FetchPendingUAT = null;
let MessageCountHistory = [];
let MessageRate = 0;
let layers;
let layers_group;
const nullStyle = new ol.style.Style({});
let estimateStyle;
let estimateStyleSlim;
let badLine;
let badLineMlat;
let badDot;
let badDotMlat;
// TAR1090 application object
let TAR;
TAR = (function (global, $, TAR) {
return TAR;
}(window, jQuery, TAR || {}));
function processAircraft(ac, init, uat) {
let isArray = Array.isArray(ac);
let hex = isArray ? ac[0] : ac.hex;
let plane = null;
// Do we already have this plane object in Planes?
// If not make it.
/*
if ( ac.messages < 2) {
return;
}
*/
if (icaoFilter && !icaoFilter.includes(hex))
return;
plane = Planes[hex];
if (uatNoTISB && uat && ac.type && ac.type.substring(0,4) == "tisb") {
// drop non ADS-B planes from UAT (TIS-B)
return;
}
if (!plane) {
plane = new PlaneObject(hex);
Planes[hex] = plane;
PlanesOrdered.push(plane);
if (uat) {
plane.receiver = "uat";
} else {
plane.receiver = "1090";
}
}
if (showTrace)
return;
// Call the function update
if (globeIndex) {
if (!onlyMilitary || plane.military)
plane.updateData(now, last, ac, init);
else
plane.last_message_time = now - ac.seen;
} else if (uat) {
if (plane.receiver == "uat" || ac.seen_pos < 1.8 || init) {
let tisb = Array.isArray(ac) ? (ac[7] == "tisb") : (ac.tisb != null && ac.tisb.indexOf("lat") >= 0);
if (tisb && plane.dataSource == "adsb") {
// ignore TIS-B data for current ADS-B 1090 planes
} else {
plane.receiver = "uat";
plane.updateData(uat_now, uat_last, ac, init);
}
}
} else {
if (plane.receiver == "1090"
|| (ac.seen_pos < 1.8 && (plane.seen_pos > 5 || !(ac.mlat && ac.mlat.indexOf("lat") >= 0)))
|| init) {
plane.receiver = "1090";
plane.updateData(now, last, ac, init);
}
}
}
function processReceiverUpdate(data, init) {
// update now and last
let uat = data.uat_978;
if (uat) {
if (data.now <= uat_now)
return;
uat_last = uat_now;
uat_now = data.now;
} else {
if (data.now <= now && !globeIndex)
return;
if (data.now > now) {
last = now;
now = data.now;
}
}
if (globeIndex) {
if (!binCraft && (showGrid || localStorage['globeGrid'] == 'true')
&& globeIndexNow[data.globeIndex] == null)
drawTileBorder(data);
if (binCraft)
wqi(data);
globeTrackedAircraft = data.global_ac_count_withpos;
globeIndexNow[data.globeIndex] = data.now;
}
if (!uat && !init && !globeIndex && !binCraft)
updateMessageRate(data);
// Loop through all the planes in the data packet
for (let j=0; j < data.aircraft.length; j++)
processAircraft(data.aircraft[j], init, uat);
}
function fetchData() {
if (heatmap)
return;
clearTimeout(refreshId);
refreshId = setTimeout(fetchData, refreshInt());
//console.log("fetch");
if (showTrace)
return;
if (PendingFetches > 0)
return;
for (let i in FetchPending) {
if (FetchPending[i] != null && FetchPending[i].state() == 'pending') {
// don't double up on fetches, let the last one resolve
return;
}
}
FetchPending = [];
if (FetchPendingUAT != null) {
// don't double up on fetches, let the last one resolve
return;
}
PendingFetches = 1;
//console.timeEnd("Starting Fetch");
//console.time("Starting Fetch");
if (enable_uat) {
FetchPendingUAT = $.ajax({ url: 'chunks/978.json',
dataType: 'json' });
FetchPendingUAT.done(function(data) {
uat_data = data;
FetchPendingUAT = null;
});
FetchPendingUAT.fail(function(jqxhr, status, error) {
FetchPendingUAT = null;
});
}
buttonActive('#F', FollowSelected);
let ac_url = [];
if (uuid != null) {
ac_url[0] = 'uuid/?feed=' + encodeURIComponent(uuid);
} else if (globeIndex) {
let indexes = globeIndexes();
indexes.sort(function(x,y) {
if (!globeIndexNow[x] && !globeIndexNow[y])
return 0;
if (globeIndexNow[x] == null)
return -1;
if (globeIndexNow[y] == null)
return 1;
return (globeIndexNow[x] - globeIndexNow[y]);
});
indexes = indexes.slice(0, globeSimLoad);
let suffix = binCraft ? '.binCraft' : '.json'
let mid = (binCraft && onlyMilitary) ? 'Mil_' : '_';
for (let i in indexes) {
ac_url.push('data/globe' + mid + indexes[i].toString().padStart(4, '0') + suffix);
}
} else {
ac_url[0] = 'data/aircraft.json';
}
PendingFetches = ac_url.length;
if (globeIndex) {
clearTimeout(refreshId);
refreshId = setTimeout(fetchData, 25000);
} else {
$("#lastLeg_cb").parent().hide();
}
for (let i in ac_url) {
//console.log(ac_url[i]);
let req;
if (binCraft) {
let xhrOverride = new XMLHttpRequest();
xhrOverride.responseType = 'arraybuffer';
req = $.ajax({
url: ac_url[i], method: 'GET',
xhr: function() { return xhrOverride; }
});
} else {
req = $.ajax({ url: ac_url[i], dataType: 'json' });
}
FetchPending.push(req);
req.done(function(data) {
if (data == null) {
return;
}
if (binCraft) {
data = { buffer: data, };
let ts = new Uint32Array(data.buffer, 0, 2);
data.now = ts[0] / 1000 + ts[1] * 4294967.296;
}
if (data.now >= now || globeIndex) {
//console.time("Process " + data.globeIndex);
processReceiverUpdate(data);
//console.timeEnd("Process " + data.globeIndex);
}
if (uat_data && uat_data.now > uat_now) {
processReceiverUpdate(uat_data);
uat_data = null;
}
if (PendingFetches <= 1) {
triggerMapRefresh = true;
}
PendingFetches--;
if (globeIndex) {
clearTimeout(refreshId);
refreshId = setTimeout(fetchData, refreshInt());
}
// Check for stale receiver data
if (last == now && !globeIndex) {
StaleReceiverCount++;
if (StaleReceiverCount > 5) {
$("#update_error_detail").text("The data from the server hasn't been updated in a while.");
$("#update_error").css('display','block');
}
} else if (StaleReceiverCount > 0){
StaleReceiverCount = 0;
$("#update_error").css('display','none');
}
if (!firstFetchDone) {
firstFetchDone = true;
if (uuid) {
followRandomPlane();
OLMap.getView().setZoom(6);
}
}
});
req.fail(function(jqxhr, status, error) {
$("#update_error_detail").text("AJAX call failed (" + status + (error ? (": " + error) : "") + ").");
console.log("AJAX call failed (" + status + (error ? (": " + error) : "") + ").");
console.log(jqxhr);
console.log(status);
console.log(error);
$("#update_error").css('display','block');
StaleReceiverCount++;
PendingFetches--;
clearTimeout(refreshId);
refreshId = setTimeout(fetchData, refreshInt());
});
}
}
// this function is called from index.html on body load
// kicks off the whole rabbit hole
function initialize() {
try {
if (new URLSearchParams(window.location.search).has('iconTest')) {
iconTest();
return;
}
} catch (error) {
console.log(error);
}
$.when(configureReceiver, heatmapDefer).done(function() {
configureReceiver = null;
// Initialize stuff
initPage();
initMap();
// Wait for history item downloads and append them to the buffer
push_history();
$.when(historyLoaded).done(function() {
startPage();
});
});
}
function initPage() {
onMobile = window.mobilecheck();
today = new Date().getDate();
let largeModeStorage = localStorage['largeMode'];
if (largeModeStorage != undefined && parseInt(largeModeStorage, 10)) {
largeMode = parseInt(largeModeStorage, 10);
}
try {
const search = new URLSearchParams(window.location.search);
if (search.has('showGrid'))
showGrid = true;
if (search.has('halloween'))
halloween = true;
if (search.has('onlyDataSource'))
onlyDataSource = search.get('onlyDataSource');
if (search.has('outlineWidth')) {
let tmp = parseInt(search.get('outlineWidth'));
if (!isNaN(tmp))
outlineWidth = tmp;
}
if (search.has('kiosk')) {
tempTrails = true;
hideButtons = true;
largeMode = 2;
}
if (search.has('pTracks')) {
noVanish = true;
buttonActive('#P', noVanish);
filterTracks = true;
selectAllPlanes();
}
if (search.has('largeMode')) {
let tmp = parseInt(search.get('largeMode'));
console.log(tmp);
if (!isNaN(tmp))
largeMode = tmp;
}
if (search.has('mobile'))
onMobile = true;
if (search.has('desktop'))
onMobile = false;
if (search.has('hideSidebar'))
localStorage['sidebar_visible'] = "false";
if (search.has('sidebarWidth')) {
localStorage['sidebar_width'] = search.get('sidebarWidth');
localStorage['sidebar_visible'] = "true";
}
if (search.has('SiteLat') && search.has('SiteLon')) {
localStorage['SiteLat'] = search.get('SiteLat');
localStorage['SiteLon'] = search.get('SiteLon');
}
if (localStorage['SiteLat'] != null && localStorage['SiteLon'] != null) {
if (search.has('SiteClear')
|| isNaN(parseFloat(localStorage['SiteLat']))
|| isNaN(parseFloat(localStorage['SiteLat']))) {
localStorage.removeItem('SiteLat');
localStorage.removeItem('SiteLon');
} else {
SiteLat = CenterLat = DefaultCenterLat = parseFloat(localStorage['SiteLat']);
SiteLon = CenterLon = DefaultCenterLon = parseFloat(localStorage['SiteLon']);
SiteOverride = true;
}
}
if (search.has('tempTrails')) {
tempTrails = true;
let tmp = parseInt(search.get('tempTrails'));
if (tmp > 0)
tempTrailsTimeout = tmp;
}
if (search.has('squareMania')) {
squareMania = true;
}
if (search.has('mapDim')) {
let dim = parseFloat(search.get('mapDim'));
if (!isNaN(dim))
mapDimPercentage = dim;
} else if (heatmap) {
mapDimPercentage = 0.6;
MapDim = true;
}
if (search.has('noRegOnly'))
noRegOnly = true;
if (search.has('mapContrast')) {
let contrast = parseFloat(search.get('mapContrast'));
if (!isNaN(contrast))
mapContrastPercentage = contrast;
}
if (search.has('iconScale')) {
let scale = parseFloat(search.get('iconScale'));
if (!isNaN(scale))
iconScale = scale;
}
if (search.has('labelScale')) {
let scale = parseFloat(search.get('labelScale'));
if (!isNaN(scale))
labelScale = scale;
}
if (search.has('scale')) {
let scale = parseFloat(search.get('scale'));
if (!isNaN(scale))
userScale = scale;
}
if (search.has('hideButtons'))
hideButtons = true;
if (search.has('baseMap'))
overrideMapType = search.get('baseMap');
icaoFilter = search.get('icaoFilter');
if (icaoFilter)
icaoFilter = icaoFilter.toLowerCase().split(',');
if (search.has('filterMaxRange')) {
let tmp = parseFloat(search.get('filterMaxRange'));
if (!isNaN(tmp))
filterMaxRange = tmp;
}
filterMaxRange *= 1852; // convert from nmi to meters
} catch (error) {
console.log(error);
}
if (onMobile)
enableMouseover = false;
if (false && iOSVersion() <= 12 && !('PointerEvent' in window)) {
$("#generic_error_detail").text("Enable Settings - Safari - Advanced - Experimental features - Pointer Events");
$("#generic_error").css('display','block');
setTimeout(function() {
$("#generic_error").css('display','none');
}, 30000);
}
if (adsbexchange) {
setInterval(function(){$.ajax({url:'data/receiver.json',cache:false,});}, 180000);
}
mapOrientation *= (Math.PI/180); // adjust to radians
if (localStorage['enableLabels'] == 'true'){
toggleLabels();
}
if (localStorage['extendedLabels']){
extendedLabels = parseInt(localStorage['extendedLabels']) + 2;
toggleExtendedLabels();
}
if (localStorage['trackLabels'] == "true") {
toggleTrackLabels();
}
if (localStorage['tableInView'] == "true") {
toggleTableInView(true);
}
if (localStorage['debug'] == "true")
debug = true;
if (localStorage['debugPosFilter'] == "true")
debugPosFilter = true;
if (localStorage['noMLAT'] == "true") {
// disable remembering this for now
//noMLAT = true;
//localStorage['noMLAT'] = "false";
}
if (localStorage['noVanish'] == "true") {
noVanish = true;
filterTracks = noVanish;
//localStorage['noVanish'] = "false";
buttonActive('#P', noVanish);
}
$('#tabs').tabs({
active: localStorage['active_tab'],
activate: function (event, ui) {
localStorage['active_tab'] = $("#tabs").tabs("option", "active");
},
collapsible: true
});
// Set page basics
document.title = PageName;
TAR.planesTable.init();
if (ExtendedData || window.location.hash == '#extended') {
$("#extendedData").removeClass("hidden");
}
// Set up map/sidebar splitter
$("#sidebar_container").resizable({
handles: {
w: '#splitter'
},
minWidth: 150,
maxWidth: ($(window).innerWidth() *0.8),
});
$("#splitter").dblclick(function() {
$('#sidebar_container').width('auto');
updateMapSize();
localStorage['sidebar_width'] = $('#sidebar_container').width();
$('#sidebar_container').width(localStorage['sidebar_width']);
});
if (localStorage['sidebar_width'] != null)
$('#sidebar_container').width(localStorage['sidebar_width']);
else
$('#sidebar_container').width('25%');
if ($('#sidebar_container').width() > $(window).innerWidth() *0.8)
$('#sidebar_container').width('30%');
localStorage['sidebar_width'] = $('#sidebar_container').width();
/*
// Set up datablock splitter
$('#selected_infoblock').resizable({
handles: {
s: '#splitter-infoblock'
},
containment: "#sidebar_container",
minHeight: 50
});
*/
$('#infoblock_close').on('click', function () {
if (showTrace) {
toggleShowTrace();
}
if (SelectedPlane) {
SelectedPlane.selected = null;
SelectedPlane.clearLines();
SelectedPlane.updateMarker();
SelectedPlane = null;
refreshSelected();
setSelectedInfoBlockVisibility();
TAR.planesTable.refresh();
updateAddressBar();
}
});
/*
// this is a little hacky, but the best, most consitent way of doing this. change the margin bottom of the table container to the height of the overlay
$('#selected_infoblock').on('resize', function() {
$('#sidebar_canvas').css('margin-bottom', $('#selected_infoblock').height() + 'px');
});
// look at the window resize to resize the pop-up infoblock so it doesn't float off the bottom or go off the top
$(window).on('resize', function() {
let topCalc = ($(window).height() - $('#selected_infoblock').height() - 25);
// check if the top will be less than zero, which will be overlapping/off the screen, and set the top correctly.
if (topCalc < 0) {
topCalc = 0;
$('#selected_infoblock').css('height', ($(window).height() - 25) +'px');
}
$('#selected_infoblock').css('top', topCalc + 'px');
});
*/
$('#sidebar_container').on('resize', function() {
localStorage['sidebar_width'] = $('#sidebar_container').width();
});
// Set up event handlers for buttons
$("#expand_sidebar_button").click(expandSidebar);
$("#shrink_sidebar_button").click(showMap);
$("#large_mode_button").click(toggleLargeMode);
// Initialize other controls
initializeUnitsSelector();
// Set up altitude filter button event handlers and validation options
$("#altitude_filter_form").submit(onFilterByAltitude);
$("#callsign_filter_form").submit(updateCallsignFilter);
$("#type_filter_form").submit(updateTypeFilter);
$("#description_filter_form").submit(updateDescriptionFilter);
$("#icao_filter_form").submit(updateIcaoFilter);
$("#search_form").submit(onSearch);
$("#jump_form").submit(onJump);
$("#show_trace").click(toggleShowTrace);
$("#trace_back_1d").click(function() {shiftTrace(-1)});
$("#trace_jump_1d").click(function() {shiftTrace(1)});
$("#leg_prev").click(function() {legShift(-1)});
$("#leg_next").click(function() {legShift(1)});
$("#altitude_filter_reset_button").click(onResetAltitudeFilter);
$("#callsign_filter_reset_button").click(onResetCallsignFilter);
$("#type_filter_reset_button").click(onResetTypeFilter);
$("#description_filter_reset_button").click(onResetDescriptionFilter);
$("#icao_filter_reset_button").click(onResetIcaoFilter);
$('#settingsCog').on('click', function() {
$('#settings_infoblock').toggle();
});
$('#settings_close').on('click', function() {
$('#settings_infoblock').hide();
});
$('#groundvehicle_filter').on('click', function() {
filterGroundVehicles(true);
refreshSelected();
refreshHighlighted();
TAR.planesTable.refresh();
mapRefresh();
});
$('#blockedmlat_filter').on('click', function() {
filterBlockedMLAT(true);
refreshSelected();
refreshHighlighted();
TAR.planesTable.refresh();
mapRefresh();
});
$('#grouptype_checkbox').on('click', function() {
if ($('#grouptype_checkbox').hasClass('settingsCheckboxChecked')) {
TAR.planesTable.cols.distance.sort();
} else {
TAR.planesTable.cols.data_source.sort();
}
});
new Toggle({
key: "lastLeg",
display: "Last Leg only",
container: "#settingsLeft",
init: true,
setState: function(state) {
lastLeg = state;
if (SelectedPlane && !showTrace)
SelectedPlane.processTrace();
}
});
if (onMobile) {
$('#large_mode_button').css('width', 'calc( 45px * let(--SCALE))');
$('#large_mode_button').css('height', 'calc( 45px * let(--SCALE))');
if (localStorage['largeMode'] == undefined && largeMode == 1)
largeMode = 2;
globeTableLimit = 40;
}
largeMode--;
toggleLargeMode();
$('#tStop').on('click', function() { traceOpts.replaySpeed = 0; });
$('#t1x').on('click', function() { traceOpts.replaySpeed = 1; legShift(); });
$('#t5x').on('click', function() { traceOpts.replaySpeed = 5; legShift(); });
$('#t10x').on('click', function() { traceOpts.replaySpeed = 10; legShift(); });
$('#t20x').on('click', function() { traceOpts.replaySpeed = 20; legShift(); });
$('#t40x').on('click', function() { traceOpts.replaySpeed = 40; legShift(); });
new Toggle({
key: "debugTracks",
display: "Debug Tracks",
container: "#settingsLeft",
init: false,
setState: function(state) {
debugTracks = state;
remakeTrails();
}
});
new Toggle({
key: "debugAll",
display: "Debug show all",
container: "#settingsLeft",
init: false,
setState: function(state) {
if (state)
debugAll = true;
else
debugAll = false;
}
});
new Toggle({
key: "ColoredPlanes",
display: "Colored Planes",
container: "#settingsRight",
init: true,
setState: function(state) {
if (state)
monochromeMarkers = null;
else
monochromeMarkers = "#EEEEEE";
refreshFeatures();
}
});
new Toggle({
key: "ColoredTrails",
display: "Colored Trails",
container: "#settingsRight",
init: true,
setState: function(state) {
if (state)
monochromeTracks = null;
else
monochromeTracks = "#000000";
remakeTrails();
}
});
new Toggle({
key: "sidebar_visible",
display: "Sidebar visible",
container: null,
checkbox: null,
button: '#toggle_sidebar_button',
init: (onMobile ? false : true),
setState: function (state) {
if (state) {
$("#sidebar_container").show();
$("#expand_sidebar_control").show();
$("#toggle_sidebar_button").removeClass("show_sidebar");
$("#toggle_sidebar_button").addClass("hide_sidebar");
} else {
$("#sidebar_container").hide();
$("#expand_sidebar_control").hide();
$("#toggle_sidebar_button").removeClass("hide_sidebar");
$("#toggle_sidebar_button").addClass("show_sidebar");
}
updateMapSize();
},
});
$('#selectall_checkbox').on('click', function() {
if ($('#selectall_checkbox').hasClass('settingsCheckboxChecked')) {
deselectAllPlanes();
} else {
selectAllPlanes();
}
})
// Force map to redraw if sidebar container is resized - use a timer to debounce
$("#sidebar_container").on("resize", function() {
clearTimeout(mapResizeTimeout);
mapResizeTimeout = setTimeout(updateMapSize, 20);
});
filterGroundVehicles(false);
filterBlockedMLAT(false);
TAR.altitudeChart.init();
if (adsbexchange) {
$('#adsbexchange_header').show();
if (window.self != window.top) {
window.top.location.href = "https://www.adsbexchange.com/"
return;
}
}
}
function push_history() {
$("#loader_progress").attr('max',nHistoryItems*2);
for (let i = 0; i < nHistoryItems; i++) {
push_history_item(i);
}
if (globeIndex) {
parseHistory();
} else if (!nHistoryItems) {
parseHistory();
console.log("History loading failed");
}
}
function push_history_item(i) {
$.when(deferHistory[i])
.done(function(json) {
if (HistoryChunks) {
if (json && json.files) {
for (let i in json.files) {
PositionHistoryBuffer.push(json.files[i]);
}
} else if (json && json.now) {
PositionHistoryBuffer.push(json);
}
} else {
PositionHistoryBuffer.push(json);
}
$("#loader_progress").attr('value',HistoryItemsReturned);
HistoryItemsReturned++;
if (HistoryItemsReturned == nHistoryItems) {
parseHistory();
}
})
.fail(function(jqxhr, status, error) {
//Doesn't matter if it failed, we'll just be missing a data point
$("#loader_progress").attr('value',HistoryItemsReturned);
//console.log(error);
HistoryItemsReturned++;
if (HistoryItemsReturned == nHistoryItems) {
parseHistory();
}
});
}
function parseHistory() {
if (nHistoryItems) {
console.timeEnd("Downloaded History");
console.time("Loaded aircraft tracks from History");
}
for (let i in deferHistory)
deferHistory[i] = null;
if (PositionHistoryBuffer.length > 0) {
// Sort history by timestamp
console.log("Sorting history: " + PositionHistoryBuffer.length);
PositionHistoryBuffer.sort(function(x,y) { return (y.now - x.now); });
// Process history
let data;
let h = 0;
let pruneInt = Math.floor(PositionHistoryBuffer.length/5);
let currentTime = new Date().getTime()/1000;
while (data = PositionHistoryBuffer.pop()) {
if (pTracks && currentTime - data.now > pTracks * 3600) {
continue;
}
// process new data
if (PositionHistoryBuffer.length < 10) {
processReceiverUpdate(data, false);
} else {
processReceiverUpdate(data, true);
}
// update aircraft tracks
if (data.uat_978 != "true") {
for (let i = 0; i < PlanesOrdered.length; ++i) {
let plane = PlanesOrdered[i];
if (plane.dataSource == "uat")
plane.updateTrack(uat_now, uat_last);
else
plane.updateTrack(now, last);
}
}
if (h==1) {
console.log("Applied history " + h + " from: "
+ (new Date(now * 1000)).toLocaleTimeString());
}
// prune aircraft list
if (h++ % pruneInt == pruneInt - 1) {
console.log("Applied history " + h + " from: "
+ (new Date(now * 1000)).toLocaleTimeString());
reaper();
}
}
// Final pass to update all planes to their latest state
console.log("Final history cleanup pass");
for (let i in PlanesOrdered) {
let plane = PlanesOrdered[i];
if (plane.position && SitePosition)
plane.sitedist = ol.sphere.getDistance(SitePosition, plane.position);
if (uatNoTISB && plane.receiver == "uat" && plane.type && plane.type.substring(0,4) == "tisb") {
plane.last_message_time -= 999;
}
}
refreshFeatures();
TAR.planesTable.refresh();
}
PositionHistoryBuffer = null;
if (nHistoryItems)
console.timeEnd("Loaded aircraft tracks from History");
historyLoaded.resolve();
}
function startPage() {
console.log("Completing init");
if (!globeIndex) {
$('#show_trace').hide();
}
if (globeIndex) {
$('#V').hide();
$('#uat_legend_2').hide();
$('#mode_s_legend_2').hide();
} else {
$('#unknown_legend_2').hide();
$('#sat_legend_2').hide();
}
if (hideButtons) {
$('#large_mode_control').hide();
$('#header_top').hide();
$('#header_side').hide();
$('#splitter').hide();
$('#tabs').hide();
$('#filterButton').hide();
$('.ol-control').hide();
$('.ol-attribution').show();
}
// Setup our timer to poll from the server.
window.setInterval(reaper, 20000);
if (tempTrails) {
window.setInterval(trailReaper, 10000);
trailReaper(now);
}
if (enable_pf_data) {
window.setInterval(fetchPfData, RefreshInterval*10.314);
}
//window.setInterval(TAR.planesTable.refresh, 1000);
//window.setInterval(function() {PendingFetches--;}, 10000);
setInterval(decrementTraceRate, 1000);
setInterval(updateIconCache, 800);
pathName = window.location.pathname;
processURLParams();
if (!icaoFilter && globeIndex)
toggleTableInView(true);
changeZoom("init");
changeCenter("init");
if (heatmap)
setInterval(checkMovement, 250);
else if (globeIndex)
setInterval(checkMovement, 80);
else
setInterval(checkMovement, 30);
// And kick off one refresh immediately.
if (!heatmap && !pTracks)
fetchData();
if (replay) {
//initReplay();
//play(); // kick off first play
}
loadFinished = true;
if (tempTrails)
selectAllPlanes();
geoMag = geoMagFactory(cof2Obj());
if (!heatmap)
$("#loader").addClass("hidden");
refreshSelected();
refreshHighlighted();
}
//
// Utils begin
//
(function (global, $, TAR) {
let utils = TAR.utils = TAR.utils || {};
// Make a LineString with 'points'-number points
// that is a closed circle on the sphere such that the
// great circle distance from 'center' to each point is
// 'radius' meters
utils.make_geodesic_circle = function (center, radius, points) {
const angularDistance = radius / 6378137.0;
const lon1 = center[0] * Math.PI / 180.0;
const lat1 = center[1] * Math.PI / 180.0;
let geom;
for (let i = 0; i <= points; ++i) {
const bearing = i * 2 * Math.PI / points;
let lat2 = Math.asin(Math.sin(lat1) * Math.cos(angularDistance) +
Math.cos(lat1) * Math.sin(angularDistance) * Math.cos(bearing));
let lon2 = lon1 + Math.atan2(Math.sin(bearing) * Math.sin(angularDistance) * Math.cos(lat1),
Math.cos(angularDistance) - Math.sin(lat1) * Math.sin(lat2));
lat2 = lat2 * 180.0 / Math.PI;
lon2 = lon2 * 180.0 / Math.PI;
if (!geom)
geom = new ol.geom.LineString([[lon2, lat2]]);
else
geom.appendCoordinate([lon2, lat2]);
}
return geom;
}
return TAR;
}(window, jQuery, TAR || {}));
//
// Utils end
//
function webglInit() {
try {
const canvas = document.createElement('canvas');
let gl = canvas.getContext("webgl");
webglSupported = true;
} catch (error) {
console.error(error);
console.log('disabling webGL support!');
webglSupported = false;
}
if (webglSupported) {
new Toggle({
key: "webgl",
display: "WebGL",
container: "#settingsLeft",
init: true,
setState: function(state) {
if (state)
webgl = true;
else
webgl = false;
},
});
let glStyle = {
symbol: {
symbolType: 'image',
src: 'images/sprites001.png',
size: [ 'get', 'size' ],
offset: [0, 0],
textureCoord: [ 'array',
[ 'get', 'cx' ],
[ 'get', 'cy' ],
[ 'get', 'dx' ],
[ 'get', 'dy' ]
],
color: [
'color',
[ 'get', 'r' ],
[ 'get', 'g' ],
[ 'get', 'b' ],
1
],
rotateWithView: false,
rotation: [ 'get', 'rotation' ],
},
};
webglLayer = new ol.layer.WebGLPoints({
name: 'webglLayer',
type: 'overlay',
title: 'Aircraft pos. webGL',
source: webglFeatures,
declutter: false,
zIndex: 200,
renderBuffer: 20,
style: glStyle,
});
layers.push(webglLayer);
}
}
// Initalizes the map and starts up our timers to call various functions
function initMap() {
if (receiverJson && receiverJson.lat != null) {
SiteLat = receiverJson.lat;
SiteLon = receiverJson.lon;
DefaultCenterLat = receiverJson.lat;
DefaultCenterLon = receiverJson.lon;
}
if (receiverJson && receiverJson.globeIndexGrid != null) {
globeIndexGrid = receiverJson.globeIndexGrid;
globeIndex = 1;
globeIndexSpecialTiles = receiverJson.globeIndexSpecialTiles;
$('#dump1090_total_history_td').hide();
$('#dump1090_message_rate_td').hide();
}
// Load stored map settings if present
CenterLon = Number(localStorage['CenterLon']) || DefaultCenterLon;
CenterLat = Number(localStorage['CenterLat']) || DefaultCenterLat;
ZoomLvl = Number(localStorage['ZoomLvl']) || DefaultZoomLvl;
ZoomLvlCache = ZoomLvl;
if (overrideMapType)
MapType_tar1090 = overrideMapType;
else if (localStorage['MapType_tar1090']) {
MapType_tar1090 = localStorage['MapType_tar1090'];
}
// Initialize OL3
layers_group = createBaseLayers();
layers = layers_group.getLayers();
layers.push(
new ol.layer.Vector({
name: 'site_pos',
type: 'overlay',
title: 'Site position and range rings',
source: StaticFeatures,
visible: !adsbexchange,
zIndex: 100,
renderOrder: null,
}));
trailLayers = new ol.layer.Group({
name: 'ac_trail',
title: 'Aircraft trails',
type: 'overlay',
layers: trailGroup,
zIndex: 150,
});
layers.push(trailLayers);
webglInit();
iconLayer = new ol.layer.Vector({
name: 'iconLayer',
type: 'overlay',
title: 'Aircraft positions',
source: PlaneIconFeatures,
declutter: false,
zIndex: 200,
renderBuffer: 20,
});
layers.push(iconLayer);
let foundType = false;
let baseCount = 0;
const dummyLayer = new ol.layer.Vector({
name: 'dummy',
renderOrder: null,
});
trailGroup.push(dummyLayer);
ol.control.LayerSwitcher.forEachRecursive(layers_group, function(lyr) {
if (!lyr.get('name'))
return;
if (lyr.get('type') === 'base') {
baseCount++;
if (MapType_tar1090 === lyr.get('name')) {
foundType = true;
lyr.setVisible(true);
} else {
lyr.setVisible(false);
}
lyr.on('change:visible', function(evt) {
if (evt.target.getVisible()) {
MapType_tar1090 = localStorage['MapType_tar1090'] = evt.target.get('name');
}
});
} else if (lyr.get('type') === 'overlay') {
let visible = localStorage['layer_' + lyr.get('name')];
if (visible != undefined) {
// javascript, why must you taunt me with gratuitous type problems
lyr.setVisible(visible === "true");
}
lyr.on('change:visible', function(evt) {
localStorage['layer_' + evt.target.get('name')] = evt.target.getVisible();
});
}
})
if (!foundType) {
ol.control.LayerSwitcher.forEachRecursive(layers_group, function(lyr) {
if (foundType)
return;
if (lyr.get('type') === 'base') {
lyr.setVisible(true);
foundType = true;
}
});
}
OLMap = new ol.Map({
target: 'map_canvas',
layers: layers,
view: new ol.View({
center: ol.proj.fromLonLat([CenterLon, CenterLat]),
zoom: ZoomLvl,
minZoom: 2,
}),
controls: [new ol.control.Zoom({delta: 1, duration: 0, target: 'map_container',}),
new ol.control.Attribution({collapsed: true}),
new ol.control.ScaleLine({units: DisplayUnits})
],
interactions: new ol.interaction.defaults({altShiftDragRotate:false, pinchRotate:false,}),
});
OLProj = OLMap.getView().getProjection();
OLMap.getView().setRotation(mapOrientation); // adjust orientation
if (baseCount > 1) {
OLMap.addControl(new ol.control.LayerSwitcher({
groupSelectStyle: 'none',
target: 'map_container',
}));
}
/*
// Listeners for newly created Map
OLMap.getView().on('change:center', function(event) {
const center = ol.proj.toLonLat(OLMap.getView().getCenter(), OLMap.getView().getProjection());
CenterLat = center[1];
CenterLon = center[0];
if (FollowSelected) {
// On manual navigation, disable follow
if (!SelectedPlane || !SelectedPlane.position ||
(Math.abs(center[0] - SelectedPlane.position[0]) > 0.0001 &&
Math.abs(center[1] - SelectedPlane.position[1]) > 0.0001)){
FollowSelected = false;
refreshSelected();
refreshHighlighted();
}
}
});
*/
/*
OLMap.getView().on('change:resolution', function(event) {
ZoomLvl = OLMap.getView().getZoom();
});
*/
OLMap.on(['click', 'dblclick'], function(evt) {
let res = evt.map.forEachFeatureAtPixel(
evt.pixel,
function(feature, layer) {
if (showTrace)
return feature.timestamp;
return feature.hex;
},
{
layerFilter: function(layer) {
return (layer == webglLayer || layer == iconLayer || layer.get('isTrail') == true);
},
hitTolerance: 6 * globalScale,
}
);
if (showTrace && res) {
gotoTime(res);
} else if (res) {
selectPlaneByHex(res, {follow: (evt.type === 'dblclick')});
} else if (!multiSelect) {
deselectAllPlanes();
}
evt.stopPropagation();
});
// show the hover box
if (!globeIndex && ZoomLvl > 5.5 && enableMouseover) {
OLMap.on('pointermove', onPointermove);
}
// handle the layer settings pane checkboxes
OLMap.once('postrender', function(e) {
toggleLayer('#nexrad_checkbox', 'nexrad');
//toggleLayer('#sitepos_checkbox', 'site_pos');
toggleLayer('#actrail_checkbox', 'ac_trail');
//toggleLayer('#acpositions_checkbox', 'webglLayer');
});
new Toggle({
key: "MapDim",
display: "Dim Map",
container: "#settingsLeft",
init: MapDim,
setState: function(state) {
if (!state) {
ol.control.LayerSwitcher.forEachRecursive(layers_group, function(lyr) {
if (lyr.get('type') != 'base')
return;
ol.Observable.unByKey(lyr.dimKey);
});
} else {
ol.control.LayerSwitcher.forEachRecursive(layers_group, function(lyr) {
if (lyr.get('type') != 'base')
return;
lyr.dimKey = lyr.on('postrender', dim);
});
}
OLMap.render();
buttonActive('#B', state);
}
});
window.addEventListener('keydown', function(e) {
active();
if (e.defaultPrevented ) {
return; // Do nothing if the event was already processed
}
if (e.target.type == "text") {
return;
}
if (e.srcElement.nodeName == 'INPUT') {
return;
}
if( e.ctrlKey || e.altKey || e.metaKey) {
return;
}
let oldCenter, extent, newCenter;
switch (e.key) {
case "c":
case "Esc":
case "Escape":
deselectAllPlanes();
break;
// zoom and movement
case "q":
zoomOut();
break;
case "e":
zoomIn();
break;
case "w":
oldCenter = OLMap.getView().getCenter();
extent = OLMap.getView().calculateExtent(OLMap.getSize());
newCenter = [oldCenter[0], (oldCenter[1] + extent[3])/2];
OLMap.getView().setCenter(newCenter);
toggleFollow(false);
break;
case "s":
oldCenter = OLMap.getView().getCenter();
extent = OLMap.getView().calculateExtent(OLMap.getSize());
newCenter = [oldCenter[0], (oldCenter[1] + extent[1])/2];
OLMap.getView().setCenter(newCenter);
toggleFollow(false);
break;
case "a":
oldCenter = OLMap.getView().getCenter();
extent = OLMap.getView().calculateExtent(OLMap.getSize());
newCenter = [(oldCenter[0] + extent[0])/2, oldCenter[1]];
OLMap.getView().setCenter(newCenter);
toggleFollow(false);
break;
case "d":
oldCenter = OLMap.getView().getCenter();
extent = OLMap.getView().calculateExtent(OLMap.getSize());
newCenter = [(oldCenter[0] + extent[2])/2, oldCenter[1]];
OLMap.getView().setCenter(newCenter);
toggleFollow(false);
break;
// misc
case "b":
toggles['MapDim'].toggle();
break;
case "m":
toggleMultiSelect();
break;
case "v":
toggleTableInView();
break;
case "r":
if (heatmap && !replay)
drawHeatmap();
else
followRandomPlane();
break;
case "R":
fetchData();
break;
case "t":
selectAllPlanes();
break;
case "h":
resetMap();
break;
case "H":
if (!hideButtons) {
$('#large_mode_control').hide();
$('#header_top').hide();
$('#header_side').hide();
$('#splitter').hide();
$('#tabs').hide();
$('#filterButton').hide();
$('.ol-control').hide();
$('.ol-attribution').show();
} else {
$('#large_mode_control').show();
$('#header_top').show();
$('#header_side').show();
$('#splitter').show();
$('#tabs').show();
$('#filterButton').show();
$('.ol-control').show();
$('#expand_sidebar_control').hide();
toggles['sidebar_visible'].restore();
TAR.altitudeChart.render();
}
hideButtons = !hideButtons;
break;
case "f":
toggleFollow();
break;
// filters
case "M":
onlyMLAT = !onlyMLAT;
TAR.planesTable.refresh();
mapRefresh();
break;
case "T":
filterTISB = !filterTISB;
break;
case "u":
toggleMilitary();
break;
case "A":
onlyADSB = !onlyADSB;
break;
// persistance mode
case "i":
toggleIsolation();
break;
case "p":
togglePersistence();
break;
// Labels
case "l":
toggleLabels();
break;
case "o":
toggleExtendedLabels();
break;
case "k":
toggleTrackLabels();
break;
// debug stuff
case "L":
toggles['lastLeg'].toggle();
break;
case "D":
debug = !debug;
localStorage['debug'] = debug;
console.log('debug = ' + debug);
break;
case "P":
debugPosFilter = !debugPosFilter;
localStorage['debugPosFilter'] = debugPosFilter;
console.log('debugPosFilter = ' + debugPosFilter);
break;
case "?":
if (!SelectedPlane) {
console.log("No plane selected");
break;
}
console.log(SelectedPlane.icao + ": " + SelectedPlane.baseMarkerKey + " " + SelectedPlane.shape);
console.log(SelectedPlane);
console.log(SelectedPlane.milRange());
break;
case "j":
selectPlaneByHex(jumpTo, {follow: true});
break;
case "J":
debugJump = !debugJump;
localStorage['debugJump'] = debugJump;
console.log('debugJump = ' + debugJump);
break;
case "N":
noMLAT = !noMLAT;
localStorage['noMLAT'] = noMLAT;
console.log('noMLAT = ' + noMLAT);
break;
}
}, true);
if (globeIndex || uuid || askLocation)
geoFindMe();
else {
initSitePos();
}
}
// This looks for planes to reap out of the master Planes variable
function reaper(all) {
//console.log("Reaping started..");
today = new Date().getDate();
if (noVanish)
return;
// Look for planes where we have seen no messages for >300 seconds
let plane;
let length = PlanesOrdered.length;
for (let i = 0; i < length; i++) {
plane = PlanesOrdered.shift()
if (plane == null)
continue;
plane.seen = now - plane.last_message_time;
if ( (!plane.selected || SelectedAllPlanes)
&& (all || plane.seen > 300)
&& (plane.dataSource != 'adsc' || plane.seen > 35*60)
) {
// Reap it.
//console.log("Removed " + plane.icao);
delete Planes[plane.icao];
plane.destroy();
} else {
// Keep it.
PlanesOrdered.push(plane);
}
};
//console.log(length - PlanesOrdered.length);
return (length - PlanesOrdered.length);
}
// Page Title update function
function refreshPageTitle() {
if (pTracks)
return;
if (!PlaneCountInTitle && !MessageRateInTitle) {
return;
}
let subtitle = "";
if (PlaneCountInTitle) {
if (globeIndex) {
subtitle += 'tracking ' + globeTrackedAircraft + ' aircraft';
} else {
subtitle += TrackedAircraftPositions + '/' + TrackedAircraft;
}
}
if (MessageRateInTitle && MessageRate != null) {
if (subtitle) subtitle += ' | ';
subtitle += MessageRate.toFixed(1) + '/s';
}
document.title = PageName + ' - ' + subtitle;
}
let selCall = null;
let selIcao = null;
let selReg = null;
// Refresh the detail window about the plane
function refreshSelected() {
buttonActive('#F', FollowSelected);
/*
if (SelectedPlane && SelectedPlane.isFiltered()) {
SelectedPlane.selected = false;
SelectedPlane.clearLines();
SelectedPlane = null;
}
*/
if (!SelectedPlane) {
setSelectedInfoBlockVisibility();
return;
}
const selected = SelectedPlane;
if (SelectedPlane.position)
SelectedPlane.updateMarker(true);
if (selected.flight != selCall) {
selCall = selected.flight;
if (selected.flight && selected.flight.trim()) {
$('#selected_callsign').text(selected.flight);
} else {
$('#selected_callsign').text('n/a');
}
}
if (flightawareLinks) {
$('#selected_flightaware_link').html(getFlightAwareModeSLink(selected.icao, selected.flight, "Visit Flight Page"));
}
if (selected.isNonIcao() && selected.source != 'mlat') {
$('#anon_mlat_info').addClass('hidden');
$('#reg_info').addClass('hidden');
$('#tisb_info').removeClass('hidden');
} else if (selected.isNonIcao() && selected.source == 'mlat') {
$('#reg_info').addClass('hidden');
$('#tisb_info').addClass('hidden');
$('#anon_mlat_info').removeClass('hidden');
} else {
$('#tisb_info').addClass('hidden');
$('#anon_mlat_info').addClass('hidden');
$('#reg_info').removeClass('hidden');
}
if (selected.registration != selReg) {
selReg = selected.registration
if (selected.registration) {
if (flightawareLinks) {
$('#selected_registration').html(getFlightAwareIdentLink(selected.registration, selected.registration));
} else {
$('#selected_registration').text(selected.registration);
}
} else {
$('#selected_registration').text("n/a");
}
}
if (selected.icaoType) {
$('#selected_icaotype').text(selected.icaoType);
} else {
$('#selected_icaotype').text("n/a");
}
if (selected.typeDescription)
$('#selected_typedesc').text(selected.typeDescription);
else
$('#selected_typedesc').text("n/a");
if (selected.typeLong)
$('#selected_typelong').text(selected.typeLong);
else
$('#selected_typelong').text("n/a");
if (showPictures && selected.icaoType){
let new_html = "
";
if (new_html != selectedPhotoCache) {
$('#selected_photo').html(new_html);
selectedPhotoCache = new_html;
}
} else {
$('#selected_photo').text("");
}
$("#selected_altitude1").text(format_altitude_long(selected.altitude, selected.vert_rate, DisplayUnits));
$("#selected_altitude2").text(format_altitude_long(selected.altitude, selected.vert_rate, DisplayUnits));
$('#selected_onground').text(format_onground(selected.altitude));
if (selected.squawk == null || selected.squawk == '0000') {
$('#selected_squawk1').text('n/a');
$('#selected_squawk2').text('n/a');
} else {
$('#selected_squawk1').text(selected.squawk);
$('#selected_squawk2').text(selected.squawk);
}
let magResult = null;
if (geoMag && selected.position != null) {
let lon = selected.position[0];
let lat = selected.position[1];
let alt = selected.altitude == "ground" ? 0 : selected.altitude;
magResult = geoMag(lat, lon, alt);
$('#selected_mag_declination').text(format_track_brief(magResult.dec));
} else {
$('#selected_mag_declination').text('n/a');
}
let heading = null;
if (selected.true_heading != null && selected.track != null) {
heading = selected.true_heading;
} else if (magResult && selected.mag_heading != null && selected.track != null) {
heading = selected.mag_heading + magResult.dec;
}
if (heading != null && heading < 0)
heading += 360;
if (heading != null && heading > 360)
heading -= 360;
$('#selected_mag_heading').text(format_track_brief(selected.mag_heading));
if (selected.wd != null && selected.ws != null) {
$('#selected_wd').text(format_track_brief(selected.wd, true));
$('#selected_ws').text(format_speed_long(selected.ws, DisplayUnits));
} else if (!globeIndex && magResult && selected.gs != null && selected.tas != null && selected.track != null && selected.mag_heading != null) {
const trk = (Math.PI / 180) * selected.track;
const hdg = (Math.PI / 180) * heading;
const tas = selected.tas;
const gs = selected.gs;
const ws = Math.round(Math.sqrt(Math.pow(tas - gs, 2) + 4 * tas * gs * Math.pow(Math.sin((hdg - trk) / 2), 2)));
let wd = trk + Math.atan2(tas * Math.sin(hdg - trk), tas * Math.cos(hdg - trk) - gs);
if (wd < 0) {
wd = wd + 2 * Math.PI;
}
if (wd > 2 * Math.PI) {
wd = wd - 2 * Math.PI;
}
wd = Math.round((180 / Math.PI) * wd);
$('#selected_wd').text(format_track_brief(wd, true));
$('#selected_ws').text(format_speed_long(ws, DisplayUnits));
} else {
$('#selected_wd').text('n/a');
$('#selected_ws').text('n/a');
}
if (!globeIndex && selected.true_heading == null && heading != null)
$('#selected_true_heading').text(format_track_brief(heading));
else
$('#selected_true_heading').text(format_track_brief(selected.true_heading));
let oat = null;
let tat = null;
if (selected.tat != null && selected.oat != null) {
oat = selected.oat;
tat = selected.tat;
} else if (!globeIndex && selected.mach != null && selected.tas != null && selected.mach > 0.395) {
oat = Math.pow((selected.tas / 661.47 / selected.mach), 2) * 288.15 - 273.15;
tat = -273.15 + (oat + 273.15) * (1 + 0.2 * selected.mach * selected.mach);
}
if (oat != null)
$('#selected_temp').text(Math.round(tat) + ' / ' + Math.round(oat) + ' °C');
else
$('#selected_temp').text('n/a');
$('#selected_speed1').text(format_speed_long(selected.gs, DisplayUnits));
$('#selected_speed2').text(format_speed_long(selected.gs, DisplayUnits));
$('#selected_ias').text(format_speed_long(selected.ias, DisplayUnits));
$('#selected_tas').text(format_speed_long(selected.tas, DisplayUnits));
if (selected.geom_rate != null) {
$('#selected_vert_rate').text(format_vert_rate_long(selected.geom_rate, DisplayUnits));
} else {
$('#selected_vert_rate').text(format_vert_rate_long(selected.baro_rate, DisplayUnits));
}
$('#selected_baro_rate').text(format_vert_rate_long(selected.baro_rate, DisplayUnits));
$('#selected_geom_rate').text(format_vert_rate_long(selected.geom_rate, DisplayUnits));
if (selected.icao != selIcao) {
selIcao = selected.icao;
if (globeIndex) {
let icao_link = "Share";
icao_link = NBSP +NBSP +NBSP + icao_link;
$('#selected_icao').html(selected.icao.toUpperCase() + icao_link);
} else {
$('#selected_icao').text(selected.icao.toUpperCase());
}
}
$('#selected_pf_info').text((selected.pfRoute ? selected.pfRoute : "") );
//+" "+ (selected.pfFlightno ? selected.pfFlightno : "")
$('#airframes_post_icao').attr('value',selected.icao);
$('#selected_track1').text(format_track_brief(selected.track));
$('#selected_track2').text(format_track_brief(selected.track));
if (selected.seen != null && selected.seen < 1000000) {
$('#selected_seen').text(format_duration(selected.seen));
} else {
$('#selected_seen').text('n/a');
}
if (selected.seen_pos != null && selected.seen_pos < 1000000) {
$('#selected_seen_pos').text(format_duration(selected.seen_pos));
} else {
$('#selected_seen_pos').text('n/a');
}
$('#selected_country').text(selected.icaorange.country.replace("special use", "special"));
if (ShowFlags && selected.icaorange.flag_image !== null) {
$('#selected_flag').removeClass('hidden');
$('#selected_flag img').attr('src', FlagPath + selected.icaorange.flag_image);
$('#selected_flag img').attr('title', selected.icaorange.country);
} else {
$('#selected_flag').addClass('hidden');
}
if (selected.position == null) {
$('#selected_position').text('n/a');
} else {
if (selected.seen_pos > -1) {
$('#selected_position').text(format_latlng(selected.position));
} else {
$('#selected_position').text(format_latlng(selected.position));
}
checkFollow();
}
if (selected.position && SitePosition) {
selected.sitedist = ol.sphere.getDistance(SitePosition, selected.position);
}
$('#selected_source').text(format_data_source(selected.getDataSource()));
$('#selected_category').text(selected.category ? selected.category : "n/a");
$('#selected_sitedist1').text(format_distance_long(selected.sitedist, DisplayUnits));
$('#selected_sitedist2').text(format_distance_long(selected.sitedist, DisplayUnits));
$('#selected_rssi1').text(selected.rssi != null ? selected.rssi.toFixed(1) : "n/a");
if (globeIndex && binCraft && !showTrace) {
$('#selected_message_count').prev().text('Receivers:');
$('#selected_message_count').prop('title', 'Number of receivers receiving this aircraft');
if (selected.receiverCount >= 5 && selected.dataSource != 'mlat') {
$('#selected_message_count').text('> ' + selected.receiverCount);
} else {
$('#selected_message_count').text(selected.receiverCount);
}
} else {
$('#selected_message_count').prev().text('Messages:');
$('#selected_message_count').prop('title', 'The total number of messages received from this aircraft');
$('#selected_message_count').text(selected.messages);
}
$('#selected_message_rate').text((selected.messageRate != null) ? (selected.messageRate.toFixed(1)) : "n/a");
$('#selected_photo_link').html(getPhotoLink(selected));
$('#selected_altitude_geom').text(format_altitude_long(selected.alt_geom, selected.geom_rate, DisplayUnits));
$('#selected_ias').text(format_speed_long(selected.ias, DisplayUnits));
$('#selected_tas').text(format_speed_long(selected.tas, DisplayUnits));
if (selected.mach == null) {
$('#selected_mach').text('n/a');
} else {
$('#selected_mach').text(selected.mach.toFixed(3));
}
if (selected.roll == null) {
$('#selected_roll').text('n/a');
} else {
$('#selected_roll').text(selected.roll.toFixed(1));
}
if (selected.track_rate == null) {
$('#selected_trackrate').text('n/a');
} else {
$('#selected_trackrate').text(selected.track_rate.toFixed(2));
}
$('#selected_geom_rate').text(format_vert_rate_long(selected.geom_rate, DisplayUnits));
if (selected.nav_qnh == null) {
$('#selected_nav_qnh').text("n/a");
} else {
$('#selected_nav_qnh').text(selected.nav_qnh.toFixed(1) + " hPa");
}
$('#selected_nav_altitude').text(format_altitude_long(selected.nav_altitude, 0, DisplayUnits));
$('#selected_nav_heading').text(format_track_brief(selected.nav_heading));
if (selected.nav_modes == null) {
$('#selected_nav_modes').text("n/a");
} else {
$('#selected_nav_modes').text(selected.nav_modes.join());
}
if (selected.nic_baro == null) {
$('#selected_nic_baro').text("n/a");
} else {
if (selected.nic_baro == 1) {
$('#selected_nic_baro').text("cross-checked");
} else {
$('#selected_nic_baro').text("not cross-checked");
}
}
$('#selected_nac_p').text(format_nac_p(selected.nac_p));
$('#selected_nac_v').text(format_nac_v(selected.nac_v));
if (selected.rc == null) {
$('#selected_rc').text("n/a");
} else if (selected.rc == 0) {
$('#selected_rc').text("unknown");
} else {
$('#selected_rc').text(format_distance_short(selected.rc, DisplayUnits));
}
if (selected.sil == null || selected.sil_type == null) {
$('#selected_sil').text("n/a");
} else {
let sampleRate = "";
let silDesc = "";
if (selected.sil_type == "perhour") {
sampleRate = " per flight hour";
} else if (selected.sil_type == "persample") {
sampleRate = " per sample";
}
switch (selected.sil) {
case 0:
silDesc = "> 1×10-3";
break;
case 1:
silDesc = "≤ 1×10-3";
break;
case 2:
silDesc = "≤ 1×10-5";
break;
case 3:
silDesc = "≤ 1×10-7";
break;
default:
silDesc = "n/a";
sampleRate = "";
break;
}
$('#selected_sil').html(silDesc + sampleRate);
}
if (selected.version == null) {
$('#selected_version').text('none');
} else if (selected.version == 0) {
$('#selected_version').text('v0 (DO-260)');
} else if (selected.version == 1) {
$('#selected_version').text('v1 (DO-260A)');
} else if (selected.version == 2) {
$('#selected_version').text('v2 (DO-260B)');
} else {
$('#selected_version').text('v' + selected.version);
}
setSelectedInfoBlockVisibility();
}
function refreshHighlighted() {
// this is following nearly identical logic, etc, as the refreshSelected function, but doing less junk for the highlighted pane
let highlighted = false;
if (!HighlightedPlane || !(highlighted = Planes[HighlightedPlane]) ) {
$('#highlighted_infoblock').hide();
return;
}
$('#highlighted_infoblock').show();
let infoBox = $('#highlighted_infoblock');
let marker = highlighted.marker;
let geom;
let markerCoordinates;
if (!marker || !(geom = marker.getGeometry()) || !(markerCoordinates = geom.getCoordinates()) ) {
$('#highlighted_infoblock').hide();
return;
}
let markerPosition = OLMap.getPixelFromCoordinate(markerCoordinates);
if (!markerPosition)
return;
let mapSize = OLMap.getSize();
if (markerPosition[0] + 200 < mapSize[0])
infoBox.css("left", markerPosition[0] + 20);
else
infoBox.css("left", markerPosition[0] - 200);
if (markerPosition[1] + 250 < mapSize[1])
infoBox.css("top", markerPosition[1] + 50);
else
infoBox.css("top", markerPosition[1] - 250);
$('#highlighted_callsign').text(highlighted.name);
if (highlighted.icaoType !== null) {
$('#highlighted_icaotype').text(highlighted.icaoType);
} else {
$('#highlighted_icaotype').text("n/a");
}
$('#highlighted_source').text(format_data_source(highlighted.getDataSource()));
if (highlighted.registration !== null) {
$('#highlighted_registration').text(highlighted.registration);
} else {
$('#highlighted_registration').text("n/a");
}
$('#highlighted_speed').text(format_speed_long(highlighted.gs, DisplayUnits));
$("#highlighted_altitude").text(format_altitude_long(highlighted.altitude, highlighted.vert_rate, DisplayUnits));
$('#highlighted_pf_route').text((highlighted.pfRoute ? highlighted.pfRoute : highlighted.icao.toUpperCase()));
$('#highlighted_rssi').text(highlighted.rssi != null ? highlighted.rssi.toFixed(1) + ' dBFS' : "n/a");
}
function removeHighlight() {
HighlightedPlane = null;
refreshHighlighted();
}
function refreshFeatures() {
for (let i in PlanesOrdered) {
PlanesOrdered[i].updateTick(true);
}
}
//
// Planes table begin
//
(function (global, $, TAR) {
let planesTable = TAR.planesTable = TAR.planesTable || {};
function compareAlpha(xa,ya) {
if (xa === ya)
return 0;
if (xa < ya)
return -1;
return 1;
}
function compareBeta(xa, ya) {
if (xa === ya)
return 0;
if (sortAscending && xa < ya)
return -1;
if (!sortAscending && (xa.replace(/ /g, "").split("").reverse().join("") > ya.replace(/ /g, "").split("").reverse().join("")))
return -1;
return 1;
}
function compareNumeric(xf,yf) {
if (Math.abs(xf - yf) < 1e-9)
return 0;
return xf - yf;
}
let cols = planesTable.cols = {};
cols.icao = {
text: 'Hex id',
sort: function () { sortBy('icao', compareAlpha, function(x) { return x.icao; }); },
value: function(plane) { return plane.icao; },
td: '