var UTM_datums = Array(Array(6378137.0,6356752.314),
Array(6378137.0,6356752.314),
Array(6378137.0,6356752.3),
Array(6378135.0,6356750.5),
Array(6378160.0,6356774.7),
Array(6378245.0,6356863.0),
Array(6378206.4,6356583.8),
Array(6378388.0,6356911.9),
Array(6378388.0,6356911.9),
Array(6378249.1,6356514.9),
Array(6378206.4,6356583.8),
Array(6377563.4,6356256.9),
Array(6377397.2,6356079.0),
Array(6377276.3,6356075.4));

var UTM_datum_names = Array("WGS 84","NAD 83","GRS 80","WGS 72","Australian 1965"
,"Krasovsky 1940","North American 1927","International 1924","Hayford 1909","Clarke 1880"
,"Clarke 1866","Airy 1830","Bessel 1841","Everest 1830");

function convertLL2UTM(lat,lon,lon_zone,dat) // DD.dddd
{
 var ret_val = false;
 
 if(UTM_datums[dat])
 {
  var dt = UTM_datums[dat];
 
  var lat_rad = lat*Math.PI/180;
  var eccentricity_n = (dt[0]-dt[1])/(dt[0]+dt[1]);
  var eccentricity_e = Math.sqrt(1-Math.pow((dt[1]/dt[0]),2));
  var eccentricity_e2sq = eccentricity_e*eccentricity_e/(1-eccentricity_e*eccentricity_e);
  
  if(lon_zone=="")
   lon_zone = 31+Math.floor(lon/6);
   
  var lon_zone_cm = 6*lon_zone-183;
  var delta_lon = (lon-lon_zone_cm)*3600/10000;
  
  var meridional_arc_A0 =  dt[0]*(1-eccentricity_n+(5*eccentricity_n*eccentricity_n/4)*(1-eccentricity_n) +(81*Math.pow(eccentricity_n,4)/64)*(1-eccentricity_n));
  var meridional_arc_B0 = (3*dt[0]*eccentricity_n/2)*(1 - eccentricity_n - (7*eccentricity_n*eccentricity_n/8)*(1-eccentricity_n) + 55*Math.pow(eccentricity_n,4)/64);         
  var meridional_arc_C0 = (15*dt[0]*eccentricity_n*eccentricity_n/16)*(1 - eccentricity_n +(3*eccentricity_n*eccentricity_n/4)*(1-eccentricity_n));        
  var meridional_arc_D0 = (35*dt[0]*Math.pow(eccentricity_n,3)/48)*(1 - eccentricity_n + 11*eccentricity_n*eccentricity_n/16);
  var meridional_arc_E0 = (315*dt[0]*Math.pow(eccentricity_n,4)/51)*(1-eccentricity_n);
  var meridional_arc_S = meridional_arc_A0*lat_rad - meridional_arc_B0*Math.sin(2*lat_rad) + meridional_arc_C0*Math.sin(4*lat_rad) - meridional_arc_D0*Math.sin(6*lat_rad) + meridional_arc_E0*Math.sin(8*lat_rad);
 
  var scale_factor = 0.9996;
  var nu = dt[0]/(Math.pow((1-Math.pow((eccentricity_e*Math.sin(lat_rad)),2)),(1/2)));
  var sin1 = Math.PI/(180*3600);
  
  var Ki = meridional_arc_S*scale_factor;
  var Kii = nu*Math.sin(lat_rad)*Math.cos(lat_rad)*Math.pow(sin1,2)*scale_factor*(100000000)/2;
  var Kiii = ((Math.pow(sin1,4)*nu*Math.sin(lat_rad)*Math.pow(Math.cos(lat_rad),3))/24)*(5-Math.pow(Math.tan(lat_rad),2)+9*eccentricity_e2sq*Math.pow(Math.cos(lat_rad),2)+4*Math.pow(eccentricity_e2sq,2)*Math.pow(Math.cos(lat_rad),4))*scale_factor*(10000000000000000);
  var Kiv = nu*Math.cos(lat_rad)*sin1*scale_factor*10000;
  var Kv = Math.pow((sin1*Math.cos(lat_rad)),3)*(nu/6)*(1-Math.pow(Math.tan(lat_rad),2)+eccentricity_e2sq*Math.pow(Math.cos(lat_rad),2))*scale_factor*(1000000000000);
 
  var raw_northing = Ki+Kii*delta_lon*delta_lon+Kiii*Math.pow(delta_lon,4); 
  
  ret_val = Array();
  ret_val[0] = (raw_northing<0?10000000+raw_northing:raw_northing);
  ret_val[1] = 500000+(Kiv*delta_lon+Kv*Math.pow(delta_lon,3));
  ret_val[2] = lon_zone;
 }   
    
 return ret_val;  
}

