
  var IceCanvas = (function(){
    
    var self, ctx, canvas, width, height, drawers = [], active = false;
    
    return {
      isActive : function(){return active;},
      getCtx   : function(){return ctx;},
      clear    : function(){canvas.width = canvas.width;},
      init : function(){
        self = this;
        try {
          canvas = document.createElement("CANVAS");
          canvas.id = "icecanvas";
          canvas.style.position = "fixed";
          canvas.style.background = "transparent";
          canvas.style.zIndex = "99";
          ctx = canvas.getContext("2d");
        } catch(e){
          console.log("NOK - Canvas not available", e);
        }
        if (EOP.deb) {this.addDrawer({name: "test", obj: this, func: this.test});}
        this.resize();
        return self;
      },
      getNPCenter : function(){

        var map      = EOP.GEO.MAP.gmap;
        var offLeft  = parseInt(map.getContainer().firstChild.firstChild.style.left, 10);
        var offTop   = parseInt(map.getContainer().firstChild.firstChild.style.top,  10);
        var northPol = map.fromLatLngToDivPixel(new GLatLng(90, 0));
        var facZoom = {4 : 0.5, 5 : 1, 6 : 2, 7 : 4, 8 : 8 }[map.getZoom()];
        return {
          x: northPol.x + offLeft,
          y: northPol.y + offTop,
          zoomLevel: map.getZoom(),
          zoomFactor: facZoom,
          offX: offLeft,
          offY: offTop
        };
        
      },
      test : function(ctx, width, height){

        var np = this.getNPCenter();

        ctx.save();
        ctx.strokeStyle = "#833";
        ctx.lineWidth = 2;
        ctx.strokeRect(20, 56, width-40, height-40-36);
        ctx.beginPath();
        ctx.moveTo(20, np.y);
        ctx.lineTo(width-20, np.y);
        ctx.moveTo(np.x, 56);
        ctx.lineTo(np.x, height-20);
        ctx.closePath();
        ctx.stroke();
        ctx.restore();

      },
      enable : function(){
        EOP.GEO.MAP.gmap.getPane(G_MAP_OVERLAY_LAYER_PANE).appendChild(canvas);
        active = true;
      },
      disable : function(){
        canvas.parentNode.removeChild(canvas);
        active = false;
      },
      update : function(){
        this.clear();
        drawers.forEach(function(drawer){
          if (drawer.active){
            drawer.func.apply(drawer.obj, [ctx, width, height])
          }
        })
      },
      addDrawer : function(drawer){
        drawer.active = true;
        drawers.push(drawer);
        self.update();
        console.log("IceCanvas.addDrawer", drawer.name);
      },
      subDrawer : function(drawer){
        drawers.forEach(function(item){
          if (item.func === drawer.func){
            item.active = false;
            console.log("Drawer.disabled", item.name);
          }
        });
        self.update();
      },
      resize : function(){
        width  = window.getSize().x;
        height = window.getSize().y;
        canvas.width  = width;
        canvas.height = height;
        canvas.style.left = "0";
        canvas.style.top  = "0";
        canvas.style.width  = width + "px";
        canvas.style.height = height + "px";
        ctx.fillStyle = "rgba(0, 0, 0, 0)";
        ctx.fillRect(0, 0, width, height);
        self.update();
      }
    };
  })();


  function IceMarker(ovl, data){
    // this.title = data.asciiname.replace(" ", "&nbsp;");
    this.title = data.asciiname;
    this.latlon = new GLatLng(data.latitude, data.longitude);
    this.xDiff = 2;
    this.yDiff = 2;
    this.data = data;
    this.ovl = ovl;
    this.ele = document.createElement("DIV");
    this.ele.title = data.title;
    this.ele.className  = "iceMarker " + ovl.cssClass;
    this.ele.appendChild($I("", this.title));
    EOP.GEO.MAP.gmap.getPane(G_MAP_MARKER_PANE).appendChild(this.ele);
  }

  IceMarker.prototype = {

    initialize : function(force) {},
    redraw : function() {
      var p = EOP.GEO.MAP.gmap.fromLatLngToDivPixel(this.latlon);
      this.ele.style.left = p.x - this.xDiff + "px";
      this.ele.style.top  = p.y - this.yDiff + "px";
    },
    remove : function() {
      this.ele.parentNode.removeChild(this.ele);
    },
    reuse  : function(data){
      this.latlon = new GLatLng(data.latitude, data.longitude);
      this.data  = data;
      this.ele.title      = data.title;
      this.ele.className  = "iceMarker " + this.ovl.cssClass;

      if (this.type == "img"){
        this.ele.alt         = data.name;
        this.ele.src         = data.picture;
        if (EOP.isIE){
          this.ele.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.ele.src + "', sizingMethod='scale')";
          this.ele.src          = this.picTrans;
      }}
      else {this.ele.firstChild.data = data.asciiname;}
      this.redraw();
    }

  }

