Commit 08569a6b50caa694b921c4f44cc303900dda965e

Authored by Goutte
1 parent 908b4423

Add a zooming brush on each time series.

Implement changes suggested by Vincent Génot.
config.yml
... ... @@ -23,15 +23,12 @@ authors:
23 23 role: Software Ninja
24 24 mail: antoine.goutenoir@irap.omp.eu
25 25 - name: Myriam Bouchemit
26   - role: Server Maintenance
  26 + role: Code Reviewer
27 27 mail: myriam.bouchemit@irap.omp.eu
28   - - name: Mikel Indurain
29   - role: Simulation Software
30   - mail: mindurain@irap.omp.eu
31 28 - name: Nicolas André
32 29 role: Project Lead
33 30 mail: nicolas.andre@irap.omp.eu
34   - - name: Vincent Genot
  31 + - name: Vincent Génot
35 32 role: Project Coordinator
36 33 mail: vincent.genot@irap.omp.eu
37 34  
... ...
web/static/js/swapp.js
... ... @@ -243,6 +243,7 @@
243 243 this.options = options != null
244 244 ? options
245 245 : {};
  246 + this.onBrushEnd = bind$(this, 'onBrushEnd', prototype);
246 247 this.onMouseOut = bind$(this, 'onMouseOut', prototype);
247 248 this.onMouseOver = bind$(this, 'onMouseOver', prototype);
248 249 this.onMouseMove = bind$(this, 'onMouseMove', prototype);
... ... @@ -263,7 +264,7 @@
263 264 this.yScale = d3.scaleLinear().domain(d3.extent(this.data, function(d){
264 265 return d.y;
265 266 }));
266   - this.xAxis = d3.axisBottom().tickFormat(d3.timeFormat("%Y-%m-%d")).ticks(7);
  267 + this.xAxis = d3.axisBottom().ticks(7);
267 268 this.yAxis = d3.axisLeft().ticks(10);
268 269 this.line = d3.line().x(function(d){
269 270 return this$.xScale(d.x);
... ... @@ -275,8 +276,8 @@
275 276 this.plotWrapper = this.svg.append('g');
276 277 this.plotWrapper.attr('transform', 'translate(' + this.margin.left + ',' + this.margin.top + ')');
277 278 this.path = this.plotWrapper.append('path').datum(this.data).classed('line', true);
278   - this.mouseCanvas = this.plotWrapper.append("rect").style("fill", "none").style("pointer-events", "all");
279   - this.mouseCanvas.on("mouseover", this.onMouseOver).on("mouseout", this.onMouseOut).on("mousemove", this.onMouseMove);
  279 + this.brush = this.plotWrapper.append("g").attr("class", "brush");
  280 + this.mouseCanvas = this.plotWrapper.append("rect").style("fill", "none");
280 281 this.plotWrapper.append('g').classed('x axis', true);
281 282 this.plotWrapper.append('g').classed('y axis', true);
282 283 this.yAxisText = this.plotWrapper.append("text").attr("transform", "rotate(-90)").attr("dy", "1em").style("text-anchor", "middle").text(this.title);
... ... @@ -296,7 +297,6 @@
296 297 height = GOLDEN_RATIO * GOLDEN_RATIO * GOLDEN_RATIO * GOLDEN_RATIO * width;
297 298 this.plotWidth = width;
298 299 this.plotHeight = height;
299   - console.log("Resizing time series " + this.title + " of " + this.target.name + ": " + width + " x " + height);
300 300 this.xScale.range([0, width]);
301 301 this.yScale.range([height, 0]);
302 302 this.svg.attr('width', width + this.margin.right + this.margin.left).attr('height', height + this.margin.top + this.margin.bottom);
... ... @@ -310,6 +310,12 @@
310 310 this.yAxisText.attr("y", 20 - this.margin.left).attr("x", 0 - height / 2);
311 311 this.yAxisTextTarget.attr("y", 0 - this.margin.left).attr("x", 0 - height / 2);
312 312 this.mouseCanvas.attr("width", width).attr("height", height);
  313 + if (this.brushFunction == null) {
  314 + console.log("Creating the zooming brush for time series " + this.title + " of " + this.target.name + "...");
  315 + this.brushFunction = d3.brushX().extent([[0, 0], [width, height]]).handleSize(0).on("end", this.onBrushEnd);
  316 + this.brush.call(this.brushFunction);
  317 + this.svg.select(".brush .overlay").on("mouseover", this.onMouseOver).on("mouseout", this.onMouseOut).on("mousemove", this.onMouseMove);
  318 + }
313 319 if (!this.active) {
314 320 $(this.svg.node()).hide();
315 321 }
... ... @@ -320,10 +326,11 @@
320 326 var x;
321 327 x = this.xScale.invert(d3.mouse(this.mouseCanvas.node())[0]);
322 328 if (this.options.onMouseMove != null) {
323   - return this.options.onMouseMove(x);
  329 + this.options.onMouseMove(x);
324 330 } else {
325   - return this.moveCursor(x);
  331 + this.moveCursor(x);
326 332 }
  333 + return true;
327 334 };
328 335 TimeSeries.prototype.onMouseOver = function(){
329 336 if (this.options.onMouseOver != null) {
... ... @@ -339,6 +346,24 @@
339 346 return this.hideCursor();
340 347 }
341 348 };
  349 + TimeSeries.prototype.onBrushEnd = function(){
  350 + var s, minmax;
  351 + s = d3.event.selection;
  352 + console.log("on brush end", s);
  353 + if (s) {
  354 + 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);
  357 + return this.zoomIn();
  358 + }
  359 + };
  360 + TimeSeries.prototype.zoomIn = function(){
  361 + var t;
  362 + t = this.svg.transition().duration(750);
  363 + this.svg.select('.x.axis').transition(t).call(this.xAxis);
  364 + this.svg.select('.y.axis').transition(t).call(this.yAxis);
  365 + return this.path.transition(t).attr('d', this.line);
  366 + };
