// map.js - GMaps integration for big map

var load_hook;
var loc;
var max;
if (!max) {
    max = 50;
}
var navaid_max;
if (!navaid_max) {
	navaid_max = 50;
}

var home_airports;
var show_overview;
var hide_selected_airport;
var edit_marker;
var poi_markers = null;
var pois_displayed = false;
var navaid_marker_map = null;
var navaids_displayed = false;
var url= "/airports.json?limit=" + max;
var xmlhttp=false;
var map;
var airport_marker_map = [];
var last_zoom = 99999;
var selected_airport;
var selected_airport_ident;
var selected_continent_code;
var selected_country_code;
var selected_region_code;
var selected_member;
var selected_tag;
var selected_role;
var selected_poi_id;
var selected_navaid_id;
var bounds;


var markers_visible = true;

var latlon_node = document.getElementById('latlon');

/**
 * Marker icon for a closed airport.
 */
var closed_airport_marker = new GIcon();
closed_airport_marker.image = '/images/closed-airport-marker.png';
closed_airport_marker.iconSize = new GSize(20, 20);
closed_airport_marker.shadow = '/images/closed-airport-shadow.png';
closed_airport_marker.shadowSize = new GSize(36, 20);
closed_airport_marker.transparent = '/images/closed-airport-mask.png';
closed_airport_marker.imageMap = [14,0, 14,1, 12,1, 12,5, 19,5, 19,9, 12,9,
                                 11,16, 14,16, 14,19,
                                 5,19, 5,16, 8,16, 7,9, 0,9, 0,5, 7,5,
                                 7,1, 5,1, 5,0];
closed_airport_marker.iconAnchor = new GPoint(10, 10);
closed_airport_marker.infoWindowAnchor = new GPoint(10, 10);

var closed_selected_marker = new GIcon(closed_airport_marker);
closed_selected_marker.image = '/images/closed-airport-selected.png';

var closed_greyed_marker = new GIcon(closed_airport_marker);
closed_greyed_marker.image = '/images/closed-airport-greyed.png';


/**
 * Marker icon for a heliport.
 */
var heliport_marker = new GIcon();
heliport_marker.image = '/images/heliport-marker.png';
heliport_marker.iconSize = new GSize(20, 20);
heliport_marker.shadow = '/images/heliport-shadow.png';
heliport_marker.shadowSize = new GSize(36, 20);
heliport_marker.transparent = '/images/heliport-mask.png';
heliport_marker.imageMap = [0,0, 0,19, 19,19, 19,0];
heliport_marker.iconAnchor = new GPoint(10, 10);
heliport_marker.infoWindowAnchor = new GPoint(0, 19);

/**
 * Marker icon for a selected heliport.
 */
var heliport_selected_marker = new GIcon(heliport_marker);
heliport_selected_marker.image = '/images/heliport-selected-marker.png';


/**
 * Greyed-out heliport.
 */
var heliport_greyed_marker = new GIcon(heliport_marker);
heliport_greyed_marker.image = '/images/heliport-greyed-marker.png';


/**
 * Marker icon for a seaplane base.
 */
var seaplane_base_marker = new GIcon();
seaplane_base_marker.image = '/images/seaplane-base-marker.png';
seaplane_base_marker.iconSize = new GSize(20, 20);
seaplane_base_marker.shadow = '/images/seaplane-shadow.png';
seaplane_base_marker.shadow_size = new GSize(36, 20);
seaplane_base_marker.transparent = '/images/seaplane-base-mask.png';
heliport_marker.imageMap = [0,0, 0,19, 19,19, 19,0];
seaplane_base_marker.iconAnchor = new GPoint(10, 10);
seaplane_base_marker.infoWindowAnchor = new GPoint(10, 10);

var seaplane_selected_marker = new GIcon(seaplane_base_marker);
seaplane_selected_marker.image = '/images/seaplane-base-selected.png';

/**
 * Marker icon for a greyed-out seaplane base.
 */
var seaplane_greyed_marker = new GIcon(seaplane_base_marker);
seaplane_greyed_marker.image = '/images/seaplane-greyed-marker.png';


/**
 * Marker icon for a small airport.
 */
var small_airport_marker = new GIcon();
small_airport_marker.image = '/images/small-airport-marker.png';
small_airport_marker.iconSize = new GSize(20, 20);
small_airport_marker.shadow = '/images/small-airport-shadow.png';
small_airport_marker.shadowSize = new GSize(36, 20);
small_airport_marker.transparent = '/images/small-airport-mask.png';
small_airport_marker.imageMap = [14,0, 14,1, 12,1, 12,5, 19,5, 19,9, 12,9,
                                 11,16, 14,16, 14,19,
                                 5,19, 5,16, 8,16, 7,9, 0,9, 0,5, 7,5,
                                 7,1, 5,1, 5,0];
