Canvas MapLayers for Airport
Add RWY, TAXI, TWR, PARKING map layers Add new static position controller Update Select Airport dialog to use new MapLayers.
This commit is contained in:
parent
3283e9721f
commit
d84c527ca7
11 changed files with 753 additions and 72 deletions
|
@ -450,7 +450,7 @@ var Group = {
|
||||||
|
|
||||||
# Map
|
# Map
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
# Class for a group element on a canvas with possibly geopgraphic positions
|
# Class for a group element on a canvas with possibly geographic positions
|
||||||
# which automatically get projected according to the specified projection.
|
# which automatically get projected according to the specified projection.
|
||||||
# Each map consists of an arbitrary number of layers (canvas groups)
|
# Each map consists of an arbitrary number of layers (canvas groups)
|
||||||
#
|
#
|
||||||
|
@ -502,6 +502,10 @@ var Map = {
|
||||||
|
|
||||||
return me;
|
return me;
|
||||||
},
|
},
|
||||||
|
getController: func()
|
||||||
|
{
|
||||||
|
return me.controller;
|
||||||
|
},
|
||||||
addLayer: func(factory, type_arg=nil, priority=nil, style=nil, opts=nil, visible=1)
|
addLayer: func(factory, type_arg=nil, priority=nil, style=nil, opts=nil, visible=1)
|
||||||
{
|
{
|
||||||
if(contains(me.layers, type_arg))
|
if(contains(me.layers, type_arg))
|
||||||
|
|
48
Nasal/canvas/map/PARKING.lcontroller
Normal file
48
Nasal/canvas/map/PARKING.lcontroller
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
# See: http://wiki.flightgear.org/MapStructure
|
||||||
|
# Class things:
|
||||||
|
var name = 'PARKING';
|
||||||
|
var parents = [SymbolLayer.Controller];
|
||||||
|
var __self__ = caller(0)[0];
|
||||||
|
SymbolLayer.Controller.add(name, __self__);
|
||||||
|
SymbolLayer.add(name, {
|
||||||
|
parents: [MultiSymbolLayer],
|
||||||
|
type: name, # Symbol type
|
||||||
|
df_controller: __self__, # controller to use by default -- this one
|
||||||
|
df_style: {
|
||||||
|
line_width: 3,
|
||||||
|
scale_factor: 1,
|
||||||
|
debug: 1,
|
||||||
|
color_default: [0,0.85,0.6],
|
||||||
|
label_font_color:[0,0.85,0.6],
|
||||||
|
label_font_size: 28,
|
||||||
|
text_offset: [10,0],
|
||||||
|
svg_path: nil
|
||||||
|
},
|
||||||
|
df_options: { # default configuration options
|
||||||
|
disable_position: 1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
var a_instance = nil;
|
||||||
|
var new = func(layer) {
|
||||||
|
var m = {
|
||||||
|
parents: [__self__],
|
||||||
|
layer: layer,
|
||||||
|
map: layer.map,
|
||||||
|
listeners: [],
|
||||||
|
};
|
||||||
|
m.addVisibilityListener();
|
||||||
|
|
||||||
|
return m;
|
||||||
|
};
|
||||||
|
var del = func() {
|
||||||
|
#print(name,".lcontroller.del()");
|
||||||
|
foreach (var l; me.listeners)
|
||||||
|
removelistener(l);
|
||||||
|
};
|
||||||
|
|
||||||
|
var searchCmd = func {
|
||||||
|
#print("Running query:", name);
|
||||||
|
var range = me.map.getRange();
|
||||||
|
if (range == nil) return;
|
||||||
|
return positioned.findAirportsWithinRange(me.map.getPosCoord(), range);
|
||||||
|
};
|
46
Nasal/canvas/map/PARKING.symbol
Normal file
46
Nasal/canvas/map/PARKING.symbol
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
# See: http://wiki.flightgear.org/MapStructure
|
||||||
|
# Class things:
|
||||||
|
var name = 'PARKING';
|
||||||
|
var parents = [DotSym];
|
||||||
|
var __self__ = caller(0)[0];
|
||||||
|
DotSym.makeinstance( name, __self__ );
|
||||||
|
|
||||||
|
var element_type = "group"; # we want a group, becomes "me.element"
|
||||||
|
var rwys = nil;
|
||||||
|
|
||||||
|
var init = func {
|
||||||
|
var apt=airportinfo(me.model.id);
|
||||||
|
var style = me.layer.style;
|
||||||
|
var svg_path = style.svg_path;
|
||||||
|
|
||||||
|
var group = me.element.createChild("group", "parking-"~apt.id);
|
||||||
|
foreach(var park; apt.parking()) {
|
||||||
|
var p = me.element.createChild("group", "parking-"~park.name);
|
||||||
|
p.setGeoPosition(park.lat, park.lon);
|
||||||
|
|
||||||
|
if (svg_path != nil) {
|
||||||
|
canvas.parsesvg(p, svg_path);
|
||||||
|
} else {
|
||||||
|
p.createChild("path", name ~ " icon" )
|
||||||
|
.moveTo(-10,-10)
|
||||||
|
.lineTo(10,10)
|
||||||
|
.moveTo(10,-10)
|
||||||
|
.lineTo(-10,10)
|
||||||
|
.close()
|
||||||
|
.setColor(style.color_default)
|
||||||
|
.setStrokeLineWidth(style.line_width)
|
||||||
|
.setScale(style.scale_factor);
|
||||||
|
}
|
||||||
|
|
||||||
|
p.createChild("text", "parking-" ~ park.name)
|
||||||
|
.setDrawMode( canvas.Text.ALIGNMENT
|
||||||
|
+ canvas.Text.TEXT )
|
||||||
|
.setTranslation([style.scale_factor * style.text_offset[0], style.scale_factor * style.text_offset[1]])
|
||||||
|
.setText(park.name)
|
||||||
|
.setFont("LiberationFonts/LiberationSans-Regular.ttf")
|
||||||
|
.setColor(style.label_font_color)
|
||||||
|
.setFontSize(style.label_font_size, 1.3)
|
||||||
|
.setScale(style.scale_factor);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var draw = func;
|
41
Nasal/canvas/map/RWY.lcontroller
Normal file
41
Nasal/canvas/map/RWY.lcontroller
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
# See: http://wiki.flightgear.org/MapStructure
|
||||||
|
# Class things:
|
||||||
|
var name = 'RWY';
|
||||||
|
var parents = [SymbolLayer.Controller];
|
||||||
|
var __self__ = caller(0)[0];
|
||||||
|
SymbolLayer.Controller.add(name, __self__);
|
||||||
|
SymbolLayer.add(name, {
|
||||||
|
parents: [MultiSymbolLayer],
|
||||||
|
type: name, # Symbol type
|
||||||
|
df_controller: __self__, # controller to use by default -- this one
|
||||||
|
df_style: {
|
||||||
|
surface_color: canvas.SURFACECOLORS
|
||||||
|
},
|
||||||
|
df_options: { # default configuration options
|
||||||
|
disable_position: 1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
var a_instance = nil;
|
||||||
|
var new = func(layer) {
|
||||||
|
var m = {
|
||||||
|
parents: [__self__],
|
||||||
|
layer: layer,
|
||||||
|
map: layer.map,
|
||||||
|
listeners: [],
|
||||||
|
};
|
||||||
|
m.addVisibilityListener();
|
||||||
|
|
||||||
|
return m;
|
||||||
|
};
|
||||||
|
var del = func() {
|
||||||
|
#print(name,".lcontroller.del()");
|
||||||
|
foreach (var l; me.listeners)
|
||||||
|
removelistener(l);
|
||||||
|
};
|
||||||
|
|
||||||
|
var searchCmd = func {
|
||||||
|
#print("Running query:", name);
|
||||||
|
var range = me.map.getRange();
|
||||||
|
if (range == nil) return;
|
||||||
|
return positioned.findAirportsWithinRange(me.map.getPosCoord(), range);
|
||||||
|
};
|
137
Nasal/canvas/map/RWY.symbol
Normal file
137
Nasal/canvas/map/RWY.symbol
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
# See: http://wiki.flightgear.org/MapStructure
|
||||||
|
# Class things:
|
||||||
|
var name = 'RWY';
|
||||||
|
var parents = [DotSym];
|
||||||
|
var __self__ = caller(0)[0];
|
||||||
|
DotSym.makeinstance( name, __self__ );
|
||||||
|
|
||||||
|
var element_type = "group"; # we want a group, becomes "me.element"
|
||||||
|
var rwys = nil;
|
||||||
|
|
||||||
|
var SURFACECOLORS = {
|
||||||
|
1 : { type: "asphalt", r:0.2, g:0.2, b:0.2 },
|
||||||
|
2 : { type: "concrete", r:0.3, g:0.3, b:0.3 },
|
||||||
|
3 : { type: "turf", r:0.2, g:0.5, b:0.2 },
|
||||||
|
4 : { type: "dirt", r:0.4, g:0.3, b:0.3 },
|
||||||
|
5 : { type: "gravel", r:0.35, g:0.3, b:0.3 },
|
||||||
|
# Helipads
|
||||||
|
6 : { type: "asphalt", r:0.2, g:0.2, b:0.2 },
|
||||||
|
7 : { type: "concrete", r:0.3, g:0.3, b:0.3 },
|
||||||
|
8 : { type: "turf", r:0.2, g:0.5, b:0.2 },
|
||||||
|
9 : { type: "dirt", r:0.4, g:0.3, b:0.3 },
|
||||||
|
0 : { type: "gravel", r:0.35, g:0.3, b:0.3 },
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var init = func {
|
||||||
|
var apt=airportinfo(me.model.id);
|
||||||
|
var rwys = apt.runwaysWithoutReciprocals();
|
||||||
|
|
||||||
|
foreach (var rw1; rwys)
|
||||||
|
{
|
||||||
|
var clr = me.style.surface_color[rw1.surface];
|
||||||
|
if (clr == nil) { clr = SURFACECOLORS[rw1.surface]};
|
||||||
|
if (clr == nil) { clr = SURFACECOLORS[0]};
|
||||||
|
|
||||||
|
var icon_rw =
|
||||||
|
me.element.createChild("path", "runway-" ~ rw1.id)
|
||||||
|
.setStrokeLineWidth(0.5)
|
||||||
|
.setColor(1.0,1.0,1.0)
|
||||||
|
.setColorFill(clr.r, clr.g, clr.b);
|
||||||
|
|
||||||
|
var rwy1 = Runway.new(rw1);
|
||||||
|
var beg_thr = rwy1.pointOffCenterline(rw1.threshold);
|
||||||
|
var beg_thr1 = rwy1.pointOffCenterline(rw1.threshold, 0.5 * rw1.width);
|
||||||
|
var beg_thr2 = rwy1.pointOffCenterline(rw1.threshold, -0.5 * rw1.width);
|
||||||
|
var beg1 = rwy1.pointOffCenterline(0, 0.5 * rw1.width);
|
||||||
|
var beg2 = rwy1.pointOffCenterline(0, -0.5 * rw1.width);
|
||||||
|
|
||||||
|
var rw2 = rw1.reciprocal;
|
||||||
|
var rwy2 = Runway.new(rw2);
|
||||||
|
var end_thr = rwy2.pointOffCenterline(rw2.threshold);
|
||||||
|
var end_thr1 = rwy2.pointOffCenterline(rw2.threshold, 0.5 * rw2.width);
|
||||||
|
var end_thr2 = rwy2.pointOffCenterline(rw2.threshold, -0.5 * rw2.width);
|
||||||
|
var end1 = rwy2.pointOffCenterline(0, 0.5 * rw2.width);
|
||||||
|
var end2 = rwy2.pointOffCenterline(0, -0.5 * rw2.width);
|
||||||
|
|
||||||
|
icon_rw.setDataGeo
|
||||||
|
(
|
||||||
|
[ canvas.Path.VG_MOVE_TO,
|
||||||
|
canvas.Path.VG_LINE_TO,
|
||||||
|
canvas.Path.VG_LINE_TO,
|
||||||
|
canvas.Path.VG_LINE_TO,
|
||||||
|
canvas.Path.VG_CLOSE_PATH ],
|
||||||
|
[ beg1[0], beg1[1],
|
||||||
|
beg2[0], beg2[1],
|
||||||
|
end1[0], end1[1],
|
||||||
|
end2[0], end2[1] ]
|
||||||
|
);
|
||||||
|
|
||||||
|
var icon_cl =
|
||||||
|
me.element.createChild("path", "centerline")
|
||||||
|
.setStrokeLineWidth(0.5)
|
||||||
|
.setColor(1,1,1)
|
||||||
|
.setStrokeDashArray([15, 10]);
|
||||||
|
|
||||||
|
icon_cl.setDataGeo
|
||||||
|
(
|
||||||
|
[ canvas.Path.VG_MOVE_TO,
|
||||||
|
canvas.Path.VG_LINE_TO ],
|
||||||
|
[ beg_thr[0], beg_thr[1],
|
||||||
|
end_thr[0], end_thr[1] ]
|
||||||
|
);
|
||||||
|
|
||||||
|
var icon_thr =
|
||||||
|
me.element.createChild("path", "threshold")
|
||||||
|
.setStrokeLineWidth(1.5)
|
||||||
|
.setColor(1,1,1);
|
||||||
|
|
||||||
|
icon_thr.setDataGeo
|
||||||
|
(
|
||||||
|
[ canvas.Path.VG_MOVE_TO,
|
||||||
|
canvas.Path.VG_LINE_TO,
|
||||||
|
canvas.Path.VG_MOVE_TO,
|
||||||
|
canvas.Path.VG_LINE_TO ],
|
||||||
|
[ beg_thr1[0], beg_thr1[1],
|
||||||
|
beg_thr2[0], beg_thr2[1],
|
||||||
|
end_thr1[0], end_thr1[1],
|
||||||
|
end_thr2[0], end_thr2[1] ]
|
||||||
|
);
|
||||||
|
|
||||||
|
#draw helipads
|
||||||
|
foreach(var hp; keys(apt.helipads))
|
||||||
|
{
|
||||||
|
var hp = apt.runway(hp);
|
||||||
|
var clr = me.style.surface_color[hp.surface];
|
||||||
|
if (clr == nil) { clr = SURFACECOLORS[hp.surface]};
|
||||||
|
if (clr == nil) { clr = SURFACECOLORS[0]};
|
||||||
|
|
||||||
|
var icon_hp =
|
||||||
|
me.element.createChild("path", "helipad-" ~ hp.id)
|
||||||
|
.setStrokeLineWidth(0.5)
|
||||||
|
.setColor(1.0,1.0,1.0)
|
||||||
|
.setColorFill(clr.r, clr.g, clr.b);
|
||||||
|
|
||||||
|
var heli = Runway.new(hp);
|
||||||
|
var p1 = heli.pointOffCenterline(0.5 * hp.length, 0.5 * hp.width);
|
||||||
|
var p2 = heli.pointOffCenterline(0.5 * hp.length, -0.5 * hp.width);
|
||||||
|
var p3 = heli.pointOffCenterline(-0.5 * hp.length, -0.5 * hp.width);
|
||||||
|
var p4 = heli.pointOffCenterline(-0.5 * hp.length, 0.5 * hp.width);
|
||||||
|
|
||||||
|
icon_hp.setDataGeo
|
||||||
|
(
|
||||||
|
[ canvas.Path.VG_MOVE_TO,
|
||||||
|
canvas.Path.VG_LINE_TO,
|
||||||
|
canvas.Path.VG_LINE_TO,
|
||||||
|
canvas.Path.VG_LINE_TO,
|
||||||
|
canvas.Path.VG_CLOSE_PATH ],
|
||||||
|
[ p1[0], p1[1],
|
||||||
|
p2[0], p2[1],
|
||||||
|
p3[0], p3[1],
|
||||||
|
p4[0], p4[1] ]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var draw = func;
|
41
Nasal/canvas/map/TAXI.lcontroller
Normal file
41
Nasal/canvas/map/TAXI.lcontroller
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
# See: http://wiki.flightgear.org/MapStructure
|
||||||
|
# Class things:
|
||||||
|
var name = 'TAXI';
|
||||||
|
var parents = [SymbolLayer.Controller];
|
||||||
|
var __self__ = caller(0)[0];
|
||||||
|
SymbolLayer.Controller.add(name, __self__);
|
||||||
|
SymbolLayer.add(name, {
|
||||||
|
parents: [MultiSymbolLayer],
|
||||||
|
type: name, # Symbol type
|
||||||
|
df_controller: __self__, # controller to use by default -- this one
|
||||||
|
df_style: {
|
||||||
|
surface_color: canvas.SURFACECOLORS,
|
||||||
|
},
|
||||||
|
df_options: { # default configuration options
|
||||||
|
disable_position: 1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
var a_instance = nil;
|
||||||
|
var new = func(layer) {
|
||||||
|
var m = {
|
||||||
|
parents: [__self__],
|
||||||
|
layer: layer,
|
||||||
|
map: layer.map,
|
||||||
|
listeners: [],
|
||||||
|
};
|
||||||
|
m.addVisibilityListener();
|
||||||
|
|
||||||
|
return m;
|
||||||
|
};
|
||||||
|
var del = func() {
|
||||||
|
#print(name,".lcontroller.del()");
|
||||||
|
foreach (var l; me.listeners)
|
||||||
|
removelistener(l);
|
||||||
|
};
|
||||||
|
|
||||||
|
var searchCmd = func {
|
||||||
|
#print("Running query:", name);
|
||||||
|
var range = me.map.getRange();
|
||||||
|
if (range == nil) return;
|
||||||
|
return positioned.findAirportsWithinRange(me.map.getPosCoord(), range);
|
||||||
|
};
|
60
Nasal/canvas/map/TAXI.symbol
Normal file
60
Nasal/canvas/map/TAXI.symbol
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
# See: http://wiki.flightgear.org/MapStructure
|
||||||
|
# Class things:
|
||||||
|
var name = 'TAXI';
|
||||||
|
var parents = [DotSym];
|
||||||
|
var __self__ = caller(0)[0];
|
||||||
|
DotSym.makeinstance( name, __self__ );
|
||||||
|
|
||||||
|
var element_type = "group"; # we want a group, becomes "me.element"
|
||||||
|
var rwys = nil;
|
||||||
|
|
||||||
|
var SURFACECOLORS = {
|
||||||
|
1 : { type: "asphalt", r:0.2, g:0.2, b:0.2 },
|
||||||
|
2 : { type: "concrete", r:0.3, g:0.3, b:0.3 },
|
||||||
|
3 : { type: "turf", r:0.2, g:0.5, b:0.2 },
|
||||||
|
4 : { type: "dirt", r:0.4, g:0.3, b:0.3 },
|
||||||
|
5 : { type: "gravel", r:0.35, g:0.3, b:0.3 },
|
||||||
|
# Helipads
|
||||||
|
6 : { type: "asphalt", r:0.2, g:0.2, b:0.2 },
|
||||||
|
7 : { type: "concrete", r:0.3, g:0.3, b:0.3 },
|
||||||
|
8 : { type: "turf", r:0.2, g:0.5, b:0.2 },
|
||||||
|
9 : { type: "dirt", r:0.4, g:0.3, b:0.3 },
|
||||||
|
0 : { type: "gravel", r:0.35, g:0.3, b:0.3 },
|
||||||
|
};
|
||||||
|
|
||||||
|
var init = func {
|
||||||
|
var apt=airportinfo(me.model.id);
|
||||||
|
foreach (var taxi; apt.taxiways)
|
||||||
|
{
|
||||||
|
var clr = me.style.surface_color[taxi.surface];
|
||||||
|
if (clr == nil) { clr = SURFACECOLORS[taxi.surface]};
|
||||||
|
if (clr == nil) { clr = SURFACECOLORS[0]};
|
||||||
|
|
||||||
|
var taxi_paths =
|
||||||
|
me.element.createChild("path", "runway-" ~ taxi.id)
|
||||||
|
.setStrokeLineWidth(0.5)
|
||||||
|
.setColor(clr.r, clr.g, clr.b)
|
||||||
|
.setColorFill(clr.r, clr.g, clr.b);
|
||||||
|
|
||||||
|
var txi = Runway.new(taxi);
|
||||||
|
var beg1 = txi.pointOffCenterline(0, 0.5 * taxi.width);
|
||||||
|
var beg2 = txi.pointOffCenterline(0, -0.5 * taxi.width);
|
||||||
|
var end1 = txi.pointOffCenterline(taxi.length, 0.5 * taxi.width);
|
||||||
|
var end2 = txi.pointOffCenterline(taxi.length, -0.5 * taxi.width);
|
||||||
|
|
||||||
|
taxi_paths.setColorFill(clr.r, clr.g, clr.b)
|
||||||
|
.setDataGeo
|
||||||
|
(
|
||||||
|
[ canvas.Path.VG_MOVE_TO,
|
||||||
|
canvas.Path.VG_LINE_TO,
|
||||||
|
canvas.Path.VG_LINE_TO,
|
||||||
|
canvas.Path.VG_LINE_TO,
|
||||||
|
canvas.Path.VG_CLOSE_PATH ],
|
||||||
|
[ beg1[0], beg1[1],
|
||||||
|
beg2[0], beg2[1],
|
||||||
|
end2[0], end2[1],
|
||||||
|
end1[0], end1[1] ]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var draw = func;
|
42
Nasal/canvas/map/TWR.lcontroller
Normal file
42
Nasal/canvas/map/TWR.lcontroller
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
# See: http://wiki.flightgear.org/MapStructure
|
||||||
|
# Class things:
|
||||||
|
var name = 'TWR';
|
||||||
|
var parents = [SymbolLayer.Controller];
|
||||||
|
var __self__ = caller(0)[0];
|
||||||
|
SymbolLayer.Controller.add(name, __self__);
|
||||||
|
SymbolLayer.add(name, {
|
||||||
|
parents: [MultiSymbolLayer],
|
||||||
|
type: name, # Symbol type
|
||||||
|
df_controller: __self__, # controller to use by default -- this one
|
||||||
|
df_style: {
|
||||||
|
color: { r:0.2, g:0.2, b:1.2 },
|
||||||
|
line_width: 1,
|
||||||
|
},
|
||||||
|
df_options: { # default configuration options
|
||||||
|
disable_position: 1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
var a_instance = nil;
|
||||||
|
var new = func(layer) {
|
||||||
|
var m = {
|
||||||
|
parents: [__self__],
|
||||||
|
layer: layer,
|
||||||
|
map: layer.map,
|
||||||
|
listeners: [],
|
||||||
|
};
|
||||||
|
m.addVisibilityListener();
|
||||||
|
|
||||||
|
return m;
|
||||||
|
};
|
||||||
|
var del = func() {
|
||||||
|
#print(name,".lcontroller.del()");
|
||||||
|
foreach (var l; me.listeners)
|
||||||
|
removelistener(l);
|
||||||
|
};
|
||||||
|
|
||||||
|
var searchCmd = func {
|
||||||
|
#print("Running query:", name);
|
||||||
|
var range = me.map.getRange();
|
||||||
|
if (range == nil) return;
|
||||||
|
return positioned.findAirportsWithinRange(me.map.getPosCoord(), range);
|
||||||
|
};
|
31
Nasal/canvas/map/TWR.symbol
Normal file
31
Nasal/canvas/map/TWR.symbol
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
# See: http://wiki.flightgear.org/MapStructure
|
||||||
|
# Class things:
|
||||||
|
var name = 'TWR';
|
||||||
|
var parents = [DotSym];
|
||||||
|
var __self__ = caller(0)[0];
|
||||||
|
DotSym.makeinstance( name, __self__ );
|
||||||
|
|
||||||
|
var element_type = "group"; # we want a group, becomes "me.element"
|
||||||
|
|
||||||
|
var init = func {
|
||||||
|
var apt=airportinfo(me.model.id);
|
||||||
|
|
||||||
|
var clr = me.style.color;
|
||||||
|
var line_width = me.style.line_width;
|
||||||
|
|
||||||
|
|
||||||
|
var icon_tower =
|
||||||
|
me.element.createChild("path", "tower")
|
||||||
|
.setStrokeLineWidth(line_width)
|
||||||
|
.setScale(1.5)
|
||||||
|
.setColor(clr.r, clr.g, clr.b)
|
||||||
|
.moveTo(-3, 0)
|
||||||
|
.vert(-10)
|
||||||
|
.line(-3, -10)
|
||||||
|
.horiz(12)
|
||||||
|
.line(-3, 10)
|
||||||
|
.vert(10);
|
||||||
|
|
||||||
|
icon_tower.setGeoPosition(apt.tower().lat, apt.tower().lon);
|
||||||
|
};
|
||||||
|
var draw = func;
|
74
Nasal/canvas/map/staticpos.controller
Normal file
74
Nasal/canvas/map/staticpos.controller
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
# Class things:
|
||||||
|
var parents = [Map.Controller];
|
||||||
|
var __self__ = caller(0)[0];
|
||||||
|
Map.Controller.add("Static position", __self__);
|
||||||
|
#Map.df_controller = __self__;
|
||||||
|
|
||||||
|
##
|
||||||
|
# A controller to handle static updates of position, for example an airport
|
||||||
|
# diagram or non-moving map.
|
||||||
|
#
|
||||||
|
# Note that in contrast to the Aircraft Controllers, update_pos() or
|
||||||
|
# update_layers() must be called explicitly to trigger an update of the map.
|
||||||
|
##
|
||||||
|
|
||||||
|
var new = func(map, source='main') {
|
||||||
|
var m = {
|
||||||
|
parents: [__self__],
|
||||||
|
map: map,
|
||||||
|
_pos: nil, _time: nil, _range: nil,
|
||||||
|
_alt: 0, _hdg: 0,
|
||||||
|
};
|
||||||
|
m._pos = geo.Coord.new();
|
||||||
|
m._pos.set_latlon(0.0,0.0);
|
||||||
|
m.update_pos();
|
||||||
|
return m;
|
||||||
|
};
|
||||||
|
|
||||||
|
var del = func(map) {
|
||||||
|
if (map != me.map) die();
|
||||||
|
};
|
||||||
|
|
||||||
|
var setHeading = func(hdg) { me._hdg = hdg; };
|
||||||
|
var setAltitude = func(alt) { me._alt = alt; };
|
||||||
|
var getHeading = func() { return me._hdg; };
|
||||||
|
var getAltitude = func() { return me._alt; };
|
||||||
|
var setPosition = func(lat, lon) {
|
||||||
|
me._pos.set_latlon(lat, lon);
|
||||||
|
me.update_pos();
|
||||||
|
};
|
||||||
|
|
||||||
|
var applyOffset = func(x, y) {
|
||||||
|
# Apply an offset in screen coordinates, e.g. from a mouse event
|
||||||
|
var crs = 0.0;
|
||||||
|
if (x != 0.0) {
|
||||||
|
# Calculate course in degrees
|
||||||
|
crs = 90.0 + 180.0 / math.pi * math.atan(y/x);
|
||||||
|
if (x < 0.0 ) crs = crs + 180.0;
|
||||||
|
} else {
|
||||||
|
if (y < 0.0) crs = 0.0;
|
||||||
|
if (y > 0.0) crs = 180.0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Screen resolution m/pixel is range/screen_range
|
||||||
|
var screen_range = me.map.getScreenRange() or 200;
|
||||||
|
var screen_resolution = me.map.getRange() * globals.NM2M / screen_range;
|
||||||
|
me._pos.apply_course_distance(crs, math.sqrt(x*x + y*y) * screen_resolution);
|
||||||
|
me.update_pos();
|
||||||
|
};
|
||||||
|
|
||||||
|
# Controller methods
|
||||||
|
var update_pos = func {
|
||||||
|
me.map.setPos(lat: me._pos.lat(), lon: me._pos.lon(),
|
||||||
|
hdg: me._hdg,,
|
||||||
|
alt: me._alt,);
|
||||||
|
me.map.update();
|
||||||
|
};
|
||||||
|
|
||||||
|
var update_layers = func {
|
||||||
|
me.map.update();
|
||||||
|
};
|
||||||
|
|
||||||
|
var get_position = func {
|
||||||
|
return [ me._pos.lat(), me._pos.lon() ];
|
||||||
|
}
|
|
@ -42,15 +42,16 @@
|
||||||
var MAX_RUNWAYS = 28; # number of entries at KEDW
|
var MAX_RUNWAYS = 28; # number of entries at KEDW
|
||||||
|
|
||||||
var DIALOG = cmdarg();
|
var DIALOG = cmdarg();
|
||||||
|
var listeners = [];
|
||||||
|
|
||||||
## "prologue" currently required by the canvas-generic-map
|
## "prologue" currently required by the canvas-generic-map
|
||||||
var dialog_name ="airports"; #TODO: use substr() and cmdarg() to get this dynamically
|
var dialog_name ="airports"; #TODO: use substr() and cmdarg() to get this dynamically
|
||||||
var dialog_property = func(p) return "/sim/gui/dialogs/airports/"~p; #TODO: generalize using cmdarg
|
var dialog_property = func(p) return "/sim/gui/dialogs/airports/"~p; #TODO: generalize using cmdarg
|
||||||
var DIALOG_CANVAS = gui.findElementByName(DIALOG, "airport-selection");
|
var DIALOG_CANVAS = gui.findElementByName(DIALOG, "airport-selection");
|
||||||
canvas.GenericMap.setupGUI(DIALOG_CANVAS, "canvas-control"); #TODO: this is not a method!
|
|
||||||
## end of canvas-generic-map prologue
|
|
||||||
|
|
||||||
|
|
||||||
|
setprop("/sim/gui/dialogs/airports/selected-airport/lat", 0);
|
||||||
|
setprop("/sim/gui/dialogs/airports/selected-airport/lon", 0);
|
||||||
setprop("/sim/gui/dialogs/airports/selected-airport/rwy", "");
|
setprop("/sim/gui/dialogs/airports/selected-airport/rwy", "");
|
||||||
setprop("/sim/gui/dialogs/airports/selected-airport/parkpos", "");
|
setprop("/sim/gui/dialogs/airports/selected-airport/parkpos", "");
|
||||||
setprop("/sim/gui/dialogs/airports/mode", "search");
|
setprop("/sim/gui/dialogs/airports/mode", "search");
|
||||||
|
@ -115,9 +116,11 @@
|
||||||
setprop("/sim/gui/dialogs/airports/selected-airport/name", info.name);
|
setprop("/sim/gui/dialogs/airports/selected-airport/name", info.name);
|
||||||
setprop("/sim/gui/dialogs/airports/selected-airport/location", sprintf("%.3f / %.3f", info.lat, info.lon));
|
setprop("/sim/gui/dialogs/airports/selected-airport/location", sprintf("%.3f / %.3f", info.lat, info.lon));
|
||||||
setprop("/sim/gui/dialogs/airports/selected-airport/lon", info.lon);
|
setprop("/sim/gui/dialogs/airports/selected-airport/lon", info.lon);
|
||||||
|
setprop("/sim/gui/dialogs/airports/selected-airport/lat", info.lat);
|
||||||
setprop("/sim/gui/dialogs/airports/selected-airport/elevation-ft", 3.28 * info.elevation);
|
setprop("/sim/gui/dialogs/airports/selected-airport/elevation-ft", 3.28 * info.elevation);
|
||||||
setprop("/sim/gui/dialogs/airports/selected-airport/rwy", "");
|
setprop("/sim/gui/dialogs/airports/selected-airport/rwy", "");
|
||||||
setprop("/sim/gui/dialogs/airports/selected-airport/parkpos", "");
|
setprop("/sim/gui/dialogs/airports/selected-airport/parkpos", "");
|
||||||
|
AirportChart.getController().setPosition(info.lat, info.lon);
|
||||||
|
|
||||||
if (info.has_metar) {
|
if (info.has_metar) {
|
||||||
# Retrieve an updated METAR, and indicate that we've not got one currently.
|
# Retrieve an updated METAR, and indicate that we've not got one currently.
|
||||||
|
@ -247,11 +250,15 @@
|
||||||
canvas.register_callback(update_info); # FIXME: this is a workaround to run dialog-specific code in the canvas block
|
canvas.register_callback(update_info); # FIXME: this is a workaround to run dialog-specific code in the canvas block
|
||||||
]]>
|
]]>
|
||||||
</open>
|
</open>
|
||||||
<close>
|
<close><![CDATA[
|
||||||
fgcommand("clear-metar", var n = props.Node.new({ "path": "/sim/gui/dialogs/airports/selected-airport/metar",
|
fgcommand("clear-metar", var n = props.Node.new({ "path": "/sim/gui/dialogs/airports/selected-airport/metar",
|
||||||
"station": airport_id}));
|
"station": airport_id}));
|
||||||
map.cleanup_listeners(); #TODO: We should be setting a signal when closing the dialog, so that cleanup code can be invoked automatically
|
#map.cleanup_listeners(); #TODO: We should be setting a signal when closing the dialog, so that cleanup code can be invoked automatically
|
||||||
</close>
|
AirportChart.del();
|
||||||
|
foreach (var l; listeners)
|
||||||
|
removelistener(l);
|
||||||
|
setsize(listeners, 0);
|
||||||
|
]]></close>
|
||||||
</nasal>
|
</nasal>
|
||||||
|
|
||||||
<group>
|
<group>
|
||||||
|
@ -648,86 +655,236 @@
|
||||||
<hrule><stretch>1</stretch></hrule>
|
<hrule><stretch>1</stretch></hrule>
|
||||||
</group>
|
</group>
|
||||||
|
|
||||||
<!-- Instantiate a generic canvas map and parametrize it via inclusion -->
|
<canvas>
|
||||||
<!-- TODO: use params and aliasing -->
|
<name>canvas-map</name>
|
||||||
<canvas include="/Nasal/canvas/generic-canvas-map.xml">
|
|
||||||
|
|
||||||
<name>airport-selection</name>
|
|
||||||
<valign>fill</valign>
|
<valign>fill</valign>
|
||||||
<halign>fill</halign>
|
<halign>fill</halign>
|
||||||
<stretch>true</stretch>
|
<stretch>true</stretch>
|
||||||
<pref-width>400</pref-width>
|
<pref-width>600</pref-width>
|
||||||
<pref-height>400</pref-height>
|
<pref-height>400</pref-height>
|
||||||
|
<nasal><load><![CDATA[
|
||||||
|
var myCanvas = canvas.get( cmdarg() );
|
||||||
|
var mapGrp = myCanvas.createGroup();
|
||||||
|
var AirportChart = mapGrp.createChild("map");
|
||||||
|
|
||||||
<features>
|
# Initialize the controller:
|
||||||
<!-- TODO: use params and aliases to make this shorter -->
|
AirportChart.setController("Static position", "main");
|
||||||
<!-- TODO: support styling, i.e. image sets/fonts and colors to be used -->
|
var controller = AirportChart.getController();
|
||||||
<!-- this will set up individual "layers" and map them to boolean "toggle" properties -->
|
|
||||||
<!-- providing an optional "description" tag here allows us to create all checkboxes procedurally -->
|
|
||||||
<dialog-root>/sim/gui/dialogs/airports</dialog-root>
|
|
||||||
<range-property>zoom</range-property>
|
|
||||||
|
|
||||||
<!-- These are the ranges available for the map: var ranges = [0.1, 0.25, 0.5, 1, 2.5, 5] -->
|
# Initialize a range and screen resolution. Setting a range
|
||||||
|
# to 4nm means we pick up a good set of surrounding fixes
|
||||||
|
# We will use the screen range for zooming. If we use range
|
||||||
|
# then as we zoom in the airport center goes out of range
|
||||||
|
# and all the runways disappear.
|
||||||
|
AirportChart.setRange(4.0);
|
||||||
|
AirportChart.setScreenRange(500);
|
||||||
|
|
||||||
<ranges>
|
var range_step = 1.5;
|
||||||
<range>0.1</range>
|
|
||||||
<range>0.25</range>
|
|
||||||
<range>0.5</range>
|
|
||||||
<range>1</range>
|
|
||||||
<range>2.5</range>
|
|
||||||
<range>5</range>
|
|
||||||
</ranges>
|
|
||||||
|
|
||||||
<!-- available layers and their toggle property (appended to dialog-root specified above) -->
|
# Center the map's origin: FIXME: move to api.nas, i.e. allow maps to have a size/view that differs from the actual canvas ??
|
||||||
|
AirportChart.setTranslation(
|
||||||
|
myCanvas.get("view[0]")/2,
|
||||||
|
myCanvas.get("view[1]")/2
|
||||||
|
);
|
||||||
|
|
||||||
<layer>
|
##
|
||||||
<name>runways</name> <!-- the name of the layer -->
|
# Styling: This is a bit crude at the moment, i.e. no dedicated APIs yet - but it's
|
||||||
<init-property>selected-airport/id</init-property> <!-- the init/input property that re-inits the layer MODEL -->
|
# just there to prototype things for now
|
||||||
<property>display-runways</property> <!-- the property switch that toggles the layer on/off (show/hide) -->
|
var Styles = {};
|
||||||
<description>Show Runways</description> <!-- the checkbox label for the property -->
|
Styles.get = func(type) return Styles[type];
|
||||||
<default>enabled</default> <!-- default state -->
|
var Options = {};
|
||||||
<hide-checkbox>true</hide-checkbox> <!-- if the checkbox should be shown or hidden -->
|
Options.get = func(type) return Options[type];
|
||||||
</layer>
|
|
||||||
<layer>
|
|
||||||
<name>taxiways</name>
|
|
||||||
<init-property>selected-airport/id</init-property>
|
|
||||||
<property>display-taxiways</property>
|
|
||||||
<description>Show Taxiways</description>
|
|
||||||
<default>enabled</default>
|
|
||||||
</layer>
|
|
||||||
|
|
||||||
<layer>
|
## set up a few keys supported by the DME.symbol file to customize appearance:
|
||||||
<name>parkings</name>
|
Styles.DME = {};
|
||||||
<init-property>selected-airport/id</init-property>
|
Styles.DME.debug = 1; # HACK for benchmarking/debugging purposes
|
||||||
<property>display-parking</property>
|
Styles.DME.animation_test = 0; # for prototyping animated symbols
|
||||||
<description>Show Parking</description>
|
|
||||||
<default>disabled</default>
|
|
||||||
</layer>
|
|
||||||
|
|
||||||
<layer>
|
Styles.DME.scale_factor = 0.4; # 40% (applied to whole group)
|
||||||
<name>towers</name>
|
Styles.DME.line_width = 3.0;
|
||||||
<init-property>selected-airport/id</init-property>
|
Styles.DME.color_tuned = [0,1,0]; #rgb
|
||||||
<property>display-tower</property>
|
Styles.DME.color_default = [1,1,0]; #rgb
|
||||||
<description>Show Tower</description>
|
|
||||||
<default>enabled</default>
|
|
||||||
</layer>
|
|
||||||
<!-- Uncomment this to add a navaid layer (not yet fully implemented, and no LOD yet)
|
|
||||||
<layer>
|
|
||||||
<name>navaids</name>
|
|
||||||
<init-property>selected-airport/id</init-property>
|
|
||||||
<property>display-navaids</property>
|
|
||||||
<description>Display Navaids within current range</description>
|
|
||||||
<default>disabled</default>
|
|
||||||
</layer>
|
|
||||||
-->
|
|
||||||
|
|
||||||
</features>
|
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.FLT = {};
|
||||||
|
Styles.FLT.line_width = 3;
|
||||||
|
|
||||||
|
Styles.FIX = {};
|
||||||
|
Styles.FIX.color = [1,0,0];
|
||||||
|
Styles.FIX.scale_factor = 0.4; # 40%
|
||||||
|
|
||||||
|
Styles.VOR = {};
|
||||||
|
Styles.VOR.range_line_width = 2;
|
||||||
|
Styles.VOR.radial_line_width = 1;
|
||||||
|
Styles.VOR.scale_factor = 0.6; # 60%
|
||||||
|
|
||||||
|
var ToggleLayerVisible = func(name) {
|
||||||
|
(var l = AirportChart.getLayer(name)).setVisible(l.getVisible());
|
||||||
|
};
|
||||||
|
var SetLayerVisible = func(name,n=1) {
|
||||||
|
AirportChart.getLayer(name).setVisible(n);
|
||||||
|
};
|
||||||
|
|
||||||
|
Styles.APS = {};
|
||||||
|
Styles.APS.scale_factor = 0.25;
|
||||||
|
|
||||||
|
var r = func(name,vis=1,zindex=nil) return caller(0)[0];
|
||||||
|
# TODO: we'll need some z-indexing here, right now it's in the layer order
|
||||||
|
foreach(var type; [r('TAXI',1,0),r('RWY',1,1),r('TWR',1,2),r('DME',0,3),r('VOR',0,4),r('NDB',0,5),r('FIX',0,6),r('PARKING',0,7)] ) {
|
||||||
|
AirportChart.addLayer(factory: canvas.SymbolLayer, type_arg: type.name,
|
||||||
|
visible: type.vis, priority: 4,
|
||||||
|
style: Styles.get(type.name),
|
||||||
|
options: Options.get(type.name) );
|
||||||
|
(func {
|
||||||
|
# Notify MapStructure about layer visibility changes:
|
||||||
|
var name = type.name;
|
||||||
|
props.globals.initNode("/sim/gui/dialogs/map-canvas/draw-"~name, type.vis, "BOOL");
|
||||||
|
append(listeners,
|
||||||
|
setlistener("/sim/gui/dialogs/map-canvas/draw-"~name,
|
||||||
|
func(n) SetLayerVisible(name,n.getValue()))
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add some event listeners to handle mouse interactions
|
||||||
|
myCanvas.addEventListener("drag", func(e)
|
||||||
|
{
|
||||||
|
(func {
|
||||||
|
controller.applyOffset(-e.deltaX, -e.deltaY); })();
|
||||||
|
});
|
||||||
|
|
||||||
|
myCanvas.addEventListener("click", func(e)
|
||||||
|
{
|
||||||
|
(func {
|
||||||
|
controller.applyOffset(e.localX - myCanvas.get("view[0]")/2,
|
||||||
|
e.localY - myCanvas.get("view[1]")/2); })();
|
||||||
|
});
|
||||||
|
|
||||||
|
myCanvas.addEventListener("wheel", func(e)
|
||||||
|
{
|
||||||
|
var range = AirportChart.getScreenRange();
|
||||||
|
if (e.deltaY >0) {
|
||||||
|
if (range < 10000)
|
||||||
|
AirportChart.setScreenRange(range*range_step);
|
||||||
|
} else {
|
||||||
|
if (range > 100)
|
||||||
|
AirportChart.setScreenRange(range/range_step);
|
||||||
|
}
|
||||||
|
setprop("/sim/gui/dialogs/airports/zoom-range", AirportChart.getScreenRange());
|
||||||
|
});
|
||||||
|
|
||||||
|
if ((getprop("/sim/gui/dialogs/airports/selected-airport/lat") != nil) and
|
||||||
|
(getprop("/sim/gui/dialogs/airports/selected-airport/lon") != nil) )
|
||||||
|
{
|
||||||
|
# If we've got some values from a previous instantiation of the dialog
|
||||||
|
# then use them to display the correct position, consistent with the
|
||||||
|
# rest of the dialog.
|
||||||
|
AirportChart.getController().setPosition(
|
||||||
|
getprop("/sim/gui/dialogs/airports/selected-airport/lat"),
|
||||||
|
getprop("/sim/gui/dialogs/airports/selected-airport/lon"));
|
||||||
|
}
|
||||||
|
]]></load></nasal>
|
||||||
</canvas>
|
</canvas>
|
||||||
|
|
||||||
<group>
|
<group>
|
||||||
<name>canvas-control</name> <!-- this is the handle we use to procedurally add all "toggle layer" checkboxes and the zoom control-->
|
|
||||||
<layout>hbox</layout>
|
<layout>hbox</layout>
|
||||||
|
|
||||||
|
<checkbox>
|
||||||
|
<label>Nav data</label>
|
||||||
|
<halign>left</halign>
|
||||||
|
<property>/sim/gui/dialogs/map-canvas/draw-DME</property>
|
||||||
|
<live>true</live>
|
||||||
|
<binding>
|
||||||
|
<command>property-toggle</command>
|
||||||
|
</binding>
|
||||||
|
<binding>
|
||||||
|
<command>nasal</command>
|
||||||
|
<script>
|
||||||
|
var visible = ! getprop("/sim/gui/dialogs/map-canvas/draw-DME");
|
||||||
|
setprop("/sim/gui/dialogs/map-canvas/draw-DME", visible);
|
||||||
|
setprop("/sim/gui/dialogs/map-canvas/draw-VOR", visible);
|
||||||
|
setprop("/sim/gui/dialogs/map-canvas/draw-NDB", visible);
|
||||||
|
setprop("/sim/gui/dialogs/map-canvas/draw-FIX", visible);
|
||||||
|
</script>
|
||||||
|
<binding>
|
||||||
|
<command>dialog-apply</command>
|
||||||
|
</binding>
|
||||||
|
</binding>
|
||||||
|
</checkbox>
|
||||||
|
|
||||||
|
<checkbox>
|
||||||
|
<label>Parking</label>
|
||||||
|
<halign>left</halign>
|
||||||
|
<property>/sim/gui/dialogs/map-canvas/draw-PARKING</property>
|
||||||
|
<live>true</live>
|
||||||
|
<binding>
|
||||||
|
<command>dialog-apply</command>
|
||||||
|
</binding>
|
||||||
|
<binding>
|
||||||
|
<command>property-toggle</command>
|
||||||
|
</binding>
|
||||||
|
</checkbox>
|
||||||
|
|
||||||
|
<empty><stretch>1</stretch></empty>
|
||||||
|
|
||||||
|
<button>
|
||||||
|
<name>zoomout</name>
|
||||||
|
<legend>+</legend>
|
||||||
|
<pref-width>52</pref-width>
|
||||||
|
<pref-height>22</pref-height>
|
||||||
|
|
||||||
|
<binding>
|
||||||
|
<command>nasal</command>
|
||||||
|
<script>
|
||||||
|
var range = AirportChart.getScreenRange();
|
||||||
|
if (range < 10000)
|
||||||
|
AirportChart.setScreenRange(range*range_step);
|
||||||
|
setprop("/sim/gui/dialogs/airports/zoom-range", AirportChart.getScreenRange());
|
||||||
|
</script>
|
||||||
|
</binding>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<text>
|
||||||
|
<name>zoomdisplay</name>
|
||||||
|
<label>MMMMMMMMMMMMM</label>
|
||||||
|
<format>Zoom %d</format>
|
||||||
|
<property>/sim/gui/dialogs/airports/zoom-range</property>
|
||||||
|
<live>true</live>
|
||||||
|
</text>
|
||||||
|
|
||||||
|
<button>
|
||||||
|
<name>zoomin</name>
|
||||||
|
<legend>-</legend>
|
||||||
|
<pref-width>52</pref-width>
|
||||||
|
<pref-height>22</pref-height>
|
||||||
|
|
||||||
|
<binding>
|
||||||
|
<command>nasal</command>
|
||||||
|
<script>
|
||||||
|
var range = AirportChart.getScreenRange();
|
||||||
|
if (range > 100)
|
||||||
|
AirportChart.setScreenRange(range/range_step);
|
||||||
|
setprop("/sim/gui/dialogs/airports/zoom-range", AirportChart.getScreenRange());
|
||||||
|
</script>
|
||||||
|
</binding>
|
||||||
|
</button>
|
||||||
</group>
|
</group>
|
||||||
|
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue