//Mattefunktioner för att förenkla formlerna
function pow(a,b) {return Math.pow(a,b)}
function exp(a) {return Math.exp(a)}
function log(a) {return Math.log(a)}
function sqrt(a) {return Math.sqrt(a)}
function floor(a) {return Math.floor(a)}
function sin(a) {return Math.sin(a)}
function cos(a) {return Math.cos(a)}
function tan(a) {return Math.tan(a)}
function asin(a) {return Math.asin(a)}
function acos(a) {return Math.acos(a)}
function atan(a) {return Math.atan(a)}
function atanh(a) {return (log((1+a)/(1-a))/2)}
function sinh(a) {return ((1/2)*(exp(a)-exp(-a)))}
function cosh(a) {return ((1/2)*(exp(a)+exp(-a)))}
function tanh(a) {return (exp(2*a)-1)/(exp(2*a)+1)}

function DegMinSec2DecDeg(a) {return (Number(a[0])+(Number(a[1])/60)+(Number(a[2])/3600))}
function DecDeg2DegMinSec(a) 
{
 var decdeg = Number(a);
 var deg = floor(decdeg);
 var min = floor((decdeg-deg)*60);
 var sec = (((decdeg-deg)*60)-min)*60;
 return new Array(deg, min, sec);
}

function Deg2Rad(a) {return a*(Math.PI/180)}
function Rad2Deg(a) {return a*(180/Math.PI)}

// ************************************
//  CProjectionGaussKruger

function CProjectionGaussKruger(projectionName, ellipsoid, projectionParameters)
{
 this.name = projectionName;
 this.E = ellipsoid;
 this.P = projectionParameters;

 this.Forward = function(latlong)
 {
  try
  {
   var E = this.E;
   var P = this.P;
   var fi = latlong[0] * P.rpu;
   var lambda = latlong[1] * P.rpu;
   var dlambda = lambda - P.lambdaNoll;

   //Avbilda ellipsoiden på klotet. Ger konforma latituden 
   var fis = fi - sin(fi)*cos(fi)*(E.A+E.B*pow(sin(fi),2)+E.C*pow(sin(fi),4)+E.D*pow(sin(fi),6));

   //Avbilda klotet på planet med serieutveckling för x och y
   var xip = atan(tan(fis)/cos(dlambda));
   var etap = atanh(cos(fis)*sin(dlambda));

   var x = P.kNoll*E.atak*(xip+E.beta1*sin(2*xip)*cosh(2*etap)+E.beta2*sin(4*xip)*cosh(4*etap)+E.beta3*sin(6*xip)*cosh(6*etap)+E.beta4*sin(8*xip)*cosh(8*etap))+P.FN;
   var y = P.kNoll*E.atak*(etap+E.beta1*cos(2*xip)*sinh(2*etap)+E.beta2*cos(4*xip)*sinh(4*etap)+E.beta3*cos(6*xip)*sinh(6*etap)+E.beta4*cos(8*xip)*sinh(8*etap))+P.FE;

   var XY = new Array(x,y);
   return XY;
  }
  catch(err) {return false;}
 }

 this.Inverse = function(XY)
 {
  try
  {
   var E = this.E;
   var P = this.P;
   var x = XY[0];
   var y = XY[1];

   //Serieutveckling för att ta fram konform latitud (fis) och longitudskillnad (dlambda)
   var xi = (x-P.FN)/(P.kNoll*E.atak);
   var eta = (y-P.FE)/(P.kNoll*E.atak);

   var xip = xi-E.delta1*sin(2*xi)*cosh(2*eta)-E.delta2*sin(4*xi)*cosh(4*eta)-E.delta3*sin(6*xi)*cosh(6*eta)-E.delta4*sin(8*xi)*cosh(8*eta);
   var etap = eta-E.delta1*cos(2*xi)*sinh(2*eta)-E.delta2*cos(4*xi)*sinh(4*eta)-E.delta3*cos(6*xi)*sinh(6*eta)-E.delta4*cos(8*xi)*sinh(8*eta);

   var fis = asin(sin(xip)/cosh(etap));
   var dlambda = atan(sinh(etap)/cos(xip));

   //Konform latitud (fis) omräknas till latitud (fi) samt byte av medelmeridian (lambdaNoll)
   var fi = fis+sin(fis)*cos(fis)*(E.As+E.Bs*pow(sin(fis),2)+E.Cs*pow(sin(fis),4)+E.Ds*pow(sin(fis),6));
   var lambda = P.lambdaNoll+dlambda;

   var latlong = new Array(fi,lambda);
   return latlong;
  }
  catch(err) {return false;}
 }

}


// ************************************
//  CProjectionParameters

function CProjectionParameters(projectionParametersName, centralMeridian, radiansPerUnit, scaleFactor, falseNorthing, falseEasting)
{
 this.name = projectionParametersName;
 var rpu = this.rpu  = radiansPerUnit;
 this.lambdaNoll = centralMeridian * rpu;
 this.kNoll = scaleFactor;
 this.FN = falseNorthing;
 this.FE = falseEasting;
}



// ************************************
//  CEllipsoid

function CEllipsoid(ellipsoidName, semiMinorAxis, flattening)
{
 var a, f, e, n, atak;
 this.name = ellipsoidName;
 this.a = a = semiMinorAxis;
 this.f = f = flattening;

 this.e = e = sqrt(f*(2-f));
 this.n = n = f/(2-f);
 this.atak = atak = (a/(1+n))*(1+(1/4)*pow(n,2)+(1/64)*pow(n,4));

 this.A = pow(e,2);
 this.B = (1/6)*(5*pow(e,4)-pow(e,6));
 this.C = (1/120)*(104*pow(e,6)-45*pow(e,8));
 this.D = (1/1260)*(1237*pow(e,8));

 this.As = pow(e,2)+pow(e,4)+pow(e,6)+pow(e,8);
 this.Bs = (-1/6)*(7*pow(e,4)+17*pow(e,6)+30*pow(e,8));
 this.Cs = (1/120)*(224*pow(e,6)+889*pow(e,8));
 this.Ds = (-1/1260)*(4279*pow(e,8));

 this.beta1 = (1/2)*n-(2/3)*pow(n,2)+(1/16)*pow(n,3)+(41/180)*pow(n,4);
 this.beta2 = (13/48)*pow(n,2)-(3/5)*pow(n,3)+(557/1440)*pow(n,4);
 this.beta3 = (61/240)*pow(n,3)-(103/140)*pow(n,4);
 this.beta4 = (49561/161280)*pow(n,4);

 this.delta1 = (1/2)*n-(2/3)*pow(n,2)+(37/96)*pow(n,3)-(1/360)*pow(n,4);
 this.delta2 = (1/48)*pow(n,2)+(1/15)*pow(n,3)-(437/1440)*pow(n,4);
 this.delta3 = (17/480)*pow(n,3)-(37/840)*pow(n,4);
 this.delta4 = (4397/161280)*pow(n,4);
}