small_airport_marker.iconAnchor = new GPoint(10, 10);
small_airport_marker.infoWindowAnchor = new GPoint(10, 10);


/**
 * Marker icon for a small selected airport.
 */
var small_selected_marker = new GIcon(small_airport_marker);
small_selected_marker.image = '/images/small-selected-marker.png';


/**
 * Marker icon for a small greyed-out airport.
 */
var small_greyed_marker = new GIcon(small_airport_marker);
small_greyed_marker.image = '/images/small-greyed-marker.png';


/**
 * Marker icon for a medium airport.
 */
var medium_airport_marker = new GIcon();
medium_airport_marker.image = '/images/medium-airport-marker.png';
medium_airport_marker.iconSize = new GSize(24, 24);
medium_airport_marker.shadow = '/images/medium-airport-shadow.png';
medium_airport_marker.shadowSize = new GSize(40, 24);
medium_airport_marker.transparent = '/images/medium-airport-mask.png';
medium_airport_marker.imageMap = [13,0, 13,3, 20,3, 20,6, 23,6, 23,10,
                                  13,12, 13,19, 18,19, 18,23,
                                  5,23, 5,19, 10,19, 10,12, 0,10, 0,6,
                                  4,6, 4,3, 11,3, 11,0];
medium_airport_marker.iconAnchor = new GPoint(12, 12);
medium_airport_marker.infoWindowAnchor = new GPoint(12, 12);


/**
 * Marker icon for a medium selected airport.
 */
var medium_selected_marker = new GIcon(medium_airport_marker);
medium_selected_marker.image = '/images/medium-selected-marker.png';


/**
 * Marker icon for a medium greyed-out airport.
 */
var medium_greyed_marker = new GIcon(medium_airport_marker);
medium_greyed_marker.image = '/images/medium-greyed-marker.png';


/**
 * Marker icon for a large airport.
 */
var large_airport_marker = new GIcon();
large_airport_marker.image = '/images/large-airport-marker.png';
large_airport_marker.iconSize = new GSize(32, 32);
large_airport_marker.shadow = '/images/large-airport-shadow.png';
large_airport_marker.shadowSize = new GSize(44, 32);
large_airport_marker.transparent = '/images/large-airport-mask.png';
large_airport_marker.imageMap = [16,0, 19,4, 19,9, 31,21, 31,26, 19,19,
                                 18,20, 18,26, 24,31,
                                 7,31, 13,26, 13,20, 12,19, 0,26, 0,21,
                                 12,9, 12,4, 15,0];
large_airport_marker.iconAnchor = new GPoint(16, 16);
large_airport_marker.infoWindowAnchor = new GPoint(16, 16);


/**
 * Marker icon for a large selected airport.
 */
var large_selected_marker = new GIcon(large_airport_marker);
large_selected_marker.image = '/images/large-selected-marker.png';


/**
 * Marker icon for a large greyed airport.
 */
var large_greyed_marker = new GIcon(large_airport_marker);
large_greyed_marker.image = '/images/large-greyed-marker.png';


/**
 * Marker icon for a single member.
 */
var single_member_marker = new GIcon();
single_member_marker.image = '/images/single-member-marker.png';
single_member_marker.iconSize = new GSize(16, 16);
single_member_marker.shadow = '/images/single-member-shadow.png';
single_member_marker.shadowSize = new GSize(24, 16);
single_member_marker.transparent = '/images/single-member-mask.png';
single_member_marker.imageMap = [0,0, 0,15, 15,15, 15,0];
single_member_marker.iconAnchor = new GPoint(8, 8);
single_member_marker.infoWindowAnchor = new GPoint(8, 8);


/**
 * Marker icon for multiple members
 */
var multiple_member_marker = new GIcon();
multiple_member_marker.image = '/images/multiple-member-marker.png';
multiple_member_marker.iconSize = new GSize(18, 18);
multiple_member_marker.shadow = '/images/multiple-member-shadow.png';
multiple_member_marker.shadowSize = new GSize(26, 18);
multiple_member_marker.transparent = '/images/multiple-member-mask.png';
multiple_member_marker.imageMap = [0,0, 0,17, 17,17, 17,0];
multiple_member_marker.iconAnchor = new GPoint(9, 9);
multiple_member_marker.infoWindowAnchor = new GPoint(9, 9);

/**
 * Marker icon for a VOR
 */
