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 | 20 | function SpaceWeather(configuration){ |
21 | 21 | var configs, res$, k, this$ = this; |
22 | 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 | 24 | this.targets = {}; |
25 | 25 | res$ = []; |
26 | 26 | for (k in this.configuration.targets) { |
... | ... | @@ -244,14 +244,18 @@ |
244 | 244 | ? options |
245 | 245 | : {}; |
246 | 246 | this.onBrushEnd = bind$(this, 'onBrushEnd', prototype); |
247 | + this.onDoubleClick = bind$(this, 'onDoubleClick', prototype); | |
247 | 248 | this.onMouseOut = bind$(this, 'onMouseOut', prototype); |
248 | 249 | this.onMouseOver = bind$(this, 'onMouseOver', prototype); |
249 | 250 | this.onMouseMove = bind$(this, 'onMouseMove', prototype); |
250 | 251 | this.init(); |
251 | 252 | } |
253 | + TimeSeries.prototype.toString = function(){ | |
254 | + return this.title + " of " + this.target.name; | |
255 | + }; | |
252 | 256 | TimeSeries.prototype.init = function(){ |
253 | 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 | 259 | this.margin = { |
256 | 260 | top: 30, |
257 | 261 | right: 20, |
... | ... | @@ -311,26 +315,25 @@ |
311 | 315 | this.yAxisTextTarget.attr("y", 0 - this.margin.left).attr("x", 0 - height / 2); |
312 | 316 | this.mouseCanvas.attr("width", width).attr("height", height); |
313 | 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 | 319 | this.brushFunction = d3.brushX().extent([[0, 0], [width, height]]).handleSize(0).on("end", this.onBrushEnd); |
316 | 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 | 323 | if (!this.active) { |
320 | 324 | $(this.svg.node()).hide(); |
321 | 325 | } |
322 | 326 | return this; |
323 | 327 | }; |
324 | - TimeSeries.prototype.resizeDomain = function(started_at, stopped_at){}; | |
328 | + TimeSeries.prototype.resizeDomain = function(startDate, stopDate){}; | |
325 | 329 | TimeSeries.prototype.onMouseMove = function(){ |
326 | 330 | var x; |
327 | 331 | x = this.xScale.invert(d3.mouse(this.mouseCanvas.node())[0]); |
328 | 332 | if (this.options.onMouseMove != null) { |
329 | - this.options.onMouseMove(x); | |
333 | + return this.options.onMouseMove(x); | |
330 | 334 | } else { |
331 | - this.moveCursor(x); | |
335 | + return this.moveCursor(x); | |
332 | 336 | } |
333 | - return true; | |
334 | 337 | }; |
335 | 338 | TimeSeries.prototype.onMouseOver = function(){ |
336 | 339 | if (this.options.onMouseOver != null) { |
... | ... | @@ -346,18 +349,44 @@ |
346 | 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 | 356 | TimeSeries.prototype.onBrushEnd = function(){ |
350 | 357 | var s, minmax; |
351 | 358 | s = d3.event.selection; |
352 | - console.log("on brush end", s); | |
353 | 359 | if (s) { |
354 | 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 | 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 | 390 | var t; |
362 | 391 | t = this.svg.transition().duration(750); |
363 | 392 | this.svg.select('.x.axis').transition(t).call(this.xAxis); | ... | ... |
web/static/js/swapp.ls
... | ... | @@ -31,7 +31,21 @@ export class SpaceWeather |
31 | 31 | """ |
32 | 32 | |
33 | 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 | 49 | @targets = {} |
36 | 50 | configs = [@configuration.targets[k] for k of @configuration.targets] |
37 | 51 | configs.forEach((target_config) ~> |
... | ... | @@ -191,8 +205,10 @@ export class TimeSeries |
191 | 205 | # data : list of {x: <datetime>, y: <float>} |
192 | 206 | @init() |
193 | 207 | |
208 | + toString: -> "#{@title} of #{@target.name}" | |
209 | + | |
194 | 210 | init: -> |
195 | - console.info "Initializing time series #{@title} of #{@target.name}..." | |
211 | + console.info "Initializing time series #{@}..." | |
196 | 212 | |
197 | 213 | @margin = { |
198 | 214 | top: 30, |
... | ... | @@ -204,8 +220,6 @@ export class TimeSeries |
204 | 220 | @xScale = d3.scaleTime().domain(d3.extent(@data, (d) -> d.x)) |
205 | 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 | 223 | @xAxis = d3.axisBottom() |
210 | 224 | # .tickFormat(d3.timeFormat("%Y-%m-%d")) |
211 | 225 | .ticks(7) |
... | ... | @@ -220,22 +234,20 @@ export class TimeSeries |
220 | 234 | @svg.attr("class", "#{@parameter} #{@target.slug}") |
221 | 235 | |
222 | 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 | 240 | @path = @plotWrapper.append('path') |
226 | 241 | .datum(@data) |
227 | 242 | .classed('line', true) |
228 | 243 | |
229 | 244 | @brush = @plotWrapper.append("g") |
230 | - .attr("class", "brush") | |
245 | + .attr("class", "brush") | |
231 | 246 | |
247 | + # deprecated, use brush's 'overlay' child | |
232 | 248 | @mouseCanvas = @plotWrapper.append("rect") |
233 | 249 | .style("fill", "none") |
234 | 250 | # .style("pointer-events", "all") |
235 | -# @mouseCanvas | |
236 | -# .on("mouseover", @onMouseOver) | |
237 | -# .on("mouseout", @onMouseOut) | |
238 | -# .on("mousemove", @onMouseMove) | |
239 | 251 | |
240 | 252 | @plotWrapper.append('g').classed('x axis', true) |
241 | 253 | @plotWrapper.append('g').classed('y axis', true) |
... | ... | @@ -322,7 +334,7 @@ export class TimeSeries |
322 | 334 | .attr("height", height) |
323 | 335 | |
324 | 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 | 338 | # looks like d3.brush handles its own resizing on window.resize |
327 | 339 | @brushFunction = |
328 | 340 | d3.brushX() |
... | ... | @@ -333,19 +345,18 @@ export class TimeSeries |
333 | 345 | # .on("move", @onBrushMove) |
334 | 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 | 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 | 356 | unless @active then $(@svg.node()).hide() |
346 | 357 | this |
347 | 358 | |
348 | - resizeDomain: (started_at, stopped_at) -> | |
359 | + resizeDomain: (startDate, stopDate) -> | |
349 | 360 | # fixme |
350 | 361 | # d3.behavior.zoom() |
351 | 362 | |
... | ... | @@ -355,7 +366,6 @@ export class TimeSeries |
355 | 366 | @options.onMouseMove(x) |
356 | 367 | else |
357 | 368 | @moveCursor(x) |
358 | - true # fixme: figure out what to return | |
359 | 369 | |
360 | 370 | onMouseOver: ~> |
361 | 371 | if @options.onMouseOver? |
... | ... | @@ -369,26 +379,36 @@ export class TimeSeries |
369 | 379 | else |
370 | 380 | @hideCursor() |
371 | 381 | |
382 | + onDoubleClick: ~> | |
383 | + console.debug "Resetting zoom of #{@}." | |
384 | + @resetZoom() | |
385 | + | |
372 | 386 | onBrushEnd: ~> |
373 | 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 | 388 | if s |
379 | 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 | 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 | 407 | t = @svg.transition().duration(750); |
387 | 408 | @svg.select('.x.axis').transition(t).call(@xAxis); |
388 | 409 | @svg.select('.y.axis').transition(t).call(@yAxis); |
389 | 410 | @path.transition(t).attr('d', @line) |
390 | 411 | |
391 | - | |
392 | 412 | showCursor: -> |
393 | 413 | @focus.style("display", null) |
394 | 414 | ... | ... |