Canvas Map API
| IMPORTANT: Some, and possibly most, of the features/ideas discussed here are likely to be affected, and possibly even deprecated, by the ongoing work on providing a property tree-based 2D drawing API accessible from Nasal using the new Canvas system available since FlightGear 2.80 (08/2012). Please see: MapStructure for further information
You are advised not to start working on anything directly related to this without first discussing/coordinating your ideas with other FlightGear contributors using the FlightGear developers mailing list or the Canvas forum. Anything related to Canvas Core Development should be discussed first of all with TheTom and Zakalawe. Nasal-space frameworks are being maintained by Philosopher and Hooray currently. talk page..
| The FlightGear forum has a
subforum related to: Canvas
Update 11/2013: please see MapStructure
As of FlightGear 2.9, the base package contains additional Canvas helpers to help with the creation of navigational displays (map dialogs or map instruments). These are currently under development and will change rapidly during the next weeks and months, so that we can come up with a generic and re-usable design for different dialogs/instruments.
Heavily reduce the amount of specialized Canvas/Nasal code in XML dialogs and instruments related to mapping/charting (airport-selection, map widget, navdisplay etc) and ensure that a maximum degree of code sharing is accomplished, regardless of the canvas placement mode that is used - bottom line being, it really shouldn't matter if a map is shown as part of an airport selection dialog, as part of the moving map dialog, as part of the navdisplay etc.
The design follows the basic MVC (Model/View/Controller) principle, i.e.:
- the Model contains the data to be shown
- the View contains the Layer (Canvas group)
- the Controller contains the interface to update the model and the view (using timers and/or listeners)
The basic idea is such that each layer is linked to a "control" property to easily toggle its visibility (this can be a GUI property for a checkbox or a cockpit hotspot), drawables (symbols) can be put in different layers and easily toggled on/off.
As of 10/2012, the following "layers" are supported (not yet using MapStructure):
See the forum for details: http://forum.flightgear.org/viewtopic.php?f=71&t=21139
Originally, the idea was that we want to avoid the ongoing degree of copy/paste programming that could be seen in the airport selection dialog - next, the 744 ND duplicates lots of code used in the airport dialog, but also in the underlying framework - all without using inheritance or delegates/predicates to customize things. So over time, we would end up with tons of custom code that is highly specific to each aircraft/display and GUI dialog.
From a low-level point of view, it shouldn't matter to the draw routines if they're called by an aircraft instrument or by a GUI dialog. However, once they contain use-case specific logic, such as getprop() calls and other conditionals, things do get messy pretty quickly. Thus, what I have been doing is simply copying things to separate *.draw files for different scenarios, i.e. I now ended up with airport.draw and airports-nd.draw for example - that isn't all that elegant, but at least it solves the problem of having to implement full models and controllers for each scenario (well for now), because all the embedded logic can stay, and only needs to be refactored later on.
Thus, the idea is basically this:
- .draw files contain the low-level logic to draw arbitrary symbols (runway, waypoint, taxiways, airports, traffic) - without knowing anything about the frontend/user - so that such details needs to be separately encapsulated. The *.model files merely determine what is to be drawn, data-wise, as in NasalPositioned queries and their results - all stored in a single Nasal vector. The layer files turn everything into a dedicated canvas group that can be separately toggled/redrawn/updated - the currently missing bits are controllers that tie everything together, so that each frontend (instrument, MFD, GUI dialog, widget) can separately specify and customize behavior by registering their own callbacks.
Most of this is already working here (with very minor regressions so far that Gijs won't be too happy about ...), but otherwise I managed to turn Gijs code into layers/models that can be directly used by the old framework, i.e. I can now add a handful of new ND layers (fixes, vor, MP or AI traffic, route etc) to the airport selection dialog (or any other dialog actually), and things do show up properly.
What's still missing is the controller part, and styling for different aircraft/GUI purposes - also, Gijs' ND routines are currently used directly, without any LOD applied - but I think that can be easily solved by using the canvas's scale() method.
Just to clarify: stuff like getprop("instrumentation/nav/frequencies/selected-mhz") doesn't belong into the visualization part of the code (i.e. the draw/view stuff) - simply because that's the sort of stuff that would "break" once we're using a different use-case, e.g. the map dialog - which is such conditions would need to be either specified in some form of condition hash, as part of the draw* signature, or via a dedicated Drawable class that's able to evaluate such conditions.
To be honest, that was the whole point of moving stuff to different *.layer and *.model files - so that these *.draw files can be shared, without requiring any modifications - your current example would still require using differrent *.draw files for each purpose.
- Implementing proper LOD support for *.draw callbacks (i.e. using canvas/scaling)
- Implementing support for different styles
- Supporting multiple NDs per aircraft
- Supporting multiple maps per dialog
- Stress Test: render a dialog with 10 NDs, for 10 different MP/AI aircraft
- clean up map.nas, get rid of stubs and old code
- get rid of the PUI helpers in map.nas, and use real canvas dialogs instead
- Optimize (Nasal/Canvas and C++ space)
- come up with a framework for MFDs
- use the MFD framework for creating a stylable ND class