var vor_marker = new GIcon();
vor_marker.image = '/images/vor-marker.png';
vor_marker.iconSize = new GSize(20, 20);
vor_marker.shadow = '/images/vor-shadow.png';
vor_marker.shadowSize = new GSize(26, 18);
vor_marker.transparent = '/images/vor-mask.png';
vor_marker.imageMap = [0,0, 0,19, 19,19, 19,0];
vor_marker.iconAnchor = new GPoint(10, 10);
vor_marker.infoWindowAnchor = new GPoint(10, 10);

/**
 * Marker icon for a VOR-DME
 */
var vordme_marker = new GIcon();
vordme_marker.image = '/images/vor-dme-marker.png';
vordme_marker.iconSize = new GSize(16, 16);
vordme_marker.shadow = '/images/vor-dme-shadow.png';
vordme_marker.shadowSize = new GSize(36, 16);
vordme_marker.transparent = '/images/vor-dme-mask.png';
vordme_marker.imageMap = [0,0, 0,15, 15,15, 15,0];
vordme_marker.iconAnchor = new GPoint(8, 8);
vordme_marker.infoWindowAnchor = new GPoint(8, 8);

/**
 * Marker icon for a VORTAC
 */
var vortac_marker = new GIcon();
vortac_marker.image = '/images/vortac-marker.png';
vortac_marker.iconSize = new GSize(20, 20);
vortac_marker.shadow = '/images/vortac-shadow.png';
vortac_marker.shadowSize = new GSize(40, 20);
vortac_marker.transparent = '/images/vortac-mask.png';
vortac_marker.imageMap = [0,0, 0,19, 19,19, 19,0];
vortac_marker.iconAnchor = new GPoint(10, 10);
vortac_marker.infoWindowAnchor = new GPoint(10, 10);

/**
 * Marker icon for a TACAN
 */
var tacan_marker = new GIcon();
tacan_marker.image = '/images/tacan-marker.png';
tacan_marker.iconSize = new GSize(20, 20);
tacan_marker.shadow = '/images/tacan-shadow.png';
tacan_marker.shadowSize = new GSize(40, 20);
tacan_marker.transparent = '/images/tacan-mask.png';
tacan_marker.imageMap = [0,0, 0,19, 19,19, 19,0];
tacan_marker.iconAnchor = new GPoint(10, 10);
tacan_marker.infoWindowAnchor = new GPoint(10, 10);

/**
 * Marker icon for an NDB
 */
var ndb_marker = new GIcon();
ndb_marker.image = '/images/ndb-marker.png';
ndb_marker.iconSize = new GSize(16, 16);
ndb_marker.shadow = '/images/ndb-shadow.png';
ndb_marker.shadowSize = new GSize(32, 16);
ndb_marker.transparent = '/images/ndb-mask.png';
ndb_marker.imageMap = [0,0, 0,15, 15,15, 15,0];
ndb_marker.iconAnchor = new GPoint(8, 8);
ndb_marker.infoWindowAnchor = new GPoint(8, 8);

/**
 * Marker icon for an NDB-DME
 */
var ndbdme_marker = new GIcon();
ndbdme_marker.image = '/images/ndb-dme-marker.png';
ndbdme_marker.iconSize = new GSize(16, 16);
ndbdme_marker.shadow = '/images/ndb-dme-shadow.png';
ndbdme_marker.shadowSize = new GSize(36, 16);
ndbdme_marker.transparent = '/images/ndb-dme-mask.png';
ndbdme_marker.imageMap = [0,0, 0,15, 15,15, 15,0];
ndbdme_marker.iconAnchor = new GPoint(8, 8);
ndbdme_marker.infoWindowAnchor = new GPoint(8, 8);

/**
 * Marker icon for a DME
 */
var dme_marker = new GIcon();
dme_marker.image = '/images/dme-marker.png';
dme_marker.iconSize = new GSize(16, 16);
dme_marker.shadow = '/images/dme-shadow.png';
dme_marker.shadowSize = new GSize(36, 16);
dme_marker.transparent = '/images/dme-mask.png';
dme_marker.imageMap = [0,0, 0,15, 15,15, 15,0];
dme_marker.iconAnchor = new GPoint(8, 8);
dme_marker.infoWindowAnchor = new GPoint(8, 8);

/**
 * Marker map.
 */
var airport_icon_map = {
    'selected': {
        'closed': closed_selected_marker,
        'heliport': heliport_selected_marker,
        'seaplane_base': seaplane_selected_marker,
        'large_airport': large_selected_marker,
        'medium_airport': medium_selected_marker,
        'small_airport': small_selected_marker,
        'balloonport': small_selected_marker
    },
    'excluded': {
        'closed': closed_greyed_marker,
        'heliport': heliport_greyed_marker,
        'seaplane_base': seaplane_greyed_marker,
        'large_airport': large_greyed_marker,
        'medium_airport': medium_greyed_marker,
        'small_airport': small_greyed_marker,
        'balloonport': small_greyed_marker
    },
    'normal': {
        'closed': closed_airport_marker,
        'heliport': heliport_marker,
        'seaplane_base': seaplane_base_marker,
        'large_airport': large_airport_marker,
        'medium_airport': medium_airport_marker,
        'small_airport': small_airport_marker,
        'balloonport': small_airport_marker
    }
};


