zkv1000 / Nasal / map.nas /
Newer Older
238 lines | 8.88kb
adds Map on MFD
Sébastien MARQUE authored on 2017-03-20
1
# vim: set foldmethod=marker foldmarker={{{,}}} :
2
var MapTiles = {
3
# code from http://wiki.flightgear.org/Canvas_Snippets#A_simple_tile_map
4
    new : func (display) {
5
        var m = { parents: [MapTiles] };
6
        m.display = display;
7
        m.group = m.display.createGroup();
8
        m.tile_size = 256;
9
        m.zoom = 10;
10
        m.tile_server = getprop('/instrumentation/zkv1000/mfd/online-tiles-server');
11
        m.type = getprop('/instrumentation/zkv1000/mfd/online-tiles-type');
12
        m.maps_base = getprop("/sim/fg-home") ~ '/cache/maps';
13
        m.makeUrl = string.compileTemplate('https://{tile_server}/{type}/{z}/{x}/{y}.png');
14
        m.makePath = string.compileTemplate(m.maps_base ~ '/{tile_server}/{type}/{z}/{x}/{y}.png');
15
        m.num_tiles = [
16
            math.ceil( m.display.get('view[0]') / m.tile_size ) + 1, 
17
            math.ceil( m.display.get('view[1]') / m.tile_size ) + 1
18
        ];
19
        m.center_tile_offset = [
20
            (m.num_tiles[0] - 1) / 2,
21
            (m.num_tiles[1] - 1) / 2
22
        ];
23
        m.tiles = setsize([], m.num_tiles[0]);
24
        m.last_tile = [-1,-1];
25
        m.last_type = m.type;
26
        m.update_timer = maketimer(2, m, m.updateTiles);
27
        return m;
28
    },
29

            
30
# Simple user interface (Buttons for zoom and label for displaying it)
31
    changeZoom : func(d) {
32
        me.zoom = math.max(2, math.min(19, me.zoom + d));
33
        call(me.updateTiles, [], me);
34
    },
35

            
36
# initialize the map by setting up a grid of raster images  
37
    initialize_grid : func {
38
        for(var x = 0; x < me.num_tiles[0]; x += 1) {
39
            me.tiles[x] = setsize([], me.num_tiles[1]);
40
            for(var y = 0; y < me.num_tiles[1]; y += 1)
41
                me.tiles[x][y] = me.group.createChild("image", "map-tile");
42
        }
43
    },
44

            
45
# this is the callback that will be regularly called by the timer to update the map
46
    updateTiles : func() {
47
        var lat = getprop('/position/latitude-deg');
48
        var lon = getprop('/position/longitude-deg');
49

            
50
        var n = math.pow(2, me.zoom);
51
        var offset = [
52
            n * ((lon + 180) / 360) - me.center_tile_offset[0],
53
            (1 - math.ln(math.tan(lat * math.pi/180) + 1 / math.cos(lat * math.pi/180)) / math.pi) / 2 * n - me.center_tile_offset[1]
54
        ];
55
        var tile_index = [int(offset[0]), int(offset[1])];
56

            
57
        var ox = tile_index[0] - offset[0];
58
        var oy = tile_index[1] - offset[1];
59

            
60
        for(var x = 0; x < me.num_tiles[0]; x += 1)
61
            for(var y = 0; y < me.num_tiles[1]; y += 1)
62
                me.tiles[x][y]
63
                    .setTranslation(int((ox + x) * me.tile_size + 0.5), int((oy + y) * me.tile_size + 0.5));
64

            
65
        if(    tile_index[0] != me.last_tile[0]
66
                or tile_index[1] != me.last_tile[1]
67
                or me.type != me.last_type ) {
68
            for(var x = 0; x < me.num_tiles[0]; x += 1)
69
                for(var y = 0; y < me.num_tiles[1]; y += 1) {
70
                    var pos = {
71
                        z: me.zoom,
72
                        x: int(offset[0] + x),
73
                        y: int(offset[1] + y),
74
                        type: me.type,
75
                        tile_server : me.tile_server,
76
                    };
77

            
78
                    (func {
79
                        var img_path = me.makePath(pos);
80
                        printlog('debug', 'img_path: ', img_path);
81
                        var tile = me.tiles[x][y];
82

            
83
                        if( io.stat(img_path) == nil ) { # image not found, save in $FG_HOME
84
                            var img_url = me.makeUrl(pos);
85
                            printlog('debug', 'requesting ' ~ img_url);
86
                            http.save(img_url, img_path)
87
                                .done(func {printlog('info', 'received image ' ~ img_path); tile.set("src", img_path);})
88
                                .fail(func (r) printlog('warn', 'Failed to get image ' ~ img_path ~ ' ' ~ r.status ~ ': ' ~ r.reason));
89
                        }
90
                        else { # cached image found, reusing
91
                            printlog('debug', 'loading ' ~ img_path);
92
                            tile.set("src", img_path);
93
                        }
94
                    })();
95
                }
96
            me.last_tile = tile_index;
97
            me.last_type = me.type;
98
        }
99
    },
100

            
101
    del : func() {
102
        me.update_timer.stop();
103
        call(canvas.Window.del, [], me);
104
    },
105
};
106

            
107
var MapNavDisplay = {
108
    new : func (display) {
109
        var m = { parents: [MapNavDisplay] };
110
        m.display = display;
111
        m.map = m.display.createGroup().createChild('map');
112
        m.ctrl_ns = canvas.Map.Controller.get("Aircraft position");
113
        m.ctrl_ns.SOURCES["map-dialog"] = {
114
            getPosition: func subvec(geo.aircraft_position().latlon(), 0, 2),
115
            getAltitude: func getprop('/position/altitude-ft'),
116
            getHeading:  func {
117
                if (me.aircraft_heading)
118
                    getprop('/orientation/heading-deg')
119
                else 0
120
            },
121
            aircraft_heading: 1,
122
        };
123

            
124
        m.Styles = {
125
            get : func(type) return m.Styles[type],
126
        };
127

            
128
        m.Options = {
129
            get : func(type) return m.Options[type],
130
        };
131

            
132
        m.listeners = [];
133

            
134
        return m;
135
    },
136

            
137
    showMap : func {
138
        me.setMap();
139
        me.setStyles();
140
        me.setOptions();
141
        me.refresh();
142
    },
143

            
144
    setMap : func {
145
        var source = me.ctrl_ns.SOURCES["map-dialog"];
146
        me.map.setController("Aircraft position", "map-dialog");
147
        me.map.setRange(40);
148
        me.map.setTranslation(
149
            me.display.get("view[0]")/2,
150
            me.display.get("view[1]")/2
151
        );
152
    },
153

            
154
    setStyles : func {
155
## set up a few keys supported by the DME.symbol file to customize appearance: {{{
156
        me.Styles.DME = {};
157
        me.Styles.DME.debug = 1; # HACK for benchmarking/debugging purposes
158
        me.Styles.DME.animation_test = 0; # for prototyping animated symbols
159

            
160
        me.Styles.DME.scale_factor = 0.4; # 40% (applied to whole group)
161
        me.Styles.DME.line_width = 3.0;
162
        me.Styles.DME.color_tuned = [0,1,0]; #rgb
163
        me.Styles.DME.color_default = [1,1,0];  #rgb
164

            
165
        me.Styles.APT = {};
166
        me.Styles.APT.scale_factor = 0.4; # 40% (applied to whole group)
167
        me.Styles.APT.line_width = 3.0;
168
        me.Styles.APT.color_default = [0,0.6,0.85];  #rgb
169
        me.Styles.APT.label_font_color = me.Styles.APT.color_default;
170
        me.Styles.APT.label_font_size=16;
171

            
172
        me.Styles.TFC = {};
173
        me.Styles.TFC.scale_factor = 0.4; # 40% (applied to whole group)
174

            
175
        me.Styles.WPT = {};
176
        me.Styles.WPT.scale_factor = 0.5; # 50% (applied to whole group)
177

            
178
        me.Styles.RTE = {};
179
        me.Styles.RTE.line_width = 2;
180

            
181
        me.Styles.FLT = {};
182
        me.Styles.FLT.line_width = 3;
183

            
184
        me.Styles.FIX = {};
185
        me.Styles.FIX.color = [1,0,0];
186
        me.Styles.FIX.scale_factor = 0.4; # 40%
187

            
188
        me.Styles.VOR = {};
189
        me.Styles.VOR.range_line_width = 2;
190
        me.Styles.VOR.radial_line_width = 1;
191
        me.Styles.VOR.scale_factor = 0.6; # 60%
192

            
193
        me.Styles.APS = {};
194
        me.Styles.APS.scale_factor = 0.5;
195
    },
196
#}}}
197

            
198
    setOptions : func {
199
        me.Options.FLT = {};
200
    },
201

            
202
    make_update_wrapper : func(name) {
203
        if (!contains(me.Options, name)) me.Options[name] = {};
204
        me.Options[name].update_wrapper = func(layer, fn) {
205
            fn();
commit initial
Sébastien MARQUE authored on 2017-03-07
206
        }
adds Map on MFD
Sébastien MARQUE authored on 2017-03-20
207
    },
208

            
209
    ToggleLayerVisible : func(name) {
210
        (var l = me.map.getLayer(name)).setVisible(l.getVisible());
211
    },
212

            
213
    SetLayerVisible : func(name,n=1) {
214
        me.map.getLayer(name).setVisible(n);
215
    },
216

            
217
    refresh : func {
218
        var r = func(name,vis=1,zindex=nil) return caller(0)[0];
219
        # TODO: we'll need some z-indexing here, right now it's just random
220
        foreach(var type; [r('TFC',0),r('APT'),r('DME'),r('VOR'),r('NDB'),r('FIX',0),r('RTE'),r('WPT'),r('FLT'),r('WXR',0),r('APS'), ] ) {
221
            if (1 and type.name != 'APS' and type.name != 'FLT') me.make_update_wrapper(type.name);
222
            me.map.addLayer(factory: canvas.SymbolLayer, type_arg: type.name,
223
                    visible: type.vis, priority: type.zindex,
224
                    style: me.Styles.get(type.name),
225
                    options: me.Options.get(type.name) 
226
                    );
227
            (func {
228
                 # Notify MapStructure about layer visibility changes:
229
                 var name = type.name;
230
                 props.globals.initNode("/sim/gui/dialogs/map-canvas/draw-"~name, type.vis, "BOOL");
231
                 append(me.listeners,
232
                         setlistener("/sim/gui/dialogs/map-canvas/draw-"~name,
233
                             func(n) me.SetLayerVisible(name,n.getValue()), 1, 2)
234
                       );
235
             })();
commit initial
Sébastien MARQUE authored on 2017-03-07
236
        }
adds Map on MFD
Sébastien MARQUE authored on 2017-03-20
237
    },
238
};