Commit 2038c9fb475f8b7ad7ead44afa1c853f4a4ac0e9

Authored by Goutte
1 parent f7a10f28

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  
... ...