Displaying multiple KML files in CloudMade
The documentation for CloudMade’s Web Maps API is often a bit wanting. For example, a few pages consist of the words “Interface description coming soon…” In any case, I wanted to load multiple KML files at once, and the way to get that working ended up being a bit unintuitive, so I thought I’d share my experiences here.
Loading one KML file is easy enough. Straight from the Web Maps API wiki:
var geoxml = new CM.GeoXml('http://earthquake.usgs.gov/eqcenter/catalogs/eqs7day-M2.5.xml'); CM.Event.addListener(geoxml, 'load', function() { map.zoomToBounds(geoxml.getDefaultBounds()); map.addOverlay(geoxml); });
One weakness with the API is that you can’t pass CM.GeoXml a string. You can, however provide {local: true} as an additional argument if the url points to a local address:
var geoxml = new CM.GeoXml('/static/kmls/catalogs/eqs7day-M2.5.xml', {local: true}); CM.Event.addListener(geoxml, 'load', function() { map.zoomToBounds(geoxml.getDefaultBounds()); map.addOverlay(geoxml); });
For my current project, I have about 30 KML files of location data, each KML depicting the state of a network in a different year. I want to show one of these KMLs at a time, but I’d like to have them all loaded up front to keep interaction with the user nice and quick. Rather than combining all 30 KMLs into one master KML file and then using javascript and CloudMade to manually control which objects to hide and show, I wanted towrite a function that just looped through the 30 filenames and loaded them all in the method shown above.
The problem was that doing so caused my browser to go into meltdown.
Here’s an example of that first (unsuccessful) attempt to load all the KML files:
var geoxml;
for (i in filenames)
{
geoxmls[i] = new CM.GeoXml(filenames[i], {local: true}); CM.Event.addListener(geoxmls[i], 'load', function() { map.zoomToBounds(geoxmls[i].getDefaultBounds()); map.addOverlay(geoxmls[i]);
alert("Finished loading XML #" + i);
}
});
Honestly, I’ve never seen a browser freak out more, except maybe ten years ago on Internet Explorer when I inevitably stumbled onto an endless minefield of pop-up windows all offering me chances to win free iPods and free trials of get-rich-quick schemes.
Anyway, the next thing I did was try to understand what this code was actually doing–a strange second step, I realize, but usually having faith in code examples works just fine for me.
Apparently, when you call CM.GeoXml that call to the url you provide is made right away. But there is a CM.Event attached to that GeoXml object, called ‘load,’ that will fire whenever the server call is complete. The “Listener” function that you add will then be called as soon as the KML object is loaded.
In other words, I can’t be guaranteed that my KML files will be loaded in the same order as they are listed in my “filenames” array. This was immediately evident when, just before my browser melted, I saw a flash of alert windows listing the recently loaded KML filenames indices out of order.
Possibly, the reason the above code doesn’t work is because all the load events will fire at strange times, and maybe it’s too much for the CM.Event listeners to handle. Maybe calling map.addOverlay on multiple things at once just screws things up. Who knows.
In the end, I decided to nest my Listeners so that each KML file would not be loaded until the one before it had fired its ‘load’ Event and been added to the overlay. This might take a while, so I made sure to temporarily dim the screen so that the user doesn’t try to interact with anything until every file is loaded.
The final (working) result looks something like this:
initialize_assignments();
load_kml_and_listen(0, 0);
function initialize_assignments()
{
current_bmap.kmls = [];
current_bmap.kmls[“all”] = [];
for (var i in all_subproblems)
{
cur_sub = all_subproblems[i];
current_bmap.kmls.all[cur_sub] = [];
}
$.loading({pulse:’fade’, mask: true, align: ‘center’, text: ‘Loading KMLs…’}); // dim screen
}
function load_kml_and_listen(i, j)
{
cur_subproblem = all_subproblems[i];
cur_product = all_products[j];
current_bmap.kmls.all[cur_subproblem][cur_product] = new CM.GeoXml(‘quoll/assignments_to_kml?subproblem_name=’+cur_subproblem+’&product_name=’+cur_product, {local:true} );
CM.Event.addListener(current_bmap.kmls.all[cur_subproblem][cur_product], ‘load’, function()
{
if (i == 0 && j == 0)
{
show_kml(current_bmap, cur_subproblem, cur_product);
}if (i < all_subproblems.length – 1)
{
next_i = i + 1;
next_j = j;
}
else
{
next_i = 0;
next_j = j + 1;
}
if (next_j <= all_products.length – 1)
{
load_kml_and_listen(current_bmap, te_key, next_i, next_j);
}
else
{
$.loading(); // lighten screen
}
});
}
Leave a Reply
Want to join the discussion?Feel free to contribute!