/**
 * Escape text for HTML.
 */
function esc (text)
{
    if (text) {
        return text.split("&").join("&amp;").split("<").join("&lt;")
            .split(">").join("&gt;");
    } else {
        return "";
    }
}


/**
 * Javascript callback: zoom to an airport.
 */
function zoom_airport (ident)
{
    var airport = airport_marker_map[ident].airport;
    var zoom = map.getZoom();
    if (zoom < 8) {
	zoom = 8;
    } else {
	zoom = zoom + 1;
    }
    map.setCenter(new GLatLng(airport.latitude_deg,
    			      airport.longitude_deg), zoom);
}


/**
 * Correct the map if it's overzoomed.
 * 
 * HACK ALERT: the GMaps API gives no proper way to check for overzoom; this
 * function counts 'p' elements, which happen to be present only when the map is
 * overzoomed. Google could break this hack at any time, resulting in bizarre
 * auto-zooming behaviour.
 */
function check_zoom (map)
{
    var zoom = map.getZoom();
    var count = map.getContainer().getElementsByTagName('p').length;

    if (zoom > 1 && count > 3) {
        map.setZoom(zoom - 1);
    }
}


/**
 * Javascript callback: select an airport as reference.
 */
function select_airport (ident)
{
    var old_selected = selected_airport;
    selected_airport_ident = ident;
    selected_airport = airport_marker_map[ident].airport;
    selected_airport.selected = true;
    if (old_selected) {
	old_selected.selected = false;
	update_marker(old_selected);
    }
    map.closeInfoWindow();
    update_marker(selected_airport);
    update_hash();
    clear_pois();
    update_pois(map.getBounds());
}


/**
 * Calculate the great-circle distance between two airports.
 */
function great_circle_distance (a1, a2)
{
    var R = 6371; // km
    var lat1 = a1['latitude_deg'] * Math.PI / 180;
    var lat2 = a2['latitude_deg'] * Math.PI / 180;
    var lon1 = a1['longitude_deg'] * Math.PI / 180;
    var lon2 = a2['longitude_deg'] * Math.PI / 180;
    var dLat = (lat2-lat1);
    var dLon = (lon2-lon1);
    var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
        Math.cos(lat1) * Math.cos(lat2) * 
        Math.sin(dLon/2) * Math.sin(dLon/2); 
    var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
    var d = R * c;
    return d;
}


/**
 * Check if an airport is outside the selected viewing filter.
 */
function is_excluded (airport)
{
    if (selected_member && !selected_tag && !airport.visited)
    	return false;
    if (selected_continent_code && airport.continent_code != selected_continent_code)
    	return true;
    if (selected_country_code && airport.country_code != selected_country_code)
    	return true;
    if (selected_region_code && airport.region_code != selected_region_code)
    	return true;
    return false;
}


/**
 * Given an airport structure, make a marker for it.
 */
function make_marker (airport)
{
    var location =
        new GLatLng(airport['latitude_deg'], airport['longitude_deg']);
    var icon;

    if (home_airports) {
        if (airport.home_count > 1) {
            icon = multiple_member_marker;
        } else {
            icon = single_member_marker;
        }
    } else if (airport.selected) {
        icon = airport_icon_map['selected'][airport.type];
    } else if (is_excluded(airport)) {
        icon = airport_icon_map['excluded'][airport.type];
    } else {
        icon = airport_icon_map['normal'][airport.type];
    }

    var title = airport.name;
    if (home_airports) {
        title += " (" + airport.home_count + " member" + (airport.home_count > 1 ? "s)" : ")");
    }
    var marker =
        new GMarker(location, {'icon': icon,
                               'title': title});
    marker.airport = airport;
    return marker;
}


/**
 * Hide all markers.
 */
function toggle_markers ()
{
    var ident;

    for (ident in airport_marker_map) {
        if (markers_visible) {
            airport_marker_map[ident].hide();
        } else {
            airport_marker_map[ident].show();
        }
    }

    markers_visible = !markers_visible;
}


/**
 * Given a list of airports, create markers and add them to the map.
 */
