Howto:Supporting additional MapStructure TileMap providers
This article is a stub. You can help the wiki by expanding it. |
Objective
Demonstrate how to extend map-canvas.xml so that it supports additional tilemap providers (a trivial drop-down box to choose between STAMEN, OSM and VFRMap as it should be really straightforward[1] ), including mutually-exclusive ones; i.e. implement tile map providers with gui selector in PUI/XML[2]
This will also involve adapting the caching scheme.
Background
Thanks to Stuart's recent work, it's now rather easy to create additional tile providers. One such example is Stamen maps.
Exemplary patch to implement this is located here: https://gist.github.com/slawekmikula/7e037d3a45169b71df3cf86ffeecbd86 There is commented out three different map styles (terrain + overlay, terrain, toner). One can apply this patch to get new tile rendering.[3]
We will be editing map-canvas.xml to add a new <combo>
widget that is populated via the lcontroller to contain a list of supported tilemap providers.
To address the issue of mutually-exclusive "layers", it would be best not to use a checkbox but rather a combo/select (list) box.
To see how this is done, refer to $FG_ROOT/Docs/README.gui and $FG_ROOT/gui/dialogs for existing examples. That should be straightforward to do.
it's probably not a good idea to "hard-code" these map providers/web services at the lcontroller level - instead, it would be better to introduce a new PropertyList-XML based configuration file that contains a list of supported APIs, and then only parse that to populate said combo box. That way, it would also be straightforward to provide a UI to add/edit and maintain/update entries.[4]
Such a config file could also be more easily updated/maintained in the future (akin to defaults.xml), possibly using a script running on the build server
Tile Ordering Scheme
Simply use {tms_y} instead of {y} in the compileTemplate() call. See the OpenAIP.lcontroller for an example. Apologies for not documenting this.[5]
Step by Step
- Open $FG_ROOT/Docs/README.gui to see how
<combo>
widgets can be used - Open flightgear/fgdata/next/gui/dialogs/map-canvas.xml
- Open OpenAIP
Populating the combo box
To populate the combo box dynamically, this requires executing a bit of Nasal code as part of the nasal/open section in map-canvas.xml, which currently looks like this:
<nasal>
<open><![CDATA[
var self = cmdarg();
var listeners = [];
var setTransparency = func(updateDialog){
var alpha = (getprop("/sim/gui/dialogs/map-canvas/transparent") or 0);
self.getNode("color/alpha").setValue(1-alpha*0.3);
var n = props.Node.new({ "dialog-name": "map-canvas" });
if (updateDialog)
{
fgcommand("dialog-close", n);
fgcommand("dialog-show", n);
}
}
setTransparency(0);
]]></open>
<close><![CDATA[
TestMap.del();
foreach (var l; listeners)
removelistener(l);
setsize(listeners, 0);
]]></close>
</nasal>
Note The findElementByName() API requires a handle to the PropertyList file, i.e. using the cmdarg() API |
Basically, we need to get a handle to the combo box and fill in a list of values using the data fetched from the lcontroller.
This is commonly done using an API found in gui.nas: gui.findElementByName
, e.g.:
var combo = gui.findElementByName(self, "tilemap-combo");
The only thing missing to make this work is adding a corresponding <name>
to the corresponding <combo>
tag, referring to the example found in $FG_ROOT/Docs/README.gui, slightly adapting it:
<combo>
<name>tilemap-combo</name>
<property>/sim/gui/dialogs/map-canvas/selected-tilemap-provider</property>
<value>OSM</value>
<value>OpenAIP</value>
<value>VFChart</value>
<value>Stamen</value>
</combo>
Next, we need to register a listener to map tilemap changes to the corresponding lcontroller (make sure to remove the listener in the dialog's <close>
block shown above)
Instead of hard-coding the layer names in map-canvas.xml, we want to call an API in the lcontroller to fetch a vector of layers and popupate the values of the combo/list box dynamically. To see how this is done look for "combo" and "value" in gui.nas:
foreach(opt; opts) {
combo.addChild("value").prop().setValue(opt);
}
Requirements
- randomize URL parts (CDN)
- support constraints (latitude/longitude)
- support constraints (zoom level)
File Format
See PropertyList XML File for the main article about this subject. |
The markup should be based on the customization requirements of the existing 3 tilemap layers we currently have, namely:
In particular, the last one (VFRChart.lcontroller) is well-suited to serve as a template, because it already uses a vector with sources for different places:
m.sources = [
{ name : 'US',
url : 'http://vfrmap.com/20140918/tiles/vfrc/{z}/{tms_y}/{x}.jpg',
path : layer.maps_base ~ '/vfrmap/{z}/{tms_y}/{x}.jpg',
min_zoom : 3, max_zoom : 12,
min_lat : 16, max_lat : 72,
min_lon : -179, max_lon: -60
},
{ name: 'DE',
url : 'https://secais.dfs.de/static-maps/ICAO500-2015-EUR-Reprojected_07/tiles/{z}/{x}/{y}.png',
path : layer.maps_base ~ '/secais/{z}/{x}/{y}.png',
min_zoom : 5, max_zoom : 15,
min_lat : 46, max_lat : 55.1,
min_lon : 5, max_lon: 16.5 }
];
Work in progress This article or section will be worked on in the upcoming hours or days. See history for the latest developments. |
<?xml version="1.0" encoding="UTF-8"?>
<PropertyList>
<tilemap-providers>
<provider>
<name type="string"></name>
<url-template type="string"></url-template>
<path-template type="string"></path-template>
<constraints>
<zoom>
<minimum type="int"></minimum>
<maximum type="int"></maximum>
</zoom>
<latitude>
<minimum type="double"></minimum>
<maximum type="double"></maximum>
</latitude>
<longitude>
<minimum type="double"></minimum>
<maximum type="double"></maximum>
</longitude>
</constraints>
</provider>
</tilemap-providers>
</PropertyList>
UI Frontend
See Howto:Creating a Canvas GUI dialog file for the main article about this subject. |
Note for the following snippet to work, the lcontroller/MapStructure API needs to be extended so that the lcontroller can provide a list of supported sources, so that the combo/list widgets can be populated accordingly. Equally, it would make sense to also support parsing the same file from $FG_HOME, so that end-users can override/customize these sources |
This code is intended to also work by running via the built-in Nasal Console
Related
References
|