Howto:Prototyping an OANS using MapStructure

From FlightGear wiki
Jump to navigation Jump to search
This article is a stub. You can help the wiki by expanding it.
Screen shot showing an OANS prototype based on the Canvas/MapStructure system

Objective

Status

Due to lack of apt.dat v850 support, most airports show only up as runways.[1]

Last updated: 08/2021

The latest apt.dat format is 1100. genapts already supports up to a fairly recent version. Functionally identical to 850, it has several new lighting and linear features (available since 2019.2 in terragear already, thanks to D-ECHO) and improvements to ATC / ground services.[2]

The newest specification is available at: http://developer.x-plane.com/wp-content/uploads/2019/04/XP-APT-1100-Spec_revised_04-11-2019.pdf

X-Plane also now makes extensive use of so-called "polygons". This causes problems when authors use them for aprons or taxiways despite X-Plane's efforts to discourage this. Stuggart in TerraSync is a good example and a fairly substantial number of airports, and a majority of major ones would be unusable using pure apt.dat at present.

This would either require manual conversion of polygons to taxiways (one click per airport using World Editor, not multiple airports at once however), or else parsing of the .dsf format.

The .dsf format is a binary format, with each .dsf corresponding to a scenery tile (should be 1 lat / lon divisions). The file may be extracted to a text file using the XPTools commandline toolset provided by Laminar. This is feasible for large .dsf files (one per scenery tile - still 64,800 files, however).

Specification: https://developer.x-plane.com/article/dsf-file-format-specification/#DSF_Specification


Our navdb only parses the (old) 810 surface format. The rectangular blocks of taxiway in your screenshot indicate that that specific airport is in 810 format. Airports with polygon surfaces (850 format) therefore only show the runways, since the polygon surfaces are simply not available to the canvas layer. I believe the airport preview in the Qt launcher has the same limitation, as it uses the same navdb.[3]

Background

MapStructure is all about identifying static elements and dynamic ones - to selectively update things that change, unlike things that don't change.

So the next step is to logically divide a "map" into a set of "layers", that contain layer-specific elements/information (think taxiways, runways, buildings, compass rose, text labels) - as a rule of thumb, anything that can be toggled on/off separately, could go into its own layer (for the sake of simplicity). Also, anything that responds to scale (LOD) needs be handled correctly - e.g., switching the range of map display, does not invalidate the previous data (model).

Basically, the taxiway layout of the airport no longer changes once it's initialized - so you primarily need one layer for the taxiways, and use other layers for more dynamic stuff.

We developed MapStructure so that people can easily reuse existing layers. The idea was to allow people to find related layers and only adapt what's needed. In this case, I would use the taxiway layer or ThomasS' ground services layers to get started quickly - and then copy/adapt those layers as needed, styling I would add once all other functionality is working.

Example

    var (width,height) = (640,480);
    var title = 'Canvas OANS experiments (by Nia)';

    var window = canvas.Window.new([width,height],"dialog").set('title',title);
    
    # adding a canvas to the new window and setting up background colors/transparency
    var myCanvas = window.createCanvas().set("background", canvas.style.getColor("bg_color"));

    # creating the top-level/root group which will contain all other elements/group
    var root = myCanvas.createGroup();

    var TestMap = root.createChild("map");
    
    var AirportChart = TestMap; # copy/paste adaptation
    
    
    
# Initialize the controller:
AirportChart.setController("Static position", "main");
var controller = AirportChart.getController();

 var info = airportinfo("KNUQ");
AirportChart.getController().setPosition(info.lat, info.lon);

AirportChart.setRange(3.0);
AirportChart.setScreenRange(800);


   
     
    TestMap.setTranslation(    myCanvas.get("view[0]")/2,
                               myCanvas.get("view[1]")/2
                            );
         
   ##
   # Styling: 
   var Styles = {};
   Styles.get = func(type) return Styles[type];
   var Options = {};
   Options.get = func(type) return Options[type];
     
    Styles.APT = {};
    Styles.APT.scale_factor = 0.4; # 40% (applied to whole group)
    Styles.APT.line_width = 3.0;
    Styles.APT.color_default = [0,0.6,0.85];  #rgb
    Styles.APT.label_font_color = Styles.APT.color_default;
    Styles.APT.label_font_size=28;
              
                            
    Styles.PARKING = {};
    Styles.PARKING.scale_factor = 0.4; # 40% (applied to whole group)
    Styles.PARKING.line_width = 3.0;
    Styles.PARKING.color_default = [0,0.85,0.6];  #rgb
    Styles.PARKING.label_font_color = Styles.APT.color_default;
    Styles.PARKING.label_font_size=28;
                        
    Styles.APS = {};
    Styles.APS.scale_factor = 0.25;
    Styles.APS.color_default = [1,0,0];


    # this sets up a namespace for each layer with associated meta information                        
    var r = func(name,vis=1,zindex=nil) return caller(0)[0];

    foreach(var type; [r('TAXI',1,0),r('RWY',1,1),r('TWR',1,2),r('PARKING',0,3),r('APS',1,4)] ) 
    TestMap.addLayer(	factory: canvas.SymbolLayer, 
     					type_arg: type.name, 
     					visible: type.vis, 
     					priority: type.zindex,                                        
     					style: Styles.get(type.name),
     					options: Options.get(type.name) );

References

References