function make_markers (airports)
{
    if (airports) {
        for (i = airports.length-1; i >= 0; i--) {
            var airport = airports[i];
            if (selected_airport_ident == airports[i]['ident']) {
                selected_airport = airports[i];
                airports[i].selected = true;
                if (hide_selected_airport) {
                    continue;
                }
            }
            var marker;
            if (!airport_marker_map[airport['ident']]) {
                marker = make_marker(airport);
                map.addOverlay(marker);
                airport_marker_map[airport['ident']] = marker;
                if (!markers_visible) {
                    airport_marker_map[airport['ident']].hide();
                }
            }
            if (airport.selected)
                selected_airport = airport;
        }
    }
}


/**
 * Update the marker in place for a specific airport.
 */
function update_marker (airport)
{
    var old_marker = airport_marker_map[airport['ident']];
    if (old_marker) {
	map.removeOverlay(old_marker);
    }
    var new_marker = make_marker(airport);
    airport_marker_map[airport['ident']] = new_marker;
    map.addOverlay(new_marker);
}


/**
 * Generate an airport description for an info balloon.
 */
function airport_info (marker)
{
    var airport = marker.airport;
    var html = "<span class=\"airport-marker\" style=\"font-size: 80%\">";
    if (airport.iata_code) {
	html += "<strong>" + esc(airport.iata_code) + "</strong> ";
    } else if (airport.gps_code) {
	html += "<strong>" + esc(airport.gps_code) + "</strong> ";
    }
    html += "<a target=\"_top\" href=\"/airports/" +
        escape(airport.ident) + "/\">" + esc(airport.name) + "</a><br />\n";
    if (airport.municipality) {
	html += esc(airport.municipality) + ", ";
    }
    if (airport.region_name && airport.region_local_code != 'U-A') {
	html += "<a target=\"_top\" href=\"/countries/" +
            escape(airport.country_code) + "/" +
            escape(airport.region_local_code) + "/\">" +
            esc(airport.region_name) + "</a>, ";
    }
    html += "<a target=\"_top\" href=\"/countries/" +
        escape(airport.country_code) + "/\">" +
	esc(airport.country_name) + "</a> ";
    if (selected_airport && (marker.airport.ident != selected_airport.ident)) {
	html += '<br />';

	dist_km = Math.floor(great_circle_distance(selected_airport, marker.airport));
	html += dist_km;
	html += " km | ";
	html += Math.floor(dist_km * .6214);
	html += " mi | ";
	html += Math.floor(dist_km * .54);
	html += " nm to " + esc(selected_airport.name);
    }
    html += '<br />';
    html += '<a href="javascript:zoom_airport(\'' +
	escape(airport.ident) + '\');">zoom</a> ';
    if (!airport.selected) {
	html += '| <a href="javascript:select_airport(\'' +
	    escape(airport.ident) + '\');">select</a> ';
    }
    if (airport.comment_count > 0) {
	html += "| <a target=\"_top\" href=\"/airports/" +
            escape(airport.ident) + "/comments.html\">" +
	    esc(airport.comment_count) + " comment" +
            (airport.comment_count > 1 ? 's' : '') + "</a> ";
    }
    if (airport.visitor_count > 0) {
	html += "| <a target=\"_top\" href=\"/airports/" +
            escape(airport.ident) + "/visitors.html\">" +
	    esc(airport.visitor_count) + " visitor" +
            (airport.visitor_count > 1 ? 's' : '') + "</a> ";
    }
    if (airport.home_count > 0) {
	html += "| " + esc(airport.home_count) + " member" +
            (airport.home_count > 1 ? 's' : '');
    }
    html += '</span>';

    return html;
}


/**
 * Clear a selected member (e.g. show all airports).
 */
function clear_member ()
{
    selected_member = null;
    selected_tag = null;
    update_airports(map.getBounds());
    update_pois(map.getBounds());
    update_hash();
}


/**
 * Update the displayed airports when the map has changed.
 */
