changeset 2610:5ce1b4b29869

client: waterlevel diagram: refactored code for better readability
author Markus Kottlaender <markus@intevation.de>
date Wed, 13 Mar 2019 11:25:03 +0100
parents 546ed93a9829
children e8c97481438f
files client/src/components/gauge/Waterlevel.vue
diffstat 1 files changed, 122 insertions(+), 131 deletions(-) [+]
line wrap: on
line diff
--- a/client/src/components/gauge/Waterlevel.vue	Wed Mar 13 10:08:31 2019 +0100
+++ b/client/src/components/gauge/Waterlevel.vue	Wed Mar 13 11:25:03 2019 +0100
@@ -5,7 +5,7 @@
 <style lang="sass" scoped>
 .diagram-container
   /deep/
-    .area
+    .line
       stroke: steelblue
       stroke-width: 2
       fill: transparent
@@ -69,184 +69,200 @@
     drawDiagram() {
       if (!this.selectedGauge || !this.waterlevels.length) return;
 
+      // remove old diagram
+      d3.select(".diagram-container svg").remove();
+
+      // get HDC/LDC/MW of the gauge
       let refWaterLevels = JSON.parse(
         this.selectedGauge.properties.reference_water_levels
       );
 
+      // CREATE SVG AND SET DIMENSIONS/MARGINS
+
       let svgWidth = document.querySelector(".diagram-container").clientWidth;
       let svgHeight = document.querySelector(".diagram-container").clientHeight;
-      d3.select(".diagram-container svg").remove();
-      let svg = d3.select(".diagram-container").append("svg");
-      svg.attr("width", "100%").attr("height", "100%");
-      let margin = { top: 20, right: 20, bottom: 110, left: 40 },
-        margin2 = {
-          top: svgHeight - margin.top - 50,
+      let svg = d3
+        .select(".diagram-container")
+        .append("svg")
+        .attr("width", "100%")
+        .attr("height", "100%");
+      let mainMargin = { top: 50, right: 20, bottom: 110, left: 40 },
+        navMargin = {
+          top: svgHeight - mainMargin.top - 35,
           right: 20,
           bottom: 30,
           left: 40
         },
-        width = +svgWidth - margin.left - margin.right,
-        height = +svgHeight - margin.top - margin.bottom,
-        height2 = +svgHeight - margin2.top - margin2.bottom;
+        width = +svgWidth - mainMargin.left - mainMargin.right,
+        mainHeight = +svgHeight - mainMargin.top - mainMargin.bottom,
+        navHeight = +svgHeight - navMargin.top - navMargin.bottom;
 
+      // PREPARING AXES/SCALING
+
+      // 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([height, 0]),
-        y2 = d3.scaleLinear().range([height2, 0]);
-
-      let xAxis = d3.axisBottom(x),
-        xAxis2 = d3.axisBottom(x2),
-        yAxis = d3.axisLeft(y);
-
+        y = d3.scaleLinear().range([mainHeight, 0]),
+        y2 = d3.scaleLinear().range([navHeight, 0]);
       // find min/max values for the waterlevel axis
-      // based on hdc/ldc +/- 100 cm
-      let xMinMax = d3.extent(
+      // including hdc/ldc (+/- 100 cm)
+      let WaterlevelMinMax = d3.extent(
         [
           ...this.waterlevels,
           { waterlevel: refWaterLevels.HDC + 100 },
           { waterlevel: Math.max(refWaterLevels.LDC - 100, 0) }
         ],
-        function(d) {
-          return d.waterlevel;
-        }
+        d => d.waterlevel
       );
-
-      x.domain(
-        d3.extent(this.waterlevels, function(d) {
-          return d.date;
-        })
-      );
-      y.domain(xMinMax);
+      // setting the min and max values for the diagram axes
+      x.domain(d3.extent(this.waterlevels, d => d.date));
+      y.domain(WaterlevelMinMax);
       x2.domain(x.domain());
       y2.domain(y.domain());
-
-      let brush = d3
-        .brushX()
-        .handleSize(4)
-        .extent([[0, 0], [width, height2]])
-        .on("brush end", brushed);
+      // creating the axes based on these scales
+      let xAxis = d3.axisBottom(x),
+        xAxis2 = d3.axisBottom(x2),
+        yAxis = d3.axisLeft(y);
 
-      let zoom = d3
-        .zoom()
-        .scaleExtent([1, Infinity])
-        .translateExtent([[0, 0], [width, height]])
-        .extent([[0, 0], [width, height]])
-        .on("zoom", zoomed);
+      // PREPARING CHART FUNCTIONS
 
       // waterlevel line in big chart
-      let area = d3
+      let mainLineChart = d3
         .line()
         .curve(d3.curveMonotoneX)
-        .x(function(d) {
-          return x(d.date);
-        })
-        .y(function(d) {
-          return y(d.waterlevel);
-        });
-
+        .x(d => x(d.date))
+        .y(d => y(d.waterlevel));
       // waterlevel line in small chart
-      let area2 = d3
+      let navLineChart = d3
         .line()
         .curve(d3.curveMonotoneX)
-        .x(function(d) {
-          return x2(d.date);
-        })
-        .y(function(d) {
-          return y2(d.waterlevel);
-        });
-
+        .x(d => x2(d.date))
+        .y(d => y2(d.waterlevel));
+      // hdc/ldc/mw
       let refWaterlevelLine = d3
         .line()
         .x(d => x(d.x))
         .y(d => y(d.y));
 
+      // DRAWING MAINCHART
+
+      // define visible area
+      // everything outside this area will be hidden (clipped)
       svg
         .append("defs")
         .append("clipPath")
         .attr("id", "clip")
         .append("rect")
         .attr("width", width)
-        .attr("height", height);
+        .attr("height", mainHeight);
 
-      let focus = svg
+      let mainChart = svg
         .append("g")
-        .attr("class", "focus")
-        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
+        .attr("transform", `translate(${mainMargin.left}, ${mainMargin.top})`);
 
-      let context = svg
+      // axes
+      mainChart
         .append("g")
-        .attr("class", "context")
-        .attr(
-          "transform",
-          "translate(" + margin2.left + "," + margin2.top + ")"
-        );
+        .attr("transform", `translate(0, ${mainHeight})`)
+        .call(xAxis);
+      mainChart.append("g").call(yAxis);
 
-      focus
+      // waterlevel chart
+      mainChart
         .append("path")
         .datum(this.waterlevels)
-        .attr("class", "area")
-        .attr("d", area);
+        .attr("class", "line")
+        .attr("d", mainLineChart);
 
-      focus
+      // reference waterlevels
+      let lastDate = this.waterlevels[this.waterlevels.length - 1].date;
+      mainChart
         .append("path")
         .datum([
           { x: 0, y: refWaterLevels.HDC },
-          {
-            x: this.waterlevels[this.waterlevels.length - 1].date,
-            y: refWaterLevels.HDC
-          }
+          { x: lastDate, y: refWaterLevels.HDC }
         ])
         .attr("class", "hdc-line")
         .attr("d", refWaterlevelLine);
-
-      focus
+      mainChart
         .append("path")
         .datum([
           { x: 0, y: refWaterLevels.LDC },
-          {
-            x: this.waterlevels[this.waterlevels.length - 1].date,
-            y: refWaterLevels.LDC
-          }
+          { x: lastDate, y: refWaterLevels.LDC }
         ])
         .attr("class", "ldc-line")
         .attr("d", refWaterlevelLine);
-
-      focus
+      mainChart
         .append("path")
         .datum([
           { x: 0, y: refWaterLevels.MW },
-          {
-            x: this.waterlevels[this.waterlevels.length - 1].date,
-            y: refWaterLevels.MW
-          }
+          { x: lastDate, y: refWaterLevels.MW }
         ])
         .attr("class", "mw-line")
         .attr("d", refWaterlevelLine);
 
-      focus
+      // DRAWING NAVCHART
+
+      let navChart = svg
+        .append("g")
+        .attr("transform", `translate(${navMargin.left}, ${navMargin.top})`);
+
+      // axis (nav chart only has y-axis)
+      navChart
         .append("g")
         .attr("class", "axis axis--x")
-        .attr("transform", "translate(0," + height + ")")
-        .call(xAxis);
+        .attr("transform", `translate(0, ${navHeight})`)
+        .call(xAxis2);
 
-      focus
-        .append("g")
-        .attr("class", "axis axis--y")
-        .call(yAxis);
-
-      context
+      // waterlevel chart
+      navChart
         .append("path")
         .datum(this.waterlevels)
-        .attr("class", "area")
-        .attr("d", area2);
+        .attr("class", "line")
+        .attr("d", navLineChart);
+
+      // INTERACTIVITY
 
-      context
-        .append("g")
-        .attr("class", "axis axis--x")
-        .attr("transform", "translate(0," + height2 + ")")
-        .call(xAxis2);
+      // selecting time period in nav chart
+      let 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));
+          mainChart.select(".line").attr("d", mainLineChart);
+          mainChart.select(".axis--x").call(xAxis);
+          svg
+            .select(".zoom")
+            .call(
+              zoom.transform,
+              d3.zoomIdentity.scale(width / (s[1] - s[0])).translate(-s[0], 0)
+            );
+        });
 