function convertUTM2LL(nor,eas,zone,nor_lat,dat)
{
 ret_val = false;
 
 var dt = UTM_datums[dat];
 
 var e = Math.sqrt(1-Math.pow((dt[1]/dt[0]),2));
 var e1sq = e*e/(1-e*e);
 var eisq = e1sq;  
 
 var ec = e;
 var ei = (1-Math.pow(1-ec*ec,(1/2)))/(1+Math.pow(1-ec*ec,(1/2)));
 var k0 = 0.9996;
 var arc_length = nor / k0;

 var mu = arc_length/(dt[0]*(1 - Math.pow(e,2)/4 - 3*Math.pow(e,4)/64 - 5*Math.pow(e,6)/256));
 var c1 = 3*ei/2-27*Math.pow(ei,3)/32;
 var c2 = 21*Math.pow(ei,2)/16-55*Math.pow(ei,4)/32;
 var c3 = 151*Math.pow(ei,3)/96;
 var c4 = 1097*Math.pow(ei,4)/512;
 
 var east_prime = 500000-eas;

 var phi = mu + c1*Math.sin(2*mu) + c2*Math.sin(4*mu) + c3*Math.sin(6*mu) + c4*Math.sin(8*mu);
 
 var n1 = dt[0]/Math.pow(1-Math.pow(e*Math.sin(phi),2),(1/2));
 var r1 = dt[0]*(1-e*e)/Math.pow(1-Math.pow(e*Math.sin(phi),2),(3/2));
 var d = east_prime/(n1*k0);
    
 var t1 = Math.pow(Math.tan(phi),2);
 var c1_line = eisq*Math.pow(Math.cos(phi),2);
 
 var fact1 = n1*Math.tan(phi)/r1;
 var fact2 = d*d/2;
 var fact3 = (5 + 3*t1 + 10*c1_line - 4*c1_line*c1_line - 9*eisq)*Math.pow(d,4)/24;
 var fact4 = (61 + 90*t1 + 298*c1_line + 45*t1*t1 - 252*eisq - 3*c1_line*c1_line)*Math.pow(d,6)/720;
    
 var row_latitude = 180*(phi-fact1*(fact2+fact3+fact4))/Math.PI;
    
 var lat = (nor_lat=="N"?row_latitude:-row_latitude);
 
 var lofact1 = d;
 var lofact2 = (1+2*t1+c1_line)*Math.pow(d,3)/6;
 var lofact3 = (5-2*c1_line+28*t1-3*Math.pow(c1_line,2)+8*eisq+24*Math.pow(t1,2))*Math.pow(d,5)/120;
 
 var zone_cm = 6*zone-183;
 var delta_long = (lofact1-lofact2+lofact3)/Math.cos(phi);
 
 var lon = zone_cm-delta_long*180/Math.PI;
 
 return Array(lat,lon);
}

function convertUTMZone(nor,eas,zone_from,zone_to)
{
 var coord = convertUTM2LL(nor,eas,zone_from,"N",0);
 return convertLL2UTM(coord[0],coord[1],zone_to,0);
}