function update_airports (bounds)
{
    last_zoom = map.getZoom();
    var newUrl = url +
	"&min_lat=" + bounds.getSouthWest().lat() +
	"&min_lon=" + bounds.getSouthWest().lng() +
	"&max_lat=" + bounds.getNorthEast().lat() +
	"&max_lon=" + bounds.getNorthEast().lng();

    var node = document.getElementById('messages');
    if (node) {
    	if (selected_member) {
            if (selected_tag) {
            	domSetValue(node, 'Airports tagged "' + selected_tag + '" by ' + selected_member + ' | ');
            } else {
                domSetValue(node, 'Airports visited by ' + selected_member);
            }
            node.appendChild(domMakeLink('javascript:clear_member()',
            'all airports'));
        	} else if (selected_tag) {
        		domSetValue(node, 'Airports tagged "' + selected_tag + '" | ');
        		node.appendChild(domMakeLink('javascript:clear_member()',
        		'all airports'));
        	} else {
        		domSetValue(node, 'All airports (zoom in for smaller airports)');
        	}
    	}

    	if (home_airports) {
    		newUrl += '&homes=true';
    	} else if (selected_role) {
    		newUrl += '&role=' + selected_role;
    	}

    	if (selected_airport_ident) {
    		newUrl += '&airport=' + selected_airport_ident;
    	}
    	if (selected_member) {
	        newUrl += '&member=' + selected_member;
	    } else if (selected_continent_code) {
	    	newUrl += '&continent=' + selected_continent_code;
	    } else if (selected_country_code) {
	    	newUrl += '&country=' + selected_country_code;
	    	if (selected_region_code) {
	    		newUrl += '&region=' + selected_region_code;
	    	}
	    }
    	if (selected_tag) {
    		newUrl += '&tag=' + selected_tag;
    	}
    	xmlhttp.open("GET", newUrl, true);
    	xmlhttp.onreadystatechange=function() {
    		if (xmlhttp.readyState==4) {
    			var airports = eval(xmlhttp.responseText);
    			make_markers(airports);
    		}
    	}
    	xmlhttp.send(null);
}

/**
 * Clear any existing POIs.
 */
function clear_pois ()
{
    if (poi_markers) {
        for (var i = 0; i < poi_markers.length; i++) {
            map.removeOverlay(poi_markers[i]);
        }
        poi_markers = null;
    }
}

/**
 * Toggle POI visibility.
 */
function toggle_pois ()
{
    if (pois_displayed) {
        pois_displayed = false;
        clear_pois();
    } else {
        pois_displayed = true;
        update_pois();
    }
}


/**
 * Update the displayed POIs when necessary.
 */
function update_pois (bounds)
{
    if (!pois_displayed || !selected_airport_ident) {
        return;
    } else if (poi_markers) {
        for (i = 0; i < poi_markers.length; i++) {
            map.addOverlay(poi_markers[i]);
        }
    } else {
        var newUrl = "/airports/" + selected_airport_ident + "/places.json";
        var xhr = GXmlHttp.create();
        xhr.open("GET", newUrl, true);
        xhr.onreadystatechange=function () {
            if (xhr.readyState == 4) {
                var pois = eval(xhr.responseText);
                make_poi_markers(pois);
            }
        }
        xhr.send(null);
    }
}


/**
 * Show POI markers on the map.
 */
function make_poi_markers (pois)
{
    poi_markers = [];
    if (pois) {
        for (i = 0; i < pois.length; i++) {
            var poi = pois[i];
            var marker = make_poi_marker(poi);
            map.addOverlay(marker);
            poi_markers.push(marker);
        }
    }
    pois_displayed = true;
}

function make_poi_marker (poi)
{
    var icon = G_DEFAULT_ICON;
    if (poi.id == selected_poi_id) {
        var icon = new GIcon(G_DEFAULT_ICON);
        icon.image="/images/gmaps-marker-green.png";
    }
    var location = new GLatLng(poi['latitude_deg'], poi['longitude_deg']);
    var marker = new GMarker(location, {'title': poi.name, 'icon': icon});
    marker.poi = poi;
    return marker;
}

/**
 * Called when someone clicks on a POI marker.
 */
function poi_info (marker)
{
    var poi = marker.poi;
    var html = "<span class=\"poi-marker\" style=\"font-size: 80%\">\n";
    var airport_url = "/airports/" + escape(poi.airportIdent) + "/";
    var poi_url = airport_url + "places/" + escape(poi.id) + "/";
    html += "<strong><a href=\"" + poi_url + "\">" + esc(poi.name) +
        "</a></strong><br />\n";
    html += "<a href=\"" + airport_url + "\"/>" + esc(poi.airportName) +
        "</a><br />";
    html += "<a href=\"" + poi_url + "comments.html\">";
    if (poi.comment_count == 0) {
        html += "No comments yet.";
    } else if (poi.comment_count == 1) {
        html += "1 comment";
    } else {
        html += esc(poi.comment_count) + " comments";
    }
    html += "</a>\n</span>\n";
    return html;
}


/**
 * Show any existing navaids.
 */
function show_navaids ()
{
    if (navaid_marker_map) {
        for (id in navaid_marker_map) {
        	navaid_marker_map[id].show();
        }
    }
    navaids_displayed = true;
}

/**
 * Hide any existing navaids.
 */
function hide_navaids ()
{
    if (navaid_marker_map) {
        for (id in navaid_marker_map) {
        	navaid_marker_map[id].hide();
        }
    }
    navaids_displayed = false;
}

/**
 * Toggle navaid visibility.
 */