-      context
+      // zooming with mousewheel in main chart
+      let 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());
+          mainChart.select(".line").attr("d", mainLineChart);
+          mainChart.select(".axis--x").call(xAxis);
+          navChart
+            .select(".brush")
+            .call(brush.move, x.range().map(t.invertX, t));
+        });
+
+      navChart
         .append("g")
         .attr("class", "brush")
         .call(brush)
@@ -256,34 +272,9 @@
         .append("rect")
         .attr("class", "zoom")
         .attr("width", width)
-        .attr("height", height)
-        .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
+        .attr("height", mainHeight)
+        .attr("transform", `translate(${mainMargin.left}, ${mainMargin.top})`)
         .call(zoom);
-
-      function brushed() {
-        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));
-        focus.select(".area").attr("d", area);
-        focus.select(".axis--x").call(xAxis);
-        svg
-          .select(".zoom")
-          .call(
-            zoom.transform,
-            d3.zoomIdentity.scale(width / (s[1] - s[0])).translate(-s[0], 0)
-          );
-      }
-
-      function zoomed() {
-        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());
-        focus.select(".area").attr("d", area);
-        focus.select(".axis--x").call(xAxis);
-        context.select(".brush").call(brush.move, x.range().map(t.invertX, t));
-      }
     }
   },
   created() {