///////     S U P E R C L A S S   E O P   E X T E N S I O N S          ///////

  function IceExtension() {
    this.enabled = false;
    this.data = "";
    this.hasAllData = false;
    this.xDiff = 0;
    this.yDiff = 0;
    this.markers = [];
  }

  IceExtension.prototype = {

    updateMove : function (){return false;},
    update : function (){
      if (this.enabled && !this.hasAllData){
        this.requestData();
      }
    },
    clear      : function () {
      for (var i = 0; i < this.markers.length ; i++)
        {EOP.GEO.MAP.gmap.removeOverlay(this.markers[i]);}
      this.markers = [];
    },
    toggle     : function () {
      console.log("IceExtension.toggle", this, !this.enabled);
      this.enabled = !this.enabled;
      if (!this.enabled){
        this.clear();
        this.hasAllData = false;
      } else {this.requestData();}
    },
    show       : function (vData){
      try {
        if (!this.enabled || !vData.Data){return;}
        this.Data = eval(vData.Data);
        if (!this.Data[this.JSONObject]){return;}
      } catch (e){catchError(this.AnchorName + ".show", e);return;}
      this.display();
    }

  }


///////     I M A G E   C A N V A S             ///////////////////////////////

  function getImgCanvas(name, url, callback){

    var self = {
      url: url,
      name: name,
      img: document.createElement("image"),
      cvs: document.createElement("canvas")
    };

    return ({
      init: function(){
        self.img.onerror = function(){
          console.log("Onerror: ImgCanvas - ", self.name);
        }
        self.img.onload = function(){
          self.width = self.img.width;
          self.height = self.img.height;
          self.cvs.width = self.width;
          self.cvs.height = self.height;
          self.ctx = self.ctx.getContext("2d");
          self.ctx.drawImage(self.img, 0, 0, self.width, self.height, 0, 0, self.width, self.height);
          callback(self);
          console.log("Did.callback");
        };
        self.img.src = url;
        return self;
      }

    }).init();

  }