function toggle_navaids ()
{
    if (navaids_displayed) {
    	hide_navaids();
    } else {
    	show_navaids();
    	if (!navaid_marker_map) {
    		update_navaids(map.getBounds());
    	}
    }
}


/**
 * Update the displayed navaids when necessary.
 */
function update_navaids (bounds)
{
	var node = document.getElementById('navaid-control');
    if (!navaids_displayed) {
    	if (node) {
    		node.checked = false;
    	}
        return;
    } else {
    	if (node) {
    		node.checked = true;
    	}
        var newUrl = "/navaids.json?max=" + navaid_max + 
        	"&min_lat=" + bounds.getSouthWest().lat() +
        	"&min_lon=" + bounds.getSouthWest().lng() + 
        	"&max_lat=" + bounds.getNorthEast().lat() +
        	"&max_lon=" + bounds.getNorthEast().lng();
        var xhr = GXmlHttp.create();
        xhr.open("GET", newUrl, true);
        xhr.onreadystatechange=function () {
            if (xhr.readyState == 4) {
                var navaids = eval(xhr.responseText);
                make_navaid_markers(navaids);
            }
        }
        xhr.send(null);
    }
}


/**
 * Show navaid markers on the map.
 */
function make_navaid_markers (navaids)
{
    if (navaids) {
    	if (!navaid_marker_map) {
    		navaid_marker_map = [];
    	}
        for (i = 0; i < navaids.length; i++) {
        	var navaid = navaids[i];
            if (!navaid_marker_map[navaid.id]) {
            	var marker = make_navaid_marker(navaid);
            	navaid_marker_map[navaid.id] = marker;
            	map.addOverlay(marker);
            }
        }
    }
    show_navaids();
}

function make_navaid_marker (navaid)
{
	var icon;
	if (navaid.type == 'VOR') {
		icon = vor_marker;
	} else if (navaid.type == 'VOR-DME') {
		icon = vordme_marker;
	} else if (navaid.type == 'VORTAC') {
		icon = vortac_marker;
	} else if (navaid.type == 'TACAN') {
		icon = tacan_marker;
	} else if (navaid.type == 'NDB') {
		icon = ndb_marker;
	} else if (navaid.type == 'NDB-DME') {
		icon = ndbdme_marker;
	} else if (navaid.type == 'DME') {
		icon = dme_marker;
	} else {
		icon = G_DEFAULT_ICON;
	}
    var location = new GLatLng(navaid['latitude_deg'], navaid['longitude_deg']);
    var marker = new GMarker(location, {'title': navaid.name + ' ' + navaid.type, 'icon': icon});
    marker.navaid = navaid;
    return marker;
}

/**
 * Called when someone clicks on a navaid marker.
 */
function navaid_info (marker)
{
    var navaid = marker.navaid;
    var html = "<span class=\"navaid-marker\" style=\"font-size: 80%\">\n";
    var navaid_url = "/navaids/" + esc(navaid.ident) + "/" + esc(navaid.filename) + "/";
    html += "<strong><a href=\"" + navaid_url + "\">" + esc(navaid.name) + ' ' + esc(navaid.type) +
        "</a></strong><br />\n";
    html += esc(navaid.ident) + ', ';
    html += '<a href="/countries/' + esc(navaid.country_code) + '/">' + esc(navaid.country_name) + '</a>, ';
    if (navaid.type == 'NDB') {
    	html += navaid.frequency_khz + ' KHz';
    } else if (navaid.type == 'NDB-DME') {
    	html += navaid.frequency_khz + ' KHZ/' + (navaid.dme_frequency_khz/1000) + ' MHz';
    } else {
    	html += (navaid.frequency_khz/1000) + 'MHz';
    }
    html += "\n</span>\n";
    return html;
}


/**
 * Callback to handle a map change (move, zoom, etc.).
 */
function do_move ()
{
    if (map.getZoom() < last_zoom) {
    	map.clearOverlays();
    	airport_marker_map = [];
    	navaid_marker_map = [];
        if (edit_marker) {
            map.addOverlay(edit_marker);
        }
    }
    update_airports(map.getBounds());
    update_pois(map.getBounds());
    update_navaids(map.getBounds());
    update_hash();
    update_latlon();
}


function update_latlon ()
{
    if (latlon_node) {
        domSetValue(latlon_node, map.getCenter().lat().toFixed(6) + " " + map.getCenter().lng().toFixed(6));
    }
}

/**
 * Callback to handle a map type change.
 */
function do_type_change ()
{
    update_hash();
}


/**
 * Parse the current location identifier for the URL.
 */
