Commit 2038c9fb475f8b7ad7ead44afa1c853f4a4ac0e9
1 parent
f7a10f28
Exists in
master
and in
3 other branches
Add a zoom reset on double click, and a nice console log masthead.
Showing
2 changed files
with
91 additions
and
42 deletions
Show diff stats
web/static/js/swapp.js
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | function SpaceWeather(configuration){ | 20 | function SpaceWeather(configuration){ |
21 | var configs, res$, k, this$ = this; | 21 | var configs, res$, k, this$ = this; |
22 | this.configuration = configuration; | 22 | this.configuration = configuration; |
23 | - console.info("Creating HelioPropa app...", this.configuration); | 23 | + console.info(" _ _ _ _ ____\n | | | | ___| (_) ___ | _ \\ _ __ ___ _ __ __ _\n | |_| |/ _ \\ | |/ _ \\| |_) | '__/ _ \\| '_ \\ / _` |\n | _ | __/ | | (_) | __/| | | (_) | |_) | (_| |\n |_| |_|\\___|_|_|\\___/|_|_ |_|_ \\___/| .__/ \\__,_|\n | |__ _ _ / ___| _ \\| _ \\| _ \\_|\n | '_ \\| | | | | | | | | | |_) | |_) |\n | |_) | |_| | | |___| |_| | __/| __/\n |_.__/ \\__, | \\____|____/|_| |_|\n |___/\n\nThe full source of this website is available at :\nhttps://gitlab.irap.omp.eu/CDPP/SPACEWEATHERONLINE"); |
24 | this.targets = {}; | 24 | this.targets = {}; |
25 | res$ = []; | 25 | res$ = []; |
26 | for (k in this.configuration.targets) { | 26 | for (k in this.configuration.targets) { |
@@ -244,14 +244,18 @@ | @@ -244,14 +244,18 @@ | ||
244 | ? options | 244 | ? options |
245 | : {}; | 245 | : {}; |
246 | this.onBrushEnd = bind$(this, 'onBrushEnd', prototype); | 246 | this.onBrushEnd = bind$(this, 'onBrushEnd', prototype); |
247 | + this.onDoubleClick = bind$(this, 'onDoubleClick', prototype); | ||
247 | this.onMouseOut = bind$(this, 'onMouseOut', prototype); | 248 | this.onMouseOut = bind$(this, 'onMouseOut', prototype); |
248 | this.onMouseOver = bind$(this, 'onMouseOver', prototype); | 249 | this.onMouseOver = bind$(this, 'onMouseOver', prototype); |
249 | this.onMouseMove = bind$(this, 'onMouseMove', prototype); | 250 | this.onMouseMove = bind$(this, 'onMouseMove', prototype); |
250 | this.init(); | 251 | this.init(); |
251 | } | 252 | } |
253 | + TimeSeries.prototype.toString = function(){ | ||
254 | + return this.title + " of " + this.target.name; | ||
255 | + }; | ||
252 | TimeSeries.prototype.init = function(){ | 256 | TimeSeries.prototype.init = function(){ |
253 | var dx, this$ = this; | 257 | var dx, this$ = this; |
254 | - console.info("Initializing time series " + this.title + " of " + this.target.name + "..."); | 258 | + console.info("Initializing time series " + this + "..."); |
255 | this.margin = { | 259 | this.margin = { |
256 | top: 30, | 260 | top: 30, |
257 | right: 20, | 261 | right: 20, |
@@ -311,26 +315,25 @@ | @@ -311,26 +315,25 @@ | ||
311 | this.yAxisTextTarget.attr("y", 0 - this.margin.left).attr("x", 0 - height / 2); | 315 | this.yAxisTextTarget.attr("y", 0 - this.margin.left).attr("x", 0 - height / 2); |
312 | this.mouseCanvas.attr("width", width).attr("height", height); | 316 | this.mouseCanvas.attr("width", width).attr("height", height); |
313 | if (this.brushFunction == null) { | 317 | if (this.brushFunction == null) { |
314 | - console.log("Creating the zooming brush for time series " + this.title + " of " + this.target.name + "..."); | 318 | + console.log("Creating the zooming brush for " + this + "..."); |
315 | this.brushFunction = d3.brushX().extent([[0, 0], [width, height]]).handleSize(0).on("end", this.onBrushEnd); | 319 | this.brushFunction = d3.brushX().extent([[0, 0], [width, height]]).handleSize(0).on("end", this.onBrushEnd); |
316 | this.brush.call(this.brushFunction); | 320 | this.brush.call(this.brushFunction); |
317 | - this.svg.select(".brush .overlay").on("mouseover", this.onMouseOver).on("mouseout", this.onMouseOut).on("mousemove", this.onMouseMove); | 321 | + this.svg.select(".brush .overlay").on("mouseover.swappcursor", this.onMouseOver).on("mouseout.swappcursor", this.onMouseOut).on("mousemove.swappcursor", this.onMouseMove).on("dblclick", this.onDoubleClick); |
318 | } | 322 | } |
319 | if (!this.active) { | 323 | if (!this.active) { |
320 | $(this.svg.node()).hide(); | 324 | $(this.svg.node()).hide(); |
321 | } | 325 | } |
322 | return this; | 326 | return this; |
323 | }; | 327 | }; |
324 | - TimeSeries.prototype.resizeDomain = function(started_at, stopped_at){}; | 328 | + TimeSeries.prototype.resizeDomain = function(startDate, stopDate){}; |
325 | TimeSeries.prototype.onMouseMove = function(){ | 329 | TimeSeries.prototype.onMouseMove = function(){ |
326 | var x; | 330 | var x; |
327 | x = this.xScale.invert(d3.mouse(this.mouseCanvas.node())[0]); | 331 | x = this.xScale.invert(d3.mouse(this.mouseCanvas.node())[0]); |
328 | if (this.options.onMouseMove != null) { | 332 | if (this.options.onMouseMove != null) { |
329 | - this.options.onMouseMove(x); | 333 | + return this.options.onMouseMove(x); |
330 | } else { | 334 | } else { |
331 | - this.moveCursor(x); | 335 | + return this.moveCursor(x); |
332 | } | 336 | } |
333 | - return true; | ||
334 | }; | 337 | }; |
335 | TimeSeries.prototype.onMouseOver = function(){ | 338 | TimeSeries.prototype.onMouseOver = function(){ |
336 | if (this.options.onMouseOver != null) { | 339 | if (this.options.onMouseOver != null) { |
@@ -346,18 +349,44 @@ | @@ -346,18 +349,44 @@ | ||
346 | return this.hideCursor(); | 349 | return this.hideCursor(); |
347 | } | 350 | } |
348 | }; | 351 | }; |
352 | + TimeSeries.prototype.onDoubleClick = function(){ | ||
353 | + console.debug("Resetting zoom of " + this + "."); | ||
354 | + return this.resetZoom(); | ||
355 | + }; | ||
349 | TimeSeries.prototype.onBrushEnd = function(){ | 356 | TimeSeries.prototype.onBrushEnd = function(){ |
350 | var s, minmax; | 357 | var s, minmax; |
351 | s = d3.event.selection; | 358 | s = d3.event.selection; |
352 | - console.log("on brush end", s); | ||
353 | if (s) { | 359 | if (s) { |
354 | minmax = [s[0], s[1]].map(this.xScale.invert, this.xScale); | 360 | minmax = [s[0], s[1]].map(this.xScale.invert, this.xScale); |
355 | - console.info("Zooming in from " + minmax[0], this.xScale.domain([s[0], s[1]].map(this.xScale.invert, this.xScale))); | ||
356 | this.brush.call(this.brushFunction.move, null); | 361 | this.brush.call(this.brushFunction.move, null); |
357 | - return this.zoomIn(); | 362 | + return this.zoomIn(minmax[0], minmax[1]); |
363 | + } | ||
364 | + }; | ||
365 | + TimeSeries.prototype.zoomIn = function(startDate, stopDate){ | ||
366 | + var ref$, minDate, maxDate; | ||
367 | + console.debug("Zooming in " + this + " from " + startDate + " to " + stopDate + "."); | ||
368 | + ref$ = d3.extent(this.data, function(d){ | ||
369 | + return d.x; | ||
370 | + }), minDate = ref$[0], maxDate = ref$[1]; | ||
371 | + if (startDate < minDate) { | ||
372 | + startDate = minDate; | ||
373 | + } | ||
374 | + if (stopDate > maxDate) { | ||
375 | + stopDate = maxDate; | ||
358 | } | 376 | } |
377 | + this.xScale.domain([startDate, stopDate]); | ||
378 | + return this.applyZoom(); | ||
379 | + }; | ||
380 | + TimeSeries.prototype.resetZoom = function(){ | ||
381 | + this.xScale.domain(d3.extent(this.data, function(d){ | ||
382 | + return d.x; | ||
383 | + })); | ||
384 | + this.yScale.domain(d3.extent(this.data, function(d){ | ||
385 | + return d.y; | ||
386 | + })); | ||
387 | + return this.applyZoom(); | ||
359 | }; | 388 | }; |
360 | - TimeSeries.prototype.zoomIn = function(){ | 389 | + TimeSeries.prototype.applyZoom = function(){ |
361 | var t; | 390 | var t; |
362 | t = this.svg.transition().duration(750); | 391 | t = this.svg.transition().duration(750); |
363 | this.svg.select('.x.axis').transition(t).call(this.xAxis); | 392 | this.svg.select('.x.axis').transition(t).call(this.xAxis); |
web/static/js/swapp.ls
@@ -31,7 +31,21 @@ export class SpaceWeather | @@ -31,7 +31,21 @@ export class SpaceWeather | ||
31 | """ | 31 | """ |
32 | 32 | ||
33 | (@configuration) -> | 33 | (@configuration) -> |
34 | - console.info "Creating HelioPropa app...", @configuration | 34 | + console.info """ |
35 | + _ _ _ _ ____ | ||
36 | + | | | | ___| (_) ___ | _ \\ _ __ ___ _ __ __ _ | ||
37 | + | |_| |/ _ \\ | |/ _ \\| |_) | '__/ _ \\| '_ \\ / _` | | ||
38 | + | _ | __/ | | (_) | __/| | | (_) | |_) | (_| | | ||
39 | + |_| |_|\\___|_|_|\\___/|_|_ |_|_ \\___/| .__/ \\__,_| | ||
40 | + | |__ _ _ / ___| _ \\| _ \\| _ \\_| | ||
41 | + | '_ \\| | | | | | | | | | |_) | |_) | | ||
42 | + | |_) | |_| | | |___| |_| | __/| __/ | ||
43 | + |_.__/ \\__, | \\____|____/|_| |_| | ||
44 | + |___/ | ||
45 | + | ||
46 | +The full source of this website is available at : | ||
47 | +https://gitlab.irap.omp.eu/CDPP/SPACEWEATHERONLINE | ||
48 | +""" # HelioPropa by CDPP | ||
35 | @targets = {} | 49 | @targets = {} |
36 | configs = [@configuration.targets[k] for k of @configuration.targets] | 50 | configs = [@configuration.targets[k] for k of @configuration.targets] |
37 | configs.forEach((target_config) ~> | 51 | configs.forEach((target_config) ~> |
@@ -191,8 +205,10 @@ export class TimeSeries | @@ -191,8 +205,10 @@ export class TimeSeries | ||
191 | # data : list of {x: <datetime>, y: <float>} | 205 | # data : list of {x: <datetime>, y: <float>} |
192 | @init() | 206 | @init() |
193 | 207 | ||
208 | + toString: -> "#{@title} of #{@target.name}" | ||
209 | + | ||
194 | init: -> | 210 | init: -> |
195 | - console.info "Initializing time series #{@title} of #{@target.name}..." | 211 | + console.info "Initializing time series #{@}..." |
196 | 212 | ||
197 | @margin = { | 213 | @margin = { |
198 | top: 30, | 214 | top: 30, |
@@ -204,8 +220,6 @@ export class TimeSeries | @@ -204,8 +220,6 @@ export class TimeSeries | ||
204 | @xScale = d3.scaleTime().domain(d3.extent(@data, (d) -> d.x)) | 220 | @xScale = d3.scaleTime().domain(d3.extent(@data, (d) -> d.x)) |
205 | @yScale = d3.scaleLinear().domain(d3.extent(@data, (d) -> d.y)) | 221 | @yScale = d3.scaleLinear().domain(d3.extent(@data, (d) -> d.y)) |
206 | 222 | ||
207 | - #console.info("Y domain #{@title}", d3.extent(@data, (d) -> d.y)) | ||
208 | - | ||
209 | @xAxis = d3.axisBottom() | 223 | @xAxis = d3.axisBottom() |
210 | # .tickFormat(d3.timeFormat("%Y-%m-%d")) | 224 | # .tickFormat(d3.timeFormat("%Y-%m-%d")) |
211 | .ticks(7) | 225 | .ticks(7) |
@@ -220,22 +234,20 @@ export class TimeSeries | @@ -220,22 +234,20 @@ export class TimeSeries | ||
220 | @svg.attr("class", "#{@parameter} #{@target.slug}") | 234 | @svg.attr("class", "#{@parameter} #{@target.slug}") |
221 | 235 | ||
222 | @plotWrapper = @svg.append('g') | 236 | @plotWrapper = @svg.append('g') |
223 | - @plotWrapper.attr('transform', 'translate(' + @margin.left + ',' + @margin.top + ')') | 237 | + @plotWrapper.attr('transform', |
238 | + 'translate(' + @margin.left + ',' + @margin.top + ')') | ||
224 | 239 | ||
225 | @path = @plotWrapper.append('path') | 240 | @path = @plotWrapper.append('path') |
226 | .datum(@data) | 241 | .datum(@data) |
227 | .classed('line', true) | 242 | .classed('line', true) |
228 | 243 | ||
229 | @brush = @plotWrapper.append("g") | 244 | @brush = @plotWrapper.append("g") |
230 | - .attr("class", "brush") | 245 | + .attr("class", "brush") |
231 | 246 | ||
247 | + # deprecated, use brush's 'overlay' child | ||
232 | @mouseCanvas = @plotWrapper.append("rect") | 248 | @mouseCanvas = @plotWrapper.append("rect") |
233 | .style("fill", "none") | 249 | .style("fill", "none") |
234 | # .style("pointer-events", "all") | 250 | # .style("pointer-events", "all") |
235 | -# @mouseCanvas | ||
236 | -# .on("mouseover", @onMouseOver) | ||
237 | -# .on("mouseout", @onMouseOut) | ||
238 | -# .on("mousemove", @onMouseMove) | ||
239 | 251 | ||
240 | @plotWrapper.append('g').classed('x axis', true) | 252 | @plotWrapper.append('g').classed('x axis', true) |
241 | @plotWrapper.append('g').classed('y axis', true) | 253 | @plotWrapper.append('g').classed('y axis', true) |
@@ -322,7 +334,7 @@ export class TimeSeries | @@ -322,7 +334,7 @@ export class TimeSeries | ||
322 | .attr("height", height) | 334 | .attr("height", height) |
323 | 335 | ||
324 | if not @brushFunction? | 336 | if not @brushFunction? |
325 | - console.log "Creating the zooming brush for time series #{@title} of #{@target.name}..." | 337 | + console.log "Creating the zooming brush for #{@}..." |
326 | # looks like d3.brush handles its own resizing on window.resize | 338 | # looks like d3.brush handles its own resizing on window.resize |
327 | @brushFunction = | 339 | @brushFunction = |
328 | d3.brushX() | 340 | d3.brushX() |
@@ -333,19 +345,18 @@ export class TimeSeries | @@ -333,19 +345,18 @@ export class TimeSeries | ||
333 | # .on("move", @onBrushMove) | 345 | # .on("move", @onBrushMove) |
334 | @brush.call(@brushFunction) | 346 | @brush.call(@brushFunction) |
335 | 347 | ||
336 | -# @mouseCanvas = @brush.append("rect") | ||
337 | -# .style("fill", "none") | ||
338 | -# .style("pointer-events", "all") | 348 | + # We're also adding our own cursor events to the brush's overlay, |
349 | + # because it captures events and a rect cannot contain another. | ||
339 | @svg.select(".brush .overlay") | 350 | @svg.select(".brush .overlay") |
340 | - .on("mouseover", @onMouseOver) | ||
341 | - .on("mouseout", @onMouseOut) | ||
342 | - .on("mousemove", @onMouseMove) | ||
343 | - | 351 | + .on("mouseover.swappcursor", @onMouseOver) |
352 | + .on("mouseout.swappcursor", @onMouseOut) | ||
353 | + .on("mousemove.swappcursor", @onMouseMove) | ||
354 | + .on("dblclick", @onDoubleClick) | ||
344 | 355 | ||
345 | unless @active then $(@svg.node()).hide() | 356 | unless @active then $(@svg.node()).hide() |
346 | this | 357 | this |
347 | 358 | ||
348 | - resizeDomain: (started_at, stopped_at) -> | 359 | + resizeDomain: (startDate, stopDate) -> |
349 | # fixme | 360 | # fixme |
350 | # d3.behavior.zoom() | 361 | # d3.behavior.zoom() |
351 | 362 | ||
@@ -355,7 +366,6 @@ export class TimeSeries | @@ -355,7 +366,6 @@ export class TimeSeries | ||
355 | @options.onMouseMove(x) | 366 | @options.onMouseMove(x) |
356 | else | 367 | else |
357 | @moveCursor(x) | 368 | @moveCursor(x) |
358 | - true # fixme: figure out what to return | ||
359 | 369 | ||
360 | onMouseOver: ~> | 370 | onMouseOver: ~> |
361 | if @options.onMouseOver? | 371 | if @options.onMouseOver? |
@@ -369,26 +379,36 @@ export class TimeSeries | @@ -369,26 +379,36 @@ export class TimeSeries | ||
369 | else | 379 | else |
370 | @hideCursor() | 380 | @hideCursor() |
371 | 381 | ||
382 | + onDoubleClick: ~> | ||
383 | + console.debug "Resetting zoom of #{@}." | ||
384 | + @resetZoom() | ||
385 | + | ||
372 | onBrushEnd: ~> | 386 | onBrushEnd: ~> |
373 | s = d3.event.selection | 387 | s = d3.event.selection |
374 | - console.log("on brush end", s) | ||
375 | -# if not s | ||
376 | -# @xScale.domain(d3.extent(@data, (d) -> d.x)).nice() | ||
377 | -# @yScale.domain(d3.extent(@data, (d) -> d.y)).nice() | ||
378 | if s | 388 | if s |
379 | minmax = [s[0], s[1]].map(@xScale.invert, @xScale) | 389 | minmax = [s[0], s[1]].map(@xScale.invert, @xScale) |
380 | - console.info "Zooming in from #{minmax[0]}", | ||
381 | - @xScale.domain([s[0], s[1]].map(@xScale.invert, @xScale)) | ||
382 | @brush.call(@brushFunction.move, null) # some voodoo to hide the brush | 390 | @brush.call(@brushFunction.move, null) # some voodoo to hide the brush |
383 | - @zoomIn() | ||
384 | - | ||
385 | - zoomIn: -> | 391 | + @zoomIn(minmax[0], minmax[1]) |
392 | + | ||
393 | + zoomIn: (startDate, stopDate) -> | ||
394 | + console.debug "Zooming in #{@} from #{startDate} to #{stopDate}." | ||
395 | + [minDate, maxDate] = d3.extent(@data, (d) -> d.x) | ||
396 | + if startDate < minDate then startDate = minDate | ||
397 | + if stopDate > maxDate then stopDate = maxDate | ||
398 | + @xScale.domain([startDate, stopDate]) | ||
399 | + @applyZoom() | ||
400 | + | ||
401 | + resetZoom: -> | ||
402 | + @xScale.domain(d3.extent(@data, (d) -> d.x)) | ||
403 | + @yScale.domain(d3.extent(@data, (d) -> d.y)) | ||
404 | + @applyZoom() | ||
405 | + | ||
406 | + applyZoom: -> | ||
386 | t = @svg.transition().duration(750); | 407 | t = @svg.transition().duration(750); |
387 | @svg.select('.x.axis').transition(t).call(@xAxis); | 408 | @svg.select('.x.axis').transition(t).call(@xAxis); |
388 | @svg.select('.y.axis').transition(t).call(@yAxis); | 409 | @svg.select('.y.axis').transition(t).call(@yAxis); |
389 | @path.transition(t).attr('d', @line) | 410 | @path.transition(t).attr('d', @line) |
390 | 411 | ||
391 | - | ||
392 | showCursor: -> | 412 | showCursor: -> |
393 | @focus.style("display", null) | 413 | @focus.style("display", null) |
394 | 414 |