/******************************************************************** * 2D Map based on Google Maps ******************************************************************** * Guenter Koellner Embedded Development GmbH * * Am Rain 24 * 85256 Vierkirchen * Geschaeftsfuehrer Guenter Koellner * ********************************************************************/ var Gmap=null; var Planes={}; var Sensors={}; var flightPath={}; var flightPath2={}; var HighlightSensors = null; var SingleAircraft = null; var SingleSensor = null; var Filters = { enable_alt:false, altitude_min:null,altitude_max:null, enable_gnd : true, enable_spd:false, speed_min :null,speed_max:null, enable_dis:false, distance_min:null,distance_max:null, reg_enable:false, reg_filter :null, sensors_enable:false,sensors_filter:null, typ_enable: false,typ_filter: null, fli_enable : false, fli_filter : null, dest_enable : false, dest_filter : null, orig_enable : false, orig_filter : null, squawk_enable : false, squawk_filter : null, prio_enable : false, prio_filter : null, enable_adsb : true, enable_mlat : true, enable_modeac : true, enable_flawa : true, enable_ogn : true, enable_flarm : true, pref_source : 'auto' }; var prio_filter_splitted = []; var GmapInit = { Zoomlevel : 8, CenterLat : 48.353013, CenterLon : 11.785958, LatLonSrc : 'GPS', DistanceUnit : 'km', enable_night : true, mapstyle : 'terrain', refreshtime : 5, tracklength : 300, enable_atrack: true, enable_mtrack: true, show_sensors : true }; var Selected=[]; var PreSelected=undefined; var NauticalMiles_to_Meters=1852; var KiloMeters_to_Meters=1000; //var map_container; var map_box; var map_info_box; var flight_info_box_adsb; var flight_info_box_modeac; var flight_info_box_klick; var hide_info; var hide_info_btn; // https://stackoverflow.com/questions/26246601/wildcard-string-comparison-in-javascript // Short code function matchRuleShort(str, rule) { var escapeRegex = (str) => str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"); return new RegExp("^" + rule.split("*").map(escapeRegex).join(".*") + "$").test(str); } function getTimeString(isLocal) { var hours; var minutes; var date = new Date(); var zone_offset = date.getTimezoneOffset(); if (isLocal) { hours = date.getHours(); minutes = date.getMinutes(); seconds = date.getSeconds(); } else { hours = date.getUTCHours(); minutes = date.getUTCMinutes(); seconds = date.getUTCSeconds(); } if (hours < 10) { hours = '0' + hours; } if (minutes < 10) { minutes = '0' + minutes; } if (seconds < 10) { seconds = '0' + seconds; } var zeit_string = hours + ':' + minutes + ':' + seconds; return zeit_string; } function getStrokeColor(alt, isGround) { const colorMap = [ [ 0,"ffffff"], [ 100,"ffe062"], [ 200,"ffea00"], [ 300,"f0ff00"], [ 400,"ccff00"], [ 600,"42ff00"], [ 800,"1eff00"], [ 1000,"00ff0c"], [ 1200,"00ff36"], [ 1500,"00ff72"], [ 2000,"00ff9c"], [ 2500,"00ffd2"], [ 3000,"00ffe4"], [ 3500,"00eaff"], [ 4000,"00c0ff"], [ 4500,"00a8ff"], [ 5000,"0096ff"], [ 5500,"0078ff"], [ 6000,"0054ff"], [ 6500,"0030ff"], [ 7000,"001eff"], [ 7500,"0000ff"], [ 8500,"1200ff"], [ 8500,"2400ff"], [ 9000,"3600ff"], [ 9500,"4e00ff"], [10000,"6000ff"], [10500,"7800ff"], [11000,"9600ff"], [11500,"ae00ff"], [12000,"d800ff"], [12500,"ff00e4"], [13000,"ff0000"] ]; if( isGround ) { return '8B4513'; } var color; for (var i = 0; i < colorMap.length; i++) { if (alt > colorMap[i][0]) { color = colorMap[i][1]; } else { break; } } return color; } function getIconForPlane(plane) { var Border = plane.src == 'M' ? 'Red' : plane.src == 'F' ? 'LimeGreen' : plane.src == 'L' ? 'DeepPink' : plane.src == 'O' ? 'HotPink' : plane.src == 'AC' ? 'Blue' : 'Black'; var Fill = plane.vrt > 300 ? 'rgb(53, 235, 244)' //'DeepSkyBlue' : plane.vrt < -300 ? 'rgb(185, 92, 0)' //'Sienna' : 'rgb(255,255,0)'; //'Yellow'; var maxalt = 40000; /* Max altitude in the average case */ var invalt = maxalt-plane.alt; if( plane.data_src=='AC' ) { var imageObj = get_modeac_image_object(); } else { var imageObj = get_image_object(plane.cat, plane.typ); } var object_box = Raphael.pathBBox(imageObj.path); var offset_x = (object_box.x + object_box.x2) / 2; var offset_y = (object_box.y + object_box.y2) / 2; var strokew = Selected.includes(plane.hex) ? 2 : 1; var Zindex = Selected.includes(plane.hex) ? 5 : 1; var myZoom = Gmap.getZoom(); var Scale = myZoom>=12 ? imageObj.scale * 1.45 : myZoom==11 ? imageObj.scale * 1.3 : myZoom==10 ? imageObj.scale * 1.15 : myZoom== 9 ? imageObj.scale * 1.0 : myZoom== 8 ? imageObj.scale * 0.85 : myZoom== 7 ? imageObj.scale * 0.7 : myZoom== 6 ? imageObj.scale * 0.55 : imageObj.scale * 0.4 var ac_symbol = { strokeColor : Border, strokeWeight: strokew, path : imageObj.path, scale : Scale, fillColor : Fill, fillOpacity : 0.9, anchor : new google.maps.Point(offset_x, offset_y), rotation : (plane.trk + imageObj.rotcorr) % 360, zIndex : Zindex }; return ac_symbol; } function showInfoBox() { hide_info.style.visibility = 'visible'; map_info_box.style.width = '210px'; hide_info_btn.setAttribute("class", "glyphicon glyphicon-minus"); } function hideInfoBox() { hide_info.style.visibility = 'hidden'; map_info_box.style.width = '1px'; hide_info_btn.setAttribute("class", "glyphicon glyphicon-plus"); } function toggleInfoBox() { if (hide_info.style.visibility == 'hidden') showInfoBox(); else hideInfoBox(); } /******************************************************************************** * clicking on a sensor selects only this one *******************************************************************************/ function selectSensor() { //console.log("selectSensor="+this.sensorid); SingleSensor = this.sensorid; fetchAllData(); } /******************************************************************************** * clicking on a plane selects display of details in sidebar *******************************************************************************/ function selectPlane() { var hex = this.hex; //console.log('selectPlane hex='+hex+' event.altKey='+event.altKey+' event.ctrlKey='+event.ctrlKey); //console.log('Selected='+Selected); if( !Planes[hex] ) { console.log ('not in Planes'); return; } if( Selected && Selected.includes(hex) ) { Selected.splice(Selected.indexOf(hex), 1); } if( Selected===null) Selected = [hex]; else Selected.push(hex); Planes[hex].marker1.setIcon(getIconForPlane(Planes[hex])); labelSetStyle(Planes[hex], Planes[hex].marker1); if (event.shiftKey) { SingleAircraft = (SingleAircraft==hex) ? null : hex; } refreshSelectedInfo(); fetchAllData(); showInfoBox(); } function rightSelectPlane() { var hex = this.hex; //console.log('rightSelectPlane hex='+hex+' event.altKey='+event.altKey+' event.ctrlKey='+event.ctrlKey); if( Selected && !Selected.includes(hex) ) { console.log ('not in Selected'); return; } deselectPlane(hex); } function preselectPlane(hex) { //console.log('preselectPlane src=ADSB id='+hex); this.src='ADSB'; this.id=hex; selectPlane(); } function updateURLPlaneSelection() { var hashObj = {}; location.hash.replace('#', '').split('&').forEach(function(item) { hashObj[item.split('=')[0]] = item.split('=')[1]; }); PreSelected = { 'src':'ADSB', 'hex':hashObj.icao }; } function deselectPlaneAll() { var old = Selected; Selected = []; old.forEach( function(hex) { deselectPlane(hex); }); SingleAircraft = null; SingleSensor = null; fetchAllData(); } function deselectPlane(hex) { Selected.splice(Selected.indexOf(hex), 1); /* Remove the highlight in the previously selected plane. */ if( Planes[hex] ) { Planes[hex].marker1.setIcon(getIconForPlane(Planes[hex])); labelSetStyle(Planes[hex], Planes[hex].marker1); } if( flightPath[hex] ) { flightPath[hex].forEach ( function( X ) { X.setMap(null); }); delete flightPath[hex]; } if( flightPath2[hex] ) { flightPath2[hex].forEach ( function( X ) { X.setMap(null); }); delete flightPath2[hex]; } if( !Selected.length ) { // Selected is empty now, remove sidebar flight_info_box_adsb.style.display='none'; flight_info_box_modeac.style.display='none'; flight_info_box_klick.style.display=''; } } /******************************************************************************* * Info box in div map_info_box - left side, always visible ******************************************************************************/ function fetchMapInfo() { getJSON(rcd_globals.fcgipath+'mapinfo.json', function(mapinfo) { // equal, but not better: $('#TimeStringUTC')[0].innerHTML = ''+getTimeString(false)+''; document.getElementById('TimeStringUTC' ).innerHTML = ''+getTimeString(false)+''; document.getElementById('TimeStringLocal' ).innerHTML = ''+getTimeString(true )+''; document.getElementById('mapinfo_dbsize' ).innerHTML = ''+mapinfo.dbsize +''; document.getElementById('mapinfo_watched' ).innerHTML = ''+mapinfo.watched +''; document.getElementById('mapinfo_num_adsb' ).innerHTML = ''+mapinfo.num_adsb +''; document.getElementById('mapinfo_num_mlat' ).innerHTML = ''+mapinfo.num_mlat +''; document.getElementById('mapinfo_num_flarm').innerHTML = ''+mapinfo.num_flarm+''; }); } function isAltitude ( code ) { if( typeof(code)!='string' || code.length!=4 ) return false; var d = parseInt(code.substring(3,4)); var c = code.substring(2,3); var ret = !( d&1 || c=='0' || c=='5' || c=='7' ) return ret; } function decodeModeACAltitude ( code ) { if( typeof(code)!='string' || code.length!=4 ) return false; var V0 = parseInt(code.substr(0,2),16); var V1 = parseInt(code.substr(2,4),16); var altitude = ((V1 << 9) & 0x400) | // D2 1 + 9 =10 ((V1 << 7) & 0x200) | // D4 2 + 7 = 9 ((V0 << 4) & 0x100) | // A1 4 + 4 = 8 ((V0 << 2) & 0x080) | // A2 5 + 2 = 7 ((V0 ) & 0x040) | // A4 6 + 0 = 6 ((V0 << 5) & 0x020) | // B1 0 + 5 = 5 ((V0 << 3) & 0x010) | // B2 1 + 3 = 4 ((V0 << 1) & 0x008) | // B4 2 + 1 = 3 ((V1 >> 2) & 0x004) | // C1 4 - 2 = 2 ((V1 >> 4) & 0x002) | // C2 5 - 4 = 1 ((V1 >> 6) & 0x001); // C4 6 - 6 = 0 altitude ^= (altitude >> 8); altitude ^= (altitude >> 4); altitude ^= (altitude >> 2); altitude ^= (altitude >> 1); altitude -= ((altitude >> 4) * 6) + (((altitude % 16) / 5) * 2); altitude = (altitude - 13) * 100; ret = Math.round(altitude); return ret; } /******************************************************************************** * Additional info about plane, visible in left sidebar if a plane is selected *******************************************************************************/ function refreshSelectedInfo() { var p = Planes[Selected.slice(-1)[0]]; if ( p ) { if( p.data_src=='AC' ) { flight_info_box_adsb.style.display = 'none'; flight_info_box_modeac.style.display = ''; flight_info_box_klick.style.display = 'none'; document.getElementById('fib_ac_code' ).innerHTML = p.ac + ' '; document.getElementById('fib_ac_decoded').innerHTML = (isAltitude(p.ac) ? decodeModeACAltitude(p.ac)+'ft' : 'Squawk'); p.achistory.sort(function(a, b) { if( a[2]==b[2] ) return 0; return (a[2] < b[2]) ? -1 : 1; }); var nof_ac_codes = p.achistory.length; var counts = 'Count
'; var codes = 'Mode-A/C
'; var alts = 'Alt [ft]
'; var llas = 'Age[s]
'; for ( var i=0; i'; alts += (isAltitude(p.achistory[i][1]) ? decodeModeACAltitude(p.achistory[i][1]) : '') + '
'; counts += p.achistory[i ][0] + '
'; llas += p.achistory[i ][2] + '
'; } document.getElementById('fib_ac_history_c1').innerHTML = counts; document.getElementById('fib_ac_history_c2').innerHTML = codes; document.getElementById('fib_ac_history_c3').innerHTML = alts; document.getElementById('fib_ac_history_c4').innerHTML = llas; document.getElementById('fib_ac_trk').innerHTML = p.trk + ' °'; document.getElementById('fib_ac_spd').innerHTML = (p.spd) ? (p.spd + ' kt' ) : ' '; document.getElementById('fib_ac_alt').innerHTML = (p.alt) ? (p.alt + ' ft') : ' '; document.getElementById('fib_ac_vrt').innerHTML = (p.vrt) ? (p.vrt + ' ft/min') : ' '; document.getElementById('fib_ac_lat').innerHTML = p.lat + ' °'; document.getElementById('fib_ac_lon').innerHTML = p.lon + ' °'; document.getElementById('fib_ac_dis').innerHTML = ' '; document.getElementById('fib_ac_src').innerHTML = 'Mode-A/C'; document.getElementById('fib_ac_lla' ).innerHTML = p.lla + ' sec'; document.getElementById('fib_ac_pest').innerHTML = p.pest + ' '; } else { flight_info_box_adsb.style.display = ''; flight_info_box_modeac.style.display = 'none'; flight_info_box_klick.style.display = 'none'; document.getElementById('fib_reg').innerHTML = ( p.reg && p.reg!= '' && !p.reg.includes('state') ) ? '' + p.reg + '' : p.reg + ' '; document.getElementById('fib_log').innerHTML = ' Network Info' document.getElementById('fib_hex').innerHTML = p.hex + ' '; document.getElementById('fib_typ').innerHTML = (p.typ) ? (p.typ + ' ') : ' '; document.getElementById('fib_cat').innerHTML = (p.cat) ? (p.cat + ' ') : ' '; document.getElementById('fib_fli').innerHTML = (p.fli) ? (p.fli + ' ') : ' '; document.getElementById('fib_squ').innerHTML = (p.squ) ? (p.squ + ' ') : ' '; /* Flight Details Row 2 */ document.getElementById('fib_org').innerHTML = (!p.org) ? 'n/a' : '' + p.org + ' 
' + resolveAirportById(p.org).label + ' '; document.getElementById('fib_dst').innerHTML = (!p.dst) ? 'n/a' : '' + p.dst + ' 
' + resolveAirportById(p.dst).label + ' '; document.getElementById('fib_trk').innerHTML = p.trk + ' °'; document.getElementById('fib_spd').innerHTML = (p.spd) ? (p.spd + ' kt' ) : ' '; document.getElementById('fib_alt').innerHTML = (p.alt) ? (p.alt + ' ft') : ' '; document.getElementById('fib_vrt').innerHTML = (p.vrt) ? (p.vrt + ' ft/min') : ' '; document.getElementById('fib_lat').innerHTML = p.lat + ' °'; document.getElementById('fib_lon').innerHTML = p.lon + ' °'; document.getElementById('fib_dis').innerHTML = p.dis.toFixed(1) + ' ' + DistanceUnit; document.getElementById('fib_src').innerHTML = (p.src=='A') ? 'ADS-B' : (p.src=='M') ? 'MLAT' : (p.src=='F') ? 'FlAwa' : (p.src=='O') ? 'OGN' : (p.src=='L') ? 'Flarm' : ' '; document.getElementById('fib_lla' ).innerHTML = p.lla + ' sec'; document.getElementById('fib_pest').innerHTML = p.pest + ' '; document.getElementById('fib_nof_clnt').innerHTML = 'Current Sensors: ' + p.nocl; if( rcd_globals.productname=='MLAT_SERVER' ) { var i = 0; var clnt = ['','','','','']; p.clnt.sort().forEach(function(C){ clnt[i%5] += C + '
'; i++; }); document.getElementById('fib_clnt0').innerHTML = clnt[0] + ' '; document.getElementById('fib_clnt1').innerHTML = clnt[1] + ' '; document.getElementById('fib_clnt2').innerHTML = clnt[2] + ' '; document.getElementById('fib_clnt3').innerHTML = clnt[3] + ' '; document.getElementById('fib_clnt4').innerHTML = clnt[4] + ' '; } } } else { flight_info_box_adsb.style.display = 'none'; flight_info_box_modeac.style.display = 'none'; flight_info_box_klick.style.display = ''; } } /******************************************************************************** * Information that is displayed on map together with plane *******************************************************************************/ function labelDataID(plane) { if( plane.fli!==null && plane.fli!=="") return plane.fli; if( plane.reg!==null && plane.reg!=="" && plane.reg!=="(state)" ) return plane.reg; return plane.hex; } function labelDataVerticalRate(plane) { // Vertical Rate if (!plane.vrt ) return ' ' else if (plane.vrt < -300) return Math.round(plane.vrt/100); else if (plane.vrt > 300) return "+" + Math.round(plane.vrt/100); return '\u00B10'; // unicode plusminus + '0' } function labelDataAltitude(plane) { if (plane.gda == "G") { return "GND"; } // Altitude var s = Math.round(plane.alt/100); if (s < 0) { s = '-0' + -1*s; } else { if (s < 10) s = "00" + s; else if (s < 100) s = "0" + s; } return s; } function labelDataSpeed(plane) { return Math.round(plane.spd/10); } function permanentLabelTable(plane) { var ret; if( plane.data_src=='AC' ) { ret = '' + '' + '' + '' + '
' + plane.ac + ' ' + plane.uid + '
' + labelDataAltitude(plane) + '' + labelDataVerticalRate(plane) + '' + labelDataSpeed(plane) + '
' + plane.tsz + '' + plane.lla + '' + plane.pest + '
'; } else { ret = '' + '' + '' + //'' + '
' + labelDataID(plane) + ' ' + (plane.reg==null?'':plane.reg) + '
' + labelDataAltitude(plane) + '' + labelDataVerticalRate(plane) + '' + labelDataSpeed(plane) + '
' + plane.clnt.length + '' + plane.squ + '' + plane.pest + '
'; } return ret; } function getPopupForPlane(plane) { if( plane.data_src=='AC' ) { var ret = ''; plane.achistory.forEach ( function (E) { ret += E[0] + ':' + E[1] + ' '; }); return ret; } if( plane.fli.length == 0 ) { return plane.hex; } else { var title = plane.hex + " " + plane.typ; if ( plane.org!=null && plane.org.length ) { title += '\n' + plane.org; } if ( plane.dst!=null && plane.dst.length ) { title += '->' + plane.dst; } if ( plane.squ!=null && plane.squ.length ) { title += '\nSQ:' + plane.squ; } return title; } } // function labelSetAnchor(plane, marker, offset) { // if (plane.trk > 315) { marker.set("labelAnchor", new google.maps.Point( 66, -1 + offset)); return; } // if (plane.trk < 45) { marker.set("labelAnchor", new google.maps.Point( 66, -1 + offset)); return; } // if (plane.trk < 135) { marker.set("labelAnchor", new google.maps.Point( 37, -1 + 28 + offset)); return; } // if (plane.trk < 225) { marker.set("labelAnchor", new google.maps.Point(-17, -1 + offset)); return; } // marker.set("labelAnchor", new google.maps.Point(37, -1 - 28 + offset)); // } function isInPrio(item) { var ret=false; prio_filter_splitted.forEach(function(mask) { if ( matchRuleShort(item, mask) ) { ret=true; return; }; }); return ret; } // https://www.quackit.com/html/codes/color/html_color_chart.cfm function labelSetStyle(plane, marker) { if ( Selected.includes(plane.hex) ) {marker.set('labelStyle', { opacity: 0.5, 'background-color': 'greenyellow'}); return; } if ( plane.squ && plane.squ=="7700") {marker.set('labelStyle', { opacity: 0.5, 'background-color': 'red'}); return; } if ( plane.spi ) {marker.set('labelStyle', { opacity: 0.5, 'background-color': 'aqua', border: '2px solid darkblue'}); return; } if ( plane.alr && plane.alr!=0 ) {marker.set('labelStyle', { opacity: 0.5, 'background-color': 'gold', border: '2px solid black' }); return; } if ( Filters.prio_enable===true ) { if ( isInPrio(plane.hex) || isInPrio(plane.reg) ) {marker.set('labelStyle', { opacity: 0.8, 'background-color': 'yellow'}); return; } } marker.set('labelStyle', { opacity: 0.5, 'background-color': 'gold'}); } // Shapes: https://developers.google.com/maps/documentation/javascript/shapes // https://developers-dot-devsite-v2-prod.appspot.com/maps/documentation/javascript/examples/overlay-symbol-dashed?hl=vi function drawColoredTrack (data) { data.forEach( function(path) { var hex = path.src=='AC' ? 'AC_'+path.uid : path.icao; if( !Selected.includes(hex) ) return; // no longer selected if( flightPath[hex] ) flightPath[hex].forEach( function ( X ) { X.setMap(null); }); flightPath[hex] = []; if( path.fpth.length<2 ) return; var trackpoints = []; var veryFirst = true; var strokeColor_tm1; path.fpth.forEach( function (trackpoint, index) { var strokeColor = getStrokeColor(trackpoint.alt, trackpoint.gnd ? trackpoint.gnd : false ); // AC has no gnd field var g_tp = { 'lat':trackpoint.lat, 'lng':trackpoint.lon }; trackpoints.push( g_tp ); if( veryFirst ) { strokeColor_tm1=strokeColor; veryFirst=false; } else { if( strokeColor!=strokeColor_tm1 || index==path.fpth.length-1 ) { var pathSegment = new google.maps.Polyline({ path: trackpoints, geodesic: true, strokeColor: '#'+strokeColor_tm1, strokeOpacity: 1.0, strokeWeight: 2, zIndex : 1, map: Gmap }); flightPath[hex].push(pathSegment); trackpoints = [ g_tp ]; strokeColor_tm1 = strokeColor; } } }); }); } function drawDashedTrack (data) { // Define a symbol using SVG path notation, with an opacity of 1. const lineSymbol = { path: 'M 0,-1 0,1', strokeOpacity: 1, scale: 2 }; data.forEach( function(path) { var hex = path.src=='AC' ? 'AC_'+path.uid : path.icao; if( !Selected.includes(hex) ) return; // no longer selected if( flightPath2[hex] ) flightPath2[hex].forEach( function ( X ) { X.setMap(null); }); flightPath2[hex] = []; if( path.fpth.length<2 ) return; var trackpoints = []; var veryFirst = true; path.fpth.forEach( function (trackpoint) { g_tp = { 'lat':trackpoint.lat, 'lng':trackpoint.lon }; trackpoints.push( g_tp ); }); var pathSegment = new google.maps.Polyline({ path: trackpoints, geodesic: true, strokeColor: 'black', strokeOpacity: 0, icons: [{ icon: lineSymbol, offset: '0', repeat: '10px' }], zIndex : 10, map: Gmap }); flightPath2[hex].push(pathSegment); }); } function fetchTrackData() { if( Selected.length==0 ) { return; } var vps = Gmap.getBounds().getSouthWest().lat().toFixed(5); var vpw = Gmap.getBounds().getSouthWest().lng().toFixed(5); var vpn = Gmap.getBounds().getNorthEast().lat().toFixed(5); var vpe = Gmap.getBounds().getNorthEast().lng().toFixed(5); var tracks_adsb = []; var tracks_mlat = []; var tracks_flarm = []; var tracks_ogn = []; var tracks_flawa = []; var tracks_modeac = []; // sort selected targets by source because flightpath must be called separately Selected.forEach( function (hex) { if( Planes[hex].data_src=='AC') { tracks_modeac.push(Planes[hex].uid); } else if( Planes[hex].src=='A' ) { tracks_adsb.push(hex); } else if( Planes[hex].src=='M' ) { tracks_mlat.push(hex); } else if( Planes[hex].src=='L' ) { tracks_flarm.push(hex); } else if( Planes[hex].src=='F' ) { tracks_flawa.push(hex); } else if( Planes[hex].src=='O' ) { tracks_ogn.push(hex); } else console.log('unknown data_src=' + Planes[hex].data_src + ' src=' + Planes[hex].src + ' for flightpath.json' ) }); // colored tracks [tracks_adsb, tracks_mlat, tracks_flarm, tracks_ogn, tracks_flawa].forEach( function (tracks) { if( tracks.length ) { var link = rcd_globals.fcgipath + 'flightpath.json?icao=' + tracks.join(',') + '&srcpref=' + Planes[tracks[0]].src + ( GmapInit.tracklength ? ( '&lookback=' + GmapInit.tracklength ) : '' ) + '&pixelxy=' + window.innerWidth + ',' + window.innerHeight + '&vpn=' + vpn + '&vpe=' + vpe + '&vps=' + vps + '&vpw=' + vpw; getJSON(link, drawColoredTrack); } }); // dashed tracks tracks_dashed = []; if( GmapInit.enable_atrack ) { tracks_dashed.push({'src':'A', 'points':tracks_mlat}); } if( GmapInit.enable_mtrack ) { tracks_dashed.push({'src':'M', 'points':tracks_adsb}); } tracks_dashed.forEach( function (tracks) { if( tracks['points'].length ) { var link = rcd_globals.fcgipath + 'flightpath.json?icao=' + tracks['points'].join(',') + '&srcpref=' + tracks['src'] + ( GmapInit.tracklength ? ( '&lookback=' + GmapInit.tracklength ) : '' ) + '&pixelxy=' + window.innerWidth + ',' + window.innerHeight + '&vpn=' + vpn + '&vpe=' + vpe + '&vps=' + vps + '&vpw=' + vpw; getJSON(link, drawDashedTrack); } }); // Mode-A/C tracks if( tracks_modeac.length ) { var link = rcd_globals.fcgipath + 'modeacpath.json?uid=' + tracks_modeac.join(',') + ( GmapInit.tracklength ? ( '&lookback=' + GmapInit.tracklength ) : '' ) + '&pixelxy=' + window.innerWidth + ',' + window.innerHeight + '&vpn=' + vpn + '&vpe=' + vpe + '&vps=' + vps + '&vpw=' + vpw; getJSON(link, drawColoredTrack); } } // Wrappers because callback cannot have parameter function updatePlanes (data) { document.getElementById('num_planesOnMap').innerHTML = ''+data.length+''; updateObjects ( data, 'ADSB' ); } function updateModeAC (data) { updateObjects ( data, 'AC' ); } function updateSensors (data) { updateObjects ( data, 'Sensor' ); } /* Functionality of this display assumes that all 'hex' codes are unique by the provider */ /* Mode-S, ADS-B and MLAT sources use 0x00000000 - 0x00ffffff hex id */ /* Mode-A/C sources use 0xACxxxxxx identifiers */ /* Sensors should use 0x5Exxxxxx identifiers */ /* and 'hex' is only traditional... */ function updateObjects (data, data_src) { var stillhere = []; data.forEach ( function (plane) { var marker1; if( data_src=='AC' ) plane.hex = 'AC_'+plane.uid; stillhere.push(plane.hex); plane.data_src = data_src; plane.fli = $.trim(plane.fli); /* update */ if( Planes[plane.hex] ) { marker1 = Planes[plane.hex].marker1; var newpos = new google.maps.LatLng(plane.lat, plane.lon); /* update flight number */ marker1.setIcon ( getIconForPlane(plane)) ; marker1.setPosition( newpos ); marker1.set ('labelContent', permanentLabelTable(plane) ); labelSetStyle ( plane, marker1 ); marker1.label.draw(); if( Selected.includes(plane.hex) ) refreshSelectedInfo(); } else { /* new flight */ marker1 = new MarkerWithLabel({ position : new google.maps.LatLng(plane.lat, plane.lon), draggable : false, raiseOnDrag : false, zIndex : 70000, map : Gmap, labelContent : permanentLabelTable(plane), labelAnchor : plane.data_src=='AC' ? new google.maps.Point(0, 32) : new google.maps.Point(0, 0), labelClass : 'labels', labelStyle : {}, labelZIndex : 60000, icon : getIconForPlane(plane) }); labelSetStyle(plane, marker1); /* Trap clicks for this marker. */ marker1.addListener('click', selectPlane); marker1.addListener('rightclick', rightSelectPlane); } marker1.hex = plane.hex; /* remember for click */ plane.marker1 = marker1; Planes[plane.hex] = plane; /* Popup window when mouse over aircraft icon */ marker1.setTitle( getPopupForPlane(plane) ) }); /* Remove no longer given planes. */ //console.log("Planes["+T+"]\n"+Planes[T]); //console.log("stillhere\n"+stillhere); for( var hex in Planes ) { if( !stillhere.includes(hex) ) { if( Planes[hex].data_src != data_src ) continue; if( Selected.includes(hex) ) { deselectPlane(hex); } Planes[hex].marker1.setMap(null); delete Planes[hex]; } } if( PreSelected ) { preselectPlane(PreSelected); PreSelected=undefined; } } function fetchAdsbData() { var bounds = Gmap.getBounds(); if (bounds == null) { return; } // getBounds might not be defined even if map already is var link = rcd_globals.fcgipath + 'aircraftlist.json?knownposonly=1'; var vps = Gmap.getBounds().getSouthWest().lat().toFixed(5); var vpw = Gmap.getBounds().getSouthWest().lng().toFixed(5); var vpn = Gmap.getBounds().getNorthEast().lat().toFixed(5); var vpe = Gmap.getBounds().getNorthEast().lng().toFixed(5); link += '&vpn=' + vpn + '&vpe=' + vpe + '&vps=' + vps + '&vpw=' + vpw; if( Filters.enable_alt ) { if( Filters.altitude_min ) { link += '&altitude.from=' + Filters.altitude_min; } if( Filters.altitude_max ) { link += '&altitude.to=' + Filters.altitude_max; } } if (Filters.enable_gnd===false) { link+="&gndexcl=1"; } if (Filters.enable_spd) { if( Filters.speed_min ) { link += "&speed.from=" + Filters.speed_min; } if( Filters.speed_max ) { link += "&speed.to=" + Filters.speed_max; } } if (Filters.enable_dis) { if( Filters.distance_min ) { link += "&distance.from=" + Filters.distance_min; } if( Filters.distance_max ) { link += "&distance.to=" + Filters.distance_max; } } if( SingleAircraft ) { link+= "&icao=" + SingleAircraft; } else if( Filters.reg_enable && Filters.reg_filter ) { link += "&icao=" + Filters.reg_filter; } if( Filters.typ_enable && Filters.typ_filter ) { link += "&type=" + Filters.typ_filter; } if( Filters.fli_enable && Filters.fli_filter ) { link += "&flight=" + Filters.fli_filter; } if( Filters.dest_enable && Filters.dest_filter ) { link += "&dest=" + Filters.dest_filter; } if( Filters.orig_enable && Filters.orig_filter ) { link += "&orig=" + Filters.orig_filter; } if( Filters.squawk_enable && Filters.squawk_filter) { link += "&squawk=" + Filters.squawk_filter; } if( Filters.prio_enable && Filters.prio_filter ) { link += "&prio=" + Filters.prio_filter; } // C++ backend assumes that default is all on, so we only need to send the 'off' condition if (!Filters.enable_adsb) { link += "&srcexcl=a"; } if (!Filters.enable_mlat) { link += "&srcexcl=m"; } if (!Filters.enable_flawa) { link += "&srcexcl=f"; } if (!Filters.enable_ogn) { link += "&srcexcl=o"; } if (!Filters.enable_flarm) { link += "&srcexcl=l"; } if (typeof Filters.pref_source !== "undefined" && Filters.pref_source !== null && Filters.perfsource !== "") { srcpref = "&srcpref=" + Filters.pref_source; } if( SingleSensor ) { link += "&sensor.only=" + SingleSensor; } else if( Filters.sensors_enable && Filters.sensors_filter ) { link += "&sensor.only=" + Filters.sensors_filter; } getJSON( link, updatePlanes ); } function fetchModeACData() { if( !Filters.enable_modeac ) { updateModeAC([]); // empty list clears the list return; } if (Gmap.getBounds() == null) { return; } // getBounds might not be defined even if map already is var link = rcd_globals.fcgipath + 'modeactargets.json'; getJSON( link, updateModeAC ); } function FilterControl(controlDiv) { var controlUI = document.createElement('div'); controlUI.className = 'filter-button'; controlUI.title = 'Click to modify the filters'; controlDiv.appendChild(controlUI); var controlText = document.createElement('div'); controlText.className = 'filter-icon'; controlUI.appendChild(controlText); controlUI.addEventListener('click', function() { if (document.getElementById('filter-dialog') === null) { var filterDialog = document.createElement('div'); filterDialog.id = 'filter-dialog'; filterDialog.innerHTML = '
' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + ( ( rcd_globals.productname=='MLAT_SERVER' ) ? '' + '' : '' ) + '' + '' + ( ( rcd_globals.productname=='MLAT_SERVER' ) ? '' : '' ) + '' + ( ( rcd_globals.productname=='MLAT_SERVER' ) ? '' : '' ) + '' + '' + '
Prio & Highlight
Ground Traffic
Altitude (ft)
Speed (kt)
Distance (km)
Destination
Origin
Squawk
Flight
Aircraft Type
Fleet Watch
Sensors
Sources ADSB MLAT A/C FLARM
Sources ADSB MLAT Flarm FlaWa OGN
MLAT track with ADS-B ADS-B track with MLAT
Show Sensors Show day and night
Show day and night
Preferred Source
Track Length Map Style
Refresh Time
' + '
'; controlDiv.appendChild(filterDialog); $("#filter-apply").click(function() { handleChange(); $("#filter-dialog").hide(); }); $('#mapstyle, #enable_night, #refreshtime, #tracklength, #enable_atrack, #enable_mtrack, #show_sensors').change(mapstyleChange); $('#filter-alt-min, #filter-alt-max, #enable_alt, #enable_gnd, ' + '#filter-spd-min, #filter-spd-max, #enable_spd, ' + '#filter-dis-min, #filter-dis-max, #enable_dis, ' + '#reg_filter, #reg_enable, ' + '#sensors_filter, #sensors_enable, ' + '#typ_filter, #typ_enable, ' + '#fli_filter, #fli_enable, ' + '#dest_filter, #dest_enable, ' + '#orig_filter, #orig_enable, ' + '#squawk_filter, #squawk_enable, ' + '#prio_filter, #prio_enable, ' + '#pref_source, ' + '#enable_adsb, #enable_mlat, #enable_modeac, #enable_flawa, #enable_ogn, #enable_flarm' ).change(handleChange); $("#filter-dialog .close").click(function() { $("#filter-dialog").hide(); }); $("#filter-dialog .clear").click(clear_filters); $("#filter-alt-min").val(Filters.altitude_min); $("#filter-alt-max").val(Filters.altitude_max); $("#enable_alt" ).prop("checked",Filters.enable_alt ); $("#enable_gnd" ).prop("checked",Filters.enable_gnd ); $("#filter-spd-min").val(Filters.speed_min); $("#filter-spd-max").val(Filters.speed_max); $("#enable_spd" ).prop("checked",Filters.enable_spd ); $("#filter-dis-min").val(Filters.distance_min); $("#filter-dis-max").val(Filters.distance_max); $("#enable_dis" ).prop("checked",Filters.enable_dis ); $("#reg_filter" ).val(Filters.reg_filter); $("#reg_enable" ).prop("checked",Filters.reg_enable ); $("#sensors_filter").val(Filters.sensors_filter); $("#sensors_enable").prop("checked",Filters.sensors_enable); $("#typ_enable" ).prop("checked",Filters.typ_enable ); $("#typ_filter" ).val(Filters.typ_filter); $("#fli_enable" ).prop("checked",Filters.fli_enable ); $("#fli_filter" ).val(Filters.fli_filter); $("#dest_enable" ).prop("checked",Filters.dest_enable ); $("#dest_filter" ).val(Filters.dest_filter); $("#orig_enable" ).prop("checked",Filters.orig_enable ); $("#orig_filter" ).val(Filters.orig_filter); $("#squawk_enable" ).prop("checked",Filters.squawk_enable ); $("#squawk_filter" ).val(Filters.squawk_filter); $("#prio_enable" ).prop("checked",Filters.prio_enable ); $("#prio_filter" ).val(Filters.prio_filter); $("#enable_adsb" ).prop("checked",Filters.enable_adsb ); $("#enable_mlat" ).prop("checked",Filters.enable_mlat ); $("#enable_modeac" ).prop("checked",Filters.enable_modeac ); $("#enable_flawa" ).prop("checked",Filters.enable_flawa ); $("#enable_ogn" ).prop("checked",Filters.enable_ogn ); $("#enable_flarm" ).prop("checked",Filters.enable_flarm ); $("#pref_source" ).val(Filters.pref_source); $("#enable_night" ).prop("checked",GmapInit.enable_night ); $("#enable_atrack" ).prop("checked",GmapInit.enable_atrack ); $("#enable_mtrack" ).prop("checked",GmapInit.enable_mtrack ); $("#tracklength" ).val(GmapInit.tracklength); $('#mapstyle' ).val(GmapInit.mapstyle); $('#refreshtime' ).val(GmapInit.refreshtime); $('#show_sensors' ).val(GmapInit.show_sensors); function handleChange() { Filters.altitude_min = $("#filter-alt-min").val() || undefined; Filters.altitude_max = $("#filter-alt-max").val() || undefined; Filters.enable_alt = $("#enable_alt" ).prop( "checked" ); Filters.enable_gnd = $("#enable_gnd" ).prop( "checked" ); Filters.speed_min = $("#filter-spd-min").val() || undefined; Filters.speed_max = $("#filter-spd-max").val() || undefined; Filters.enable_spd = $("#enable_spd" ).prop( "checked" ); Filters.distance_min = $("#filter-dis-min").val() || undefined; Filters.distance_max = $("#filter-dis-max").val() || undefined; Filters.enable_dis = $("#enable_dis" ).prop( "checked" ); Filters.reg_enable = $("#reg_enable" ).prop( "checked" ); Filters.reg_filter = $("#reg_filter" ).val() || undefined; Filters.sensors_enable= $("#sensors_enable").prop( "checked" ); Filters.sensors_filter= $("#sensors_filter").val() || undefined; Filters.typ_enable = $("#typ_enable" ).prop( "checked" ); Filters.typ_filter = $("#typ_filter" ).val() || undefined; Filters.fli_enable = $("#fli_enable" ).prop( "checked" ); Filters.fli_filter = $("#fli_filter" ).val() || undefined; Filters.dest_enable = $("#dest_enable" ).prop( "checked" ); Filters.dest_filter = $("#dest_filter" ).val() || undefined; Filters.orig_enable = $("#orig_enable" ).prop( "checked" ); Filters.orig_filter = $("#orig_filter" ).val() || undefined; Filters.squawk_enable = $("#squawk_enable" ).prop( "checked" ); Filters.squawk_filter = $("#squawk_filter" ).val() || undefined; Filters.prio_enable = $("#prio_enable" ).prop( "checked" ); Filters.prio_filter = $("#prio_filter" ).val() || undefined; Filters.enable_adsb = $("#enable_adsb" ).prop( "checked" ); Filters.enable_mlat = $("#enable_mlat" ).prop( "checked" ); Filters.enable_modeac = $("#enable_modeac" ).prop( "checked" ); Filters.enable_flawa = $("#enable_flawa" ).prop( "checked" ); Filters.enable_ogn = $("#enable_ogn" ).prop( "checked" ); Filters.enable_flarm = $("#enable_flarm" ).prop( "checked" ); Filters.pref_source = $("#pref_source" ).val() || undefined; localStorage.setItem(rcd_globals.ls_filters_id, JSON.stringify(Filters)); if( Filters.prio_filter ) { prio_filter_splitted = Filters.prio_filter.toUpperCase().split(","); } else { prio_filter_splitted = []; } fetchAdsbData(); } function mapstyleChange() { GmapInit.mapstyle = $('#mapstyle' ).val() || undefined; GmapInit.enable_night = $('#enable_night' ).prop('checked'); GmapInit.refreshtime = $("#refreshtime" ).val() || undefined; GmapInit.tracklength = $("#tracklength" ).val() || undefined; GmapInit.enable_atrack = $("#enable_atrack").prop( "checked" ); GmapInit.enable_mtrack = $("#enable_mtrack").prop( "checked" ); GmapInit.show_sensors = $("#show_sensors" ).prop( "checked" ); Gmap.setMapTypeId(GmapInit.mapstyle); localStorage.setItem(rcd_globals.ls_gmapinit_id, JSON.stringify(GmapInit)); if( GmapInit.enable_night ) nite.show(); else nite.hide(); fetchAllData(); for( var hex in flightPath2 ) { flightPath2[hex].forEach( function ( X ) { X.setMap(null); }); delete flightPath2[hex]; } } function clear_filters() { $('#filter-alt-min, #filter-alt-max, ' + '#filter-spd-min, #filter-spd-max, ' + '#filter-dis-min, #filter-dis-max, ' + '#reg_filter, #sensors_filter, #typ_filter, #fli_filter, #dest_filter, ' + "#orig_filter, #squawk_filter, #prio_filter, #pref_source").val(null); $('#enable_alt, #enable_dis, #enable_spd, #reg_enable, #sensors_enable, #typ_enable, #fli_enable, #dest_enable, ' + '#orig_enable, #squawk_enable, #prio_enable, #enable_atrack, #enable_mtrack, #enable_night').prop( 'checked', false ); $('#enable_adsb, #enable_gnd, #enable_mlat, #enable_modeac, #enable_flawa, #enable_ogn, #enable_flarm').prop( 'checked', true ); prio_filter_splitted = []; handleChange(); }; } else { $("#filter-dialog").show(); } }); } /******************************************************************************** * Information that is displayed as permanent label * Color Table: * https://www.quackit.com/html/codes/color/html_color_chart.cfm *******************************************************************************/ function getIconForSensor(sensor) { var Scale = 1; var Border = 'Black'; var Strokew = 1; if( SingleSensor==sensor.id ) { Border = 'Red'; Strokew = 3; Scale = 1.5; } else if( Selected.length>0 ) { var p = Planes[Selected.slice(-1)[0]].hex; if( Planes[p].clnt.includes(sensor.id) ) { Border = 'Blue'; Strokew = 3; Scale = 1.5; } } var Fill = ( sensor.active && sensor.iBy>30 ) ? 'Lime' : ( sensor.active ) ? 'Orange' : 'Red'; //http://tutorials.jenkov.com/svg/path-element.html //var imageObj = {path:"M0,0 6,12 12,0z"}; // Triangle //var imageObj = {path:"m25,1 6,17h18l-14,11 5,17-15-10-15,10 5-17-14-11h18z"}; // Star var imageObj = {path:'M 13.6,18.8 L 6.3,18.8 1.1,13.6 1.1,6.3 6.3,1.1 13.6,1.1 18.8,6.3 18.8,13.6 z'}; // Octagon var object_box = Raphael.pathBBox(imageObj.path); var offset_x = (object_box.x + object_box.x2) / 2; var offset_y = (object_box.y + object_box.y2) / 2; var sensor_symbol = { strokeColor : Border, strokeWeight : Strokew, path : imageObj.path, scale : Scale, fillColor : Fill, fillOpacity : 0.9, anchor : new google.maps.Point(offset_x, offset_y), rotation : 180, zIndex : 10 }; return sensor_symbol; } function labelSensorSetStyle(sensor, marker) { marker.set('labelStyle', { opacity: 0.5, 'background-color': 'LightGreen'}); } function fetchAllData() { fetchAdsbData(); fetchTrackData(); fetchMapInfo(); if( rcd_globals.productname=='MLAT_SERVER' ) { fetchSensors(GmapInit.show_sensors); fetchModeACData(); } } /******************************************************************************** * Main *******************************************************************************/ $(document).ready(function() { getJSON( rcd_globals.fcgipath+"param.json?param=ownlat,ownlon,distanceunit", function(params) { CenterLat = params.ownlat; CenterLon = params.ownlon; DistanceUnit = params.distanceunit ? "km" : "nm"; initialize(); }); }); function initialize() { updateURLPlaneSelection(); /* There is no info_box_table without refreshGeneralInfo() */ loadAirports(); map_box = document.getElementById('map_box'); map_info_box = document.getElementById('map_info_box'); flight_info_box_adsb = document.getElementById('flight_info_box_adsb'); flight_info_box_modeac = document.getElementById('flight_info_box_modeac'); flight_info_box_klick = document.getElementById('flight_info_box_klick'); info_box_table = document.getElementById('info_box_table'); hide_info = document.getElementById('hide_info'); hide_info_btn = document.getElementById('hide_info_btn'); document.getElementById("btn_hide_info_box").onclick = toggleInfoBox; showInfoBox(); // hideInfoBox(); var LS; LS = localStorage.getItem(rcd_globals.ls_filters_id); if( LS ) $.extend(Filters, JSON.parse(LS)); if( Filters.prio_filter ) prio_filter_splitted = Filters.prio_filter.toUpperCase().split(','); LS = localStorage.getItem(rcd_globals.ls_gmapinit_id); if( LS ) $.extend(GmapInit, JSON.parse(LS)); Gmap = new google.maps.Map( map_box, { disableDefaultUI : true, center : new google.maps.LatLng(GmapInit.CenterLat, GmapInit.CenterLon), zoom : GmapInit.Zoomlevel, mapTypeId : GmapInit.mapstyle, zoomControl : true, zoomControlOptions : { position : google.maps.ControlPosition.RIGHT_BOTTOM }, scaleControl : false, streetViewControl : false } ); var currCenter = new google.maps.LatLng(GmapInit.CenterLat, GmapInit.CenterLon); Gmap.addListener('click', deselectPlaneAll); Gmap.addListener('zoom_changed', function() { GmapInit.Zoomlevel=Gmap.getZoom(); localStorage.setItem(rcd_globals.ls_gmapinit_id, JSON.stringify(GmapInit)); }); Gmap.addListener('dragend', function() { var currCenter = new google.maps.LatLng; GmapInit.CenterLat = Gmap.getCenter().lat(); GmapInit.CenterLon = Gmap.getCenter().lng(); localStorage.setItem(rcd_globals.ls_gmapinit_id, JSON.stringify(GmapInit)); }); var filterControlDiv = document.createElement('div'); var filterControl = new FilterControl(filterControlDiv); filterControlDiv.index = 1; Gmap.controls[google.maps.ControlPosition.RIGHT_BOTTOM].push(filterControlDiv); /* Setup night/day overlay */ nite.init(Gmap); window.setInterval(function() { nite.setDate(null); nite.refresh(); }, 20000); // every 20s if( Filters.enable_night ) nite.show(); else nite.hide(); /* Modify menus */ var MlatServerShow = document.getElementsByClassName('MlatServerShow'); console.log (MlatServerShow); if( rcd_globals.productname=='MLAT_SERVER' ) { document.getElementById("StatusHeader").innerHTML = ' MLAT Server Status'; } var refreshLoop = function() { return function() { var timeout = parseFloat(GmapInit.refreshtime); if( !timeout ) { timeout=1; } if( rcd_globals.initialized ) { fetchAllData(); } timeout = Math.max(1000, 1000 * timeout); window.setTimeout(refreshLoop, timeout); } }(); window.setTimeout(refreshLoop, 1000); }