function vychodZapad(lat, lon, time, offset, dst) 
{
 // lat, lon = DD.ddddd
 // return [astronomical[sr,ss],nautical[sr,ss],civil[sr,ss],sun[sr,ss]]

 
 if(offset == undefined)
  offset = 0;

 if(dst == undefined)
  dst = 0;

 offset += dst;
 offset = (offset == 13 ? -11 : offset);
 
 var math = new Array(1.5708, 4.71239, 3.14159, 6.28319, (0.0174533 * lat), (0.0174533 * lon), (0.261799  * offset));
 var data = new Array(-.309017, -.207912, -.104528, -.0145439);
 var times = new Array();

 for(a=0; a<data.length; a++)
 {
     b=data[a];
     times[a] = new Array();
     
     for (x = 0; x <= 1; x++) 
     {
         var e = time.getDOY() + ((math[x] - math[5]) / math[3]);
         var f = (e * .017202) - .0574039;
         var g = f + .0334405 * Math.sin(f);
         g +=  4.93289 + (3.49066E-04) * Math.sin(2 * f);
         while (g < 0) 
         {
             g += math[3];
         }
         while (g >= math[3]) 
         {
             g -= math[3];
         }
         g += (g / math[0]) - parseInt(g / math[0]) == 0 ? 4.84814E-06 : 0;
         var h = Math.sin(g) / Math.cos(g);
         h = Math.atan2(.91746 * h, 1);
         h += g > math[1] ? math[3] : (g > math[0] ? math[2] : 0);
         var i = .39782 * Math.sin(g);
         i = i / Math.sqrt (-i * i + 1);
         i = Math.atan2(i, 1);
         var j = b - (Math.sin(i) * Math.sin(math[4]));
         j = j / (Math.cos(i) * Math.cos(math[4]));
         j = j / Math.sqrt(-j * j + 1);
         j = math[0] - Math.atan2(j, 1);
         if (!x) {
             j = math[3] - j;
         }
         k = j + h - 0.0172028 * e - 1.73364;
         l = k - math[5];
         n = l + math[6];

         key = (x ? 1 : 0);
         if(n == Number.NaN) {
             times[a][key] = '-';
         } else {
             while (n < 0) {
                 n += math[3];
             }
             while (n >= math[3]) {
                 n -= math[3];
             }
             n = n * 3.81972;
             hour = parseInt(n);
             mins = parseInt(((n - hour) * 60) + 0.5);
             times[a][key] = UTCDate(time.getFullYear(),time.getMonth(),time.getDate(),hour,mins,0);
         }
     }
 }
 
 return times;
}

function getDist(lat1, lon1, lat2, lon2, precision) 
{
 var rawdistance = computeDistHead(lat1,lon1,lat2,lon2);
 var distance = Math.round(rawdistance[2] * Math.pow(10,precision))/Math.pow(10,precision); 
    
 return distance;
}

    
function signlatlon(hemi)
{ 
 var sign = 0;
 
 switch(hemi)
 { 
  case "N":
  case "W":
   sign=1;
   break;
  case "S":
  case "E":
   sign=-1;
   break;
 }
 
 return sign;
}

function getEllipsoid(id)
{
 var ells;
 
 switch(id)
 {
  case 1: 
   ells= Array("Sphere",180*60/pi(),"Infinite");
   break;
  case 2:
   ells= Array("WGS84",6378.137/1.852,298.257223563);
   break;
  case 3:
   ells= Array("NAD27",6378.2064/1.852,294.9786982138);
   break;
  case 4:
   ells= Array("International",6378.388/1.852,297.0);
   break;
  case 5:
   ells= Array("Krasovsky",6378.245/1.852,298.3);
   break;
  case 6:
   ells= Array("Bessel",6377.397155/1.852,299.1528);
   break;
  case 7:
   ells= Array("WGS72",6378.135/1.852,298.26);
   break;
  case 8:
   ells= Array("WGS66",6378.145/1.852,298.25);
   break;
  case 9:
   ells= Array("User",0,0);
   break;
 }

 return ells;
}

function computeDistHead(lat1,lon1,lat2,lon2)
{
 if(lat1 > 1000)
  lat1 /= 3600;   
  
 if(lat2 > 1000)
  lat2 /= 3600;

 if(lon1 > 1000)
  lon1 /= 3600;

 if(lon2 > 1000)
  lon2 /= 3600;

 var signlat1=signlatlon("N");
 var signlat2=signlatlon("N");
 var signlon1=signlatlon("E");
 var signlon2=signlatlon("E");

 lat1=(Math.PI/180)*signlat1*lat1;
 lat2=(Math.PI/180)*signlat2*lat2;
 lon1=(Math.PI/180)*signlon1*lon1;
 lon2=(Math.PI/180)*signlon2*lon2;

 var dc=1.852; // get distance conversion factor 1=NM, 1.852=km, 1852=m, 1.15077 = SM

 var ellipse=getEllipsoid(2); //WGS84

 if(ellipse[0]=="Sphere")
 {
  var cd=crsdist(lat1,lon1,lat2,lon2); // compute crs and distance 
  var crs12 = cd[1]*(180/Math.PI);
  var crs21 = cd[2]*(180/Math.PI);
  var d = cd[0]*(180/Math.PI)*60*dc;  // go to physical units  
  var d_nm = cd[0]*(180/Math.PI)*60;  // go to physical units  
 }
 else 
 {
  // elliptic code
  var cde=crsdist_ell(lat1,-lon1,lat2,-lon2,ellipse);  // ellipse uses East negative
  var crs12 = cde[1]*(180/Math.PI);  
  var crs21 = cde[2]*(180/Math.PI);
  var d = cde[0]*dc;  // go to physical units
  var d_nm = cde[0];
 }

 var ret_val = Array(crs12,crs21,d,d_nm);

 return ret_val;
}