///////     R A D A R            ///////

  function OvlRadar(ovl){
    this.enabled = false;
    this.loaded = false;
    this.ovl = ovl;
    this.json = null;
    this.vectors = [];
//    this.home = "/ajax/proxy/?url=http://ocean.dmi.dk/arctic/satellite/plots/";
    this.home = "/ajax/proxy/?url=http://www.seaice.dk/iwicos/latest/envisat.GMM3d.n.20110826.gif";
    this.home = "/static/envisat.GMM3d.n.20110826.gif";
    this.image = document.createElement("IMG");
    this.image.width  = 1800;
    this.image.height = 2200;
    this.targetLeft   = -45;
    this.targetTop    = 55;
    this.targetRotate = 0;
    this.targetWidth  = 1536;
    this.targetAlpha  = 0.8;
    this.targetZoom   = this.targetWidth / this.image.width * 1.078;
    this.image.onerror = function(e){
      console.log("Radar.image", e, this.image.src);
    }
  }
  OvlRadar.prototype = new IceExtension();
  OvlRadar.prototype.constructor = OvlRadar;
  OvlRadar.prototype.update = function(){/* do nothing */};
  OvlRadar.prototype.getUrl = function(){
    //-/ajax/proxy/?url=http://ocean.dmi.dk/arctic/satellite/plots/satsst.arc.d-00.png
    var off = EOP.GEO.getDay(new Date()) - EOP.GEO.getDay(EOP.GEO.getCurDay()) -1;
    off = ("00" + off)
    off = off.substr(off.length -2);
//    return this.home + "satsst.arc.d-" + off + ".png";
    return this.home
//    return this.home + "satsst.arc.d-" + off + ".png";
  },
  OvlRadar.prototype.toggle = function(){
    var self = this;
    this.enabled = !this.enabled;
    if (!this.enabled){
      IceCanvas.subDrawer({name: this.ovl.name, func: this.paint})
    } else {
      if (!this.loaded) {
        this.requestData(this.getUrl(), function(){
          IceCanvas.addDrawer({name: self.ovl.name, obj: self, func: self.paint})
        });
      } else {
        IceCanvas.addDrawer({name: this.ovl.name, obj: this, func: this.paint})
      }
    }
  };
  OvlRadar.prototype.paint = function(ctx, width, height){

    function pic(url){var file = url.split("/"); return file[file.length-1]}

    if (pic(this.image.src) !== pic(this.getUrl())){
      this.requestData(this.getUrl(), function(){
        IceCanvas.update();
      });
      return;
    }

    var np = IceCanvas.getNPCenter();

    var x = this.targetLeft * np.zoomFactor + np.x - (this.image.width/2 * np.zoomFactor * this.targetZoom);
    var y = this.targetTop  * np.zoomFactor + np.y - (this.image.width/2 * np.zoomFactor * this.targetZoom);
    var w = this.image.width * np.zoomFactor * this.targetZoom;
    var h = this.image.width * np.zoomFactor * this.targetZoom;

    ctx.rotate(Math.PI *2);
//    ctx.save();
    ctx.globalAlpha = this.targetAlpha;
    ctx.globalCompositeOperation = "source-over";
    ctx.translate(np.x, np.y)

    ctx.strokeStyle = "#EE0";
    ctx.lineWidth = 2;
    ctx.strokeRect(-100, -200, 200, 400);

    ctx.drawImage(this.image, 0, 0, this.image.width, this.image.width, x, y, w, h);
//    ctx.restore();
    ctx.rotate(-Math.PI *2);

  };
  OvlRadar.prototype.requestData = function(url, callback){
    var self = this;
    this.image.src = url;
    this.image.onload = function(){
      self.loaded = true;
      callback();
    }
  };


///////     S S T           ///////

  function OvlSST(ovl){
    this.enabled = false;
    this.loaded = false;
    this.ovl = ovl;
    this.json = null;
    this.vectors = [];
    this.home = "/ajax/proxy/?url=http://ocean.dmi.dk/arctic/satellite/plots/";
    this.image = document.createElement("IMG");
    this.defaultwidth  = 604;
    this.defaultheight = 760;
    this.targetWidth   = 1536;
//    this.targetZoom   = this.targetWidth / this.image.width * 1.078;
    this.image.onerror = function(e){
      console.log("SST.image", e, this.image.src);
    }
  }
  OvlSST.prototype = new IceExtension();
  OvlSST.prototype.constructor = OvlSST;
  OvlSST.prototype.update = function(){ /* do nothing */ };
  OvlSST.prototype.getUrl = function(){
    //-/ajax/proxy/?url=http://ocean.dmi.dk/arctic/satellite/plots/satsst.arc.d-00.png
    var off = EOP.GEO.getDay(new Date()) - EOP.GEO.getDay(EOP.GEO.getCurDay()) -1;
    off = ("00" + off);
    off = off.substr(off.length -2);
    return this.home + "satsst.arc.d-" + off + ".png";
  },
  OvlSST.prototype.toggle = function(){
    var self = this;
    this.enabled = !this.enabled;
    if (!this.enabled){
      IceCanvas.subDrawer({name: this.ovl.name, func: this.paint})
    } else {
      if (!this.loaded) {
        this.requestData(this.getUrl(), function(){
          IceCanvas.addDrawer({name: self.ovl.name, obj: self, func: self.paint})
        });
      } else {
        IceCanvas.addDrawer({name: this.ovl.name, obj: this, func: this.paint})
      }
    }
  };
  OvlSST.prototype.paint = function(ctx, width, height){

    function pic(url){var file = url.split("/"); return file[file.length-1]}

    if (pic(this.image.src) !== pic(this.getUrl())){
      this.requestData(this.getUrl(), function(){
        IceCanvas.update();
      });
      return;
    }

    var np = IceCanvas.getNPCenter();

    var x = np.x  - (this.image.width * this.sourceScale * np.zoomFactor * this.targetZoom/2);
    var y = np.y  - (this.image.width * this.sourceScale * np.zoomFactor * this.targetZoom/2);
    var w = this.image.width * this.sourceScale * np.zoomFactor * this.targetZoom;
    var h = this.image.width * this.sourceScale * np.zoomFactor * this.targetZoom;

    ctx.save();
    ctx.globalAlpha = 0.5;
    ctx.globalCompositeOperation = "source-over";
    ctx.drawImage(this.image, 0, 65/this.sourceScale, this.image.width, this.image.width, x, y, w, h);
    ctx.restore();
//    console.log("OvlSST.paint", this.image.width, x, y, w, h);

  };
  OvlSST.prototype.requestData = function(url, callback){
    var self = this;
    this.image.src = url;
    this.image.onload = function(){
      self.sourceScale  = self.defaultwidth / self.image.width ;
      self.targetZoom   = self.targetWidth  /(self.sourceScale * self.image.width) * 1.078;
//      console.log("requestData", self.image.width, self.image.height, self.sourceScale);
      self.loaded = true;
      callback();
    }
  };


