Commit 17c52617f831a8b117dba01261bb7b1b4d8fd351

Authored by Goutte
1 parent b500e561

Make the orbits plot dynamic. (finally!)

VERSION
1   -1.0.0-beta
2 1 \ No newline at end of file
  2 +1.0.0-rc3
3 3 \ No newline at end of file
... ...
config.yml
... ... @@ -102,7 +102,7 @@ targets:
102 102 - type: 'planet'
103 103 slug: 'earth'
104 104 name: 'Earth'
105   - title: 'Earth'
  105 + title: 'Earth (Coming NEXT!)'
106 106 orbit:
107 107 models:
108 108 - slug: 'earth_orb_all'
... ...
web/static/js/swapp.js
... ... @@ -132,22 +132,29 @@
132 132 return results$;
133 133 };
134 134 SpaceWeather.prototype.enableTarget = function(target_slug){
135   - var this$ = this;
  135 + var ref$, this$ = this;
136 136 this.time_series.forEach(function(ts){
137 137 if (ts.target.slug === target_slug && this$.parameters[ts.parameter].active) {
138 138 return ts.show();
139 139 }
140 140 });
141 141 this.targets[target_slug].active = true;
  142 + if ((ref$ = this.orbits) != null) {
  143 + ref$.showOrbiter(target_slug);
  144 + }
142 145 return this;
143 146 };
144 147 SpaceWeather.prototype.disableTarget = function(target_slug){
  148 + var ref$;
145 149 this.time_series.forEach(function(ts){
146 150 if (ts.target.slug === target_slug) {
147 151 return ts.hide();
148 152 }
149 153 });
150 154 this.targets[target_slug].active = false;
  155 + if ((ref$ = this.orbits) != null) {
  156 + ref$.hideOrbiter(target_slug);
  157 + }
151 158 return this;
152 159 };
153 160 SpaceWeather.prototype.resize = function(){
... ... @@ -706,9 +713,10 @@
706 713 this.data = {};
707 714 this.orbiters = {};
708 715 this.orbitersElements = {};
709   - this.extremum = 1;
710   - this.xScale = d3.scaleLinear().domain([-1 * this.extremum, this.extremum]);
711   - this.yScale = d3.scaleLinear().domain([this.extremum, -1 * this.extremum]);
  716 + this.orbitersExtrema = {};
  717 + this.lastOrbiterData = {};
  718 + this.xScale = d3.scaleLinear().domain([-1, 1]);
  719 + this.yScale = d3.scaleLinear().domain([1, -1]);
712 720 this.xAxis = d3.axisBottom().ticks(10);
713 721 this.yAxis = d3.axisLeft().ticks(10);
714 722 this.svg = d3.select(this.container).append('svg');
... ... @@ -738,11 +746,6 @@
738 746 if (slug in this.orbitersElements) {
739 747 throw new Error("Second init of " + slug);
740 748 }
741   - this.extremum = Math.max(this.extremum, 1.11 * d3.max(data, function(d){
742   - return Math.max(Math.abs(d.x), Math.abs(d.y));
743   - }));
744   - this.xScale = d3.scaleLinear().domain([-1 * this.extremum, this.extremum]);
745   - this.yScale = d3.scaleLinear().domain([-1 * this.extremum, this.extremum]);
746 749 orbit_ellipse = this.plotWrapper.append("svg:ellipse").classed('orbit orbit_ellipse', true);
747 750 orbiter = this.plotWrapper.append("svg:image").attr('xlink:href', config['img']).attr('width', '32px').attr('height', '32px');
748 751 orbiter.append('svg:title').text(config.name);
... ... @@ -752,65 +755,122 @@
752 755 return this$.yScale(d.x);
753 756 });
754 757 orbit_section = this.plotWrapper.append('path').datum(data).classed('orbit orbit_section', true);
755   - this.orbiters[slug] = config;
756 758 this.data[slug] = data;
  759 + this.orbiters[slug] = config;