342 367 TimeSeries.prototype.showCursor = function(){
343 368 return this.focus.style("display", null);
344 369 };
... ...
web/static/js/swapp.ls
... ... @@ -207,7 +207,7 @@ export class TimeSeries
207 207 #console.info("Y domain #{@title}", d3.extent(@data, (d) -> d.y))
208 208  
209 209 @xAxis = d3.axisBottom()
210   - .tickFormat(d3.timeFormat("%Y-%m-%d"))
  210 +# .tickFormat(d3.timeFormat("%Y-%m-%d"))
211 211 .ticks(7)
212 212 @yAxis = d3.axisLeft()
213 213 .ticks(10)
... ... @@ -226,17 +226,16 @@ export class TimeSeries
226 226 .datum(@data)
227 227 .classed('line', true)
228 228  
  229 + @brush = @plotWrapper.append("g")
  230 + .attr("class", "brush")
  231 +
229 232 @mouseCanvas = @plotWrapper.append("rect")
230 233 .style("fill", "none")
231   - .style("pointer-events", "all")
232   - @mouseCanvas
233   - .on("mouseover", @onMouseOver)
234   - .on("mouseout", @onMouseOut)
235   - .on("mousemove", @onMouseMove)
236   -
237   -# @plotWrapper.append("g")
238   -# .attr("class", "brush")
239   -# .call(d3.brush().on("brush", @onBrushEnd));
  234 +# .style("pointer-events", "all")
  235 +# @mouseCanvas
  236 +# .on("mouseover", @onMouseOver)
  237 +# .on("mouseout", @onMouseOut)
  238 +# .on("mousemove", @onMouseMove)
240 239  
241 240 @plotWrapper.append('g').classed('x axis', true)
242 241 @plotWrapper.append('g').classed('y axis', true)
... ... @@ -289,7 +288,7 @@ export class TimeSeries
289 288 @plotWidth = width
290 289 @plotHeight = height
291 290  
292   - console.log("Resizing time series #{@title} of #{@target.name}: #{width} x #{height}")
  291 + #console.log("Resizing time series #{@title} of #{@target.name}: #{width} x #{height}")
293 292  
294 293 @xScale.range([0, width]);
295 294 @yScale.range([height, 0]);
... ... @@ -297,7 +296,7 @@ export class TimeSeries
297 296 @svg.attr('width', width + @margin.right + @margin.left)
298 297 .attr('height', height + @margin.top + @margin.bottom)
299 298  
300   - @path.attr('d', @line) # wip, do we need to put this in resize ?
  299 + @path.attr('d', @line)