///////     S E A  I C E  D R I F T          ///////

  function OvlIceDrift(ovl){
    this.enabled = false;
    this.ovl = ovl;
    this.json = null;
    this.vectors = [];
    this.url = "/ajax/sea-ice-drift/24";
    this.offDay = 3;
  }
  OvlIceDrift.prototype = new IceExtension();
  OvlIceDrift.prototype.constructor = OvlIceDrift;
  OvlIceDrift.prototype.update = function(){/* do nothing */};
  OvlIceDrift.prototype.keyevent = function(enable){
    var self = this;
    window.onkeypress = enable ? function(e){
      var e  = (!e) ? window.event : e;
      var key  = String.fromCharCode(e.charCode).toLowerCase();
      switch (key) {
          case "1" : 
          case "2" :
          case "3" :
          case "4" :
          case "5" : 
          case "6" :
            clearInterval(self.timer);
            self.offDay = parseInt(key, 10);
            IceCanvas.update();
            e.preventDefault();
            return false;
            break;
          case "0":

            self.offDay = 1;
            IceCanvas.update();
            clearInterval(self.timer);
            self.timer = setInterval(function(){
              switch (self.offDay){
                case 1 : case 2 : case 3 : case 4 : case 5 : case 6 :
                  IceCanvas.update();
                  self.offDay += 1;
                  break;
                case 7:
                  self.offDay += 1;
                  break;
                default:
                  self.offDay = 1;
                  IceCanvas.update();
                  break;
              }
            }, 1000);
            return false;
            break;

        }
    } : null;
  };

  OvlIceDrift.prototype.toggle = function(){
    this.enabled = !this.enabled;
    if (!this.enabled){
      IceCanvas.subDrawer({name: this.ovl.name, func: this.paint});
      this.offDay = 3;
      this.keyevent(false);
      clearInterval(this.timer);

    } else {
      this.keyevent(true);
      if (!this.vectors.length) {
        this.requestData();
      } else {
        IceCanvas.addDrawer({name: this.ovl.name, obj: this, func: this.paint});
      }
    }
  };
  OvlIceDrift.prototype.paint = function(ctx, width, height){

    var map = EOP.GEO.MAP.gmap;
//    var offLeft = parseInt(map.getContainer().firstChild.firstChild.style.left, 10);
//    var offTop  = parseInt(map.getContainer().firstChild.firstChild.style.top,  10);

    var off = this.offDay;
    var np = IceCanvas.getNPCenter();
    ctx.globalAlpha = 1;
    ctx.globalCompositeOperation = "source-over";

    this.vectors.forEach(function(vector){

      var p = map.fromLatLngToDivPixel(new GLatLng(vector.lat, vector.lon));
      ctx.save()
      ctx.translate(p.x + np.offX, p.y + np.offY)

      if (vector.forecast[off][0] > 4) {
        ctx.strokeStyle = "rgba(0, 0, 0, 0.8)";
        ctx.fillStyle   = "rgba(255, 255, 255, 0.9)";
        ctx.lineWidth = 4;
        ctx.beginPath();
        ctx.arc(0, 0, 3, 0, Math.PI*2, true);
        ctx.closePath();
        ctx.stroke();
        ctx.fill();
      }

      for (var f=off; f<off+1; f++){

        if (vector.forecast[f][0] > 1) {

          ctx.rotate((Math.PI/180) * vector.forecast[f][1] + Math.PI/2);
          ctx.lineCap = "round";

          ctx.strokeStyle = "rgba(0, 0, 0, 0.8)"; // colors[f][0];
          ctx.lineWidth = 5;
          ctx.beginPath();
          ctx.moveTo(4, 0);
          ctx.lineTo(vector.forecast[f][0], 0);
          ctx.closePath();
          ctx.stroke();

          ctx.strokeStyle = "rgba(255, 255, 255, 0.9)";
          ctx.lineCap = "round";
          ctx.lineWidth = 2;
          ctx.beginPath();
          ctx.moveTo(2, 0);
          ctx.lineTo(vector.forecast[f][0]-1, 0);
          ctx.closePath();
          ctx.stroke();

          ctx.translate(vector.forecast[f][0] -2, 0)

          ctx.strokeStyle = "rgba(0, 0, 0, 0.8)"; // colors[f][0];
          ctx.lineWidth = 5;
          ctx.beginPath();
          ctx.moveTo(0, 0);
          ctx.lineTo(-6, -6);
          ctx.moveTo(0, 0);
          ctx.lineTo(-6.5, 6.5);
          ctx.closePath();
          ctx.stroke();

          ctx.strokeStyle = "rgba(255, 255, 255, 0.9)";
          ctx.lineCap = "round";
          ctx.lineWidth = 2;
          ctx.beginPath();
          ctx.moveTo(0, 0);
          ctx.lineTo(-5, -5);
          ctx.moveTo(0, 0);
          ctx.lineTo(-5, 5);
          ctx.moveTo(0, 0);
          ctx.lineTo(-5, 0);
          ctx.closePath();
          ctx.stroke();
        }
      };
      
      ctx.restore();
      
    });
  };
  
  OvlIceDrift.prototype.requestData = function(){

    var self = this;

    GDownloadUrl(this.url, function(data, status){
      var i, item, json, vec;
      if (status != 200) {console.log(self.name + ".error", data, status);} else {
        self.json = JSON.decode(data);

        console.log("OvlIceDrift", self.json.forecastdate, self.json.amount);

        for (i=0; i<500; i++){
          if (self.json.vectors[i]) {
            vec = self.json.vectors[i]
            if (vec.mod === "skiles") {
              item = {idx: i, lat: parseFloat(vec.lat), lon: parseFloat(vec.lon) + 180};
              item.forecast = {
                "1": [parseFloat(vec.forecast["24"].dis),  parseFloat(vec.forecast["24"].dir)],
                "2": [parseFloat(vec.forecast["48"].dis),  parseFloat(vec.forecast["48"].dir)],
                "3": [parseFloat(vec.forecast["72"].dis),  parseFloat(vec.forecast["72"].dir)],
                "4": [parseFloat(vec.forecast["96"].dis),  parseFloat(vec.forecast["96"].dir)],
                "5": [parseFloat(vec.forecast["120"].dis), parseFloat(vec.forecast["120"].dir)],
                "6": [parseFloat(vec.forecast["144"].dis), parseFloat(vec.forecast["144"].dir)]
              }
              self.vectors.push(item);
            }
          }
        }
        IceCanvas.addDrawer({name: self.ovl.name, obj: self, func: self.paint})
      }
    });

  };