757 760 this.orbitersElements[slug] = {
758 761 orbiter: orbiter,
759 762 orbit_ellipse: orbit_ellipse,
760 763 orbit_section: orbit_section,
761 764 orbit_line: orbit_line
762 765 };
763   - this.resize();
  766 + this.orbitersExtrema[slug] = d3.max(data, function(d){
  767 + return Math.max(Math.abs(d.x), Math.abs(d.y));
  768 + });
764 769 $(this.svg.node()).show();
  770 + this.resize(true);
765 771 return this;
766 772 };
  773 + Orbits.prototype.showOrbiter = function(slug){
  774 + this.orbiters[slug].hidden = false;
  775 + this.orbitersElements[slug].orbiter.style("display", null);
  776 + this.orbitersElements[slug].orbit_ellipse.style("display", null);
  777 + this.orbitersElements[slug].orbit_section.style("display", null);
  778 + return this.resize(true);
  779 + };
  780 + Orbits.prototype.hideOrbiter = function(slug){
  781 + this.orbiters[slug].hidden = true;
  782 + this.orbitersElements[slug].orbiter.style("display", "none");
  783 + this.orbitersElements[slug].orbit_ellipse.style("display", "none");
  784 + this.orbitersElements[slug].orbit_section.style("display", "none");
  785 + return this.resize(true);
  786 + };