301 300  
302 301 @xAxis.scale(@xScale)
303 302 @yAxis.scale(@yScale)
... ... @@ -322,6 +321,26 @@ export class TimeSeries
322 321 @mouseCanvas.attr("width", width)
323 322 .attr("height", height)
324 323  
  324 + if not @brushFunction?
  325 + console.log "Creating the zooming brush for time series #{@title} of #{@target.name}..."
  326 + # looks like d3.brush handles its own resizing on window.resize
  327 + @brushFunction =
  328 + d3.brushX()
  329 + .extent([[0, 0], [width, height]])
  330 + .handleSize(0)
  331 + .on("end", @onBrushEnd)
  332 +# .on("start", @onBrushStart)
  333 +# .on("move", @onBrushMove)
  334 + @brush.call(@brushFunction)
  335 +
  336 +# @mouseCanvas = @brush.append("rect")
  337 +# .style("fill", "none")
  338 +# .style("pointer-events", "all")
  339 + @svg.select(".brush .overlay")
  340 + .on("mouseover", @onMouseOver)
  341 + .on("mouseout", @onMouseOut)
  342 + .on("mousemove", @onMouseMove)
  343 +
325 344  
326 345 unless @active then $(@svg.node()).hide()
327 346 this
... ... @@ -336,6 +355,7 @@ export class TimeSeries
336 355 @options.onMouseMove(x)
337 356 else
338 357 @moveCursor(x)
  358 + true # fixme: figure out what to return
339 359  
340 360 onMouseOver: ~>
341 361 if @options.onMouseOver?
... ... @@ -349,17 +369,25 @@ export class TimeSeries
349 369 else
350 370 @hideCursor()
351 371  
352   -# onBrushEnd: ~>
353   -# s = d3.event.selection
354   -# console.info("on brush end", s)
  372 + onBrushEnd: ~>
  373 + s = d3.event.selection
  374 + console.log("on brush end", s)
355 375 # if not s
356 376 # @xScale.domain(d3.extent(@data, (d) -> d.x)).nice()
357 377 # @yScale.domain(d3.extent(@data, (d) -> d.y)).nice()
358   -# else
359   -# console.info "brush end", s
360   -# @xScale.domain([s[0][0], s[1][0]].map(x.invert, x))
361   -# @yScale.domain([s[1][1], s[0][1]].map(y.invert, y))
362   -# scatter.select(".brush").call(brush.move, null)
  378 + if s
  379 + 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
  383 + @zoomIn()
  384 +
  385 + zoomIn: ->
  386 + t = @svg.transition().duration(750);
  387 + @svg.select('.x.axis').transition(t).call(@xAxis);
  388 + @svg.select('.y.axis').transition(t).call(@yAxis);
  389 + @path.transition(t).attr('d', @line)
  390 +
363 391  
364 392 showCursor: ->
365 393 @focus.style("display", null)
... ...
web/view/home.html.jinja2
... ... @@ -51,7 +51,7 @@
51 51 <a class="mdl-navigation__link parameter" data-ts-slug="vlen" href="#">Velocity</a>
52 52 <a class="mdl-navigation__link parameter" data-ts-slug="temp" href="#">Temperature</a>
53 53 <a class="mdl-navigation__link parameter" data-ts-slug="dens" href="#">Density</a>
54   - <a class="mdl-navigation__link parameter" data-ts-slug="angl" href="#">Angle Source-Sun-Earth</a>
  54 + <a class="mdl-navigation__link parameter" data-ts-slug="angl" href="#">Angle Target-Sun-Earth</a>
55 55 </nav>
56 56  
57 57 <div class="mdl-layout-spacer"></div>
... ... @@ -98,8 +98,9 @@
98 98 svg text {
99 99 {# fill: #f4f4f4;#}
100 100 }
101   - svg .brush {
102   - border: 1px solid black;
  101 + #time_series svg .brush .selection {
  102 + fill: #efa02c;
  103 + fill-opacity: 0.382;
103 104 }
104 105 path.line {
105 106 fill: none;
... ... @@ -241,7 +242,7 @@ var configuration = {
241 242 },
242 243 {
243 244 id: 'angl',
244   - title: "Angle S-S-E (deg)",
  245 + title: "Angle T-S-E (deg)",
245 246 active: false,
246 247 unit: "deg"
247 248 }
... ...