///////     F U S I O N   C I T I E S           ///////

  function OvlCities(ovl){
    var self = this;
    this.ovl = ovl;
    this.limit = 30;
    this.callback = "EOP.GEO.MAP.updateCityLayer";
    EOP.GEO.MAP.updateCityLayer = function(json){
      self.updateData(json);
    }
  }
  OvlCities.prototype = new IceExtension();
  OvlCities.prototype.constructor = OvlCities;
  OvlCities.prototype.requestData = function(){
    var sql, src, rec, script, bbox = EOP.GEO.MAP.getBBox();
    rec = "RECTANGLE(LATLNG(" + bbox.south + ", " + + bbox.west + "), LATLNG(" + bbox.north + ", " + + bbox.east + "))";
    sql = "select * from 812840 WHERE ST_INTERSECTS(latitude, " + rec + ") ORDER BY sort DESC LIMIT " + this.limit;
    src = 'http://www.google.com/fusiontables/api/query?sql=' + encodeURIComponent(sql) + '&jsonCallback=' + this.callback;
    script = document.createElement('SCRIPT');
    script.src = src;
    $("scripts").appendChild(script);
  };

  OvlCities.prototype.updateData = function(json){

    var i, a, idx = 0, cty, item, items = [];

//    console.log("Cities: j/m", json.table.rows.length, this.markers.length);

    // make objects from rows
    for (i=0; i < json.table.rows.length; i++){
      item = {className: this.ovl.cssClass};
      for (a=0; a<json.table.cols.length; a++){
        item[json.table.cols[a]] = json.table.rows[i][a];
        items.push(item);
      }
    }

    for (i=idx; i<this.markers.length; i++){
      item = items[i];
      if (item){
        item.title = item.fcode;
        this.markers[i].reuse(item);
      }
      idx += 1;
    }

    for (i=idx; i<items.length; i++){
      item = items[i];
      item.title = item.population;
      cty = new IceMarker(this.ovl, item);
      EOP.GEO.MAP.gmap.addOverlay(cty);
      this.markers.push(cty);
    }

  };


