swapp.js 14 KB
// Generated by LiveScript 1.5.0
(function(){
  var GOLDEN_RATIO, SpaceWeather, Source, TimeSeries, Orbits, out$ = typeof exports != 'undefined' && exports || this;
  GOLDEN_RATIO = 2 / (1 + Math.sqrt(5));
  out$.SpaceWeather = SpaceWeather = (function(){
    SpaceWeather.displayName = 'SpaceWeather';
    var prototype = SpaceWeather.prototype, constructor = SpaceWeather;
    function SpaceWeather(configuration){
      console.info("Creating Space Weather app...", configuration);
    }
    return SpaceWeather;
  }());
  Source = (function(){
    Source.displayName = 'Source';
    var prototype = Source.prototype, constructor = Source;
    function Source(slug){
      this.slug = slug;
      this.time_series = {};
    }
    Source.prototype.addTimeSeries = function(ts){
      return this.time_series[ts.slug] = ts;
    };
    Source.prototype.show = function(){
      var slug, ref$, ts, results$ = [];
      for (slug in ref$ = this.time_series) {
        ts = ref$[slug];
        results$.push($(ts.svg).show());
      }
      return results$;
    };
    Source.prototype.hide = function(){
      var slug, ref$, ts, results$ = [];
      for (slug in ref$ = this.time_series) {
        ts = ref$[slug];
        results$.push($(ts.svg).hide());
      }
      return results$;
    };
    return Source;
  }());
  out$.TimeSeries = TimeSeries = (function(){
    TimeSeries.displayName = 'TimeSeries';
    var prototype = TimeSeries.prototype, constructor = TimeSeries;
    function TimeSeries(slug, title, data, container, options){
      this.slug = slug;
      this.title = title;
      this.data = data;
      this.container = container;
      this.options = options != null
        ? options
        : {};
      this.onMouseOut = bind$(this, 'onMouseOut', prototype);
      this.onMouseOver = bind$(this, 'onMouseOver', prototype);
      this.onMouseMove = bind$(this, 'onMouseMove', prototype);
      console.info("Creating time series '" + this.title + "'...");
      this.init();
    }
    TimeSeries.prototype.init = function(){
      var dx, this$ = this;
      console.info("Initializing time series '" + this.title + "'...", this.data, this.options);
      this.margin = {
        top: 30,
        right: 20,
        bottom: 30,
        left: 60
      };
      this.xScale = d3.scaleTime().domain(d3.extent(this.data, function(d){
        return d.x;
      }));
      this.yScale = d3.scaleLinear().domain(d3.extent(this.data, function(d){
        return d.y;
      }));
      this.xAxis = d3.axisBottom().ticks(7, ",f").tickFormat(d3.timeFormat("%Y-%m-%d"));
      this.yAxis = d3.axisLeft().ticks(10);
      this.line = d3.line().x(function(d){
        return this$.xScale(d.x);
      }).y(function(d){
        return this$.yScale(d.y);
      });
      this.svg = d3.select(this.container).append('svg');
      this.svg.attr("class", this.slug);
      this.plotWrapper = this.svg.append('g');
      this.plotWrapper.attr('transform', 'translate(' + this.margin.left + ',' + this.margin.top + ')');
      this.path = this.plotWrapper.append('path').datum(this.data).classed('line', true);
      this.mouseCanvas = this.plotWrapper.append("rect").style("fill", "none").style("pointer-events", "all");
      this.mouseCanvas.on("mouseover", this.onMouseOver).on("mouseout", this.onMouseOut).on("mousemove", this.onMouseMove);
      this.plotWrapper.append('g').classed('x axis', true);
      this.plotWrapper.append('g').classed('y axis', true);
      this.yAxisText = this.plotWrapper.append("text").attr("transform", "rotate(-90)").attr("dy", "1em").style("text-anchor", "middle").text(this.title);
      this.focus = this.plotWrapper.append('g').style("display", "none");
      this.cursorCircle = this.focus.append("circle").attr("class", "cursor-circle").attr("r", 3);
      dx = 8;
      this.cursorValueShadow = this.focus.append("text").attr("class", "cursor-text cursor-text-shadow").attr("dx", dx).attr("dy", "-.3em");
      this.cursorValue = this.focus.append("text").attr("class", "cursor-text cursor-value").attr("dx", dx).attr("dy", "-.3em");
      this.cursorDateShadow = this.focus.append("text").attr("class", "cursor-text cursor-text-shadow").attr("dx", dx).attr("dy", "1em");
      this.cursorDate = this.focus.append("text").attr("class", "cursor-text cursor-date").attr("dx", dx).attr("dy", "1em");
      return this.resize();
    };
    TimeSeries.prototype.resize = function(){
      var width, height;
      width = jQuery(this.container).width() - this.margin.left - this.margin.right;
      height = GOLDEN_RATIO * GOLDEN_RATIO * GOLDEN_RATIO * GOLDEN_RATIO * width;
      this.plotWidth = width;
      this.plotHeight = height;
      console.log("Resize time series " + this.title + " : " + width + " x " + height);
      this.xScale.range([0, width]);
      this.yScale.range([height, 0]);
      this.svg.attr('width', width + this.margin.right + this.margin.left).attr('height', height + this.margin.top + this.margin.bottom);
      this.path.attr('d', this.line);
      this.xAxis.scale(this.xScale);
      this.yAxis.scale(this.yScale);
      this.xAxis.ticks(Math.floor(width / 60));
      this.yAxis.ticks(Math.floor(height / 18));
      this.svg.select('.x.axis').attr('transform', 'translate(0,' + height + ')').call(this.xAxis);
      this.svg.select('.y.axis').call(this.yAxis);
      this.yAxisText.attr("y", 0 - this.margin.left).attr("x", 0 - height / 2);
      this.mouseCanvas.attr("width", width).attr("height", height);
      return this;
    };
    TimeSeries.prototype.onMouseMove = function(){
      var x;
      x = this.xScale.invert(d3.mouse(this.mouseCanvas.node())[0]);
      if (this.options.onMouseMove != null) {
        return this.options.onMouseMove(x);
      } else {
        return this.moveCursor(x);
      }
    };
    TimeSeries.prototype.onMouseOver = function(){
      if (this.options.onMouseOver != null) {
        return this.options.onMouseOver();
      } else {
        return this.showCursor();
      }
    };
    TimeSeries.prototype.onMouseOut = function(){
      if (this.options.onMouseOut != null) {
        return this.options.onMouseOut();
      } else {
        return this.hideCursor();
      }
    };
    TimeSeries.prototype.showCursor = function(){
      return this.focus.style("display", null);
    };
    TimeSeries.prototype.hideCursor = function(){
      return this.focus.style("display", "none");
    };
    TimeSeries.prototype.bisectDate = d3.bisector(function(d){
      return d.x;
    }).left;
    TimeSeries.prototype.timeFormat = d3.timeFormat("%Y-%m-%d %Hh");
    TimeSeries.prototype.moveCursor = function(x0){
      var i, d0, d1, d, xx, yy, transform, mirrored, dx;
      i = this.bisectDate(this.data, x0, 1);
      d0 = this.data[i - 1];
      d1 = this.data[i];
      if (!(d1 && d0)) {
        return;
      }
      d = x0 - d0.x > d1.x - x0 ? d1 : d0;
      xx = this.xScale(d.x);
      yy = this.yScale(d.y);
      transform = "translate(" + xx + ", " + yy + ")";
      mirrored = this.plotWidth != null && xx > this.plotWidth / 2 ? true : false;
      dx = 8;
      if (mirrored) {
        dx = -1 * dx;
      }
      this.cursorCircle.attr("transform", transform);
      this.cursorValue.attr("transform", transform).text(d.y).attr('text-anchor', mirrored ? 'end' : 'start').attr("dx", dx);
      this.cursorValueShadow.attr("transform", transform).text(d.y).attr('text-anchor', mirrored ? 'end' : 'start').attr("dx", dx);
      this.cursorDate.attr("transform", transform).text(this.timeFormat(d.x)).attr('text-anchor', mirrored ? 'end' : 'start').attr("dx", dx);
      this.cursorDateShadow.attr("transform", transform).text(this.timeFormat(d.x)).attr('text-anchor', mirrored ? 'end' : 'start').attr("dx", dx);
      return this;
    };
    return TimeSeries;
  }());
  out$.Orbits = Orbits = (function(){
    Orbits.displayName = 'Orbits';
    var prototype = Orbits.prototype, constructor = Orbits;
    function Orbits(orbiters, data, container, options){
      this.orbiters = orbiters;
      this.data = data;
      this.container = container;
      this.options = options != null
        ? options
        : {};
      console.log("Create orbits");
      this.init();
    }
    Orbits.prototype.init = function(){
      var slug, ref$, config;
      console.log("Initialize orbits", this.data, this.options);
      this.margin = {
        top: 30,
        right: 20,
        bottom: 42,
        left: 60
      };
      this.extremum = 1.11 * d3.max(this.data, function(d){
        return Math.max(Math.abs(d.x), Math.abs(d.y));
      });
      this.xScale = d3.scaleLinear().domain([-1 * this.extremum, this.extremum]);
      this.yScale = d3.scaleLinear().domain([-1 * this.extremum, this.extremum]);
      this.xAxis = d3.axisBottom().ticks(10);
      this.yAxis = d3.axisLeft().ticks(10);
      this.svg = d3.select(this.container).append('svg');
      this.plotWrapper = this.svg.append('g');
      this.plotWrapper.attr('transform', 'translate(' + this.margin.left + ',' + this.margin.top + ')');
      this.xAxisLine = this.plotWrapper.append('g').classed('x axis', true);
      this.yAxisLine = this.plotWrapper.append('g').classed('y axis', true);
      this.xAxisTitle = this.xAxisLine.append('text').attr('fill', '#000');
      this.xAxisTitle.style("text-anchor", "middle");
      this.xAxisTitle.append('tspan').text('X');
      this.xAxisTitle.append('tspan').attr('dy', '3px').text('HEE').attr('font-size', '8px');
      this.xAxisTitle.append('tspan').attr('dy', '-3px').text('   (AU)');
      this.yAxisTitle = this.yAxisLine.append('text').attr('fill', '#000');
      this.yAxisTitle.style("text-anchor", "middle");
      this.yAxisTitle.append('tspan').text('Y');
      this.yAxisTitle.append('tspan').attr('dy', '3px').text('HEE').attr('font-size', '8px');
      this.yAxisTitle.append('tspan').attr('dy', '-3px').text('   (AU)');
      this.yAxisTitle.attr('transform', 'rotate(-90)');
      this.sun = this.plotWrapper.append("svg:circle");
      this.sun.append('svg:title').text("Sol");
      this.sun.attr("r", 17).style("fill", "yellow");
      for (slug in ref$ = this.orbiters) {
        config = ref$[slug];
        this.initOrbiter(slug, config);
      }
      return this.resize();
    };
    Orbits.prototype.orbitersElements = {};
    Orbits.prototype.initOrbiter = function(slug, config){
      var orbit_ellipse, orbiter, orbit_line, orbit_section, this$ = this;
      if (slug in this.orbitersElements) {
        throw new Error("Second init of " + slug);
      }
      orbit_ellipse = this.plotWrapper.append("svg:ellipse").classed('orbit orbit_ellipse', true);
      orbiter = this.plotWrapper.append("svg:image").attr('xlink:href', config['img']).attr('width', '32px').attr('height', '32px');
      orbit_line = d3.line().x(function(d){
        return this$.xScale(d.x);
      }).y(function(d){
        return this$.yScale(d.y);
      });
      orbit_section = this.plotWrapper.append('path').datum(this.data).classed('orbit orbit_section', true);
      this.orbitersElements[slug] = {
        orbiter: orbiter,
        orbit_ellipse: orbit_ellipse,
        orbit_section: orbit_section,
        orbit_line: orbit_line
      };
      return this;
    };
    Orbits.prototype.resize = function(){
      var width, height, slug, ref$, config;
      width = jQuery(this.container).width() - this.margin.left - this.margin.right;
      height = 1.0 * width;
      console.log("Resize orbits : " + width + " x " + height);
      this.xScale.range([0, width]);
      this.yScale.range([height, 0]);
      this.svg.attr('width', width + this.margin.right + this.margin.left).attr('height', height + this.margin.top + this.margin.bottom);
      this.sun.attr("cx", width / 2).attr("cy", height / 2);
      for (slug in ref$ = this.orbiters) {
        config = ref$[slug];
        this.resizeOrbiter(slug, config);
      }
      this.xAxis.scale(this.xScale);
      this.yAxis.scale(this.yScale);
      this.svg.select('.x.axis').attr('transform', 'translate(0,' + height + ')').call(this.xAxis);
      this.svg.select('.y.axis').call(this.yAxis);
      this.xAxisTitle.attr("x", width / 2).attr("y", 37);
      this.yAxisTitle.attr("x", -1 * height / 2).attr("y", -30);
      return this;
    };
    Orbits.prototype.resizeOrbiter = function(slug, config){
      var width, height, el, a, b, c, cx, cy, data;
      width = jQuery(this.container).width() - this.margin.left - this.margin.right;
      height = 1.0 * width;
      console.log("Resize orbiter " + slug);
      el = this.orbitersElements[slug];
      el['orbit_section'].attr('d', el['orbit_line']);
      a = config['orbit']['a'];
      b = config['orbit']['b'];
      c = Math.sqrt(a * a - b * b);
      cx = width / 2 - c;
      cy = height / 2;
      this.yScale.range([0, height]);
      el['orbit_ellipse'].attr('cx', cx).attr('cy', cy).attr('rx', this.xScale(a) - this.xScale(0)).attr('ry', this.yScale(b) - this.yScale(0)).attr('transform', 'rotate(66,' + (cx + c) + ', ' + cy + ')');
      this.yScale.range([height, 0]);
      data = this.data;
      el['orbiter'].attr('x', this.xScale(data[data.length - 1].x) - 16);
      el['orbiter'].attr('y', this.yScale(data[data.length - 1].y) - 16);
      return this;
    };
    Orbits.prototype.repositionOrbiter = function(slug, datum){
      var data, el;
      data = this.data;
      datum == null && (datum = data[data.length - 1]);
      el = this.orbitersElements[slug];
      el['orbiter'].attr('x', this.xScale(datum.x) - 16);
      el['orbiter'].attr('y', this.yScale(datum.y) - 16);
      return this;
    };
    Orbits.prototype.bisectDate = d3.bisector(function(d){
      return d.t;
    }).left;
    Orbits.prototype.moveToDate = function(t){
      var slug, ref$, el, data, i, d0, d1, d, results$ = [];
      for (slug in ref$ = this.orbitersElements) {
        el = ref$[slug];
        data = this.data;
        i = this.bisectDate(data, t, 1);
        d0 = data[i - 1];
        d1 = data[i];
        if (!(d1 && d0)) {
          continue;
        }
        d = t - d0.t > d1.t - t ? d1 : d0;
        results$.push(this.repositionOrbiter(slug, d));
      }
      return results$;
    };
    return Orbits;
  }());
  function bind$(obj, key, target){
    return function(){ return (target || obj)[key].apply(obj, arguments) };
  }
}).call(this);