function parse_hash ()
{
    var hash = window.location.hash;
    var info;
    if (loc) {
    	info = loc;
    } else {
    	info = {'lat': 0,
		'lon': 0,
		'zoom': 2,
		'type': 'Hybrid'};
    }

    if (hash) {
	if (hash.substring(0, 1) == '#') {
	    hash = hash.substring(1);
	}
    
	var f = hash.split(',');
	for (i = 0; i < f.length; i++) {
	    var nv = f[i].split('=');
	    info[nv[0]] = nv[1];
	}
    }

    // This overrides latitude: figure out how to zoom to this bounding box
    if (info.min_lat) {
	info.lat = (info.min_lat - 0) + (info.max_lat - info.min_lat - 0) / 2;
	info.lon = (info.min_lon - 0) + (info.max_lon - info.min_lon - 0) / 2;
	info.zoom =
	    map.getBoundsZoomLevel
	    (new GLatLngBounds(new GLatLng(info.min_lat, info.min_lon),
			       new GLatLng(info.max_lat, info.max_lon)));
    }

    // FIXME refactor
    selected_airport_ident = info.airport;
    selected_continent_code = info.continent;
    selected_country_code = info.country;
    selected_region_code = info.region;
    selected_member = info.member;
    selected_tag = info.tag;
    navaids_displayed = (info.navaids?true:false);

    return info;
}


/**
 * Update the location identifier in the URL.
 */
function update_hash ()
{
    var hash = 'lat=' + map.getCenter().lat() + ',' +
	'lon=' + map.getCenter().lng() + ',' +
	'zoom=' + map.getZoom() + ',' +
	'type=' + map.getCurrentMapType().getName();
    if (selected_airport_ident) {
	hash += ',airport=' + selected_airport_ident;
    }
    if (selected_continent_code) {
	hash += ',continent=' + selected_continent_code;
    }
    if (selected_country_code) {
	hash += ',country=' + selected_country_code;
	if (selected_region_code) {
	    hash += ',region=' + selected_region_code;
	}
    }
    if (selected_member) {
	hash += ',member=' + selected_member;
    }
    if (selected_tag) {
        hash += ',tag=' + selected_tag;
    }
    window.location.hash = hash;
}


/**
 * Parse a string describing the map type.
 */
function get_map_type (name)
{
    if (name == 'Satellite') {
	return G_SATELLITE_MAP;
    } else if (name == 'Terrain') {
        return G_PHYSICAL_MAP;
    } else if (name == 'Map') {
	return G_NORMAL_MAP;
    } else {
	return G_HYBRID_MAP;
    }
}


/**
 * Load the map.
 */
function load () {
    if (GBrowserIsCompatible()) {
	var node = document.getElementById("map");

	map = new GMap2(document.getElementById("map"));
        map.addMapType(G_PHYSICAL_MAP);

	if (node.clientHeight >= 300) {
	    map.addControl(new GLargeMapControl());
	} else {
	    map.addControl(new GSmallMapControl());
	}
	if (node.clientWidth >= 400) {
            map.addControl(new GScaleControl());
	    map.addControl(new GHierarchicalMapTypeControl());
            if (show_overview) {
                map.addControl(new GOverviewMapControl());
            }
	}
	// hash has to override bounds
	if (!loc || (window.location.hash.length > 0)) {
	    loc = parse_hash();
	} else if (bounds) {
		loc.zoom = map.getBoundsZoomLevel(bounds);
		if (loc.zoom > 10)
		    loc.zoom = 10;
		var center = bounds.getCenter();
		loc.lat = center.lat();
		loc.lon = center.lng();
	}

	map.setCenter(new GLatLng(loc.lat - 0, loc.lon - 0), (loc.zoom - 0));
	map.setMapType(get_map_type(loc.type));
	GEvent.addListener(map, 'moveend', do_move);
	GEvent.addListener(map, 'maptypechanged', do_type_change);
	GEvent.addListener(map, 'click', function (marker) {
		if (marker && marker.airport) {
		    marker.openInfoWindowHtml(airport_info(marker));
                } else if (marker && marker.poi) {
                    marker.openInfoWindowHtml(poi_info(marker));
                } else if (marker && marker.navaid) {
                	marker.openInfoWindowHtml(navaid_info(marker));
                }
	    });
	xmlhttp = GXmlHttp.create();
	update_airports(map.getBounds());
        update_pois(map.getBounds());
        update_navaids(map.getBounds());
        update_latlon();
        if (edit_marker) {
            map.addOverlay(edit_marker);
        }
        if (load_hook) {
            load_hook(map);
        }

        // check every 2 seconds for overzoom.
        // setInterval("check_zoom(map)", 4000);
    }
}

/**
 * Work around bug in Google Maps API.
 */
function doUnload ()
{
    try { 
        GUnload();
    } catch(e) {} 
}

/* end */
