From 2038c9fb475f8b7ad7ead44afa1c853f4a4ac0e9 Mon Sep 17 00:00:00 2001 From: Goutte Date: Wed, 28 Jun 2017 12:05:46 +0200 Subject: [PATCH] Add a zoom reset on double click, and a nice console log masthead. --- web/static/js/swapp.js | 53 +++++++++++++++++++++++++++++++++++++++++------------ web/static/js/swapp.ls | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------ 2 files changed, 91 insertions(+), 42 deletions(-) diff --git a/web/static/js/swapp.js b/web/static/js/swapp.js index f96308a..c4ebfc5 100644 --- a/web/static/js/swapp.js +++ b/web/static/js/swapp.js @@ -20,7 +20,7 @@ function SpaceWeather(configuration){ var configs, res$, k, this$ = this; this.configuration = configuration; - console.info("Creating HelioPropa app...", this.configuration); + 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"); this.targets = {}; res$ = []; for (k in this.configuration.targets) { @@ -244,14 +244,18 @@ ? options : {}; this.onBrushEnd = bind$(this, 'onBrushEnd', prototype); + this.onDoubleClick = bind$(this, 'onDoubleClick', prototype); this.onMouseOut = bind$(this, 'onMouseOut', prototype); this.onMouseOver = bind$(this, 'onMouseOver', prototype); this.onMouseMove = bind$(this, 'onMouseMove', prototype); this.init(); } + TimeSeries.prototype.toString = function(){ + return this.title + " of " + this.target.name; + }; TimeSeries.prototype.init = function(){ var dx, this$ = this; - console.info("Initializing time series " + this.title + " of " + this.target.name + "..."); + console.info("Initializing time series " + this + "..."); this.margin = { top: 30, right: 20, @@ -311,26 +315,25 @@ this.yAxisTextTarget.attr("y", 0 - this.margin.left).attr("x", 0 - height / 2); this.mouseCanvas.attr("width", width).attr("height", height); if (this.brushFunction == null) { - console.log("Creating the zooming brush for time series " + this.title + " of " + this.target.name + "..."); + console.log("Creating the zooming brush for " + this + "..."); this.brushFunction = d3.brushX().extent([[0, 0], [width, height]]).handleSize(0).on("end", this.onBrushEnd); this.brush.call(this.brushFunction); - this.svg.select(".brush .overlay").on("mouseover", this.onMouseOver).on("mouseout", this.onMouseOut).on("mousemove", this.onMouseMove); + this.svg.select(".brush .overlay").on("mouseover.swappcursor", this.onMouseOver).on("mouseout.swappcursor", this.onMouseOut).on("mousemove.swappcursor", this.onMouseMove).on("dblclick", this.onDoubleClick); } if (!this.active) { $(this.svg.node()).hide(); } return this; }; - TimeSeries.prototype.resizeDomain = function(started_at, stopped_at){}; + TimeSeries.prototype.resizeDomain = function(startDate, stopDate){}; TimeSeries.prototype.onMouseMove = function(){ var x; x = this.xScale.invert(d3.mouse(this.mouseCanvas.node())[0]); if (this.options.onMouseMove != null) { - this.options.onMouseMove(x); + return this.options.onMouseMove(x); } else { - this.moveCursor(x); + return this.moveCursor(x); } - return true; }; TimeSeries.prototype.onMouseOver = function(){ if (this.options.onMouseOver != null) { @@ -346,18 +349,44 @@ return this.hideCursor(); } }; + TimeSeries.prototype.onDoubleClick = function(){ + console.debug("Resetting zoom of " + this + "."); + return this.resetZoom(); + }; TimeSeries.prototype.onBrushEnd = function(){ var s, minmax; s = d3.event.selection; - console.log("on brush end", s); if (s) { minmax = [s[0], s[1]].map(this.xScale.invert, this.xScale); - console.info("Zooming in from " + minmax[0], this.xScale.domain([s[0], s[1]].map(this.xScale.invert, this.xScale))); this.brush.call(this.brushFunction.move, null); - return this.zoomIn(); + return this.zoomIn(minmax[0], minmax[1]); + } + }; + TimeSeries.prototype.zoomIn = function(startDate, stopDate){ + var ref$, minDate, maxDate; + console.debug("Zooming in " + this + " from " + startDate + " to " + stopDate + "."); + ref$ = d3.extent(this.data, function(d){ + return d.x; + }), minDate = ref$[0], maxDate = ref$[1]; + if (startDate < minDate) { + startDate = minDate; + } + if (stopDate > maxDate) { + stopDate = maxDate; } + this.xScale.domain([startDate, stopDate]); + return this.applyZoom(); + }; + TimeSeries.prototype.resetZoom = function(){ + this.xScale.domain(d3.extent(this.data, function(d){ + return d.x; + })); + this.yScale.domain(d3.extent(this.data, function(d){ + return d.y; + })); + return this.applyZoom(); }; - TimeSeries.prototype.zoomIn = function(){ + TimeSeries.prototype.applyZoom = function(){ var t; t = this.svg.transition().duration(750); this.svg.select('.x.axis').transition(t).call(this.xAxis); diff --git a/web/static/js/swapp.ls b/web/static/js/swapp.ls index 9d5b074..8085c93 100644 --- a/web/static/js/swapp.ls +++ b/web/static/js/swapp.ls @@ -31,7 +31,21 @@ export class SpaceWeather """ (@configuration) -> - console.info "Creating HelioPropa app...", @configuration + console.info """ + _ _ _ _ ____ + | | | | ___| (_) ___ | _ \\ _ __ ___ _ __ __ _ + | |_| |/ _ \\ | |/ _ \\| |_) | '__/ _ \\| '_ \\ / _` | + | _ | __/ | | (_) | __/| | | (_) | |_) | (_| | + |_| |_|\\___|_|_|\\___/|_|_ |_|_ \\___/| .__/ \\__,_| + | |__ _ _ / ___| _ \\| _ \\| _ \\_| + | '_ \\| | | | | | | | | | |_) | |_) | + | |_) | |_| | | |___| |_| | __/| __/ + |_.__/ \\__, | \\____|____/|_| |_| + |___/ + +The full source of this website is available at : +https://gitlab.irap.omp.eu/CDPP/SPACEWEATHERONLINE +""" # HelioPropa by CDPP @targets = {} configs = [@configuration.targets[k] for k of @configuration.targets] configs.forEach((target_config) ~> @@ -191,8 +205,10 @@ export class TimeSeries # data : list of {x: , y: } @init() + toString: -> "#{@title} of #{@target.name}" + init: -> - console.info "Initializing time series #{@title} of #{@target.name}..." + console.info "Initializing time series #{@}..." @margin = { top: 30, @@ -204,8 +220,6 @@ export class TimeSeries @xScale = d3.scaleTime().domain(d3.extent(@data, (d) -> d.x)) @yScale = d3.scaleLinear().domain(d3.extent(@data, (d) -> d.y)) - #console.info("Y domain #{@title}", d3.extent(@data, (d) -> d.y)) - @xAxis = d3.axisBottom() # .tickFormat(d3.timeFormat("%Y-%m-%d")) .ticks(7) @@ -220,22 +234,20 @@ export class TimeSeries @svg.attr("class", "#{@parameter} #{@target.slug}") @plotWrapper = @svg.append('g') - @plotWrapper.attr('transform', 'translate(' + @margin.left + ',' + @margin.top + ')') + @plotWrapper.attr('transform', + 'translate(' + @margin.left + ',' + @margin.top + ')') @path = @plotWrapper.append('path') .datum(@data) .classed('line', true) @brush = @plotWrapper.append("g") - .attr("class", "brush") + .attr("class", "brush") + # deprecated, use brush's 'overlay' child @mouseCanvas = @plotWrapper.append("rect") .style("fill", "none") # .style("pointer-events", "all") -# @mouseCanvas -# .on("mouseover", @onMouseOver) -# .on("mouseout", @onMouseOut) -# .on("mousemove", @onMouseMove) @plotWrapper.append('g').classed('x axis', true) @plotWrapper.append('g').classed('y axis', true) @@ -322,7 +334,7 @@ export class TimeSeries .attr("height", height) if not @brushFunction? - console.log "Creating the zooming brush for time series #{@title} of #{@target.name}..." + console.log "Creating the zooming brush for #{@}..." # looks like d3.brush handles its own resizing on window.resize @brushFunction = d3.brushX() @@ -333,19 +345,18 @@ export class TimeSeries # .on("move", @onBrushMove) @brush.call(@brushFunction) -# @mouseCanvas = @brush.append("rect") -# .style("fill", "none") -# .style("pointer-events", "all") + # We're also adding our own cursor events to the brush's overlay, + # because it captures events and a rect cannot contain another. @svg.select(".brush .overlay") - .on("mouseover", @onMouseOver) - .on("mouseout", @onMouseOut) - .on("mousemove", @onMouseMove) - + .on("mouseover.swappcursor", @onMouseOver) + .on("mouseout.swappcursor", @onMouseOut) + .on("mousemove.swappcursor", @onMouseMove) + .on("dblclick", @onDoubleClick) unless @active then $(@svg.node()).hide() this - resizeDomain: (started_at, stopped_at) -> + resizeDomain: (startDate, stopDate) -> # fixme # d3.behavior.zoom() @@ -355,7 +366,6 @@ export class TimeSeries @options.onMouseMove(x) else @moveCursor(x) - true # fixme: figure out what to return onMouseOver: ~> if @options.onMouseOver? @@ -369,26 +379,36 @@ export class TimeSeries else @hideCursor() + onDoubleClick: ~> + console.debug "Resetting zoom of #{@}." + @resetZoom() + onBrushEnd: ~> s = d3.event.selection - console.log("on brush end", s) -# if not s -# @xScale.domain(d3.extent(@data, (d) -> d.x)).nice() -# @yScale.domain(d3.extent(@data, (d) -> d.y)).nice() if s minmax = [s[0], s[1]].map(@xScale.invert, @xScale) - console.info "Zooming in from #{minmax[0]}", - @xScale.domain([s[0], s[1]].map(@xScale.invert, @xScale)) @brush.call(@brushFunction.move, null) # some voodoo to hide the brush - @zoomIn() - - zoomIn: -> + @zoomIn(minmax[0], minmax[1]) + + zoomIn: (startDate, stopDate) -> + console.debug "Zooming in #{@} from #{startDate} to #{stopDate}." + [minDate, maxDate] = d3.extent(@data, (d) -> d.x) + if startDate < minDate then startDate = minDate + if stopDate > maxDate then stopDate = maxDate + @xScale.domain([startDate, stopDate]) + @applyZoom() + + resetZoom: -> + @xScale.domain(d3.extent(@data, (d) -> d.x)) + @yScale.domain(d3.extent(@data, (d) -> d.y)) + @applyZoom() + + applyZoom: -> t = @svg.transition().duration(750); @svg.select('.x.axis').transition(t).call(@xAxis); @svg.select('.y.axis').transition(t).call(@yAxis); @path.transition(t).attr('d', @line) - showCursor: -> @focus.style("display", null) -- libgit2 0.21.2