//
/////////////////////////      G E O D E S K T O P        ////////////////////////////////////////////////////////////
//


var cGeoDesktop = new Class ({

  initialize : function(sender) {
    this.ActionKey = false;
    this.overlays = [];
    this.geoCoder = new GClientGeocoder();
    this.permaText = "";
  },

  initStructure : function() {
    this.MAP = new cGoogleMap();
    this.MAP.initStructure();
    this.initMenu();
    this.initCalendar();
  },
  activate : function() {
    this.updateLocation();
//    console.log("GEO.activate", this.overlays, eopDATA.ovl);

    IceCanvas.init();
    IceCanvas.enable();

    this.overlays.forEach(function(ovl){
      if (eopDATA.ovl.indexOf(ovl.ovl.name) > -1 ) {
        ovl.toggle();
      }
    });
  },
  initMenu     : function() {

    var li, ah, ul = $("ovlFeat1").parentNode.getElementsByTagName("ul")[0];

    ovlDATA.each(function (ovl, key){

      if (ovl.active){
        li = $I("LI", "");
        ah = $I("A", ovl.title, {id: ovl.jsClass, title: ovl.title, href: "#", className: key});
        ah.style.whiteSpace = "nowrap"
        li.appendChild(ah);
        ul.appendChild(li);
      }

    });

    this.MENU = new UvumiDropdown("dropdown-menu",{
      duration:100,
      closeDelay:100,
      openDelay:100,
      clickToOpen:false
    });
    this.MENU.domReady();
    tim.step(" OK - MEN loaded");

  },
  initCalendar : function() {
    try {

      var curDay = this.getCurDay();

      eopDATA.yir =  curDay.getFullYear();
      eopDATA.doy =  this.getDay(curDay);

      $("mapDate").value = trimZero(curDay.getUTCDate(),2) + "/" + trimZero((curDay.getUTCMonth() +1),2) + "/" + curDay.getUTCFullYear();
      $("mapDate").title = curDay.toGMTString();

      this.calCtrl = new Calendar({mapDate: 'd/m/Y'}, {
        tweak     : {x:-140, y:-4},
        direction : -0.5,
        draggable : false,
        blocked   : ['1-31 1 2009', '1-28 2 2009', '1-17 3 2009'],
        offset    : 1
      });

      this.CAL = this.calCtrl.calendars[0];

    } catch (e){catchError("GeoDesktop.initCalendar", e) ;}
  },

  getDay        : function(dat) {
    return Math.floor(((dat - new Date(dat.getFullYear(), 0, 1)) / 24 / 3600 / 1000).toFixed(0)) +1;
  },

  getCurDay     : function(dat) {
    return new Date(eopDATA.yir, 0, eopDATA.doy, 0, new Date().getTimezoneOffset()*-1);
  },

  toggleOvl : function(ovl) {
    for (var overlay, i=0; overlay = this.overlays[i]; i++){
      if (overlay.ovl.title === ovl.title){
        overlay.toggle();
      }
    }
    this.updatePermalink(EOP.GEO.permaText);
  },
  initBehaviour : function() {
    try {

      this.MAP.initBehaviour();

      mapDATA.each(function (map, key){
        $$("a." + key).set ("title", map.title);
        $$("a." + key).addEvent("click", function(){
          EOP.GEO.MAP.switchMap(map);
          return false;
        });
      });
      
      ovlDATA.each(function (ovl, key){
        EOP.GEO.overlays.push(new window[ovl.jsClass](ovl));
        $$("a." + key).addEvent("click", function(){
          EOP.GEO.toggleOvl(ovl);
          return false;
        });
      });

     // if (EOP.svr == "local" && !EOP.Disable.markers )  {this.addMarkers();}
      if (eopDATA.deb){
//        this.addExtent();
//        this.addPolylines();
//        this.addCircle();
      }
      tim.step(" OK - MAP loaded");

      $("MAPCAL").addEvent("mouseleave", function(){
        EOP.GEO.calCtrl.toggle(EOP.GEO.CAL);
        $("focus").focus();
      });
      $("mapDate").addEvent("mouseenter", function(){
        EOP.GEO.calCtrl.toggle(EOP.GEO.CAL);
      });

      document.addEvent('keydown', EOP.GEO.keyDown);
      document.addEvent('keyup',   EOP.GEO.keyUp);

    } catch (e){catchError("GeoDesktop.initBehaviour", e) ;}
  },

  keyUp     : function(e) {
    EOP.GEO.ActionKey = false;
    EOP.GEO.updateLocation();  // clean up
  },

  keyDown : function(e) {
    try {
//      console.log("keyDown", e, e.control, e.key, e.code);
      if (e.control || e.code == 17) {
        EOP.GEO.ActionKey = true;
        switch(e.key) {
          case "right" :EOP.GEO.MAP.setTime("nextDay");e.stop();break;
          case "left"  :EOP.GEO.MAP.setTime("prevDay");e.stop();break;
          case "up"    :EOP.GEO.MAP.zoom({action : "zoomin"});e.stop();break;
          case "down"  :EOP.GEO.MAP.zoom({action : "zoomout"});e.stop();break;
        }
      }
    } catch (e){catchError("GeoDesktop.keyChange", e) ;}
  },

  addExtent : function() {
    //http://www.polarview.aq/arctic/kml_seaiceedge15n.php
    //http://www.polarview.aq/arctic/kml_seaiceedge15n.update.php

    var url = "/static/extent.kml";
    GDownloadUrl(url, function(data, status){
      if (status != 200) {console.log("addExtent.error", data, status);} else {

        var i, j, k=0, latlons, points, point, lenCoords, lenPoints,
            xml = GXml.parse(data),
            coords = xml.documentElement.getElementsByTagName("coordinates");

        lenCoords = coords.length;

        for (i = 0; i<lenCoords; i++ ){

          points = coords[i].childNodes[0].data.split(" ");
          latlons = [];
          lenPoints = points.length;
          if (lenPoints > 4) {
            for (j=0; j<lenPoints ; j++){
              point = points[j].split(",");
              if (parseFloat(point[1]) < 60 ) {break;}
              latlons.push(new GLatLng(point[1], point[0]));
            }
            k += 1;
          }
          EOP.GEO.MAP.gmap.addOverlay(new GPolyline(latlons, "#C33", 3));
        }
        tim.step(" OK - Coords ("+ coords.length + "/" + k + ")");
//        console.log(" OK - Coords loaded", coords.length, "lines", k);

      }
    });
  },
  addCircle  : function() {
    // 66.5622
    var lon, lat, dLon=3, points = [];
    for (lon = -180; lon <= 180; lon += dLon) {
      points.push(new GLatLng(66.5622, lon));
    }
    this.MAP.gmap.addOverlay(new GPolyline(points, "#33F", 1));
    
    // 89.9
    points = [];
    for (lon = -180; lon <= 180; lon += 10) {
      points.push(new GLatLng(89.9, lon));
    }
    this.MAP.gmap.addOverlay(new GPolyline(points, "#000", 1));
  },
  addPolylines : function() {
    try {

      var lon, lat, dLat = 6, dLon=5, points = [];

//      for (a = 0; a < 360; a += dx) {
//        this.MAP.gmap.addOverlay(new GPolyline([new GLatLng(90, a),new GLatLng(0, a)],"#0000ff", 2));
//      }
      for (lat = 90; lat >= 60; lat -= dLat) {
        for (lon = -180; lon <= 180; lon += dLon) {
          points.push(new GLatLng(lat, lon));
        }
        this.MAP.gmap.addOverlay(new GPolyline(points, "#cc9933", 1));
      }
    } catch (e){catchError("GeoDesktop.addPolylines", e) ;}
  },

  addMarkers : function() {
    try {
      var i, blueIcon, redIcon;

      blueIcon = new GIcon(G_DEFAULT_ICON);
      blueIcon.image    = "/images/tinybluecirclemarker.png";
      blueIcon.iconSize = new GSize(8, 8);
      blueIcon.shadow   = "";

      redIcon = new GIcon(G_DEFAULT_ICON);
      redIcon.image = "http://gmaps-samples.googlecode.com/svn/trunk/markers/red/blank.png";

      [89.999, 89, 88, 87, 86, 85, 80, 75, 70, 65, 60].each( function(lat){
        EOP.GEO.MAP.gmap.addOverlay(new GMarker(new GLatLng(lat,  0), {icon:blueIcon}));
      });

      for (i = -18; i < 18; i++) {
        this.MAP.gmap.addOverlay(new GMarker(new GLatLng(80, i*10), {icon:blueIcon}));
        this.MAP.gmap.addOverlay(new GMarker(new GLatLng(70, i*10), {icon:blueIcon}));
      }

      // Reykjavik
      this.MAP.gmap.addOverlay(new GMarker(new GLatLng(64.1333, -21.9), {icon:redIcon}));

    } catch (e){catchError("GeoDesktop.addMarkers", e) ;}
  },


//
//
//
//////////////////////   P E R M A L I N K   //////////////////////////////////////////

  updateLocation: function()  {
    try {

      var lat, lon, latlon;

//      $("permaLink").setStyle("background", "transparent url(/images/satellite/waiter.gif) no-repeat scroll left top");
//      $("permaLink").setStyle("background", "transparent url(/images/waiting.gif) no-repeat scroll left top");
      $("permaLink").className = "permaWait";

      EOP.GEO.permaText = "";
      lat = this.MAP.gmap.getCenter().lat();
      lon = this.MAP.gmap.getCenter().lng();
      latlon = new GLatLng(lat, lon);

      EOP.GEO.geoCoder.getLocations(latlon  , function(addresses) {

        var out = [], line = "", i = 0,  title = "";

        if(addresses.Status.code !== 200) {
          EOP.GEO.updatePermalink();
        } else {
          for (i = 16; i > 0 ; i--) {
            if(addresses.Placemark[i]) {
              out.include(addresses.Placemark[i].address.split(",")[0]);
            }
          }
          if (out.length == 0) {
            EOP.GEO.updatePermalink();
          } else if (out.length == 1) {
            EOP.GEO.permaText = out[0];
            EOP.GEO.updatePermalink(out[0]);
          } else {
            for (i = 0; i < out.length ; i++) {
              if (i == 0)      {line += "<span class='permaCC'>" + out[i] + " - </span>";}
              else if (i == 1) {line += "<span class='permaLL'>" + out[i] + ", </span>" ;}
              else             {line += "<span class='permaSM'>" + out[i] + ", </span>";}
              title += out[i] + ", ";
            }
            EOP.GEO.permaText = out[0] + "-" + out[out.length-1];
            EOP.GEO.updatePermalink(out[0] + "-" + out[out.length-1]);
          }
        }
//        EOP.GEO.updatePermalinkHref();
      });

    } catch (e){catchError("cGoogleMap.updatePosition", e) ;}
  },

  updatePermalink: function(location)  {

    function getBits(){
      var ovlbits = 0;
      EOP.GEO.overlays.forEach(function(ovl){
        if (ovl.enabled) {
          ovlbits |= Math.pow(2, ovl.ovl.bit);
        }
      });
      return Math.pow(2, eopDATA.map.bit) | ovlbits;
    }
    function getDate(){
      var d = new Date(eopDATA.yir, 0, eopDATA.doy);
      return d.getFullYear() + "-" + trimZero(d.getMonth() +1, 2) + "-" + trimZero(d.getDate(), 2);
    }
    function getCoords(){
      var lat, lon, lvl, NS, EW;
      lat = EOP.GEO.MAP.gmap.getCenter().lat().toFixed(6);
      lon = EOP.GEO.MAP.gmap.getCenter().lng().toFixed(6);
      lvl = EOP.GEO.MAP.gmap.getZoom();

      NS = (lat >= 0) ? "N" : "S"; lat = Math.abs(lat);
      EW = (lon >= 0) ? "E" : "W"; lon = Math.abs(lon);

      return lvl + "-" + NS + lat + "-" + EW + lon;
    }

    var perma, url, loc, b = getBits(), d = getDate(), c = getCoords();
    loc = (location) ? "/" + location.replace(" ", "-").replace("%20", "-") : "";
    url = "/observations/" + b + "/" + d + "/" + c + loc;
    $("permaLink").set("href" , url);

    perma = "//ARCTIC.IO/OBSERVATIONS/" + b ;

    if (eopDATA.map.doesTime) {
      perma += "/" + d
    }
    if (location) {
      perma += "/" + location;
    } else {
      perma += "/" + c;
    }
//    if (perma.length > 65) {perma = perma.slice(0, 65) + "...";}

    $("permaLink").set("html",  perma);
    $("permaLink").set("title", "PermaLink: " + decodeURI($("permaLink").href));
//    $("permaLink").setStyle("background", "transparent url(/images/satellite/perma.gif) no-repeat scroll left top");
    $("permaLink").className = "permaActiv";

  }

});


