2020-03-27 08:45:56 +01:00
// Some global letiables are defined in early.js
2020-01-03 21:16:40 +01:00
// 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" ;
2020-03-27 08:45:56 +01:00
// Define our global letiables
let OLMap = null ;
let StaticFeatures = new ol . source . Vector ( ) ;
let PlaneIconFeatures = new ol . source . Vector ( ) ;
let trailGroup = new ol . Collection ( ) ;
let iconLayer ;
let trailLayers ;
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 infoBoxOriginalPosition = { } ;
let customAltitudeColors = true ;
let loadtime = "loadtime" ;
let loadFinished = false ;
let mapResizeTimeout ;
let pointerMoveTimeout ;
let refresh ;
let scaleFactor ;
let debugTracks = false ;
let debugAll = false ;
let trackLabels = false ;
let fragment ;
let grouptype _checkbox ;
let multiSelect = false ;
let uat _data = null ;
let enableLabels = false ;
let extendedLabels = 0 ;
let mapIsVisible = true ;
let columnVis = Array ( 30 ) . fill ( true ) ;
let emptyStyle = new ol . style . Style ( { } ) ;
let show _squawk _warning _cache = false ;
let tableInView = false ;
let historyOutdated = false ;
let onlyMLAT = false ;
let onlyMilitary = false ;
let onlyADSB = false ;
let onlySelected = false ;
let fetchingPf = false ;
let reaping = false ;
let debug = false ;
let debugJump = false ;
let jumpTo = null ;
let noMLAT = false ;
let noVanish = false ;
let sidebarVisible = true ;
let filterTracks = false ;
let refreshId = 0 ;
let globeIndexGrid = 0 ;
let globeIndexNow = { } ;
let globeIndexSpecialTiles ;
let globeSimLoad = 4 ;
let globeTableLimit = 80 ;
let lastRealExtent ;
let lastGlobeExtent ;
let lastRenderExtent ;
let globeIndexExtent ;
let PendingFetches = 0 ;
let lastRequestFiles = 0 ;
let debugCounter = 0 ;
let selectedPhotoCache = null ;
let pathName = null ;
let icaoFilter = null ;
let showTrace = false ;
let showTraceExit = false ;
let traceDate = null ;
let traceDateString = null ;
2020-03-30 10:46:26 +02:00
let traceDay = null ;
2020-03-27 08:45:56 +01:00
let icaoParam = null ;
let globalScale = 1 ;
let newWidth = lineWidth ;
let SitePosInitialized = false ;
let SiteOverride = false ;
let airport = null ;
let labelFill = null ;
let blackFill = null ;
let labelStroke = null ;
let legSel = - 1 ;
2020-03-25 23:27:54 +01:00
let geoMag = null ;
2020-02-28 19:17:19 +01:00
2020-03-27 08:45:56 +01:00
let shareLink = '' ;
2020-02-29 20:22:16 +01:00
2020-03-27 08:45:56 +01:00
let onMobile = false ;
2020-02-29 17:34:46 +01:00
2020-03-27 08:45:56 +01:00
let SpecialSquawks = {
2020-01-03 21:16:40 +01:00
'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
2020-03-27 08:45:56 +01:00
let CenterLat , CenterLon , ZoomLvl , ZoomLvlCache ;
let zoomTimeout ;
let noMovement ;
let checkMoveZoom ;
let checkMoveCenter = [ 0 , 0 ] ;
2020-01-03 21:16:40 +01:00
2020-03-27 08:45:56 +01:00
let PlaneRowTemplate = null ;
let tableinfoFragment = null ;
2020-01-03 21:16:40 +01:00
2020-03-27 08:45:56 +01:00
let TrackedAircraft = 0 ;
let globeTrackedAircraft = 0 ;
let TrackedAircraftPositions = 0 ;
let TrackedHistorySize = 0 ;
2020-01-03 21:16:40 +01:00
2020-03-27 08:45:56 +01:00
let SitePosition = null ;
2020-01-03 21:16:40 +01:00
// timestamps
2020-03-27 08:45:56 +01:00
let now = 0 ;
2020-03-30 10:46:26 +02:00
let last = 0 ;
let uat _now = 0 ;
let uat _last = 0 ;
let today = 0 ;
2020-03-27 08:45:56 +01:00
let StaleReceiverCount = 0 ;
let FetchPending = [ ] ;
let FetchPendingUAT = null ;
2020-01-03 21:16:40 +01:00
2020-03-27 08:45:56 +01:00
let MessageCountHistory = [ ] ;
let MessageRate = 0 ;
2020-01-03 21:16:40 +01:00
2020-03-27 08:45:56 +01:00
let layers ;
let layers _group ;
2020-01-03 21:16:40 +01:00
// piaware vs flightfeeder
2020-03-27 08:45:56 +01:00
let isFlightFeeder = false ;
2020-01-03 21:16:40 +01:00
2020-03-27 08:45:56 +01:00
let estimateStyle = new ol . style . Style ( {
2020-02-07 07:17:01 +01:00
stroke : new ol . style . Stroke ( {
color : '#808080' ,
width : 1.2 * lineWidth ,
} )
} ) ;
2020-03-27 08:45:56 +01:00
let estimateStyleSlim = new ol . style . Style ( {
2020-02-07 07:17:01 +01:00
stroke : new ol . style . Stroke ( {
color : '#808080' ,
width : 0.4 * lineWidth ,
} )
} ) ;
const nullStyle = new ol . style . Style ( { } ) ;
2020-03-27 08:45:56 +01:00
let badLine = new ol . style . Style ( {
2020-02-07 07:17:01 +01:00
stroke : new ol . style . Stroke ( {
color : '#FF0000' ,
width : 2 * lineWidth ,
} )
} ) ;
2020-03-27 08:45:56 +01:00
let badLineMlat = new ol . style . Style ( {
2020-02-07 07:17:01 +01:00
stroke : new ol . style . Stroke ( {
color : '#FFA500' ,
width : 2 * lineWidth ,
} )
} ) ;
2020-03-27 08:45:56 +01:00
let badDot = new ol . style . Style ( {
2020-02-07 07:17:01 +01:00
image : new ol . style . Circle ( {
radius : 3.5 * lineWidth ,
fill : new ol . style . Fill ( {
color : '#FF0000' ,
} )
} ) ,
} ) ;
2020-03-27 08:45:56 +01:00
let badDotMlat = new ol . style . Style ( {
2020-02-07 07:17:01 +01:00
image : new ol . style . Circle ( {
radius : 3.5 * lineWidth ,
fill : new ol . style . Fill ( {
color : '#FFA500' ,
} )
} ) ,
} ) ;
2020-01-03 21:16:40 +01:00
function processAircraft ( ac , init , uat ) {
2020-03-27 08:45:56 +01:00
let isArray = Array . isArray ( ac ) ;
let hex = isArray ? ac [ 0 ] : ac . hex ;
let plane = null ;
2020-01-03 21:16:40 +01:00
// Do we already have this plane object in Planes?
// If not make it.
/ *
if ( ac . messages < 2 ) {
return ;
}
* /
2020-01-14 15:02:04 +01:00
if ( icaoFilter && ! icaoFilter . includes ( hex ) )
return ;
2020-01-03 21:16:40 +01:00
plane = Planes [ hex ] ;
if ( uatNoTISB && ! init && 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" ;
}
}
// Call the function update
2020-03-06 00:27:30 +01:00
if ( globeIndex ) {
if ( ! onlyMilitary || plane . military )
plane . updateData ( now , last , ac , init ) ;
2020-03-24 21:25:08 +01:00
else
plane . last _message _time = now - ac . seen ;
2020-03-06 00:27:30 +01:00
} else if ( uat ) {
2020-01-03 21:16:40 +01:00
if ( plane . receiver == "uat" || ac . seen _pos < 1.8 || init ) {
plane . receiver = "uat" ;
plane . updateData ( uat _now , uat _last , ac , init ) ;
}
} else {
if ( plane . receiver == "1090" || ac . seen _pos < 1.8 || init ) {
plane . receiver = "1090" ;
plane . updateData ( now , last , ac , init ) ;
}
}
}
function processReceiverUpdate ( data , init ) {
// update now and last
2020-03-27 08:45:56 +01:00
let uat = false ;
2020-01-03 21:16:40 +01:00
if ( data . uat _978 == "true" ) {
uat = true ;
uat _last = uat _now ;
uat _now = data . now ;
} else {
2020-01-14 10:27:36 +01:00
if ( data . now > now || globeIndex ) {
2020-01-03 21:16:40 +01:00
last = now ;
now = data . now ;
2020-03-30 10:46:26 +02:00
today = new Date ( now * 1000 ) . getDate ( ) ;
2020-01-03 21:16:40 +01:00
}
}
// Loop through all the planes in the data packet
2020-03-27 08:45:56 +01:00
let acs = data . aircraft ;
2020-01-03 21:16:40 +01:00
if ( ! uat && ! init && ! globeIndex ) {
// Detect stats reset
if ( MessageCountHistory . length > 0 && MessageCountHistory [ MessageCountHistory . length - 1 ] . messages > data . messages ) {
MessageCountHistory = [ { 'time' : MessageCountHistory [ MessageCountHistory . length - 1 ] . time ,
'messages' : 0 } ] ;
}
// Note the message count in the history
MessageCountHistory . push ( { 'time' : now , 'messages' : data . messages } ) ;
// .. and clean up any old values
if ( ( now - MessageCountHistory [ 0 ] . time ) > 30 )
MessageCountHistory . shift ( ) ;
if ( MessageCountHistory . length > 1 ) {
2020-03-27 08:45:56 +01:00
let message _time _delta = MessageCountHistory [ MessageCountHistory . length - 1 ] . time - MessageCountHistory [ 0 ] . time ;
let message _count _delta = MessageCountHistory [ MessageCountHistory . length - 1 ] . messages - MessageCountHistory [ 0 ] . messages ;
2020-01-03 21:16:40 +01:00
if ( message _time _delta > 0 )
MessageRate = message _count _delta / message _time _delta ;
} else {
MessageRate = null ;
}
}
2020-03-27 08:45:56 +01:00
for ( let j = 0 ; j < acs . length ; j ++ ) {
2020-01-03 21:16:40 +01:00
processAircraft ( acs [ j ] , init , uat ) ;
}
// jquery stuff might still have references to the json, so null the
// aircraft array to make it easier for the garbage collector.
2020-03-11 20:11:52 +01:00
//data.aircraft = null;
2020-01-03 21:16:40 +01:00
}
function fetchData ( ) {
2020-01-25 18:23:39 +01:00
ZoomLvl = OLMap . getView ( ) . getZoom ( ) ;
2020-03-27 08:45:56 +01:00
let center = ol . proj . toLonLat ( OLMap . getView ( ) . getCenter ( ) , OLMap . getView ( ) . getProjection ( ) ) ;
2020-01-25 18:23:39 +01:00
localStorage [ 'CenterLon' ] = CenterLon = center [ 0 ] ;
localStorage [ 'CenterLat' ] = CenterLat = center [ 1 ] ;
2020-01-03 21:16:40 +01:00
clearTimeout ( refreshId ) ;
2020-01-18 07:43:07 +01:00
refreshId = setTimeout ( fetchData , refreshInt ( ) ) ;
2020-01-22 20:49:11 +01:00
if ( showTrace )
return ;
2020-01-03 21:16:40 +01:00
if ( PendingFetches > 0 )
return ;
2020-03-27 08:45:56 +01:00
for ( let i in FetchPending ) {
2020-01-03 21:16:40 +01:00
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 ;
2020-01-25 02:50:55 +01:00
//console.timeEnd("Starting Fetch");
//console.time("Starting Fetch");
2020-01-25 02:27:52 +01:00
2020-01-03 21:16:40 +01:00
2020-03-27 08:45:56 +01:00
let item ;
let tryAgain = [ ] ;
2020-01-03 21:16:40 +01:00
while ( item = addToIconCache . pop ( ) ) {
2020-03-27 08:45:56 +01:00
let svgKey = item [ 0 ] ;
let element = item [ 1 ] ;
2020-01-03 21:16:40 +01:00
if ( iconCache [ svgKey ] != undefined ) {
continue ;
}
if ( ! element ) {
element = new Image ( ) ;
element . src = item [ 2 ] ;
item [ 1 ] = element ;
tryAgain . push ( item ) ;
continue ;
}
if ( ! element . complete ) {
console . log ( "moep" ) ;
tryAgain . push ( item ) ;
continue ;
}
iconCache [ svgKey ] = element ;
}
addToIconCache = tryAgain ;
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 ) ;
2020-03-27 08:45:56 +01:00
let ac _url = [ ] ;
2020-02-28 19:17:19 +01:00
if ( adsbexchange ) {
2020-02-23 10:47:37 +01:00
$ ( '#adsbexchange_header' ) . show ( ) ;
2020-02-15 01:36:30 +01:00
}
2020-01-03 21:16:40 +01:00
if ( globeIndex ) {
2020-03-27 08:45:56 +01:00
let indexes = globeIndexes ( ) ;
let count = 0 ;
2020-01-03 21:16:40 +01:00
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 ] ) ;
} ) ;
2020-01-18 07:43:07 +01:00
indexes = indexes . slice ( 0 , globeSimLoad ) ;
2020-03-27 08:45:56 +01:00
for ( let i in indexes ) {
2020-01-03 21:16:40 +01:00
ac _url . push ( 'data/globe_' + indexes [ i ] . toString ( ) . padStart ( 4 , '0' ) + '.json' ) ;
}
} else {
ac _url [ 0 ] = 'data/aircraft.json' ;
if ( uuid != null ) {
2020-01-23 17:16:22 +01:00
ac _url [ 0 ] = 'data/?feed=' + encodeURIComponent ( uuid ) ;
2020-01-03 21:16:40 +01:00
}
2020-03-18 19:38:08 +01:00
2020-03-18 21:38:20 +01:00
$ ( "#lastLeg_checkbox" ) . parent ( ) . hide ( ) ;
2020-01-03 21:16:40 +01:00
}
2020-01-18 07:43:07 +01:00
lastRequestFiles = ac _url . length ;
2020-01-03 21:16:40 +01:00
PendingFetches = ac _url . length ;
2020-01-25 08:37:13 +01:00
if ( globeIndex ) {
clearTimeout ( refreshId ) ;
refreshId = setTimeout ( fetchData , 25000 ) ;
}
2020-01-03 21:16:40 +01:00
2020-03-27 08:45:56 +01:00
for ( let i in ac _url ) {
2020-01-03 21:16:40 +01:00
//console.log(ac_url[i]);
2020-03-27 08:45:56 +01:00
let req = $ . ajax ( { url : ac _url [ i ] ,
2020-01-03 21:16:40 +01:00
dataType : 'json' } ) ;
FetchPending . push ( req ) ;
req . done ( function ( data ) {
if ( data == null ) {
return ;
}
if ( globeIndex ) {
2020-01-05 18:37:04 +01:00
globeTrackedAircraft = data . global _ac _count _withpos ;
2020-03-05 15:16:50 +01:00
if ( localStorage [ 'globeGrid' ] == 'true' && globeIndexNow [ data . globeIndex ] == null ) {
2020-03-27 08:45:56 +01:00
let southWest = ol . proj . fromLonLat ( [ data . west , data . south ] ) ;
let south180p = ol . proj . fromLonLat ( [ 180 , data . south ] ) ;
let south180m = ol . proj . fromLonLat ( [ - 180 , data . south ] ) ;
let southEast = ol . proj . fromLonLat ( [ data . east , data . south ] ) ;
let northEast = ol . proj . fromLonLat ( [ data . east , data . north ] ) ;
let north180p = ol . proj . fromLonLat ( [ 180 , data . north ] ) ;
let north180m = ol . proj . fromLonLat ( [ - 180 , data . north ] ) ;
let northWest = ol . proj . fromLonLat ( [ data . west , data . north ] ) ;
2020-01-03 21:16:40 +01:00
const estimateStyle = new ol . style . Style ( {
stroke : new ol . style . Stroke ( {
color : '#808080' ,
width : 1 ,
} )
} ) ;
if ( data . west < data . east ) {
2020-03-27 08:45:56 +01:00
let tile = new ol . geom . LineString ( [ southWest , southEast , northEast , northWest , southWest ] ) ;
let tileFeature = new ol . Feature ( tile ) ;
2020-01-03 21:16:40 +01:00
tileFeature . setStyle ( estimateStyle ) ;
2020-03-16 18:12:59 +01:00
StaticFeatures . addFeature ( tileFeature ) ;
2020-01-03 21:16:40 +01:00
} else {
2020-03-27 08:45:56 +01:00
let west = new ol . geom . LineString ( [ south180p , southWest , northWest , north180p ] ) ;
let east = new ol . geom . LineString ( [ south180m , southEast , northEast , north180m ] ) ;
let westF = new ol . Feature ( west ) ;
let eastF = new ol . Feature ( east ) ;
2020-01-03 21:16:40 +01:00
westF . setStyle ( estimateStyle ) ;
eastF . setStyle ( estimateStyle ) ;
2020-03-16 18:12:59 +01:00
StaticFeatures . addFeature ( westF ) ;
StaticFeatures . addFeature ( eastF ) ;
2020-01-03 21:16:40 +01:00
}
}
globeIndexNow [ data . globeIndex ] = data . now ;
}
if ( data . now >= now || globeIndex ) {
2020-01-14 22:00:43 +01:00
//console.time("Process " + data.globeIndex);
2020-01-03 21:16:40 +01:00
processReceiverUpdate ( data ) ;
2020-01-14 22:00:43 +01:00
//console.timeEnd("Process " + data.globeIndex);
2020-01-03 21:16:40 +01:00
}
if ( uat _data && uat _data . now > uat _now ) {
processReceiverUpdate ( uat _data ) ;
uat _data = null ;
}
2020-01-25 02:50:55 +01:00
if ( PendingFetches <= 1 ) {
2020-01-14 22:00:43 +01:00
//console.time("refreshTable");
2020-01-03 21:16:40 +01:00
refreshTableInfo ( ) ;
2020-01-14 22:00:43 +01:00
//console.timeEnd("refreshTable");
2020-01-03 21:16:40 +01:00
refreshClock ( new Date ( now * 1000 ) ) ;
2020-03-19 18:49:17 +01:00
refreshSelected ( ) ;
refreshHighlighted ( ) ;
2020-01-03 21:16:40 +01:00
}
2020-01-07 14:49:33 +01:00
if ( globeIndex ) {
clearTimeout ( refreshId ) ;
2020-01-18 07:43:07 +01:00
refreshId = setTimeout ( fetchData , refreshInt ( ) ) ;
2020-01-07 14:49:33 +01:00
}
2020-01-25 02:50:55 +01:00
PendingFetches -- ;
2020-01-07 14:49:33 +01:00
2020-01-03 21:16:40 +01:00
// Check for stale receiver data
if ( last == now && ! globeIndex ) {
StaleReceiverCount ++ ;
if ( StaleReceiverCount > 5 ) {
2020-01-25 02:27:52 +01:00
$ ( "#update_error_detail" ) . text ( "The data from the server hasn't been updated in a while." ) ;
2020-01-03 21:16:40 +01:00
$ ( "#update_error" ) . css ( 'display' , 'block' ) ;
}
} else if ( StaleReceiverCount > 0 ) {
StaleReceiverCount = 0 ;
$ ( "#update_error" ) . css ( 'display' , 'none' ) ;
}
} ) ;
req . fail ( function ( jqxhr , status , error ) {
2020-01-25 02:27:52 +01:00
$ ( "#update_error_detail" ) . text ( "AJAX call failed (" + status + ( error ? ( ": " + error ) : "" ) + ")." ) ;
console . log ( "AJAX call failed (" + status + ( error ? ( ": " + error ) : "" ) + ")." ) ;
2020-01-15 02:36:49 +01:00
console . log ( jqxhr ) ;
console . log ( status ) ;
console . log ( error ) ;
2020-01-03 21:16:40 +01:00
$ ( "#update_error" ) . css ( 'display' , 'block' ) ;
StaleReceiverCount ++ ;
PendingFetches -- ;
2020-01-25 02:50:55 +01:00
clearTimeout ( refreshId ) ;
refreshId = setTimeout ( fetchData , refreshInt ( ) ) ;
2020-01-03 21:16:40 +01:00
} ) ;
}
}
// this function is called from index.html on body load
// kicks off the whole rabbit hole
function initialize ( ) {
2020-02-29 19:11:33 +01:00
2020-03-02 20:53:56 +01:00
onMobile = window . mobilecheck ( ) ;
2020-03-30 10:46:26 +02:00
today = new Date ( ) . getDate ( ) ;
2020-03-27 08:45:56 +01:00
let largeModeStorage = localStorage [ 'largeMode' ] ;
2020-02-29 23:01:56 +01:00
if ( largeModeStorage != undefined && parseInt ( largeModeStorage , 10 ) ) {
2020-02-29 19:11:33 +01:00
largeMode = parseInt ( largeModeStorage , 10 ) ;
}
2020-02-29 19:04:08 +01:00
try {
const search = new URLSearchParams ( window . location . search ) ;
2020-03-27 08:45:56 +01:00
let tracks = search . get ( 'monochromeTracks' ) ;
2020-02-29 20:01:51 +01:00
if ( tracks != undefined ) {
if ( tracks . length == 6 )
monochromeTracks = '#' + tracks ;
else
monochromeTracks = "#000000" ;
2020-02-29 19:04:08 +01:00
}
2020-02-29 20:01:51 +01:00
2020-03-27 08:45:56 +01:00
let markers = search . get ( 'monochromeMarkers' ) ;
2020-02-29 20:01:51 +01:00
if ( markers != undefined ) {
if ( markers . length == 6 )
monochromeMarkers = '#' + markers ;
else
monochromeMarkers = "#FFFFFF" ;
}
2020-03-27 08:45:56 +01:00
let outlineColor = search . get ( 'outlineColor' ) ;
2020-02-29 20:01:51 +01:00
if ( outlineColor != undefined ) {
if ( markers . length == 6 )
OutlineADSBColor = '#' + outlineColor ;
else
OutlineADSBColor = "#000000" ;
2020-02-29 19:04:08 +01:00
}
2020-02-29 20:01:51 +01:00
2020-03-06 16:29:35 +01:00
if ( search . has ( 'kiosk' ) ) {
tempTrails = true ;
hideButtons = true ;
largeMode = 2 ;
}
2020-03-05 14:09:15 +01:00
if ( search . has ( 'largeMode' ) ) {
2020-03-27 08:45:56 +01:00
let tmp = parseInt ( search . get ( 'largeMode' ) ) ;
2020-02-29 19:11:33 +01:00
console . log ( tmp ) ;
if ( ! isNaN ( tmp ) )
largeMode = tmp ;
}
2020-03-01 00:11:19 +01:00
2020-03-04 19:44:35 +01:00
if ( search . has ( 'mobile' ) )
onMobile = true ;
if ( search . has ( 'desktop' ) )
onMobile = false ;
2020-03-01 00:11:19 +01:00
if ( search . has ( 'hideSidebar' ) )
localStorage [ 'sidebar_visible' ] = "false" ;
if ( search . has ( 'sidebarWidth' ) ) {
localStorage [ 'sidebar_width' ] = search . get ( 'sidebarWidth' ) ;
localStorage [ 'sidebar_visible' ] = "true" ;
}
2020-03-05 14:09:15 +01:00
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 ;
}
}
2020-03-06 16:29:35 +01:00
2020-03-06 15:25:53 +01:00
if ( search . has ( 'tempTrails' ) ) {
tempTrails = true ;
2020-03-27 08:45:56 +01:00
let tmp = parseInt ( search . get ( 'tempTrails' ) ) ;
2020-03-06 15:25:53 +01:00
if ( tmp > 0 )
tempTrailsTimeout = tmp ;
}
2020-03-06 16:29:35 +01:00
if ( search . has ( 'hideButtons' ) )
hideButtons = true ;
2020-03-30 09:43:28 +02:00
icaoFilter = search . get ( 'icaoFilter' ) ;
if ( icaoFilter )
icaoFilter = icaoFilter . toLowerCase ( ) . split ( ',' ) ;
2020-02-29 19:04:08 +01:00
} catch ( error ) {
console . log ( error ) ;
}
2020-01-03 21:16:40 +01:00
2020-03-04 19:44:35 +01:00
if ( onMobile )
enableMouseover = false ;
2020-03-09 20:02:49 +01:00
if ( document . getElementById ( 'adsense' ) != null || adsbexchange ) {
2020-03-06 16:22:01 +01:00
if ( onMobile || hideButtons ) {
2020-03-04 19:44:35 +01:00
document . getElementById ( 'adsense' ) . style . display = 'none' ;
2020-03-06 16:22:01 +01:00
} else {
setTimeout ( function ( ) {
2020-03-09 20:29:24 +01:00
try {
( adsbygoogle = window . adsbygoogle || [ ] ) . push ( { } ) ;
} catch ( error ) {
console . log ( error ) ;
}
2020-03-06 16:22:01 +01:00
2020-03-27 08:45:56 +01:00
let countDown = 20 ;
let i = setInterval ( function ( ) {
2020-03-06 16:22:01 +01:00
2020-03-27 08:45:56 +01:00
let b1 = document . getElementById ( 'waittohide' ) ;
let b2 = document . getElementById ( 'letuserhide' ) ;
2020-03-06 16:22:01 +01:00
if ( countDown === 1 ) {
if ( b1 [ 'style' ] . display == 'none' ) {
b1 [ 'style' ] . display = 'block' ;
b2 [ 'style' ] . display = 'none' ;
} else {
b1 [ 'style' ] . display = 'none' ;
b2 [ 'style' ] . display = 'block' ;
}
clearInterval ( i ) ;
}
countDown -- ;
b1 . innerHTML = 'Hide in ' + countDown + ' seconds' ;
} , 1000 ) ;
} , 1000 ) ;
}
2020-03-04 19:44:35 +01:00
}
2020-01-03 21:16:40 +01:00
mapOrientation *= ( Math . PI / 180 ) ; // adjust to radians
if ( localStorage [ 'enableLabels' ] == 'true' ) {
toggleLabels ( ) ;
}
2020-01-14 16:06:45 +01:00
if ( localStorage [ 'extendedLabels' ] ) {
extendedLabels = parseInt ( localStorage [ 'extendedLabels' ] ) + 2 ;
2020-01-03 21:16:40 +01:00
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" ) {
noMLAT = true ;
//localStorage['noMLAT'] = "false";
}
if ( localStorage [ 'noVanish' ] == "true" ) {
noVanish = true ;
filterTracks = noVanish ;
//localStorage['noVanish'] = "false";
buttonActive ( '#P' , noVanish ) ;
}
$ . when ( configureReceiver ) . done ( function ( ) {
configureReceiver = null ;
// Initialize stuff
init _page ( ) ;
// Wait for history item downloads and append them to the buffer
push _history ( ) ;
// this will be needed later
2020-02-26 17:22:58 +01:00
$ . getJSON ( databaseFolder + "/icao_aircraft_types.js" )
2020-01-03 21:16:40 +01:00
. done ( function ( typeLookupData ) {
_aircraft _type _cache = typeLookupData ;
2020-02-26 18:09:45 +01:00
} ) ;
2020-03-18 09:26:01 +01:00
if ( ! onMobile && ! hideButtons ) {
2020-03-02 20:53:56 +01:00
$ . getJSON ( databaseFolder + "/files.js" )
. done ( function ( data ) {
2020-03-27 08:45:56 +01:00
for ( let i in data ) {
2020-03-02 20:53:56 +01:00
const icao = data [ i ] . padEnd ( 6 , 0 ) ;
//console.log(icao);
2020-03-27 08:45:56 +01:00
let req = getAircraftData ( icao ) ;
2020-03-02 20:53:56 +01:00
req . icao = icao ;
req . fail ( function ( jqXHR , textStatus , errorThrown ) {
if ( textStatus == 'timeout' ) {
getAircraftData ( this . icao ) ;
console . log ( 'Database load timeout:' + this . icao ) ;
} else {
console . log ( this . icao + ': Database load error: ' + textStatus + ' at URL: ' + jqXHR . url ) ;
}
} ) ;
}
} ) ;
}
2020-01-03 21:16:40 +01:00
} ) ;
2020-03-27 08:45:56 +01:00
let coll = document . getElementsByClassName ( "collapseButton" ) ;
2020-01-22 20:49:11 +01:00
2020-03-27 08:45:56 +01:00
for ( let i = 0 ; i < coll . length ; i ++ ) {
2020-01-22 20:49:11 +01:00
coll [ i ] . addEventListener ( "click" , function ( ) {
this . classList . toggle ( "active" ) ;
2020-03-27 08:45:56 +01:00
let content = this . nextElementSibling ;
2020-01-22 20:49:11 +01:00
if ( content . style . display === "block" ) {
content . style . display = "none" ;
} else {
content . style . display = "block" ;
}
} ) ;
}
2020-01-03 21:16:40 +01:00
}
function init _page ( ) {
// Set page basics
document . title = PageName ;
//flightFeederCheck();
PlaneRowTemplate = document . getElementById ( "plane_row_template" ) ;
$ ( '#clock_div' ) . text ( new Date ( ) . toLocaleString ( ) ) ;
$ ( "#loader" ) . removeClass ( "hidden" ) ;
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 ) ,
} ) ;
if ( localStorage [ 'sidebar_width' ] != null )
$ ( '#sidebar_container' ) . width ( localStorage [ 'sidebar_width' ] ) ;
2020-01-17 23:27:21 +01:00
else
$ ( '#sidebar_container' ) . width ( '25%' ) ;
2020-01-03 21:16:40 +01:00
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
} ) ;
* /
$ ( '#close-button' ) . on ( 'click' , function ( ) {
if ( SelectedPlane ) {
SelectedPlane . selected = null ;
SelectedPlane . clearLines ( ) ;
SelectedPlane . updateMarker ( ) ;
SelectedPlane = null ;
refreshSelected ( ) ;
refreshHighlighted ( ) ;
$ ( '#selected_infoblock' ) . hide ( ) ;
refreshTableInfo ( ) ;
}
} ) ;
/ *
// 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 ( ) {
2020-03-27 08:45:56 +01:00
let topCalc = ( $ ( window ) . height ( ) - $ ( '#selected_infoblock' ) . height ( ) - 25 ) ;
2020-01-03 21:16:40 +01:00
// 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' ) ;
} ) ;
* /
2020-01-14 22:00:43 +01:00
$ ( '#sidebar_container' ) . on ( 'resize' , function ( ) {
localStorage [ 'sidebar_width' ] = $ ( '#sidebar_container' ) . width ( ) ;
} ) ;
2020-01-03 21:16:40 +01:00
// Set up event handlers for buttons
$ ( "#toggle_sidebar_button" ) . click ( toggleSidebarVisibility ) ;
$ ( "#expand_sidebar_button" ) . click ( expandSidebar ) ;
$ ( "#show_map_button" ) . click ( showMap ) ;
2020-02-07 07:17:01 +01:00
$ ( "#large_mode_button" ) . click ( toggleLargeMode ) ;
2020-01-03 21:16:40 +01:00
// Set initial element visibility
$ ( "#show_map_button" ) . hide ( ) ;
setColumnVisibility ( ) ;
// Initialize other controls
initializeUnitsSelector ( ) ;
// Set up altitude filter button event handlers and validation options
$ ( "#altitude_filter_form" ) . submit ( onFilterByAltitude ) ;
2020-01-04 13:51:14 +01:00
$ ( "#callsign_filter_form" ) . submit ( updateCallsignFilter ) ;
2020-01-14 22:00:43 +01:00
$ ( "#type_filter_form" ) . submit ( updateTypeFilter ) ;
$ ( "#description_filter_form" ) . submit ( updateDescriptionFilter ) ;
2020-01-03 21:16:40 +01:00
$ ( "#search_form" ) . submit ( onSearch ) ;
2020-01-09 22:10:51 +01:00
$ ( "#jump_form" ) . submit ( onJump ) ;
2020-01-03 21:16:40 +01:00
2020-01-22 20:49:11 +01:00
$ ( "#show_trace" ) . click ( toggleShowTrace ) ;
$ ( "#trace_back_1d" ) . click ( function ( ) { shiftTrace ( - 1 ) } ) ;
$ ( "#trace_jump_1d" ) . click ( function ( ) { shiftTrace ( 1 ) } ) ;
2020-01-14 22:00:43 +01:00
2020-03-22 22:40:17 +01:00
$ ( "#leg_prev" ) . click ( function ( ) { legShift ( - 1 ) } ) ;
$ ( "#leg_next" ) . click ( function ( ) { legShift ( 1 ) } ) ;
2020-01-14 22:00:43 +01:00
$ ( "#altitude_filter_reset_button" ) . click ( onResetAltitudeFilter ) ;
$ ( "#callsign_filter_reset_button" ) . click ( onResetCallsignFilter ) ;
$ ( "#type_filter_reset_button" ) . click ( onResetTypeFilter ) ;
$ ( "#description_filter_reset_button" ) . click ( onResetDescriptionFilter ) ;
2020-01-03 21:16:40 +01:00
// 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 ;
}
2020-01-14 22:00:43 +01:00
$ ( '#settingsCog' ) . on ( 'click' , function ( ) {
$ ( '#settings_infoblock' ) . toggle ( ) ;
} ) ;
2020-01-03 21:16:40 +01:00
2020-01-14 22:00:43 +01:00
$ ( '#settings_close' ) . on ( 'click' , function ( ) {
$ ( '#settings_infoblock' ) . hide ( ) ;
} ) ;
2020-01-03 21:16:40 +01:00
2020-01-14 22:00:43 +01:00
$ ( '#groundvehicle_filter' ) . on ( 'click' , function ( ) {
filterGroundVehicles ( true ) ;
refreshSelected ( ) ;
refreshHighlighted ( ) ;
refreshTableInfo ( ) ;
} ) ;
2020-01-03 21:16:40 +01:00
2020-01-14 22:00:43 +01:00
$ ( '#blockedmlat_filter' ) . on ( 'click' , function ( ) {
filterBlockedMLAT ( true ) ;
refreshSelected ( ) ;
refreshHighlighted ( ) ;
refreshTableInfo ( ) ;
} ) ;
2020-01-03 21:16:40 +01:00
2020-01-14 22:00:43 +01:00
$ ( '#grouptype_checkbox' ) . on ( 'click' , function ( ) {
if ( $ ( '#grouptype_checkbox' ) . hasClass ( 'settingsCheckboxChecked' ) ) {
sortByDistance ( ) ;
} else {
sortByDataSource ( ) ;
}
2020-01-03 21:16:40 +01:00
2020-01-14 22:00:43 +01:00
} ) ;
2020-01-03 21:16:40 +01:00
2020-01-14 22:00:43 +01:00
/ *
2020-01-03 21:16:40 +01:00
$ ( '#altitude_checkbox' ) . on ( 'click' , function ( ) {
toggleAltitudeChart ( true ) ;
} ) ;
* /
2020-02-04 21:14:54 +01:00
$ ( '#lastLeg_checkbox' ) . on ( 'click' , function ( ) {
toggleLastLeg ( ) ;
} ) ;
2020-02-29 21:25:18 +01:00
if ( onMobile ) {
2020-03-27 08:45:56 +01:00
$ ( '#large_mode_button' ) . css ( 'width' , 'calc( 45px * let(--SCALE))' ) ;
$ ( '#large_mode_button' ) . css ( 'height' , 'calc( 45px * let(--SCALE))' ) ;
2020-02-29 23:01:56 +01:00
if ( localStorage [ 'largeMode' ] == undefined && largeMode == 1 )
largeMode = 2 ;
2020-03-02 23:22:01 +01:00
globeTableLimit = 40 ;
2020-02-29 21:25:18 +01:00
}
2020-02-29 17:34:46 +01:00
largeMode -- ;
toggleLargeMode ( ) ;
2020-02-04 21:14:54 +01:00
if ( localStorage [ 'lastLeg' ] === "true" ) {
lastLeg = true ;
$ ( '#lastLeg_checkbox' ) . addClass ( 'settingsCheckboxChecked' ) ;
} else if ( localStorage [ 'lastLeg' ] === "false" ) {
lastLeg = false ;
$ ( '#lastLeg_checkbox' ) . removeClass ( 'settingsCheckboxChecked' ) ;
}
2020-01-14 22:00:43 +01:00
$ ( '#debugAll_checkbox' ) . on ( 'click' , function ( ) {
toggleDebugAll ( ) ;
} ) ;
2020-01-03 21:16:40 +01:00
2020-01-14 22:00:43 +01:00
if ( localStorage [ 'debugAll' ] === "true" ) {
debugAll = true ;
$ ( '#debugAll_checkbox' ) . addClass ( 'settingsCheckboxChecked' ) ;
} else {
debugAll = false ;
$ ( '#debugAll_checkbox' ) . removeClass ( 'settingsCheckboxChecked' ) ;
}
2020-01-03 21:16:40 +01:00
2020-01-14 22:00:43 +01:00
$ ( '#debug_checkbox' ) . on ( 'click' , function ( ) {
toggleDebugTracks ( ) ;
} ) ;
2020-01-03 21:16:40 +01:00
2020-01-14 22:00:43 +01:00
if ( localStorage [ 'debugTracks' ] === "true" ) {
debugTracks = true ;
$ ( '#debug_checkbox' ) . addClass ( 'settingsCheckboxChecked' ) ;
2020-01-03 21:16:40 +01:00
} else {
2020-01-14 22:00:43 +01:00
debugTracks = false ;
$ ( '#debug_checkbox' ) . removeClass ( 'settingsCheckboxChecked' ) ;
2020-01-03 21:16:40 +01:00
}
2020-01-14 22:00:43 +01:00
$ ( '#selectall_checkbox' ) . on ( 'click' , function ( ) {
if ( $ ( '#selectall_checkbox' ) . hasClass ( 'settingsCheckboxChecked' ) ) {
deselectAllPlanes ( ) ;
} else {
selectAllPlanes ( ) ;
}
} )
$ ( '#mapdim_checkbox' ) . on ( 'click' , function ( ) {
toggleMapDim ( ) ;
} ) ;
// Force map to redraw if sidebar container is resized - use a timer to debounce
$ ( "#sidebar_container" ) . on ( "resize" , function ( ) {
clearTimeout ( mapResizeTimeout ) ;
2020-01-15 16:38:07 +01:00
mapResizeTimeout = setTimeout ( updateMapSize , 20 ) ;
2020-01-14 22:00:43 +01:00
} ) ;
filterGroundVehicles ( false ) ;
filterBlockedMLAT ( false ) ;
//toggleAltitudeChart(false);
2020-01-03 21:16:40 +01:00
}
function push _history ( ) {
$ ( "#loader_progress" ) . attr ( 'max' , nHistoryItems * 2 ) ;
2020-03-27 08:45:56 +01:00
for ( let i = 0 ; i < nHistoryItems ; i ++ ) {
2020-01-03 21:16:40 +01:00
push _history _item ( i ) ;
}
if ( globeIndex ) {
parse _history ( ) ;
} else if ( ! nHistoryItems ) {
parse _history ( ) ;
console . log ( "History loading failed" ) ;
}
}
function push _history _item ( i ) {
$ . when ( deferHistory [ i ] )
. done ( function ( json ) {
if ( HistoryChunks ) {
if ( json && json . files ) {
2020-03-27 08:45:56 +01:00
for ( let i in json . files ) {
2020-01-03 21:16:40 +01:00
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 ) {
parse _history ( ) ;
}
} )
. 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 ) {
parse _history ( ) ;
}
} ) ;
}
function parse _history ( ) {
if ( nHistoryItems ) {
console . timeEnd ( "Downloaded History" ) ;
console . time ( "Loaded aircraft tracks from History" ) ;
}
$ ( "#loader" ) . addClass ( "hidden" ) ;
2020-03-27 08:45:56 +01:00
for ( let i in deferHistory )
2020-01-03 21:16:40 +01:00
deferHistory [ i ] = null ;
initialize _map ( ) ;
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
2020-03-27 08:45:56 +01:00
let data ;
let h = 0 ;
let pruneInt = Math . floor ( PositionHistoryBuffer . length / 5 ) ;
2020-01-03 21:16:40 +01:00
while ( data = PositionHistoryBuffer . pop ( ) ) {
// process new data
if ( PositionHistoryBuffer . length < 10 ) {
processReceiverUpdate ( data , false ) ;
if ( now - new Date ( ) . getTime ( ) / 1000 > 600 )
historyOutdated = true ;
} else {
processReceiverUpdate ( data , true ) ;
}
// update aircraft tracks
if ( data . uat _978 != "true" ) {
2020-03-27 08:45:56 +01:00
for ( let i = 0 ; i < PlanesOrdered . length ; ++ i ) {
let plane = PlanesOrdered [ i ] ;
2020-01-03 21:16:40 +01:00
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" ) ;
2020-03-27 08:45:56 +01:00
for ( let i in PlanesOrdered ) {
let plane = PlanesOrdered [ i ] ;
2020-01-03 21:16:40 +01:00
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 ( ) ;
}
PositionHistoryBuffer = null ;
if ( nHistoryItems )
console . timeEnd ( "Loaded aircraft tracks from History" ) ;
console . log ( "Completing init" ) ;
refreshSelected ( ) ;
refreshHighlighted ( ) ;
// Setup our timer to poll from the server.
2020-03-16 12:21:02 +01:00
window . setInterval ( reaper , 20000 ) ;
2020-01-03 21:16:40 +01:00
if ( tempTrails ) {
window . setInterval ( trailReaper , 10000 ) ;
trailReaper ( now ) ;
}
if ( enable _pf _data ) {
window . setInterval ( fetchPfData , RefreshInterval * 10.314 ) ;
}
//window.setInterval(refreshTableInfo, 1000);
2020-01-24 22:37:13 +01:00
//window.setInterval(function() {PendingFetches--;}, 10000);
2020-01-03 21:16:40 +01:00
2020-01-22 20:49:11 +01:00
pathName = window . location . pathname ;
2020-01-03 21:16:40 +01:00
// And kick off one refresh immediately.
2020-01-05 22:33:58 +01:00
processURLParams ( ) ;
2020-01-14 15:02:04 +01:00
if ( ! icaoFilter && globeIndex )
toggleTableInView ( true ) ;
2020-01-25 18:23:39 +01:00
changeZoom ( "init" ) ;
2020-01-25 18:44:35 +01:00
changeCenter ( "init" ) ;
2020-01-25 18:23:39 +01:00
2020-01-26 00:11:00 +01:00
if ( globeIndex )
setInterval ( checkMovement , 100 ) ;
else
setInterval ( checkMovement , 30 ) ;
2020-01-22 20:49:11 +01:00
fetchData ( ) ;
if ( ! globeIndex ) {
$ ( '#show_trace' ) . hide ( ) ;
}
2020-02-27 04:32:28 +01:00
if ( globeIndex ) {
$ ( '#V' ) . hide ( ) ;
$ ( '#uat1' ) . hide ( ) ;
$ ( '#uat2' ) . hide ( ) ;
}
2020-01-03 21:16:40 +01:00
updateMapSize ( ) ;
loadFinished = true ;
2020-03-03 14:51:34 +01:00
//drawAlt();
2020-01-03 21:16:40 +01:00
if ( localStorage [ 'sidebar_visible' ] == "false" )
toggleSidebarVisibility ( ) ;
2020-03-05 16:21:16 +01:00
if ( onMobile && localStorage [ 'sidebar_visible' ] == undefined )
toggleSidebarVisibility ( ) ;
2020-03-01 00:11:19 +01:00
if ( hideButtons ) {
$ ( '#large_mode_control' ) . hide ( ) ;
$ ( '#header_top' ) . hide ( ) ;
$ ( '#header_side' ) . hide ( ) ;
$ ( '#splitter' ) . hide ( ) ;
$ ( '#jumpSearch' ) . hide ( ) ;
$ ( '#filterButton' ) . hide ( ) ;
$ ( '.ol-control' ) . hide ( ) ;
}
2020-03-06 15:25:53 +01:00
if ( tempTrails )
selectAllPlanes ( ) ;
2020-03-25 23:27:54 +01:00
geoMag = geoMagFactory ( cof2Obj ( ) ) ;
2020-01-03 21:16:40 +01:00
}
// 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 ) {
2020-03-27 08:45:56 +01:00
let angularDistance = radius / 6378137.0 ;
let lon1 = center [ 0 ] * Math . PI / 180.0 ;
let lat1 = center [ 1 ] * Math . PI / 180.0 ;
let geom ;
for ( let i = 0 ; i <= points ; ++ i ) {
let bearing = i * 2 * Math . PI / points ;
let lat2 = Math . asin ( Math . sin ( lat1 ) * Math . cos ( angularDistance ) +
2020-01-03 21:16:40 +01:00
Math . cos ( lat1 ) * Math . sin ( angularDistance ) * Math . cos ( bearing ) ) ;
2020-03-27 08:45:56 +01:00
let lon2 = lon1 + Math . atan2 ( Math . sin ( bearing ) * Math . sin ( angularDistance ) * Math . cos ( lat1 ) ,
2020-01-03 21:16:40 +01:00
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 ;
}
2020-03-27 08:45:56 +01:00
// Initalizes the map and starts up our timers to call letious functions
2020-01-03 21:16:40 +01:00
function initialize _map ( ) {
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 ;
2020-01-05 18:37:04 +01:00
$ ( '#dump1090_total_history_td' ) . hide ( ) ;
$ ( '#dump1090_message_rate_td' ) . hide ( ) ;
2020-01-03 21:16:40 +01:00
}
// Load stored map settings if present
CenterLon = Number ( localStorage [ 'CenterLon' ] ) || DefaultCenterLon ;
CenterLat = Number ( localStorage [ 'CenterLat' ] ) || DefaultCenterLat ;
ZoomLvl = Number ( localStorage [ 'ZoomLvl' ] ) || DefaultZoomLvl ;
ZoomLvlCache = ZoomLvl ;
if ( localStorage [ 'MapType_tar1090' ] ) {
MapType _tar1090 = localStorage [ 'MapType_tar1090' ] ;
}
// Maybe hide flag info
if ( ! ShowFlags ) {
PlaneRowTemplate . cells [ 1 ] . style . display = 'none' ; // hide flag column
document . getElementById ( "flag" ) . style . display = 'none' ; // hide flag header
}
// Initialize OL3
layers _group = createBaseLayers ( ) ;
layers = layers _group . getLayers ( ) ;
iconLayer = new ol . layer . Vector ( {
name : 'ac_positions' ,
type : 'overlay' ,
title : 'Aircraft positions' ,
2020-03-16 18:12:59 +01:00
source : PlaneIconFeatures ,
2020-03-18 13:17:29 +01:00
declutter : false ,
2020-03-03 09:59:05 +01:00
zIndex : 200 ,
2020-03-18 13:17:29 +01:00
renderBuffer : 20 ,
//renderOrder: null,
2020-01-03 21:16:40 +01:00
} ) ;
layers . push (
new ol . layer . Vector ( {
name : 'site_pos' ,
type : 'overlay' ,
title : 'Site position and range rings' ,
2020-03-16 18:12:59 +01:00
source : StaticFeatures ,
2020-03-02 19:02:34 +01:00
visible : ! adsbexchange ,
2020-03-03 09:59:05 +01:00
zIndex : 100 ,
2020-03-18 13:17:29 +01:00
renderOrder : null ,
2020-01-03 21:16:40 +01:00
} ) ) ;
trailLayers = new ol . layer . Group ( {
name : 'ac_trail' ,
title : 'Aircraft trails' ,
type : 'overlay' ,
layers : trailGroup ,
2020-03-03 09:59:05 +01:00
zIndex : 150 ,
2020-01-03 21:16:40 +01:00
} ) ;
layers . push ( trailLayers ) ;
layers . push ( iconLayer ) ;
2020-03-27 08:45:56 +01:00
let foundType = false ;
let baseCount = 0 ;
2020-01-03 21:16:40 +01:00
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' ) {
2020-03-27 08:45:56 +01:00
let visible = localStorage [ 'layer_' + lyr . get ( 'name' ) ] ;
2020-01-03 21:16:40 +01:00
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 ,
2020-01-18 07:43:07 +01:00
minZoom : 2 ,
2020-01-03 21:16:40 +01:00
} ) ,
2020-01-23 16:20:27 +01:00
controls : [ new ol . control . Zoom ( { delta : 1 , duration : 0 , } ) ,
2020-01-03 21:16:40 +01:00
new ol . control . Attribution ( { collapsed : true } ) ,
new ol . control . ScaleLine ( { units : DisplayUnits } )
] ,
loadTilesWhileAnimating : false ,
loadTilesWhileInteracting : false ,
2020-02-28 09:29:20 +01:00
interactions : new ol . interaction . defaults ( { altShiftDragRotate : false , pinchRotate : false , } ) ,
2020-01-03 21:16:40 +01:00
} ) ;
OLMap . getView ( ) . setRotation ( mapOrientation ) ; // adjust orientation
if ( baseCount > 1 ) {
2020-03-02 13:20:12 +01:00
OLMap . addControl ( new ol . control . LayerSwitcher ( {
groupSelectStyle : 'none'
} ) ) ;
2020-01-03 21:16:40 +01:00
}
2020-01-25 18:23:39 +01:00
/ *
2020-01-03 21:16:40 +01:00
// Listeners for newly created Map
OLMap . getView ( ) . on ( 'change:center' , function ( event ) {
2020-01-11 22:15:03 +01:00
const center = ol . proj . toLonLat ( OLMap . getView ( ) . getCenter ( ) , OLMap . getView ( ) . getProjection ( ) ) ;
CenterLat = center [ 1 ] ;
CenterLon = center [ 0 ] ;
2020-01-03 21:16:40 +01:00
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 ( ) ;
}
}
} ) ;
2020-01-25 18:23:39 +01:00
* /
2020-01-03 21:16:40 +01:00
2020-01-25 18:23:39 +01:00
/ *
2020-01-03 21:16:40 +01:00
OLMap . getView ( ) . on ( 'change:resolution' , function ( event ) {
ZoomLvl = OLMap . getView ( ) . getZoom ( ) ;
} ) ;
2020-01-25 18:23:39 +01:00
* /
2020-01-03 21:16:40 +01:00
OLMap . on ( [ 'click' , 'dblclick' ] , function ( evt ) {
2020-03-27 08:45:56 +01:00
let hex = evt . map . forEachFeatureAtPixel (
2020-01-03 21:16:40 +01:00
evt . pixel ,
function ( feature , layer ) {
return feature . hex ;
} ,
{
layerFilter : function ( layer ) {
return ( layer == iconLayer || layer . get ( 'isTrail' ) == true ) ;
} ,
hitTolerance : 5 ,
}
) ;
if ( hex ) {
2020-01-07 23:31:35 +01:00
selectPlaneByHex ( hex , { follow : ( evt . type === 'dblclick' ) } ) ;
2020-01-03 21:16:40 +01:00
} else if ( ! multiSelect ) {
deselectAllPlanes ( ) ;
}
evt . stopPropagation ( ) ;
} ) ;
// show the hover box
2020-01-07 23:56:51 +01:00
if ( ! globeIndex && ZoomLvl > 5.5 && enableMouseover ) {
2020-01-03 21:16:40 +01:00
OLMap . on ( 'pointermove' , onPointermove ) ;
}
// handle the layer settings pane checkboxes
OLMap . once ( 'postrender' , function ( e ) {
toggleLayer ( '#nexrad_checkbox' , 'nexrad' ) ;
2020-03-02 19:02:34 +01:00
//toggleLayer('#sitepos_checkbox', 'site_pos');
2020-01-03 21:16:40 +01:00
toggleLayer ( '#actrail_checkbox' , 'ac_trail' ) ;
toggleLayer ( '#acpositions_checkbox' , 'ac_positions' ) ;
} ) ;
2020-01-09 16:32:12 +01:00
if ( localStorage [ 'MapDim' ] === "true" || ( MapDim && localStorage [ 'MapDim' ] == null ) ) {
2020-01-03 21:16:40 +01:00
toggleMapDim ( true ) ;
}
window . addEventListener ( 'keydown' , function ( e ) {
if ( e . defaultPrevented ) {
return ; // Do nothing if the event was already processed
}
2020-01-04 13:51:14 +01:00
if ( e . target . type == "text" ) {
return ;
}
2020-01-03 21:16:40 +01:00
if ( e . srcElement . nodeName == 'INPUT' ) {
return ;
}
if ( e . ctrlKey || e . altKey || e . metaKey ) {
return ;
}
2020-03-27 08:45:56 +01:00
let oldCenter , extent , newCenter ;
2020-01-03 21:16:40 +01:00
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 ) ;
2020-03-24 19:45:03 +01:00
toggleFollow ( false ) ;
2020-01-03 21:16:40 +01:00
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 ) ;
2020-03-24 19:45:03 +01:00
toggleFollow ( false ) ;
2020-01-03 21:16:40 +01:00
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 ) ;
2020-03-24 19:45:03 +01:00
toggleFollow ( false ) ;
2020-01-03 21:16:40 +01:00
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 ) ;
2020-03-24 19:45:03 +01:00
toggleFollow ( false ) ;
2020-01-03 21:16:40 +01:00
break ;
// misc
case "b" :
toggleMapDim ( ) ;
break ;
case "m" :
toggleMultiSelect ( ) ;
break ;
case "v" :
toggleTableInView ( ) ;
break ;
case "r" :
followRandomPlane ( ) ;
break ;
case "t" :
selectAllPlanes ( ) ;
break ;
case "h" :
resetMap ( ) ;
break ;
2020-03-01 01:14:23 +01:00
case "H" :
if ( ! hideButtons ) {
$ ( '#large_mode_control' ) . hide ( ) ;
$ ( '#header_top' ) . hide ( ) ;
$ ( '#header_side' ) . hide ( ) ;
$ ( '#splitter' ) . hide ( ) ;
$ ( '#jumpSearch' ) . hide ( ) ;
$ ( '#filterButton' ) . hide ( ) ;
$ ( '.ol-control' ) . hide ( ) ;
} else {
$ ( '#large_mode_control' ) . show ( ) ;
$ ( '#header_top' ) . show ( ) ;
$ ( '#header_side' ) . show ( ) ;
$ ( '#splitter' ) . show ( ) ;
$ ( '#jumpSearch' ) . show ( ) ;
$ ( '#filterButton' ) . show ( ) ;
$ ( '.ol-control' ) . show ( ) ;
$ ( '#expand_sidebar_control' ) . hide ( ) ;
toggleSidebarVisibility ( ) ;
toggleSidebarVisibility ( ) ;
}
hideButtons = ! hideButtons ;
break ;
2020-01-03 21:16:40 +01:00
case "f" :
2020-03-24 19:45:03 +01:00
toggleFollow ( ) ;
2020-01-03 21:16:40 +01:00
break ;
// filters
case "M" :
onlyMLAT = ! onlyMLAT ;
2020-01-13 13:16:32 +01:00
refreshTableInfo ( ) ;
2020-01-03 21:16:40 +01:00
break ;
case "T" :
filterTISB = ! filterTISB ;
break ;
case "u" :
toggleMilitary ( ) ;
2020-01-13 22:31:09 +01:00
break ;
2020-01-03 21:16:40 +01:00
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
2020-02-04 21:14:54 +01:00
case "L" :
toggleLastLeg ( ) ;
break ;
2020-01-03 21:16:40 +01:00
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 ) ;
break ;
case "j" :
2020-01-07 23:31:35 +01:00
selectPlaneByHex ( jumpTo , { follow : true } ) ;
2020-01-03 21:16:40 +01:00
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 ) ;
2020-03-06 20:55:05 +01:00
if ( globeIndex )
geoFindMe ( ) ;
else {
initSitePos ( ) ;
}
2020-01-03 21:16:40 +01:00
}
2020-03-27 08:45:56 +01:00
// This looks for planes to reap out of the master Planes letiable
2020-01-03 21:16:40 +01:00
function reaper ( all ) {
//console.log("Reaping started..");
if ( noVanish )
return ;
reaping = true ;
// Look for planes where we have seen no messages for >300 seconds
2020-03-27 08:45:56 +01:00
let plane ;
let length = PlanesOrdered . length ;
for ( let i = 0 ; i < length ; i ++ ) {
2020-03-16 09:04:52 +01:00
plane = PlanesOrdered . shift ( )
if ( plane == null )
continue ;
2020-01-03 21:16:40 +01:00
plane . seen = now - plane . last _message _time ;
2020-03-11 20:11:52 +01:00
if ( ( ! plane . selected || SelectedAllPlanes )
2020-03-24 21:25:08 +01:00
&& ( all || plane . seen > 300 )
&& ( ! plane . jaero || plane . seen > 35 * 60 )
2020-03-11 20:11:52 +01:00
) {
2020-01-03 21:16:40 +01:00
// Reap it.
//console.log("Removed " + plane.icao);
delete Planes [ plane . icao ] ;
plane . destroy ( ) ;
} else {
// Keep it.
2020-03-16 09:04:52 +01:00
PlanesOrdered . push ( plane ) ;
2020-01-03 21:16:40 +01:00
}
} ;
2020-03-16 12:21:02 +01:00
//console.log(length - PlanesOrdered.length);
return ( length - PlanesOrdered . length ) ;
2020-01-03 21:16:40 +01:00
}
// Page Title update function
function refreshPageTitle ( ) {
if ( ! PlaneCountInTitle && ! MessageRateInTitle ) {
return ;
}
2020-03-27 08:45:56 +01:00
let subtitle = "" ;
2020-01-03 21:16:40 +01:00
if ( PlaneCountInTitle ) {
2020-03-06 21:51:23 +01:00
if ( globeIndex ) {
subtitle += 'tracking ' + globeTrackedAircraft + ' aircraft' ;
} else {
subtitle += TrackedAircraftPositions + '/' + TrackedAircraft ;
}
2020-01-03 21:16:40 +01:00
}
if ( MessageRateInTitle && MessageRate != null ) {
if ( subtitle ) subtitle += ' | ' ;
subtitle += MessageRate . toFixed ( 1 ) + '/s' ;
}
document . title = PageName + ' - ' + subtitle ;
}
2020-03-27 08:45:56 +01:00
let selCall = null ;
let selIcao = null ;
let selReg = null ;
2020-03-26 19:46:55 +01:00
2020-01-03 21:16:40 +01:00
// Refresh the detail window about the plane
function refreshSelected ( ) {
2020-01-13 14:24:32 +01:00
buttonActive ( '#F' , FollowSelected ) ;
2020-01-03 21:16:40 +01:00
2020-01-04 13:51:14 +01:00
if ( SelectedPlane && SelectedPlane . isFiltered ( ) ) {
SelectedPlane . selected = false ;
SelectedPlane . clearLines ( ) ;
SelectedPlane = null ;
}
2020-01-03 21:16:40 +01:00
if ( ! SelectedPlane ) {
setSelectedInfoBlockVisibility ( ) ;
return ;
}
const selected = SelectedPlane ;
2020-01-15 09:47:51 +01:00
if ( SelectedPlane . position && SelectedPlane . seen _pos > 25 )
SelectedPlane . updateMarker ( true ) ;
2020-03-26 19:46:55 +01:00
if ( selected . flight != selCall ) {
selCall = selected . flight ;
if ( selected . flight && selected . flight . trim ( ) ) {
$ ( '#selected_callsign' ) . text ( selected . flight ) ;
} else {
$ ( '#selected_callsign' ) . text ( 'n/a' ) ;
}
2020-01-03 21:16:40 +01:00
}
if ( flightawareLinks ) {
$ ( '#selected_flightaware_link' ) . html ( getFlightAwareModeSLink ( selected . icao , selected . flight , "Visit Flight Page" ) ) ;
}
2020-03-26 19:46:55 +01:00
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 ) ;
}
2020-01-03 21:16:40 +01:00
} else {
2020-03-26 19:46:55 +01:00
$ ( '#selected_registration' ) . text ( "n/a" ) ;
2020-01-03 21:16:40 +01:00
}
}
if ( selected . icaoType ) {
2020-01-14 22:00:43 +01:00
$ ( '#selected_icaotype' ) . text ( selected . typeDescription + ' - ' + selected . icaoType ) ;
2020-01-03 21:16:40 +01:00
} else {
$ ( '#selected_icaotype' ) . text ( "n/a" ) ;
}
2020-02-26 08:15:48 +01:00
if ( showPictures && selected . icaoType ) {
2020-03-27 08:45:56 +01:00
let new _html = "<img width='150px' src='aircraft_sil/" + selected . icaoType + ".png' />" ;
2020-01-10 20:08:18 +01:00
if ( new _html != selectedPhotoCache ) {
$ ( '#selected_photo' ) . html ( new _html ) ;
selectedPhotoCache = new _html ;
}
2020-01-09 14:15:10 +01:00
} else {
$ ( '#selected_photo' ) . text ( "" ) ;
}
2020-01-03 21:16:40 +01:00
// Not using this logic for the redesigned info panel at the time, but leaving it in if/when adding it back
2020-03-27 08:45:56 +01:00
// let emerg = document.getElementById('selected_emergency');
2020-01-03 21:16:40 +01:00
// 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_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 ) ;
}
2020-03-25 23:27:54 +01:00
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' ) ;
}
2020-03-26 01:02:16 +01:00
let crab = null ;
let heading = null ;
let track = selected . track ;
if ( magResult && selected . mag _heading != null && selected . track != null ) {
heading = selected . mag _heading + magResult . dec ;
} else if ( selected . true _heading != null && selected . track != null ) {
heading = selected . true _heading ;
}
if ( heading != null && heading < 0 )
heading += 360 ;
if ( heading != null && heading > 360 )
heading -= 360 ;
if ( heading != null && track != null ) {
crab = heading - track ;
if ( crab > 180 )
crab -= 360 ;
if ( crab < - 180 )
crab += 360 ;
}
2020-03-25 23:27:54 +01:00
if ( magResult && selected . gs != null && selected . tas != null && selected . track != null && selected . mag _heading != null ) {
const trk = ( Math . PI / 180 ) * selected . track ;
2020-03-26 01:02:16 +01:00
const hdg = ( Math . PI / 180 ) * heading ;
2020-03-25 23:27:54 +01:00
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 ) ;
2020-03-26 09:34:10 +01:00
$ ( '#selected_wd' ) . text ( format _track _brief ( wd , true ) ) ;
2020-03-25 23:27:54 +01:00
$ ( '#selected_ws' ) . text ( format _speed _long ( ws , DisplayUnits ) ) ;
} else {
$ ( '#selected_wd' ) . text ( 'n/a' ) ;
$ ( '#selected_ws' ) . text ( 'n/a' ) ;
}
2020-03-26 00:05:43 +01:00
$ ( '#selected_crab' ) . text ( format _track _brief ( crab ) ) ;
2020-03-25 23:27:54 +01:00
$ ( '#selected_mag_heading' ) . text ( format _track _brief ( selected . mag _heading ) ) ;
2020-03-26 00:05:43 +01:00
if ( selected . true _heading == null && heading != null )
$ ( '#selected_true_heading' ) . text ( format _track _brief ( heading ) ) ;
2020-03-25 23:27:54 +01:00
else
$ ( '#selected_true_heading' ) . text ( format _track _brief ( selected . true _heading ) ) ;
2020-03-26 00:36:36 +01:00
let temp = null ;
if ( selected . mach != null && selected . tas != null ) {
temp = Math . pow ( ( selected . tas / 661.47 / selected . mach ) , 2 ) * 288.15 - 273.15 ;
2020-03-26 09:34:10 +01:00
$ ( '#selected_temp' ) . text ( Math . round ( temp ) + ' °C' ) ;
2020-03-26 00:36:36 +01:00
} else {
$ ( '#selected_temp' ) . text ( 'n/a' ) ;
}
2020-01-03 21:16:40 +01:00
$ ( '#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 ) ) ;
2020-03-26 19:46:55 +01:00
if ( selected . icao != selIcao ) {
selIcao = selected . icao ;
if ( globeIndex ) {
2020-03-27 08:45:56 +01:00
let icao _link = "<a style=\"color: blue\" target=\"_blank\" href=\"" + shareLink + "\">Share</a>" ;
2020-03-26 19:46:55 +01:00
icao _link = NBSP + NBSP + NBSP + NBSP + NBSP + NBSP + icao _link ;
$ ( '#selected_icao' ) . html ( selected . icao . toUpperCase ( ) + icao _link ) ;
} else {
$ ( '#selected_icao' ) . text ( selected . icao . toUpperCase ( ) ) ;
}
2020-02-28 19:17:19 +01:00
}
2020-01-03 21:16:40 +01:00
$ ( '#selected_pf_info' ) . text ( ( selected . pfRoute ? selected . pfRoute : "" ) ) ;
//+" "+ (selected.pfFlightno ? selected.pfFlightno : "")
$ ( '#airframes_post_icao' ) . attr ( 'value' , selected . icao ) ;
2020-03-26 09:34:10 +01:00
$ ( '#selected_track1' ) . text ( format _track _brief ( selected . track ) ) ;
2020-01-03 21:16:40 +01:00
$ ( '#selected_track2' ) . text ( format _track _brief ( selected . track ) ) ;
if ( selected . seen != null && selected . seen < 1000000 ) {
2020-01-28 02:18:21 +01:00
$ ( '#selected_seen' ) . text ( format _duration ( selected . seen ) ) ;
2020-01-03 21:16:40 +01:00
} else {
$ ( '#selected_seen' ) . text ( 'n/a' ) ;
}
if ( selected . seen _pos != null && selected . seen _pos < 1000000 ) {
2020-01-28 02:18:21 +01:00
$ ( '#selected_seen_pos' ) . text ( format _duration ( selected . seen _pos ) ) ;
2020-01-03 21:16:40 +01:00
} 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' ) ;
}
2020-03-24 19:45:03 +01:00
if ( selected . position == null ) {
2020-01-03 21:16:40 +01:00
$ ( '#selected_position' ) . text ( 'n/a' ) ;
2020-03-24 19:45:03 +01:00
//$('#selected_follow').addClass('hidden');
2020-01-03 21:16:40 +01:00
} else {
if ( selected . seen _pos > - 1 ) {
$ ( '#selected_position' ) . text ( format _latlng ( selected . position ) ) ;
} else {
$ ( '#selected_position' ) . text ( format _latlng ( selected . position ) ) ;
}
if ( FollowSelected ) {
2020-03-24 19:45:03 +01:00
//$('#selected_follow').removeClass('hidden');
//$('#selected_follow').css('font-weight', 'bold');
const center = ol . proj . toLonLat ( OLMap . getView ( ) . getCenter ( ) , OLMap . getView ( ) . getProjection ( ) ) ;
if ( Math . abs ( center [ 0 ] - SelectedPlane . position [ 0 ] ) > 0.05 ||
Math . abs ( center [ 1 ] - SelectedPlane . position [ 1 ] ) > 0.05 ) {
toggleFollow ( false ) ;
} else {
toggleFollow ( true ) ;
}
2020-01-03 21:16:40 +01:00
} else {
2020-03-24 19:45:03 +01:00
// $('#selected_follow').css('font-weight', 'normal');
2020-01-03 21:16:40 +01:00
}
}
$ ( '#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" ) ;
$ ( '#selected_message_count' ) . text ( selected . messages ) ;
$ ( '#selected_message_rate' ) . text ( ( selected . messageRate != null ) ? ( selected . messageRate . toFixed ( 1 ) ) : "n/a" ) ;
if ( flightawareLinks ) {
$ ( '#selected_photo_link' ) . html ( getFlightAwarePhotoLink ( selected . registration ) ) ;
}
$ ( '#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 ) ) ;
2020-03-26 09:34:10 +01:00
$ ( '#selected_nav_heading' ) . text ( format _track _brief ( selected . nav _heading ) ) ;
2020-01-03 21:16:40 +01:00
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 {
2020-03-27 08:45:56 +01:00
let sampleRate = "" ;
let silDesc = "" ;
2020-01-03 21:16:40 +01:00
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<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 ( '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
2020-03-27 08:45:56 +01:00
let highlighted = false ;
2020-01-03 21:16:40 +01:00
if ( ! HighlightedPlane || ! ( highlighted = Planes [ HighlightedPlane ] ) ) {
$ ( '#highlighted_infoblock' ) . hide ( ) ;
return ;
}
$ ( '#highlighted_infoblock' ) . show ( ) ;
2020-03-27 08:45:56 +01:00
let infoBox = $ ( '#highlighted_infoblock' ) ;
2020-01-03 21:16:40 +01:00
2020-03-27 08:45:56 +01:00
let marker = highlighted . marker ;
let geom ;
let markerCoordinates ;
2020-01-03 21:16:40 +01:00
if ( ! marker || ! ( geom = marker . getGeometry ( ) ) || ! ( markerCoordinates = geom . getCoordinates ( ) ) ) {
$ ( '#highlighted_infoblock' ) . hide ( ) ;
return ;
}
2020-03-27 08:45:56 +01:00
let markerPosition = OLMap . getPixelFromCoordinate ( markerCoordinates ) ;
2020-02-15 01:36:30 +01:00
if ( ! markerPosition )
return ;
2020-01-03 21:16:40 +01:00
2020-03-27 08:45:56 +01:00
let mapSize = OLMap . getSize ( ) ;
2020-01-09 14:57:14 +01:00
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 ) ;
2020-01-03 21:16:40 +01:00
$ ( '#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 . speed , DisplayUnits ) ) ;
$ ( "#highlighted_altitude" ) . text ( format _altitude _long ( highlighted . altitude , highlighted . vert _rate , DisplayUnits ) ) ;
2020-01-09 15:02:00 +01:00
$ ( '#highlighted_pf_route' ) . text ( ( highlighted . pfRoute ? highlighted . pfRoute : highlighted . icao . toUpperCase ( ) ) ) ;
2020-01-03 21:16:40 +01:00
$ ( '#highlighted_rssi' ) . text ( highlighted . rssi != null ? highlighted . rssi . toFixed ( 1 ) + ' dBFS' : "n/a" ) ;
}
function refreshClock ( now _date ) {
2020-03-27 08:45:56 +01:00
let hhmm = now _date . getHours ( ) . toString ( ) . padStart ( 2 , '0' ) + ":" + now _date . getMinutes ( ) . toString ( ) . padStart ( 2 , '0' ) ;
let hms = hhmm + ":" + now _date . getSeconds ( ) . toString ( ) . padStart ( 2 , '0' ) ;
2020-01-03 21:16:40 +01:00
$ ( '#clock_div' ) . text ( hms + " " + now _date . toDateString ( ) ) ;
}
function removeHighlight ( ) {
HighlightedPlane = null ;
refreshHighlighted ( ) ;
}
function refreshFeatures ( ) {
2020-03-27 08:45:56 +01:00
for ( let i in PlanesOrdered ) {
2020-01-03 21:16:40 +01:00
PlanesOrdered [ i ] . updateTick ( true ) ;
}
}
// Refreshes the larger table of all the planes
function refreshTableInfo ( ) {
refreshPageTitle ( ) ;
resortTable ( PlanesOrdered ) ;
//$('#dump1090_infoblock').css('display','block');
$ ( '#dump1090_total_history' ) . text ( TrackedHistorySize ) ;
if ( MessageRate !== null ) {
$ ( '#dump1090_message_rate' ) . text ( MessageRate . toFixed ( 1 ) ) ;
} else {
$ ( '#dump1090_message_rate' ) . text ( "n/a" ) ;
}
2020-03-27 08:45:56 +01:00
let show _squawk _warning = false ;
2020-01-03 21:16:40 +01:00
TrackedAircraft = 0 ;
TrackedAircraftPositions = 0 ;
TrackedHistorySize = 0 ;
2020-03-27 08:45:56 +01:00
let nplanes = 0 ;
let nMapPlanes = 0 ;
2020-01-03 21:16:40 +01:00
2020-01-04 10:14:00 +01:00
if ( mapIsVisible || lastRealExtent == null ) {
2020-03-27 08:45:56 +01:00
let mapSize = OLMap . getSize ( ) ;
2020-01-04 10:14:00 +01:00
lastRealExtent = OLMap . getView ( ) . calculateExtent ( mapSize ) ;
2020-03-27 08:45:56 +01:00
let size = [ mapSize [ 0 ] * 1.2 , mapSize [ 1 ] * 1.2 ] ;
2020-01-04 10:14:00 +01:00
lastRenderExtent = OLMap . getView ( ) . calculateExtent ( size ) ;
2020-01-03 21:16:40 +01:00
}
//console.time("updateCells");
2020-03-27 08:45:56 +01:00
for ( let i = 0 ; i < PlanesOrdered . length ; ++ i ) {
let plane = PlanesOrdered [ i ] ;
2020-03-18 13:17:29 +01:00
TrackedHistorySize += plane . history _size ;
2020-03-27 08:45:56 +01:00
let classes ;
2020-01-03 21:16:40 +01:00
2020-03-18 13:17:29 +01:00
plane . inView = inView ( plane , lastRealExtent ) ;
2020-01-03 21:16:40 +01:00
2020-01-14 15:02:04 +01:00
if ( globeIndex && ! icaoFilter ) {
2020-03-18 13:17:29 +01:00
if ( ( ( nMapPlanes < 100 || ! onMobile )
&& ( ! onMobile || ZoomLvl > 10 || ! plane . onGround )
&& inView ( plane , lastRenderExtent )
) || ( plane . selected && ! SelectedAllPlanes ) ) {
plane . updateFeatures ( now , last ) ;
} else if ( plane . visible ) {
plane . clearMarker ( ) ;
plane . clearLines ( ) ;
plane . visible = false ;
2020-01-04 10:14:00 +01:00
}
2020-01-14 15:02:04 +01:00
} else {
2020-03-18 13:17:29 +01:00
plane . updateTick ( ) ;
2020-01-03 21:16:40 +01:00
}
2020-03-18 13:17:29 +01:00
plane . showInTable = false ;
2020-01-03 21:16:40 +01:00
classes = "plane_table_row" ;
2020-03-18 13:17:29 +01:00
if ( tableInView && plane . visible &&
( plane . inView || ( plane . selected && ! SelectedAllPlanes ) )
2020-01-12 15:13:35 +01:00
) {
2020-03-18 13:17:29 +01:00
plane . showInTable = true ;
2020-01-13 11:03:59 +01:00
++ TrackedAircraftPositions ;
2020-03-03 22:04:12 +01:00
nMapPlanes ++ ;
2020-01-12 15:13:35 +01:00
}
2020-01-03 21:16:40 +01:00
2020-03-18 13:17:29 +01:00
if ( ! plane . isFiltered ( ) && ( plane . seen < 58 || noVanish ) ) {
2020-01-12 15:13:35 +01:00
TrackedAircraft ++ ;
2020-01-13 11:03:59 +01:00
2020-03-18 13:17:29 +01:00
if ( ! tableInView && plane . position != null )
2020-01-13 11:03:59 +01:00
++ TrackedAircraftPositions ;
if ( ! tableInView )
2020-03-18 13:17:29 +01:00
plane . showInTable = true ;
2020-01-12 15:13:35 +01:00
}
2020-03-18 13:17:29 +01:00
if ( ! sidebarVisible || ( nplanes > globeTableLimit && mapIsVisible && globeIndex ) ) {
plane . showInTable = false ;
2020-01-12 15:13:35 +01:00
continue ;
}
2020-01-03 21:16:40 +01:00
2020-03-16 12:21:02 +01:00
2020-03-18 13:17:29 +01:00
if ( plane . showInTable ) {
nplanes ++ ;
2020-01-03 21:16:40 +01:00
2020-03-18 13:17:29 +01:00
if ( plane . tr == null )
plane . makeTR ( ) ;
2020-03-16 16:09:11 +01:00
2020-03-18 13:17:29 +01:00
if ( plane . dataSource == "adsb" ) {
2020-01-03 21:16:40 +01:00
classes += " vPosition" ;
} else {
classes += " " ;
2020-03-18 13:17:29 +01:00
classes += plane . dataSource ;
2020-01-03 21:16:40 +01:00
}
2020-03-18 13:17:29 +01:00
if ( plane . selected && ! SelectedAllPlanes )
2020-01-03 21:16:40 +01:00
classes += " selected" ;
2020-03-18 13:17:29 +01:00
if ( plane . squawk in SpecialSquawks ) {
classes = classes + " " + SpecialSquawks [ plane . squawk ] . cssClass ;
2020-01-03 21:16:40 +01:00
show _squawk _warning = true ;
}
// ICAO doesn't change
if ( flightawareLinks ) {
2020-03-18 13:17:29 +01:00
updateCell ( plane , 2 , getFlightAwareModeSLink ( plane . icao , plane . flight , plane . name ) , true ) ;
updateCell ( plane , 3 , getFlightAwareIdentLink ( plane . registration , plane . registration ) , true ) ;
2020-01-03 21:16:40 +01:00
} else {
2020-03-18 13:17:29 +01:00
updateCell ( plane , 2 , plane . name ) ;
updateCell ( plane , 3 , plane . registration ? plane . registration : "" ) ;
2020-01-03 21:16:40 +01:00
}
2020-03-18 13:17:29 +01:00
updateCell ( plane , 4 , ( plane . icaoType != null ? plane . icaoType : "" ) ) ;
updateCell ( plane , 5 , ( plane . squawk != null ? plane . squawk : "" ) ) ;
updateCell ( plane , 6 , format _altitude _brief ( plane . altitude , plane . vert _rate , DisplayUnits ) ) ;
updateCell ( plane , 7 , format _speed _brief ( plane . gs , DisplayUnits ) ) ;
updateCell ( plane , 8 , format _vert _rate _brief ( plane . vert _rate , DisplayUnits ) ) ;
updateCell ( plane , 9 , format _distance _brief ( plane . sitedist , DisplayUnits ) ) ;
updateCell ( plane , 10 , format _track _brief ( plane . track ) ) ;
updateCell ( plane , 11 , plane . messages ) ;
updateCell ( plane , 12 , plane . seen . toFixed ( 0 ) ) ;
updateCell ( plane , 13 , ( plane . rssi != null ? plane . rssi . toFixed ( 1 ) : "" ) ) ;
updateCell ( plane , 14 , ( plane . position != null ? plane . position [ 1 ] . toFixed ( 4 ) : "" ) ) ;
updateCell ( plane , 15 , ( plane . position != null ? plane . position [ 0 ] . toFixed ( 4 ) : "" ) ) ;
updateCell ( plane , 16 , format _data _source ( plane . getDataSource ( ) ) ) ;
//updateCell(plane, 17, plane.baseMarkerKey);
2020-01-03 21:16:40 +01:00
}
2020-03-18 13:17:29 +01:00
if ( plane . tr && plane . classesCache != classes ) {
plane . classesCache = classes ;
plane . tr . className = classes ;
2020-01-03 21:16:40 +01:00
}
}
//console.timeEnd("updateCells");
if ( show _squawk _warning _cache != show _squawk _warning && show _squawk _warning ) {
$ ( "#SpecialSquawkWarning" ) . css ( 'display' , 'block' ) ;
show _squawk _warning _cache = show _squawk _warning ;
}
if ( show _squawk _warning _cache != show _squawk _warning && ! show _squawk _warning ) {
$ ( "#SpecialSquawkWarning" ) . css ( 'display' , 'none' ) ;
show _squawk _warning _cache = show _squawk _warning ;
}
2020-01-05 18:37:04 +01:00
if ( ! globeIndex )
$ ( '#dump1090_total_ac' ) . text ( TrackedAircraft ) ;
else
$ ( '#dump1090_total_ac' ) . text ( globeTrackedAircraft ) ;
2020-01-03 21:16:40 +01:00
$ ( '#dump1090_total_ac_positions' ) . text ( TrackedAircraftPositions ) ;
//console.time("DOM");
//tableinfoFragment = document.createDocumentFragment();
2020-03-27 08:45:56 +01:00
let tbody = document . getElementById ( 'tableinfo' ) . tBodies [ 0 ] ;
for ( let i = 0 ; i < PlanesOrdered . length ; ++ i ) {
2020-03-18 09:04:35 +01:00
const plane = PlanesOrdered [ i ] ;
if ( plane . inTable ) {
tbody . removeChild ( plane . tr ) ;
plane . inTable = false ;
2020-03-16 08:02:42 +01:00
}
2020-03-18 09:04:35 +01:00
if ( plane . showInTable ) {
tbody . appendChild ( plane . tr ) ;
plane . inTable = true ;
2020-01-03 21:16:40 +01:00
}
}
//tbody.appendChild(tableinfoFragment);
//console.timeEnd("DOM");
//console.log(tableinfo);
}
//
// ---- table sorting ----
//
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 ;
}
function sortByICAO ( ) { sortBy ( 'icao' , compareAlpha , function ( x ) { return x . icao ; } ) ; }
function sortByFlight ( ) { sortBy ( 'flight' , compareBeta , 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' , compareNumeric , function ( x ) { return x . getDataSourceNumber ( ) } ) ; }
function sortByBaseMarkerKey ( ) { sortBy ( 'base_marker_key' , compareAlpha , function ( x ) { return x . baseMarkerKey ; } ) ; }
2020-03-27 08:45:56 +01:00
let sortId = '' ;
let sortCompare = null ;
let sortExtract = null ;
let sortAscending = true ;
2020-01-03 21:16:40 +01:00
function sortFunction ( x , y ) {
2020-03-27 08:45:56 +01:00
let xv = x . _sort _value ;
let yv = y . _sort _value ;
2020-01-03 21:16:40 +01:00
// 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 ;
2020-03-27 08:45:56 +01:00
let c = sortAscending ? sortCompare ( xv , yv ) : sortCompare ( yv , xv ) ;
2020-01-03 21:16:40 +01:00
if ( c !== 0 ) return c ;
return x . _sort _pos - y . _sort _pos ;
}
function resortTable ( pList ) {
2020-01-12 21:51:38 +01:00
if ( ! sortExtract )
return ;
2020-01-03 21:16:40 +01:00
// presort by dataSource
if ( sortId == "sitedist" ) {
2020-03-27 08:45:56 +01:00
for ( let i = 0 ; i < pList . length ; ++ i ) {
2020-01-03 21:16:40 +01:00
pList [ i ] . _sort _pos = i ;
}
pList . sort ( function ( x , y ) {
const a = x . getDataSourceNumber ( ) ;
const b = y . getDataSourceNumber ( ) ;
if ( a == b )
return ( x . _sort _pos - y . _sort _pos ) ;
return ( a - b ) ;
} ) ;
}
// or distance
else if ( sortId == "data_source" ) {
pList . sort ( function ( x , y ) {
return ( x . sitedist - y . sitedist ) ;
} ) ;
}
// or longitude
else {
pList . sort ( function ( x , y ) {
const xlon = x . position ? x . position [ 0 ] : 500 ;
const ylon = y . position ? y . position [ 0 ] : 500 ;
return ( xlon - ylon ) ;
} ) ;
}
// 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.
2020-03-27 08:45:56 +01:00
for ( let i = 0 ; i < pList . length ; ++ i ) {
2020-01-03 21:16:40 +01:00
pList [ i ] . _sort _pos = i ;
pList [ i ] . _sort _value = sortExtract ( pList [ i ] ) ;
}
pList . sort ( sortFunction ) ;
// Put selected planes on top, do a stable sort!
// actually that's a bad idea, disable this for now
if ( ! SelectedAllPlanes && multiSelect ) {
2020-03-27 08:45:56 +01:00
for ( let i = 0 ; i < pList . length ; ++ i ) {
2020-01-03 21:16:40 +01:00
pList [ i ] . _sort _pos = i ;
}
pList . sort ( function ( x , y ) {
if ( x . selected && y . selected ) {
return ( x . _sort _pos - y . _sort _pos ) ;
}
if ( x . selected )
return - 1 ;
if ( y . selected )
return 1 ;
return ( x . _sort _pos - y . _sort _pos ) ;
} ) ;
}
}
function sortBy ( id , sc , se ) {
if ( id != 'data_source' && grouptype _checkbox ) {
$ ( '#grouptype_checkbox' ) . removeClass ( 'settingsCheckboxChecked' ) ;
grouptype _checkbox = false ;
} else if ( id == 'data_source' && ! grouptype _checkbox ) {
$ ( '#grouptype_checkbox' ) . addClass ( 'settingsCheckboxChecked' ) ;
grouptype _checkbox = true ;
}
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 ;
refreshTableInfo ( ) ;
//resortTable(PlanesTableList);
}
2020-01-07 11:51:55 -08:00
function selectPlaneByHex ( hex , options ) {
2020-01-08 16:06:05 +01:00
//console.log("SELECTING", hex, options);
2020-01-07 11:51:55 -08:00
options = options || { } ;
2020-01-03 21:16:40 +01:00
//console.log("select: " + hex);
// If SelectedPlane has something in it, clear out the selected
if ( SelectedAllPlanes ) {
deselectAllPlanes ( ) ;
}
// already selected plane
2020-03-27 08:45:56 +01:00
let oldPlane = SelectedPlane ;
2020-01-03 21:16:40 +01:00
// plane to be selected
2020-03-27 08:45:56 +01:00
let newPlane = Planes [ hex ] ;
2020-01-03 21:16:40 +01:00
2020-01-08 23:40:25 +01:00
if ( ! options . noFetch && globeIndex && hex ) {
2020-03-27 08:45:56 +01:00
let URL1 = 'data/traces/' + hex . slice ( - 2 ) + '/trace_recent_' + hex + '.json' ;
let URL2 = 'data/traces/' + hex . slice ( - 2 ) + '/trace_full_' + hex + '.json' ;
2020-01-08 16:06:05 +01:00
//console.log('Requesting trace: ' + hex);
2020-01-10 22:34:23 +01:00
2020-02-03 21:55:16 +01:00
if ( ! newPlane ) {
processAircraft ( { hex : hex , } ) ;
Planes [ hex ] . last _message _time = NaN ;
newPlane = Planes [ hex ] ;
}
2020-01-19 14:02:57 +01:00
if ( showTrace ) {
2020-01-22 22:21:42 +01:00
URL1 = null ;
2020-01-31 01:08:40 +01:00
URL2 = 'globe_history/' + traceDateString + '/traces/' + hex . slice ( - 2 ) + '/trace_full_' + hex + '.json' ;
2020-01-19 14:02:57 +01:00
}
2020-01-28 02:18:21 +01:00
if ( newPlane && ( showTrace || showTraceExit ) ) {
2020-01-28 15:52:44 +01:00
SelectedPlane = oldPlane = null ;
processAircraft ( { hex : hex , } ) ;
2020-01-28 01:19:10 +01:00
newPlane . trace = [ ] ;
newPlane . recentTrace = null ;
newPlane . fullTrace = null ;
newPlane . position = null ;
newPlane . callsign = null ;
newPlane . track = null ;
newPlane . rotation = null ;
newPlane . altitude = null ;
2020-01-28 15:52:44 +01:00
newPlane . messages = NaN ;
2020-01-28 01:19:10 +01:00
newPlane . seen = NaN ;
newPlane . last _message _time = NaN ;
newPlane . seen _pos = NaN ;
newPlane . position _time = NaN ;
}
2020-01-19 14:02:57 +01:00
2020-03-27 08:45:56 +01:00
let req1 = null ;
let req2 = null ;
2020-01-22 22:21:42 +01:00
if ( URL1 ) {
2020-03-27 08:45:56 +01:00
req1 = $ . ajax ( { url : URL1 ,
2020-01-19 14:02:57 +01:00
dataType : 'json' ,
2020-03-24 19:45:03 +01:00
options : options ,
2020-01-19 14:02:57 +01:00
} ) ;
}
2020-01-22 22:21:42 +01:00
req2 = $ . ajax ( { url : URL2 ,
dataType : 'json' ,
2020-03-24 19:45:03 +01:00
options : options ,
2020-01-22 22:21:42 +01:00
} ) ;
2020-02-04 13:38:47 +01:00
if ( req1 ) {
req1 . done ( function ( data ) {
Planes [ data . icao ] . recentTrace = data ;
2020-03-24 19:45:03 +01:00
Planes [ data . icao ] . processTrace ( this . options ) ;
2020-01-22 22:21:42 +01:00
} ) ;
2020-01-03 21:16:40 +01:00
}
2020-02-04 13:38:47 +01:00
req2 . done ( function ( data ) {
Planes [ data . icao ] . fullTrace = data ;
2020-03-23 09:21:02 +01:00
if ( showTrace )
legShift ( 0 ) ;
else
2020-03-24 19:45:03 +01:00
Planes [ data . icao ] . processTrace ( this . options ) ;
2020-03-23 09:21:02 +01:00
} ) ;
req2 . fail ( function ( ) {
legShift ( 0 ) ;
2020-02-04 13:38:47 +01:00
} ) ;
2020-01-03 21:16:40 +01:00
}
if ( ! multiSelect && oldPlane ) {
oldPlane . selected = false ;
oldPlane . clearLines ( ) ;
oldPlane . updateMarker ( ) ;
$ ( oldPlane . tr ) . removeClass ( "selected" ) ;
// scroll the infoblock back to the top for the next plane to be selected
//$('.infoblock-container').scrollTop(0);
}
// multiSelect deselect
2020-01-07 23:31:35 +01:00
if ( multiSelect && newPlane && newPlane . selected && ! options . follow && ! onlySelected ) {
2020-01-03 21:16:40 +01:00
newPlane . selected = false ;
newPlane . clearLines ( ) ;
newPlane . updateMarker ( ) ;
$ ( newPlane . tr ) . removeClass ( "selected" ) ;
newPlane = null ;
}
// If we are clicking the same plane, we are deselecting it.
// (unless it was a doubleclick..)
2020-03-23 08:58:45 +01:00
if ( oldPlane == newPlane && ! options . follow && ! options . noDeselect ) {
2020-01-03 21:16:40 +01:00
newPlane = null ;
}
if ( newPlane ) {
// Assign the new selected
SelectedPlane = newPlane ;
newPlane . selected = true ;
newPlane . updateTick ( true ) ;
$ ( newPlane . tr ) . addClass ( "selected" ) ;
newPlane . logSel ( newPlane . history _size ) ;
//console.log(newPlane.baseMarkerKey);
} else {
SelectedPlane = null ;
}
2020-01-07 23:31:35 +01:00
if ( newPlane && newPlane . position && options . follow ) {
2020-03-24 19:45:03 +01:00
toggleFollow ( true ) ;
2020-01-07 23:31:35 +01:00
if ( ! options . zoom )
options . zoom = 'follow' ;
} else {
2020-03-24 19:45:03 +01:00
toggleFollow ( false ) ;
2020-01-07 23:31:35 +01:00
}
2020-01-08 23:40:25 +01:00
if ( newPlane && newPlane . position ) {
newPlane . updateLines ( ) ;
newPlane . updateMarker ( true ) ;
}
2020-01-03 21:16:40 +01:00
2020-01-07 23:31:35 +01:00
if ( options . zoom == 'follow' ) {
if ( OLMap . getView ( ) . getZoom ( ) < 8 )
OLMap . getView ( ) . setZoom ( 8 ) ;
2020-01-07 11:51:55 -08:00
} else if ( options . zoom ) {
OLMap . getView ( ) . setZoom ( options . zoom ) ;
}
2020-01-03 21:16:40 +01:00
2020-03-23 09:34:14 +01:00
updateAddressBar ( ) ;
2020-01-03 21:16:40 +01:00
refreshSelected ( ) ;
2020-01-09 15:15:34 +01:00
refreshTableInfo ( ) ;
2020-01-03 21:16:40 +01:00
}
// 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 ( ) ;
return ;
}
buttonActive ( '#T' , true ) ;
// If SelectedPlane has something in it, clear out the selected
if ( SelectedPlane != null ) {
SelectedPlane . selected = false ;
SelectedPlane . clearLines ( ) ;
SelectedPlane . updateMarker ( ) ;
$ ( SelectedPlane . tr ) . removeClass ( "selected" ) ;
}
SelectedPlane = null ;
SelectedAllPlanes = true ;
refreshFeatures ( ) ;
$ ( '#selectall_checkbox' ) . addClass ( 'settingsCheckboxChecked' ) ;
refreshSelected ( ) ;
refreshHighlighted ( ) ;
refreshTableInfo ( ) ;
}
// deselect all the planes
function deselectAllPlanes ( ) {
2020-01-22 20:49:11 +01:00
if ( showTrace )
return ;
2020-01-19 21:10:27 +01:00
if ( ! multiSelect && SelectedPlane )
toggleIsolation ( false , "off" ) ;
2020-01-03 21:16:40 +01:00
buttonActive ( '#T' , false ) ;
2020-03-27 08:45:56 +01:00
for ( let key in Planes ) {
2020-01-03 21:16:40 +01:00
Planes [ key ] . selected = false ;
$ ( Planes [ key ] . tr ) . removeClass ( "selected" ) ;
}
$ ( '#selectall_checkbox' ) . removeClass ( 'settingsCheckboxChecked' ) ;
SelectedAllPlanes = false ;
SelectedPlane = null ;
refreshFeatures ( ) ;
refreshHighlighted ( ) ;
refreshTableInfo ( ) ;
2020-01-23 16:20:27 +01:00
updateAddressBar ( ) ;
2020-03-23 09:34:14 +01:00
refreshSelected ( ) ;
2020-01-03 21:16:40 +01:00
}
2020-03-24 19:45:03 +01:00
function toggleFollow ( override ) {
if ( override == true )
FollowSelected = true ;
else if ( override == false )
2020-01-13 14:24:32 +01:00
FollowSelected = false ;
2020-03-24 19:45:03 +01:00
else
FollowSelected = ! FollowSelected ;
if ( FollowSelected ) {
if ( ! SelectedPlane || ! SelectedPlane . position )
FollowSelected = false ;
}
if ( FollowSelected ) {
2020-03-30 17:30:42 +02:00
if ( override == undefined && OLMap . getView ( ) . getZoom ( ) < 8 )
2020-03-24 19:45:03 +01:00
OLMap . getView ( ) . setZoom ( 8 ) ;
OLMap . getView ( ) . setCenter ( ol . proj . fromLonLat ( SelectedPlane . position ) ) ;
}
2020-01-13 14:24:32 +01:00
buttonActive ( '#F' , FollowSelected ) ;
2020-01-03 21:16:40 +01:00
}
function resetMap ( ) {
2020-02-15 00:39:16 +01:00
2020-01-03 21:16:40 +01:00
// Reset localStorage values and map settings
localStorage [ 'CenterLat' ] = CenterLat = DefaultCenterLat ;
localStorage [ 'CenterLon' ] = CenterLon = DefaultCenterLon ;
localStorage [ 'ZoomLvl' ] = ZoomLvl = DefaultZoomLvl ;
// Set and refresh
OLMap . getView ( ) . setZoom ( ZoomLvl ) ;
OLMap . getView ( ) . setCenter ( ol . proj . fromLonLat ( [ CenterLon , CenterLat ] ) ) ;
OLMap . getView ( ) . setRotation ( mapOrientation ) ;
//selectPlaneByHex(null,false);
$ ( "#update_error" ) . css ( 'display' , 'none' ) ;
}
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" ) ;
localStorage [ 'sidebar_visible' ] = sidebarVisible = $ ( "#sidebar_container" ) . is ( ":visible" ) ;
updateMapSize ( ) ;
}
function expandSidebar ( e ) {
e . preventDefault ( ) ;
$ ( "#map_container" ) . hide ( )
mapIsVisible = false ;
$ ( "#toggle_sidebar_control" ) . hide ( ) ;
$ ( "#splitter" ) . hide ( ) ;
$ ( "#sudo_buttons" ) . hide ( ) ;
$ ( "#show_map_button" ) . show ( ) ;
$ ( "#sidebar_container" ) . width ( "100%" ) ;
setColumnVisibility ( ) ;
clearTimeout ( refreshId ) ;
fetchData ( ) ;
refreshTableInfo ( ) ;
updateMapSize ( ) ;
setSelectedInfoBlockVisibility ( ) ;
}
function showMap ( ) {
$ ( '#sidebar_container' ) . width ( localStorage [ 'sidebar_width' ] ) . css ( 'margin-left' , '0' ) ;
$ ( "#map_container" ) . show ( )
mapIsVisible = true ;
$ ( "#toggle_sidebar_control" ) . show ( ) ;
$ ( "#splitter" ) . show ( ) ;
$ ( "#sudo_buttons" ) . show ( ) ;
$ ( "#show_map_button" ) . hide ( ) ;
setColumnVisibility ( ) ;
clearTimeout ( refreshId ) ;
fetchData ( ) ;
refreshTableInfo ( ) ;
updateMapSize ( ) ;
}
function showColumn ( table , columnId , visible ) {
2020-03-27 08:45:56 +01:00
let index = $ ( columnId ) . index ( ) ;
2020-01-03 21:16:40 +01:00
columnVis [ index ] = visible ;
if ( index >= 0 ) {
2020-03-27 08:45:56 +01:00
let cells = $ ( table ) . find ( "td:nth-child(" + ( index + 1 ) . toString ( ) + ")" ) ;
2020-01-03 21:16:40 +01:00
if ( visible ) {
cells . show ( ) ;
} else {
cells . hide ( ) ;
}
}
}
function setColumnVisibility ( ) {
2020-03-27 08:45:56 +01:00
let infoTable = $ ( "#tableinfo" ) ;
2020-01-03 21:16:40 +01:00
2020-03-27 08:45:56 +01:00
let tbody = document . getElementById ( 'tableinfo' ) . tBodies [ 0 ] ;
for ( let i = 0 ; i < PlanesOrdered . length ; ++ i ) {
let plane = PlanesOrdered [ i ] ;
2020-03-18 09:04:35 +01:00
if ( plane . tr ) {
if ( plane . inTable ) {
tbody . removeChild ( plane . tr ) ;
}
tbody . appendChild ( plane . tr ) ;
plane . inTable = true ;
}
2020-01-03 21:16:40 +01:00
}
2020-03-27 08:45:56 +01:00
for ( let col in HideCols ) {
2020-01-03 21:16:40 +01:00
showColumn ( infoTable , HideCols [ col ] , ! mapIsVisible ) ;
}
}
function setSelectedInfoBlockVisibility ( ) {
if ( SelectedPlane ) {
$ ( '#selected_infoblock' ) . show ( ) ;
if ( ! mapIsVisible )
$ ( "#sidebar_container" ) . css ( 'margin-left' , '140pt' ) ;
//$('#sidebar_canvas').css('margin-bottom', $('#selected_infoblock').height() + 'px');
2020-02-29 17:34:46 +01:00
//
$ ( '#large_mode_control' ) . css ( 'left' , ( 190 * globalScale ) + 'px' ) ;
2020-01-03 21:16:40 +01:00
}
else {
$ ( '#selected_infoblock' ) . hide ( ) ;
if ( ! mapIsVisible )
$ ( "#sidebar_container" ) . css ( 'margin-left' , '0' ) ;
//$('#sidebar_canvas').css('margin-bottom', 0);
2020-02-07 07:57:41 +01:00
2020-02-29 17:34:46 +01:00
$ ( '#large_mode_control' ) . css ( 'left' , ( 5 * globalScale ) + 'px' ) ;
2020-01-03 21:16:40 +01:00
}
}
function initializeUnitsSelector ( ) {
// Get display unit preferences from local storage
if ( ! localStorage . getItem ( 'displayUnits' ) ) {
localStorage [ 'displayUnits' ] = "nautical" ;
}
2020-03-27 08:45:56 +01:00
let displayUnits = localStorage [ 'displayUnits' ] ;
2020-01-03 21:16:40 +01:00
DisplayUnits = displayUnits ;
setAltitudeLegend ( displayUnits ) ;
// Initialize drop-down
2020-03-27 08:45:56 +01:00
let unitsSelector = $ ( "#units_selector" ) ;
2020-01-03 21:16:40 +01:00
unitsSelector . val ( displayUnits ) ;
unitsSelector . on ( "change" , onDisplayUnitsChanged ) ;
$ ( ".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 ) ) ;
}
function onDisplayUnitsChanged ( e ) {
2020-03-27 08:45:56 +01:00
let displayUnits = e . target . value ;
2020-01-03 21:16:40 +01:00
// Save display units to local storage
localStorage [ 'displayUnits' ] = displayUnits ;
DisplayUnits = displayUnits ;
setAltitudeLegend ( displayUnits ) ;
// Update filters
updatePlaneFilter ( ) ;
// Refresh data
refreshSelected ( ) ;
refreshHighlighted ( ) ;
refreshTableInfo ( ) ;
// Redraw range rings
2020-03-02 19:02:34 +01:00
if ( SitePosition != null && SiteCircles ) {
2020-01-03 21:16:40 +01:00
createSiteCircleFeatures ( ) ;
}
// Reset map scale line units
OLMap . getControls ( ) . forEach ( function ( control ) {
if ( control instanceof ol . control . ScaleLine ) {
control . setUnits ( displayUnits ) ;
}
} ) ;
$ ( ".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 ) ) ;
}
function setAltitudeLegend ( units ) {
if ( units === 'metric' ) {
$ ( '#altitude_chart_button' ) . addClass ( 'altitudeMeters' ) ;
} else {
$ ( '#altitude_chart_button' ) . removeClass ( 'altitudeMeters' ) ;
}
}
function onFilterByAltitude ( e ) {
e . preventDefault ( ) ;
$ ( "#altitude_filter_min" ) . blur ( ) ;
$ ( "#altitude_filter_max" ) . blur ( ) ;
updatePlaneFilter ( ) ;
if ( SelectedPlane && SelectedPlane . isFiltered ( ) ) {
SelectedPlane . selected = false ;
SelectedPlane . clearLines ( ) ;
SelectedPlane = null ;
}
refreshSelected ( ) ;
refreshHighlighted ( ) ;
refreshTableInfo ( ) ;
}
function filterGroundVehicles ( switchFilter ) {
if ( typeof localStorage [ 'groundVehicleFilter' ] === 'undefined' ) {
localStorage [ 'groundVehicleFilter' ] = 'not_filtered' ;
}
2020-03-27 08:45:56 +01:00
let groundFilter = localStorage [ 'groundVehicleFilter' ] ;
2020-01-03 21:16:40 +01:00
if ( switchFilter === true ) {
groundFilter = ( groundFilter === 'not_filtered' ) ? 'filtered' : 'not_filtered' ;
}
if ( groundFilter === 'not_filtered' ) {
$ ( '#groundvehicle_filter' ) . addClass ( 'settingsCheckboxChecked' ) ;
} else {
$ ( '#groundvehicle_filter' ) . removeClass ( 'settingsCheckboxChecked' ) ;
}
localStorage [ 'groundVehicleFilter' ] = groundFilter ;
PlaneFilter . groundVehicles = groundFilter ;
}
function filterBlockedMLAT ( switchFilter ) {
if ( typeof localStorage [ 'blockedMLATFilter' ] === 'undefined' ) {
localStorage [ 'blockedMLATFilter' ] = 'not_filtered' ;
}
2020-03-27 08:45:56 +01:00
let blockedMLATFilter = localStorage [ 'blockedMLATFilter' ] ;
2020-01-03 21:16:40 +01:00
if ( switchFilter === true ) {
blockedMLATFilter = ( blockedMLATFilter === 'not_filtered' ) ? 'filtered' : 'not_filtered' ;
}
if ( blockedMLATFilter === 'not_filtered' ) {
$ ( '#blockedmlat_filter' ) . addClass ( 'settingsCheckboxChecked' ) ;
} else {
$ ( '#blockedmlat_filter' ) . removeClass ( 'settingsCheckboxChecked' ) ;
}
localStorage [ 'blockedMLATFilter' ] = blockedMLATFilter ;
PlaneFilter . blockedMLAT = blockedMLATFilter ;
}
function buttonActive ( id , state ) {
if ( state ) {
$ ( id ) . addClass ( 'activeButton' ) ;
$ ( id ) . removeClass ( 'inActiveButton' ) ;
} else {
$ ( id ) . addClass ( 'inActiveButton' ) ;
$ ( id ) . removeClass ( 'activeButton' ) ;
}
}
2020-01-19 21:10:27 +01:00
function toggleIsolation ( on , off ) {
2020-01-03 21:16:40 +01:00
onlySelected = ! onlySelected ;
2020-01-19 21:10:27 +01:00
if ( on )
onlySelected = true ;
if ( off )
onlySelected = false ;
2020-01-03 21:16:40 +01:00
buttonActive ( '#I' , onlySelected ) ;
refreshFeatures ( ) ;
2020-01-09 15:15:34 +01:00
refreshTableInfo ( ) ;
2020-01-03 21:16:40 +01:00
}
function toggleMilitary ( ) {
onlyMilitary = ! onlyMilitary ;
buttonActive ( '#U' , onlyMilitary ) ;
2020-01-13 13:16:32 +01:00
refreshTableInfo ( ) ;
2020-01-03 21:16:40 +01:00
}
function togglePersistence ( ) {
noVanish = ! noVanish ;
filterTracks = noVanish ;
buttonActive ( '#P' , noVanish ) ;
2020-03-18 11:18:48 +01:00
remakeTrails ( ) ;
2020-01-03 21:16:40 +01:00
if ( ! noVanish )
reaper ( ) ;
localStorage [ 'noVanish' ] = noVanish ;
console . log ( 'noVanish = ' + noVanish ) ;
2020-01-13 13:16:32 +01:00
refreshTableInfo ( ) ;
2020-01-03 21:16:40 +01:00
}
2020-02-04 21:14:54 +01:00
function toggleLastLeg ( ) {
2020-03-18 18:56:22 +01:00
if ( ! globeIndex )
return ;
2020-02-18 10:24:11 +01:00
if ( lastLeg ) {
2020-02-04 21:14:54 +01:00
lastLeg = false ;
localStorage [ 'lastLeg' ] = "false" ;
$ ( '#lastLeg_checkbox' ) . removeClass ( 'settingsCheckboxChecked' ) ;
} else {
lastLeg = true ;
localStorage [ 'lastLeg' ] = "true" ;
$ ( '#lastLeg_checkbox' ) . addClass ( 'settingsCheckboxChecked' ) ;
}
2020-03-22 22:40:17 +01:00
if ( SelectedPlane && ! showTrace )
2020-02-04 21:14:54 +01:00
SelectedPlane . processTrace ( ) ;
}
2020-01-03 21:16:40 +01:00
function toggleDebugAll ( ) {
if ( localStorage [ 'debugAll' ] === "true" ) {
debugAll = false ;
localStorage [ 'debugAll' ] = "false" ;
$ ( '#debugAll_checkbox' ) . removeClass ( 'settingsCheckboxChecked' ) ;
} else {
debugAll = true ;
localStorage [ 'debugAll' ] = "true" ;
$ ( '#debugAll_checkbox' ) . addClass ( 'settingsCheckboxChecked' ) ;
}
}
function toggleDebugTracks ( ) {
if ( localStorage [ 'debugTracks' ] === "true" ) {
debugTracks = false ;
localStorage [ 'debugTracks' ] = "false" ;
$ ( '#debug_checkbox' ) . removeClass ( 'settingsCheckboxChecked' ) ;
} else {
debugTracks = true ;
localStorage [ 'debugTracks' ] = "true" ;
$ ( '#debug_checkbox' ) . addClass ( 'settingsCheckboxChecked' ) ;
}
2020-03-18 11:18:48 +01:00
remakeTrails ( ) ;
2020-01-03 21:16:40 +01:00
}
function dim ( evt ) {
2020-03-09 20:29:24 +01:00
const dim = 0.35 ;
const contrast = 0.13 ;
2020-01-03 21:16:40 +01:00
evt . context . globalCompositeOperation = 'multiply' ;
if ( evt . context . globalCompositeOperation == 'multiply' ) {
evt . context . fillStyle = 'rgba(0,0,0,' + dim + ')' ;
evt . context . fillRect ( 0 , 0 , evt . context . canvas . width , evt . context . canvas . height ) ;
}
evt . context . globalCompositeOperation = 'overlay' ;
if ( evt . context . globalCompositeOperation == 'overlay' ) {
evt . context . fillStyle = 'rgba(0,0,0,' + contrast + ')' ;
evt . context . fillRect ( 0 , 0 , evt . context . canvas . width , evt . context . canvas . height ) ;
}
evt . context . globalCompositeOperation = 'source-over' ;
}
function toggleMapDim ( switchOn ) {
if ( ! switchOn && localStorage [ 'MapDim' ] === "true" ) {
localStorage [ 'MapDim' ] = "false" ;
MapDim = false ;
ol . control . LayerSwitcher . forEachRecursive ( layers _group , function ( lyr ) {
if ( lyr . get ( 'type' ) != 'base' )
return ;
ol . Observable . unByKey ( lyr . dimKey ) ;
} ) ;
$ ( '#mapdim_checkbox' ) . removeClass ( 'settingsCheckboxChecked' ) ;
/ *
$ ( 'html' ) . css ( 'background-color' , '#F8F8F8' ) ;
$ ( 'body' ) . css ( 'background-color' , '#F8F8F8' ) ;
$ ( '#selected_infoblock' ) . css ( 'background-color' , '#F8F8F8' ) ;
$ ( '#highlighted_infoblock' ) . css ( 'background-color' , '#F8F8F8' ) ;
$ ( '.altitudeFilterInput' ) . css ( 'background-color' , '#F8F8F8' ) ;
* /
} else {
localStorage [ 'MapDim' ] = "true" ;
MapDim = true ;
ol . control . LayerSwitcher . forEachRecursive ( layers _group , function ( lyr ) {
if ( lyr . get ( 'type' ) != 'base' )
return ;
2020-03-18 10:54:47 +01:00
lyr . dimKey = lyr . on ( 'postrender' , dim ) ;
2020-01-03 21:16:40 +01:00
} ) ;
$ ( '#mapdim_checkbox' ) . addClass ( 'settingsCheckboxChecked' ) ;
}
OLMap . render ( ) ;
buttonActive ( '#B' , localStorage [ 'MapDim' ] == "true" ) ;
}
function toggleAltitudeChart ( switchToggle ) {
if ( typeof localStorage [ 'altitudeChart' ] === 'undefined' ) {
localStorage [ 'altitudeChart' ] = 'show' ;
}
2020-03-27 08:45:56 +01:00
let altitudeChartDisplay = localStorage [ 'altitudeChart' ] ;
2020-01-03 21:16:40 +01:00
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 [ 'altitudeChart' ] = altitudeChartDisplay ;
}
function followRandomPlane ( ) {
2020-01-28 02:18:21 +01:00
if ( showTrace )
return ;
2020-03-27 08:45:56 +01:00
let this _one = null ;
let tired = 0 ;
2020-01-03 21:16:40 +01:00
do {
this _one = PlanesOrdered [ Math . floor ( Math . random ( ) * PlanesOrdered . length ) ] ;
2020-01-28 01:19:10 +01:00
if ( ! this _one || tired ++ > 1000 )
2020-01-03 21:16:40 +01:00
break ;
} while ( this _one . isFiltered ( ) || ! this _one . position || ( now - this _one . position _time > 30 ) ) ;
//console.log(this_one.icao);
2020-01-28 01:19:10 +01:00
if ( this _one )
selectPlaneByHex ( this _one . icao , { follow : true } ) ;
2020-01-03 21:16:40 +01:00
}
function toggleTableInView ( switchOn ) {
2020-01-14 15:02:04 +01:00
if ( switchOn || ( globeIndex && ! icaoFilter ) ) {
2020-01-03 21:16:40 +01:00
tableInView = true ;
} else {
tableInView = ! tableInView ;
refreshTableInfo ( ) ;
}
localStorage [ 'tableInView' ] = tableInView ;
if ( tableInView ) {
$ ( '#with_positions' ) . text ( "On Screen:" ) ;
} else {
$ ( '#with_positions' ) . text ( "With Position:" ) ;
}
buttonActive ( '#V' , tableInView ) ;
}
function toggleLabels ( switchOn ) {
enableLabels = ! enableLabels ;
localStorage [ 'enableLabels' ] = enableLabels ;
2020-03-27 08:45:56 +01:00
for ( let key in PlanesOrdered ) {
2020-01-03 21:16:40 +01:00
PlanesOrdered [ key ] . updateMarker ( false ) ;
}
buttonActive ( '#L' , enableLabels ) ;
2020-03-23 10:17:43 +01:00
if ( showTrace )
remakeTrails ( ) ;
2020-01-03 21:16:40 +01:00
}
function toggleExtendedLabels ( ) {
2020-01-14 16:06:45 +01:00
if ( isNaN ( extendedLabels ) )
extendedLabels = 0 ;
extendedLabels ++ ;
extendedLabels %= 3 ;
console . log ( extendedLabels ) ;
2020-01-03 21:16:40 +01:00
localStorage [ 'extendedLabels' ] = extendedLabels ;
2020-03-27 08:45:56 +01:00
for ( let key in PlanesOrdered ) {
2020-01-03 21:16:40 +01:00
PlanesOrdered [ key ] . updateMarker ( false ) ;
}
buttonActive ( '#O' , extendedLabels ) ;
}
function toggleTrackLabels ( ) {
trackLabels = ! trackLabels ;
localStorage [ 'trackLabels' ] = trackLabels ;
2020-03-18 11:18:48 +01:00
remakeTrails ( ) ;
2020-01-03 21:16:40 +01:00
buttonActive ( '#K' , trackLabels ) ;
}
2020-01-19 21:10:27 +01:00
function toggleMultiSelect ( on , off ) {
multiSelect = ! multiSelect ;
if ( on )
multiSelect = true ;
if ( off )
2020-01-03 21:16:40 +01:00
multiSelect = false ;
2020-01-19 21:10:27 +01:00
if ( ! multiSelect ) {
if ( ! SelectedPlane )
toggleIsolation ( false , "off" ) ;
2020-03-27 08:45:56 +01:00
let plane = SelectedPlane ;
2020-01-19 21:10:27 +01:00
SelectedPlane = null ;
2020-01-03 21:16:40 +01:00
deselectAllPlanes ( ) ;
2020-01-07 22:19:54 +01:00
if ( plane )
selectPlaneByHex ( plane . icao ) ;
2020-01-03 21:16:40 +01:00
}
2020-01-19 21:10:27 +01:00
2020-01-03 21:16:40 +01:00
buttonActive ( '#M' , multiSelect ) ;
}
2020-01-09 22:10:51 +01:00
function onJump ( e ) {
2020-03-24 19:45:03 +01:00
toggleFollow ( false ) ;
2020-03-18 09:26:01 +01:00
if ( e ) {
e . preventDefault ( ) ;
airport = $ ( "#jump_input" ) . val ( ) . trim ( ) . toUpperCase ( ) ;
$ ( "#jump_input" ) . val ( "" ) ;
$ ( "#jump_input" ) . blur ( ) ;
}
if ( ! _airport _coords _cache ) {
$ . getJSON ( databaseFolder + "/airport-coords.js" )
. done ( function ( data ) {
_airport _coords _cache = data ;
onJump ( ) ;
} ) ;
} else {
const coords = _airport _coords _cache [ airport ] ;
if ( coords ) {
OLMap . getView ( ) . setCenter ( ol . proj . fromLonLat ( [ coords [ 1 ] , coords [ 0 ] ] ) ) ;
refreshTableInfo ( ) ;
if ( ZoomLvl >= 7 )
fetchData ( ) ;
}
2020-01-09 22:10:51 +01:00
}
}
2020-01-03 21:16:40 +01:00
function onSearch ( e ) {
e . preventDefault ( ) ;
const searchTerm = $ ( "#search_input" ) . val ( ) . trim ( ) ;
$ ( "#search_input" ) . val ( "" ) ;
$ ( "#search_input" ) . blur ( ) ;
2020-01-13 13:39:39 +01:00
if ( searchTerm )
findPlanes ( searchTerm , true , true , true , true ) ;
2020-01-03 21:16:40 +01:00
return false ;
}
2020-01-04 13:51:14 +01:00
function onResetCallsignFilter ( e ) {
$ ( "#callsign_filter" ) . val ( "" ) ;
$ ( "#callsign_filter" ) . blur ( ) ;
updateCallsignFilter ( ) ;
}
function updateCallsignFilter ( e ) {
if ( e )
e . preventDefault ( ) ;
$ ( "#callsign_filter" ) . blur ( ) ;
PlaneFilter . callsign = $ ( "#callsign_filter" ) . val ( ) . trim ( ) . toUpperCase ( ) ;
refreshSelected ( ) ;
refreshHighlighted ( ) ;
refreshTableInfo ( ) ;
}
function onResetTypeFilter ( e ) {
$ ( "#type_filter" ) . val ( "" ) ;
$ ( "#type_filter" ) . blur ( ) ;
updateTypeFilter ( ) ;
}
function updateTypeFilter ( e ) {
if ( e )
e . preventDefault ( ) ;
$ ( "#type_filter" ) . blur ( ) ;
2020-03-27 08:45:56 +01:00
let type = $ ( "#type_filter" ) . val ( ) . trim ( ) ;
2020-01-04 13:51:14 +01:00
PlaneFilter . type = type . toUpperCase ( ) ;
refreshSelected ( ) ;
refreshHighlighted ( ) ;
refreshTableInfo ( ) ;
}
2020-01-14 22:00:43 +01:00
function onResetDescriptionFilter ( e ) {
$ ( "#description_filter" ) . val ( "" ) ;
$ ( "#description_filter" ) . blur ( ) ;
updateTypeFilter ( ) ;
}
function updateDescriptionFilter ( e ) {
if ( e )
e . preventDefault ( ) ;
$ ( "#description_filter" ) . blur ( ) ;
2020-03-27 08:45:56 +01:00
let description = $ ( "#description_filter" ) . val ( ) . trim ( ) ;
2020-01-14 22:00:43 +01:00
PlaneFilter . description = description . toUpperCase ( ) ;
refreshSelected ( ) ;
refreshHighlighted ( ) ;
refreshTableInfo ( ) ;
}
2020-01-03 21:16:40 +01:00
function onResetAltitudeFilter ( e ) {
$ ( "#altitude_filter_min" ) . val ( "" ) ;
$ ( "#altitude_filter_max" ) . val ( "" ) ;
$ ( "#altitude_filter_min" ) . blur ( ) ;
$ ( "#altitude_filter_max" ) . blur ( ) ;
updatePlaneFilter ( ) ;
refreshTableInfo ( ) ;
}
function updatePlaneFilter ( ) {
2020-03-27 08:45:56 +01:00
let minAltitude = parseFloat ( $ ( "#altitude_filter_min" ) . val ( ) . trim ( ) ) ;
let maxAltitude = parseFloat ( $ ( "#altitude_filter_max" ) . val ( ) . trim ( ) ) ;
let enabled = false ;
2020-01-03 21:16:40 +01:00
if ( minAltitude < - 1e6 || minAltitude > 1e6 || isNaN ( minAltitude ) )
minAltitude = - 1e6 ;
else
enabled = true ;
if ( maxAltitude < - 1e6 || maxAltitude > 1e6 || isNaN ( maxAltitude ) )
maxAltitude = 1e6 ;
else
enabled = true ;
PlaneFilter . enabled = enabled ;
if ( DisplayUnits == "metric" ) {
PlaneFilter . minAltitude = minAltitude * 3.2808 ;
PlaneFilter . maxAltitude = maxAltitude * 3.2808 ;
} else {
PlaneFilter . minAltitude = minAltitude ;
PlaneFilter . maxAltitude = maxAltitude ;
}
if ( filterTracks ) {
2020-03-18 11:18:48 +01:00
remakeTrails ( ) ;
2020-01-03 21:16:40 +01:00
}
refreshFeatures ( ) ;
}
function getFlightAwareIdentLink ( ident , linkText ) {
if ( ident !== null && ident !== "" ) {
if ( ! linkText ) {
linkText = ident ;
}
return "<a target=\"_blank\" href=\"https://flightaware.com/live/flight/" + ident . trim ( ) + "\">" + linkText + "</a>" ;
}
return "" ;
}
function getFlightAwareModeSLink ( code , ident , linkText ) {
if ( code !== null && code . length > 0 && code [ 0 ] !== '~' && code !== "000000" ) {
if ( ! linkText ) {
linkText = "FlightAware: " + code . toUpperCase ( ) ;
}
2020-03-27 08:45:56 +01:00
let linkHtml = "<a target=\"_blank\" href=\"https://flightaware.com/live/modes/" + code ;
2020-01-03 21:16:40 +01:00
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 "" ;
}
// 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 ( layers _group , function ( lyr ) {
if ( lyr . get ( 'name' ) === layer && lyr . getVisible ( ) ) {
$ ( element ) . addClass ( 'settingsCheckboxChecked' ) ;
}
} ) ;
$ ( element ) . on ( 'click' , function ( ) {
2020-03-27 08:45:56 +01:00
let visible = false ;
2020-01-03 21:16:40 +01:00
if ( $ ( element ) . hasClass ( 'settingsCheckboxChecked' ) ) {
visible = true ;
}
ol . control . LayerSwitcher . forEachRecursive ( layers _group , 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 ( ) ;
}
}
} )
}
// 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 fetchPfData ( ) {
if ( fetchingPf )
return ;
fetchingPf = true ;
2020-03-27 08:45:56 +01:00
for ( let i in pf _data ) {
2020-01-03 21:16:40 +01:00
const req = $ . ajax ( { url : pf _data [ i ] ,
dataType : 'json' } ) ;
$ . when ( req ) . done ( function ( data ) {
2020-03-27 08:45:56 +01:00
for ( let i in PlanesOrdered ) {
2020-01-03 21:16:40 +01:00
const plane = PlanesOrdered [ i ] ;
const ac = data . aircraft [ plane . icao . toUpperCase ( ) ] ;
if ( ! ac ) {
continue ;
}
plane . pfRoute = ac . route ;
plane . pfMach = ac . mach ;
plane . pfFlightno = ac . flightno ;
if ( ac . reg && ac . reg != "????" && ac . reg != "z.NO-REG" )
plane . registration = ac . reg ;
if ( ac . type && ac . type != "????" && ac . type != "ZVEH" )
plane . icaoType = ac . type ;
if ( plane . icaoType != plane . icaoTypeCache ) {
2020-03-27 08:45:56 +01:00
let typeData = _aircraft _type _cache [ plane . icaoType ] ;
2020-01-03 21:16:40 +01:00
if ( typeData ) {
plane . typeDescription = typeData . desc ;
plane . wtc = typeData . wtc ;
}
//console.log(plane.icao +" "+ plane.flight + " was " + plane.icaoTypeCache + " and is now " + plane.icaoType + " " + plane.typeDescription + "-" + plane.wtc);
//console.log(plane.flight);
plane . icaoTypeCache = plane . icaoType ;
}
}
fetchingPf = false ;
} ) ;
}
}
function bearingFromLonLat ( position1 , position2 ) {
// Positions in format [lon in deg, lat in deg]
const lon1 = position1 [ 0 ] * Math . PI / 180 ;
const lat1 = position1 [ 1 ] * Math . PI / 180 ;
const lon2 = position2 [ 0 ] * Math . PI / 180 ;
const lat2 = position2 [ 1 ] * Math . PI / 180 ;
const y = Math . sin ( lon2 - lon1 ) * Math . cos ( lat2 ) ;
const x = Math . cos ( lat1 ) * Math . sin ( lat2 )
- Math . sin ( lat1 ) * Math . cos ( lat2 ) * Math . cos ( lon2 - lon1 ) ;
return ( Math . atan2 ( y , x ) * 180 / Math . PI + 360 ) % 360 ;
}
function zoomIn ( ) {
2020-01-26 00:11:00 +01:00
const zoom = OLMap . getView ( ) . getZoom ( ) ;
OLMap . getView ( ) . setZoom ( ( zoom + 1 ) . toFixed ( ) ) ;
2020-01-03 21:16:40 +01:00
}
function zoomOut ( ) {
2020-01-26 00:11:00 +01:00
const zoom = OLMap . getView ( ) . getZoom ( ) ;
OLMap . getView ( ) . setZoom ( ( zoom - 1 ) . toFixed ( ) ) ;
2020-01-03 21:16:40 +01:00
}
2020-01-25 18:44:35 +01:00
function changeCenter ( init ) {
const center = ol . proj . toLonLat ( OLMap . getView ( ) . getCenter ( ) , OLMap . getView ( ) . getProjection ( ) ) ;
2020-01-25 18:23:39 +01:00
localStorage [ 'CenterLon' ] = CenterLon = center [ 0 ] ;
localStorage [ 'CenterLat' ] = CenterLat = center [ 1 ] ;
2020-01-25 18:44:35 +01:00
2020-01-25 20:08:00 +01:00
if ( ! onlySelected )
refreshTableInfo ( ) ;
2020-01-25 18:23:39 +01:00
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 ) ) {
2020-03-24 19:45:03 +01:00
toggleFollow ( false ) ;
2020-01-25 18:23:39 +01:00
refreshSelected ( ) ;
refreshHighlighted ( ) ;
}
}
2020-01-25 18:44:35 +01:00
}
2020-01-26 00:11:00 +01:00
function checkMovement ( ) {
const zoom = OLMap . getView ( ) . getZoom ( ) ;
const center = ol . proj . toLonLat ( OLMap . getView ( ) . getCenter ( ) , OLMap . getView ( ) . getProjection ( ) ) ;
if (
checkMoveZoom != zoom ||
checkMoveCenter [ 0 ] != center [ 0 ] ||
checkMoveCenter [ 1 ] != center [ 1 ]
) {
noMovement = 0 ;
}
checkMoveZoom = zoom ;
checkMoveCenter [ 0 ] = center [ 0 ] ;
checkMoveCenter [ 1 ] = center [ 1 ] ;
if ( noMovement ++ != 3 )
return ;
// no zoom/pan inputs for 450 ms after a zoom/pan input
//
//console.time("fire!");
changeZoom ( ) ;
changeCenter ( ) ;
//console.timeEnd("fire!");
}
2020-01-25 18:44:35 +01:00
function changeZoom ( init ) {
2020-02-07 07:17:01 +01:00
if ( ! OLMap )
return ;
2020-01-25 18:44:35 +01:00
ZoomLvl = OLMap . getView ( ) . getZoom ( ) ;
2020-01-25 18:23:39 +01:00
// small zoomstep, no need to change aircraft scaling
2020-02-04 13:17:48 +01:00
if ( ! init && Math . abs ( ZoomLvl - ZoomLvlCache ) < 0.4 )
2020-01-25 18:23:39 +01:00
return ;
2020-01-03 21:16:40 +01:00
localStorage [ 'ZoomLvl' ] = ZoomLvl ;
2020-01-25 18:23:39 +01:00
ZoomLvlCache = ZoomLvl ;
2020-02-29 17:34:46 +01:00
if ( globalScale <= 1.15 )
scaleFactor = Math . max ( markerMinSize , Math . min ( markerMaxSize , markerScaleFactor * 0.09 * Math . pow ( 1.35 , ZoomLvl ) ) ) ;
else {
scaleFactor = Math . max ( markerMinSize * Math . pow ( 1.3 , globalScale ) * globalScale , Math . min ( markerMaxSize , markerScaleFactor * 0.09 * Math . pow ( 1.35 , ZoomLvl ) ) ) ;
}
2020-01-26 00:11:00 +01:00
2020-01-26 20:33:16 +01:00
if ( ! onlySelected )
refreshTableInfo ( ) ;
2020-01-26 00:11:00 +01:00
2020-01-07 23:56:51 +01:00
if ( ZoomLvl > 5.5 && enableMouseover ) {
2020-01-03 21:16:40 +01:00
OLMap . on ( 'pointermove' , onPointermove ) ;
} else {
OLMap . un ( 'pointermove' , onPointermove ) ;
removeHighlight ( ) ;
}
}
function updateCell ( plane , cell , newValue , html ) {
if ( columnVis [ cell ] && newValue != plane . trCache [ cell ] ) {
plane . trCache [ cell ] = newValue ;
if ( html ) {
plane . tr . cells [ cell ] . innerHTML = newValue ;
} else {
plane . tr . cells [ cell ] . textContent = newValue ;
}
}
}
2020-01-07 23:56:51 +01:00
2020-01-03 21:16:40 +01:00
function onPointermove ( evt ) {
2020-01-07 23:56:51 +01:00
//clearTimeout(pointerMoveTimeout);
//pointerMoveTimeout = setTimeout(highlight(evt), 100);
highlight ( evt ) ;
}
function highlight ( evt ) {
2020-01-03 21:16:40 +01:00
const hex = evt . map . forEachFeatureAtPixel ( evt . pixel ,
function ( feature , layer ) {
return feature . hex ;
} ,
2020-01-07 23:56:51 +01:00
{
layerFilter : function ( layer ) {
return ( layer == iconLayer ) ;
} ,
hitTolerance : 5 ,
}
2020-01-03 21:16:40 +01:00
) ;
2020-01-07 23:56:51 +01:00
clearTimeout ( pointerMoveTimeout ) ;
2020-01-03 21:16:40 +01:00
if ( hex ) {
2020-01-07 23:56:51 +01:00
HighlightedPlane = hex ;
pointerMoveTimeout = setTimeout ( refreshHighlighted ( ) , 300 ) ;
2020-01-03 21:16:40 +01:00
} else {
2020-01-07 23:56:51 +01:00
HighlightedPlane = null ;
pointerMoveTimeout = setTimeout ( removeHighlight ( ) , 300 ) ;
2020-01-03 21:16:40 +01:00
}
}
function processURLParams ( ) {
2020-01-07 23:31:35 +01:00
try {
2020-01-09 14:15:10 +01:00
const search = new URLSearchParams ( window . location . search ) ;
2020-01-14 15:02:04 +01:00
2020-03-27 08:45:56 +01:00
let icao = search . get ( 'icao' ) ;
2020-03-09 18:09:50 +01:00
if ( icao && ( icao . length == 7 || icao . length == 6 ) && icao . toLowerCase ( ) . match ( /[a-f,0-9]{6}/ ) )
2020-01-22 20:49:11 +01:00
icaoParam = icao = icao . toLowerCase ( ) ;
2020-02-28 19:17:19 +01:00
else
icao = null ;
2020-01-22 20:49:11 +01:00
traceDateString = search . get ( 'showTrace' ) ;
2020-01-10 19:57:19 +01:00
const callsign = search . get ( 'callsign' ) ;
2020-03-27 08:45:56 +01:00
let zoom ;
let follow = true ;
2020-01-09 14:15:10 +01:00
if ( search . get ( "zoom" ) ) {
try {
2020-03-23 09:21:02 +01:00
zoom = parseInt ( search . get ( "zoom" ) , 10 ) ;
2020-01-10 19:57:19 +01:00
if ( zoom === 0 )
zoom = 1 ;
2020-01-09 14:15:10 +01:00
} catch ( error ) {
console . log ( "Error parsing zoom:" , error ) ;
}
2020-01-03 21:16:40 +01:00
}
2020-01-10 19:57:19 +01:00
if ( search . get ( "lat" ) && search . get ( "lon" ) ) {
try {
const lat = parseFloat ( search . get ( "lat" ) ) ;
const lon = parseFloat ( search . get ( "lon" ) ) ;
OLMap . getView ( ) . setCenter ( ol . proj . fromLonLat ( [ lon , lat ] ) ) ;
follow = false ;
}
catch ( error ) {
console . log ( "Error parsing lat/lon:" , error ) ;
}
}
2020-01-09 14:15:10 +01:00
if ( icao != null ) {
2020-03-01 00:11:19 +01:00
if ( ! search . has ( 'noIsolation' ) )
toggleIsolation ( "on" , false ) ;
2020-01-22 20:49:11 +01:00
if ( Planes [ icao ] || globeIndex ) {
2020-01-09 14:15:10 +01:00
console . log ( 'Selected ICAO id: ' + icao ) ;
2020-03-27 08:45:56 +01:00
let selectOptions = { follow : follow } ;
2020-01-09 14:15:10 +01:00
if ( zoom ) {
selectOptions . zoom = zoom ;
}
2020-02-29 17:34:46 +01:00
if ( traceDateString != null ) {
toggleShowTrace ( ) ;
if ( ! zoom )
zoom = 5 ;
2020-03-05 12:27:33 +01:00
} else {
selectPlaneByHex ( icao , selectOptions )
2020-02-29 17:34:46 +01:00
}
2020-01-09 14:15:10 +01:00
} else {
console . log ( 'ICAO id not found: ' + icao ) ;
2020-01-07 11:51:55 -08:00
}
2020-01-10 19:57:19 +01:00
} else if ( callsign != null ) {
findPlanes ( callsign , false , true , false , false ) ;
}
if ( zoom ) {
OLMap . getView ( ) . setZoom ( zoom ) ;
2020-01-07 11:51:55 -08:00
}
2020-03-15 20:22:47 +01:00
if ( search . has ( 'mil' ) )
toggleMilitary ( ) ;
2020-03-18 09:26:01 +01:00
if ( search . has ( 'airport' ) ) {
airport = search . get ( 'airport' ) . trim ( ) . toUpperCase ( ) ;
onJump ( ) ;
}
2020-03-23 09:21:02 +01:00
if ( search . has ( 'leg' ) ) {
legSel = parseInt ( search . get ( 'leg' ) , 10 ) ;
if ( isNaN ( legSel ) || legSel < - 1 )
legSel = - 1 ;
else
legSel -- ;
}
2020-01-07 23:31:35 +01:00
} catch ( error ) {
console . log ( error ) ;
}
2020-01-03 21:16:40 +01:00
}
2020-01-04 14:02:10 +01:00
function findPlanes ( query , byIcao , byCallsign , byReg , byType ) {
2020-01-03 21:16:40 +01:00
if ( query == null )
return ;
query = query . toLowerCase ( ) ;
2020-03-27 08:45:56 +01:00
let results = [ ] ;
2020-02-26 18:09:45 +01:00
if ( byReg && regCache [ query . toUpperCase ( ) ] ) {
selectPlaneByHex ( regCache [ query . toUpperCase ( ) ] . toLowerCase ( ) , { follow : true } ) ;
return ;
}
2020-03-27 08:45:56 +01:00
for ( let i in PlanesOrdered ) {
2020-01-03 21:16:40 +01:00
const plane = PlanesOrdered [ i ] ;
if (
( byCallsign && plane . flight != null && plane . flight . toLowerCase ( ) . match ( query ) )
|| ( byIcao && plane . icao . toLowerCase ( ) . match ( query ) )
|| ( byReg && plane . registration != null && plane . registration . toLowerCase ( ) . match ( query ) )
2020-01-04 14:02:10 +01:00
|| ( byType && plane . icaoType != null && plane . icaoType . toLowerCase ( ) . match ( query ) )
2020-01-03 21:16:40 +01:00
) {
if ( plane . seen < 70 || noVanish )
results . push ( plane ) ;
}
}
if ( results . length > 1 ) {
2020-01-19 21:10:27 +01:00
toggleMultiSelect ( "on" ) ;
2020-03-27 08:45:56 +01:00
for ( let i in results ) {
2020-01-03 21:16:40 +01:00
results [ i ] . selected = true ;
results [ i ] . updateTick ( true ) ;
}
} else if ( results . length == 1 ) {
2020-01-07 23:31:35 +01:00
selectPlaneByHex ( results [ 0 ] . icao , { follow : true } ) ;
2020-01-03 21:16:40 +01:00
console . log ( "query selected: " + query ) ;
} else {
console . log ( "No match found for query: " + query ) ;
2020-02-28 19:17:19 +01:00
if ( globeIndex && query . length == 6 && query . toLowerCase ( ) . match ( /[a-f,0-9]{6}/ ) ) {
2020-01-05 22:33:58 +01:00
console . log ( "maybe it's an icao, let's try to fetch the history for it!" ) ;
2020-01-07 23:31:35 +01:00
selectPlaneByHex ( query , { follow : true } )
2020-01-05 22:33:58 +01:00
}
2020-01-03 21:16:40 +01:00
}
}
function trailReaper ( ) {
2020-03-27 08:45:56 +01:00
for ( let i in PlanesOrdered ) {
2020-01-03 21:16:40 +01:00
PlanesOrdered [ i ] . reapTrail ( ) ;
}
}
function globeIndexes ( ) {
2020-01-04 10:14:00 +01:00
if ( mapIsVisible || lastGlobeExtent == null ) {
2020-03-27 08:45:56 +01:00
let mapSize = OLMap . getSize ( ) ;
let size = [ mapSize [ 0 ] * 1.1 , mapSize [ 1 ] * 1.1 ] ;
2020-01-04 10:14:00 +01:00
lastGlobeExtent = OLMap . getView ( ) . calculateExtent ( size ) ;
2020-01-03 21:16:40 +01:00
}
2020-03-27 08:45:56 +01:00
let extent = lastGlobeExtent ;
2020-01-03 21:16:40 +01:00
const bottomLeft = ol . proj . toLonLat ( [ extent [ 0 ] , extent [ 1 ] ] ) ;
const topRight = ol . proj . toLonLat ( [ extent [ 2 ] , extent [ 3 ] ] ) ;
2020-03-27 08:45:56 +01:00
let x1 = bottomLeft [ 0 ] ;
let y1 = bottomLeft [ 1 ] ;
let x2 = topRight [ 0 ] ;
let y2 = topRight [ 1 ] ;
2020-01-03 21:16:40 +01:00
if ( Math . abs ( extent [ 2 ] - extent [ 0 ] ) > 40075016 ) {
// all longtitudes in view, only check latitude
x1 = - 180 ;
x2 = 180 ;
}
if ( y1 < - 90 )
y1 = - 90 ;
if ( y2 > 90 )
y2 = 90 ;
2020-03-27 08:45:56 +01:00
let indexes = [ ] ;
2020-01-03 21:16:40 +01:00
//console.log(x1 + ' ' + x2);
2020-03-27 08:45:56 +01:00
let grid = globeIndexGrid ;
2020-01-03 21:16:40 +01:00
2020-03-27 08:45:56 +01:00
let x3 = ( x1 < x2 ) ? x2 : 300 ;
let count = 0 ;
2020-01-03 21:16:40 +01:00
2020-03-27 08:45:56 +01:00
for ( let lon = x1 ; lon < x3 + grid ; lon += grid ) {
2020-01-03 21:16:40 +01:00
if ( x1 >= x2 && lon > 180 ) {
lon -= 360 ;
x3 = x2 ;
}
if ( lon > x3 )
lon = x3 + 0.01 ;
2020-03-27 08:45:56 +01:00
for ( let lat = y1 ; lat < y2 + grid ; lat += grid ) {
2020-01-03 21:16:40 +01:00
if ( lat > y2 )
lat = y2 + 0.01 ;
if ( count ++ > 2000 ) {
console . log ( "globeIndexes fail, lon: " + lon + ", lat: " + lat ) ;
break ;
}
2020-01-07 20:06:27 +01:00
if ( lat > 90 )
break ;
2020-03-27 08:45:56 +01:00
let index = globe _index ( lat , lon ) ;
2020-01-03 21:16:40 +01:00
//console.log(lat + ' ' + lon + ' ' + index);
if ( ! indexes . includes ( index ) ) {
indexes . push ( index ) ;
}
}
}
return indexes ;
}
function globe _index ( lat , lon ) {
2020-03-27 08:45:56 +01:00
let grid = globeIndexGrid ;
2020-01-03 21:16:40 +01:00
lat = grid * Math . floor ( ( lat + 90 ) / grid ) - 90 ;
lon = grid * Math . floor ( ( lon + 180 ) / grid ) - 180 ;
2020-03-27 08:45:56 +01:00
for ( let i = 0 ; i < globeIndexSpecialTiles . length ; i ++ ) {
let tile = globeIndexSpecialTiles [ i ] ;
2020-01-03 21:16:40 +01:00
if ( lat >= tile . south && lat < tile . north ) {
if ( tile . west < tile . east && lon >= tile . west && lon < tile . east ) {
return i ;
}
if ( tile . west > tile . east && ( lon >= tile . west || lon < tile . east ) ) {
return i ;
}
}
}
2020-03-27 08:45:56 +01:00
let i = Math . floor ( ( lat + 90 ) / grid ) ;
let j = Math . floor ( ( lon + 180 ) / grid ) ;
2020-01-03 21:16:40 +01:00
2020-03-27 08:45:56 +01:00
let lat _multiplier = Math . floor ( 360 / grid + 1 ) ;
2020-01-03 21:16:40 +01:00
return ( i * lat _multiplier + j + 1000 ) ;
}
2020-01-04 10:14:00 +01:00
function inView ( tableplane , currExtent ) {
2020-01-12 15:13:35 +01:00
if ( tableplane . position == null )
return false ;
2020-03-03 13:57:42 +01:00
if ( tableplane . isFiltered ( ) )
return false ;
2020-01-12 15:13:35 +01:00
2020-03-27 08:45:56 +01:00
let inView ;
2020-01-04 10:14:00 +01:00
//console.log((currExtent[2]-currExtent[0])/40075016);
const bottomLeft = ol . proj . toLonLat ( [ currExtent [ 0 ] , currExtent [ 1 ] ] ) ;
const topRight = ol . proj . toLonLat ( [ currExtent [ 2 ] , currExtent [ 3 ] ] ) ;
//console.log([bottomLeft[0], topRight[0]]);
//console.log([bottomLeft[1], topRight[1]]);
//sidebarVisible = $("#sidebar_container").is(":visible");
const pos = tableplane . position ;
const proj = tableplane . position ? ol . proj . fromLonLat ( tableplane . position ) : null ;
if ( pos && currExtent [ 2 ] - currExtent [ 0 ] > 40075016 ) {
// all longtitudes in view, only check latitude
inView = (
pos [ 1 ] > bottomLeft [ 1 ]
&& pos [ 1 ] < topRight [ 1 ]
)
} else if ( pos && bottomLeft [ 0 ] < topRight [ 0 ] ) {
// no wraparound: view not crossing 179 to -180 transition line
inView = (
pos [ 0 ] > bottomLeft [ 0 ]
&& pos [ 0 ] < topRight [ 0 ]
&& pos [ 1 ] > bottomLeft [ 1 ]
&& pos [ 1 ] < topRight [ 1 ]
)
} else if ( pos && bottomLeft [ 0 ] > topRight [ 0 ] ) {
// wraparound: view crossing 179 to -180 transition line
inView = (
( pos [ 0 ] > bottomLeft [ 0 ]
|| pos [ 0 ] < topRight [ 0 ] )
&& pos [ 1 ] > bottomLeft [ 1 ]
&& pos [ 1 ] < topRight [ 1 ]
)
}
return inView ;
}
2020-01-11 22:15:03 +01:00
function updateAddressBar ( ) {
2020-03-27 08:45:56 +01:00
let posString = 'lat=' + CenterLat . toFixed ( 3 ) + '&lon=' + CenterLon . toFixed ( 3 ) + '&zoom=' + ZoomLvl . toFixed ( 1 ) ;
let string ;
2020-01-11 22:15:03 +01:00
if ( true || ! globeIndex )
posString = ""
else if ( SelectedPlane )
posString = "&" + posString ;
else
posString = "?" + posString ;
if ( SelectedPlane )
string = pathName + '?icao=' + SelectedPlane . icao + posString ;
else
string = pathName + posString ;
2020-03-05 12:27:33 +01:00
if ( SelectedPlane && showTrace ) {
string += '&showTrace=' + traceDateString ;
2020-03-23 09:21:02 +01:00
if ( legSel != - 1 )
string += '&leg=' + ( legSel + 1 ) ;
2020-03-05 12:27:33 +01:00
}
2020-02-29 20:22:16 +01:00
shareLink = string ;
2020-03-05 12:27:33 +01:00
if ( uuid )
return ;
if ( icaoFilter )
return ;
2020-03-23 09:34:14 +01:00
if ( SelectedPlane && globeIndex ) {
2020-03-27 08:45:56 +01:00
let icao _link = "<a style=\"color: blue\" target=\"_blank\" href=\"" + shareLink + "\">Share</a>" ;
2020-03-23 09:34:14 +01:00
icao _link = NBSP + NBSP + NBSP + NBSP + NBSP + NBSP + icao _link ;
$ ( '#selected_icao' ) . html ( SelectedPlane . icao . toUpperCase ( ) + icao _link ) ;
}
2020-03-05 12:27:33 +01:00
window . history . replaceState ( "object or string" , "Title" , string ) ;
2020-01-11 22:15:03 +01:00
}
2020-01-18 07:43:07 +01:00
function refreshInt ( ) {
2020-03-27 08:45:56 +01:00
let refresh = RefreshInterval ;
2020-01-18 07:43:07 +01:00
if ( ! globeIndex )
return refresh ;
if ( ! mapIsVisible )
refresh *= 2 ;
2020-03-02 20:53:56 +01:00
if ( onMobile )
refresh *= 1.5 ;
2020-01-18 07:43:07 +01:00
if ( lastRequestFiles >= 4 )
return 1.6 * refresh ;
return refresh * ( 1 + 0.6 / 4 * lastRequestFiles ) ;
}
2020-01-22 20:49:11 +01:00
2020-02-07 07:17:01 +01:00
function toggleLargeMode ( ) {
largeMode ++ ;
2020-02-29 17:34:46 +01:00
if ( ! ( largeMode >= 1 && largeMode <= 4 ) )
2020-02-07 07:17:01 +01:00
largeMode = 1 ;
let root = document . documentElement ;
2020-02-29 17:34:46 +01:00
const base = 1.2 ;
2020-03-04 21:39:56 +01:00
globalScale = Math . pow ( base , largeMode ) / base ;
root . style . setProperty ( "--SCALE" , globalScale ) ;
2020-02-07 07:17:01 +01:00
2020-03-19 09:00:28 +01:00
labelFont = "bold " + ( 12 * globalScale ) + "px/" + ( 14 * globalScale ) + "px Tahoma, Verdana, Helvetica, sans-serif" ;
2020-02-07 07:17:01 +01:00
localStorage [ 'largeMode' ] = largeMode ;
changeZoom ( "init" ) ;
setLineWidth ( ) ;
refreshFeatures ( ) ;
2020-02-07 07:57:41 +01:00
refreshSelected ( ) ;
2020-03-18 11:18:48 +01:00
remakeTrails ( ) ;
2020-02-07 07:17:01 +01:00
}
2020-01-22 20:49:11 +01:00
function toggleShowTrace ( ) {
if ( ! showTrace ) {
showTrace = true ;
2020-03-24 19:45:03 +01:00
toggleFollow ( false ) ;
2020-01-28 01:19:10 +01:00
toggleIsolation ( "on" , null ) ;
2020-01-22 20:49:11 +01:00
shiftTrace ( ) ;
$ ( '#history_collapse' ) [ 0 ] . style . display = "block" ;
$ ( '#show_trace' ) . addClass ( "active" ) ;
} else {
showTrace = false ;
2020-03-22 22:40:17 +01:00
legSel = - 1 ;
$ ( '#leg_sel' ) . text ( 'Legs: All' ) ;
2020-01-28 01:19:10 +01:00
toggleIsolation ( null , "off" ) ;
2020-03-27 08:45:56 +01:00
//let string = pathName + '?icao=' + SelectedPlane.icao;
2020-02-29 20:22:16 +01:00
//window.history.replaceState("object or string", "Title", string);
2020-03-05 12:27:33 +01:00
//shareLink = string;
updateAddressBar ( ) ;
2020-01-22 20:49:11 +01:00
$ ( '#history_collapse' ) [ 0 ] . style . display = "none" ;
$ ( '#show_trace' ) . removeClass ( "active" ) ;
2020-01-28 01:19:10 +01:00
const hex = SelectedPlane . icao ;
SelectedPlane = null ;
2020-01-28 02:18:21 +01:00
showTraceExit = true ;
selectPlaneByHex ( hex , { follow : true , zoom : ZoomLvl , } ) ;
2020-01-22 20:49:11 +01:00
}
}
2020-03-22 22:40:17 +01:00
function legShift ( offset ) {
legSel += offset ;
2020-03-23 09:21:02 +01:00
if ( ! SelectedPlane . fullTrace ) {
$ ( '#leg_sel' ) . text ( 'No Data available' ) ;
return ;
}
2020-03-27 08:45:56 +01:00
let trace = SelectedPlane . fullTrace . trace ;
let legStart = 0 ;
let legEnd = trace . length ;
let count = 0 ;
2020-03-23 09:21:02 +01:00
2020-03-27 08:45:56 +01:00
for ( let i = 1 ; i < trace . length ; i ++ ) {
2020-03-22 22:40:17 +01:00
if ( trace [ i ] [ 6 ] & 2 ) {
count ++ ;
}
}
2020-03-22 22:52:35 +01:00
if ( legSel < - 1 )
2020-03-22 22:40:17 +01:00
legSel = count ;
2020-03-22 22:52:35 +01:00
if ( legSel > count )
legSel = - 1 ;
if ( legSel == - 1 ) {
$ ( '#leg_sel' ) . text ( 'Legs: All' ) ;
SelectedPlane . processTrace ( ) ;
2020-03-23 09:21:02 +01:00
updateAddressBar ( ) ;
2020-03-22 22:52:35 +01:00
return ;
}
2020-03-22 22:40:17 +01:00
count = 0 ;
2020-03-27 08:45:56 +01:00
for ( let i = 1 ; i < trace . length ; i ++ ) {
2020-03-22 22:40:17 +01:00
if ( trace [ i ] [ 6 ] & 2 ) {
if ( count == legSel - 1 )
legStart = i ;
if ( count == legSel )
legEnd = i ; // exclusive
count ++ ;
}
}
$ ( '#leg_sel' ) . text ( 'Leg: ' + ( legSel + 1 ) ) ;
2020-03-24 19:45:03 +01:00
SelectedPlane . processTrace ( { legStart : legStart , legEnd : legEnd } ) ;
2020-03-22 22:40:17 +01:00
2020-03-23 09:21:02 +01:00
updateAddressBar ( ) ;
2020-03-22 22:40:17 +01:00
}
2020-01-22 20:49:11 +01:00
function shiftTrace ( offset ) {
2020-01-22 22:21:42 +01:00
if ( traceDateString && ! traceDate ) {
2020-03-27 08:45:56 +01:00
let numbers = traceDateString . split ( '-' ) ;
2020-01-22 22:21:42 +01:00
traceDate = new Date ( ) ;
traceDate . setUTCFullYear ( numbers [ 0 ] ) ;
traceDate . setUTCMonth ( numbers [ 1 ] - 1 ) ;
traceDate . setUTCDate ( numbers [ 2 ] ) ;
}
2020-01-22 20:49:11 +01:00
if ( ! traceDate || offset == "today" ) {
traceDate = new Date ( ) ;
} else if ( offset ) {
2020-03-27 08:45:56 +01:00
let sinceEpoch = traceDate . getTime ( ) ;
2020-01-22 20:49:11 +01:00
traceDate . setTime ( sinceEpoch + offset * 86400 * 1000 ) ;
}
2020-03-30 10:46:26 +02:00
traceDay = traceDate . getUTCDate ( ) ;
2020-01-22 22:21:42 +01:00
2020-03-30 10:46:26 +02:00
traceDateString = zDateString ( traceDate ) ;
2020-01-22 20:49:11 +01:00
2020-03-30 10:46:26 +02:00
$ ( '#trace_date' ) . text ( 'UTC day:\n' + traceDateString ) ;
2020-01-22 20:49:11 +01:00
2020-03-27 08:45:56 +01:00
let hex = SelectedPlane ? SelectedPlane . icao : icaoParam ;
2020-01-22 20:49:11 +01:00
2020-03-27 08:45:56 +01:00
let selectOptions = { noDeselect : true , zoom : ZoomLvl } ;
2020-01-28 01:19:10 +01:00
selectPlaneByHex ( hex , selectOptions ) ;
2020-03-05 12:27:33 +01:00
updateAddressBar ( ) ;
2020-01-22 20:49:11 +01:00
}
2020-01-22 22:21:42 +01:00
2020-03-30 10:46:26 +02:00
function zDateString ( date ) {
2020-03-27 08:45:56 +01:00
let string = date . getUTCFullYear ( ) + '-'
2020-01-22 22:21:42 +01:00
+ ( date . getUTCMonth ( ) + 1 ) . toString ( ) . padStart ( 2 , '0' ) + '-'
+ date . getUTCDate ( ) . toString ( ) . padStart ( 2 , '0' )
return string ;
}
2020-02-07 07:17:01 +01:00
2020-03-30 10:46:26 +02:00
function lDateString ( date ) {
let string = date . getFullYear ( ) + '-'
+ ( date . getMonth ( ) + 1 ) . toString ( ) . padStart ( 2 , '0' ) + '-'
+ date . getDate ( ) . toString ( ) . padStart ( 2 , '0' )
return string ;
}
2020-02-07 07:17:01 +01:00
function setLineWidth ( ) {
2020-03-03 15:00:18 +01:00
newWidth = lineWidth * Math . pow ( 2 , globalScale ) / 2 * globalScale
2020-02-07 07:17:01 +01:00
estimateStyle = new ol . style . Style ( {
stroke : new ol . style . Stroke ( {
color : '#808080' ,
width : 1.2 * newWidth ,
} )
} ) ;
estimateStyleSlim = new ol . style . Style ( {
stroke : new ol . style . Stroke ( {
color : '#808080' ,
width : 0.4 * newWidth ,
} )
} ) ;
badLine = new ol . style . Style ( {
stroke : new ol . style . Stroke ( {
color : '#FF0000' ,
width : 2 * newWidth ,
} )
} ) ;
badLineMlat = new ol . style . Style ( {
stroke : new ol . style . Stroke ( {
color : '#FFA500' ,
width : 2 * newWidth ,
} )
} ) ;
badDot = new ol . style . Style ( {
image : new ol . style . Circle ( {
radius : 3.5 * newWidth ,
fill : new ol . style . Fill ( {
color : '#FF0000' ,
} )
} ) ,
} ) ;
badDotMlat = new ol . style . Style ( {
image : new ol . style . Circle ( {
radius : 3.5 * newWidth ,
fill : new ol . style . Fill ( {
color : '#FFA500' ,
} )
} ) ,
} ) ;
2020-03-18 11:18:48 +01:00
labelFill = new ol . style . Fill ( { color : 'white' } ) ;
blackFill = new ol . style . Fill ( { color : 'black' } ) ;
labelStroke = new ol . style . Stroke ( { color : 'rgba(0,0,0,0.7' , width : 4 * globalScale } ) ;
2020-02-07 07:17:01 +01:00
}
2020-02-15 00:39:16 +01:00
function geoFindMe ( ) {
function success ( position ) {
2020-03-05 14:09:15 +01:00
if ( ! SiteOverride ) {
SiteLat = CenterLat = DefaultCenterLat = position . coords . latitude ;
SiteLon = CenterLon = DefaultCenterLon = position . coords . longitude ;
}
2020-02-15 00:39:16 +01:00
if ( localStorage [ 'geoFindMeFirstVisit' ] == undefined ) {
OLMap . getView ( ) . setCenter ( ol . proj . fromLonLat ( [ CenterLon , CenterLat ] ) ) ;
localStorage [ 'geoFindMeFirstVisit' ] = 'no' ;
}
2020-03-02 19:02:34 +01:00
initSitePos ( ) ;
2020-02-15 00:39:16 +01:00
}
function error ( ) {
console . log ( "Unable to query location." ) ;
2020-03-02 23:22:01 +01:00
initSitePos ( ) ;
2020-02-15 00:39:16 +01:00
}
if ( ! navigator . geolocation ) {
console . log ( 'Geolocation is not supported by your browser' ) ;
} else {
console . log ( 'Locating…' ) ;
navigator . geolocation . getCurrentPosition ( success , error ) ;
}
}
2020-02-29 17:34:46 +01:00
window . mobilecheck = function ( ) {
2020-03-27 08:45:56 +01:00
let check = false ;
2020-02-29 17:34:46 +01:00
( function ( a ) { if ( /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i . test ( a ) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i . test ( a . substr ( 0 , 4 ) ) ) check = true ; } ) ( navigator . userAgent || navigator . vendor || window . opera ) ;
return check ;
} ;
2020-03-02 19:02:34 +01:00
function initSitePos ( ) {
if ( SitePosInitialized )
return ;
// Set SitePosition
if ( SiteLat != null && SiteLon != null ) {
SitePosition = [ SiteLon , SiteLat ] ;
// Add home marker if requested
createSiteCircleFeatures ( ) ;
} else {
SitePosition = null ;
PlaneRowTemplate . cells [ 9 ] . style . display = 'none' ; // hide distance column
document . getElementById ( "distance" ) . style . display = 'none' ; // hide distance header
}
2020-03-05 14:09:15 +01:00
if ( SitePosition && ! onMobile ) {
sortByDistance ( ) ;
} else {
sortByAltitude ( ) ;
2020-03-05 15:32:46 +01:00
sortByAltitude ( ) ;
2020-03-05 14:09:15 +01:00
}
2020-03-02 19:02:34 +01:00
}
2020-03-03 14:51:34 +01:00
function drawAlt ( ) {
processAircraft ( { hex : 'c0ffee' , } ) ;
2020-03-27 08:45:56 +01:00
let plane = Planes [ 'c0ffee' ] ;
2020-03-03 14:51:34 +01:00
newWidth = 4 ;
2020-03-27 08:45:56 +01:00
for ( let i = 0 ; i <= 50000 ; i += 500 ) {
2020-03-03 14:51:34 +01:00
plane . position = [ i / 10000 , 0 ] ;
plane . altitude = i ;
plane . alt _rounded = calcAltitudeRounded ( plane . altitude ) ;
plane . updateTrack ( now - i , now - i - 5000 , { serverTrack : true } ) ;
}
}
2020-03-18 11:18:48 +01:00
function remakeTrails ( ) {
2020-03-27 08:45:56 +01:00
for ( let i in PlanesOrdered ) {
2020-03-18 11:18:48 +01:00
PlanesOrdered [ i ] . remakeTrail ( ) ;
PlanesOrdered [ i ] . updateTick ( true ) ;
}
}
2020-03-28 13:54:55 +01:00
function createSiteCircleFeatures ( ) {
StaticFeatures . clear ( ) ;
drawUpintheair ( ) ;
// Clear existing circles first
if ( ! SitePosition )
return ;
if ( SiteShow ) {
let 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
} )
} )
} ) ;
let feature = new ol . Feature ( new ol . geom . Point ( ol . proj . fromLonLat ( SitePosition ) ) ) ;
feature . setStyle ( markerStyle ) ;
StaticFeatures . addFeature ( feature ) ;
}
if ( ! SiteCircles )
return ;
let circleStyle = function ( distance ) {
return new ol . style . Style ( {
fill : null ,
stroke : new ol . style . Stroke ( {
color : '#000000' ,
width : 1
} ) ,
text : new ol . style . Text ( {
font : '10px Helvetica Neue, sans-serif' ,
fill : new ol . style . Fill ( { color : '#000' } ) ,
offsetY : - 8 ,
text : format _distance _long ( distance , DisplayUnits , 0 )
} )
} ) ;
} ;
let conversionFactor = 1000.0 ;
if ( DisplayUnits === "nautical" ) {
conversionFactor = 1852.0 ;
} else if ( DisplayUnits === "imperial" ) {
conversionFactor = 1609.0 ;
}
for ( let i = 0 ; i < SiteCirclesDistances . length ; ++ i ) {
let distance = SiteCirclesDistances [ i ] * conversionFactor ;
let circle = make _geodesic _circle ( SitePosition , distance , 180 ) ;
circle . transform ( 'EPSG:4326' , 'EPSG:3857' ) ;
let feature = new ol . Feature ( circle ) ;
feature . setStyle ( circleStyle ( distance ) ) ;
StaticFeatures . addFeature ( feature ) ;
}
}
function drawUpintheair ( ) {
// 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/local/share/tar1090/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
if ( ! globeIndex ) {
let request = $ . ajax ( { url : 'upintheair.json' ,
cache : true ,
dataType : 'json' } ) ;
request . done ( function ( data ) {
for ( let i = 0 ; i < data . rings . length ; ++ i ) {
let geom = null ;
let points = data . rings [ i ] . points ;
let altitude = ( 3.28084 * data . rings [ i ] . alt ) . toFixed ( 0 ) ;
let color = range _outline _color ;
if ( range _outline _colored _by _altitude ) {
let colorArr = altitudeColor ( altitude ) ;
color = 'hsl(' + colorArr [ 0 ] . toFixed ( 0 ) + ',' + colorArr [ 1 ] . toFixed ( 0 ) + '%,' + colorArr [ 2 ] . toFixed ( 0 ) + '%)' ;
}
let ringStyle = new ol . style . Style ( {
fill : null ,
stroke : new ol . style . Stroke ( {
color : color ,
width : range _outline _width ,
} )
} ) ;
if ( points . length > 0 ) {
geom = new ol . geom . LineString ( [ [ points [ 0 ] [ 1 ] , points [ 0 ] [ 0 ] ] ] ) ;
for ( let 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' ) ;
let feature = new ol . Feature ( geom ) ;
feature . setStyle ( ringStyle ) ;
StaticFeatures . addFeature ( feature ) ;
}
}
} ) ;
request . fail ( function ( jqxhr , status , error ) {
// no rings available, do nothing
} ) ;
}
}