changeset 2791:2b79c0871138

client: spuc8: draw diagrams The line and area charts from spuc 8 are being drawn but there's a lot missing like tooltips and toggeling individual charts.
author Markus Kottlaender <markus@intevation.de>
date Mon, 25 Mar 2019 12:07:00 +0100
parents 563bcd8b7d7b
children cfd9ac0c92ee
files client/src/components/gauge/Gauges.vue client/src/components/gauge/HydrologicalConditions.vue client/src/store/gauges.js
diffstat 3 files changed, 118 insertions(+), 66 deletions(-) [+]
line wrap: on
line diff
--- a/client/src/components/gauge/Gauges.vue	Mon Mar 25 11:42:31 2019 +0100
+++ b/client/src/components/gauge/Gauges.vue	Mon Mar 25 12:07:00 2019 +0100
@@ -71,7 +71,6 @@
           <button
             @click="showHydrologicalConditionsDiagram()"
             class="btn btn-sm btn-info d-block w-100 mt-2"
-            disabled
           >
             <translate>Show Hydrological Conditions</translate>
           </button>
--- a/client/src/components/gauge/HydrologicalConditions.vue	Mon Mar 25 11:42:31 2019 +0100
+++ b/client/src/components/gauge/HydrologicalConditions.vue	Mon Mar 25 12:07:00 2019 +0100
@@ -11,8 +11,24 @@
 <style lang="sass" scoped>
 .diagram-container
   /deep/
+    .hide
+      opacity: 0
     .line
       clip-path: url(#clip)
+      stroke-width: 2
+      fill: none
+      &.mean
+        stroke: steelblue
+      &.median
+        stroke: black
+      &.q25
+        stroke: orange
+      &.q75
+        stroke: purple
+    .area
+      clip-path: url(#clip)
+      stroke: none
+      fill: lightsteelblue
 
     .hdc-line,
     .ldc-line,
@@ -64,15 +80,10 @@
  */
 
 import { mapState, mapGetters } from "vuex";
-import * as d3Base from "d3";
+import * as d3 from "d3";
 import debounce from "debounce";
-import { lineChunked } from "d3-line-chunked";
 import { startOfYear, endOfYear } from "date-fns";
 
-// we should load only d3 modules we need but for now we'll go with the lazy way
-// https://www.giacomodebidda.com/how-to-import-d3-plugins-with-webpack/
-const d3 = Object.assign(d3Base, { lineChunked });
-
 export default {
   computed: {
     ...mapState("gauges", ["meanWaterlevels"]),
@@ -158,6 +169,9 @@
         .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
@@ -180,27 +194,28 @@
 
       // PREPARING CHART FUNCTIONS
 
-      // points are "next to each other" when they are exactly 1 day apart
-      const isNext = (prev, current) =>
-        current.date - prev.date === 24 * 60 * 60 * 1000;
+      // waterlevel line charts in big chart
+      const lineChart = type =>
+        d3
+          .line()
+          .x(d => x(d.date))
+          .y(d => y(d[type]))
+          .curve(d3.curveLinear);
 
-      // waterlevel line in big chart
-      // d3-line-chunked plugin: https://github.com/pbeshai/d3-line-chunked
-      var mainLineChart = d3
-        .lineChunked()
+      // overall min/max area chart
+      const areaChart = d3
+        .area()
         .x(d => x(d.date))
-        .y(d => y(d.waterlevel))
-        .curve(d3.curveLinear)
-        .isNext(isNext)
-        .pointAttrs({ r: 2.2 });
-      // waterlevel line in small chart
-      let navLineChart = d3
-        .lineChunked()
+        .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))
-        .y(d => y2(d.waterlevel))
-        .curve(d3.curveMonotoneX)
-        .isNext(isNext)
-        .pointAttrs({ r: 1.7 });
+        .y0(d => y2(d.min))
+        .y1(d => y2(d.max));
+
       // hdc/ldc/mw
       let refWaterlevelLine = d3
         .line()
@@ -243,6 +258,13 @@
         .selectAll(".tick text")
         .attr("x", -25);
 
+      // overall min/max area chart
+      mainChart
+        .append("path")
+        .datum(this.meanWaterlevels)
+        .attr("class", "area")
+        .attr("d", areaChart);
+
       // reference waterlevels
       // HDC
       mainChart
@@ -290,12 +312,30 @@
         .attr("x", x(yearEnd) - 20)
         .attr("y", y(refWaterLevels.MW) - 3);
 
-      // waterlevel chart
+      // mean waterlevel chart
+      mainChart
+        .append("path")
+        .attr("class", "line mean")
+        .datum(this.meanWaterlevels)
+        .attr("d", lineChart("mean"));
+      // median waterlevel chart
       mainChart
-        .append("g")
-        .attr("class", "line")
-        .datum([])
-        .call(mainLineChart);
+        .append("path")
+        .attr("class", "line median")
+        .datum(this.meanWaterlevels)
+        .attr("d", lineChart("median"));
+      // q25 waterlevel chart
+      mainChart
+        .append("path")
+        .attr("class", "line q25")
+        .datum(this.meanWaterlevels)
+        .attr("d", lineChart("q25"));
+      // q75 waterlevel chart
+      mainChart
+        .append("path")
+        .attr("class", "line q75")
+        .datum(this.meanWaterlevels)
+        .attr("d", lineChart("q75"));
 
       // DRAWING NAVCHART
 
@@ -311,15 +351,28 @@
         .attr("transform", `translate(0, ${navHeight})`)
         .call(xAxis2);
 
-      // waterlevel chart
+      // overall min/max area chart
       navChart
-        .append("g")
-        .attr("class", "line")
-        .datum([])
-        .call(navLineChart);
+        .append("path")
+        .datum(this.meanWaterlevels)
+        .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
         .brushX()
@@ -330,12 +383,7 @@
             return; // ignore brush-by-zoom
           let s = d3.event.selection || x2.range();
           x.domain(s.map(x2.invert, x2));
-          mainChart.select(".line").call(mainLineChart);
-          mainChart
-            .select(".axis--x")
-            .call(xAxis)
-            .selectAll(".tick text")
-            .attr("y", 15);
+          updateChart();
           svg
             .select(".zoom")
             .call(
@@ -355,12 +403,7 @@
             return; // ignore zoom-by-brush
           let t = d3.event.transform;
           x.domain(t.rescaleX(x2).domain());
-          mainChart.select(".line").call(mainLineChart);
-          mainChart
-            .select(".axis--x")
-            .call(xAxis)
-            .selectAll(".tick text")
-            .attr("y", 15);
+          updateChart();
           navChart
             .select(".brush")
             .call(brush.move, x.range().map(t.invertX, t));
--- a/client/src/store/gauges.js	Mon Mar 25 11:42:31 2019 +0100
+++ b/client/src/store/gauges.js	Mon Mar 25 12:07:00 2019 +0100
@@ -137,26 +137,36 @@
           });
       });
     },
