Files
npease 88f2682fb7
Gitea Pages / Gitea-Pages (push) Successful in 3s
Cache Control
2024-01-31 04:13:26 +00:00

2847 lines
103 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// -*- mode: javascript; indent-tabs-mode: nil; c-basic-offset: 8 -*-
"use strict";
// Define our global variables
var OLMap = null;
var StaticFeatures = new ol.Collection();
var SiteCircleFeatures = new ol.Collection();
var PlaneIconFeatures = new ol.Collection();
var PlaneTrailFeatures = new ol.Collection();
var Planes = {};
var PlanesOrdered = [];
var PlaneFilter = {};
var SelectedPlane = null;
var SelectedAllPlanes = false;
var HighlightedPlane = null;
var FollowSelected = false;
var infoBoxOriginalPosition = {};
var customAltitudeColors = true;
var myAdsbStatsSiteUrl = null;
var ADSB_Enabled = true;
var UAT_Enabled = false;
var 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
var CenterLat, CenterLon, ZoomLvl, MapType, SiteCirclesCount, SiteCirclesBaseDistance, SiteCirclesInterval;
var SkyAwareVersion = "unknown version";
var RefreshInterval = 1000;
var PlaneRowTemplate = null;
var TrackedAircraft = 0;
var TrackedAircraftPositions = 0;
var TrackedHistorySize = 0;
var SitePosition = null;
var LastReceiverTimestamp = 0;
var StaleReceiverCount = 0;
var FetchPending = null;
var FetchPending_UAT = null;
var MessageCountHistory = [];
var MessageCountHistory_UAT = [];
var MessageRate = 0;
var UatMessageRate = 0;
var NBSP='\u00a0';
var layers;
var layerGroup;
var ActiveFilterCount = 0;
var altitude_slider = null;
var speed_slider = null;
var AircraftLabels = false;
// piaware vs flightfeeder
var isFlightFeeder = false;
var checkbox_div_map = new Map ([
['#icao_col_checkbox', '#icao'],
['#flag_col_checkbox', '#flag'],
['#ident_col_checkbox', '#flight'],
['#reg_col_checkbox', '#registration'],
['#ac_col_checkbox', '#aircraft_type'],
['#squawk_col_checkbox', '#squawk'],
['#alt_col_checkbox', '#altitude'],
['#speed_col_checkbox', '#speed'],
['#vrate_col_checkbox', '#vert_rate'],
['#distance_col_checkbox', '#distance'],
['#heading_col_checkbox', '#track'],
['#messages_col_checkbox', '#msgs'],
['#msg_age_col_checkbox', '#seen'],
['#rssi_col_checkbox', '#rssi'],
['#lat_col_checkbox', '#lat'],
['#lon_col_checkbox', '#lon'],
['#datasource_col_checkbox', '#data_source'],
['#airframes_col_checkbox', '#airframes_mode_s_link'],
['#fa_modes_link_checkbox', '#flightaware_mode_s_link'],
['#fa_photo_link_checkbox', '#flightaware_photo_link'],
]);
var DefaultMinMaxFilters = {
'nautical': {min: 0, maxSpeed: 1000, maxAltitude: 65000}, // kt, ft
'metric' : {min: 0, maxSpeed: 1000, maxAltitude: 20000}, // km/h, m
'imperial' : {min: 0, maxSpeed: 600, maxAltitude: 65000} // mph, ft
};
// Update Planes with data in aircraft json
// receiver_source will specify where the aircraft.json originated from (dump1090-fa or skyaware978)
function processReceiverUpdate(data, receiver_source) {
// Loop through all the planes in the data packet
var now = data.now;
var acs = data.aircraft;
if (receiver_source === "skyaware978") {
// Detect stats reset (i.e. if MessageCountHistory array is > 0 and the latest value > the "messages" field in the data packet)
if (MessageCountHistory_UAT.length > 0 && MessageCountHistory_UAT[MessageCountHistory_UAT.length-1].messages > data.messages) {
MessageCountHistory_UAT = [{'time' : MessageCountHistory_UAT[MessageCountHistory_UAT.length-1].time,
'messages' : 0}];
}
// Maintain a 30 second rollinng history of message counts
MessageCountHistory_UAT.push({ 'time' : now, 'messages' : data.messages});
// .. and clean up any old values
if ((now - MessageCountHistory_UAT[0].time) > 30)
MessageCountHistory_UAT.shift();
} else {
// Detect stats reset (i.e. if MessageCountHistory array is > 0 and the latest value > the "messages" field in the data packet)
if (MessageCountHistory.length > 0 && MessageCountHistory[MessageCountHistory.length-1].messages > data.messages) {
MessageCountHistory = [{'time' : MessageCountHistory[MessageCountHistory.length-1].time,
'messages' : 0}];
}
// Maintain a 30 second rollinng history of message counts
MessageCountHistory.push({ 'time' : now, 'messages' : data.messages});
// .. and clean up any old values
if ((now - MessageCountHistory[0].time) > 30)
MessageCountHistory.shift();
}
for (var j=0; j < acs.length; j++) {
var ac = acs[j];
var hex = ac.hex;
var squawk = ac.squawk;
var plane = null;
// Do we already have this plane object in Planes?
// If not make it.
if (Planes[hex]) {
plane = Planes[hex];
} else {
plane = new PlaneObject(hex);
plane.filter = PlaneFilter;
plane.tr = PlaneRowTemplate.cloneNode(true);
if (hex[0] === '~') {
// Non-ICAO address
plane.tr.cells[0].textContent = hex.substring(1);
$(plane.tr).css('font-style', 'italic');
} else {
plane.tr.cells[0].textContent = hex;
}
// set flag image if available
if (ShowFlags && plane.icaorange.flag_image !== null) {
$('img', plane.tr.cells[1]).attr('src', FlagPath + plane.icaorange.flag_image);
$('img', plane.tr.cells[1]).attr('title', plane.icaorange.country);
} else {
$('img', plane.tr.cells[1]).css('display', 'none');
}
plane.tr.addEventListener('click', function(h, evt) {
if (evt.srcElement instanceof HTMLAnchorElement) {
evt.stopPropagation();
return;
}
if (!$("#map_container").is(":visible")) {
showMap();
}
selectPlaneByHex(h, false);
adjustSelectedInfoBlockPosition();
evt.preventDefault();
}.bind(undefined, hex));
plane.tr.addEventListener('dblclick', function(h, evt) {
if (!$("#map_container").is(":visible")) {
showMap();
}
selectPlaneByHex(h, true);
adjustSelectedInfoBlockPosition();
evt.preventDefault();
}.bind(undefined, hex));
Planes[hex] = plane;
PlanesOrdered.push(plane);
}
// Call the function update
plane.updateData(now, ac, receiver_source);
}
}
function fetchData() {
if (ADSB_Enabled) {
if (FetchPending !== null && FetchPending.state() == 'pending') {
// don't double up on fetches, let the last one resolve
return;
}
FetchPending = $.ajax({ url: 'data/aircraft.json?_='+Date.now(),
timeout: 5000,
cache: false,
dataType: 'json' });
FetchPending.done(function(data) {
process_aircraft_json(data, 'dump1090-fa');
});
FetchPending.fail(function(jqxhr, status, error) {
$("#update_error_detail").text("AJAX call failed (" + status + (error ? (": " + error) : "") + "). Maybe dump1090 is no longer running?");
$("#update_error").css('display','block');
});
}
// Fetch UAT if enabled
if (UAT_Enabled) {
if (FetchPending_UAT !== null && FetchPending_UAT.state() == 'pending') {
// don't double up on fetches, let the last one resolve
return;
}
FetchPending_UAT = $.ajax({ url: 'data-978/aircraft.json',
timeout: 5000,
cache: false,
dataType: 'json' });
FetchPending_UAT.done(function(data) {
// Process UAT aircraft.json here
process_aircraft_json(data, 'skyaware978');
});
FetchPending_UAT.fail(function(jqxhr, status, error) {
$("#uat_update_error_detail").text("AJAX call failed (" + status + (error ? (": " + error) : "") + "). Maybe skyaware978 is no longer running?");
$("#uat_update_error").css('display','block');
});
}
}
// Process an aircraft.json and update Planes.
// receiver_source will specify where the aircraft.json originated from (dump1090-fa or skyaware978)
function process_aircraft_json(data, receiver_source) {
var now = data.now;
processReceiverUpdate(data, receiver_source);
// update timestamps, visibility, history track for all planes - not only those updated
for (var i = 0; i < PlanesOrdered.length; ++i) {
var plane = PlanesOrdered[i];
plane.updateTick(now, LastReceiverTimestamp);
}
selectNewPlanes();
refreshTableInfo();
refreshSelected();
refreshHighlighted();
// Check for stale receiver data
if (LastReceiverTimestamp === now) {
StaleReceiverCount++;
if (StaleReceiverCount > 5) {
$("#update_error_detail").text("The data from dump1090 hasn't been updated in a while. Maybe dump1090 is no longer running?");
$("#update_error").css('display','block');
}
} else {
StaleReceiverCount = 0;
LastReceiverTimestamp = now;
$("#update_error").css('display','none');
}
}
var PositionHistorySize = 0;
var UatPositionHistorySize = 0;
function initialize() {
// Set page basics
document.title = PageName;
flightFeederCheck();
setStatsLink();
PlaneRowTemplate = document.getElementById("plane_row_template");
refreshClock();
$("#loader").removeClass("hidden");
if (ExtendedData || window.location.hash == '#extended') {
$("#extendedData").removeClass("hidden");
}
// Set up map/sidebar splitter
$("#sidebar_container").resizable({
handles: {
w: '#splitter'
},
minWidth: 350
});
// Set up datablock splitter
$('#selected_infoblock').resizable({
handles: {
s: '#splitter-infoblock'
},
containment: "#sidebar_container",
minHeight: 50
});
$('#close-button').on('click', function() {
if (SelectedPlane !== null) {
var selectedPlane = Planes[SelectedPlane];
SelectedPlane = null;
selectedPlane.selected = null;
selectedPlane.clearLines();
selectedPlane.updateMarker();
refreshSelected();
refreshHighlighted();
$('#selected_infoblock').hide();
}
});
// 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() {
var topCalc = ($(window).height() - $('#selected_infoblock').height() - 60);
// 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() - 60) +'px');
}
$('#selected_infoblock').css('top', topCalc + 'px');
});
// to make the infoblock responsive
$('#sidebar_container').on('resize', function() {
if ($('#sidebar_container').width() < 500) {
$('#selected_infoblock').addClass('infoblock-container-small');
} else {
$('#selected_infoblock').removeClass('infoblock-container-small');
}
});
// Set up event handlers for buttons
$("#toggle_sidebar_button").click(toggleSidebarVisibility);
$("#expand_sidebar_button").click(expandSidebar);
$("#show_map_button").click(showMap);
// Set initial element visibility
$("#show_map_button").hide();
$("#range_ring_column").hide();
setColumnVisibility();
// Initialize other controls
initializeUnitsSelector();
// check if the altitude color values are default to enable the altitude filter
if (ColorByAlt.air.h.length === 3 && ColorByAlt.air.h[0].alt === 2000 && ColorByAlt.air.h[0].val === 20 && ColorByAlt.air.h[1].alt === 10000 && ColorByAlt.air.h[1].val === 140 && ColorByAlt.air.h[2].alt === 40000 && ColorByAlt.air.h[2].val === 300) {
customAltitudeColors = false;
}
create_filter_sliders();
$("#aircraft_type_filter_form").submit(onFilterByAircraftType);
$("#aircraft_type_filter_reset_button").click(onResetAircraftTypeFilter);
$("#aircraft_ident_filter_form").submit(onFilterByAircraftIdent);
$("#aircraft_ident_filter_reset_button").click(onResetAircraftIdentFilter);
$('#settingsCog').on('click', function() {
$('#settings_infoblock').toggle();
});
$('#settings_close').on('click', function() {
$('#settings_infoblock').hide();
});
$('#groundvehicle_filter').on('click', function() {
filterGroundVehicles(true);
refreshSelected();
refreshHighlighted();
refreshTableInfo();
});
$('#blockedmlat_filter').on('click', function() {
filterBlockedMLAT(true);
refreshSelected();
refreshHighlighted();
refreshTableInfo();
});
$('#grouptype_checkbox').on('click', function() {
toggleGroupByDataType(true);
});
$('#aircraft_label_checkbox').on('click', function() {
toggleAircraftLabels(true);
});
$('#altitude_checkbox').on('click', function() {
toggleAltitudeChart(true);
});
$('#selectall_checkbox').on('click', function() {
toggleAllPlanes(true);
})
$('#select_all_column_checkbox').on('click', function() {
toggleAllColumns(true);
})
$('#adsb_datasource_checkbox').on('click', function() {
toggleADSBAircraft(true);
refreshDataSourceFilters();
})
$('#uat_datasource_checkbox').on('click', function() {
toggleUATAircraft(true);
refreshDataSourceFilters();
})
$('#mlat_datasource_checkbox').on('click', function() {
toggleMLATAircraft(true);
refreshDataSourceFilters();
})
$('#other_datasource_checkbox').on('click', function() {
toggleOtherAircraft(true);
refreshDataSourceFilters();
})
$('#tisb_datasource_checkbox').on('click', function() {
toggleTISBAircraft(true);
refreshDataSourceFilters();
})
$('#column_select_button').on('click', function() {
this.classList.toggle("config_button_active");
$('#column_select_panel').toggle();
});
$('#filter_button').on('click', function() {
this.classList.toggle("config_button_active");
$('#filter_panel').toggle();
});
$('#stats_page_button').on('click', function() {
if (myAdsbStatsSiteUrl) {
window.open(myAdsbStatsSiteUrl);
}
});
// Event handlers for to column checkboxes
checkbox_div_map.forEach(function (checkbox, div) {
$(div).on('click', function() {
toggleColumn(checkbox, div, true);
});
});
// Force map to redraw if sidebar container is resized - use a timer to debounce
var mapResizeTimeout;
$("#sidebar_container").on("resize", function() {
clearTimeout(mapResizeTimeout);
mapResizeTimeout = setTimeout(updateMapSize, 10);
});
// Initialize settings from local storage
filterGroundVehicles(false);
filterBlockedMLAT(false);
toggleAltitudeChart(false);
toggleAllPlanes(false);
toggleGroupByDataType(false);
toggleAircraftLabels(false);
toggleAllColumns(false);
toggleADSBAircraft(false);
toggleUATAircraft(false);
toggleMLATAircraft(false);
toggleOtherAircraft(false);
toggleTISBAircraft(false);
refreshDataSourceFilters();
// Get 978 receiver metadata if present
$.ajax({ url: 'data-978/receiver.json',
timeout: 5000,
cache: false,
dataType: 'json' })
.done(function(data) {
console.log('SkyAware978 enabled')
UAT_Enabled = true;
UatPositionHistorySize = data.history;
})
.fail(function(data) {
console.warn('Error reading SkyAware978 receiver.json. SkyAware978 may be disabled')
UAT_Enabled = false;
});
// Get receiver metadata, reconfigure using it, then continue
// with initialization
$.ajax({ url: 'data/receiver.json',
timeout: 5000,
cache: false,
dataType: 'json' })
.done(function(data) {
console.log('dump1090-fa enabled');
ADSB_Enabled = true;
if (typeof data.lat !== "undefined") {
SiteShow = true;
SiteLat = data.lat;
SiteLon = data.lon;
DefaultCenterLat = data.lat;
DefaultCenterLon = data.lon;
}
SkyAwareVersion = data.version;
RefreshInterval = data.refresh;
PositionHistorySize = data.history;
})
.fail(function(data) {
console.warn('Error reading dump1090-fa receiver.json. dump1090-fa may be disabled');
ADSB_Enabled = false;
})
.always(function() {
initialize_map();
start_load_history();
});
}
function create_filter_sliders() {
var maxAltitude = DefaultMinMaxFilters[DisplayUnits].maxAltitude;
var minAltitude = DefaultMinMaxFilters[DisplayUnits].min;
var maxSpeed = DefaultMinMaxFilters[DisplayUnits].maxSpeed;
var minSpeed = DefaultMinMaxFilters[DisplayUnits].min;
altitude_slider = document.getElementById('altitude_slider');
noUiSlider.create(altitude_slider, {
start: [minAltitude, maxAltitude],
connect: true,
range: {
'min': minAltitude,
'max': maxAltitude
},
step: 25,
format: {
to: (v) => parseFloat(v).toFixed(0),
from: (v) => parseFloat(v).toFixed(0)
}
});
// Change text to reflect slider values
var minAltitudeInput = document.getElementById('minAltitudeText'),
maxAltitudeInput = document.getElementById('maxAltitudeText');
altitude_slider.noUiSlider.on('update', function (values, handle) {
if (handle) {
maxAltitudeInput.innerHTML = values[handle];
} else {
minAltitudeInput.innerHTML = values[handle];
}
});
// 'Set' event - Whenever a slider is changed to a new value, this event is fired. This function will trigger every time a slider stops changing, including after calls to the .set() method. This event can be considered as the 'end of slide'.
altitude_slider.noUiSlider.on('set', function (values, handle) {
onFilterByAltitude();
});
speed_slider = document.getElementById('speed_slider');
noUiSlider.create(speed_slider, {
start: [minSpeed, maxSpeed],
connect: true,
range: {
'min': minSpeed,
'max': maxSpeed
},
step: 5,
format: {
to: (v) => parseFloat(v).toFixed(0),
from: (v) => parseFloat(v).toFixed(0)
}
});
// Change text to reflect slider values
var minSpeedInput = document.getElementById('minSpeedText'),
maxSpeedInput = document.getElementById('maxSpeedText');
speed_slider.noUiSlider.on('update', function (values, handle) {
if (handle) {
maxSpeedInput.innerHTML = values[handle];
} else {
minSpeedInput.innerHTML = values[handle];
}
});
// 'Set' event - Whenever a slider is changed to a new value, this event is fired. This function will trigger every time a slider stops changing, including after calls to the .set() method. This event can be considered as the 'end of slide'.
speed_slider.noUiSlider.on('set', function (values, handle) {
onFilterBySpeed();
});
}
function reset_filter_sliders() {
var maxAltitude = DefaultMinMaxFilters[DisplayUnits].maxAltitude;
var minAltitude = DefaultMinMaxFilters[DisplayUnits].min;
var maxSpeed = DefaultMinMaxFilters[DisplayUnits].maxSpeed;
var minSpeed = DefaultMinMaxFilters[DisplayUnits].min;
altitude_slider.noUiSlider.updateOptions({
start: [minAltitude, maxAltitude],
range: {
'min': minAltitude,
'max': maxAltitude
}
});
speed_slider.noUiSlider.updateOptions({
start: [minSpeed, maxSpeed],
range: {
'min': minSpeed,
'max': maxSpeed
}
});
// Update filters
updatePlaneFilter();
}
var CurrentHistoryFetch = 0;
var PositionHistoryBuffer = [];
var HistoryItemsReturned = 0;
var TotalPositionHistorySize = 0;
function start_load_history() {
let url = new URL(window.location.href);
let params = new URLSearchParams(url.search);
// Get total number of history.json files to load
TotalPositionHistorySize = PositionHistorySize + UatPositionHistorySize;
if (TotalPositionHistorySize > 0 && params.get('nohistory') !== 'true') {
$("#loader_progress").attr('max', TotalPositionHistorySize);
console.log("Starting to load history (" + TotalPositionHistorySize + " items)");
// Load dump1090 history.json files
for (var i = 0; i < PositionHistorySize; i++) {
load_history_item(i, 'data');
CurrentHistoryFetch++;
}
// Load skyaware978 history.json files
for (var i = 0; i < UatPositionHistorySize; i++) {
load_history_item(i, 'data-978');
CurrentHistoryFetch++;
}
} else {
// Nothing to load
end_load_history();
}
}
// Loads a history json file
function load_history_item(i, source) {
var historyfile = 'history_' + i + '.json';
console.log('Loading ' + source + ' ' + historyfile);
$("#loader_progress").attr('value', CurrentHistoryFetch);
var receiver_source = (source == "data-978" ? "skyaware978" : "dump1090-fa");
$.ajax({ url: source + '/' + historyfile,
timeout: 5000,
cache: false,
dataType: 'json' })
.done(function(data) {
// Tag history.json files with the source we fetched from (/data or /data-978)
data["source"] = receiver_source;
PositionHistoryBuffer.push(data);
HistoryItemsReturned++;
if (HistoryItemsReturned == TotalPositionHistorySize) {
// End load history when all files have been loaded
end_load_history();
}
})
.fail(function(jqxhr, status, error) {
//Doesn't matter if it failed, we'll just be missing a data point
HistoryItemsReturned++;
if (HistoryItemsReturned == TotalPositionHistorySize) {
// End load history when all files have been loaded
end_load_history();
}
});
}
function end_load_history() {
$("#loader").addClass("hidden");
console.log("Done loading history");
if (PositionHistoryBuffer.length > 0) {
var now, last=0;
// Sort history by timestamp
console.log("Sorting history");
PositionHistoryBuffer.sort(function(x,y) { return (x.now - y.now); });
// Process history
for (var h = 0; h < PositionHistoryBuffer.length; ++h) {
now = PositionHistoryBuffer[h].now;
console.log("Applying history " + (h + 1) + "/" + PositionHistoryBuffer.length + " at: " + now);
processReceiverUpdate(PositionHistoryBuffer[h], PositionHistoryBuffer[h].source);
// Update track
console.log("Updating tracks at: " + now);
for (var i = 0; i < PlanesOrdered.length; ++i) {
var plane = PlanesOrdered[i];
plane.updateTrack(now, last);
}
last = now;
}
// Final pass to update all planes to their latest state
console.log("Final history cleanup pass");
for (var i = 0; i < PlanesOrdered.length; ++i) {
var plane = PlanesOrdered[i];
plane.updateTick(now);
}
LastReceiverTimestamp = last;
}
PositionHistoryBuffer = null;
console.log("Completing init");
refreshTableInfo();
refreshSelected();
refreshHighlighted();
reaper();
// Setup our timer to poll from the server.
window.setInterval(fetchData, RefreshInterval);
window.setInterval(reaper, 60000);
// And kick off one refresh immediately.
fetchData();
// update the display layout from any URL query strings
applyUrlQueryStrings();
}
// Function to apply any URL query value to the map before we start
function applyUrlQueryStrings() {
// if asked, toggle featrues at start
let url = new URL(window.location.href);
let params = new URLSearchParams(url.search);
// be sure we start with a 'clean' layout, but only if we need it
var allOptions = [
'banner',
'altitudeChart',
'aircraftTrails',
'map',
'sidebar',
'zoomOut',
'zoomIn',
'moveNorth',
'moveSouth',
'moveWest',
'moveEast',
'displayUnits',
'rangeRings',
'ringCount',
'ringBaseDistance',
'ringInterval'
]
var needReset = false;
for (var option of allOptions) {
if (params.has(option)) {
needReset = true;
break;
}
}
if (needReset) {
resetMap();
}
if (params.get('banner') === 'hide') {
hideBanner();
}
if (params.get('altitudeChart') === 'hide') {
$('#altitude_checkbox').removeClass('settingsCheckboxChecked');
$('#altitude_chart').hide();
}
if (params.get('altitudeChart') === 'show') {
$('#altitude_checkbox').addClass('settingsCheckboxChecked');
$('#altitude_chart').show();
}
if (params.get('aircraftTrails') === 'show') {
selectAllPlanes();
}
if (params.get('aircraftTrails') === 'hide') {
deselectAllPlanes();
}
if (params.get('map') === 'show') {
showMap();
}
if (params.get('map') === 'hide') {
expandSidebar();
}
if (params.get('sidebar') === 'show') {
$("#sidebar_container").show();
updateMapSize();
}
if (params.get('sidebar') === 'hide') {
$("#sidebar_container").hide();
updateMapSize();
}
if (params.get('zoomOut')) {
zoomMap(params.get('zoomOut'), true);
}
if (params.get('zoomIn')) {
zoomMap(params.get('zoomIn'), false);
}
if (params.get('moveNorth')) {
moveMap(params.get('moveNorth'), true, false);
}
if (params.get('moveSouth')) {
moveMap(params.get('moveSouth'), true, true);
}
if (params.get('moveEast')) {
moveMap(params.get('moveEast'), false, false);
}
if (params.get('moveWest')) {
moveMap(params.get('moveWest'), false, true);
}
if (params.get('displayUnits')) {
setDisplayUnits(params.get('displayUnits'));
}
if (params.get('rangeRings')) {
setRangeRingVisibility(params.get('rangeRings'));
}
if (params.get('ringCount')) {
setRingCount(params.get('ringCount'));
}
if (params.get('ringBaseDistance')) {
setRingBaseDistance(params.get('ringBaseDistance'));
}
if (params.get('ringInterval')) {
setRingInterval(params.get('ringInterval'));
}
}
// 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
function make_geodesic_circle(center, radius, points) {
var angularDistance = radius / 6378137.0;
var lon1 = center[0] * Math.PI / 180.0;
var lat1 = center[1] * Math.PI / 180.0;
var geom;
for (var i = 0; i <= points; ++i) {
var bearing = i * 2 * Math.PI / points;
var lat2 = Math.asin( Math.sin(lat1)*Math.cos(angularDistance) +
Math.cos(lat1)*Math.sin(angularDistance)*Math.cos(bearing) );
var 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;
}
// Initalizes the map and starts up our timers to call various functions
function initialize_map() {
// Load stored map settings if present
CenterLat = Number(localStorage['CenterLat']) || DefaultCenterLat;
CenterLon = Number(localStorage['CenterLon']) || DefaultCenterLon;
ZoomLvl = Number(localStorage['ZoomLvl']) || DefaultZoomLvl;
MapType = localStorage['MapType'];
var groupByDataTypeBox = localStorage.getItem('groupByDataType');
// Set SitePosition, initialize sorting
if (SiteShow && (typeof SiteLat !== 'undefined') && (typeof SiteLon !== 'undefined')) {
SitePosition = [SiteLon, SiteLat];
if (groupByDataTypeBox === 'deselected') {
sortByDistance();
}
} else {
SitePosition = null;
PlaneRowTemplate.cells[9].style.display = 'none'; // hide distance column
document.getElementById("distance").style.display = 'none'; // hide distance header
if (groupByDataTypeBox === 'deselected') {
sortByAltitude();
}
}
// Maybe hide flag info
if (!ShowFlags) {
PlaneRowTemplate.cells[1].style.display = 'none'; // hide flag column
document.getElementById("flag").style.display = 'none'; // hide flag header
document.getElementById("infoblock_country").style.display = 'none'; // hide country row
}
// Initialize OL3
layers = createBaseLayers();
var iconsLayer = new ol.layer.Vector({
name: 'ac_positions',
type: 'overlay',
title: 'Aircraft positions',
source: new ol.source.Vector({
features: PlaneIconFeatures,
})
});
layers.push(new ol.layer.Group({
title: 'Overlays',
layers: [
new ol.layer.Vector({
name: 'site_pos',
type: 'overlay',
title: 'Site position and range rings',
source: new ol.source.Vector({
features: StaticFeatures,
})
}),
new ol.layer.Vector({
name: 'ac_trail',
type: 'overlay',
title: 'Selected aircraft trail',
source: new ol.source.Vector({
features: PlaneTrailFeatures,
})
}),
iconsLayer
]
}));
var foundType = false;
var baseCount = 0;
layerGroup = new ol.layer.Group({
layers: layers
})
ol.control.LayerSwitcher.forEachRecursive(layerGroup, function(lyr) {
if (!lyr.get('name'))
return;
if (lyr.get('type') === 'base') {
baseCount++;
if (MapType === lyr.get('name')) {
foundType = true;
lyr.setVisible(true);
} else {
lyr.setVisible(false);
}
lyr.on('change:visible', function(evt) {
if (evt.target.getVisible()) {
MapType = localStorage['MapType'] = evt.target.get('name');
createSiteCircleFeatures();
}
});
} else if (lyr.get('type') === 'overlay') {
var 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(layerGroup, 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
}),
controls: [new ol.control.Zoom(),
new ol.control.Rotate(),
new ol.control.Attribution({collapsed: true}),
new ol.control.ScaleLine({units: DisplayUnits})
],
loadTilesWhileAnimating: true,
loadTilesWhileInteracting: true
});
if (baseCount > 1) {
OLMap.addControl(new ol.control.LayerSwitcher());
}
// Listeners for newly created Map
OLMap.getView().on('change:center', function(event) {
var center = ol.proj.toLonLat(OLMap.getView().getCenter(), OLMap.getView().getProjection());
localStorage['CenterLon'] = center[0]
localStorage['CenterLat'] = center[1]
if (FollowSelected) {
// On manual navigation, disable follow
var selected = Planes[SelectedPlane];
if (typeof selected === 'undefined' ||
(Math.abs(center[0] - selected.position[0]) > 0.0001 &&
Math.abs(center[1] - selected.position[1]) > 0.0001)){
FollowSelected = false;
refreshSelected();
refreshHighlighted();
}
}
});
OLMap.getView().on('change:resolution', function(event) {
ZoomLvl = localStorage['ZoomLvl'] = OLMap.getView().getZoom();
for (var plane in Planes) {
Planes[plane].updateMarker(false);
};
});
OLMap.on(['click', 'dblclick'], function(evt) {
var hex = evt.map.forEachFeatureAtPixel(evt.pixel,
function(feature, layer) {
return feature.hex;
},
{
layerFilter: function(layer) {
return (layer === iconsLayer);
},
hitTolerance: 5,
});
if (hex) {
selectPlaneByHex(hex, (evt.type === 'dblclick'));
adjustSelectedInfoBlockPosition();
evt.stopPropagation();
} else {
deselectAllPlanes();
evt.stopPropagation();
}
});
// show the hover box
OLMap.on('pointermove', function(evt) {
var hex = evt.map.forEachFeatureAtPixel(evt.pixel,
function(feature, layer) {
return feature.hex;
},
{
layerFilter: function(layer) {
return (layer === iconsLayer);
},
hitTolerance: 5,
}
);
if (hex) {
highlightPlaneByHex(hex);
} else {
removeHighlight();
}
})
// 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', 'ac_positions');
});
// Add home marker if requested
if (SitePosition) {
var markerStyle = new ol.style.Style({
image: new ol.style.Circle({
radius: 7,
snapToPixel: false,
fill: new ol.style.Fill({color: 'black'}),
stroke: new ol.style.Stroke({
color: 'white', width: 2
})
})
});
var feature = new ol.Feature(new ol.geom.Point(ol.proj.fromLonLat(SitePosition)));
feature.setStyle(markerStyle);
StaticFeatures.push(feature);
$('#range_ring_column').show();
setRangeRings();
$('#range_rings_button').click(onSetRangeRings);
$("#range_ring_form").validate({
errorPlacement: function(error, element) {
return true;
},
rules: {
ringCount: {
number: true,
min: 0
},
baseRing: {
number: true,
min: 0
},
ringInterval: {
number: true,
min: 0
}
}
});
if (SiteCircles) {
createSiteCircleFeatures();
}
}
// Add terrain-limit rings. To enable this:
//
// create a panorama for your receiver location on heywhatsthat.com
//
// note the "view" value from the URL at the top of the panorama
// i.e. the XXXX in http://www.heywhatsthat.com/?view=XXXX
//
// fetch a json file from the API for the altitudes you want to see:
//
// wget -O /usr/share/dump1090-mutability/html/upintheair.json \
// 'http://www.heywhatsthat.com/api/upintheair.json?id=XXXX&refraction=0.25&alts=3048,9144'
//
// NB: altitudes are in _meters_, you can specify a list of altitudes
// kick off an ajax request that will add the rings when it's done
var request = $.ajax({ url: 'upintheair.json',
timeout: 5000,
cache: true,
dataType: 'json' });
request.done(function(data) {
for (var i = 0; i < data.rings.length; ++i) {
var geom = new ol.geom.LineString([]);
var points = data.rings[i].points;
if (points.length > 0) {
for (var j = 0; j < points.length; ++j) {
geom.appendCoordinate([ points[j][1], points[j][0] ]);
}
geom.appendCoordinate([ points[0][1], points[0][0] ]);
geom.transform('EPSG:4326', 'EPSG:3857');
var feature = new ol.Feature(geom);
feature.setStyle(ringStyleForAlt(data.rings[i].alt));
StaticFeatures.push(feature);
}
}
});
request.fail(function(jqxhr, status, error) {
// no rings available, do nothing
});
}
function ringStyleForAlt(altitude) {
return new ol.style.Style({
fill: null,
stroke: new ol.style.Stroke({
color: PlaneObject.prototype.hslRepr(PlaneObject.prototype.getAltitudeColor(altitude*3.281)), // converting from m to ft
width: 1
})
});
}
function createSiteCircleFeatures() {
const darkMaps = ['carto_dark_nolabels', 'carto_dark_all', 'esri_satellite'];
if (darkMaps.includes(MapType)) {
var SiteCircleColor = '#FFFFFF'
} else {
var SiteCircleColor = '#000000'
}
// Clear existing circles first
SiteCircleFeatures.forEach(function(circleFeature) {
StaticFeatures.remove(circleFeature);
});
SiteCircleFeatures.clear();
var circleStyle = function(distance) {
return new ol.style.Style({
fill: null,
stroke: new ol.style.Stroke({
color: SiteCircleColor,
width: 1
}),
text: new ol.style.Text({
font: '10px Helvetica Neue, sans-serif',
fill: new ol.style.Fill({ color: SiteCircleColor }),
offsetY: -8,
text: format_distance_long(distance, DisplayUnits, 0)
})
});
};
var conversionFactor = 1000.0;
if (DisplayUnits === "nautical") {
conversionFactor = 1852.0;
} else if (DisplayUnits === "imperial") {
conversionFactor = 1609.0;
}
for (var i=0; i < SiteCirclesCount; ++i) {
var distance = (SiteCirclesBaseDistance + (SiteCirclesInterval * i)) * conversionFactor;
var circle = make_geodesic_circle(SitePosition, distance, 360);
circle.transform('EPSG:4326', 'EPSG:3857');
var feature = new ol.Feature(circle);
feature.setStyle(circleStyle(distance));
StaticFeatures.push(feature);
SiteCircleFeatures.push(feature);
}
}
// This looks for planes to reap out of the master Planes variable
function reaper() {
//console.log("Reaping started..");
// Look for planes where we have seen no messages for >300 seconds
var newPlanes = [];
for (var i = 0; i < PlanesOrdered.length; ++i) {
var plane = PlanesOrdered[i];
if (plane.seen > 300) {
// Reap it.
plane.tr.parentNode.removeChild(plane.tr);
plane.tr = null;
delete Planes[plane.icao];
plane.destroy();
} else {
// Keep it.
newPlanes.push(plane);
}
};
PlanesOrdered = newPlanes;
refreshTableInfo();
refreshSelected();
refreshHighlighted();
}
// Page Title update function
function refreshPageTitle() {
if (!PlaneCountInTitle && !MessageRateInTitle) {
document.title = PageName;
return;
}
var aircraftCount = "";
var rate = "";
if (PlaneCountInTitle) {
aircraftCount += TrackedAircraft;
}
if (MessageRateInTitle && MessageRate) {
rate += ' - ' + MessageRate.toFixed(1) + ' msg/sec';
}
document.title = '(' + aircraftCount + ') ' + PageName + rate;
}
// Refresh the detail window about the plane
function refreshSelected() {
updateMessageRates();
refreshPageTitle();
var selected = false;
if (typeof SelectedPlane !== 'undefined' && SelectedPlane != "ICAO" && SelectedPlane != null) {
selected = Planes[SelectedPlane];
}
$('#dump1090_infoblock').css('display','block');
$('#skyaware_version').text('SkyAware ' + SkyAwareVersion);
$('#dump1090_total_ac').text(TrackedAircraft);
$('#dump1090_total_ac_positions').text(TrackedAircraftPositions);
$('#dump1090_total_history').text(TrackedHistorySize);
$('#active_filter_count').text(ActiveFilterCount);
if (ADSB_Enabled) {
$('#adsb_datasource_checkbox, #adsb_datasource_label').show();
$('#adsb_message_rate_row').show();
if (MessageRate !== null) {
$('#dump1090_message_rate').text(MessageRate.toFixed(1) + '/sec');
}
} else {
$('#adsb_datasource_checkbox, #adsb_datasource_label').hide();
$('#adsb_message_rate_row').hide();
}
if (UAT_Enabled) {
$('#uat_datasource_checkbox, #uat_datasource_label').show();
$('#uat_message_rate_row').show();
if (UatMessageRate !== null) {
$('#uat_message_rate').text(UatMessageRate.toFixed(1) + '/sec');
}
} else {
$('#uat_datasource_checkbox, #uat_datasource_label').hide();
$('#uat_message_rate_row').hide();
}
setSelectedInfoBlockVisibility();
if (!selected) {
return;
}
if (selected.flight !== null && selected.flight !== "") {
$('#selected_callsign').text(selected.flight);
} else {
$('#selected_callsign').text('n/a');
}
$('#selected_flightaware_link').html(getFlightAwareModeSLink(selected.icao, selected.flight, "Visit Flight Page"));
if (selected.registration !== null) {
$('#selected_registration').text(selected.registration);
} else {
$('#selected_registration').text("n/a");
}
if (selected.icaotype !== null) {
$('#selected_icaotype').text(selected.icaotype);
} else {
$('#selected_icaotype').text("n/a");
}
// Not using this logic for the redesigned info panel at the time, but leaving it in if/when adding it back
// var emerg = document.getElementById('selected_emergency');
// if (selected.squawk in SpecialSquawks) {
// emerg.className = SpecialSquawks[selected.squawk].cssClass;
// emerg.textContent = NBSP + 'Squawking: ' + SpecialSquawks[selected.squawk].text + NBSP ;
// } else {
// emerg.className = 'hidden';
// }
$("#selected_altitude").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_squawk').text('n/a');
} else {
$('#selected_squawk').text(selected.squawk);
}
$('#selected_speed').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));
$('#selected_vertical_rate').text(format_vert_rate_long(selected.baro_rate, DisplayUnits));
$('#selected_vertical_rate_geo').text(format_vert_rate_long(selected.geom_rate, DisplayUnits));
$('#selected_icao').text(selected.icao.toUpperCase());
$('#airframes_post_icao').attr('value',selected.icao);
$('#selected_track').text(format_track_long(selected.track));
if (selected.seen <= 1) {
$('#selected_seen').text('now');
} else {
$('#selected_seen').text(selected.seen.toFixed(1) + 's');
}
if (selected.seen_pos <= 1) {
$('#selected_seen_pos').text('now');
} else {
$('#selected_seen_pos').text(selected.seen_pos.toFixed(1) + 's');
}
$('#selected_country').text(selected.icaorange.country);
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');
$('#selected_follow').addClass('hidden');
} else {
$('#selected_position').text(format_latlng(selected.position));
$('#position_age').text(selected.seen_pos.toFixed(1) + 's');
$('#selected_follow').removeClass('hidden');
if (FollowSelected) {
$('#selected_follow').css('font-weight', 'bold');
OLMap.getView().setCenter(ol.proj.fromLonLat(selected.position));
} else {
$('#selected_follow').css('font-weight', 'normal');
}
}
var datasource = selected.getDataSource();
if (datasource === "uat") {
$('#selected_source').text("UAT");
} else if (datasource === "adsb_icao") {
$('#selected_source').text("ADS-B");
} else if (datasource === "tisb_trackfile" || datasource === "tisb_icao" || datasource === "tisb_other") {
$('#selected_source').text("TIS-B");
} else if (datasource === "mlat") {
$('#selected_source').text("MLAT");
} else {
$('#selected_source').text("Other");
}
$('#selected_category').text(selected.category ? selected.category : "n/a");
$('#selected_sitedist').text(format_distance_long(selected.sitedist, DisplayUnits));
$('#selected_rssi').text(selected.rssi.toFixed(1) + ' dBFS');
$('#selected_message_count').text(selected.messages);
$('#selected_photo_link').html(getFlightAwarePhotoLink(selected.registration));
$('#selected_altitude_geom').text(format_altitude_long(selected.alt_geom, selected.geom_rate, DisplayUnits));
$('#selected_mag_heading').text(format_track_long(selected.mag_heading));
$('#selected_true_heading').text(format_track_long(selected.true_heading));
$('#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_long(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 {
var sampleRate = "";
var 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 = "&gt; 1×10<sup>-3</sup>";
break;
case 1:
silDesc = "≤ 1×10<sup>-3</sup>";
break;
case 2:
silDesc = "≤ 1×10<sup>-5</sup>";
break;
case 3:
silDesc = "≤ 1×10<sup>-7</sup>";
break;
default:
silDesc = "n/a";
sampleRate = "";
break;
}
$('#selected_sil').html(silDesc + sampleRate);
}
if (selected.version == null) {
$('#selected_version').text('N/A');
} 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);
}
if (selected.uat_version == null) {
$('#selected_uat_version').text('N/A');
} else if (selected.uat_version == 0) {
$('#selected_uat_version').text('v0 (DO-282)');
} else if (selected.uat_version == 1) {
$('#selected_uat_version').text('v1 (DO-282A)');
} else if (selected.uat_version == 2) {
$('#selected_uat_version').text('v2 (DO-282B)');
} else {
$('#selected_uat_version').text('v' + selected.uat_version);
}
}
// Calculate 1090 and 978 Message rate using the rolling history of Message Counts recorded from processing aircraft.json
function updateMessageRates () {
if (MessageCountHistory.length > 1) {
var message_time_delta = MessageCountHistory[MessageCountHistory.length-1].time - MessageCountHistory[0].time;
var message_count_delta = MessageCountHistory[MessageCountHistory.length-1].messages - MessageCountHistory[0].messages;
if (message_time_delta > 0)
MessageRate = message_count_delta / message_time_delta;
} else {
MessageRate = null;
}
if (MessageCountHistory_UAT.length > 1) {
var message_time_delta = MessageCountHistory_UAT[MessageCountHistory_UAT.length-1].time - MessageCountHistory_UAT[0].time;
var message_count_delta = MessageCountHistory_UAT[MessageCountHistory_UAT.length-1].messages - MessageCountHistory_UAT[0].messages;
if (message_time_delta > 0)
UatMessageRate = message_count_delta / message_time_delta;
} else {
UatMessageRate = null;
}
}
function refreshHighlighted() {
// this is following nearly identical logic, etc, as the refreshSelected function, but doing less junk for the highlighted pane
var highlighted = false;
if (typeof HighlightedPlane !== 'undefined' && HighlightedPlane !== null) {
highlighted = Planes[HighlightedPlane];
}
var infoBox = $('#highlighted_infoblock');
// no highlighted plane or in process of removing plane
if (!highlighted || !highlighted.marker) {
infoBox.fadeOut();
return;
}
var mapCanvas = $('#map_canvas');
var markerCoordinates = highlighted.marker.getGeometry().getCoordinates();
var markerPosition = OLMap.getPixelFromCoordinate(markerCoordinates);
var x = markerPosition[0];
var y = markerPosition[1];
if (x < 0 || y < 0 || x > mapCanvas.width() || y > mapCanvas.height()) {
infoBox.fadeOut();
return;
}
x = x + 20;
y = y + 60;
var w = infoBox.outerWidth() + 20;
var h = infoBox.outerHeight();
if (x > mapCanvas.width() - w) {
x -= w + 20;
}
if (y > mapCanvas.height() - h) {
y -= h;
}
if (infoBox.css('visibility', 'visible')) {
infoBox.animate({ left: x, top: y }, 500);
} else {
infoBox.css({ left: x, top: y });
}
infoBox.fadeIn(100);
if (highlighted.flight !== null && highlighted.flight !== "") {
$('#highlighted_callsign').text(highlighted.flight);
} else {
$('#highlighted_callsign').text('n/a');
}
if (highlighted.icaotype !== null) {
$('#higlighted_icaotype').text(highlighted.icaotype);
} else {
$('#higlighted_icaotype').text("n/a");
}
var datasource = highlighted.getDataSource();
if (datasource === "uat") {
$('#highlighted_source').text("UAT");
} else if (datasource === "adsb_icao") {
$('#highlighted_source').text("ADS-B");
} else if (datasource === "tisb_trackfile" || datasource === "tisb_icao" || datasource === "tisb_other") {
$('#highlighted_source').text("TIS-B");
} else if (datasource === "mlat") {
$('#highlighted_source').text("MLAT");
} else {
$('#highlighted_source').text("Other");
}
if (highlighted.registration !== null) {
$('#highlighted_registration').text(highlighted.registration);
} else {
$('#highlighted_registration').text("n/a");
}
$('#highlighted_speed').text(format_speed_long(highlighted.speed, DisplayUnits));
$("#highlighted_altitude").text(format_altitude_long(highlighted.altitude, highlighted.vert_rate, DisplayUnits));
$('#highlighted_icao').text(highlighted.icao.toUpperCase());
}
function refreshClock() {
$('#clock_div').text(new Date().toLocaleString());
var c = setTimeout(refreshClock, 500);
}
function removeHighlight() {
HighlightedPlane = null;
refreshHighlighted();
}
// Refreshes the larger table of all the planes
function refreshTableInfo() {
var show_squawk_warning = false;
TrackedAircraft = 0
TrackedAircraftPositions = 0
TrackedHistorySize = 0
$(".altitudeUnit").text(get_unit_label("altitude", DisplayUnits));
$(".speedUnit").text(get_unit_label("speed", DisplayUnits));
$(".distanceUnit").text(get_unit_label("distance", DisplayUnits));
$(".verticalRateUnit").text(get_unit_label("verticalRate", DisplayUnits));
for (var i = 0; i < PlanesOrdered.length; ++i) {
var tableplane = PlanesOrdered[i];
TrackedHistorySize += tableplane.history_size;
if (tableplane.seen >= 58 || tableplane.isFiltered()) {
tableplane.tr.className = "plane_table_row hidden";
} else {
TrackedAircraft++;
var classes = "plane_table_row";
if (tableplane.position !== null && tableplane.seen_pos < 60) {
++TrackedAircraftPositions;
}
var datasource = tableplane.getDataSource();
if (datasource === "uat") {
classes += " uat";
} else if (datasource === "adsb_icao") {
classes += " vPosition";
} else if (datasource === "tisb_trackfile" || datasource === "tisb_icao" || datasource === "tisb_other") {
classes += " tisb";
} else if (datasource === "mlat") {
classes += " mlat";
} else {
classes += " other";
}
if (tableplane.icao == SelectedPlane)
classes += " selected";
if (tableplane.squawk in SpecialSquawks) {
classes = classes + " " + SpecialSquawks[tableplane.squawk].cssClass;
show_squawk_warning = true;
}
// ICAO doesn't change
if (tableplane.flight) {
tableplane.tr.cells[2].innerHTML = getFlightAwareModeSLink(tableplane.icao, tableplane.flight, tableplane.flight);
tableplane.tr.cells[2].className = "ident_normal";
} else if (tableplane.registration !== null) {
// Show registration with special styling if ident is not present
tableplane.tr.cells[2].innerHTML = getFlightAwareIdentLink(tableplane.registration, tableplane.registration);
tableplane.tr.cells[2].className = "ident_fallback";
} else {
tableplane.tr.cells[2].innerHTML = "";
tableplane.tr.cells[2].className = "";
}
tableplane.tr.cells[3].textContent = (tableplane.registration !== null ? tableplane.registration : "");
tableplane.tr.cells[4].textContent = (tableplane.icaotype !== null ? tableplane.icaotype : "");
tableplane.tr.cells[5].textContent = (tableplane.squawk !== null ? tableplane.squawk : "");
tableplane.tr.cells[6].innerHTML = format_altitude_brief(tableplane.altitude, tableplane.vert_rate, DisplayUnits);
tableplane.tr.cells[7].textContent = format_speed_brief(tableplane.gs, DisplayUnits);
tableplane.tr.cells[8].textContent = format_vert_rate_brief(tableplane.vert_rate, DisplayUnits);
tableplane.tr.cells[9].textContent = format_distance_brief(tableplane.sitedist, DisplayUnits);
tableplane.tr.cells[10].textContent = format_track_brief(tableplane.track);
tableplane.tr.cells[11].textContent = tableplane.messages;
tableplane.tr.cells[12].textContent = tableplane.seen.toFixed(0);
tableplane.tr.cells[13].textContent = (tableplane.rssi !== null ? tableplane.rssi : "");
tableplane.tr.cells[14].textContent = (tableplane.position !== null ? tableplane.position[1].toFixed(4) : "");
tableplane.tr.cells[15].textContent = (tableplane.position !== null ? tableplane.position[0].toFixed(4) : "");
tableplane.tr.cells[16].textContent = format_data_source(tableplane.getDataSource());
tableplane.tr.cells[17].innerHTML = getAirframesModeSLink(tableplane.icao);
tableplane.tr.cells[18].innerHTML = getFlightAwareModeSLink(tableplane.icao, tableplane.flight);
tableplane.tr.cells[19].innerHTML = getFlightAwarePhotoLink(tableplane.registration);
tableplane.tr.className = classes;
}
}
if (show_squawk_warning) {
$("#SpecialSquawkWarning").css('display','block');
} else {
$("#SpecialSquawkWarning").css('display','none');
}
resortTable();
}
//
// ---- table sorting ----
//
function compareAlpha(xa,ya) {
if (xa === ya)
return 0;
if (xa < ya)
return -1;
return 1;
}
function compareNumeric(xf,yf) {
if (Math.abs(xf - yf) < 1e-9)
return 0;
return xf - yf;
}
function sortByICAO() { sortBy('icao', compareAlpha, function(x) { return x.icao; }); }
function sortByFlight() { sortBy('flight', compareAlpha, function(x) { return x.flight ? x.flight : x.registration; }); }
function sortByRegistration() { sortBy('registration', compareAlpha, function(x) { return x.registration; }); }
function sortByAircraftType() { sortBy('icaotype', compareAlpha, function(x) { return x.icaotype; }); }
function sortBySquawk() { sortBy('squawk', compareAlpha, function(x) { return x.squawk; }); }
function sortByAltitude() { sortBy('altitude',compareNumeric, function(x) { return (x.altitude == "ground" ? -1e9 : x.altitude); }); }
function sortBySpeed() { sortBy('speed', compareNumeric, function(x) { return x.gs; }); }
function sortByVerticalRate() { sortBy('vert_rate', compareNumeric, function(x) { return x.vert_rate; }); }
function sortByDistance() { sortBy('sitedist',compareNumeric, function(x) { return x.sitedist; }); }
function sortByTrack() { sortBy('track', compareNumeric, function(x) { return x.track; }); }
function sortByMsgs() { sortBy('msgs', compareNumeric, function(x) { return x.messages; }); }
function sortBySeen() { sortBy('seen', compareNumeric, function(x) { return x.seen; }); }
function sortByCountry() { sortBy('country', compareAlpha, function(x) { return x.icaorange.country; }); }
function sortByRssi() { sortBy('rssi', compareNumeric, function(x) { return x.rssi }); }
function sortByLatitude() { sortBy('lat', compareNumeric, function(x) { return (x.position !== null ? x.position[1] : null) }); }
function sortByLongitude() { sortBy('lon', compareNumeric, function(x) { return (x.position !== null ? x.position[0] : null) }); }
function sortByDataSource() { sortBy('data_source', compareAlpha, function(x) { return x.getDataSource() } ); }
var sortId = '';
var sortCompare = null;
var sortExtract = null;
var sortAscending = true;
function sortFunction(x,y) {
var xv = x._sort_value;
var yv = y._sort_value;
// always sort missing values at the end, regardless of
// ascending/descending sort
if (xv == null && yv == null) return x._sort_pos - y._sort_pos;
if (xv == null) return 1;
if (yv == null) return -1;
var c = sortAscending ? sortCompare(xv,yv) : sortCompare(yv,xv);
if (c !== 0) return c;
return x._sort_pos - y._sort_pos;
}
function resortTable() {
// number the existing rows so we can do a stable sort
// regardless of whether sort() is stable or not.
// Also extract the sort comparison value.
for (var i = 0; i < PlanesOrdered.length; ++i) {
PlanesOrdered[i]._sort_pos = i;
PlanesOrdered[i]._sort_value = sortExtract(PlanesOrdered[i]);
}
PlanesOrdered.sort(sortFunction);
var tbody = document.getElementById('tableinfo').tBodies[0];
for (var i = 0; i < PlanesOrdered.length; ++i) {
tbody.appendChild(PlanesOrdered[i].tr);
}
}
function sortBy(id,sc,se) {
if (id !== 'data_source') {
$('#grouptype_checkbox').removeClass('settingsCheckboxChecked');
localStorage.setItem('groupByDataType', 'deselected');
}
if (id === sortId) {
sortAscending = !sortAscending;
PlanesOrdered.reverse(); // this correctly flips the order of rows that compare equal
} else {
sortAscending = true;
}
sortId = id;
sortCompare = sc;
sortExtract = se;
resortTable();
}
function selectPlaneByHex(hex,autofollow) {
//console.log("select: " + hex);
// If SelectedPlane has something in it, clear out the selected
if (SelectedAllPlanes) {
deselectAllPlanes();
}
if (SelectedPlane != null) {
Planes[SelectedPlane].selected = false;
Planes[SelectedPlane].clearLines();
Planes[SelectedPlane].updateMarker();
$(Planes[SelectedPlane].tr).removeClass("selected");
// scroll the infoblock back to the top for the next plane to be selected
$('.infoblock-container').scrollTop(0);
}
// If we are clicking the same plane, we are deselecting it.
// (unless it was a doubleclick..)
if (SelectedPlane === hex && !autofollow) {
hex = null;
}
if (hex !== null) {
// Assign the new selected
SelectedPlane = hex;
Planes[SelectedPlane].selected = true;
Planes[SelectedPlane].updateLines();
Planes[SelectedPlane].updateMarker();
$(Planes[SelectedPlane].tr).addClass("selected");
} else {
SelectedPlane = null;
}
if (SelectedPlane !== null && autofollow) {
FollowSelected = true;
if (OLMap.getView().getZoom() < 8)
OLMap.getView().setZoom(8);
} else {
FollowSelected = false;
}
refreshSelected();
refreshHighlighted();
}
function highlightPlaneByHex(hex) {
if (hex != null) {
HighlightedPlane = hex;
}
}
// loop through the planes and mark them as selected to show the paths for all planes
function selectAllPlanes() {
HighlightedPlane = null;
// if all planes are already selected, deselect them all
if (SelectedAllPlanes) {
deselectAllPlanes();
} else {
// If SelectedPlane has something in it, clear out the selected
if (SelectedPlane != null) {
Planes[SelectedPlane].selected = false;
Planes[SelectedPlane].clearLines();
Planes[SelectedPlane].updateMarker();
$(Planes[SelectedPlane].tr).removeClass("selected");
}
SelectedPlane = null;
SelectedAllPlanes = true;
for(var key in Planes) {
if (Planes[key].visible && !Planes[key].isFiltered()) {
Planes[key].selected = true;
Planes[key].updateLines();
Planes[key].updateMarker();
}
}
}
$('#selectall_checkbox').addClass('settingsCheckboxChecked');
refreshSelected();
refreshHighlighted();
}
// on refreshes, try to find new planes and mark them as selected
function selectNewPlanes() {
if (SelectedAllPlanes) {
for (var key in Planes) {
if (!Planes[key].visible || Planes[key].isFiltered()) {
Planes[key].selected = false;
Planes[key].clearLines();
Planes[key].updateMarker();
} else {
if (Planes[key].selected !== true) {
Planes[key].selected = true;
Planes[key].updateLines();
Planes[key].updateMarker();
}
}
}
}
}
function toggleGroupByDataType(switchToggle) {
if (typeof localStorage['groupByDataType'] === 'undefined') {
localStorage.setItem('groupByDataType', 'deselected');
}
var groupByDataType = localStorage.getItem('groupByDataType');
if (switchToggle === true) {
groupByDataType = (groupByDataType === 'deselected') ? 'selected' : 'deselected';
}
if (groupByDataType === 'deselected') {
$('#grouptype_checkbox').removeClass('settingsCheckboxChecked');
} else {
sortByDataSource();
$('#grouptype_checkbox').addClass('settingsCheckboxChecked');
}
localStorage.setItem('groupByDataType', groupByDataType);
}
function toggleAircraftLabels(switchToggle) {
if (typeof localStorage['showAircraftLabels'] === 'undefined') {
localStorage.setItem('showAircraftLabels', 'deselected');
}
var showAircraftLabels = localStorage.getItem('showAircraftLabels');
if (switchToggle === true) {
showAircraftLabels = (showAircraftLabels === 'deselected') ? 'selected' : 'deselected';
}
if (showAircraftLabels === 'deselected') {
// hide aircraft labels
AircraftLabels = false;
$('#aircraft_label_checkbox').removeClass('settingsCheckboxChecked');
} else {
// show aicraft labels
AircraftLabels = true;
$('#aircraft_label_checkbox').addClass('settingsCheckboxChecked');
}
localStorage.setItem('showAircraftLabels', showAircraftLabels);
}
function toggleAllPlanes(switchToggle) {
if (typeof localStorage['allPlanesSelection'] === 'undefined') {
localStorage.setItem('allPlanesSelection','deselected');
}
var allPlanesSelection = localStorage.getItem('allPlanesSelection');
if (switchToggle === true) {
allPlanesSelection = (allPlanesSelection === 'deselected') ? 'selected' : 'deselected';
}
if (allPlanesSelection === 'deselected') {
deselectAllPlanes();
} else {
selectAllPlanes();
}
localStorage.setItem('allPlanesSelection', allPlanesSelection);
}
// deselect all the planes
function deselectAllPlanes() {
for(var key in Planes) {
Planes[key].selected = false;
Planes[key].clearLines();
Planes[key].updateMarker();
$(Planes[key].tr).removeClass("selected");
}
$('#selectall_checkbox').removeClass('settingsCheckboxChecked');
SelectedPlane = null;
SelectedAllPlanes = false;
refreshSelected();
refreshHighlighted();
}
function toggleFollowSelected() {
FollowSelected = !FollowSelected;
if (FollowSelected && OLMap.getView().getZoom() < 8)
OLMap.getView().setZoom(8);
refreshSelected();
}
function resetMap() {
// Reset localStorage values and map settings
localStorage['CenterLat'] = CenterLat = DefaultCenterLat;
localStorage['CenterLon'] = CenterLon = DefaultCenterLon;
localStorage['ZoomLvl'] = ZoomLvl = DefaultZoomLvl;
// Reset to default range rings
localStorage['SiteCirclesCount'] = SiteCirclesCount = DefaultSiteCirclesCount;
localStorage['SiteCirclesBaseDistance'] = SiteCirclesBaseDistance = DefaultSiteCirclesBaseDistance;
localStorage['SiteCirclesInterval'] = SiteCirclesInterval = DefaultSiteCirclesInterval;
setRangeRings();
createSiteCircleFeatures();
// Set and refresh
OLMap.getView().setZoom(ZoomLvl);
OLMap.getView().setCenter(ol.proj.fromLonLat([CenterLon, CenterLat]));
selectPlaneByHex(null,false);
}
function updateMapSize() {
OLMap.updateSize();
}
function toggleSidebarVisibility(e) {
if (e) {
e.preventDefault();
}
$("#sidebar_container").toggle();
$("#expand_sidebar_control").toggle();
$("#toggle_sidebar_button").toggleClass("show_sidebar");
$("#toggle_sidebar_button").toggleClass("hide_sidebar");
updateMapSize();
}
function expandSidebar(e) {
if (e) {
e.preventDefault();
}
$("#map_container").hide()
$("#toggle_sidebar_control").hide();
$("#splitter").hide();
$("#sudo_buttons").hide();
$("#show_map_button").show();
$("#sidebar_container").width("100%");
setColumnVisibility();
setSelectedInfoBlockVisibility();
updateMapSize();
}
function showMap() {
$("#map_container").show()
$("#toggle_sidebar_control").show();
$("#splitter").show();
$("#sudo_buttons").show();
$("#show_map_button").hide();
$("#sidebar_container").width("470px");
setColumnVisibility();
setSelectedInfoBlockVisibility();
updateMapSize();
}
function showColumn(table, columnId, visible) {
var index = $(columnId).index();
if (index >= 0) {
var cells = $(table).find("td:nth-child(" + (index + 1).toString() + ")");
if (visible) {
cells.show();
} else {
cells.hide();
}
}
}
function setColumnVisibility() {
var mapIsVisible = $("#map_container").is(":visible");
var infoTable = $("#tableinfo");
var defaultCheckBoxes = [
'#icao_col_checkbox',
'#flag_col_checkbox',
'#ident_col_checkbox',
'#squawk_col_checkbox',
'#alt_col_checkbox',
'#speed_col_checkbox',
'#distance_col_checkbox',
'#heading_col_checkbox',
'#messages_col_checkbox',
'#msg_age_col_checkbox'
]
// Show default columns if checkboxes have not been set
for (var i=0; i < defaultCheckBoxes.length; i++) {
var checkBoxdiv = defaultCheckBoxes[i];
var columnDiv = checkbox_div_map.get(checkBoxdiv)
if (typeof localStorage[checkBoxdiv] === 'undefined') {
$(checkBoxdiv).addClass('settingsCheckboxChecked');
localStorage.setItem(checkBoxdiv, 'selected');
showColumn(infoTable, columnDiv, true);
}
}
// Now check local storage checkbox status
checkbox_div_map.forEach(function (div, checkbox) {
var status = localStorage.getItem(checkbox);
if (status === 'selected') {
$(checkbox).addClass('settingsCheckboxChecked');
showColumn(infoTable, div, true);
} else {
$(checkbox).removeClass('settingsCheckboxChecked');
showColumn(infoTable, div, false);
}
});
}
function setSelectedInfoBlockVisibility() {
var mapIsVisible = $("#map_container").is(":visible");
var planeSelected = (typeof SelectedPlane !== 'undefined' && SelectedPlane != null && SelectedPlane != "ICAO");
if (planeSelected && mapIsVisible) {
$('#selected_infoblock').show();
$('#sidebar_canvas').css('margin-bottom', $('#selected_infoblock').height() + 'px');
}
else {
$('#selected_infoblock').hide();
$('#sidebar_canvas').css('margin-bottom', 0);
}
}
// Reposition selected plane info box if it overlaps plane marker
function adjustSelectedInfoBlockPosition() {
if (typeof Planes === 'undefined' || typeof SelectedPlane === 'undefined' || Planes === null) {
return;
}
var selectedPlane = Planes[SelectedPlane];
if (selectedPlane === undefined || selectedPlane === null || selectedPlane.marker === undefined || selectedPlane.marker === null) {
return;
}
try {
// Get marker position
var marker = selectedPlane.marker;
var markerCoordinates = selectedPlane.marker.getGeometry().getCoordinates();
var markerPosition = OLMap.getPixelFromCoordinate(markerCoordinates);
// Get map size
var mapCanvas = $('#map_canvas');
var mapExtent = getExtent(0, 0, mapCanvas.width(), mapCanvas.height());
// Check for overlap
if (isPointInsideExtent(markerPosition[0], markerPosition[1], infoBoxExtent)) {
// Array of possible new positions for info box
var candidatePositions = [];
candidatePositions.push( { x: 40, y: 60 } );
candidatePositions.push( { x: 40, y: markerPosition[1] + 80 } );
// Find new position
for (var i = 0; i < candidatePositions.length; i++) {
var candidatePosition = candidatePositions[i];
var candidateExtent = getExtent(candidatePosition.x, candidatePosition.y, infoBox.outerWidth(), infoBox.outerHeight());
if (!isPointInsideExtent(markerPosition[0], markerPosition[1], candidateExtent) && isPointInsideExtent(candidatePosition.x, candidatePosition.y, mapExtent)) {
// Found a new position that doesn't overlap marker - move box to that position
infoBox.css("left", candidatePosition.x);
infoBox.css("top", candidatePosition.y);
return;
}
}
}
}
catch(e) { }
}
function getExtent(x, y, width, height) {
return {
xMin: x,
yMin: y,
xMax: x + width - 1,
yMax: y + height - 1,
};
}
function isPointInsideExtent(x, y, extent) {
return x >= extent.xMin && x <= extent.xMax && y >= extent.yMin && y <= extent.yMax;
}
function initializeUnitsSelector() {
// Get display unit preferences from local storage
if (!localStorage.getItem('displayUnits')) {
localStorage['displayUnits'] = DisplayUnits;
}
var displayUnits = localStorage['displayUnits'];
DisplayUnits = displayUnits;
setAltitudeLegend(displayUnits);
// Initialize drop-down
var unitsSelector = $("#units_selector");
unitsSelector.val(displayUnits);
unitsSelector.on("change", onDisplayUnitsChanged);
}
function onDisplayUnitsChanged(e) {
if (e) {
var displayUnits = e.target.value;
// Save display units to local storage
localStorage['displayUnits'] = displayUnits;
}
DisplayUnits = localStorage['displayUnits'];
setAltitudeLegend(DisplayUnits);
// Update filters
updatePlaneFilter();
// Refresh data
refreshTableInfo();
refreshSelected();
refreshHighlighted();
// Reset filter sliders on Display Units change
reset_filter_sliders();
// Redraw range rings
if (SitePosition !== null && SitePosition !== undefined && SiteCircles) {
createSiteCircleFeatures();
}
// Reset map scale line units
OLMap.getControls().forEach(function(control) {
if (control instanceof ol.control.ScaleLine) {
control.setUnits(DisplayUnits);
}
});
}
function setAltitudeLegend(units) {
if (units === 'metric') {
$('#altitude_chart_button').addClass('altitudeMeters');
} else {
$('#altitude_chart_button').removeClass('altitudeMeters');
}
}
function onFilterByAltitude() {
updatePlaneFilter();
refreshTableInfo();
var selectedPlane = Planes[SelectedPlane];
if (selectedPlane !== undefined && selectedPlane !== null && selectedPlane.isFiltered()) {
SelectedPlane = null;
selectedPlane.selected = false;
selectedPlane.clearLines();
selectedPlane.updateMarker();
refreshSelected();
refreshHighlighted();
}
}
function onFilterBySpeed() {
updatePlaneFilter();
refreshTableInfo();
}
function onFilterByAircraftType(e) {
e.preventDefault();
updatePlaneFilter();
refreshTableInfo();
}
function onResetAircraftTypeFilter(e) {
$("#aircraft_type_filter").val("");
updatePlaneFilter();
refreshTableInfo();
}
function onFilterByAircraftIdent(e) {
e.preventDefault();
updatePlaneFilter();
refreshTableInfo();
}
function onResetAircraftIdentFilter(e) {
$("#aircraft_ident_filter").val("");
updatePlaneFilter();
refreshTableInfo();
}
function filterGroundVehicles(switchFilter) {
if (typeof localStorage['groundVehicleFilter'] === 'undefined') {
localStorage.setItem('groundVehicleFilter' , 'not_filtered');
}
var groundFilter = localStorage.getItem('groundVehicleFilter');
if (switchFilter === true) {
groundFilter = (groundFilter === 'not_filtered') ? 'filtered' : 'not_filtered';
}
if (groundFilter === 'not_filtered') {
$('#groundvehicle_filter').addClass('settingsCheckboxChecked');
} else {
$('#groundvehicle_filter').removeClass('settingsCheckboxChecked');
}
localStorage.setItem('groundVehicleFilter',groundFilter);
PlaneFilter.groundVehicles = groundFilter;
}
function filterBlockedMLAT(switchFilter) {
if (typeof localStorage['blockedMLATFilter'] === 'undefined') {
localStorage.setItem('blockedMLATFilter','not_filtered');
}
var blockedMLATFilter = localStorage.getItem('blockedMLATFilter');
if (switchFilter === true) {
blockedMLATFilter = (blockedMLATFilter === 'not_filtered') ? 'filtered' : 'not_filtered';
}
if (blockedMLATFilter === 'not_filtered') {
$('#blockedmlat_filter').addClass('settingsCheckboxChecked');
} else {
$('#blockedmlat_filter').removeClass('settingsCheckboxChecked');
}
localStorage.setItem('blockedMLATFilter', blockedMLATFilter);
PlaneFilter.blockedMLAT = blockedMLATFilter;
}
function toggleAltitudeChart(switchToggle) {
if (typeof localStorage['altitudeChart'] === 'undefined') {
localStorage.setItem('altitudeChart','show');
}
var altitudeChartDisplay = localStorage.getItem('altitudeChart');
if (switchToggle === true) {
altitudeChartDisplay = (altitudeChartDisplay === 'show') ? 'hidden' : 'show';
}
// if you're using custom colors always hide the chart
if (customAltitudeColors === true) {
altitudeChartDisplay = 'hidden';
// also hide the control option
$('#altitude_chart_container').hide();
}
if (altitudeChartDisplay === 'show') {
$('#altitude_checkbox').addClass('settingsCheckboxChecked');
$('#altitude_chart').show();
} else {
$('#altitude_checkbox').removeClass('settingsCheckboxChecked');
$('#altitude_chart').hide();
}
localStorage.setItem('altitudeChart', altitudeChartDisplay);
}
function updatePlaneFilter() {
// Get min/max altitude values from slider
var minAltitude = document.getElementById('minAltitudeText').innerHTML.trim();
var maxAltitude = document.getElementById('maxAltitudeText').innerHTML.trim();
PlaneFilter.minAltitude = minAltitude;
PlaneFilter.maxAltitude = maxAltitude;
PlaneFilter.altitudeUnits = DisplayUnits;
// Get min/max speed values from slider
var minSpeedFilter = document.getElementById('minSpeedText').innerHTML.trim();
var maxSpeedFilter = document.getElementById('maxSpeedText').innerHTML.trim();
PlaneFilter.minSpeedFilter = minSpeedFilter;
PlaneFilter.maxSpeedFilter = maxSpeedFilter;
PlaneFilter.speedUnits = DisplayUnits;
// Get aircraft type code filter from input box
var aircraftTypeCode = $("#aircraft_type_filter").val().trim().toUpperCase()
if (aircraftTypeCode === "") {
aircraftTypeCode = undefined
}
// Get aircraft ident filter from input box
var aircraftIdent = $("#aircraft_ident_filter").val().trim().toUpperCase()
if (aircraftIdent === "") {
aircraftIdent = undefined
}
PlaneFilter.aircraftTypeCode = aircraftTypeCode;
PlaneFilter.aircraftIdent = aircraftIdent;
var altitudeFilterSet = (PlaneFilter.minAltitude == DefaultMinMaxFilters[DisplayUnits].min && PlaneFilter.maxAltitude == DefaultMinMaxFilters[DisplayUnits].maxAltitude) ? 0 : 1;
var speedFilterSet = (PlaneFilter.minSpeedFilter == DefaultMinMaxFilters[DisplayUnits].min && PlaneFilter.maxSpeedFilter == DefaultMinMaxFilters[DisplayUnits].maxSpeed) ? 0 : 1;
var aircraftTypeFilterSet = (PlaneFilter.aircraftTypeCode == undefined) ? 0 : 1;
var aircraftIdentFilterSet = (PlaneFilter.aircraftIdent == undefined) ? 0 : 1;
ActiveFilterCount = altitudeFilterSet + speedFilterSet + aircraftTypeFilterSet + aircraftIdentFilterSet;
var filter = document.getElementById('filter_button');
filter.style.backgroundColor = (ActiveFilterCount > 0) ? "Lime" : "#FEBC11";
}
function refreshDataSourceFilters () {
PlaneFilter.ADSB = (localStorage.getItem('sourceADSBFilter') === 'selected') ? true : false;
PlaneFilter.MLAT = (localStorage.getItem('sourceMLATFilter') === 'selected') ? true : false;
PlaneFilter.Other = (localStorage.getItem('sourceOtherFilter') === 'selected') ? true : false;
PlaneFilter.TISB = (localStorage.getItem('sourceTISBFilter') === 'selected') ? true : false;
PlaneFilter.UAT = (localStorage.getItem('sourceUATFilter') === 'selected') ? true : false;
}
function getFlightAwareIdentLink(ident, linkText) {
if (ident !== null && ident !== "") {
if (!linkText) {
linkText = ident;
}
return "<a target=\"_blank\" href=\"https://flightaware.com/live/flight/" + ident.trim() + "\"><span title=\"Bold ident indicates this is an aircraft registration number\"> " + linkText + "</span></a>";
}
return "";
}
function getFlightAwareModeSLink(code, ident, linkText) {
if (code !== null && code.length > 0 && code[0] !== '~' && code !== "000000") {
if (!linkText) {
linkText = "FlightAware: " + code.toUpperCase();
}
var linkHtml = "<a target=\"_blank\" href=\"https://flightaware.com/live/modes/" + code ;
if (ident !== null && ident !== "") {
linkHtml += "/ident/" + ident.trim();
}
linkHtml += "/redirect\">" + linkText + "</a>";
return linkHtml;
}
return "";
}
function getFlightAwarePhotoLink(registration) {
if (registration !== null && registration !== "") {
return "<a target=\"_blank\" href=\"https://flightaware.com/photos/aircraft/" + registration.replace(/[^0-9a-z]/ig,'') + "\">See Photos</a>";
}
return "";
}
function getAirframesModeSLink(code) {
if (code !== null && code.length > 0 && code[0] !== '~' && code !== "000000") {
return "<a href=\"http://www.airframes.org/\" onclick=\"$('#airframes_post_icao').attr('value','" + code + "'); document.getElementById('horrible_hack').submit.call(document.getElementById('airframes_post')); return false;\">Airframes.org: " + code.toUpperCase() + "</a>";
}
return "";
}
// takes in an elemnt jQuery path and the OL3 layer name and toggles the visibility based on clicking it
function toggleLayer(element, layer) {
// set initial checked status
ol.control.LayerSwitcher.forEachRecursive(layerGroup, function(lyr) {
if (lyr.get('name') === layer && lyr.getVisible()) {
$(element).addClass('settingsCheckboxChecked');
}
});
$(element).on('click', function() {
var visible = false;
if ($(element).hasClass('settingsCheckboxChecked')) {
visible = true;
}
ol.control.LayerSwitcher.forEachRecursive(layerGroup, function(lyr) {
if (lyr.get('name') === layer) {
if (visible) {
lyr.setVisible(false);
$(element).removeClass('settingsCheckboxChecked');
} else {
lyr.setVisible(true);
$(element).addClass('settingsCheckboxChecked');
}
}
});
});
}
// check status.json if it has a serial number for a flightfeeder
function flightFeederCheck() {
$.ajax('/status.json', {
success: function(data) {
if (data.type === "flightfeeder") {
isFlightFeeder = true;
updatePiAwareOrFlightFeeder();
}
}
})
}
function setStatsLink() {
$.ajax('/status.json', {
success: function(data) {
if (data.unclaimed_feeder_id) {
var claim_link = "https://flightaware.com/adsb/piaware/claim/" + data.unclaimed_feeder_id;
$('#stats_page_button').text("Claim this feeder on FlightAware")
myAdsbStatsSiteUrl = claim_link;
} else if (data.site_url) {
myAdsbStatsSiteUrl = data.site_url;
}
}
}).fail(function() {
$('#stats_page_button').hide();
});
}
// updates the page to replace piaware with flightfeeder references
function updatePiAwareOrFlightFeeder() {
if (isFlightFeeder) {
$('.piAwareLogo').hide();
$('.flightfeederLogo').show();
PageName = 'FlightFeeder SkyAware';
} else {
$('.flightfeederLogo').hide();
$('.piAwareLogo').show();
PageName = 'PiAware SkyAware';
}
refreshPageTitle();
}
// Function to hide banner (ex. for a kiosk to show maximum data possible)
function hideBanner() {
document.getElementById("header").style.display = 'none';
document.getElementById("layout_container").style.height = '100%';
updateMapSize();
}
// Helper function to restrict the range of the inputs
function restrictUrlRequest(c) {
let v = parseFloat(c);
if (v < 0) {
v = 0;
} else if (v > 5) {
v = 5;
}
return v;
}
// Function to zoom, but not by too much per 'amount'
function zoomMap(c, zoomOut) {
c = restrictUrlRequest(c);
ZoomLvl = OLMap.getView().getZoom();
if (zoomOut) {
ZoomLvl *= Math.pow(0.95, c);
} else {
ZoomLvl /= Math.pow(0.95, c);
}
localStorage['ZoomLvl'] = ZoomLvl;
OLMap.getView().setZoom(ZoomLvl);
}
// Function to move map at 0.005% of the extent per 'move'
function moveMap(c, moveVertical, moveDownLeft) {
c = restrictUrlRequest(c);
let cn = OLMap.getView().getCenter();
let dist = 0;
if (moveVertical) {
dist = ol.extent.getHeight(OLMap.getView().getProjection().getExtent());
} else {
dist = ol.extent.getWidth(OLMap.getView().getProjection().getExtent());
}
let d = c * (dist * .005);
// 'up' or 'right' needs a negative number
if (moveDownLeft) {
d *= -1.0;
}
if (moveVertical) {
ol.coordinate.add(cn, [0, d]);
} else {
ol.coordinate.add(cn, [d, 0]);
}
OLMap.getView().setCenter(cn);
}
// Function to set displayUnits
function setDisplayUnits(units) {
if (units === 'nautical') {
localStorage['displayUnits'] = "nautical";
} else if (units === 'metric') {
localStorage['displayUnits'] = "metric";
} else if (units === 'imperial') {
localStorage['displayUnits'] = "imperial";
}
onDisplayUnitsChanged();
}
// Function to set range ring visibility
function setRangeRingVisibility (showhide) {
var show = null;
if (showhide === 'hide') {
$('#sitepos_checkbox').removeClass('settingsCheckboxChecked')
show = false;
} else if (showhide === 'show') {
$('#sitepos_checkbox').addClass('settingsCheckboxChecked')
show = true;
} else {
return
}
ol.control.LayerSwitcher.forEachRecursive(layerGroup, function(lyr) {
if (lyr.get('name') === 'site_pos') {
lyr.setVisible(show);
}
});
}
// simple function to set range ring count
function setRingCount(val) {
localStorage['SiteCirclesCount'] = val;
setRangeRings();
createSiteCircleFeatures();
}
// simple function to set range ring distance
function setRingBaseDistance(val) {
localStorage['SiteCirclesBaseDistance'] = val;
setRangeRings();
createSiteCircleFeatures();
}
// simple function to set range ring interval
function setRingInterval(val) {
localStorage['SiteCirclesInterval'] = val;
setRangeRings();
createSiteCircleFeatures();
}
// Set range ring globals and populate form values
function setRangeRings() {
SiteCirclesCount = Number(localStorage['SiteCirclesCount']) || DefaultSiteCirclesCount;
SiteCirclesBaseDistance = Number(localStorage['SiteCirclesBaseDistance']) || DefaultSiteCirclesBaseDistance;
SiteCirclesInterval = Number(localStorage['SiteCirclesInterval']) || DefaultSiteCirclesInterval;
// Populate text fields with current values
$('#range_ring_count').val(SiteCirclesCount);
$('#range_ring_base').val(SiteCirclesBaseDistance);
$('#range_ring_interval').val(SiteCirclesInterval);
}
// redraw range rings with form values
function onSetRangeRings() {
// Save state to localStorage
localStorage.setItem('SiteCirclesCount', parseFloat($("#range_ring_count").val().trim()));
localStorage.setItem('SiteCirclesBaseDistance', parseFloat($("#range_ring_base").val().trim()));
localStorage.setItem('SiteCirclesInterval', parseFloat($("#range_ring_interval").val().trim()));
setRangeRings();
createSiteCircleFeatures();
}
function toggleColumn(div, checkbox, toggled) {
if (typeof localStorage[checkbox] === 'undefined') {
localStorage.setItem(checkbox, 'deselected');
}
var status = localStorage.getItem(checkbox);
var infoTable = $("#tableinfo");
if (toggled === true) {
status = (status === 'deselected') ? 'selected' : 'deselected';
}
// Toggle checkbox and column visibility
if (status === 'selected') {
$(checkbox).addClass('settingsCheckboxChecked');
showColumn(infoTable, div, true);
} else {
$(checkbox).removeClass('settingsCheckboxChecked');
showColumn(infoTable, div, false);
$('#select_all_column_checkbox').removeClass('settingsCheckboxChecked');
localStorage.setItem('selectAllColumnsCheckbox', 'deselected');
}
localStorage.setItem(checkbox, status);
}
function toggleAllColumns(switchToggle) {
if (typeof localStorage['selectAllColumnsCheckbox'] === 'undefined') {
localStorage.setItem('selectAllColumnsCheckbox','deselected');
}
var infoTable = $("#tableinfo");
var selectAllColumnsCheckbox = localStorage.getItem('selectAllColumnsCheckbox');
if (switchToggle === true) {
selectAllColumnsCheckbox = (selectAllColumnsCheckbox === 'deselected') ? 'selected' : 'deselected';
checkbox_div_map.forEach(function (div, checkbox) {
if (selectAllColumnsCheckbox === 'deselected') {
$('#select_all_column_checkbox').removeClass('settingsCheckboxChecked');
$(checkbox).removeClass('settingsCheckboxChecked');
showColumn(infoTable, div, false);
} else {
$('#select_all_column_checkbox').addClass('settingsCheckboxChecked');
$(checkbox).addClass('settingsCheckboxChecked');
showColumn(infoTable, div, true);
}
localStorage.setItem(checkbox, selectAllColumnsCheckbox);
});
};
if (selectAllColumnsCheckbox === 'deselected') {
$('#select_all_column_checkbox').removeClass('settingsCheckboxChecked');
} else {
$('#select_all_column_checkbox').addClass('settingsCheckboxChecked');
}
localStorage.setItem('selectAllColumnsCheckbox', selectAllColumnsCheckbox);
}
function toggleADSBAircraft(switchFilter) {
if (typeof localStorage['sourceADSBFilter'] === 'undefined') {
localStorage.setItem('sourceADSBFilter','selected');
}
var sourceADSBFilter = localStorage.getItem('sourceADSBFilter');
if (switchFilter === true) {
sourceADSBFilter = (sourceADSBFilter === 'deselected') ? 'selected' : 'deselected';
}
if (sourceADSBFilter === 'deselected') {
$('#adsb_datasource_checkbox').removeClass('sourceCheckboxChecked');
} else {
$('#adsb_datasource_checkbox').addClass('sourceCheckboxChecked');
}
localStorage.setItem('sourceADSBFilter', sourceADSBFilter);
}
function toggleUATAircraft(switchFilter) {
if (typeof localStorage['sourceUATFilter'] === 'undefined') {
localStorage.setItem('sourceUATFilter','selected');
}
var sourceUATFilter = localStorage.getItem('sourceUATFilter');
if (switchFilter === true) {
sourceUATFilter = (sourceUATFilter === 'deselected') ? 'selected' : 'deselected';
}
if (sourceUATFilter === 'deselected') {
$('#uat_datasource_checkbox').removeClass('sourceCheckboxChecked');
} else {
$('#uat_datasource_checkbox').addClass('sourceCheckboxChecked');
}
localStorage.setItem('sourceUATFilter', sourceUATFilter);
}
function toggleMLATAircraft(switchFilter) {
if (typeof localStorage['sourceMLATFilter'] === 'undefined') {
localStorage.setItem('sourceMLATFilter','selected');
}
var sourceMLATFilter = localStorage.getItem('sourceMLATFilter');
if (switchFilter === true) {
sourceMLATFilter = (sourceMLATFilter === 'deselected') ? 'selected' : 'deselected';
}
if (sourceMLATFilter === 'deselected') {
$('#mlat_datasource_checkbox').removeClass('sourceCheckboxChecked');
} else {
$('#mlat_datasource_checkbox').addClass('sourceCheckboxChecked');
}
localStorage.setItem('sourceMLATFilter', sourceMLATFilter);
}
function toggleOtherAircraft(switchFilter) {
if (typeof localStorage['sourceOtherFilter'] === 'undefined') {
localStorage.setItem('sourceOtherFilter','selected');
}
var sourceOtherFilter = localStorage.getItem('sourceOtherFilter');
if (switchFilter === true) {
sourceOtherFilter = (sourceOtherFilter === 'deselected') ? 'selected' : 'deselected';
}
if (sourceOtherFilter === 'deselected') {
$('#other_datasource_checkbox').removeClass('sourceCheckboxChecked');
} else {
$('#other_datasource_checkbox').addClass('sourceCheckboxChecked');
}
localStorage.setItem('sourceOtherFilter', sourceOtherFilter);
}
function toggleTISBAircraft(switchFilter) {
if (typeof localStorage['sourceTISBFilter'] === 'undefined') {
localStorage.setItem('sourceTISBFilter','selected');
}
var sourceTISBFilter = localStorage.getItem('sourceTISBFilter');
if (switchFilter === true) {
sourceTISBFilter = (sourceTISBFilter === 'deselected') ? 'selected' : 'deselected';
}
if (sourceTISBFilter === 'deselected') {
$('#tisb_datasource_checkbox').removeClass('sourceCheckboxChecked');
} else {
$('#tisb_datasource_checkbox').addClass('sourceCheckboxChecked');
}
localStorage.setItem('sourceTISBFilter', sourceTISBFilter);
}