//
/////////////////////////      G O O G L E   M A P        ////////////////////////////////////////////////////////////
//


var cGoogleMap = new Class ({

  Implements: [Events],

  checkResize   : function () {this.gmap.checkResize(); IceCanvas.resize();},

  initialize    : function(sender, options){
    try {
      this.gmap   = null;
      this.status = {};
    } catch (e){catchError("cGoogleMap.initialize", e) ;}
  },

  initStructure : function() {
    try {

      eopDATA.map = mapDATA.filter(function (map){
        return (map.type == eopDATA.map && map.subtype == eopDATA.sat);
      }).getValues()[0];
      //eopDATA.map = map;

      this.gmap = new GMap2($("mapFull"), "#123");
      this.gmap.setCenter(new GLatLng(eopDATA.lat, eopDATA.lon), eopDATA.lvl);
      this.switchMap(eopDATA.map);
      window.fireEvent("mapready");  // -> switchConsole

    } catch (e){catchError("cGoogleMap.initStructure", e) ;}
  },

  initBehaviour : function() {
    try {
      this.gmap.enableDragging();
      this.gmap.enableContinuousZoom();
      this.gmap.enableScrollWheelZoom();
      this.gmap.addControl(new GLargeMapControl3D(), new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(10, 42)));
      this.gmap.addControl(new GScaleControl());

      var update = function(){
        EOP.GEO.updateLocation();
        for (var i=0, ovl; ovl= EOP.GEO.overlays[i]; i++){
          ovl.update();
        }
        IceCanvas.update();
      }
      GEvent.addListener(this.gmap, "movestart", IceCanvas.clear );
      GEvent.addListener(this.gmap, "moveend", update );
      GEvent.addListener(this.gmap, "zoomend", update );
//      GEvent.addListener(this.gmap, "mousemove", function(e) {EOP.GEO.MAP.updateMouseCoords(e);} );

      var junk = new GKeyboardHandler(this.gmap);


//      this.gmap.addOverlay(new GLayer("com.panoramio.all"));
//      EOP.GEO.MAP.gmap.addOverlay(new GGeoXml("http://data.exploreourpla.net/shapes/ice200809.xml"));
//      EOP.GEO.MAP.gmap.addOverlay(new GGeoXml("http://data.exploreourpla.net/shapes/ice200808.xml"));
//      EOP.GEO.MAP.gmap.addOverlay(new GGeoXml("http://data.exploreourpla.net/shapes/ice200807.xml"));
//      EOP.GEO.MAP.gmap.addOverlay(new GGeoXml("http://data.exploreourpla.net/shapes/ice200806.xml"));
//      EOP.GEO.MAP.gmap.addOverlay(new GGeoXml("http://data.exploreourpla.net/shapes/ice200805.xml"));
//      EOP.GEO.MAP.gmap.addOverlay(new GGeoXml("http://data.exploreourpla.net/shapes/ArcticCoastline10m-01.xml"));


    } catch (e){catchError("cGoogleMap.initBehaviour", e) ;}
  },
  getBBox : function(){

    var north, south, west, east, cUL, cUR, cLL, cLR, size = EOP.GEO.MAP.gmap.getSize();

    cUL = this.gmap.fromContainerPixelToLatLng(new GPoint(0, 0));
    cUR = this.gmap.fromContainerPixelToLatLng(new GPoint(0, size.width));
    cLL = this.gmap.fromContainerPixelToLatLng(new GPoint(size.height, 0));
    cLR = this.gmap.fromContainerPixelToLatLng(new GPoint(size.height, size.width));

//    console.log("UL", cUL.lat(), cUL.lng());
//    console.log("UR", cUR.lat(), cUR.lng());
//    console.log("LL", cLL.lat(), cLL.lng());
//    console.log("LR", cLR.lat(), cLR.lng());

    north = Math.max(cUL.lat(), cUR.lat(), cLL.lat(), cLR.lat());
    east  = Math.max(cUL.lng(), cUR.lng(), cLL.lng(), cLR.lng());
    south = Math.min(cUL.lat(), cUR.lat(), cLL.lat(), cLR.lat());
    west  = Math.min(cUL.lng(), cUR.lng(), cLL.lng(), cLR.lng());

    return {north: north, east: east, south: south, west: west}

  },
  updateMouseCoords: function(e)  {
    if (EOP.GEO.ActionKey) {
//      EOP.GEO.updateLocation(e.y, e.x);
    }
  },


//
//
//
//////////////////////   M A P   //////////////////////////////////////////

  setTime: function(how)  {
    try {

      if (eopDATA.map.doesTime){
        switch (how){
          case "nextDay": eopDATA.doy++; break;
          case "prevDay": eopDATA.doy--; break;
        }
      }
      this.switchTime();
//      console.log("MAP.setTime", how, "OUT", eopDATA.map.subtype, eopDATA.day);
    } catch (e){catchError("cGoogleMap.setTime", e) ;}
  },

  switchTime: function(dat)  {
    try {

      if (dat){
        eopDATA.yir =  dat.getUTCFullYear(dat);
        eopDATA.doy =  EOP.GEO.getDay(dat);
        $("mapDate").title = dat.toGMTString();
      } else {
        var curDay = EOP.GEO.getCurDay();
        $("mapDate").value = trimZero(curDay.getUTCDate(),2) + "/" + trimZero((curDay.getUTCMonth() +1),2) + "/" + curDay.getUTCFullYear();
        $("mapDate").title = curDay.toGMTString();
      }

      this.switchMap(eopDATA.map);
      EOP.GEO.calCtrl.changed(EOP.GEO.CAL);
      IceCanvas.update();

      // console.log("MAP.switchTime", $("mapDate").value, eopDATA.day);

    } catch (e){catchError("cGoogleMap.setDate", e) ;}
  },


//  switchOvl: function(ovl)  {
//    for (var item in ovlDATA){
//      if (ovl === item){
//        item.active = !item.active;
//        console.log("switchOvl", item.title, item.active)
//      }
//    }
//
//  },
  switchMap: function(map)  {
    try {

      if (!map) {console.log("switchMap: map undefined");}

      $("divCalendar").setStyle("display", (map.doesTime) ? "block" : "none");

      this.gmap.setMapType(this.getMapType(map));
      if (eopDATA.map.type != map.type) {
        this.gmap.setCenter (new GLatLng (map.defLat, map.defLon), map.defLvl);
      }
      eopDATA.map = map;
      EOP.updateName();
      EOP.GEO.updateLocation();

//      console.log(" OK - switchMap: ", map.title);

    } catch (e){catchError("cGoogleMap.switchMap", e) ;}
  },

  getMapType   : function(map)  {
    try {
      var MT, TL, PR;

      if      (map.proj == "goo" && map.subtype == "sat") {MT = G_SATELLITE_MAP;}
      else if (map.proj == "goo" && map.subtype == "map") {MT = G_NORMAL_MAP;}
      else if (map.proj == "goo" && map.subtype == "ter") {MT = G_PHYSICAL_MAP;}
      else if (map.proj == "ice" ) {
        TL = new eopTileLayer(map);
        PR = new PolarStereographicProjection(map.maxRes, map.pHint);
        MT = new GMapType([TL], PR, map.title, {maxResolution: map.maxRes, minResolution: map.minRes, errorMessage: "", tileSize: map.tileSize});
      } else if (map.proj == "lce" ) {
        TL = new eopTileLayer(map);
        PR = new eopProjection(map, map.maxRes, map.tileSize);
        MT = new GMapType([TL], PR, map.title, {maxResolution: map.maxRes, minResolution: map.minRes, errorMessage: "", tileSize: map.tileSize});
      } else if (map.proj == "kml" ) {
        TL = new eopTileLayer(map);
        PR = new eopProjection(map, map.maxRes, map.tileSize);
        MT = new GMapType([TL], PR, map.title, {maxResolution: map.maxRes, minResolution: map.minRes, errorMessage: "", tileSize: map.tileSize});
      } else {
        console.log("NOK - getMapType: unknown map", map.proj, map.type, map);
        return false;
      }

      MT.getErrorMessage = this.mapError;
      return MT;
    } catch (e){catchError("getMapType", e, map);}
  },

  zoom : function(msg) {
    try {

      var map, gmap, center, latlon, zoom, doPan = false, doNavUpdate = true;

      console.log("cGoogleMap.zoom", msg);

      map    = eopDATA.map;
      gmap   = this.gmap;
      zoom   = gmap.getZoom();
      latlon = new GLatLng (gmap.getCenter().lat(), gmap.getCenter().lng());

      switch(msg.action) {
        case "level":zoom = msg.level;break;
        case "zoomin":if (zoom < map.maxRes) {zoom++ ;}break;
        case "zoomout":if (zoom > 0 ) {zoom-- ;}break;
        case "centerto":latlon = new GLatLng (msg.lat, msg.lon);zoom = msg.level;break;
        default         :console.log("cGoogleMap.zoom: unknown action", msg.action) ;break;
      }
      this.gmap.setCenter (latlon, zoom);

    } catch (e){catchError("cGoogleMap.zoom", e) ;}
  },

  mapError : function() {
    var out;
    out  = "<strong>" + this.getName() + "</strong><br />";
    out += "This level (" + EOP.GEO.MAP.gmap.getZoom() + ") is<br />not supported<br />";
    out += "or this tile is currently <br /> not (yet) available. <br />Try again in a few minutes.<br />or<br />";
    out += "<a class='action' href='javascript:void(0)' onclick='EOP.GEO.MAP.zoom({action : \"zoomout\"})' >Zoom out</a>, ";
    out += "<a href='" + $("permaLink").href + "'>Refresh</a>";
    return out;
  }
});


