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
|
||||
# ==============================================================================
|
||||
# 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.
|
||||
# Each map consists of an arbitrary number of layers (canvas groups)
|
||||
#
|
||||
|
@ -502,6 +502,10 @@ var Map = {
|
|||
|
||||
return me;
|
||||
},
|
||||
getController: func()
|
||||
{
|
||||
return me.controller;
|
||||
},
|
||||
addLayer: func(factory, type_arg=nil, priority=nil, style=nil, opts=nil, visible=1)
|
||||
{
|
||||
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 DIALOG = cmdarg();
|
||||
var listeners = [];
|
||||
|
||||
## "prologue" currently required by the canvas-generic-map
|
||||
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_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/parkpos", "");
|
||||
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/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/lat", info.lat);
|
||||
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/parkpos", "");
|
||||
AirportChart.getController().setPosition(info.lat, info.lon);
|
||||
|
||||
if (info.has_metar) {
|
||||
# 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
|
||||
]]>
|
||||
</open>
|
||||
<close>
|
||||
<close><![CDATA[
|
||||
fgcommand("clear-metar", var n = props.Node.new({ "path": "/sim/gui/dialogs/airports/selected-airport/metar",
|
||||
"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
|
||||
</close>
|
||||
#map.cleanup_listeners(); #TODO: We should be setting a signal when closing the dialog, so that cleanup code can be invoked automatically
|
||||
AirportChart.del();
|
||||
foreach (var l; listeners)
|
||||
removelistener(l);
|
||||
setsize(listeners, 0);
|
||||
]]></close>
|
||||
</nasal>
|
||||
|
||||
<group>
|
||||
|
@ -648,86 +655,236 @@
|
|||
<hrule><stretch>1</stretch></hrule>
|
||||
</group>
|
||||
|
||||
<!-- Instantiate a generic canvas map and parametrize it via inclusion -->
|
||||
<!-- TODO: use params and aliasing -->
|
||||
<canvas include="/Nasal/canvas/generic-canvas-map.xml">
|
||||
<canvas>
|
||||
<name>canvas-map</name>
|
||||
<valign>fill</valign>
|
||||
<halign>fill</halign>
|
||||
<stretch>true</stretch>
|
||||
<pref-width>600</pref-width>
|
||||
<pref-height>400</pref-height>
|
||||
<nasal><load><![CDATA[
|
||||
var myCanvas = canvas.get( cmdarg() );
|
||||
var mapGrp = myCanvas.createGroup();
|
||||
var AirportChart = mapGrp.createChild("map");
|
||||
|
||||
<name>airport-selection</name>
|
||||
<valign>fill</valign>
|
||||
<halign>fill</halign>
|
||||
<stretch>true</stretch>
|
||||
<pref-width>400</pref-width>
|
||||
<pref-height>400</pref-height>
|
||||
# Initialize the controller:
|
||||
AirportChart.setController("Static position", "main");
|
||||
var controller = AirportChart.getController();
|
||||
|
||||
<features>
|
||||
<!-- TODO: use params and aliases to make this shorter -->
|
||||
<!-- TODO: support styling, i.e. image sets/fonts and colors to be used -->
|
||||
<!-- 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>
|
||||
# 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);
|
||||
|
||||
<!-- These are the ranges available for the map: var ranges = [0.1, 0.25, 0.5, 1, 2.5, 5] -->
|
||||
var range_step = 1.5;
|
||||
|
||||
<ranges>
|
||||
<range>0.1</range>
|
||||
<range>0.25</range>
|
||||
<range>0.5</range>
|
||||
<range>1</range>
|
||||
<range>2.5</range>
|
||||
<range>5</range>
|
||||
</ranges>
|
||||
# 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
|
||||
);
|
||||
|
||||
<!-- available layers and their toggle property (appended to dialog-root specified above) -->
|
||||
##
|
||||
# Styling: This is a bit crude at the moment, i.e. no dedicated APIs yet - but it's
|
||||
# just there to prototype things for now
|
||||
var Styles = {};
|
||||
Styles.get = func(type) return Styles[type];
|
||||
var Options = {};
|
||||
Options.get = func(type) return Options[type];
|
||||
|
||||
<layer>
|
||||
<name>runways</name> <!-- the name of the layer -->
|
||||
<init-property>selected-airport/id</init-property> <!-- the init/input property that re-inits the layer MODEL -->
|
||||
<property>display-runways</property> <!-- the property switch that toggles the layer on/off (show/hide) -->
|
||||
<description>Show Runways</description> <!-- the checkbox label for the property -->
|
||||
<default>enabled</default> <!-- default state -->
|
||||
<hide-checkbox>true</hide-checkbox> <!-- if the checkbox should be shown or hidden -->
|
||||
</layer>
|
||||
<layer>
|
||||
<name>taxiways</name>
|
||||
<init-property>selected-airport/id</init-property>
|
||||
<property>display-taxiways</property>
|
||||
<description>Show Taxiways</description>
|
||||
<default>enabled</default>
|
||||
</layer>
|
||||
## set up a few keys supported by the DME.symbol file to customize appearance:
|
||||
Styles.DME = {};
|
||||
Styles.DME.debug = 1; # HACK for benchmarking/debugging purposes
|
||||
Styles.DME.animation_test = 0; # for prototyping animated symbols
|
||||
|
||||
<layer>
|
||||
<name>parkings</name>
|
||||
<init-property>selected-airport/id</init-property>
|
||||
<property>display-parking</property>
|
||||
<description>Show Parking</description>
|
||||
<default>disabled</default>
|
||||
</layer>
|
||||
Styles.DME.scale_factor = 0.4; # 40% (applied to whole group)
|
||||
Styles.DME.line_width = 3.0;
|
||||
Styles.DME.color_tuned = [0,1,0]; #rgb
|
||||
Styles.DME.color_default = [1,1,0]; #rgb
|
||||
|
||||
<layer>
|
||||
<name>towers</name>
|
||||
<init-property>selected-airport/id</init-property>
|
||||
<property>display-tower</property>
|
||||
<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>
|
||||
-->
|
||||
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;
|
||||
|
||||
</features>
|
||||
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>
|
||||
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
||||
|
|
Loading…
Reference in a new issue