changeset 2751:5da81634bdc4

client: waterlevel diagram: implemented nash-sutcliffe Since there is no real data available currently, I left a commented block of demo data in client/src/store/gauges.js:L139. Uncomment that block to see the coefficients in the diagram.
author Markus Kottlaender <markus@intevation.de>
date Thu, 21 Mar 2019 12:33:43 +0100
parents 6446bf6d2a89
children 018f979f9e23
files client/src/components/gauge/Gauges.vue client/src/components/gauge/Waterlevel.vue client/src/store/gauges.js
diffstat 3 files changed, 204 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/client/src/components/gauge/Gauges.vue	Thu Mar 21 12:23:52 2019 +0100
+++ b/client/src/components/gauge/Gauges.vue	Thu Mar 21 12:33:43 2019 +0100
@@ -192,8 +192,11 @@
       this.$store.commit("application/splitscreenLoading", true);
       this.loading = true;
       this.$store.commit("application/showSplitscreen", true);
-      this.$store
-        .dispatch("gauges/loadWaterlevels")
+
+      Promise.all([
+        this.$store.dispatch("gauges/loadWaterlevels"),
+        this.$store.dispatch("gauges/loadNashSutcliffe")
+      ])
         .catch(error => {
           const { status, data } = error.response;
           displayError({
--- a/client/src/components/gauge/Waterlevel.vue	Thu Mar 21 12:23:52 2019 +0100
+++ b/client/src/components/gauge/Waterlevel.vue	Thu Mar 21 12:33:43 2019 +0100
@@ -18,14 +18,14 @@
       .line
         stroke: steelblue
         stroke-width: 2
-        fill: transparent
+        fill: none
         clip-path: url(#clip)
 
     .hdc-line,
     .ldc-line,
     .mw-line
       stroke-width: 1
-      fill: transparent
+      fill: none
       clip-path: url(#clip)
     .hdc-line
       stroke: red
@@ -39,6 +39,20 @@
     .hdc-ldc-area
       fill: rgba(0, 255, 0, 0.15)
 
+    path.nash-sutcliffe
+      fill: none
+      stroke: black
+      stroke-width: 1
+      clip-path: url(#clip)
+      &.ns72
+        fill: rgba(0, 0, 0, 0.05)
+    text.nash-sutcliffe
+      font-size: 10px
+      clip-path: url(#clip)
+      tspan:last-child
+        font-size: 9px
+        fill: #777
+
     .tick
       line
         stroke-dasharray: 5
@@ -50,7 +64,7 @@
       pointer-events: all
     .brush
       .selection
-        stroke: transparent
+        stroke: none
         fill-opacity: 0.2
       .handle
         stroke: rgba($color-info, 0.5)
@@ -73,6 +87,8 @@
       text
         fill: #666
         font-size: 12px
+        tspan:last-child
+          font-weight: bold
 </style>
 
 <script>
@@ -101,7 +117,12 @@
 
 export default {
   computed: {
-    ...mapState("gauges", ["waterlevels", "dateFrom", "dateTo"]),
+    ...mapState("gauges", [
+      "waterlevels",
+      "dateFrom",
+      "dateTo",
+      "nashSutcliffe"
+    ]),
     ...mapGetters("gauges", ["selectedGauge"])
   },
   watch: {
@@ -111,6 +132,9 @@
   },
   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();
 
@@ -161,7 +185,7 @@
           {
             waterlevel: Math.max(
               refWaterLevels.LDC -
-                (refWaterLevels.HDC - refWaterLevels.LDC) / 8,
+                (refWaterLevels.HDC - refWaterLevels.LDC) / 4,
               0
             )
           }
@@ -325,7 +349,7 @@
         .attr("class", "nav")
         .attr("transform", `translate(${navMargin.left}, ${navMargin.top})`);
 
-      // axis (nav chart only has y-axis)
+      // axis (nav chart only has x-axis)
       navChart
         .append("g")
         .attr("class", "axis axis--x")
@@ -339,6 +363,98 @@
         .attr("class", "line")
         .attr("d", navLineChart);
 
+      // NASH SUTCLIFFE
+
+      let nashSut24 = this.nashSutcliffe.coeffs.find(c => c.hours === 24);
+      let nashSut48 = this.nashSutcliffe.coeffs.find(c => c.hours === 48);
+      let nashSut72 = this.nashSutcliffe.coeffs.find(c => c.hours === 72);
+
+      let nashSutDateNow = new Date(this.nashSutcliffe.when);
+      let nashSutDate24 = new Date(this.nashSutcliffe.when);
+      let nashSutDate48 = new Date(this.nashSutcliffe.when);
+      let nashSutDate72 = new Date(this.nashSutcliffe.when);
+      nashSutDate24.setDate(nashSutDate24.getDate() - 1);
+      nashSutDate48.setDate(nashSutDate48.getDate() - 2);
+      nashSutDate72.setDate(nashSutDate72.getDate() - 3);
+
+      const nashSutcliffeBox = hours => {
+        return d3
+          .area()
+          .x(d => x(d))
+          .y0(() => mainHeight + 0.5)
+          .y1(() => mainHeight - 15 * (hours / 24));
+      };
+
+      const nashSutcliffeLabel = (label, date, hours) => {
+        let days = hours / 24;
+        label
+          .attr("x", x(date) + 3)
+          .attr("y", mainHeight - (15 * days + 0.5) + 12);
+      };
+
+      // Show nash-sutcliffe only when x-axis extent is smaller than 31 days
+      // (2678400000 ms). Since it shows squares representing 1, 2 and 3 days
+      // it does not make sense to show them on a x-axis with hundres of days.
+      if (this.nashSutcliffe && x.domain()[1] - x.domain()[0] < 2678400000) {
+        if (nashSut24.samples) {
+          mainChart
+            .append("path")
+            .datum([nashSutDate24, nashSutDateNow])
+            .attr("class", "nash-sutcliffe ns24")
+            .attr("d", nashSutcliffeBox(24));
+          mainChart
+            .append("text")
+            .attr("class", "nash-sutcliffe ns24")
+            .call(nashSutcliffeLabel, nashSutDate24, 24)
+            .append("tspan")
+            .text(nashSut24.value.toFixed(2))
+            .select(function() {
+              return this.parentNode;
+            })
+            .append("tspan")
+            .text(` (${nashSut24.samples})`)
+            .attr("dy", -1);
+        }
+        if (nashSut48.samples) {
+          mainChart
+            .append("path")
+            .datum([nashSutDate48, nashSutDateNow])
+            .attr("class", "nash-sutcliffe ns48")
+            .attr("d", nashSutcliffeBox(48));
+          mainChart
+            .append("text")
+            .attr("class", "nash-sutcliffe ns48")
+            .call(nashSutcliffeLabel, nashSutDate48, 48)
+            .append("tspan")
+            .text(nashSut48.value.toFixed(2))
+            .select(function() {
+              return this.parentNode;
+            })
+            .append("tspan")
+            .text(` (${nashSut48.samples})`)
+            .attr("dy", -1);
+        }
+        if (nashSut72.samples) {
+          mainChart
+            .append("path")
+            .datum([nashSutDate72, nashSutDateNow])
+            .attr("class", "nash-sutcliffe ns72")
+            .attr("d", nashSutcliffeBox(72));
+          mainChart
+            .append("text")
+            .attr("class", "nash-sutcliffe ns72")
+            .call(nashSutcliffeLabel, nashSutDate72, 72)
+            .append("tspan")
+            .text(nashSut72.value.toFixed(2))
+            .select(function() {
+              return this.parentNode;
+            })
+            .append("tspan")
+            .text(` (${nashSut72.samples})`)
+            .attr("dy", -1);
+        }
+      }
+
       // INTERACTIVITY
 
       // selecting time period in nav chart
@@ -353,6 +469,24 @@
           x.domain(s.map(x2.invert, x2));
           mainChart.select(".line").call(mainLineChart);
           mainChart
+            .select("path.nash-sutcliffe.ns24")
+            .attr("d", nashSutcliffeBox(24));
+          mainChart
+            .select("text.nash-sutcliffe.ns24")
+            .call(nashSutcliffeLabel, nashSutDate24, 24);
+          mainChart
+            .select("path.nash-sutcliffe.ns48")
+            .attr("d", nashSutcliffeBox(48));
+          mainChart
+            .select("text.nash-sutcliffe.ns48")
+            .call(nashSutcliffeLabel, nashSutDate48, 48);
+          mainChart
+            .select("path.nash-sutcliffe.ns72")
+            .attr("d", nashSutcliffeBox(72));
+          mainChart
+            .select("text.nash-sutcliffe.ns72")
+            .call(nashSutcliffeLabel, nashSutDate72, 72);
+          mainChart
             .select(".axis--x")
             .call(xAxis)
             .selectAll(".tick text")
@@ -378,6 +512,24 @@
           x.domain(t.rescaleX(x2).domain());
           mainChart.select(".line").call(mainLineChart);
           mainChart
+            .select("path.nash-sutcliffe.ns24")
+            .attr("d", nashSutcliffeBox(24));
+          mainChart
+            .select("text.nash-sutcliffe.ns24")
+            .call(nashSutcliffeLabel, nashSutDate24, 24);
+          mainChart
+            .select("path.nash-sutcliffe.ns48")
+            .attr("d", nashSutcliffeBox(48));
+          mainChart
+            .select("text.nash-sutcliffe.ns48")
+            .call(nashSutcliffeLabel, nashSutDate48, 48);
+          mainChart
+            .select("path.nash-sutcliffe.ns72")
+            .attr("d", nashSutcliffeBox(72));
+          mainChart
+            .select("text.nash-sutcliffe.ns72")
+            .call(nashSutcliffeLabel, nashSutDate72, 72);
+          mainChart
             .select(".axis--x")
             .call(xAxis)
             .selectAll(".tick text")
@@ -429,8 +581,7 @@
       tooltipText
         .append("tspan")
         .attr("x", 8)
-        .attr("y", 8)
-        .style("font-weight", "bold");
+        .attr("y", 8);
 
       let bisectDate = d3.bisector(d => d.date).left;
       zoomRect
--- a/client/src/store/gauges.js	Thu Mar 21 12:23:52 2019 +0100
+++ b/client/src/store/gauges.js	Thu Mar 21 12:33:43 2019 +0100
@@ -22,6 +22,7 @@
     gauges: [],
     selectedGaugeISRS: null,
     waterlevels: [],
+    nashSutcliffe: null,
     dateFrom: dateFrom,
     dateTo: new Date()
   };
@@ -48,6 +49,9 @@
     waterlevels: (state, data) => {
       state.waterlevels = data;
     },
+    nashSutcliffe: (state, data) => {
+      state.nashSutcliffe = data;
+    },
     dateFrom: (state, dateFrom) => {
       state.dateFrom = dateFrom;
     },
@@ -124,6 +128,42 @@
             reject(error);
           });
       });
+    },
+    loadNashSutcliffe({ state, commit }) {
+      return new Promise((resolve, reject) => {
+        HTTP.get(`/data/nash-sutcliffe/${state.selectedGaugeISRS}`, {
+          headers: { "X-Gemma-Auth": localStorage.getItem("token") }
+        })
+          .then(response => {
+            commit("nashSutcliffe", response.data);
+            // dest data
+            // commit("nashSutcliffe", {
+            //   when: "2019-03-20T10:38:05.687",
+            //   coeffs: [
+            //     {
+            //       value: 0.814,
+            //       samples: 18,
+            //       hours: 24
+            //     },
+            //     {
+            //       value: 0.319,
+            //       samples: 36,
+            //       hours: 48
+            //     },
+            //     {
+            //       value: -0.20546757,
+            //       samples: 54,
+            //       hours: 72
+            //     }
+            //   ]
+            // });
+            resolve(response.data);
+          })
+          .catch(error => {
+            commit("nashSutcliffe", null);
+            reject(error);
+          });
+      });
     }
   }
 };