//
//
//
//////////////////////   T I L E S   //////////////////////////////////////////

  function getTileCode(map, z, x, y) {             // "2009.123.10.654.456.Ant.ter"
    var out = eopDATA.yir + "." ;
    out   += trimZero(eopDATA.doy, 3) + ".";
    out   += trimZero(z, 2) + ".";
    out   += trimZero(x, 3) + ".";
    out   += trimZero(y, 3) + ".";
    out   += map.type + ".";
    out   += map.subtype;
    return out;
  }

  function trimZero (s, l) {
     var out = "000000" + s;
     return out.substr(out.length -l);
  }

  function getWMSDate(){
    var date  = new Date(eopDATA.yir, 0, eopDATA.doy);
    var month = trimZero(date.getMonth() +1, 2);
    var day   = trimZero(date.getDate(), 2);
    return eopDATA.yir + "-" + month + "-" + day;
  }

  function getLanceDate(){
    var date  = new Date(eopDATA.yir, 0, eopDATA.doy);
    var month = trimZero(date.getMonth() +1, 2);
    var day   = trimZero(date.getDate(), 2);
    return eopDATA.yir + "" + month + "" + day;
  }

  function eopTileLayer (map)  {
    try {

      var ts, url, tile, yearday;

      ts = new GTileLayer(new GCopyrightCollection(""), map.minRes, map.maxRes);
      ts.isPng            = $F("return false");
      ts.getOpacity       = $F("return 1");
      ts.getTextColor     = $F("return '#CC9933'");
      ts.getLinkColor     = $F("return '#CC9933'");
      ts.getErrorMessage  = $F("return ''");
      ts.getCopyright     = function () {return "<a title='"  + map.title + "' onclick='window.open(this.href);' href='" + map.url + "'>" + map.title + "</a>";};


      switch (map.proj) {

        case "ice" :
          ts.getTileUrl   = function(gp, zoom) {

            var tc, r, c, urlTile, urlHome, boundUpp, boundLow, lvlOrg, lengthEdge;

            lvlOrg     = 5;                                // directly from NASA
            lengthEdge = Math.pow(2, zoom - lvlOrg) * 6;    // 6 Nasa tiles per zoom level
            boundLow   = Math.pow(2, zoom - lvlOrg);
            boundUpp   = boundLow + lengthEdge -1;

            r  = Math.pow(2, zoom) - gp.y - 1 -2 * lengthEdge;
            c  = gp.x -2 * lengthEdge;
            tc = getTileCode(map, zoom, c, r);

            if (zoom == 4) {
              boundLow   = Math.pow(2, zoom - lvlOrg) -1;
              boundUpp   = boundLow + lengthEdge + 1;
            }

            if (c > boundUpp || r > boundUpp || c < boundLow || r < boundLow ){
              urlHome = "http://ice-map.appspot.com/images/"; urlTile = "trans256.gif";
            } else {
              if (zoom === 8){
                urlHome = "http://ice-level8.appspot.com/tiles/?tile="; urlTile = tc;
              } else {
                urlHome = "http://ice-map.appspot.com/tiles/?tile=";   urlTile = tc;
              }
            }

            return urlHome + urlTile;
          };
        break;


        case "kml":
          //http://onearth.jpl.nasa.gov/wms.cgi?request=GetMap&layers=daily_planet&srs=EPSG:4326&format=image/jpeg&styles=&time=2010-06-10&width=512&height=512&bbox=76.0000000000,-38.0000000000,140.0000000000,26.0000000000
          ts.getTileUrl   = function(gp, zoom) {

            var urlHome, urlTile, xTiles, yTiles, nNorth, nSouth, nWest, nEast, xCover, yCover, layer ;

            switch (map.subtype) {
              case "aqa" :layer = "daily_afternoon";break;
              case "ter" :layer = "daily_planet"   ;break;
            }

            urlHome  =  "http://onearth.jpl.nasa.gov/wms.cgi?request=GetMap&layers=" + layer + "&srs=EPSG:4326&format=image/jpeg&styles=";
            urlHome  += "&time=" + getWMSDate() + "&width=512&height=512";

            xTiles  = Math.pow(2, zoom -1) ;xCover = 360/xTiles;
            yTiles  = xTiles / 2           ;yCover = 180/yTiles;
            nNorth  = 90 - yCover * gp.y;
            nSouth  = nNorth - yCover;
            nWest   = -180 + xCover * gp.x;
            nEast   = nWest + xCover;

            urlTile = "&bbox=" + nWest + "," + nSouth + "," + nEast + "," + nNorth;

            return urlHome + urlTile;

          };
        break;

        case "lce":

          ts.getTileUrl   = function(gp, zoom) {

            var urlBase, paraLayer, paraExtent, paraSize, paraDate;
            var xTiles, yTiles, nNorth, nSouth, nWest, nEast, xCover, yCover, layer ;

            //urlBase  = "http://wms.jpl.nasa.gov/wms.cgi?request=GetMap&width=512&height=512&layers=";
            //urlBase  += layer  + "&styles=&srs=EPSG:4326&format=image/jpeg&bbox=";

            urlBase  = "http://lance2.modaps.eosdis.nasa.gov/cgi-bin/mapserv.cgi";
            paraSize = "&map_size=256+256&imgx=128&imgy=128&imgxy=256+256";
            paraDate = "" + eopDATA.yir + eopDATA.doy;


            switch (map.subtype) {
              case "T143" :
                paraLayer  = "?map_layer[Terra250]=DATA+terr_250m_" + paraDate + ".vrt";
                paraLayer += "&layers=Terra250" + "&map=rrglobal_terra_250m.map" + "&mode=map" + "&map_imagetype=jpeg";
              break;
              case "A143" :
                paraLayer  = "?map_layer[Aqua250]=DATA+aqua_250m_" + paraDate + ".vrt";
                paraLayer += "&layers=Aqua250" + "&map=rrglobal_aqua_250m.map" + "&mode=map" + "&map_imagetype=jpeg";
              break;
              default: console.log("Unknown Subtype", map.subtype);
            }

            // http://lance2.modaps.eosdis.nasa.gov/cgi-bin/mapserv.cgi
            // ?map_layer[Terra250]=DATA+terr_250m_2011164.vrt
            // &layers=Terra250
            // &map=rrglobal_terra_250m.map
            // &mode=map
            // &map_imagetype=jpeg
            // &mapext=2+-25+47+20
            // &imgext=2+-25+47+20
            // &map_size=256+256
            // &imgx=128
            // &imgy=128
            // &imgxy=256+256


            xTiles  = Math.pow(2, zoom) ; xCover = 360/xTiles;
            yTiles  = xTiles / 2        ; yCover = 180/yTiles;
            nNorth  = 90 - yCover * gp.y;
            nSouth  = nNorth - yCover;
            nWest   = -180 + xCover * gp.x;
            nEast   = nWest + xCover;

            paraExtent = nWest + "+" + nSouth + "+" + nEast + "+" + nNorth;
            paraExtent = "&mapext=" + paraExtent + "&imgExt=" + paraExtent;

            return urlBase + paraLayer + paraExtent + paraSize;

          };
        break;
      }

      return ts;

    } catch (e){catchError("eopTileLayer", e, map);}
  }