///////     F U S I O N   F E T U R E S            ///////

  function OvlFeats(ovl){
    var self = this;
    this.ovl = ovl;
    this.limit = 30;
    this.callback = "EOP.GEO.MAP.updateFeatLayer";
    EOP.GEO.MAP.updateFeatLayer = function(json){
      self.updateData(json);
    }
  }
  OvlFeats.prototype = new IceExtension();
  OvlFeats.prototype.constructor = OvlFeats;
  OvlFeats.prototype.requestData = function(){
    var sql, src, rec, script, bbox = EOP.GEO.MAP.getBBox();
    rec = "RECTANGLE(LATLNG(" + bbox.south + ", " + + bbox.west + "), LATLNG(" + bbox.north + ", " + + bbox.east + "))";
    sql = "select * from 808195 WHERE ST_INTERSECTS(latitude, " + rec + ") AND enabled > 0 ORDER BY sort DESC LIMIT " + this.limit;
    src = 'http://www.google.com/fusiontables/api/query?sql=' + encodeURIComponent(sql) + '&jsonCallback=' + this.callback;
    script = document.createElement('SCRIPT');
    script.src = src;
    $("scripts").appendChild(script);
  };

  OvlFeats.prototype.updateData = function(json){

    var i, a, idx = 0, cty, item, items = [];

    console.log("Features: j/m", json.table.rows.length, this.markers.length);

    // make objects from rows
    for (i=0; i < json.table.rows.length; i++){
      item = {className: this.ovl.cssClass};
      for (a=0; a<json.table.cols.length; a++){
        item[json.table.cols[a]] = json.table.rows[i][a];
        items.push(item);
      }
    }

    function getTitle(item){
      return item.id + " - " + item.fcode;
    }

    for (i=idx; i<this.markers.length; i++){
      item = items[i];
      if (item){
        item.title = getTitle(item);
        this.markers[i].reuse(item);
      }
      idx += 1;
    }

    for (i=idx; i<items.length; i++){
      item = items[i];
      item.title = getTitle(item);
      cty = new IceMarker(this.ovl, item);
      EOP.GEO.MAP.gmap.addOverlay(cty);
      this.markers.push(cty);
    }

  };


  function OvlHarbours(ovl){
    var self = this;
    this.ovl = ovl;
    this.limit = 30;
    this.callback = "EOP.GEO.MAP.updateHarbLayer";
    EOP.GEO.MAP.updateHarbLayer = function(json){
      self.updateData(json);
    }
  }
  OvlHarbours.prototype = new IceExtension();
  OvlHarbours.prototype.constructor = OvlHarbours;




  function ShapeExtension(){}
  ShapeExtension.prototype = {
    update  : function(){},
    clear   : function () {
      this.polylines.forEach(function(pl){
        EOP.GEO.MAP.gmap.removeOverlay(pl);
      });
      this.polylines = [];
    },
    toggle  : function () {

      var self = this;
      this.enabled = !this.enabled;
      if (!this.enabled){
        this.clear();
      } else {
        this.requestData(function(){
          self.show();
        });
      }
    },
    show    : function (){
      this.polylines.forEach(function(pl){
        EOP.GEO.MAP.gmap.addOverlay(pl);
      });
//      console.log("show", this);
    },
    requestData : function(callback){

      var self = this;

      GDownloadUrl(this.url, function(data, status){

        if (status != 200) {console.log(self.name + ".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;
            }
            self.polylines.push(new GPolyline(latlons, self.color, self.width));
          }
          console.log(" OK - Coords ("+ coords.length + "/" + k + ")", self.polylines.length);
          callback();

        }
      });
    }
  };


