From c409864dac0931599aad7497a6e6014249ef21d5 Mon Sep 17 00:00:00 2001 From: Gijs de Rooy Date: Wed, 4 Dec 2013 23:19:22 +0100 Subject: [PATCH] Navigation display: - move SVG to Canvas directory - add basic wxradar - differentiate between track and heading - improve altitude arc - add range arcs - display correct ETA for next waypoint --- Nasal/canvas/map/altitude-arc.draw | 32 - Nasal/canvas/map/altitude-arc.layer | 12 - Nasal/canvas/map/altitude-arc.model | 19 - Nasal/canvas/map/boeingND.svg | 1921 +++++++++++++++++++++++++++ Nasal/canvas/map/navdisplay.mfd | 178 ++- Nasal/canvas/map/storm.draw | 22 + Nasal/canvas/map/storm.png | Bin 0 -> 5209 bytes Nasal/canvas/map/storms.layer | 10 + Nasal/canvas/map/storms.model | 31 + 9 files changed, 2100 insertions(+), 125 deletions(-) delete mode 100644 Nasal/canvas/map/altitude-arc.draw delete mode 100644 Nasal/canvas/map/altitude-arc.layer delete mode 100644 Nasal/canvas/map/altitude-arc.model create mode 100644 Nasal/canvas/map/boeingND.svg create mode 100644 Nasal/canvas/map/storm.draw create mode 100644 Nasal/canvas/map/storm.png create mode 100644 Nasal/canvas/map/storms.layer create mode 100644 Nasal/canvas/map/storms.model diff --git a/Nasal/canvas/map/altitude-arc.draw b/Nasal/canvas/map/altitude-arc.draw deleted file mode 100644 index 7cbc446d1..000000000 --- a/Nasal/canvas/map/altitude-arc.draw +++ /dev/null @@ -1,32 +0,0 @@ -## -# pseudo draw routine - rangeNm is passed by the model -# needs further work once we adopt the MapStructure framwork -# HOWEVER, the alt-arc is an exception in that it's not rendered as -# a map item ... - -var draw_altarc = func (group, rangeNm, controller=nil, lod = 0) { -# print("drawing alt-arc, range:", rangeNm); - -var altArc = group.createChild("path","alt-arc") - .setStrokeLineWidth(3) - .setColor(0,1,0) - .set("clip", "rect(124, 1024, 1024, 0)"); - - -if (abs(getprop("velocities/vertical-speed-fps")) > 10) { - altArc.reset(); - - var altRangeNm = (getprop("autopilot/settings/target-altitude-ft")- - getprop("instrumentation/altimeter/indicated-altitude-ft"))/getprop("velocities/vertical-speed-fps")* - getprop("/velocities/groundspeed-kt")*KT2MPS*M2NM; - - if(altRangeNm > 1) { - var altRangePx = (256/rangeNm )*altRangeNm; - altArc.moveTo(-altRangePx*2.25,0) - .arcSmallCW(altRangePx*2.25,altRangePx*2.25,0,altRangePx*4.5,0) - .setTranslation(512,824); - } # altRangeNm > 1 - } # fps > 10 - - -} # draw_altarc diff --git a/Nasal/canvas/map/altitude-arc.layer b/Nasal/canvas/map/altitude-arc.layer deleted file mode 100644 index cddc94ef3..000000000 --- a/Nasal/canvas/map/altitude-arc.layer +++ /dev/null @@ -1,12 +0,0 @@ -var AltitudeArcLayer = {}; -AltitudeArcLayer.new = func(group, name, controller=nil) { - var m = Layer.new(group, name, AltitudeArcModel ); - m._model._controller = controller; - m.setDraw(func draw_layer(layer:m, callback:canvas.draw_altarc, lod:0) ); - return m; -} - -## -# airport-nd lookup key -register_layer("altitude-arc", AltitudeArcLayer); - diff --git a/Nasal/canvas/map/altitude-arc.model b/Nasal/canvas/map/altitude-arc.model deleted file mode 100644 index 663a40973..000000000 --- a/Nasal/canvas/map/altitude-arc.model +++ /dev/null @@ -1,19 +0,0 @@ -var AltitudeArcModel = {}; - AltitudeArcModel.new = func make( LayerModel, AltitudeArcModel ); - - AltitudeArcModel.init = func { -# print("Updating alt-arc model"); -var rangeNm = me._controller['query_range'](); - -me._view.reset(); -me.push(rangeNm); -me.notifyView(); - -## -# FIXME this should be configurable via the controller -# and it should only be running if the predicate (altitude check) is true -# for now it'll suffice though -settimer(func me.init(), 0.3); -} - - diff --git a/Nasal/canvas/map/boeingND.svg b/Nasal/canvas/map/boeingND.svg new file mode 100644 index 000000000..db5bcf84d --- /dev/null +++ b/Nasal/canvas/map/boeingND.svg @@ -0,0 +1,1921 @@ + + + +image/svg+xmlGijs de Rooy + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +ABCD + + + + + + + + + + + + + + + + + + + + + + + + + + +99.9 + + + + + + + + + + + + + + + + + + + + + + + + + + +999 + + + + + + + + + + + + + + + + + + + + + + + + + +999°/ 99 + + + + + + + + + + + + + + + + + + + + + + + + + +999 + + + + + + + + + + + + + + + + + + + + + + + + + +999 + + + + + + + + + + + + + + + + + + + + + + + + + +GS + + + + + + + + + + + + + + + + + + + + + + + + + +TAS + + + + + + + + + + + + + + + + + + + + + + + + + +HDG + + + + + + + + + + + + + + + + + + + + + + + + + +MAG + + + + + + + + + + + + + + + + + + + + + + + + + +VOR L + + + + + + + + + + + + + + + + + + + + + + + + +DME + + + + + + + + + + + + + + + + + + + + + + + + +99.9 + + + + + + + + + + + + + + + + + + + + + + + + +ABC + + + + + + + + + + + + + + + + + + + + + + + + +VOR R + + + + + + + + + + + + + + + + + + + + + + + + +DME + + + + + + + + + + + + + + + + + + + + + + + + +99.9 + + + + + + + + + + + + + + + + + + + + + + + + +ABC + + + + + + + + + + + + + + + + + + + + + + + + +08 34.4z + + + + + + + + + + + + + + + + + + + + + + + + +0 + + + + + + + + + + + + + + + + + + + + + + + +33 + + + + + + + + + + + + + + + + + + + + + + + +30 + + + + + + + + + + + + + + + + + + + + + + + +27 + + + + + + + + + + + + + + + + + + + + + + + +24 + + + + + + + + + + + + + + + + + + + + + + + +21 + + + + + + + + + + + + + + + + + + + + + + + +18 + + + + + + + + + + + + + + + + + + + + + + + +15 + + + + + + + + + + + + + + + + + + + + + + + +12 + + + + + + + + + + + + + + + + + + + + + + + +9 + + + + + + + + + + + + + + + + + + + + + + + +6 + + + + + + + + + + + + + + + + + + + + + + + +3 + + + + + + + + + + + + + + + + + + + + + + + +99 + + + + + + + + + + + + + + + + + + + + +TA ONLY + + + + + + + + + + + + + + + + +WXR + + + + + + + + + + + + + + + + +ARPT + + + + + + + + + + + + + + + + +WPT + + + + + + + + + + + + + + + + +STA + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Nasal/canvas/map/navdisplay.mfd b/Nasal/canvas/map/navdisplay.mfd index cf3fe8161..5d7f580e0 100644 --- a/Nasal/canvas/map/navdisplay.mfd +++ b/Nasal/canvas/map/navdisplay.mfd @@ -42,12 +42,10 @@ var NDStyles = { }, # where all the symbols are stored - # TODO: the SVG image should be moved to the canvas folder - # so that it can be shared by other aircraft, i.e. no 744 dependencies - # also SVG elements should be renamed to use boeing/airbus prefix + # TODO: SVG elements should be renamed to use boeing/airbus prefix # aircraft developers should all be editing the same ND.svg image # the code can deal with the differences now - svg_filename: "Aircraft/747-400/Models/Cockpit/Instruments/ND/ND.svg", + svg_filename: "Nasal/canvas/map/boeingND.svg", ## ## this loads and configures existing layers (currently, *.layer files in Nasal/canvas/map) @@ -65,6 +63,18 @@ var NDStyles = { }, # end of layer update predicate }, # end of fixes layer + + # Should redraw every 10 seconds + { name:'storms', update_on:['toggle_range','toggle_weather','toggle_display_mode'], predicate: func(nd, layer) { + # print("Running fixes predicate"); + var visible=nd.get_switch('toggle_weather') and nd.get_switch('toggle_display_mode') != "PLAN"; + if (visible) { + print("storms update requested!"); + trigger_update( layer ); + } layer._view.setVisible(visible); + + }, # end of layer update predicate + }, # end of storms layer { name:'airports-nd', update_on:['toggle_range','toggle_airports','toggle_display_mode'], predicate: func(nd, layer) { # print("Running airports-nd predicate"); @@ -119,12 +129,6 @@ var NDStyles = { }, # end of layer update predicate }, # end of route layer - { name:'altitude-arc', not_a_map:1, update_on:['toggle_range','toggle_display_mode'], predicate: func(nd, layer) { - trigger_update( layer ); # clear & redraw - layer._view.setVisible( 1 ); - }, # end of layer update predicate - }, # end of alt-arc layer - ## add other layers here, layer names must match the registered names as used in *.layer files for now ## this will all change once we're using Philosopher's MapStructure framework @@ -158,31 +162,57 @@ var NDStyles = { is_false: func(nd) nd.symbols.tas.hide(), }, # end of tas behavior callbacks }, # end of tas hash - { + { + id: 'wpActiveId', + impl: { + init: func(nd,symbol), + predicate: func(nd) getprop("/autopilot/route-manager/wp/id") != nil and getprop("autopilot/route-manager/active"), + is_true: func(nd) { + nd.symbols.wpActiveId.setText(getprop("/autopilot/route-manager/wp/id")); + nd.symbols.wpActiveId.show(); + }, + is_false: func(nd) nd.symbols.wpActiveId.hide(), + }, # of wpActiveId.impl + }, # of wpActiveId + { + id: 'wpActiveDist', + impl: { + init: func(nd,symbol), + predicate: func(nd) getprop("/autopilot/route-manager/wp/dist") != nil and getprop("autopilot/route-manager/active"), + is_true: func(nd) { + nd.symbols.wpActiveDist.setText(sprintf("%3.01fNM",getprop("/autopilot/route-manager/wp/dist"))); + nd.symbols.wpActiveDist.show(); + }, + is_false: func(nd) nd.symbols.wpActiveDist.hide(), + }, # of wpActiveDist.impl + }, # of wpActiveDist + { id: 'eta', impl: { init: func(nd,symbol), - predicate: func(nd) getprop("autopilot/route-manager/wp/eta") != nil, + predicate: func(nd) getprop("autopilot/route-manager/wp/eta") != nil and getprop("autopilot/route-manager/active"), is_true: func(nd) { - var eta=getprop("autopilot/route-manager/wp/eta"); - var etaWp = split(":",eta); - var h = getprop("/sim/time/utc/hour"); - var m = getprop("/sim/time/utc/minute")+sprintf("%02f",etaWp[0]); - var s = getprop("/sim/time/utc/second")+sprintf("%02f",etaWp[1]); + var etaSec = getprop("/sim/time/utc/day-seconds")+getprop("autopilot/route-manager/wp/eta-seconds"); + var h = math.floor(etaSec/3600); + if (h>24) h=h-24; + etaSec=etaSec-3600*h; + var m = math.floor(etaSec/60); + etaSec=etaSec-60*m; + var s = etaSec; nd.symbols.eta.setText(sprintf("%02.0f%02.0f.%02.0fz",h,m,s)); nd.symbols.eta.show(); }, is_false: func(nd) nd.symbols.eta.hide(), }, # of eta.impl - }, # of eta - { id:'hdg', + }, # of eta + { id:'hdg', impl: { init: func(nd,symbol), predicate: ALWAYS, # always true - is_true: func(nd) nd.symbols.hdg.setText(sprintf("%03.0f", nd.aircraft_source.get_hdg() )), + is_true: func(nd) nd.symbols.hdg.setText(sprintf("%03.0f", nd.aircraft_source.get_hdg_mag() )), is_false: NOTHING, }, # of hdg.impl - }, # of hdg + }, # of hdg { id:'gs', impl: { @@ -195,23 +225,15 @@ var NDStyles = { is_false: func(nd) nd.symbols.gs.setFontSize(52), }, # of gs.impl }, # of gs - - { id:'compass', + + { id:'rangeArcs', impl: { - init: func(nd,symbol) nd.getElementById(symbol.id).updateCenter(), - common: func(nd) , - predicate: func(nd) (nd.in_mode('toggle_display_mode', ['APP','MAP','PLAN','VOR'] )), - is_true: func(nd) { - # # orientation/track-magnetic-deg is noisy - nd.symbols.compass.setRotation(-nd.aircraft_source.get_hdg() *D2R); - nd.symbols.compass.show(); - }, - is_false: func(nd) nd.symbols.compass.hide(), - }, # of compass.impl - }, # of compass - - - + init: func(nd,symbol), + predicate: func(nd) (((nd.get_switch('toggle_display_mode') == "APP" or nd.get_switch('toggle_display_mode') == "VOR") and nd.get_switch('toggle_weather')) or nd.get_switch('toggle_display_mode') == "MAP"), + is_true: func(nd) nd.symbols.rangeArcs.show(), + is_false: func(nd) nd.symbols.rangeArcs.hide(), + }, # of rangeArcs.impl + }, # of rangeArcs ], # end of vector with features @@ -233,7 +255,8 @@ var NDStyles = { var NDSourceDriver = {}; NDSourceDriver.new = func { var m = {parents:[NDSourceDriver]}; - m.get_hdg= func getprop("/orientation/heading-deg"); + m.get_hdg_mag= func getprop("/orientation/heading-magnetic-deg"); + m.get_trk_mag= func getprop("/orientation/track-magnetic-deg"); m.get_lat= func getprop("/position/latitude-deg"); m.get_lon= func getprop("/position/longitude-deg"); m.get_spd= func getprop("/velocities/groundspeed-kt"); @@ -264,6 +287,14 @@ var default_switches = { 'toggle_display_mode': {path: '/mfd/display-mode', value:'MAP', type:'STRING'}, }; +# Hack to update weather radar once every 10 seconds +var update_weather = func { + if (getprop("/instrumentation/efis/inputs/wxr") != nil) + setprop("/instrumentation/efis/inputs/wxr",getprop("/instrumentation/efis/inputs/wxr")); + settimer(update_weather, 10); +} +update_weather(); + ## # TODO: # - introduce a MFD class (use it also for PFD/EICAS) @@ -312,7 +343,8 @@ var NavDisplay = { # for creating NDs that are driven by AI traffic instead of the main aircraft (generalization rocks!) connectAI: func(source=nil) { me.aircraft_source = { - get_hdg: func source.getNode('orientation/true-heading-deg').getValue(), + get_hdg_mag: func source.getNode('orientation/heading-magnetic-deg').getValue(), + get_trk_mag: func source.getNode('orientation/track-magnetic-deg').getValue(), get_lat: func source.getNode('position/latitude-deg').getValue(), get_lon: func source.getNode('position/longitude-deg').getValue(), get_spd: func source.getNode('velocities/true-airspeed-kt').getValue(), @@ -403,7 +435,7 @@ var NavDisplay = { ### this is the "old" method that's less flexible, we want to use the style hash instead (see above) # because things are much better configurable that way # now look up all required SVG elements and initialize member fields using the same name to have a convenient handle - foreach(var element; ["wpActiveId","wpActiveDist","wind", + foreach(var element; ["wind", "dmeLDist","dmeRDist","vorLId","vorRId", "range","status.wxr","status.wpt", "status.sta","status.arpt"]) @@ -413,8 +445,8 @@ var NavDisplay = { # anything that needs updatecenter called, should be added to the vector here # foreach(var element; ["rotateComp","windArrow","selHdg", - "curHdgPtr","staFromL","staToL", - "staFromR","staToR"] ) + "curHdgPtr","staFromL","staToL","trkInd", + "staFromR","staToR","compass","hdgTrk","truMag","altArc"] ) me.symbols[element] = me.nd.getElementById(element).updateCenter(); # this should probably be using Philosopher's new SymbolLayer ? @@ -545,16 +577,15 @@ var NavDisplay = { var latNm = 60; var lonNm = 60; - # fgcommand('profiler-start'); - var userHdg = me.aircraft_source.get_hdg(); - var userTrkMag = me.aircraft_source.get_hdg(); # getprop("orientation/heading-deg"); # orientation/track-magnetic-deg is noisy + var userHdgMag = me.aircraft_source.get_hdg_mag(); + var userTrkMag = me.aircraft_source.get_trk_mag(); # getprop("orientation/heading-deg"); # orientation/track-magnetic-deg is noisy var userLat = me.aircraft_source.get_lat(); var userLon = me.aircraft_source.get_lon(); # this should only ever happen when testing the experimental AI/MP ND driver hash (not critical) - if (!userHdg or !userTrkMag or !userLat or !userLon) { + if (!userHdgMag or !userTrkMag or !userLat or !userLon) { print("aircraft source invalid, returning !"); return; } @@ -567,11 +598,9 @@ var NavDisplay = { latNm = latlen*M2NM; #60 at equator lonNm = lonlen*M2NM; #60 at equator - me.symbols.windArrow.setRotation((getprop("/environment/wind-from-heading-deg")-userHdg)*D2R); + me.symbols.windArrow.setRotation((getprop("/environment/wind-from-heading-deg")-userHdgMag)*D2R); me.symbols.wind.setText(sprintf("%3.0f / %2.0f",getprop("/environment/wind-from-heading-deg"),getprop("/environment/wind-speed-kt"))); - - if ((var navid0=getprop("instrumentation/nav/nav-id"))!=nil ) me.symbols.vorLId.setText(navid0); if ((var navid1=getprop("instrumentation/nav[1]/nav-id"))!=nil ) @@ -583,33 +612,58 @@ var NavDisplay = { me.symbols.range.setText(sprintf("%3.0f",me.rangeNm() )); #rangeNm=rangeNm*2; - - # updates two SVG symbols, should use a listener specified in the config hash - if(getprop("/autopilot/route-manager/active")) { - me.symbols.wpActiveId.setText(getprop("/autopilot/route-manager/wp/id")); - me.symbols.wpActiveDist.setText(sprintf("%3.01fNM",getprop("/autopilot/route-manager/wp/dist"))); - } # reposition the map, change heading & range: me.map._node.getNode("ref-lat",1).setDoubleValue(userLat); me.map._node.getNode("ref-lon",1).setDoubleValue(userLon); - me.map._node.getNode("hdg",1).setDoubleValue(userHdg); # should also be using a listener for this me.map._node.getNode("range",1).setDoubleValue(me.rangeNm()/2); # avoid this here, use a listener instead + if(me.get_switch('toggle_display_mode') == 'MAP' or me.get_switch('toggle_display_mode') == 'PLAN' ) { + me.symbols.rotateComp.setRotation(-userTrkMag*D2R); + me.symbols.trkInd.setRotation(0); + me.symbols.curHdgPtr.setRotation(userHdgMag*D2R); + me.map._node.getNode("hdg",1).setDoubleValue(userTrkMag); + me.symbols.compass.setRotation(-userTrkMag*D2R); + me.symbols.hdgTrk.setText("TRK"); + me.symbols.truMag.setText("MAG"); + } + if(me.get_switch('toggle_display_mode') == 'APP' or me.get_switch('toggle_display_mode') == 'VOR' ) { + me.symbols.rotateComp.setRotation(-userHdgMag*D2R); + me.symbols.trkInd.setRotation((userTrkMag-userHdgMag)*D2R); + me.symbols.curHdgPtr.setRotation(userHdgMag*D2R); + me.map._node.getNode("hdg",1).setDoubleValue(userHdgMag); + me.symbols.compass.setRotation(-userHdgMag*D2R); + me.symbols.hdgTrk.setText("HDG"); + me.symbols.truMag.setText("MAG"); + } - me.symbols.rotateComp.setRotation(-userTrkMag*D2R); + if (abs(getprop("velocities/vertical-speed-fps")) > 10) { + var altRangeNm = (getprop("autopilot/settings/target-altitude-ft")- + getprop("instrumentation/altimeter/indicated-altitude-ft"))/getprop("velocities/vertical-speed-fps")* + getprop("/velocities/groundspeed-kt")*KT2MPS*M2NM; + + if(altRangeNm > 1) { + var altRangePx = (350/me.rangeNm())*altRangeNm; + if (altRangePx > 700) + altRangePx = 700; + me.symbols.altArc.setTranslation(0,-altRangePx); + } # altRangeNm > 1 + me.symbols.altArc.show(); + } else { + me.symbols.altArc.hide(); + }# fps > 10 ## these would require additional arguments to be moved to an external config hash currently - me.symbols.curHdgPtr.setRotation(userHdg*D2R); + me.symbols.selHdg.setRotation(getprop("autopilot/settings/true-heading-deg")*D2R); if (var nav0hdg=getprop("instrumentation/nav/heading-deg") != nil) - me.symbols.staFromL.setRotation((nav0hdg-userHdg+180)*D2R); + me.symbols.staFromL.setRotation((nav0hdg-userHdgMag+180)*D2R); if (var nav0hdg=getprop("instrumentation/nav/heading-deg") != nil) - me.symbols.staToL.setRotation((nav0hdg-userHdg)*D2R); + me.symbols.staToL.setRotation((nav0hdg-userHdgMag)*D2R); if (var nav1hdg=getprop("instrumentation/nav[1]/heading-deg") != nil) - me.symbols.staFromR.setRotation((nav1hdg-userHdg+180)*D2R); + me.symbols.staFromR.setRotation((nav1hdg-userHdgMag+180)*D2R); if (var nav1hdg=getprop("instrumentation/nav[1]/heading-deg") != nil) - me.symbols.staToR.setRotation((nav1hdg-userHdg)*D2R); + me.symbols.staToR.setRotation((nav1hdg-userHdgMag)*D2R); ## run all predicates in the NDStyle hash and evaluate their true/false behavior callbacks diff --git a/Nasal/canvas/map/storm.draw b/Nasal/canvas/map/storm.draw new file mode 100644 index 000000000..37f8589f2 --- /dev/null +++ b/Nasal/canvas/map/storm.draw @@ -0,0 +1,22 @@ +## +# draw a single storm symbol +# + +var draw_storm = func (group, storm, controller=nil, lod=0) { + var lat = storm.lat; + var lon = storm.lon; + var radiusNm = storm.radiusNm; + + var storm_grp = group.createChild("group","storm"); # one group for each storm + + storm_grp.createChild("image") + .setFile("Nasal/canvas/map/storm.png") + .setSize(128*radiusNm,128*radiusNm) + .setTranslation(-64*radiusNm,-64*radiusNm) + .setCenter(0,0); + + # the storm position + storm_grp.setGeoPosition(lat, lon) + .set("z-index",0); + +} diff --git a/Nasal/canvas/map/storm.png b/Nasal/canvas/map/storm.png new file mode 100644 index 0000000000000000000000000000000000000000..8c9b3eaf06329eca44c8c6be61e82e9fc52be779 GIT binary patch literal 5209 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 abjX?8Jp*%hlcX7p8h|o1H>lLZMEnnA)S12j literal 0 HcmV?d00001 diff --git a/Nasal/canvas/map/storms.layer b/Nasal/canvas/map/storms.layer new file mode 100644 index 000000000..6504af495 --- /dev/null +++ b/Nasal/canvas/map/storms.layer @@ -0,0 +1,10 @@ +var StormLayer = {}; +StormLayer.new = func(group,name, controller) { + var m=Layer.new(group, name, StormModel); + m._model._controller = controller; # set up the controller for the model !!!!! + m.setDraw (func draw_layer(layer:m, callback: draw_storm, lod:0) ); + return m; +} + +register_layer("storms", StormLayer); + diff --git a/Nasal/canvas/map/storms.model b/Nasal/canvas/map/storms.model new file mode 100644 index 000000000..0bc6447b2 --- /dev/null +++ b/Nasal/canvas/map/storms.model @@ -0,0 +1,31 @@ +var StormModel = {}; +StormModel.new = func make( LayerModel, StormModel ); + +StormModel.init = func { + me._view.reset(); # wraps removeAllChildren() ATM + + foreach (var n; props.globals.getNode("/instrumentation/wxradar",1).getChildren("storm")) { + # Model 3 degree radar beam + var stormLat = n.getNode("latitude-deg").getValue(); + stormLon = n.getNode("longitude-deg").getValue(); + acLat = getprop("/position/latitude-deg"); + acLon = getprop("/position/longitude-deg"); + stormGeo = geo.Coord.new(); + acGeo = geo.Coord.new(); + + stormGeo.set_latlon(stormLat, stormLon); + acGeo.set_latlon(acLat, acLon); + + var directDistance = acGeo.direct_distance_to(stormGeo); + beamH = 0.1719 * directDistance; # M2FT * tan(3deg) + beamBase = getprop("position/altitude-ft") - beamH; + + if (n.getNode("top-altitude-ft").getValue() > beamBase) { + me.push( { lat: stormLat, lon : stormLon, radiusNm : n.getNode("radius-nm").getValue() } ); + } + } + + me.notifyView(); +} + +