//
//
//
//////////////////////   P R O J E C T I O N S  //////////////////////////////////////////

  function eopProjection(map, vzoomLevels, vTileSize) {

    var zoom, tilesX, pixelX;
    this.pixelsPerLonDegree = [];
    this.pixelsPerLonRadian = [];
    this.pixelOrigo         = [];
    this.tileBounds         = [];
    this.tileSize           = vTileSize;
    this.pType              = "wsg84";

    for(zoom = 0; zoom < vzoomLevels+2; zoom++) {

      tilesX = Math.pow(2, zoom);
      pixelX = this.tileSize *  tilesX;
      this.pixelsPerLonDegree.push(pixelX / 360);
      this.pixelsPerLonRadian.push(pixelX / (2 * Math.PI));
      this.pixelOrigo.push(new GPoint(pixelX / 2 , pixelX / 4));
      this.tileBounds.push(pixelX);
    }

    this.getWrapWidth  = function(zoom) {return this.tileBounds[zoom];};
    this.M             = function(zoom) {return this.tileBounds[zoom];};   // helps in 2.44

    this.fromLatLngToPixel = function(ll, zoom) {
      var d = this.pixelOrigo[zoom];
      var x = (ll.lng() + 180) * d.x / 180;
      var y = (90 - ll.lat())  * d.y / 90;
      return new GPoint(x, y);
    };

    this.fromPixelToLatLng = function(px, zoom, c) {
      var e   = this.pixelOrigo[zoom];
      var lon = (px.x - e.x) * 180 / e.x;
      var lat = (e.y - px.y) *  90 / e.y ;
      return new GLatLng(lat, lon, c);
    };

    this.tileCheckRange    = function(tile, zoom, c) {
      var d = this.tileBounds[zoom];
      if (tile.y < 0 || tile.y * c >= d) {return false;}
      if (tile.x < 0 || tile.x * c >= d) {
        var e = Math.floor(d / c);
        tile.x = tile.x % e;
        if (tile.x < 0) {tile.x += e;}
      }
      return true;
    };
  }

  eopProjection.prototype = new GProjection();


  function PolarStereographicProjection( zoomlevels, type ) {
    var me = this;
    me.zoomlevels = zoomlevels;
    me.zvector = type;
    me.magic = true;
  }

  PolarStereographicProjection.prototype = new GProjection();

  PolarStereographicProjection.prototype.fromLatLngToPixel = function(latlng, zoom) {

    var scale = 0.75; // 70N for Arctic
    var lat = Math.PI/180 * latlng.lat();
    var lon = Math.PI/180 * latlng.lng();
    var s1 = scale * Math.cos(lat)/(1.0 + this.zvector * Math.sin(lat));
    var s2 = 256 / 2 * Math.pow(2,zoom);
    var x = ( Math.sin(lon) * s1 + 1 ) * s2;
    var y = ( this.zvector * Math.cos(lon) * s1 + 1 ) * s2;
    return new GPoint(x,y);
  };

  PolarStereographicProjection.prototype.fromPixelToLatLng = function(pixel, zoom, unbounded) {

    if (zoom == 17) {
      this.magic = ! this.magic;
      if( this.magic ) {
        return new GLatLng(90,180);
      } else {
        return new GLatLng(-90,-180);
      }
    }

    var scale = 1.75; // 70N for Arctic
    var s2 = 256 / 2 * Math.pow(2,zoom);
    var x = pixel.x / s2 - 1;
    var y = pixel.y / s2 - 1;
    if ( x == 0 && y == 0 ) {return new GLatLng(90,0);}
    var z = this.zvector * Math.min( 2.0 / ( ((x*x + y*y) * scale) + 1 ) - 1.0, 1.0 );
    var lon = 180/Math.PI * Math.atan2( x, this.zvector * y );
    var lat = 180/Math.PI * Math.asin( z );
    return new GLatLng(lat,lon);
  };


  // Original Projection
  // http://ti.arc.nasa.gov/project/planetary/maps/

  PolarStereographicProjection.prototype.fromLatLngToPixelOrg = function(latlng, zoom) {

    var lat = Math.PI/180 * latlng.lat();
    var lon = Math.PI/180 * latlng.lng();
    var s1 = Math.cos(lat)/(1.0 + this.zvector * Math.sin(lat));
    var s2 = 256 / 2 * Math.pow(2,zoom);
    var x = ( Math.sin(lon) * s1 + 1 ) * s2;
    var y = ( this.zvector * Math.cos(lon) * s1 + 1 ) * s2;
    return new GPoint(x,y);

  };

  PolarStereographicProjection.prototype.fromPixelToLatLngOrg = function(pixel, zoom, unbounded) {

    var s2 = 256 / 2 * Math.pow(2,zoom);
    var x = pixel.x / s2 - 1;
    var y = pixel.y / s2 - 1;
    if ( x == 0 && y == 0 ) return new GLatLng(90,0);
    var z = this.zvector * Math.min( 2.0 / ( x*x + y*y + 1 ) - 1.0, 1.0 );
    var lon = 180/Math.PI * Math.atan2( x, this.zvector * y );
    var lat = 180/Math.PI * Math.asin( z ) ;
    return new GLatLng(lat,lon);

  };

  PolarStereographicProjection.prototype.tileCheckRange = function(tile, zoom, tilesize) {
    if( zoom > this.zoomlevels ) return false;
    else return true;
  };

  PolarStereographicProjection.prototype.getWrapWidth = function(zoom) {
    // Is there a better cross-platform way to represent Infinity here?
//    console.log("getWrapWidth");
    return 1E+100 * Math.pow(2,zoom);
  };





