/*
   Mercator Projection routines
   (c) 2005-2006 Keith Wannamaker
   Do no use without permission
   keith@wannamaker.org
*/

function MercatorProjection(scalex, scaley, xorigin, yorigin) {

  this.D2R = Math.PI / 180;
  this.R2D = 180 / Math.PI;

  this.params = {
    'ellipse_a' : 6378137,
    'ellipse_e' : 0.081819191,
    'false_easting' : 0,
    'false_northing' : 0,
    'central_meridian_radians' : 150 * Math.PI / 180,
    'mercator_scale' : 1,
    'scalex' : scalex,
    'scaley' : scaley,
    'xorigin' : xorigin,
    'yorigin' : yorigin
  };
  
  this.computedParams = {
    'term1' : 0,
    'term2' : 0,
    'term3' : 0,
    'term4' : 0,
    'alphaKilo' : 0,
    'ediv2' : 0
  };
  
  this.recompute = function() {
    this.computedParams.alphaKilo = this.params.ellipse_a * this.params.mercator_scale
    this.computedParams.ediv2 = this.params.ellipse_e / 2;
    this.computedParams.term1 =
              (Math.pow(this.params.ellipse_e, 2)        /      2) +
              (Math.pow(this.params.ellipse_e, 4) *    5 /     24) +
              (Math.pow(this.params.ellipse_e, 6)        /     12) +
              (Math.pow(this.params.ellipse_e, 8) *   13 /    360);
    this.computedParams.term2 =
              (Math.pow(this.params.ellipse_e, 4) *    7 /     48) +
              (Math.pow(this.params.ellipse_e, 6) *   29 /    240) +
              (Math.pow(this.params.ellipse_e, 8) *  811 /  11520);
    this.computedParams.term3 =
              (Math.pow(this.params.ellipse_e, 6) *    7 /    120) +
              (Math.pow(this.params.ellipse_e, 8) *   81 /   1120);
    this.computedParams.term4 =
              (Math.pow(this.params.ellipse_e, 8) * 4279 / 161280);
  }

  this.updateProjection = function(
    scalex,
    scaley,
    xorigin,
    yorigin) {
    this.params.scalex = scalex;
    this.params.scaley = scaley;
    this.params.xorigin = xorigin;
    this.params.yorigin = yorigin;
    this.recompute();
  }
  
  //takes x/y, returns lat/lon
  this.XYToGeo = function(x,y) {
  
    y = -y;
    x = x * this.params.scalex;
    y = y * this.params.scaley;
    x = x + this.params.xorigin;
    y = y + this.params.yorigin;
    
    var bexp = (this.params.false_northing - y) / this.computedParams.alphaKilo;
    var t = Math.exp(bexp);
    var kai = Math.PI/2 - (2*Math.atan(t));
    
    var lat = kai + 
      this.computedParams.term1 * Math.sin(2*kai) +
      this.computedParams.term2 * Math.sin(4*kai) +
      this.computedParams.term3 * Math.sin(6*kai) +
      this.computedParams.term4 * Math.sin(8*kai);
    var lon = (x - this.params.false_easting) / this.computedParams.alphaKilo;
    lon += this.params.central_meridian_radians;
    
    if (lon > Math.PI) {
      lon -= 2 * Math.PI;
    } else if (lon < -Math.PI) {
      lon += 2 * Math.PI;
    }    
    
    var return_array = new Array();
    return_array[0] = this.R2D * lat;
    return_array[1] = this.R2D * lon;
    return return_array;  
  }

  //takes lat/lon, return x/y
  this.GeoToXY = function(lat, lon) {
  
    lat *= this.D2R;
    lon *= this.D2R;
    
    lon -= this.params.central_meridian_radians;
    
    if (lon < -Math.PI) {
      lon += 2 * Math.PI;
    } else if (lon > Math.PI) {
      lon -= 2 * Math.PI;
    }
    
    var x = this.params.false_easting + 
            this.computedParams.alphaKilo * (lon);
    var logterm = Math.tan(Math.PI/4 + lat/2) * 
                  Math.pow(
        (1 - this.params.ellipse_e * Math.sin(lat)) /
        (1 + this.params.ellipse_e * Math.sin(lat)), this.computedParams.ediv2);
    var y = this.params.false_northing + 
            this.computedParams.alphaKilo * Math.log(logterm);

    x -= this.params.xorigin;
    y -= this.params.yorigin;
  
    x = x / this.params.scalex;
    y = y / this.params.scaley;
  
    y = -y;
  
    var return_array = new Array();
    return_array[0] = x;
    return_array[1] = y;
    return return_array;
  }
}
  