-    loadMeanWaterlevels({ /*state,*/ commit }) {
-      return new Promise(resolve => {
-        setTimeout(() => {
-          commit("meanWaterlevels", [1]);
-          resolve();
-        }, 2000);
+    loadMeanWaterlevels({ state, commit }) {
+      return new Promise((resolve, reject) => {
+        HTTP.get(`/data/average-waterlevels/${state.selectedGaugeISRS}`, {
+          headers: { "X-Gemma-Auth": localStorage.getItem("token") }
+        })
+          .then(response => {
+            let data = response.data
+              .split("\n")
+              .filter((wl, i) => wl && i) // remove empty rows and first row
+              .map(wl => {
+                wl = wl.split(",");
+                return {
+                  date: new Date(wl[0]),
+                  min: Number(wl[1]),
+                  max: Number(wl[2]),
+                  mean: Number(wl[3]),
+                  median: Number(wl[4]),
+                  q25: Number(wl[5]),
+                  q75: Number(wl[6])
+                };
+              });
+            data = data.sort((a, b) => a.date - b.date);
+            commit("meanWaterlevels", data);
+            resolve(data);
+          })
+          .catch(error => {
+            commit("meanWaterlevels", []);
+            reject(error);
+          });
       });
-      // return new Promise((resolve, reject) => {
-      //   HTTP.get(`/data/mean-waterlevels/${state.selectedGaugeISRS}`, {
-      //     headers: { "X-Gemma-Auth": localStorage.getItem("token") }
-      //   })
-      //     .then(response => {
-      //       commit("meanWaterlevels", response.data);
-      //       resolve(response.data);
-      //     })
-      //     .catch(error => {
-      //       commit("meanWaterlevels", []);
-      //       reject(error);
-      //     })
-      // });
     },
     loadNashSutcliffe({ state, commit }) {
       return new Promise((resolve, reject) => {