767 787 Orbits.prototype.clear = function(){
768 788 return $(this.svg.node()).remove();
769 789 };
770   - Orbits.prototype.resize = function(){
771   - var width, height, slug, ref$, config;
  790 + Orbits.prototype.resize = function(animate){
  791 + var width, height, extremum, s, o, slug, ref$, config, t, t1, this$ = this;
  792 + animate == null && (animate = false);
772 793 width = Math.ceil($(this.container).width() - this.margin.left - this.margin.right);
773 794 height = Math.ceil(1.0 * width);
774 795 console.debug("Resizing orbits : " + width + " × " + height + "…");
  796 + extremum = 1.1 * d3.max((function(){
  797 + var ref$, results$ = [];
  798 + for (s in ref$ = this.orbiters) {
  799 + o = ref$[s];
  800 + if (!o.hidden) {
  801 + results$.push(s);
  802 + }
  803 + }
  804 + return results$;
  805 + }.call(this)), function(d){
  806 + return this$.orbitersExtrema[d];
  807 + });
  808 + this.xScale = d3.scaleLinear().domain([-1 * extremum, extremum]);
  809 + this.yScale = d3.scaleLinear().domain([extremum, -1 * extremum]);
775 810 this.xScale.range([0, width]);
776   - this.yScale.range([0, height]);
  811 + this.yScale.range([height, 0]);
777 812 this.svg.attr('width', width + this.margin.right + this.margin.left).attr('height', height + this.margin.top + this.margin.bottom);
778 813 this.sun.attr("x", width / 2 - 16).attr("y", height / 2 - 16);
779 814 for (slug in ref$ = this.orbiters) {
780 815 config = ref$[slug];
781   - this.resizeOrbiter(slug, config, width, height);
  816 + this.resizeOrbiter(slug, config, width, height, animate);
782 817 }
783 818 this.xAxis.scale(this.xScale);
784 819 this.yAxis.scale(this.yScale);
785   - this.svg.select('.x.axis').attr('transform', 'translate(0,' + height + ')').call(this.xAxis);
786   - this.svg.select('.y.axis').call(this.yAxis);
  820 + this.svg.select('.x.axis').attr('transform', 'translate(0,' + height + ')');
  821 + if (animate) {
  822 + t = this.svg.transition().duration(750);
  823 + t1 = this.svg.transition().duration(4750);
  824 + this.svg.select('.x.axis').transition(t).call(this.xAxis);
  825 + this.svg.select('.y.axis').transition(t).call(this.yAxis);
  826 + } else {
  827 + this.svg.select('.x.axis').call(this.xAxis);
  828 + this.svg.select('.y.axis').call(this.yAxis);
  829 + }
787 830 this.xAxisTitle.attr("x", width / 2).attr("y", 37);
788 831 this.yAxisTitle.attr("x", -1 * height / 2).attr("y", -30);
789 832 return this;
790 833 };
791   - Orbits.prototype.resizeOrbiter = function(slug, config, width, height){
792   - var el, a, b, c, cx, cy, data;
  834 + Orbits.prototype.resizeOrbiter = function(slug, config, width, height, animate){
  835 + var tt, el, orbit_section, t, a, b, c, cx, cy, orbit_ellipse;
  836 + animate == null && (animate = false);
793 837 console.debug("Resizing orbit of " + slug + "…");
  838 + tt = this.svg.transition().duration(750);
794 839 el = this.orbitersElements[slug];
795   - el['orbit_section'].attr('d', el['orbit_line']);
  840 + orbit_section = el['orbit_section'];
  841 + if (animate) {
  842 + t = this.svg.transition().duration(750);
  843 + orbit_section = orbit_section.transition(tt);
  844 + }
  845 + orbit_section.attr('d', el['orbit_line']);
796 846 a = config['orbit']['a'];
797 847 b = config['orbit']['b'];
798 848 c = Math.sqrt(a * a - b * b);
799 849 cx = width / 2 - c;
800 850 cy = height / 2;
801   - 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));
802   - data = this.data[slug];
803   - el['orbiter'].attr('x', this.xScale(data[data.length - 1].y) - 16);
804   - el['orbiter'].attr('y', this.yScale(data[data.length - 1].x) - 16);
  851 + orbit_ellipse = el['orbit_ellipse'];
  852 + if (animate) {
  853 + t = this.svg.transition().duration(750);
  854 + orbit_ellipse = orbit_ellipse.transition(t);
  855 + }
  856 + orbit_ellipse.attr('cx', cx).attr('cy', cy).attr('rx', this.xScale(a) - this.xScale(0)).attr('ry', this.yScale(b) - this.yScale(0));
  857 + this.repositionOrbiter(slug, null, true);
805 858 return this;
806 859 };
807   - Orbits.prototype.repositionOrbiter = function(slug, datum){
808   - var data, el;
  860 + Orbits.prototype.repositionOrbiter = function(slug, datum, animate){
  861 + var data, el, t;
  862 + animate == null && (animate = false);
809 863 data = this.data[slug];
  864 + datum == null && (datum = this.lastOrbiterData[slug]);
810 865 datum == null && (datum = data[data.length - 1]);
811   - el = this.orbitersElements[slug];
812   - el['orbiter'].attr('x', this.xScale(datum.y) - 16);
813   - el['orbiter'].attr('y', this.yScale(datum.x) - 16);
  866 + this.lastOrbiterData[slug] = datum;
  867 + el = this.orbitersElements[slug]['orbiter'];
  868 + if (animate) {
  869 + t = this.svg.transition().duration(750);
  870 + el = el.transition(t);
  871 + }
  872 + el.attr('x', this.xScale(datum.y) - 16);
  873 + el.attr('y', this.yScale(datum.x) - 16);
814 874 return this;
815 875 };
816 876 Orbits.prototype.bisectDate = d3.bisector(function(d){
... ...
web/static/js/swapp.ls
... ... @@ -128,11 +128,13 @@ https://gitlab.irap.omp.eu/CDPP/SPACEWEATHERONLINE
128 128 enableTarget: (target_slug) ->
129 129 @time_series.forEach((ts) ~> ts.show() if ts.target.slug == target_slug && @parameters[ts.parameter].active)
130 130 @targets[target_slug].active = true
  131 + @orbits?.showOrbiter target_slug
131 132 this
132 133  
133 134 disableTarget: (target_slug) ->
134 135 @time_series.forEach((ts) -> ts.hide() if ts.target.slug == target_slug)
135 136 @targets[target_slug].active = false
  137 + @orbits?.hideOrbiter target_slug
136 138 this
137 139  
138 140 resize: ->
... ... @@ -656,10 +658,11 @@ export class Orbits
656 658  
657 659 @data = {} # slug => HEE array
658 660 @orbiters = {} # slug => config
659   - @orbitersElements = {}
660   - @extremum = 1
661   - @xScale = d3.scaleLinear().domain([-1 * @extremum, @extremum])
662   - @yScale = d3.scaleLinear().domain([@extremum, -1 * @extremum])
  661 + @orbitersElements = {} # see initOrbiter
  662 + @orbitersExtrema = {} # slug => local extrema
  663 + @lastOrbiterData = {} # slug => most recently used datum for position
  664 + @xScale = d3.scaleLinear().domain([-1, 1])
  665 + @yScale = d3.scaleLinear().domain([1, -1])
663 666  
664 667 @xAxis = d3.axisBottom().ticks(10)
665 668 @yAxis = d3.axisLeft().ticks(10)
... ... @@ -700,12 +703,6 @@ export class Orbits
700 703 console.info "Initializing orbit of #{config.name}…"
701 704 if slug of @orbitersElements then throw new Error("Second init of #{slug}")
702 705  
703   - @extremum = Math.max(@extremum, 1.11 * d3.max(data, (d) ->
704   - Math.max(Math.abs(d.x), Math.abs(d.y))
705   - ))
706   - @xScale = d3.scaleLinear().domain([-1 * @extremum, @extremum])
707   - @yScale = d3.scaleLinear().domain([-1 * @extremum, @extremum])
708   -
709 706 # The order is important, as it will define the default z-order
710 707 orbit_ellipse = @plotWrapper.append("svg:ellipse")
711 708 .classed('orbit orbit_ellipse', true)
... ... @@ -722,31 +719,54 @@ export class Orbits
722 719 .datum(data)
723 720 .classed('orbit orbit_section', true)
724 721  
725   - @orbiters[slug] = config
726 722 @data[slug] = data
  723 + @orbiters[slug] = config
727 724 @orbitersElements[slug] =
728 725 orbiter: orbiter
729 726 orbit_ellipse: orbit_ellipse
730 727 orbit_section: orbit_section
731 728 orbit_line: orbit_line
732   -
733   - @resize()
  729 + @orbitersExtrema[slug] = d3.max(data, (d) ->
  730 + Math.max(Math.abs(d.x), Math.abs(d.y))
  731 + )
734 732  
735 733 $(@svg.node()).show();
736 734  
  735 + @resize(true)
  736 +
737 737 this
738 738  
  739 + showOrbiter: (slug) ->
  740 + @orbiters[slug].hidden = false
  741 + @orbitersElements[slug].orbiter.style("display", null)
  742 + @orbitersElements[slug].orbit_ellipse.style("display", null)
  743 + @orbitersElements[slug].orbit_section.style("display", null)
  744 + @resize(true)
  745 +
  746 + hideOrbiter: (slug) ->
  747 + @orbiters[slug].hidden = true
  748 + @orbitersElements[slug].orbiter.style("display", "none")
  749 + @orbitersElements[slug].orbit_ellipse.style("display", "none")
  750 + @orbitersElements[slug].orbit_section.style("display", "none")
  751 + @resize(true)
  752 +
739 753 clear: ->
740 754 $(@svg.node()).remove()
741 755  
742   - resize: ->
  756 + resize: (animate = false) ->
743 757 width = Math.ceil($(@container).width() - @margin.left - @margin.right)
744 758 height = Math.ceil(1.0 * width)
745 759  
746 760 console.debug("Resizing orbits : #{width} × #{height}…")
747 761  
  762 + extremum = 1.1 * d3.max([s for s, o of @orbiters when not o.hidden], (d) ~>
  763 + @orbitersExtrema[d]
  764 + )
  765 + @xScale = d3.scaleLinear().domain([-1 * extremum, extremum])
  766 + @yScale = d3.scaleLinear().domain([extremum, -1 * extremum])
  767 +
748 768 @xScale.range([0, width])
749   - @yScale.range([0, height])
  769 + @yScale.range([height, 0])
750 770  
751 771 @svg.attr('width', width + @margin.right + @margin.left)
752 772 .attr('height', height + @margin.top + @margin.bottom)
... ... @@ -754,17 +774,20 @@ export class Orbits
754 774 @sun.attr("x", width / 2 - 16).attr("y", height / 2 - 16)
755 775  
756 776 for slug, config of @orbiters
757   - @resizeOrbiter(slug, config, width, height)
  777 + @resizeOrbiter(slug, config, width, height, animate)
758 778  
759 779 @xAxis.scale(@xScale)
760 780 @yAxis.scale(@yScale)
761 781  
762   - @svg.select('.x.axis')
763   - .attr('transform', 'translate(0,' + height + ')')
764   - .call(@xAxis)
765   -
766   - @svg.select('.y.axis')
767   - .call(@yAxis)
  782 + @svg.select('.x.axis').attr('transform', 'translate(0,' + height + ')')
  783 + if animate
  784 + t = @svg.transition().duration(750)
  785 + t1 = @svg.transition().duration(4750)
  786 + @svg.select('.x.axis').transition(t).call(@xAxis);
  787 + @svg.select('.y.axis').transition(t).call(@yAxis);
  788 + else
  789 + @svg.select('.x.axis').call(@xAxis)
  790 + @svg.select('.y.axis').call(@yAxis)
768 791  
769 792 @xAxisTitle.attr("x", width / 2)
770 793 .attr("y", 37)
... ... @@ -773,35 +796,49 @@ export class Orbits
773 796  
774 797 this
775 798  
776   - resizeOrbiter: (slug, config, width, height) ->
  799 + resizeOrbiter: (slug, config, width, height, animate = false) ->
777 800 console.debug("Resizing orbit of #{slug}…")
778 801  
  802 + tt = @svg.transition().duration(750)
779 803 el = @orbitersElements[slug]
780   - el['orbit_section'].attr('d', el['orbit_line'])
  804 + orbit_section = el['orbit_section']
  805 + if animate
  806 + t = @svg.transition().duration(750)
  807 + orbit_section = orbit_section.transition(tt)
  808 + orbit_section.attr('d', el['orbit_line'])
781 809  
782 810 a = config['orbit']['a']
783 811 b = config['orbit']['b']
784 812 c = Math.sqrt(a*a - b*b)
785 813 cx = (width / 2) - c
786 814 cy = (height / 2)
787   - el['orbit_ellipse'].attr('cx', cx).attr('cy', cy)
  815 +
  816 + orbit_ellipse = el['orbit_ellipse']
  817 + if animate
  818 + t = @svg.transition().duration(750)
  819 + orbit_ellipse = orbit_ellipse.transition(t)
  820 + # These ellipses ain't worth much
  821 + # Maybe a simple circle whose radius is the mean radius of the orbit ?
  822 + orbit_ellipse.attr('cx', cx).attr('cy', cy)
788 823 .attr('rx', @xScale(a) - @xScale(0))
789 824 .attr('ry', @yScale(b) - @yScale(0))
790 825 # .attr('transform', 'rotate(66,'+(cx+c)+', '+cy+')')
791 826  
792   - data = @data[slug]
793   -
794   - el['orbiter'].attr('x', @xScale(data[data.length - 1].y) - 16)
795   - el['orbiter'].attr('y', @yScale(data[data.length - 1].x) - 16)
  827 + @repositionOrbiter(slug, null, true)
796 828  
797 829 this
798 830  
799   - repositionOrbiter: (slug, datum) ->
  831 + repositionOrbiter: (slug, datum, animate = false) ->
800 832 data = @data[slug]
  833 + datum ?= @lastOrbiterData[slug]
801 834 datum ?= data[data.length - 1]
802   - el = @orbitersElements[slug]
803   - el['orbiter'].attr('x', @xScale(datum.y) - 16)
804   - el['orbiter'].attr('y', @yScale(datum.x) - 16)
  835 + @lastOrbiterData[slug] = datum
  836 + el = @orbitersElements[slug]['orbiter']
  837 + if animate
  838 + t = @svg.transition().duration(750)
  839 + el = el.transition(t)
  840 + el.attr('x', @xScale(datum.y) - 16)
  841 + el.attr('y', @yScale(datum.x) - 16)
805 842 this
806 843  
807 844 bisectDate: d3.bisector((d) -> d.t).left
... ...