Source canvas api map.nas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
#-------------------------------------------------------------------------------
# canvas.Map
#-------------------------------------------------------------------------------
# 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)
#
var Map = {
df_controller: nil,
new: func(ghost) {
var obj = {
parents: [Map, Group.new(ghost)],
layers: {},
controller: nil,
};
return obj.setController();
},
del: func() {
logprint(_API_dbg_level, "canvas.Map.del()");
if (me.controller != nil)
me.controller.del(me);
foreach (var k; keys(me.layers)) {
me.layers[k].del();
delete(me.layers, k);
}
# call inherited "del"
me.parents = subvec(me.parents,1);
me.del();
},
setController: func(controller=nil, arg...) {
if (me.controller != nil) me.controller.del(me);
if (controller == nil) {
controller = Map.df_controller;
}
elsif (typeof(controller) != "hash") {
controller = Map.Controller.get(controller);
}
if (controller == nil) {
me.controller = nil;
}
else {
if (!isa(controller, Map.Controller))
die("OOP error: controller needs to inherit from Map.Controller");
me.controller = call(controller.new, [me]~arg, controller, var err=[]); # try...
if (size(err)) {
if (err[0] != "No such member: new") # ... and either catch or rethrow
die(err[0]);
else
me.controller = controller;
}
elsif (me.controller == nil) {
me.controller = controller;
}
elsif (me.controller != controller and !isa(me.controller, controller))
die("OOP error: created instance needs to inherit from or be the specific controller class");
}
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)) {
logprint(DEV_ALERT, "addLayer() warning: overwriting existing layer:", type_arg);
}
var options = opts;
# Argument handling
if (type_arg != nil) {
var layer = factory.new(type:type_arg, group:me, map:me, style:style, options:options, visible:visible);
var type = factory.get(type_arg);
var key = type_arg;
} else {
var layer = factory.new(group:me, map:me, style:style, options:options, visible:visible);
var type = factory;
var key = factory.type;
}
me.layers[type_arg] = layer;
if (priority == nil)
priority = type.df_priority;
if (priority != nil)
layer.group.setInt("z-index", priority);
return layer; # return new layer to caller() so that we can directly work with it, i.e. to register event handlers (panning/zooming)
},
getLayer: func(type_arg) {
me.layers[type_arg];
},
setRange: func(range) {
me.set("range",range);
},
setScreenRange: func(range) {
me.set("screen-range",range);
},
setPos: func(lat, lon, hdg=nil, range=nil, alt=nil) {
# TODO: also propage setPos events to layers and symbols (e.g. for offset maps)
me.set("ref-lat", lat);
me.set("ref-lon", lon);
if (hdg != nil)
me.set("hdg", hdg);
if (range != nil)
me.setRange(range);
if (alt != nil)
me.set("altitude", alt);
},
getPos: func {
return [me.get("ref-lat"),
me.get("ref-lon"),
me.get("hdg"),
me.get("range"),
me.get("altitude")];
},
getLat: func me.get("ref-lat"),
getLon: func me.get("ref-lon"),
getHdg: func me.get("hdg"),
getAlt: func me.get("altitude"),
getRange: func me.get("range"),
getScreenRange: func me.get("screen-range"),
getLatLon: func [me.get("ref-lat"), me.get("ref-lon")],
# N.B.: This always returns the same geo.Coord object,
# so its values can and will change at any time (call
# update() on the coord to ensure it is up-to-date,
# which basically calls this method again).
getPosCoord: func {
var (lat, lon) = (me.get("ref-lat"), me.get("ref-lon"));
var alt = me.get("altitude");
if (lat == nil or lon == nil) {
if (contains(me, "coord")) {
debug.warn("canvas.Map: lost ref-lat and/or ref-lon source");
}
return nil;
}
if (!contains(me, "coord")) {
me.coord = geo.Coord.new();
var m = me;
me.coord.update = func m.getPosCoord();
}
me.coord.set_latlon(lat,lon,alt or 0);
return me.coord;
},
# Update each layer on this Map. Called by
# me.controller.
update: func(predicate=nil) {
var t = systime();
foreach (var l; keys(me.layers)) {
var layer = me.layers[l];
# Only update if the predicate allows
if (predicate == nil or predicate(layer)) {
layer.update();
}
}
logprint(_MP_dbg_lvl, "Took "~((systime()-t)*1000)~"ms to update map()");
me.setBool("update", 1); # update any coordinates that changed, to avoid floating labels etc.
return me;
},
};