Commit fe3132dd4aa9260bee2dcae60e29e224a51a99a2
1 parent
4816cef4
Exists in
master
and in
3 other branches
Refactor even more.
Showing
4 changed files
with
91 additions
and
33 deletions
Show diff stats
CHANGELOG.md
1 | 1 | ## TO DO |
2 | 2 | |
3 | 3 | - Add source name on time series |
4 | +- Support multiple sources | |
4 | 5 | - Start/Stop datetime fields |
5 | 6 | - Play button to start time dimension |
6 | -- Download raw data (as CSV) for time interval | |
7 | +- Download raw data (as CSV) for current time interval | |
7 | 8 | - Same via SAMP |
8 | 9 | |
9 | 10 | ## 0.0.0 | ... | ... |
web/static/js/swapp.js
... | ... | @@ -6,9 +6,18 @@ |
6 | 6 | SpaceWeather.displayName = 'SpaceWeather'; |
7 | 7 | var prototype = SpaceWeather.prototype, constructor = SpaceWeather; |
8 | 8 | function SpaceWeather(configuration){ |
9 | + var configs, res$, k, this$ = this; | |
9 | 10 | this.configuration = configuration; |
10 | - console.info("Creating Space Weather app...", configuration); | |
11 | + console.info("Creating Space Weather app...", this.configuration); | |
11 | 12 | this.sources = {}; |
13 | + res$ = []; | |
14 | + for (k in this.configuration.sources) { | |
15 | + res$.push(this.configuration.sources[k]); | |
16 | + } | |
17 | + configs = res$; | |
18 | + configs.forEach(function(source_config){ | |
19 | + return this$.sources[source_config.slug] = new Source(source_config.slug, source_config.name, source_config); | |
20 | + }); | |
12 | 21 | } |
13 | 22 | SpaceWeather.prototype.buildDataUrlForSource = function(source_slug){ |
14 | 23 | var url; |
... | ... | @@ -36,6 +45,30 @@ |
36 | 45 | source.hide(); |
37 | 46 | return this; |
38 | 47 | }; |
48 | + SpaceWeather.prototype.resize = function(){ | |
49 | + timeSeries.forEach(function(ts){ | |
50 | + return ts.resize(); | |
51 | + }); | |
52 | + return typeof orbits != 'undefined' && orbits !== null ? orbits.resize() : void 8; | |
53 | + }; | |
54 | + SpaceWeather.prototype.init = function(){ | |
55 | + var active_sources, res$, k, this$ = this; | |
56 | + res$ = []; | |
57 | + for (k in this.sources) { | |
58 | + if (this.sources[k].config.active) { | |
59 | + res$.push(this.sources[k]); | |
60 | + } | |
61 | + } | |
62 | + active_sources = res$; | |
63 | + return active_sources.forEach(function(source){ | |
64 | + return this$.loadData(source.slug, '2016-01-01T00:00:00', '2023-01-01T00:00:00').then(function(data){ | |
65 | + this$.createTimeSeries(source, data); | |
66 | + return this$.orbits = new Orbits(configuration.sources, data['hci'], configuration.orbits_container); | |
67 | + }, function(data){ | |
68 | + return console.error('Failed to load SW data.', data); | |
69 | + }); | |
70 | + }); | |
71 | + }; | |
39 | 72 | SpaceWeather.prototype.loadData = function(source_slug, started_at, stopped_at){ |
40 | 73 | var sw, promise; |
41 | 74 | sw = this; |
... | ... | @@ -75,8 +108,8 @@ |
75 | 108 | }); |
76 | 109 | return promise; |
77 | 110 | }; |
78 | - SpaceWeather.prototype.createTimeSeries = function(data){ | |
79 | - var timeSeries; | |
111 | + SpaceWeather.prototype.createTimeSeries = function(source, data){ | |
112 | + var timeSeries, this$ = this; | |
80 | 113 | timeSeries = []; |
81 | 114 | this.configuration['parameters'].forEach(function(parameter){ |
82 | 115 | var container, id, title; |
... | ... | @@ -86,7 +119,7 @@ |
86 | 119 | if (!(id in data)) { |
87 | 120 | console.error("No data for id '" + id + "'.", data); |
88 | 121 | } |
89 | - return timeSeries.push(new TimeSeries(id, title, data[id], container)); | |
122 | + return timeSeries.push(new TimeSeries(id, title, source, data[id], container)); | |
90 | 123 | }); |
91 | 124 | return timeSeries.forEach(function(ts){ |
92 | 125 | ts.options['onMouseOver'] = function(){ |
... | ... | @@ -100,10 +133,11 @@ |
100 | 133 | }); |
101 | 134 | }; |
102 | 135 | return ts.options['onMouseMove'] = function(t){ |
136 | + var ref$; | |
103 | 137 | timeSeries.forEach(function(ts2){ |
104 | 138 | return ts2.moveCursor(t); |
105 | 139 | }); |
106 | - return typeof orbits != 'undefined' && orbits !== null ? orbits.moveToDate(t) : void 8; | |
140 | + return (ref$ = this$.orbits) != null ? ref$.moveToDate(t) : void 8; | |
107 | 141 | }; |
108 | 142 | }); |
109 | 143 | }; |
... | ... | @@ -112,8 +146,9 @@ |
112 | 146 | Source = (function(){ |
113 | 147 | Source.displayName = 'Source'; |
114 | 148 | var prototype = Source.prototype, constructor = Source; |
115 | - function Source(slug, config){ | |
149 | + function Source(slug, name, config){ | |
116 | 150 | this.slug = slug; |
151 | + this.name = name; | |
117 | 152 | this.config = config; |
118 | 153 | this.time_series = {}; |
119 | 154 | } |
... | ... | @@ -141,9 +176,10 @@ |
141 | 176 | out$.TimeSeries = TimeSeries = (function(){ |
142 | 177 | TimeSeries.displayName = 'TimeSeries'; |
143 | 178 | var prototype = TimeSeries.prototype, constructor = TimeSeries; |
144 | - function TimeSeries(slug, title, data, container, options){ | |
179 | + function TimeSeries(slug, title, source, data, container, options){ | |
145 | 180 | this.slug = slug; |
146 | 181 | this.title = title; |
182 | + this.source = source; | |
147 | 183 | this.data = data; |
148 | 184 | this.container = container; |
149 | 185 | this.options = options != null |
... | ... | @@ -162,7 +198,7 @@ |
162 | 198 | top: 30, |
163 | 199 | right: 20, |
164 | 200 | bottom: 30, |
165 | - left: 60 | |
201 | + left: 80 | |
166 | 202 | }; |
167 | 203 | this.xScale = d3.scaleTime().domain(d3.extent(this.data, function(d){ |
168 | 204 | return d.x; |
... | ... | @@ -187,6 +223,7 @@ |
187 | 223 | this.plotWrapper.append('g').classed('x axis', true); |
188 | 224 | this.plotWrapper.append('g').classed('y axis', true); |
189 | 225 | this.yAxisText = this.plotWrapper.append("text").attr("transform", "rotate(-90)").attr("dy", "1em").style("text-anchor", "middle").text(this.title); |
226 | + this.yAxisTextSource = this.plotWrapper.append("text").attr("transform", "rotate(-90)").attr("dy", "1em").style("text-anchor", "middle").style("font-style", "oblique").text(this.source.name); | |
190 | 227 | this.focus = this.plotWrapper.append('g').style("display", "none"); |
191 | 228 | this.cursorCircle = this.focus.append("circle").attr("class", "cursor-circle").attr("r", 3); |
192 | 229 | dx = 8; |
... | ... | @@ -213,7 +250,8 @@ |
213 | 250 | this.yAxis.ticks(Math.floor(height / 18)); |
214 | 251 | this.svg.select('.x.axis').attr('transform', 'translate(0,' + height + ')').call(this.xAxis); |
215 | 252 | this.svg.select('.y.axis').call(this.yAxis); |
216 | - this.yAxisText.attr("y", 0 - this.margin.left).attr("x", 0 - height / 2); | |
253 | + this.yAxisText.attr("y", 20 - this.margin.left).attr("x", 0 - height / 2); | |
254 | + this.yAxisTextSource.attr("y", 0 - this.margin.left).attr("x", 0 - height / 2); | |
217 | 255 | this.mouseCanvas.attr("width", width).attr("height", height); |
218 | 256 | return this; |
219 | 257 | }; | ... | ... |
web/static/js/swapp.ls
... | ... | @@ -8,8 +8,12 @@ const GOLDEN_RATIO = 2 / (1 + Math.sqrt(5)) |
8 | 8 | export class SpaceWeather |
9 | 9 | |
10 | 10 | (@configuration) -> |
11 | - console.info "Creating Space Weather app...", configuration | |
11 | + console.info "Creating Space Weather app...", @configuration | |
12 | 12 | @sources = {} |
13 | + configs = [@configuration.sources[k] for k of @configuration.sources] | |
14 | + configs.forEach((source_config) ~> | |
15 | + @sources[source_config.slug] = new Source(source_config.slug, source_config.name, source_config) | |
16 | + ) | |
13 | 17 | |
14 | 18 | buildDataUrlForSource: (source_slug) -> |
15 | 19 | url = @configuration['api']['data_for_interval'] |
... | ... | @@ -33,6 +37,23 @@ export class SpaceWeather |
33 | 37 | source.hide() |
34 | 38 | this |
35 | 39 | |
40 | + resize: -> | |
41 | + timeSeries.forEach((ts) -> ts.resize()) | |
42 | + orbits?.resize(); | |
43 | + | |
44 | + init: -> | |
45 | + active_sources = [ @sources[k] for k of @sources when @sources[k].config.active ] | |
46 | + active_sources.forEach((source) ~> | |
47 | + @loadData(source.slug, '2016-01-01T00:00:00', '2023-01-01T00:00:00').then( | |
48 | + (data) ~> | |
49 | + @createTimeSeries(source, data) | |
50 | + # fixme: don't create a new Orbits instance every time | |
51 | + @orbits = new Orbits(configuration.sources, data['hci'], configuration.orbits_container) | |
52 | + , | |
53 | + (data) ~> console.error('Failed to load SW data.', data) | |
54 | + ) | |
55 | + ) | |
56 | + | |
36 | 57 | loadData: (source_slug, started_at, stopped_at) -> |
37 | 58 | sw = this |
38 | 59 | promise = new Promise((resolve, reject) -> |
... | ... | @@ -57,29 +78,29 @@ export class SpaceWeather |
57 | 78 | ) |
58 | 79 | promise |
59 | 80 | |
60 | - createTimeSeries: (data) -> | |
81 | + createTimeSeries: (source, data) -> | |
61 | 82 | timeSeries = [] |
62 | 83 | @configuration['parameters'].forEach((parameter) -> |
63 | 84 | container = @configuration['time_series_container'] |
64 | 85 | id = parameter['id'] ; title = parameter['title'] |
65 | 86 | if id not of data then console.error("No data for id '#{id}'.", data) |
66 | - timeSeries.push(new TimeSeries(id, title, data[id], container)) | |
87 | + timeSeries.push(new TimeSeries(id, title, source, data[id], container)) | |
67 | 88 | ) |
68 | - timeSeries.forEach((ts) -> | |
89 | + timeSeries.forEach((ts) ~> | |
69 | 90 | ts.options['onMouseOver'] = -> |
70 | 91 | timeSeries.forEach((ts2) -> ts2.showCursor()) |
71 | 92 | ts.options['onMouseOut'] = -> |
72 | 93 | timeSeries.forEach((ts2) -> ts2.hideCursor()) |
73 | - ts.options['onMouseMove'] = (t) -> | |
94 | + ts.options['onMouseMove'] = (t) ~> | |
74 | 95 | timeSeries.forEach((ts2) -> ts2.moveCursor(t)) |
75 | - orbits?.moveToDate(t) | |
96 | + @orbits?.moveToDate(t) | |
76 | 97 | ) |
77 | 98 | |
78 | 99 | |
79 | 100 | |
80 | 101 | |
81 | 102 | class Source |
82 | - (@slug, @config) -> | |
103 | + (@slug, @name, @config) -> | |
83 | 104 | @time_series = {} |
84 | 105 | |
85 | 106 | addTimeSeries: (ts) -> |
... | ... | @@ -102,7 +123,7 @@ export class TimeSeries |
102 | 123 | # Time in x-axis |
103 | 124 | # Data in y-axis |
104 | 125 | |
105 | - (@slug, @title, @data, @container, @options = {}) -> | |
126 | + (@slug, @title, @source, @data, @container, @options = {}) -> | |
106 | 127 | # title : string |
107 | 128 | # data : list of {x: <datetime>, y: <float>} |
108 | 129 | console.info "Creating time series '#{@title}'..." |
... | ... | @@ -115,7 +136,7 @@ export class TimeSeries |
115 | 136 | top: 30, |
116 | 137 | right: 20, |
117 | 138 | bottom: 30, |
118 | - left: 60 | |
139 | + left: 80 | |
119 | 140 | } |
120 | 141 | |
121 | 142 | @xScale = d3.scaleTime().domain(d3.extent(@data, (d) -> d.x)) |
... | ... | @@ -159,6 +180,12 @@ export class TimeSeries |
159 | 180 | .attr("dy", "1em") |
160 | 181 | .style("text-anchor", "middle") |
161 | 182 | .text(@title) |
183 | + @yAxisTextSource = @plotWrapper.append("text") | |
184 | + .attr("transform", "rotate(-90)") | |
185 | + .attr("dy", "1em") | |
186 | + .style("text-anchor", "middle") | |
187 | + .style("font-style", "oblique") | |
188 | + .text(@source.name) | |
162 | 189 | |
163 | 190 | @focus = @plotWrapper.append('g').style("display", "none") |
164 | 191 | |
... | ... | @@ -221,9 +248,12 @@ export class TimeSeries |
221 | 248 | @svg.select('.y.axis') |
222 | 249 | .call(@yAxis) |
223 | 250 | |
224 | - @yAxisText.attr("y", 0 - @margin.left) | |
251 | + @yAxisText.attr("y", 20 - @margin.left) | |
225 | 252 | .attr("x", 0 - (height / 2)) |
226 | 253 | |
254 | + @yAxisTextSource.attr("y", 0 - @margin.left) | |
255 | + .attr("x", 0 - (height / 2)) | |
256 | + | |
227 | 257 | @mouseCanvas.attr("width", width) |
228 | 258 | .attr("height", height) |
229 | 259 | ... | ... |
web/view/home.html.jinja2
... | ... | @@ -208,18 +208,8 @@ var configuration = { |
208 | 208 | }; |
209 | 209 | |
210 | 210 | jQuery().ready(function($){ |
211 | - var timeSeries = []; | |
212 | - var orbits; | |
213 | - | |
214 | 211 | var sw = new SpaceWeather(configuration); |
215 | - sw.loadData('jupiter', '2016-01-01T00:00:00', '2023-01-01T00:00:00').then( | |
216 | - function (data) { | |
217 | - console.log('OK', data); | |
218 | - sw.createTimeSeries(data); | |
219 | - orbits = new Orbits(configuration.sources, data['hci'], configuration.orbits_container); | |
220 | - }, | |
221 | - function (data) { console.error('Failed to load SW data.', data); } | |
222 | - ); | |
212 | + sw.init(); | |
223 | 213 | |
224 | 214 | var toggleTimeSeries = function(slug) { |
225 | 215 | $('#time_series .'+slug).toggleClass('hidden'); |
... | ... | @@ -232,8 +222,7 @@ jQuery().ready(function($){ |
232 | 222 | }); |
233 | 223 | |
234 | 224 | window.addEventListener('resize', function() { |
235 | - timeSeries.forEach(function(ts){ ts.resize(); }); | |
236 | - if (orbits) orbits.resize(); | |
225 | + sw.resize(); | |
237 | 226 | }); |
238 | 227 | |
239 | 228 | }); | ... | ... |