//
//	TerraServer Functionality for Google Maps API V2
//      Philip Gladstone
//      http://pond.gladstonefamily.net
//      May 2006
//      Heavily based on code by
//	Nick Jacobsen
//	http://www.lokkju.com
//	July, 2005
//
//	Some UTM functions taken from taken from http://www.mccormick.uk.com/html/distancecalculator.html
//	Some coding concepts based on David Schuetz's Terraserver Functionality
// 		for Google Maps Standalone Mode (http://www.dasnet.org/node/101)
//
//	Use of this file is governed by the 3 clause BSD style license
//

function _makeUSGSMap(url, minZ, maxZ, label, copyright) {

	var zone_width = Math.floor(1000000/102400) * 102400;
	
	
	//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 
	// UTM conversion functions, etc.
	// taken from http://www.mccormick.uk.com/html/distancecalculator.html
	//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 
	
	var deg2rad = Math.PI / 180;
	var rad2deg = 180.0 / Math.PI;
	var pi = Math.PI;
	
	
	
	//===================================================================
	function geo_constants(ellipsoid)
	{
	// return values as an object
	var ellipsoid = { axis: 6378137,
			eccentricity: 0.00669438 };
	return ellipsoid;
	}
	
	function calc_utm(lat, lon)
	{
	// DJS - hardcode ellipsoid to 21, and get the appropriate access and eccent
	var ellipsoid = geo_constants(21);  
	var axis = ellipsoid.axis;
	var eccent = ellipsoid.eccentricity;
	
	var k0 = 0.9996;
	var latrad = lat * deg2rad;
	var longrad = lon * deg2rad;
	var zonenum = floor((lon + 180) / 6) + 1;
	if (lat >= 56.0 && lat < 64.0 && lon >= 3.0 && lon < 12.0 )
	zonenum = 32;
	// Special zones for Svalbard
	if( lat >= 72.0 && lat < 84.0 ) 
	{
	if (lon >= 0.0  && lon <  9.0 ) zonenum = 31;
	else if ( lon >= 9.0  && lon < 21.0 ) zonenum = 33;
	else if ( lon >= 21.0 && lon < 33.0 ) zonenum = 35;
	else if ( lon >= 33.0 && lon < 42.0 ) zonenum = 37;
	}
	
	var lonorig = (zonenum - 1) * 6 - 180 + 3;  //+3 puts origin in middle of zone
	var lonorigrad = lonorig * deg2rad;
	
	var eccPrimeSquared = (eccent) / (1 - eccent);
	
	//calculate
	var N = axis / sqrt(1 - eccent * sin(latrad) * sin(latrad));
	var T = tan(latrad) * tan(latrad);
	var C = eccPrimeSquared * cos(latrad) * cos(latrad);
	var A = cos(latrad) * (longrad - lonorigrad);
	var M = axis * ((1 - eccent / 4 - 3 * eccent * eccent / 64 - 5 * eccent * eccent * eccent / 256) * latrad - (3 * eccent / 8 + 3 * eccent * eccent / 32 + 45 * eccent * eccent *eccent / 1024) * sin(2 * latrad) + (15 * eccent * eccent / 256 + 45 * eccent * eccent * eccent / 1024) * sin(4 * latrad) - (35 * eccent * eccent * eccent / 3072) * sin(6 * latrad));
	
	var easting = (k0 * N * (A + (1 - T + C) * A * A * A / 6 + (5 - 18 * T + T * T + 72 * C - 58 * eccPrimeSquared) * A * A * A * A * A / 120) + 500000.0);
	var northing = (k0 * (M + N * tan(latrad) * (A * A / 2 + (5 - T + 9 * C + 4 * C * C) * A * A * A * A / 24 + (61 - 58 * T + T * T + 600 * C - 330 * eccPrimeSquared) * A * A * A * A * A * A / 720)));
	if (lat < 0)
	northing += 1000000.0; // 100000 meter offset for southern hemisphere
	
	ret = { x:easting, y:northing, z:zonenum }
	
	return ret;
	}
	
	//===================================================================
	function val_utm(zone, northing, easting)
	{
	// DJS - hardcode to 21 (WGS-84)
	ellipsoid = geo_constants(21);
	
	var axis = ellipsoid.axis;
	var eccent = ellipsoid.eccentricity;
	var k0 = 0.9996;
	
	var e1 = (1 - sqrt(1 - eccent)) / (1 + sqrt(1 - eccent));
	var x = easting - 500000.0; //remove 500,000 meter offset for longitude
	var y = northing;
	
	
	var nhemisphere = 1;
	
	var longorig = (zone - 1) * 6 - 180 + 3;  //+3 puts origin in middle of zone
	
	var eccPrimeSquared = (eccent) / (1-eccent);
	var M = y / k0;
	var mu = M / (axis * (1 - eccent / 4 - 3 * eccent * eccent / 64 - 5 * eccent * eccent * eccent / 256));
	var phi1Rad = mu + (3 * e1 / 2 - 27 * e1 * e1 * e1 / 32) * sin(2 * mu) + (21 * e1 * e1 / 16 - 55 * e1 * e1 * e1 * e1 / 32) * sin(4 * mu) + (151 * e1 * e1 * e1 / 96) * sin(6 * mu);
	var phi1 = phi1Rad * rad2deg;
	var N1 = axis / sqrt(1 - eccent * sin(phi1Rad) * sin(phi1Rad));
	
	
	var T1 = tan(phi1Rad) * tan(phi1Rad);
	var C1 = eccPrimeSquared * cos(phi1Rad) * cos(phi1Rad);
	var R1 = axis * (1 - eccent) / pow(1-eccent * sin(phi1Rad) * sin(phi1Rad), 1.5);
	var D = x / (N1 * k0);
	var lat = phi1Rad - (N1 * tan(phi1Rad) / R1) * (D * D / 2 - (5 + 3 * T1 + 10 * C1 - 4 * C1 * C1 - 9 * eccPrimeSquared) * D * D * D * D / 24 + (61 + 90 * T1 + 298 * C1 + 45 * T1 * T1 - 252 * eccPrimeSquared - 3 * C1 * C1) * D * D * D * D * D * D / 720);
	lat = lat * rad2deg;
	var lon = (D - (1 + 2 * T1 + C1) * D * D * D / 6 + (5 - 2 * C1 + 28 * T1 - 3 * C1 * C1 + 8 * eccPrimeSquared + 24 * T1 * T1) * D * D * D * D * D / 120) / cos(phi1Rad);
	lon = longorig + lon * rad2deg;
	
	ret = new GLatLng(lat,lon);
	return ret;
	}


//===================================================================

    function mod(y, x)
    {
        if (y >= 0)
            return y - x * floor(y / x);
        else
            return y + x * (floor(-y / x) + 1.0);
    }

    function sqrt(x) { return Math.sqrt(x); }

    function tan(x) { return Math.tan(x); }

    function sin(x) { return Math.sin(x); }

    function cos(x) { return Math.cos(x); }

    function floor(x) { return Math.floor(x); }

    function pow(x, y) { return Math.pow(x, y); }

    function UTMProjection() {
    }

    UTMProjection.prototype = new GProjection();

    UTMProjection.prototype.fromLatLngToPixel=function(a,b) {
        var d = new GPoint;
        var ut = calc_utm(a.y, a.x);

        d.x = Math.round((ut.x + (ut.z - 1) * zone_width) / pow(2, 17 - b));
        d.y = -Math.round(ut.y / pow(2, 17 - b));
        return d;
    }

    UTMProjection.prototype.fromPixelToLatLng=function(a,s,c) {
        var x = a.x
        var k = a.y
        x = x * pow(2, 17 - s);
        var n = -k * pow(2, 17 - s);
        var e = x - floor(x/zone_width)*zone_width;
        var z = floor(x/zone_width)+1;

        var ll=val_utm(z,n,e);
        return new GLatLng(ll.lat(), ll.lng(), c)
    }

    UTMProjection.prototype.tileCheckRange=function(a,b,c) {
        return true
    }

    UTMProjection.prototype.getWrapWidth=function(zoom) {
        return 60 * zone_width / pow(2, 17 - zoom);
    }

    var copycoll = new GCopyrightCollection(copyright + " &copy; ");

    copycoll.addCopyright(new GCopyright(1, new GLatLngBounds(new GLatLng(-90, -180), new GLatLng(90, 180)), 0, "USGS"));

    var tilelayers = [new GTileLayer(copycoll,minZ,maxZ)];

    tilelayers[0].getTileUrl = function (a,zoom) {
        var s = 17 - zoom;
        var x = a.x * pow(2, s) * 200;

        var e = floor(x - floor(x/zone_width)*zone_width);
        var z = floor(x/zone_width)+1;
        var n = -a.y - 1;
        e = floor(e / (200* pow(2, s)));
        var url = this.baseURL + "&x=" + e + "&y=" + n + "&z=" + z + "&s=" + ((parseInt(s))+10);
        var srvr = 0;
        if (e & 1) srvr += 1;
        if (n & 1) srvr += 2;

        //url = url.replace(/usa.com/,"usa.com." + srvr + ".i.spf.gladstonefamily.net");
        url = url.replace(/terraserver-usa.com/,"terraserver" + srvr + ".gladstonefamily.net");
        return url
    } ;

    tilelayers[0].baseURL = url
    return new GMapType(tilelayers, new UTMProjection(), label, {errorMessage:"No Data Available", tileSize:200});

}

function USGSMapTopo(label) {
     return _makeUSGSMap("http://terraserver-usa.com/tile.ashx?t=2", 8, 16, label, "Map")
}

function USGSMapOrtho(label) {
     return _makeUSGSMap("http://terraserver-usa.com/tile.ashx?t=1", 8, 17, label, "Imagery")
}

function USGSMapUrban(label) {
     return _makeUSGSMap("http://terraserver-usa.com/tile.ashx?t=4", 8, 19, label, "Imagery")
}