function acosf(x)
{
 if (Math.abs(x) > 1 )
 {
  x /= Math.abs(x);
 }
 
 return Math.acos(x);
}

function crsdist(lat1,lon1,lat2,lon2)
{ 
  // radian args
  /* compute course and distance (spherical) */

 if ((lat1+lat2==0) && (Math.abs(lon1-lon2)==Math.PI))
 {
//  writeLog("Not possible to define course");
 }

 var d=Math.cos(Math.sin(lat1)*Math.sin(lat2)+Math.cos(lat1)*Math.cos(lat2)*Math.cos(lon1-lon2));

 if(Math.sin(d)*Math.cos(lat1)==0)
 {
  var crs12=0;
 }
 else
 {
  var argacos=(Math.sin(lat2)-Math.sin(lat1)*Math.cos(d))/(Math.sin(d)*Math.cos(lat1));
  
  if (Math.sin(lon2-lon1) < 0)
  {       
   var crs12=acosf(argacos);
  }
  else
  {
   var crs12=2*Math.PI-acosf(argacos);
  }
 }
 
 if (Math.sin(d)*Math.cos(lat2)==0)
 {
  var crs21=0;
 }
 else
 {
  argacos=(Math.sin(lat1)-Math.sin(lat2)*Math.cos(d))/(Math.sin(d)*Math.cos(lat2));
  
  if (Math.sin(lon1-lon2)<0)
  {       
   var crs21=acosf(argacos);
  }
  else
  {
   var crs21=2*Math.PI-acosf(argacos);
  }
 }

 var out = Array(d,crs12,crs21);
 
 return out;
} 

function crsdist_ell(glat1,glon1,glat2,glon2,ellipse)
{
 if(glat1 == glat2 && glon1 == glon2)
  var out = Array(0,0,180);
 else
 {
  var a=ellipse[1];
  var f=1/ellipse[2];

  var EPS= 0.00000000005;
  var iter=1;
  var MAXITER=100;
 
  if((glat1+glat2==0) && (Math.abs(glon1-glon2)==Math.PI))
  {
   glat1=glat1+0.00001;
  }

  var r = 1 - f;
  var tu1 = r * Math.tan(glat1);
  var tu2 = r * Math.tan(glat2);
  var cu1 = 1 / Math.sqrt(1 + tu1 * tu1);
  var su1 = cu1 * tu1;
  var cu2 = 1 / Math.sqrt(1 + tu2 * tu2);
  var s1 = cu1 * cu2;
  var b1 = s1 * tu2;
  var f1 = b1 * tu1;
  var x = glon2 - glon1;
  var d = x + 1;
  var c = 0;
  
  while ((Math.abs(d - x) > EPS) && (iter < MAXITER))
  {
   iter=iter+1;
   var sx = Math.sin(x);
   var cx = Math.cos(x);
   tu1 = cu2 * sx;
   tu2 = b1 - su1 * cu2 * cx;
   var sy = Math.sqrt(tu1 * tu1 + tu2 * tu2);
   var cy = s1 * cx + f1;
   var y = Math.atan2(sy, cy);
 
   var sa = s1 * sx / sy;
    
   var c2a = 1 - sa * sa;
   var cz = f1 + f1;
   
   if (c2a > 0)
    cz = cy - cz / c2a;
    
   var e = cz * cz * 2 - 1;
   var c = ((-3 * c2a + 4) * f + 4) * c2a * f / 16;
   d = x;
   x = ((e * cy * c + cz) * sy * c + y) * sa;
   x = (1 - c) * x * f + glon2 - glon1;
  }

 
  var faz = modcrs(Math.atan2(tu1, tu2));
  var baz = modcrs(Math.atan2(cu1 * sx, b1 * cx - su1 * cu2) + Math.PI);
  x = Math.sqrt((1 / (r * r) - 1) * c2a + 1);
  x +=1;
  x = (x - 2) / x;
  c = 1 - x;
  c = (x * x / 4 + 1) / c;
  d = (0.375 * x * x - 1) * x;
  x = e * cy;
  var s = ((((sy*sy*4-3)*(1-e-e)*cz*d/6-x)*d/4+cz)*sy*d+y)*c*a*r;
  
  var out = new Array(s, faz,baz);
 }
 
 return out;
}

function mod(x,y)
{
  return x-y*Math.floor(x/y);
}

function modcrs(x)
{
  ret = mod(x,2*Math.PI);
  return ret;
}

function modlat(x)
{
  return mod(x+Math.PI/2,2*Math.PI)-Math.PI/2;
}

