// "Constant" variables - Since there is no constants in make sure // not to change these CONST_ variables. var CONST_CENTERLAT = 45.0; // 45.0 var CONST_CENTERLON = 9.0; // 9.0 var CONST_ZOOMLVL = 5; //5 // Global variables var Map = null; var CenterLat = CONST_CENTERLAT; var CenterLon = CONST_CENTERLON; var ZoomLvl = CONST_ZOOMLVL; var Planes = {}; var PlanesOnMap = 0; var PlanesOnGrid = 0; var Selected = null; var iSortCol=-1; var bSortASC=true; var bDefaultSortASC=true; var iDefaultSortCol=3; if (localStorage['CenterLat']) { CenterLat = Number(localStorage['CenterLat']); } if (localStorage['CenterLon']) { CenterLon = Number(localStorage['CenterLon']); } if (localStorage['ZoomLvl']) { ZoomLvl = Number(localStorage['ZoomLvl']); } function getIconForPlane(plane, deselect) { var selected = false; var track = 0; var r = 255, g = 255, b = 0; var maxalt = 40000; // Max altitude in the average case var invalt = 0; // If there is plane object if (plane) { invalt = maxalt-plane.altitude; if (Selected == plane.hex && !deselect) { selected = true; } track = plane.track; } if (invalt < 0) invalt = 0; b = parseInt(255/maxalt*invalt); return { strokeWeight: (selected ? 2 : 1), path: google.maps.SymbolPath.FORWARD_CLOSED_ARROW, scale: 5, fillColor: 'rgb('+r+','+g+','+b+')', fillOpacity: 0.9, rotation: track }; } /* Gets hex code of selected plane as string or nothing. * * Select not valid ICAO24 (hex) address to clear selection. */ function selectPlane(selectedPlane) { if (selectedPlane.length) this.planehex = selectedPlane; // Deselect planes if (!Planes[this.planehex]) { if (Planes[Selected].marker) { Planes[Selected].marker.setIcon(getIconForPlane(Planes[Selected], true)); } Selected = null; refreshSelectedInfo(); refreshTableInfo(); return; } var old = Selected; Selected = this.planehex; if (Planes[old] && Planes[old].validposition) { // Remove the highlight in the previously selected plane. Planes[old].marker.setIcon(getIconForPlane(Planes[old])); } if (Planes[Selected].validposition) { Planes[Selected].marker.setIcon(getIconForPlane(Planes[Selected])); } refreshSelectedInfo(); refreshTableInfo(); } function refreshGeneralInfo() { var i = document.getElementById('geninfo'); i.innerHTML = PlanesOnMap + ' planes on the map. '; i.innerHTML += PlanesOnGrid + ' planes on the grid.'; } function refreshSelectedInfo() { var i = document.getElementById('selinfo'); var p = Planes[Selected]; // If no plane is selected if (!p) { p = {}; p.flight = ""; p.hex = ""; p.squawk = ""; p.altitude = "0"; p.speed = "0"; p.lat = "lat"; p.lon = "lon"; p.messages = "0"; p.seen = "0"; } var html = '<table id="selectedinfo">'; if (p.flight != "") { html += '<tr><td colspan=2><b>'+p.flight+' </b>'; html += '[<a href="http://www.flightstats.com/go/FlightStatus/flightStatusByFlight.do?'; html += 'flightNumber='+p.flight+'" target="_blank">FlightStats</a>]</td></tr>'; } else { html += '<tr><td colspan=2><b> </b></td></tr>'; } html += '<tr><td>ICAO:</td><td>'+p.hex+'</td></tr>'; if (p.squawk != "0000") { html += '<tr><td>Squawk:</td><td>'+p.squawk+'</td></tr>'; } else { html += '<tr><td>Squawk:</td><td>n/a</td></tr>'; } html += '<tr><td>Altitude:</td><td>'+p.altitude+' feet</td></tr>'; html += '<tr><td>Speed:</td><td>'+p.speed+' knots</td></tr>'; if (p.validposition) { html += '<tr><td>Coordinates:</td><td>'+p.lat+', '+p.lon+'</td></tr>'; } else { html += '<tr><td>Coordinates:</td><td>n/a</td></tr>'; } html += '<tr><td>Messages:</td><td>'+p.messages+'</td></tr>'; html += '<tr><td>Seen:</td><td>'+p.seen+' sec</td></tr>'; html += '</table>'; i.innerHTML = html; } function refreshTableInfo() { var html = '<table id="tableinfo" width="100%">'; html += '<thead style="background-color: #CCCCCC; cursor: pointer;">'; html += '<td onclick="setASC_DESC(\'0\');sortTable(\'tableinfo\',\'0\');">hex</td>'; html += '<td onclick="setASC_DESC(\'1\');sortTable(\'tableinfo\',\'1\');">Flight</td>'; html += '<td onclick="setASC_DESC(\'2\');sortTable(\'tableinfo\',\'2\');" align="right">Squawk</td>'; html += '<td onclick="setASC_DESC(\'3\');sortTable(\'tableinfo\',\'3\');" align="right">Altitude</td>'; html += '<td onclick="setASC_DESC(\'4\');sortTable(\'tableinfo\',\'4\');" align="right">Speed</td>'; html += '<td onclick="setASC_DESC(\'5\');sortTable(\'tableinfo\',\'5\');" align="right">Track</td>'; html += '<td onclick="setASC_DESC(\'6\');sortTable(\'tableinfo\',\'6\');" align="right">Msgs</td>'; html += '<td onclick="setASC_DESC(\'7\');sortTable(\'tableinfo\',\'7\');" align="right">Seen</td></thead>'; for (var p in Planes) { var specialStyle = ""; if (p == Selected) { html += '<tr id="tableinforow" style="background-color: #E0E0E0;">'; } else { html += '<tr id="tableinforow">'; } if (Planes[p].validposition) { specialStyle = 'bold'; } html += '<td class="' + specialStyle + '">' + Planes[p].hex + '</td>'; html += '<td class="' + specialStyle + '">' + Planes[p].flight + '</td>'; html += '<td class="' + specialStyle + '" align="right">' + Planes[p].squawk + '</td>'; html += '<td class="' + specialStyle + '" align="right">' + Planes[p].altitude + '</td>'; html += '<td class="' + specialStyle + '" align="right">' + Planes[p].speed + '</td>'; html += '<td class="' + specialStyle + '" align="right">' + Planes[p].track + '</td>'; html += '<td class="' + specialStyle + '" align="right">' + Planes[p].messages + '</td>'; html += '<td class="' + specialStyle + '" align="right">' + Planes[p].seen + '</td>'; html += '</tr>'; } html += '</table>'; document.getElementById('tabinfo').innerHTML = html; // Click event for table - lags sometimes for some reason? $('#tableinfo').find('tr').click( function(){ var hex = $(this).find('td:first').text(); selectPlane(hex); }); sortTable("tableinfo"); } // Credit goes to a co-worker that needed a similar functions for something else // we get a copy of it free ;) function setASC_DESC(iCol) { if(iSortCol==iCol) { bSortASC=!bSortASC; } else { bSortASC=bDefaultSortASC; } } function sortTable(szTableID,iCol) { //if iCol was not provided, and iSortCol is not set, assign default value if (typeof iCol==='undefined'){ if(iSortCol!=-1){ var iCol=iSortCol; } else { var iCol=iDefaultSortCol; } } //retrieve passed table element var oTbl=document.getElementById(szTableID).tBodies[0]; var aStore=[]; //If supplied col # is greater than the actual number of cols, set sel col = to last col if (oTbl.rows[0].cells.length<=iCol) iCol=(oTbl.rows[0].cells.length-1); //store the col # iSortCol=iCol; //determine if we are delaing with numerical, or alphanumeric content bNumeric=!isNaN(parseFloat(oTbl.rows[0].cells[iSortCol].textContent||oTbl.rows[0].cells[iSortCol].innerText))?true:false; //loop through the rows, storing each one inro aStore for (var i=0,iLen=oTbl.rows.length;i<iLen;i++){ var oRow=oTbl.rows[i]; vColData=bNumeric?parseFloat(oRow.cells[iSortCol].textContent||oRow.cells[iSortCol].innerText):String(oRow.cells[iSortCol].textContent||oRow.cells[iSortCol].innerText); aStore.push([vColData,oRow]); } //sort aStore ASC/DESC based on value of bSortASC if(bNumeric){//numerical sort aStore.sort(function(x,y){return bSortASC?x[0]-y[0]:y[0]-x[0];}); }else{//alpha sort aStore.sort(); if(!bSortASC) aStore.reverse(); } //rewrite the table rows to the passed table element for(var i=0,iLen=aStore.length;i<iLen;i++){ oTbl.appendChild(aStore[i][1]); } aStore=null; } function fetchData() { $.getJSON('data.json', function(data) { // Planes that are still with us, and set map count to 0 var stillhere = {} PlanesOnMap = 0; // Loop through all the planes in the data packet for (var j=0; j < data.length; j++) { // Set plane to be this particular plane in the data set var plane = data[j]; // Add to the still here list stillhere[plane.hex] = true; plane.flight = $.trim(plane.flight); // Set the marker to null, for now var marker = null; // Either update the data or add it if (Planes[plane.hex]) { // Declare our plane that we are working with from our old data set var myplane = Planes[plane.hex]; // If the has valid position, we should make a marker for it if (plane.validposition) { if (myplane.marker != null) { marker = myplane.marker; var icon = marker.getIcon(); var newpos = new google.maps.LatLng(plane.lat, plane.lon); marker.setPosition(newpos); marker.setIcon(getIconForPlane(plane)); PlanesOnMap++; } else { // Add new plane to map, dont ask me why this is needed here now... marker = new google.maps.Marker({ position: new google.maps.LatLng(plane.lat, plane.lon), map: Map, icon: getIconForPlane(plane) }); myplane.marker = marker; marker.planehex = plane.hex; PlanesOnMap++; // Trap clicks for this marker. google.maps.event.addListener(marker, 'click', selectPlane); } } // Update all the other information myplane.altitude = plane.altitude; myplane.speed = plane.speed; myplane.lat = plane.lat; myplane.lon = plane.lon; myplane.track = plane.track; myplane.flight = plane.flight; myplane.seen = plane.seen; myplane.messages = plane.messages; myplane.squawk = plane.squawk; myplane.validposition = plane.validposition; myplane.validtrack = plane.validtrack; // If this is a selected plane, refresh its data outside of the table if (myplane.hex == Selected) refreshSelectedInfo(); } else { // This is a new plane // Do we have a lat/long that is not 0? if (plane.validposition) { // Add new plane to map marker = new google.maps.Marker({ position: new google.maps.LatLng(plane.lat, plane.lon), map: Map, icon: getIconForPlane(plane) }); plane.marker = marker; marker.planehex = plane.hex; PlanesOnMap++; // Trap clicks for this marker. google.maps.event.addListener(marker, 'click', selectPlane); } // Copy the plane into Planes Planes[plane.hex] = plane; } // If we have lat/long, we must have a marker, so lets set the marker title if (plane.validposition) { if (plane.flight.length == 0) { marker.setTitle(plane.hex) } else { marker.setTitle(plane.flight+' ('+plane.hex+')') } } } // How many planes have we heard from? PlanesOnGrid = data.length; /* Remove idle planes. */ for (var p in Planes) { if (!stillhere[p]) { if (Planes[p].marker != null) Planes[p].marker.setMap(null); delete Planes[p]; } } refreshTableInfo(); refreshSelectedInfo(); }); } function checkTime(i) { if (i < 10) { return "0" + i; } return i; } function printTime() { var currentTime = new Date(); var hours = checkTime(currentTime.getUTCHours()); var minutes = checkTime(currentTime.getUTCMinutes()); var seconds = checkTime(currentTime.getUTCSeconds()); if (document.getElementById) { document.getElementById('utcTime').innerHTML = hours + ":" + minutes + ":" + seconds; } } function placeSettings() { // Settings link var marginLeft = $('#header').width() - $('#info_settings').width(); $('#info_settings').css('left', marginLeft); $('#info_settings').css('top', parseInt($('#utcTime').offset().top)); // Settings area $('#info_settings_area').css('top', parseInt($('#geninfo').offset().top)); $('#info_settings_area').css('left', 5); $('#info_settings_area').css('width', parseInt($('#info').width() - 40)); } function toggleSettings() { if ($('#info_settings_area').css('display') != 'none') { $('#info_settings_area').hide(350); } else { // Open settings $('#info_settings_area').show(350); } } function resetMap() { localStorage['CenterLat'] = CONST_CENTERLAT; localStorage['CenterLon'] = CONST_CENTERLON; localStorage['ZoomLvl'] = CONST_ZOOMLVL; CenterLat = CONST_CENTERLAT; CenterLon = CONST_CENTERLON; ZoomLvl = CONST_ZOOMLVL; Map.setZoom(parseInt(localStorage['ZoomLvl'])); Map.setCenter(new google.maps.LatLng(parseFloat(localStorage['CenterLat']), parseFloat(localStorage['CenterLon']))); Selected = null; refreshSelectedInfo(); } function initialize() { var mapTypeIds = []; for(var type in google.maps.MapTypeId) { mapTypeIds.push(google.maps.MapTypeId[type]); } mapTypeIds.push("OSM"); var mapOptions = { center: new google.maps.LatLng(CenterLat, CenterLon), zoom: ZoomLvl, mapTypeId: google.maps.MapTypeId.ROADMAP, mapTypeControlOptions: { mapTypeIds: mapTypeIds, } }; Map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions); //Define OSM map type pointing at the OpenStreetMap tile server Map.mapTypes.set("OSM", new google.maps.ImageMapType({ getTileUrl: function(coord, zoom) { return "http://tile.openstreetmap.org/" + zoom + "/" + coord.x + "/" + coord.y + ".png"; }, tileSize: new google.maps.Size(256, 256), name: "OpenStreetMap", maxZoom: 18 })); // show settings at info-area $(function(){ $(window).resize(function(e){ placeSettings(); }); placeSettings(); // hide it before it's positioned $('#info_settings').css('display','inline'); }); // Listener for newly created Map google.maps.event.addListener(Map, 'center_changed', function() { localStorage['CenterLat'] = Map.getCenter().lat(); localStorage['CenterLon'] = Map.getCenter().lng(); }); google.maps.event.addListener(Map, 'zoom_changed', function() { localStorage['ZoomLvl'] = Map.getZoom(); }); google.maps.event.addListener(Map, 'click', function() { if (Selected) { selectPlane("xyzxyz"); // Select not valid ICAO24 (hex) address to clear selection. } Selected = null; refreshSelectedInfo(); refreshTableInfo(); }); // Setup our timer to poll from the server. window.setInterval(function() { fetchData(); refreshGeneralInfo(); }, 1000); // Faster timer, smoother things window.setInterval(function() { printTime(); }, 250); refreshGeneralInfo(); refreshSelectedInfo(); refreshTableInfo(); }