Add Canvas Map Support for Slippy Map
- Include OSM and OpenAIP in the canvas-map dialog.
This commit is contained in:
parent
cfa967db1d
commit
85b7665c19
7 changed files with 472 additions and 27 deletions
|
@ -16,7 +16,7 @@
|
|||
## - parents
|
||||
## - __self__
|
||||
## - del (managing all listeners and timers)
|
||||
## - searchCmd -> filtering
|
||||
## - searchCmd -> filtering
|
||||
##
|
||||
## APIs to be wrapped for each layer:
|
||||
## printlog(), die(), debug.bt(), benchmark()
|
||||
|
@ -79,10 +79,16 @@ var MapStructure_selfTest = func() {
|
|||
# TODO: we'll need some z-indexing here, right now it's just random
|
||||
# TODO: use foreach/keys to show all layers in this case by traversing SymbolLayer.registry direclty ?
|
||||
# maybe encode implicit z-indexing for each lcontroller ctor call ? - i.e. preferred above/below order ?
|
||||
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'),r('APS'), ] )
|
||||
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'),r('APS'), ] )
|
||||
TestMap.addLayer(factory: canvas.SymbolLayer, type_arg: type.name,
|
||||
visible: type.vis, priority: type.zindex,
|
||||
);
|
||||
foreach(var type; [ r('OSM'), r('OpenAIP') ]) {
|
||||
TestMap.addLayer(factory: canvas.OverlayLayer, type_arg: type.name,
|
||||
visible: type.vis, priority: type.zindex,
|
||||
style: Styles.get(type.name),
|
||||
options: Options.get(type.name) );
|
||||
}
|
||||
}; # MapStructure_selfTest
|
||||
|
||||
|
||||
|
@ -854,7 +860,7 @@ var LineSymbol = {
|
|||
var path = me.model;
|
||||
if(typeof(path) == 'hash'){
|
||||
path = me.model.path;
|
||||
if(path == nil)
|
||||
if(path == nil)
|
||||
__die("LineSymbol model requires a 'path' member (vector)");
|
||||
}
|
||||
foreach (var m; path) {
|
||||
|
@ -862,7 +868,7 @@ var LineSymbol = {
|
|||
var (lat,lon) = me.controller.getpos(m);
|
||||
append(coords,"N"~lat);
|
||||
append(coords,"E"~lon);
|
||||
append(cmds,cmd);
|
||||
append(cmds,cmd);
|
||||
cmd = canvas.Path.VG_LINE_TO;
|
||||
} else {
|
||||
cmd = canvas.Path.VG_MOVE_TO;
|
||||
|
@ -934,7 +940,7 @@ var SymbolLayer = {
|
|||
# print("SymbolLayer setup options:", m.options!=nil);
|
||||
m.style = default_hash(style, m.df_style);
|
||||
m.options = default_hash(options, m.df_options);
|
||||
|
||||
|
||||
if (controller == nil)
|
||||
controller = m.df_controller;
|
||||
assert_m(controller, "parents");
|
||||
|
@ -1074,15 +1080,15 @@ var MultiSymbolLayer = {
|
|||
}
|
||||
return 0;
|
||||
},
|
||||
searchCmd: func() {
|
||||
if (me.map.getPosCoord() == nil or me.map.getRange() == nil) {
|
||||
searchCmd: func() {
|
||||
if (me.map.getPosCoord() == nil or me.map.getRange() == nil) {
|
||||
print("Map not yet initialized, returning empty result set!");
|
||||
return []; # handle maps not yet fully initialized
|
||||
}
|
||||
var result = me.controller.searchCmd();
|
||||
# some hardening
|
||||
var type=typeof(result);
|
||||
if(type != 'nil' and type != 'vector')
|
||||
if(type != 'nil' and type != 'vector')
|
||||
__die("MultiSymbolLayer: searchCmd() method MUST return a vector of valid positioned ghosts/Geo.Coord objects or nil! (was:"~type~")");
|
||||
return result;
|
||||
},
|
||||
|
@ -1127,7 +1133,7 @@ var NavaidSymbolLayer = {
|
|||
|
||||
###
|
||||
## TODO: wrappers for Horizontal vs. Vertical layers ?
|
||||
##
|
||||
##
|
||||
|
||||
var SingleSymbolLayer = {
|
||||
parents: [SymbolLayer],
|
||||
|
@ -1177,6 +1183,306 @@ var SingleSymbolLayer = {
|
|||
},
|
||||
}; # of SingleSymbolLayer
|
||||
|
||||
##
|
||||
# Base class for a OverlayLayer, e.g. a TileLayer
|
||||
#
|
||||
var OverlayLayer = {
|
||||
# Default implementations/values:
|
||||
df_controller: nil, # default controller
|
||||
df_priority: nil, # default priority for display sorting
|
||||
df_style: nil,
|
||||
df_options: nil,
|
||||
type: nil, # type of #Symbol to add (MANDATORY)
|
||||
id: nil, # id of the group #canvas.Element (OPTIONAL)
|
||||
# Static/singleton:
|
||||
registry: {},
|
||||
add: func(type, class) {
|
||||
me.registry[type] = class;
|
||||
},
|
||||
get: func(type) {
|
||||
foreach(var invalid; var invalid_types = [nil,'vector','hash'])
|
||||
if ( (var t=typeof(type)) == invalid) __die(" invalid OverlayLayer type (non-scalar) of type:"~t);
|
||||
if ((var class = me.registry[type]) == nil)
|
||||
__die("OverlayLayer.get(): unknown type '"~type~"'");
|
||||
else return class;
|
||||
},
|
||||
# Calls corresonding layer constructor
|
||||
# @param group #Canvas.Group to place this on.
|
||||
# @param map The #Canvas.Map this is a member of.
|
||||
# @param style An alternate style.
|
||||
# @param options Extra options/configurations.
|
||||
# @param visible Initially set it up as visible or invisible.
|
||||
new: func(type, group, map, controller=nil, style=nil, options=nil, visible=1, arg...) {
|
||||
# XXX: Extra named arguments are (obviously) not preserved well...
|
||||
if (me == nil) __die("OverlaySymbolLayer constructor needs to know its parent class");
|
||||
|
||||
var ret = call((var class = me.get(type)).new, [group, map, controller, style, options, visible], class);
|
||||
ret.type = type;
|
||||
ret.group.set("layer-type", type);
|
||||
return ret;
|
||||
},
|
||||
|
||||
# Private constructor:
|
||||
_new: func(m, style, controller, options) {
|
||||
m.style = default_hash(style, m.df_style);
|
||||
m.options = default_hash(options, m.df_options);
|
||||
|
||||
if (controller == nil) {
|
||||
if (m.df_controller == nil) {
|
||||
controller = OverlayLayer.Controller;
|
||||
} else {
|
||||
controller = m.df_controller;
|
||||
}
|
||||
}
|
||||
|
||||
assert_m(controller, "parents");
|
||||
if (controller.parents[0] == OverlayLayer.Controller)
|
||||
controller = controller.new(m);
|
||||
assert_m(controller, "parents");
|
||||
assert_m(controller.parents[0], "parents");
|
||||
if(options != nil){
|
||||
var listeners = opt_member(controller, 'listeners');
|
||||
var listen = opt_member(options, 'listen');
|
||||
if (listen != nil and listeners != nil){
|
||||
var listen_tp = typeof(listen);
|
||||
if(listen_tp != 'vector' and listen_tp != 'scalar')
|
||||
__die("Options 'listen' cannot be a "~ listen_tp);
|
||||
if(typeof(listen) == 'scalar')
|
||||
listen = [listen];
|
||||
foreach(var node_name; listen){
|
||||
var node = opt_member(options, node_name);
|
||||
if(node == nil)
|
||||
node = node_name;
|
||||
append(controller.listeners,
|
||||
setlistener(node, func call(m.update,[],m),0,0));
|
||||
}
|
||||
}
|
||||
}
|
||||
m.controller = controller;
|
||||
},
|
||||
# For instances:
|
||||
del: func() if (me.controller != nil) { me.controller.del(); me.controller = nil },
|
||||
update: func() { _die("Abstract OverlayLayer.update() not implemented for this Layer"); },
|
||||
};
|
||||
|
||||
var TileLayer = {
|
||||
parents: [OverlayLayer],
|
||||
# Default implementations/values:
|
||||
# @param group A group to place this on.
|
||||
# @param map The #Canvas.Map this is a member of.
|
||||
# @param controller A controller object (parents=[OverlayLayer.Controller])
|
||||
# or implementation (parents[0].parents=[OverlayLayer.Controller]).
|
||||
# @param style An alternate style.
|
||||
# @param options Extra options/configurations.
|
||||
# @param visible Initially set it up as visible or invisible.
|
||||
new: func(group, map, controller=nil, style=nil, options=nil, visible=1) {
|
||||
if (me == nil) __die("TileLayer constructor needs to know its parent class");
|
||||
var m = {
|
||||
parents: [me],
|
||||
map: map,
|
||||
group: group.createChild("group", me.type),
|
||||
maps_base: "",
|
||||
num_tiles: [5,5],
|
||||
makeURL: nil,
|
||||
makePath: nil,
|
||||
center_tile_offset : [],
|
||||
tile_size: 256,
|
||||
zoom: 9,
|
||||
tile_type: "map",
|
||||
last_tile_type: "map",
|
||||
last_tile : [-1,-1],
|
||||
tiles: [],
|
||||
};
|
||||
|
||||
# Determine the number of tiles dynamically based on the canvas size
|
||||
#var width = map.getCanvas().get("size[0]");
|
||||
#var height = map.getCanvas().get("size[1]");
|
||||
#m.num_tiles= [ math.ceil(width / m.tile_size),
|
||||
# math.ceil(height / m.tile_size) ];
|
||||
|
||||
|
||||
m.maps_base = getprop("/sim/fg-home") ~ '/cache/maps';
|
||||
m.tiles = setsize([], m.num_tiles[0]);
|
||||
m.center_tile_offset = [
|
||||
(m.num_tiles[0] - 1.0) / 2.0,
|
||||
(m.num_tiles[1] - 1.0) / 2.0
|
||||
];
|
||||
|
||||
append(m.parents, m.group);
|
||||
m.setVisible(visible);
|
||||
OverlayLayer._new(m, style, controller, options);
|
||||
#m.group.setCenter(0,0);
|
||||
|
||||
for(var x = 0; x < m.num_tiles[0]; x += 1)
|
||||
{
|
||||
m.tiles[x] = setsize([], m.num_tiles[1]);
|
||||
for(var y = 0; y < m.num_tiles[1]; y += 1) {
|
||||
m.tiles[x][y] = m.group.createChild("image", "map-tile");
|
||||
}
|
||||
}
|
||||
|
||||
m.update();
|
||||
return m;
|
||||
},
|
||||
updateLayer: func()
|
||||
{
|
||||
# get current position
|
||||
var lat = me.map.getLat();
|
||||
var lon = me.map.getLon();
|
||||
var range_nm = me.map.getRange();
|
||||
var screen_range = me.map.getScreenRange();
|
||||
|
||||
if (screen_range == nil) screen_range = 200;
|
||||
|
||||
# Screen resolution m/pixel is range/screen_range
|
||||
var screen_resolution = range_nm * globals.NM2M / screen_range;
|
||||
|
||||
# Slippy map resolution is
|
||||
# 156543.03 meters/pixel * cos(latitude) / (2 ^ zoomlevel)
|
||||
# Determine the closest zoom level and scaling ratio. Each increase in zoom level doubles resolution.
|
||||
var ideal_zoom = math.ln(156543.03 * math.cos(lat * math.pi/180.0) / screen_resolution) / math.ln(2);
|
||||
me.zoom = math.ceil(ideal_zoom);
|
||||
var ratio = 1 / math.pow(2,me.zoom - ideal_zoom);
|
||||
|
||||
for(var x = 0; x < me.num_tiles[0]; x += 1)
|
||||
{
|
||||
for(var y = 0; y < me.num_tiles[1]; y += 1) {
|
||||
me.tiles[x][y].setTranslation(int((x - me.center_tile_offset[0]) * me.tile_size * ratio + 0.5),
|
||||
int((y - me.center_tile_offset[1]) * me.tile_size * ratio + 0.5));
|
||||
me.tiles[x][y].setScale(ratio);
|
||||
me.tiles[x][y].scale_factor = ratio;
|
||||
}
|
||||
}
|
||||
|
||||
#var heading = me.map.getHdg();
|
||||
#me.group.setRotation(heading);
|
||||
|
||||
var ymax = math.pow(2, me.zoom);
|
||||
|
||||
# Slippy map location of center point
|
||||
var slippy_center = [
|
||||
math.floor(ymax * ((lon + 180.0) / 360.0)),
|
||||
math.floor((1 - math.ln(math.tan(lat * math.pi/180.0) + 1 / math.cos(lat * math.pi/180.0)) / math.pi) / 2.0 * ymax)
|
||||
];
|
||||
|
||||
# This is the Slippy Map location of the 0,0 tile
|
||||
var offset = [slippy_center[0] - me.center_tile_offset[0],
|
||||
slippy_center[1] - me.center_tile_offset[1]];
|
||||
|
||||
var tile_index = [math.floor(offset[0]), math.floor(offset[1])];
|
||||
|
||||
# Find the lon, lat of the center tile
|
||||
var center_tile_lon = slippy_center[0]/ymax * 360.0 - 180.0;
|
||||
var nn = math.pi - 2.0 * math.pi * slippy_center[1]/ ymax;
|
||||
var center_tile_lat = 180.0 / math.pi * math.atan(0.5 * (math.exp(nn) - math.exp(-nn)));
|
||||
|
||||
me.group.setGeoPosition(center_tile_lat, center_tile_lon);
|
||||
|
||||
if( tile_index[0] != me.last_tile[0]
|
||||
or tile_index[1] != me.last_tile[1]
|
||||
or me.tile_type != me.last_tile_type )
|
||||
{
|
||||
for(var x = 0; x < me.num_tiles[0]; x += 1) {
|
||||
for(var y = 0; y < me.num_tiles[1]; y += 1) {
|
||||
var pos = {
|
||||
z: me.zoom,
|
||||
x: int(tile_index[0] + x),
|
||||
y: int(tile_index[1] + y),
|
||||
tms_y: ymax - int(tile_index[1] + y) - 1,
|
||||
type: me.tile_type
|
||||
};
|
||||
|
||||
(func {
|
||||
var img_path = me.makePath(pos);
|
||||
var tile = me.tiles[x][y];
|
||||
|
||||
if( io.stat(img_path) == nil ) {
|
||||
# image not found, save in $FG_HOME
|
||||
var img_url = me.makeURL(pos);
|
||||
#print('requesting ' ~ img_url);
|
||||
http.save(img_url, img_path)
|
||||
.done(func { tile.set("src", img_path);})
|
||||
.fail(func (r) print('Failed to get image ' ~ img_path ~ ' ' ~ r.status ~ ': ' ~ r.reason));
|
||||
} else {
|
||||
# Re-use cached image
|
||||
#print('loading ' ~ img_path);
|
||||
tile.set("src", img_path)
|
||||
}
|
||||
})();
|
||||
}
|
||||
}
|
||||
|
||||
me.last_tile = tile_index;
|
||||
me.last_type = me.type;
|
||||
}
|
||||
},
|
||||
update: func() {
|
||||
if (!me.getVisible())
|
||||
return;
|
||||
#debug.warn("update traceback for "~me.type);
|
||||
|
||||
if (me.options != nil and me.options['update_wrapper'] !=nil) {
|
||||
me.options.update_wrapper( me, me.updateLayer ); # call external wrapper (usually for profiling purposes)
|
||||
} else {
|
||||
me.updateLayer();
|
||||
}
|
||||
},
|
||||
del: func() {
|
||||
printlog(_MP_dbg_lvl, "SymbolLayer.del()");
|
||||
call(OverlayLayer.del, nil, me);
|
||||
},
|
||||
}; # of TileLayer
|
||||
|
||||
# Class to manage controlling a OverlayLayer.
|
||||
# Currently handles:
|
||||
# * Simple update() call
|
||||
OverlayLayer.Controller = {
|
||||
# Static/singleton:
|
||||
registry: {},
|
||||
add: func(type, class)
|
||||
me.registry[type] = class,
|
||||
get: func(type)
|
||||
if ((var class = me.registry[type]) == nil)
|
||||
__die("unknown type '"~type~"'");
|
||||
else return class,
|
||||
# Calls corresponding controller constructor
|
||||
# @param layer The #OverlayLayer this controller is responsible for.
|
||||
new: func(type, layer, arg...)
|
||||
return call((var class = me.get(type)).new, [layer]~arg, class),
|
||||
# Default implementations for derived classes:
|
||||
# @return List of positioned objects.
|
||||
updateLayer: func()
|
||||
__die("Abstract method updateLayer() not implemented for this OverlayLayer.Controller type!"),
|
||||
addVisibilityListener: func() {
|
||||
var m = me;
|
||||
append(m.listeners, setlistener(
|
||||
m.layer._node.getNode("visible"),
|
||||
func m.layer.update(),
|
||||
#compile("m.layer.update()", "<layer visibility on node "~m.layer._node.getNode("visible").getPath()~" for layer "~m.layer.type~">"),
|
||||
0,0
|
||||
));
|
||||
},
|
||||
addRangeListener: func() {
|
||||
var m = me;
|
||||
append(m.listeners, setlistener(
|
||||
m.layer._node.getNode("range",1),
|
||||
func m.layer.update(),
|
||||
#compile("m.layer.update()", "<layer visibility on node "~m.layer._node.getNode("visible").getPath()~" for layer "~m.layer.type~">"),
|
||||
0,0
|
||||
));
|
||||
},
|
||||
addScreenRangeListener: func() {
|
||||
var m = me;
|
||||
append(m.listeners, setlistener(
|
||||
m.layer._node.getNode("screen-range",1),
|
||||
func m.layer.update(),
|
||||
#compile("m.layer.update()", "<layer visibility on node "~m.layer._node.getNode("visible").getPath()~" for layer "~m.layer.type~">"),
|
||||
0,0
|
||||
));
|
||||
},
|
||||
}; # of OverlayLayer.Controller
|
||||
|
||||
|
||||
###
|
||||
# set up a cache for 32x32 symbols (initialized below in load_MapStructure)
|
||||
var SymbolCache32x32 = nil;
|
||||
|
@ -1307,6 +1613,7 @@ var load_MapStructure = func {
|
|||
"symbol",
|
||||
"scontroller",
|
||||
"controller",
|
||||
"overlay"
|
||||
];
|
||||
var deps = {};
|
||||
foreach (var d; dep_names) deps[d] = [];
|
||||
|
|
|
@ -476,11 +476,13 @@ var Map = {
|
|||
setController: func(controller=nil, arg...)
|
||||
{
|
||||
if (me.controller != nil) me.controller.del(me);
|
||||
if (controller == nil)
|
||||
if (controller == nil) {
|
||||
controller = Map.df_controller;
|
||||
elsif (typeof(controller) != 'hash')
|
||||
}
|
||||
elsif (typeof(controller) != 'hash') {
|
||||
controller = Map.Controller.get(controller);
|
||||
|
||||
}
|
||||
|
||||
if (controller == nil) {
|
||||
me.controller = nil;
|
||||
} else {
|
||||
|
@ -527,8 +529,8 @@ var Map = {
|
|||
},
|
||||
getLayer: func(type_arg) me.layers[type_arg],
|
||||
|
||||
setRange: func(range) me.set("range",range),
|
||||
getRange: func me.get('range'),
|
||||
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)
|
||||
{
|
||||
|
@ -555,6 +557,7 @@ var Map = {
|
|||
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
|
||||
|
@ -626,7 +629,7 @@ var Text = {
|
|||
{
|
||||
die("updateText() requires enableUpdate() to be called first");
|
||||
},
|
||||
|
||||
|
||||
# enable fast setprop-based text writing
|
||||
enableFast: func ()
|
||||
{
|
||||
|
@ -981,7 +984,7 @@ var Path = {
|
|||
},
|
||||
|
||||
setColor: func me.setStroke(_getColor(arg)),
|
||||
getColor: func me.getStroke(),
|
||||
getColor: func me.getStroke(),
|
||||
|
||||
setColorFill: func me.setFill(_getColor(arg)),
|
||||
getColorFill: func me.getColorFill(),
|
||||
|
|
|
@ -243,7 +243,7 @@ LayeredMap.updateZoom = func {
|
|||
z = math.max(0, math.min(z, size(me.ranges) - 1));
|
||||
me.zoom_property.setIntValue(z);
|
||||
var zoom = me.ranges[size(me.ranges) - 1 - z];
|
||||
# print("Setting zoom range to:", zoom);
|
||||
print("Setting zoom range to: " ~ z ~ " " ~ zoom);
|
||||
benchmark("Zooming map:"~zoom, func {
|
||||
me._node.getNode("range", 1).setDoubleValue(zoom);
|
||||
# TODO update center/limit translation to keep airport always visible
|
||||
|
@ -274,7 +274,7 @@ LayeredMap.setupZoom = func(dialog) {
|
|||
foreach(var r; ranges)
|
||||
append(me.ranges, r.getValue() );
|
||||
|
||||
# print("Setting up Zoom Ranges:", size(ranges)-1);
|
||||
print("Setting up Zoom Ranges:", size(ranges)-1);
|
||||
me.listen(me.zoom_property, func me.updateZoom() );
|
||||
me.updateZoom();
|
||||
me; #chainable
|
||||
|
@ -441,4 +441,3 @@ setlistener("/nasal/canvas/loaded", func {
|
|||
# TODO: should be inside a separate subfolder, i.e. canvas/map/mfd
|
||||
load_modules( files_with('.mfd'), 'canvas' );
|
||||
});
|
||||
|
||||
|
|
37
Nasal/canvas/map/OSM.lcontroller
Normal file
37
Nasal/canvas/map/OSM.lcontroller
Normal file
|
@ -0,0 +1,37 @@
|
|||
# See: http://wiki.flightgear.org/MapStructure
|
||||
# Class things:
|
||||
var name = 'OSM';
|
||||
var parents = [OverlayLayer.Controller];
|
||||
var __self__ = caller(0)[0];
|
||||
OverlayLayer.Controller.add(name, __self__);
|
||||
TileLayer.add(name, {
|
||||
parents: [TileLayer],
|
||||
type: name, # Layer type
|
||||
df_controller: __self__, # controller to use by default -- this one
|
||||
});
|
||||
|
||||
var new = func(layer) {
|
||||
var m = {
|
||||
parents: [__self__],
|
||||
layer: layer,
|
||||
map: layer.map,
|
||||
listeners: [],
|
||||
};
|
||||
|
||||
layer.makeURL = string.compileTemplate('https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}.png');
|
||||
layer.makePath = string.compileTemplate(layer.maps_base ~ '/osm-intl/{z}/{x}/{y}.png');
|
||||
|
||||
m.addVisibilityListener();
|
||||
m.addRangeListener();
|
||||
m.addScreenRangeListener();
|
||||
return m;
|
||||
};
|
||||
|
||||
var updateLayer = func() {
|
||||
}
|
||||
|
||||
var del = func() {
|
||||
#print(name~".lcontroller.del()");
|
||||
foreach (var l; me.listeners)
|
||||
removelistener(l);
|
||||
};
|
38
Nasal/canvas/map/OpenAIP.lcontroller
Normal file
38
Nasal/canvas/map/OpenAIP.lcontroller
Normal file
|
@ -0,0 +1,38 @@
|
|||
# See: http://wiki.flightgear.org/MapStructure
|
||||
# Class things:
|
||||
var name = 'OpenAIP';
|
||||
var parents = [OverlayLayer.Controller];
|
||||
var __self__ = caller(0)[0];
|
||||
OverlayLayer.Controller.add(name, __self__);
|
||||
TileLayer.add(name, {
|
||||
parents: [TileLayer],
|
||||
type: name, # Layer type
|
||||
df_controller: __self__, # controller to use by default -- this one
|
||||
});
|
||||
|
||||
var new = func(layer) {
|
||||
var m = {
|
||||
parents: [__self__],
|
||||
layer: layer,
|
||||
map: layer.map,
|
||||
listeners: [],
|
||||
};
|
||||
|
||||
# http://1.tile.maps.openaip.net/geowebcache/service/tms/1.0.0/openaip_basemap@EPSG%3A900913@png/6/30/43.png
|
||||
layer.makeURL = string.compileTemplate('http://1.tile.maps.openaip.net/geowebcache/service/tms/1.0.0/openaip_basemap@EPSG%3A900913@png/{z}/{x}/{tms_y}.png');
|
||||
layer.makePath = string.compileTemplate(layer.maps_base ~ '/openaip_basemap/{z}/{x}/{tms_y}.png');
|
||||
|
||||
m.addVisibilityListener();
|
||||
m.addRangeListener();
|
||||
m.addScreenRangeListener();
|
||||
return m;
|
||||
};
|
||||
|
||||
var updateLayer = func() {
|
||||
}
|
||||
|
||||
var del = func() {
|
||||
#print(name~".lcontroller.del()");
|
||||
foreach (var l; me.listeners)
|
||||
removelistener(l);
|
||||
};
|
|
@ -164,12 +164,12 @@ var Coord = {
|
|||
me._pupdate();
|
||||
course *= D2R;
|
||||
dist /= ERAD;
|
||||
|
||||
|
||||
if (dist < 0.0) {
|
||||
dist = abs(dist);
|
||||
course = course - math.pi;
|
||||
course = course - math.pi;
|
||||
}
|
||||
|
||||
|
||||
me._lat = math.asin(math.sin(me._lat) * math.cos(dist)
|
||||
+ math.cos(me._lat) * math.sin(dist) * math.cos(course));
|
||||
|
||||
|
@ -398,6 +398,6 @@ var PositionedSearch = {
|
|||
debug.benchmark('Toggle '~from~'nm/'~to~'nm', func {
|
||||
s.update();
|
||||
s.update( func positioned.findWithinRange(to, 'fix') );
|
||||
}); # ~ takes
|
||||
}); # ~ takes
|
||||
}, # of test
|
||||
};
|
||||
|
|
|
@ -25,6 +25,9 @@
|
|||
}
|
||||
}
|
||||
setTransparency(0);
|
||||
|
||||
|
||||
|
||||
]]></open>
|
||||
|
||||
<close><![CDATA[
|
||||
|
@ -213,6 +216,35 @@
|
|||
</binding>
|
||||
</checkbox>
|
||||
|
||||
<checkbox>
|
||||
<label>OSM</label>
|
||||
<halign>left</halign>
|
||||
|
||||
<property>/sim/gui/dialogs/map-canvas/draw-OSM</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>property-toggle</command>
|
||||
</binding>
|
||||
</checkbox>
|
||||
|
||||
<checkbox>
|
||||
<label>OpenAIP</label>
|
||||
<halign>left</halign>
|
||||
|
||||
<property>/sim/gui/dialogs/map-canvas/draw-OpenAIP</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>property-toggle</command>
|
||||
</binding>
|
||||
</checkbox>
|
||||
|
||||
|
||||
<!-- layer only supported if tutorial system is active and targets specified-->
|
||||
<!--
|
||||
<checkbox>
|
||||
|
@ -348,7 +380,9 @@
|
|||
TestMap.setController("Aircraft position", "map-dialog"); # from aircraftpos.controller
|
||||
|
||||
# Initialize a range:
|
||||
TestMap.setRange(20);
|
||||
TestMap.setRange(60);
|
||||
TestMap.setScreenRange(200);
|
||||
|
||||
var range_step = math.log10(TestMap.getRange());
|
||||
# TODO: check if this is valid, IIRC DOM manipulation was fragile when done inside canvas/Nasal region (?)
|
||||
gui.findElementByName(self, "zoomdisplay").setValue("property", TestMap._node.getNode("range").getPath());
|
||||
|
@ -423,16 +457,25 @@
|
|||
TestMap.getLayer(name).setVisible(n);
|
||||
};
|
||||
|
||||
var SetProjection = func(projection) {
|
||||
TestMap._node.setValue("projection", projection);
|
||||
};
|
||||
|
||||
setlistener("/sim/gui/dialogs/map-canvas/projection",
|
||||
func(n) SetProjection(n.getValue());
|
||||
);
|
||||
|
||||
|
||||
Styles.APS = {};
|
||||
Styles.APS.scale_factor = 0.25;
|
||||
|
||||
# TODO: introduce some meta NAV layer that handles both VORs and NDBs, can we instantiate those layers directly ?
|
||||
var r = func(name,vis=1,zindex=nil) return caller(0)[0];
|
||||
# TODO: we'll need some z-indexing here, right now it's just random
|
||||
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'), ] ) {
|
||||
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')] ) {
|
||||
if (1 and type.name != 'APS' and type.name != 'FLT') make_update_wrapper(type.name);
|
||||
TestMap.addLayer(factory: canvas.SymbolLayer, type_arg: type.name,
|
||||
visible: type.vis, priority: type.zindex,
|
||||
visible: type.vis, priority: 4,
|
||||
style: Styles.get(type.name),
|
||||
options: Options.get(type.name) );
|
||||
(func {
|
||||
|
@ -445,6 +488,24 @@
|
|||
);
|
||||
})();
|
||||
}
|
||||
|
||||
foreach(var type; [ r('OSM'), r('OpenAIP') ]) {
|
||||
TestMap.addLayer(factory: canvas.OverlayLayer, type_arg: type.name,
|
||||
visible: type.vis, priority: 1,
|
||||
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()))
|
||||
);
|
||||
})();
|
||||
}
|
||||
|
||||
|
||||
]]></load></nasal>
|
||||
</canvas>
|
||||
<layout>hbox</layout>
|
||||
|
@ -498,7 +559,7 @@
|
|||
</binding>
|
||||
</button>
|
||||
</group>
|
||||
|
||||
</group>
|
||||
</group>
|
||||
</PropertyList>
|
||||
|
||||
|
|
Loading…
Reference in a new issue