/*
 * TerraServer Functionality for Google Maps API
 *
 * Some UTM functions based on 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)
 *
 * @author Copyright (C) 2005 Lokkju, lokkju@lokkju.com
 * @version 1.1
 * @modified 01/05/2006
 * @modified 06/15/2005
 * 
 * Other licenses are available, please contact the above address for more details.
 *
 * This program is free software; you can redistribute it and/or modify it 
 * under the terms of the GNU General Public License as published by the Free 
 * Software Foundation; either version 2 of the License, or (at your option) 
 * any later version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT 
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
 * more details.
 * 
 * You should have received a copy of the GNU General Public License along 
 * with this program; if not, write to the Free Software Foundation, Inc., 59 
 * Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

function r(x,k) {
  this.x=x;
  this.y=k
}

r.prototype.toString=function() {
  return"("+this.x+", "+this.y+")"
};

r.prototype.equals=function(ia) {
  if(!ia)
    return false;
  return this.x==ia.x&&this.y==ia.y
};

r.prototype.distanceFrom=function(ia) {
  var La=this.x-ia.x;
  var Pa=this.y-ia.y;

  return Math.sqrt(La*La+Pa*Pa)
};

r.prototype.approxEquals=function(ia) {
  if(!ia)
    return false;
  return jd(this.x,ia.x)&&jd(this.y,ia.y)
};



//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 
// Define the resolutions available to us.  
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 


var meters_per_degree = 10000000/90;  // not sure this matters(!) [a guess, anyway]
var zone_width = Math.floor(1000000/102400) * 102400;
// build resolution and offset/zoom adjustment arrays
// because urban imagery has two additional "deeper" zooms, there's a lot
//   of near duplication of functions, and data, at least until I can figure
//   out how to add a new array variable to the base topo instance
var i;
var _URBAN = 4
var _TOPO = 2

function terra (m,s,t) {
	this.maptype = m;
	this.tileSize=200;
        this.emptyTileUrl = "http://www.google.com/mapfiles/transparent.gif";
	if (this.maptype == _TOPO) {
		this.numZoomLevels=10;
                this.minZoomLevel = 1;
	} else if (this.maptype == _URBAN) {
		this.numZoomLevels=12;
                this.minZoomLevel = -2;
	}
	
	if (this.maptype == _TOPO) {
		if (s != null) {
			this.baseURL = s;
		}else {
			this.baseURL="http://www.terraserver-usa.com/tile.ashx?t=2";
		}
                if (this.baseURL.search("t=1") > 0) {
                    this.minZoomLevel = 0;
                }
		if (t != null) {
			this.linkText = t;
		} else {
			this.linkText = 'Topo';
		}
	} else if (this.maptype == _URBAN) {
		this.baseURL="http://www.terraserver-usa.com/tile.ashx?t=4";
		if (t != null) {
			this.linkText = t;
		} else {
			this.linkText = 'Urban';
		}
	}
}
terra.prototype.adjustBitmapX = function(x,s) {
	var kd=Math.floor(360*meters_per_degree / Math.pow(2, s));
	var lf=-(kd>>1);
	var Ti=lf+kd;
	while(x<lf) {
		x+=kd
	}
	while(x>=Ti) {
		x-=kd
	}
	return x
};

terra.prototype.getBitmapCoordinate = function(Ea,Ga,s,d) {
	if(!d)
		d=new r(0,0);
	var ut = calc_utm(Ea, Ga);
        d.x = Math.floor((ut.x + (ut.z - 1) * zone_width) / Math.pow(2, s));
        d.y = -Math.floor(ut.y / Math.pow(2, s)) + this.tileSize;
	return d
};
terra.prototype.getLatLng=function(x,k,s,d) {
	if(!d)
		d=new r(0,0);
	var n;
	var e;
	var z;
        k -= this.tileSize;
        x = x * Math.pow(2, s);
        n = -k * Math.pow(2, s);
        e = x - Math.floor(x/zone_width)*zone_width;
        z = Math.floor(x/zone_width)+1;
  
 	var ll=val_utm(z,n,e);
	return ll;
};
terra.prototype.getTileCoordinate=function(Ea,Ga,s,d) {
	var Ra=this.getBitmapCoordinate(Ea,Ga,s,d);
	Ra.x=Math.floor(Ra.x/this.tileSize);
	Ra.y=Math.floor(Ra.y/this.tileSize);
	return Ra
};
terra.prototype.getTileURL=function(x,k,s) {
        x = x * Math.pow(2, s) * this.tileSize;
  
	var e = Math.floor(x - Math.floor(x/zone_width)*zone_width);
	var z = Math.floor(x/zone_width)+1;
	var n = -k;
        if (s < -2) { s= -2;}
        if (s > 10) {s=10;}
        e = Math.floor(e / (this.tileSize* Math.pow(2, s)));
        return this.baseURL + "&x=" + e + "&y=" + n + "&z=" + z + "&s=" + ((parseInt(s))+10);
};
terra.prototype.getLowestZoomLevel=function(Vc,yc,Cc) {
    if (this.maptype == _URBAN) {
        return -2;
    } else if (this.maptype == _TOPO) {
        return 1;
    }

    return 0;
};
GMap.prototype.setMapType=function(t) {
    if (this.zoomLevel < (t.minZoomLevel||0)) {
        this.zoomTo(t.minZoomLevel||0);
    }
    this.switchSpecification(t);
}
GMap.prototype.zoomTo=function(a){
    if(!this.isLoaded()){return}
    if(a>=this.spec.numZoomLevels){
        a=this.spec.numZoomLevels-1
    } else if (a<(this.spec.minZoomLevel||0)){
        a= (this.spec.minZoomLevel||0);
    }
    if(a!=this.zoomLevel){
        var b=this.zoomLevel;
        if (!this.centerLatLng) this.centerLatLng = this.getCenterLatLng();
        this.zoomLevel=a;
        var c;
        c=this.spec.getBitmapCoordinate(this.centerLatLng.y,this.centerLatLng.x,this.zoomLevel)
        this.centerAtBitmap(c)
    }
    GEvent.trigger(this,"zoom",b,this.zoomLevel)
};

terra.prototype.getLinkText = function() {
		return this.linkText;
}
terra.prototype.getPixelsPerDegree=function(s) {
    return meters_per_degree / Math.pow(2, s);
};
terra.prototype.zoomBitmapCoord=function(a,b,c){var d=new r();var e=Math.pow(2,b-a);d.x=Math.round(c.x*e);d.y=Math.round(c.y*e);return d}
terra.prototype.hasOverlay=function(){return false};
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 
// 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;
var locres = 6; //locator characters
domain = 'cix.co.uk';
user = 'hzk';



//===================================================================
function geo_constants(ellipsoid)
{
// returns ellipsoid values
ellipsoid_axis = new Array();
ellipsoid_eccen = new Array();
ellipsoid_axis[0] = 6377563.396; ellipsoid_eccen[0] = 0.00667054;  //airy
ellipsoid_axis[1] = 6377340.189; ellipsoid_eccen[1] = 0.00667054;  // mod airy
ellipsoid_axis[2] = 6378160; ellipsoid_eccen[2] = 0.006694542;  //aust national
ellipsoid_axis[3] = 6377397.155; ellipsoid_eccen[3] = 0.006674372;  //bessel 1841
ellipsoid_axis[4] = 6378206.4; ellipsoid_eccen[4] = 0.006768658;  //clarke 1866
ellipsoid_axis[5] = 6378249.145; ellipsoid_eccen[5] = 0.006803511;  //clarke 1880
ellipsoid_axis[6] = 6377276.345; ellipsoid_eccen[6] = 0.00637847;  //everest
ellipsoid_axis[7] = 6377304.063; ellipsoid_eccen[7] = 0.006637847;  // mod everest
ellipsoid_axis[8] = 6378166; ellipsoid_eccen[8] = 0.006693422;  //fischer 1960
ellipsoid_axis[9] = 6378150; ellipsoid_eccen[9] = 0.006693422;  //fischer 1968
ellipsoid_axis[10] = 6378155; ellipsoid_eccen[10] = 0.006693422;  // mod fischer
ellipsoid_axis[11] = 6378160; ellipsoid_eccen[11] = 0.006694605;  //grs 1967
ellipsoid_axis[12] = 6378137; ellipsoid_eccen[12] = 0.00669438;  //  grs 1980
ellipsoid_axis[13] = 6378200; ellipsoid_eccen[13] = 0.006693422;  // helmert 1906
ellipsoid_axis[14] = 6378270; ellipsoid_eccen[14] = 0.006693422;  // hough
ellipsoid_axis[15] = 6378388; ellipsoid_eccen[15] = 0.00672267;  // int24
ellipsoid_axis[16] = 6378245; ellipsoid_eccen[16] = 0.006693422;  // krassovsky
ellipsoid_axis[17] = 6378160; ellipsoid_eccen[17] = 0.006694542;  // s america
ellipsoid_axis[18] = 6378165; ellipsoid_eccen[18] = 0.006693422;  // wgs-60
ellipsoid_axis[19] = 6378145; ellipsoid_eccen[19] = 0.006694542;  // wgs-66
ellipsoid_axis[20] = 6378135; ellipsoid_eccen[20] = 0.006694318;  // wgs-72
ellipsoid_axis[21] = 6378137; ellipsoid_eccen[21] = 0.00669438;  //wgs-84

// return values as an object
var ellipsoid = { axis: ellipsoid_axis[ellipsoid], 
                  eccentricity: ellipsoid_eccen[ellipsoid] };
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;

//get letter
var letter = get_zoneletter(lat);

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

// round up
easting = floor(easting);
northing = floor(northing);

// correlate index with letter
var cx ="C01D02E03F04G05H06J07K08L09M10N11P12Q13R14S15T16U17V18W19X20";
var cxi = cx.indexOf(letter,0);
var zl = Number(cx.substr(cxi + 1, 2));


// somehow, we gotta return this as array of three values, or object 
//   with zone, easting, and northing elements
ret = new r(0,0,0);
ret.x = easting;
ret.y = northing;
ret.z = zonenum;

return ret;
}

//===================================================================
function val_utm(zone, northing, easting)
{
//var zone = document.myform3.zone.value;
//var northing = document.myform3.north.value;
//var easting = document.myform3.east.value;
//var d = document.myform3.ellipsoid.selectedIndex;
//var zl = document.myform3.zletter.selectedIndex;
//locres = document.myform3.locc.selectedIndex;

// 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;


// DJS - Have to be sure that we return the proper zone letter in the UTM bit
// how does MS handle this?  I don't remember seeing S in the url...
// -- doesn't seem to matter, interestingly.
// correlate index with letter
//var cx ="1C2D3E4F5G6H7J8K9L10M11N12P13Q14R15S16T17U18V19W20X";
//var cxl = String(zl);
//var cxi = cx.indexOf(cxl,0);
//var zletter = cx.charAt(cxi + cxl.length);


var nhemisphere = 1;
//if (zletter >= "N")
//  nhemishere = 1;
//else
//  y -= 10000000.0; //remove 10,000,000 meter offset used for southern hemisphere

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 r(0,0);
ret.x = lon;
ret.y = lat;
return ret;
// need to return data
}


//===================================================================
function chr(x)
{
return String.fromCharCode(x);
}

//===================================================================
function get_zoneletter(lat)
{
//This routine determines the correct UTM letter designator for the given latitude
//returns 'Z' if latitude is outside the UTM limits of 84N to 80S
var zoneletter;

if ((84 >= lat) && (lat >= 72)) zoneletter = 'X';
else if ((72 > lat) && (lat >= 64)) zoneletter = 'W';
else if ((64 > lat) && (lat >= 56)) zoneletter = 'V';
else if ((56 > lat) && (lat >= 48)) zoneletter = 'U';
else if ((48 > lat) && (lat >= 40)) zoneletter = 'T';
else if ((40 > lat) && (lat >= 32)) zoneletter = 'S';
else if ((32 > lat) && (lat >= 24)) zoneletter = 'R';
else if ((24 > lat) && (lat >= 16)) zoneletter = 'Q';
else if ((16 > lat) && (lat >= 8)) zoneletter = 'P';
else if (( 8 > lat) && (lat >= 0)) zoneletter = 'N';
else if (( 0 > lat) && (lat >= -8)) zoneletter = 'M';
else if ((-8> lat) && (lat >= -16)) zoneletter = 'L';
else if ((-16 > lat) && (lat >= -24)) zoneletter = 'K';
else if ((-24 > lat) && (lat >= -32)) zoneletter = 'J';
else if ((-32 > lat) && (lat >= -40)) zoneletter = 'H';
else if ((-40 > lat) && (lat >= -48)) zoneletter = 'G';
else if ((-48 > lat) && (lat >= -56)) zoneletter = 'F';
else if ((-56 > lat) && (lat >= -64)) zoneletter = 'E';
else if ((-64 > lat) && (lat >= -72)) zoneletter = 'D';
else if ((-72 > lat) && (lat >= -80)) zoneletter = 'C';
else zoneletter = chr(32 + 66); //This is here as an error flag to show that the Latitude is outside the UTM limits
return zoneletter;
}

//end of myform3

function mod(y, x)
{
if (y >= 0)
  return y - x * floor(y / x);
else
  return y + x * (floor(-y / x) + 1.0);
}

function atan2(y, x)
{
	return Math.atan2(y, x);
}

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 acos(x)
{
	return Math.acos(x);
}

function floor(x)
{
	return Math.floor(x);
}

function round(x)
{
	return Math.round(x);
}

function ln(x)
{
	return Math.log(x);
}

function abs(x)
{
	return Math.abs(x);
}

function pow(x, y)
{
	return Math.pow(x, y);
}

function atan(x)
{
	return Math.atan(x);
}

function chr(x)
{
return String.fromCharCode(x);
}

function round(x)
{
	return Math.round(x);
}









// Add to the Array of maptypes
_TerraMapSpec = terra;