//
//
///////     F U S I O N   F E T U R E S            ///////

  function ShpCircle(ovl){
    this.ovl = ovl;
    this.color = "#33F";
    this.width = 2;
    this.name = ovl.title;
    this.overlay = null;
    this.polylines = [];
  }
  ShpCircle.prototype = new ShapeExtension();
  ShpCircle.prototype.constructor = ShpCircle;
  ShpCircle.prototype.requestData = function(callback){

    var latlons = [], latCircle = 66.5622, latPole = 89.9, lon, lat, dLon;

    this.polylines = [];    

    dLon = 3;
    for (lon = -180; lon <= 180; lon += dLon) {
      latlons.push(new GLatLng(latCircle, lon));
    }
    this.polylines.push(new GPolyline(latlons, this.color, this.width));

    latlons = [];dLon = 10;
    for (lon = -180; lon <= 180; lon += dLon) {
      latlons.push(new GLatLng(latPole, lon));
    }
    this.polylines.push(new GPolyline(latlons, this.color, this.width));
    callback()
  };


  function ShpExtent(ovl){
    this.ovl = ovl;
    this.color = "#C33";
    this.width = 3;
    this.url = "/static/extent.kml";
    this.name = ovl.title;
    this.overlay = null;
    this.polylines = [];
  }
  ShpExtent.prototype = new ShapeExtension();
  ShpExtent.prototype.constructor = ShpExtent;


  function ShpOldIce(ovl){
    this.ovl = ovl;
    this.color = "#123";
    this.width = 2;
    this.url = "/static/extent.kml";
    this.name = ovl.title;
    this.overlay = null;
    this.polylines = [];
  }
  ShpOldIce.prototype = new ShapeExtension();
  ShpOldIce.prototype.constructor = ShpOldIce;

  ShpOldIce.prototype.requestData = function(callback){

    var latlons = [], self = this, k=0, p=0;

    this.polylines = [];

    aari.forEach(function(shape){
      shape.shp_data.parts.forEach(function(part){
        latlons = [];
        part.points.forEach(function(point){
          latlons.push(new GLatLng(point.y, point.x));
          p += 1;
        });
        self.polylines.push(new GPolyline(latlons, self.color, self.width));
        k +=1;
      });
    });

    console.log("requestData.out", k, p, self.polylines.length);
    callback();

  }


  function OvlHarbours(ovl){
    var self = this;
    this.ovl = ovl;
    this.limit = 30;
    this.callback = "EOP.GEO.MAP.updateHarbLayer";
    EOP.GEO.MAP.updateHarbLayer = function(json){
      self.updateData(json);
    }
  }
  OvlHarbours.prototype = new IceExtension();
  OvlHarbours.prototype.constructor = OvlHarbours;



//
//
///////     O B S E R V A T I O N S            ///////


  function OvlObservs(ovl){
    var self = this;
    this.ovl = ovl;
    this.limit = 30;
    this.callback = "EOP.GEO.MAP.updateObsLayer";
    EOP.GEO.MAP.updateObsLayer = function(json){
      self.updateData(json);
    }
  }
  OvlObservs.prototype = new IceExtension();
  OvlObservs.prototype.constructor = OvlObservs;



  function EdtObservs(ovl){
    this.data = [];
    var self = this;
    this.ovl = ovl;
  }
  EdtObservs.prototype = {
    update  : function(){},
    clear   : function () {
      this.polylines.forEach(function(pl){
        EOP.GEO.MAP.gmap.removeOverlay(pl);
      });
      this.polylines = [];
    },
    toggle  : function () {

      var self = this;
      this.enabled = !this.enabled;
      if (!this.enabled){
        this.clear();
      } else {
        this.requestData(function(){
          self.show();
        });
      }
    },
    show    : function (){
      this.polylines.forEach(function(pl){
        EOP.GEO.MAP.gmap.addOverlay(pl);
      });
//      console.log("show", this);
    },
    requestData : function(callback){

      var self = this;

      GDownloadUrl(this.url, function(data, status){

        if (status != 200) {console.log(self.name + ".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;
            }
            self.polylines.push(new GPolyline(latlons, self.color, self.width));
          }
          console.log(" OK - Coords ("+ coords.length + "/" + k + ")", self.polylines.length);
          callback();

        }
      });
    }
  };

