# Copyright 2018 Stuart Buchanan # This file is part of FlightGear. # # FlightGear is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # FlightGear is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with FlightGear. If not, see . # # NearestAirports var NearestAirports = { new : func (mfd, myCanvas, device, svg) { var obj = { parents : [ NearestAirports, MFDPage.new(mfd, myCanvas, device, svg, "NearestAirports", "NRST - NEAREST AIRPORTS") ], }; obj.setController(fg1000.NearestAirportsController.new(obj, svg)); # Dynamic elements. There are 4 different sets of dynamic elements: # # Nearest Airports - this is a scrolling list of up to 25 airports within 200nm, shown 5 at a time. # Runways - just a single scroll element # Frequencies - 3 displayed in a scrolling list # Approaches - 3 displayed in a scrolling list # # Selection is via softkeys, the FMS knob, or via the page menu. obj.airportSelect = PFD.GroupElement.new( obj.pageName, svg, [ "Arrow", "ID", "CRS", "DST"], 5, "Arrow", 1, "AirportScrollBar", "AirportScroll", 100 ); obj.runwaySelect = PFD.ScrollElement.new(obj.pageName, svg, "RunwayID", [36,18]); # Dummy values obj.freqSelect = PFD.GroupElement.new( obj.pageName, svg, ["FreqLabel", "Freq"], 3, "Freq", 0, "FreqScrollBar", "FreqScroll", 75 ); obj.approachSelect = PFD.GroupElement.new( obj.pageName, svg, ["Approach"], 3, "Approach", 0, "ApproachScrollBar", "ApproachScroll", 75 ); # Other dynamic text elements obj.addTextElements(["Name", "Alt", "RunwaySurface", "RunwayDimensions"]); obj.topMenu(device, obj, nil); return obj; }, # Indicate which group is selected by colour of the softkeys display_toggle : func(device, svg, mi, group) { var bg_name = sprintf("SoftKey%d-bg",mi.menu_id); if (me.getController().getSelectedGroup() == group) { device.svg.getElementById(bg_name).setColorFill(0.5,0.5,0.5); svg.setColor(0.0,0.0,0.0); } else { device.svg.getElementById(bg_name).setColorFill(0.0,0.0,0.0); svg.setColor(1.0,1.0,1.0); } svg.setText(mi.title); svg.setVisible(1); # display function }, # Function to highlight the APT softkey - used when CRSR is pressed to indicate # that we're editing the airports selection. selectAirports : func() { me.resetMenuColors(); var bg_name = sprintf("SoftKey%d-bg",4); var tname = sprintf("SoftKey%d",4); me.device.svg.getElementById(bg_name).setColorFill(0.5,0.5,0.5); me.device.svg.getElementById(tname).setColor(0.0,0.0,0.0); }, # Clear any cursor, highlights. Used when exiting from CRSR mode resetCRSR : func() { me.airportSelect.hideCRSR(); me.runwaySelect.unhighlightElement(); me.freqSelect.hideCRSR(); me.approachSelect.hideCRSR(); me.resetMenuColors(); }, offdisplay : func() { # The Nearest... pages use the underlying navigation map. me.mfd.NavigationMap.offdisplayPartial(); # Reset the menu colours. Shouldn't have to do this here, but # there's not currently an obvious other location to do so. me.resetMenuColors(); me.getController().offdisplay(); }, ondisplay : func() { me.getController().ondisplay(); # The Nearest... pages use the underlying navigation map. me.mfd.NavigationMap.ondisplayPartial(); me.mfd.setPageTitle(me.title); }, updateAirports : func(apts) { if (apts == nil) return; var airportlist = []; for (var i = 0; i < size(apts); i = i + 1) { var apt = apts[i]; var crsAndDst = courseAndDistance(apt); # Display the course and distance in NM . var crs = sprintf("%i°", crsAndDst[0]); var dst = sprintf("%.1fnm", crsAndDst[1]); # Convert into something we can pass straight to the UIGroup. append(airportlist, { Arrow : apt.id, ID: apt.id, CRS: crs, DST: dst, }); } me.airportSelect.setValues(airportlist); if (size(airportlist) > 0) { me.updateAirportData(apts[0]); #me.airportSelect.showCRSR(); } else { #me.airportSelect.hideCRSR(); me.setTextElement("Name", "NONE WITHIN 200NM"); me.setTextElement("Alt", ""); } }, updateAirportData : func(apt) { if (apt == nil) return; me.setTextElement("Name", apt.name); me.setTextElement("Alt", sprintf("%ift", M2FT * apt.elevation)); # Set up the runways list, but ignoring reciprocals so we don't get # runways displayed twice. var rwys = []; var recips = {}; foreach(var rwy; sort(keys(apt.runways), string.icmp)) { var rwy_info = apt.runways[rwy]; if (recips[rwy_info.id] == nil) { var lbl = rwy_info.id ~ "-" ~ rwy_info.reciprocal.id; append(rwys, lbl); recips[rwy_info.reciprocal.id] = 1; } } if (size(rwys) > 0) { me.runwaySelect.setValues(rwys); me.updateRunwayInfo(apt.runways[keys(apt.runways)[0]]); } else { me.runwaySelect.setValues([""]); me.updateRunwayInfo(nil); } var freqarray = []; # Add Comm Frequencies var apt_comms = apt.comms(); if (size(apt_comms) > 0) { # Airport has one or more frequencies assigned to it. var freqs = {}; foreach (var c; apt_comms) { freqs[c.ident] = sprintf("%.3f", c.frequency); } foreach (var c; sort(keys(freqs), string.icmp)) { append(freqarray, {FreqLabel: c, Freq: freqs[c]}); } } # Add any ILS frequencies as well foreach(var rwy; sort(keys(apt.runways), string.icmp)) { var rwy_info = apt.runways[rwy]; if (rwy_info.ils_frequency_mhz != nil) { var label = "ILS " ~ rwy_info.id; var freq = sprintf("%.3f", rwy_info.ils_frequency_mhz); append(freqarray, {FreqLabel: label, Freq: freq}); } } me.freqSelect.setValues(freqarray); # Approaches var approachList = apt.getApproachList(); me.approachSelect.setValues(approachList); # Display the DTO line to the airport me.mfd.NavigationMap.getController().setDTOLineTarget(apt.lat, apt.lon); }, updateRunwayInfo : func(rwy_info) { if (rwy_info != nil ) { var dim = sprintf("%ift x %ift", 3.28 * rwy_info.length, 3.28 * rwy_info.width); me.setTextElement("RunwayDimensions", dim); me.setTextElement("RunwaySurface", SURFACE_TYPES[rwy_info.surface]); } else { me.setTextElement("RunwayDimensions", ""); me.setTextElement("RunwaySurface", ""); } }, getSelectedAirportID : func() { return me.airportSelect.getValue(); }, getSelectedRunway : func() { return me.runwaySelect.getValue(); }, getSelectedFreq : func() { return me.freqSelect.getValue(); }, getSelectedApproach : func() { return me.approachSelect.getValue(); }, topMenu : func(device, pg, menuitem) { pg.clearMenu(); pg.resetMenuColors(); pg.addMenuItem(0, "ENGINE", pg, pg.mfd.EIS.engineMenu); pg.addMenuItem(2, "MAP", pg, pg.mfd.NavigationMap.mapMenu); pg.addMenuItem(4, "APT", pg, func(dev, pg, mi) { pg.getController().selectAirports(); device.updateMenus(); }, # callback func(svg, mi) { pg.display_toggle(device, svg, mi, NearestAirportsController.UIGROUP.APT); } ); pg.addMenuItem(5, "RNWY", pg, func(dev, pg, mi) { pg.getController().selectRunways(); device.updateMenus(); }, # callback func(svg, mi) { pg.display_toggle(device, svg, mi, NearestAirportsController.UIGROUP.RNWY); } ); pg.addMenuItem(6, "FREQ", pg, func(dev, pg, mi) { pg.getController().selectFrequencies(); device.updateMenus(); }, # callback func(svg, mi) { pg.display_toggle(device, svg, mi, NearestAirportsController.UIGROUP.FREQ); } ); pg.addMenuItem(7, "APR", pg, func(dev, pg, mi) { pg.getController().selectApproaches(); device.updateMenus(); }, # callback func(svg, mi) { pg.display_toggle(device, svg, mi, NearestAirportsController.UIGROUP.APR); } ); device.updateMenus(); }, };