changeset 2815:12f053763be2

client: spuc8: finished drawing charts, optimized code
author Markus Kottlaender <markus@intevation.de>
date Tue, 26 Mar 2019 16:36:48 +0100
parents 6f435a9558f2
children c02cebff3f16
files client/src/components/gauge/HydrologicalConditions.vue client/src/store/gauges.js
diffstat 2 files changed, 368 insertions(+), 260 deletions(-) [+]
line wrap: on
line diff
--- a/client/src/components/gauge/HydrologicalConditions.vue	Tue Mar 26 14:56:38 2019 +0100
+++ b/client/src/components/gauge/HydrologicalConditions.vue	Tue Mar 26 16:36:48 2019 +0100
@@ -45,6 +45,14 @@
     .ref-waterlevel-label
       font-size: 11px
       fill: #999
+    .now-line
+      stroke: #999
+      stroke-width: 1
+      stroke-dasharray: 5, 5
+      clip-path: url(#clip)
+    .now-line-label
+      font-size: 11px
+      fill: #999
 
     .tick
       line
@@ -85,338 +93,443 @@
 import { startOfYear, endOfYear } from "date-fns";
 
 export default {
+  data() {
+    return {
+      svg: null,
+      diagram: null,
+      navigation: null,
+      dimensions: null,
+      extent: null,
+      scale: null,
+      axes: null
+    };
+  },
   computed: {
     ...mapState("gauges", ["longtermWaterlevels", "yearWaterlevels"]),
-    ...mapGetters("gauges", ["selectedGauge", "minMaxWaterlevelForDay"])
+    ...mapGetters("gauges", ["selectedGauge"])
   },
   methods: {
     drawDiagram() {
-      // TODO: Optimize code. I'm pretty sure all of this can be done in a much
-      // more elegant way and with less lines of code.
-
       // remove old diagram
       d3.select(".diagram-container svg").remove();
-
       if (!this.selectedGauge || !this.longtermWaterlevels.length) return;
 
-      // get HDC/LDC/MW of the gauge
-      let refWaterLevels = JSON.parse(
+      // PREPARE HELPERS
+
+      // HDC/LDC/MW for the selected gauge
+      const refWaterLevels = JSON.parse(
         this.selectedGauge.properties.reference_water_levels
       );
 
-      // CREATE SVG AND SET DIMENSIONS/MARGINS
+      // dimensions (widths, heights, margins)
+      this.dimensions = this.getDimensions();
+
+      // get min/max values for date and waterlevel axis
+      this.extent = this.getExtent(refWaterLevels);
 
-      let svgWidth = document.querySelector(".diagram-container").clientWidth;
-      let svgHeight = document.querySelector(".diagram-container").clientHeight;
-      let svg = d3
+      // scaling helpers
+      this.scale = this.getScale();
+
+      // creating the axes based on the scales
+      this.axes = this.getAxes();
+
+      // DRAW DIAGRAM/NAVIGATION AREAS
+
+      // create svg
+      this.svg = d3
         .select(".diagram-container")
         .append("svg")
         .attr("width", "100%")
         .attr("height", "100%");
-      let mainMargin = { top: 20, right: 20, bottom: 110, left: 80 },
-        navMargin = {
-          top: svgHeight - mainMargin.top - 65,
-          right: 20,
-          bottom: 30,
-          left: 80
-        },
-        width = +svgWidth - mainMargin.left - mainMargin.right,
-        mainHeight = +svgHeight - mainMargin.top - mainMargin.bottom,
-        navHeight = +svgHeight - navMargin.top - navMargin.bottom;
 
-      // PREPARING AXES/SCALING
+      // create container for main diagram
+      this.diagram = this.svg
+        .append("g")
+        .attr("class", "main")
+        .attr(
+          "transform",
+          `translate(${this.dimensions.mainMargin.left}, ${
+            this.dimensions.mainMargin.top
+          })`
+        );
 
-      // scaling helpers to convert real values to pixels
-      // based on the diagrams dimensions
-      let x = d3.scaleTime().range([0, width]),
-        x2 = d3.scaleTime().range([0, width]),
-        y = d3.scaleLinear().range([mainHeight, 0]),
-        y2 = d3.scaleLinear().range([navHeight, 0]);
-      // find min/max values for the waterlevel axis
-      // including HDC/LDC (+/- 1/8 HDC-LDC)
-      let WaterlevelMinMax = d3.extent(
-        [
-          ...this.longtermWaterlevels,
-          {
-            waterlevel:
-              refWaterLevels.HDC + (refWaterLevels.HDC - refWaterLevels.LDC) / 8
-          },
-          {
-            waterlevel: Math.max(
-              refWaterLevels.LDC -
-                (refWaterLevels.HDC - refWaterLevels.LDC) / 8,
-              0
-            )
-          }
-        ],
-        d => d.waterlevel
-      );
-      // setting the min and max values for the diagram axes
-      let yearStart = startOfYear(new Date());
-      let yearEnd = endOfYear(new Date());
-      x.domain(d3.extent([yearStart, yearEnd]));
-      y.domain(WaterlevelMinMax);
-      x2.domain(x.domain());
-      y2.domain(y.domain());
-      // creating the axes based on these scales
-      let xAxis = d3
-        .axisTop(x)
-        .tickSizeInner(mainHeight)
-        .tickSizeOuter(0)
-        .tickFormat(date => {
-          // make the x-axis label formats dynamic, based on zoom
-          // but never display year numbers since they don't make any sense in
-          // this diagram
-          return (d3.timeSecond(date) < date
-            ? d3.timeFormat(".%L")
-            : d3.timeMinute(date) < date
-            ? d3.timeFormat(":%S")
-            : d3.timeHour(date) < date
-            ? d3.timeFormat("%I:%M")
-            : d3.timeDay(date) < date
-            ? d3.timeFormat("%I %p")
-            : d3.timeMonth(date) < date
-            ? d3.timeWeek(date) < date
-              ? d3.timeFormat("%a %d")
-              : d3.timeFormat("%b %d")
-            : d3.timeFormat("%B"))(date);
-        });
-      let xAxis2 = d3.axisBottom(x2).tickFormat(d3.timeFormat("%B"));
-      let yAxis = d3
-        .axisRight(y)
-        .tickSizeInner(width)
-        .tickSizeOuter(0);
+      // create container for navigation diagram
+      this.navigation = this.svg
+        .append("g")
+        .attr("class", "nav")
+        .attr(
+          "transform",
+          `translate(${this.dimensions.navMargin.left}, ${
+            this.dimensions.navMargin.top
+          })`
+        );
 
-      // PREPARING CHART FUNCTIONS
-
-      // waterlevel line charts in big chart
-      const lineChart = type =>
-        d3
-          .line()
-          .x(d => x(d.date))
-          .y(d => y(d[type]))
-          .curve(d3.curveLinear);
-
-      // overall min/max area chart
-      const areaChart = d3
-        .area()
-        .x(d => x(d.date))
-        .y0(d => y(d.min))
-        .y1(d => y(d.max));
-
-      // overall min/max area chart in nav
-      const areaChartNav = d3
-        .area()
-        .x(d => x2(d.date))
-        .y0(d => y2(d.min))
-        .y1(d => y2(d.max));
-
-      // hdc/ldc/mw
-      let refWaterlevelLine = d3
-        .line()
-        .x(d => x(d.x))
-        .y(d => y(d.y));
-
-      // DRAWING MAINCHART
-
-      // define visible chart area
-      // everything outside this area will be hidden (clipped)
-      svg
+      // define visible area, everything outside this area will be hidden
+      this.svg
         .append("defs")
         .append("clipPath")
         .attr("id", "clip")
         .append("rect")
-        .attr("width", width)
-        .attr("height", mainHeight);
+        .attr("width", this.dimensions.width)
+        .attr("height", this.dimensions.mainHeight);
+
+      // DRAW DIAGRAM PARTS
+
+      // Each drawSomething function (with the exception of drawRefLines)
+      // returns a fuction to update the respective chart/area/etc. These
+      // updater functions are used by the zoom feature to rescale all elements.
+      const updaters = [];
+
+      // draw
+      this.drawRefLines(refWaterLevels); // static, doesn't need an updater
+      updaters.push(this.drawAxes());
+      updaters.push(this.drawNowLines());
+      updaters.push(this.drawWaterlevelMinMaxAreaChart());
+      updaters.push(this.drawWaterlevelLineChart("median"));
+      updaters.push(this.drawWaterlevelLineChart("q25"));
+      updaters.push(this.drawWaterlevelLineChart("q75"));
+      updaters.push(this.drawWaterlevelLineChart("mean", this.yearWaterlevels));
+
+      // INTERACTIONS
+
+      // create rectanlge on the main chart area to capture mouse events
+      const eventRect = this.svg
+        .append("rect")
+        .attr("class", "zoom")
+        .attr("width", this.dimensions.width)
+        .attr("height", this.dimensions.mainHeight)
+        .attr(
+          "transform",
+          `translate(${this.dimensions.mainMargin.left}, ${
+            this.dimensions.mainMargin.top
+          })`
+        );
+
+      this.createZoom(updaters, eventRect);
+    },
+    getDimensions() {
+      // dimensions and margins
+      const svgWidth = document.querySelector(".diagram-container").clientWidth;
+      const svgHeight = document.querySelector(".diagram-container")
+        .clientHeight;
+      const mainMargin = { top: 20, right: 20, bottom: 110, left: 80 };
+      const navMargin = {
+        top: svgHeight - mainMargin.top - 65,
+        right: 20,
+        bottom: 30,
+        left: 80
+      };
+      const width = +svgWidth - mainMargin.left - mainMargin.right;
+      const mainHeight = +svgHeight - mainMargin.top - mainMargin.bottom;
+      const navHeight = +svgHeight - navMargin.top - navMargin.bottom;
+
+      return { width, mainHeight, navHeight, mainMargin, navMargin };
+    },
+    getExtent(refWaterLevels) {
+      const waterlevelsRelevantForExtent = [];
+      this.longtermWaterlevels.forEach(wl => {
+        waterlevelsRelevantForExtent.push(wl.min, wl.max);
+      });
+      waterlevelsRelevantForExtent.push(
+        refWaterLevels.HDC + (refWaterLevels.HDC - refWaterLevels.LDC) / 8,
+        Math.max(
+          refWaterLevels.LDC - (refWaterLevels.HDC - refWaterLevels.LDC) / 4,
+          0
+        )
+      );
+      return {
+        // set min/max values for the date axis
+        date: [startOfYear(new Date()), endOfYear(new Date())],
+        // set min/max values for the waterlevel axis
+        // including HDC (+ 1/8 HDC-LDC) and LDC (- 1/4 HDC-LDC)
+        waterlevel: d3.extent(waterlevelsRelevantForExtent)
+      };
+    },
+    getScale() {
+      // scaling helpers to convert real world values into pixels
+      const x = d3.scaleTime().range([0, this.dimensions.width]);
+      const y = d3.scaleLinear().range([this.dimensions.mainHeight, 0]);
+      const x2 = d3.scaleTime().range([0, this.dimensions.width]);
+      const y2 = d3.scaleLinear().range([this.dimensions.navHeight, 0]);
+
+      // setting the min and max values for the diagram axes
+      x.domain(d3.extent(this.extent.date));
+      y.domain(this.extent.waterlevel);
+      x2.domain(x.domain());
+      y2.domain(y.domain());
 
-      let mainChart = svg
-        .append("g")
-        .attr("class", "main")
-        .attr("transform", `translate(${mainMargin.left}, ${mainMargin.top})`);
+      return { x, y, x2, y2 };
+    },
+    getAxes() {
+      return {
+        x: d3
+          .axisTop(this.scale.x)
+          .tickSizeInner(this.dimensions.mainHeight)
+          .tickSizeOuter(0)
+          .tickFormat(date => {
+            // make the x-axis label formats dynamic, based on zoom
+            // but never display year numbers since they don't make any sense in
+            // this diagram
+            return (d3.timeSecond(date) < date
+              ? d3.timeFormat(".%L")
+              : d3.timeMinute(date) < date
+              ? d3.timeFormat(":%S")
+              : d3.timeHour(date) < date
+              ? d3.timeFormat("%I:%M")
+              : d3.timeDay(date) < date
+              ? d3.timeFormat("%I %p")
+              : d3.timeMonth(date) < date
+              ? d3.timeWeek(date) < date
+                ? d3.timeFormat("%a %d")
+                : d3.timeFormat("%b %d")
+              : d3.timeFormat("%B"))(date);
+          }),
+        y: d3
+          .axisRight(this.scale.y)
+          .tickSizeInner(this.dimensions.width)
+          .tickSizeOuter(0),
+        x2: d3.axisBottom(this.scale.x2)
+      };
+    },
+    drawNowLines() {
+      const nowLine = d3
+        .line()
+        .x(d => this.scale.x(d.x))
+        .y(d => this.scale.y(d.y));
 
-      // axes
-      mainChart
+      const nowLabel = selection => {
+        selection.attr(
+          "transform",
+          `translate(${this.scale.x(new Date())}, ${this.scale.y(
+            this.extent.waterlevel[1] - 16
+          )})`
+        );
+      };
+
+      // draw in main
+      this.diagram
+        .append("path")
+        .datum([
+          { x: new Date(), y: this.extent.waterlevel[0] },
+          { x: new Date(), y: this.extent.waterlevel[1] - 20 }
+        ])
+        .attr("class", "now-line")
+        .attr("d", nowLine);
+      this.diagram // label
+        .append("text")
+        .text(this.$gettext("Now"))
+        .attr("class", "now-line-label")
+        .attr("text-anchor", "middle")
+        .call(nowLabel);
+
+      // draw in nav
+      this.navigation
+        .append("path")
+        .datum([
+          { x: new Date(), y: this.extent.waterlevel[0] },
+          { x: new Date(), y: this.extent.waterlevel[1] - 20 }
+        ])
+        .attr("class", "now-line")
+        .attr(
+          "d",
+          d3
+            .line()
+            .x(d => this.scale.x2(d.x))
+            .y(d => this.scale.y2(d.y))
+        );
+
+      return () => {
+        this.diagram.select(".now-line").attr("d", nowLine);
+        this.diagram.select(".now-line-label").call(nowLabel);
+      };
+    },
+    drawAxes() {
+      this.diagram
         .append("g")
         .attr("class", "axis--x")
-        .attr("transform", `translate(0, ${mainHeight})`)
-        .call(xAxis)
+        .attr("transform", `translate(0, ${this.dimensions.mainHeight})`)
+        .call(this.axes.x)
         .selectAll(".tick text")
         .attr("y", 15);
-      mainChart // label
+      this.diagram // label
         .append("text")
         .text(this.$gettext("Waterlevel [cm]"))
         .attr("text-anchor", "middle")
-        .attr("transform", `translate(-45, ${mainHeight / 2}) rotate(-90)`);
-      mainChart
+        .attr(
+          "transform",
+          `translate(-45, ${this.dimensions.mainHeight / 2}) rotate(-90)`
+        );
+      this.diagram
         .append("g")
-        .call(yAxis)
+        .call(this.axes.y)
         .selectAll(".tick text")
         .attr("x", -25);
 
-      // overall min/max area chart
-      mainChart
+      this.navigation
+        .append("g")
+        .attr("class", "axis axis--x")
+        .attr("transform", `translate(0, ${this.dimensions.navHeight})`)
+        .call(this.axes.x2);
+
+      return () => {
+        this.diagram
+          .select(".axis--x")
+          .call(this.axes.x)
+          .selectAll(".tick text")
+          .attr("y", 15);
+      };
+    },
+    drawWaterlevelMinMaxAreaChart() {
+      const areaChart = isNav =>
+        d3
+          .area()
+          .x(d => this.scale[isNav ? "x2" : "x"](d.date))
+          .y0(d => this.scale[isNav ? "y2" : "y"](d.min))
+          .y1(d => this.scale[isNav ? "y2" : "y"](d.max));
+
+      this.diagram
         .append("path")
         .datum(this.longtermWaterlevels)
         .attr("class", "area")
-        .attr("d", areaChart);
+        .attr("d", areaChart());
+
+      this.navigation
+        .append("path")
+        .datum(this.longtermWaterlevels)
+        .attr("class", "area")
+        .attr("d", areaChart(true));
 
-      // reference waterlevels
+      return () => {
+        this.diagram.select(".area").attr("d", areaChart());
+      };
+    },
+    drawWaterlevelLineChart(type, data) {
+      const lineChart = type =>
+        d3
+          .line()
+          .x(d => this.scale.x(d.date))
+          .y(d => this.scale.y(d[type]))
+          .curve(d3.curveLinear);
+      this.diagram
+        .append("path")
+        .attr("class", "line " + type)
+        .datum(data || this.longtermWaterlevels)
+        .attr("d", lineChart(type));
+
+      return () => {
+        this.diagram.select(".line." + type).attr("d", lineChart(type));
+      };
+    },
+    drawRefLines(refWaterLevels) {
+      const refWaterlevelLine = d3
+        .line()
+        .x(d => this.scale.x(d.x))
+        .y(d => this.scale.y(d.y));
+
       // HDC
-      mainChart
+      this.diagram
         .append("path")
         .datum([
           { x: 0, y: refWaterLevels.HDC },
-          { x: yearEnd, y: refWaterLevels.HDC }
+          { x: this.extent.date[1], y: refWaterLevels.HDC }
         ])
         .attr("class", "hdc-line")
         .attr("d", refWaterlevelLine);
-      mainChart // label
+      this.diagram // label
         .append("text")
-        .text("HDC")
+        .text(`HDC (${refWaterLevels.HDC})`)
         .attr("class", "ref-waterlevel-label")
-        .attr("x", x(yearEnd) - 20)
-        .attr("y", y(refWaterLevels.HDC) - 3);
+        .attr("x", 5)
+        .attr("y", this.scale.y(refWaterLevels.HDC) - 3);
       // LDC
-      mainChart
+      this.diagram
         .append("path")
         .datum([
           { x: 0, y: refWaterLevels.LDC },
-          { x: yearEnd, y: refWaterLevels.LDC }
+          { x: this.extent.date[1], y: refWaterLevels.LDC }
         ])
         .attr("class", "ldc-line")
         .attr("d", refWaterlevelLine);
-      mainChart // label
+      this.diagram // label
         .append("text")
-        .text("LDC")
+        .text(`LDC (${refWaterLevels.LDC})`)
         .attr("class", "ref-waterlevel-label")
-        .attr("x", x(yearEnd) - 20)
-        .attr("y", y(refWaterLevels.LDC) - 3);
+        .attr("x", 5)
+        .attr("y", this.scale.y(refWaterLevels.LDC) - 3);
       // MW
-      mainChart
+      this.diagram
         .append("path")
         .datum([
           { x: 0, y: refWaterLevels.MW },
-          { x: yearEnd, y: refWaterLevels.MW }
+          { x: this.extent.date[1], y: refWaterLevels.MW }
         ])
         .attr("class", "mw-line")
         .attr("d", refWaterlevelLine);
-      mainChart // label
+      this.diagram // label
         .append("text")
-        .text("MW")
+        .text(`MW (${refWaterLevels.MW})`)
         .attr("class", "ref-waterlevel-label")
-        .attr("x", x(yearEnd) - 20)
-        .attr("y", y(refWaterLevels.MW) - 3);
-
-      // mean waterlevel chart
-      mainChart
-        .append("path")
-        .attr("class", "line mean")
-        .datum(this.longtermWaterlevels)
-        .attr("d", lineChart("mean"));
-      // median waterlevel chart
-      mainChart
-        .append("path")
-        .attr("class", "line median")
-        .datum(this.longtermWaterlevels)
-        .attr("d", lineChart("median"));
-      // q25 waterlevel chart
-      mainChart
-        .append("path")
-        .attr("class", "line q25")
-        .datum(this.longtermWaterlevels)
-        .attr("d", lineChart("q25"));
-      // q75 waterlevel chart
-      mainChart
-        .append("path")
-        .attr("class", "line q75")
-        .datum(this.longtermWaterlevels)
-        .attr("d", lineChart("q75"));
-
-      // DRAWING NAVCHART
-
-      let navChart = svg
-        .append("g")
-        .attr("class", "nav")
-        .attr("transform", `translate(${navMargin.left}, ${navMargin.top})`);
-
-      // axis (nav chart only has x-axis)
-      navChart
-        .append("g")
-        .attr("class", "axis axis--x")
-        .attr("transform", `translate(0, ${navHeight})`)
-        .call(xAxis2);
-
-      // overall min/max area chart
-      navChart
-        .append("path")
-        .datum(this.longtermWaterlevels)
-        .attr("class", "area")
-        .attr("d", areaChartNav);
-
-      // INTERACTIVITY
-
-      const updateChart = () => {
-        mainChart.select(".line.mean").attr("d", lineChart("mean"));
-        mainChart.select(".line.median").attr("d", lineChart("median"));
-        mainChart.select(".line.q25").attr("d", lineChart("q25"));
-        mainChart.select(".line.q75").attr("d", lineChart("q75"));
-        mainChart.select(".area").attr("d", areaChart);
-        mainChart
-          .select(".axis--x")
-          .call(xAxis)
-          .selectAll(".tick text")
-          .attr("y", 15);
-      };
-
-      // selecting time period in nav chart
-      let brush = d3
+        .attr("x", 5)
+        .attr("y", this.scale.y(refWaterLevels.MW) - 3);
+    },
+    createZoom(updaters, eventRect) {
+      const brush = d3
         .brushX()
         .handleSize(4)
-        .extent([[0, 0], [width, navHeight]])
-        .on("brush end", () => {
-          if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom")
-            return; // ignore brush-by-zoom
-          let s = d3.event.selection || x2.range();
-          x.domain(s.map(x2.invert, x2));
-          updateChart();
-          svg
-            .select(".zoom")
-            .call(
-              zoom.transform,
-              d3.zoomIdentity.scale(width / (s[1] - s[0])).translate(-s[0], 0)
-            );
-        });
+        .extent([[0, 0], [this.dimensions.width, this.dimensions.navHeight]]);
 
-      // zooming with mousewheel in main chart
-      let zoom = d3
+      const zoom = d3
         .zoom()
         .scaleExtent([1, Infinity])
-        .translateExtent([[0, 0], [width, mainHeight]])
-        .extent([[0, 0], [width, mainHeight]])
-        .on("zoom", () => {
-          if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush")
-            return; // ignore zoom-by-brush
-          let t = d3.event.transform;
-          x.domain(t.rescaleX(x2).domain());
-          updateChart();
-          navChart
-            .select(".brush")
-            .call(brush.move, x.range().map(t.invertX, t));
-        });
+        .translateExtent([
+          [0, 0],
+          [this.dimensions.width, this.dimensions.mainHeight]
+        ])
+        .extent([[0, 0], [this.dimensions.width, this.dimensions.mainHeight]]);
 
-      navChart
+      brush.on("brush end", () => {
+        if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom")
+          return; // ignore brush-by-zoom
+        let s = d3.event.selection || this.scale.x2.range();
+        this.scale.x.domain(s.map(this.scale.x2.invert, this.scale.x2));
+        updaters.forEach(u => u && u());
+        this.svg
+          .select(".zoom")
+          .call(
+            zoom.transform,
+            d3.zoomIdentity
+              .scale(this.dimensions.width / (s[1] - s[0]))
+              .translate(-s[0], 0)
+          );
+      });
+
+      zoom.on("zoom", () => {
+        if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush")
+          return; // ignore zoom-by-brush
+        let t = d3.event.transform;
+        this.scale.x.domain(t.rescaleX(this.scale.x2).domain());
+        updaters.forEach(u => u && u());
+        this.navigation
+          .select(".brush")
+          .call(brush.move, this.scale.x.range().map(t.invertX, t));
+      });
+      zoom.on("start", () => {
+        this.svg.select(".chart-dot").style("opacity", 0);
+        this.svg.select(".chart-tooltip").style("opacity", 0);
+      });
+
+      this.navigation
         .append("g")
         .attr("class", "brush")
         .call(brush)
-        .call(brush.move, x.range());
+        .call(brush.move, this.scale.x.range());
 
-      svg
-        .append("rect")
-        .attr("class", "zoom")
-        .attr("width", width)
-        .attr("height", mainHeight)
-        .attr("transform", `translate(${mainMargin.left}, ${mainMargin.top})`)
-        .call(zoom);
+      eventRect.call(zoom);
+    },
+    isNext(seconds) {
+      // helper to check whether points in the chart are "next to each other"
+      // for that they need to be exactly the specified amount of seconds apart.
+      return (prev, current) => current.date - prev.date === seconds * 1000;
     }
   },
   created() {
--- a/client/src/store/gauges.js	Tue Mar 26 14:56:38 2019 +0100
+++ b/client/src/store/gauges.js	Tue Mar 26 16:36:48 2019 +0100
@@ -201,12 +201,7 @@
                 );
                 return {
                   date: date,
-                  min: Number(wl[1]),
-                  max: Number(wl[2]),
-                  mean: Number(wl[3]),
-                  median: Number(wl[4]),
-                  q25: Number(wl[5]),
-                  q75: Number(wl[6])
+                  mean: Number(wl[1])
                 };
               });
             data = data.sort((a, b) => a.date - b.date);