changeset 4038:1b6e1d79a0ca historization_ng

Merged
author Sascha Wilde <wilde@intevation.de>
date Mon, 22 Jul 2019 15:44:19 +0200
parents 9cfc6f478157 (current diff) b10f210af325 (diff)
children 9f9e62f6f4f9
files
diffstat 21 files changed, 320 insertions(+), 131 deletions(-) [+]
line wrap: on
line diff
--- a/.dockerignore	Wed Jul 17 18:47:42 2019 +0200
+++ b/.dockerignore	Mon Jul 22 15:44:19 2019 +0200
@@ -7,6 +7,7 @@
 cmd/gemma/gemma
 
 # Dependency directories
+.buildbase
 **/node_modules
 
 # local dotenv environment variables file
--- a/.hgtags	Wed Jul 17 18:47:42 2019 +0200
+++ b/.hgtags	Mon Jul 22 15:44:19 2019 +0200
@@ -11,3 +11,4 @@
 b166cb97b98a40f33e977c96e65e79bf22e92fca v3.1
 b166cb97b98a40f33e977c96e65e79bf22e92fca v3.1
 d78af8354b95cea86744459f350edb16662dadd0 v3.1
+5396581cf20334cbc5e69280e5d9b192640d96b9 v4-preview20190717
--- a/client/src/components/fairway/AvailableFairwayDepth.vue	Wed Jul 17 18:47:42 2019 +0200
+++ b/client/src/components/fairway/AvailableFairwayDepth.vue	Mon Jul 22 15:44:19 2019 +0200
@@ -169,12 +169,18 @@
       const w = [this.widthlimit1, this.widthlimit2].sort();
       const lowerBound = [d[0], w[0]].filter(x => x).join(", ");
       const upperBound = [d[1], w[1]].filter(x => x).join(", ");
-      return [
-        `> LDC`,
-        `>= ${upperBound}`,
-        `< ${upperBound}`,
-        `< ${lowerBound}`
-      ];
+      let result;
+      if (this.depthlimit1 !== this.depthlimit2) {
+        result = [
+          `> LDC`,
+          `>= ${upperBound}`,
+          `< ${upperBound}`,
+          `< ${lowerBound}`
+        ];
+      } else {
+        result = [`> LDC`, `>= ${upperBound}`, `< ${upperBound}`];
+      }
+      return result;
     },
     dataLink() {
       return `data:text/csv;charset=utf-8, ${encodeURIComponent(this.csv)}`;
@@ -265,28 +271,56 @@
         y = this.pdf.height - offset.y - this.getTextHeight(6);
       }
 
-      this.pdf.doc.setTextColor(color);
-      this.pdf.doc.setDrawColor(this.$options.COLORS.LDC);
-      this.pdf.doc.setFillColor(this.$options.COLORS.LDC);
-      this.pdf.doc.roundedRect(x, y, 10, 4, 1.5, 1.5, "FD");
-      this.pdf.doc.text(this.legend[0], x + 12, y + 3);
+      if (this.legend[3]) {
+        this.pdf.doc.setTextColor(color);
+        this.pdf.doc.setDrawColor(this.$options.COLORS.LDC);
+        this.pdf.doc.setFillColor(this.$options.COLORS.LDC);
+        this.pdf.doc.roundedRect(x, y, 10, 4, 1.5, 1.5, "FD");
+        this.pdf.doc.text(this.legend[0], x + 12, y + 3);
+
+        this.pdf.doc.setDrawColor(this.$options.COLORS.HIGHEST);
+        this.pdf.doc.setFillColor(this.$options.COLORS.HIGHEST);
+        this.pdf.doc.roundedRect(x, y + 5, 10, 4, 1.5, 1.5, "FD");
+        this.pdf.doc.text(this.legend[1], x + 12, y + 8);
+
+        this.pdf.doc.setDrawColor(this.$options.COLORS.REST[1]);
+        this.pdf.doc.setFillColor(this.$options.COLORS.REST[1]);
+        this.pdf.doc.roundedRect(x, y + 10, 10, 4, 1.5, 1.5, "FD");
+        this.pdf.doc.text(this.legend[2], x + 12, y + 13);
 
-      this.pdf.doc.setDrawColor(this.$options.COLORS.REST[0]);
-      this.pdf.doc.setFillColor(this.$options.COLORS.REST[0]);
-      this.pdf.doc.roundedRect(x, y + 5, 10, 4, 1.5, 1.5, "FD");
-      this.pdf.doc.text(this.legend[1], x + 12, y + 8);
+        this.pdf.doc.setDrawColor(this.$options.COLORS.REST[0]);
+        this.pdf.doc.setFillColor(this.$options.COLORS.REST[0]);
+        this.pdf.doc.roundedRect(x, y + 15, 10, 4, 1.5, 1.5, "FD");
+        this.pdf.doc.text(this.legend[3], x + 12, y + 18);
+      } else {
+        this.pdf.doc.setTextColor(color);
+        this.pdf.doc.setDrawColor(this.$options.COLORS.LDC);
+        this.pdf.doc.setFillColor(this.$options.COLORS.LDC);
+        this.pdf.doc.roundedRect(x, y, 10, 4, 1.5, 1.5, "FD");
+        this.pdf.doc.text(this.legend[0], x + 12, y + 3);
 
-      this.pdf.doc.setDrawColor(this.$options.COLORS.REST[1]);
-      this.pdf.doc.setFillColor(this.$options.COLORS.REST[1]);
-      this.pdf.doc.roundedRect(x, y + 10, 10, 4, 1.5, 1.5, "FD");
-      this.pdf.doc.text(this.legend[2], x + 12, y + 13);
+        this.pdf.doc.setDrawColor(this.$options.COLORS.HIGHEST);
+        this.pdf.doc.setFillColor(this.$options.COLORS.HIGHEST);
+        this.pdf.doc.roundedRect(x, y + 5, 10, 4, 1.5, 1.5, "FD");
+        this.pdf.doc.text(this.legend[1], x + 12, y + 8);
 
-      this.pdf.doc.setDrawColor(this.$options.COLORS.HIGHEST);
-      this.pdf.doc.setFillColor(this.$options.COLORS.HIGHEST);
-      this.pdf.doc.roundedRect(x, y + 15, 10, 4, 1.5, 1.5, "FD");
-      this.pdf.doc.text(this.legend[3], x + 12, y + 18);
+        this.pdf.doc.setDrawColor(this.$options.COLORS.REST[0]);
+        this.pdf.doc.setFillColor(this.$options.COLORS.REST[0]);
+        this.pdf.doc.roundedRect(x, y + 10, 10, 4, 1.5, 1.5, "FD");
+        this.pdf.doc.text(this.legend[2], x + 12, y + 13);
+      }
     },
     legendStyle(index) {
+      if (this.depthlimit1 === this.depthlimit2) {
+        let result = [
+          `background-color: ${this.$options.COLORS.LDC};`,
+          `background-color: ${this.$options.COLORS.HIGHEST};`
+        ];
+        this.fwData[0].lowerLevels.forEach((e, i) => {
+          result.push(`background-color: ${this.$options.COLORS.REST[i]};`);
+        });
+        return result[index];
+      }
       return [
         `background-color: ${this.$options.COLORS.LDC};`,
         `background-color: ${this.$options.COLORS.HIGHEST};`,
--- a/client/src/components/fairway/AvailableFairwayDepthLNWL.vue	Wed Jul 17 18:47:42 2019 +0200
+++ b/client/src/components/fairway/AvailableFairwayDepthLNWL.vue	Mon Jul 22 15:44:19 2019 +0200
@@ -166,12 +166,18 @@
       const w = [this.widthlimit1, this.widthlimit2].sort();
       const lowerBound = [d[0], w[0]].filter(x => x).join(", ");
       const upperBound = [d[1], w[1]].filter(x => x).join(", ");
-      return [
-        `> LDC`,
-        `< ${lowerBound}`,
-        `< ${upperBound}`,
-        `>= ${upperBound}`
-      ];
+      let result;
+      if (this.depthlimit1 !== this.depthlimit2) {
+        result = [
+          `> LDC`,
+          `< ${lowerBound}`,
+          `< ${upperBound}`,
+          `>= ${upperBound}`
+        ];
+      } else {
+        result = [`> LDC`, `< ${upperBound}`, `>= ${upperBound}`];
+      }
+      return result;
     },
     dataLink() {
       return `data:text/csv;charset=utf-8, ${encodeURIComponent(this.csv)}`;
@@ -206,12 +212,22 @@
   },
   methods: {
     legendStyle(index) {
-      const style = {
-        0: `background-color: ${this.$options.LWNLCOLORS.LDC};`,
-        1: `background-color: ${this.$options.AFDCOLORS[2]};`,
-        2: `background-color: ${this.$options.AFDCOLORS[1]};`,
-        3: `background-color: ${this.$options.AFDCOLORS[0]};`
-      };
+      let style;
+      if (this.depthlimit1 !== this.depthlimit2) {
+        style = {
+          0: `background-color: ${this.$options.LWNLCOLORS.LDC};`,
+          1: `background-color: ${this.$options.AFDCOLORS[2]};`,
+          2: `background-color: ${this.$options.AFDCOLORS[1]};`,
+          3: `background-color: ${this.$options.AFDCOLORS[0]};`
+        };
+      } else {
+        style = {
+          0: `background-color: ${this.$options.LWNLCOLORS.LDC};`,
+          1: `background-color: ${this.$options.AFDCOLORS[2]};`,
+          2: `background-color: ${this.$options.AFDCOLORS[0]};`
+        };
+      }
+
       return style[index];
     },
     applyChange() {
@@ -262,26 +278,44 @@
       if (["bottomright", "bottomleft"].indexOf(position) !== -1) {
         y = this.pdf.height - offset.y - this.getTextHeight(6);
       }
-      this.pdf.doc.setTextColor(color);
-      this.pdf.doc.setDrawColor(this.$options.LWNLCOLORS.LDC);
-      this.pdf.doc.setFillColor(this.$options.LWNLCOLORS.LDC);
-      this.pdf.doc.roundedRect(x, y, 10, 4, 1.5, 1.5, "FD");
-      this.pdf.doc.text(this.legendLNWL[0], x + 12, y + 3);
+      if (this.legendLNWL[3]) {
+        this.pdf.doc.setTextColor(color);
+        this.pdf.doc.setDrawColor(this.$options.LWNLCOLORS.LDC);
+        this.pdf.doc.setFillColor(this.$options.LWNLCOLORS.LDC);
+        this.pdf.doc.roundedRect(x, y, 10, 4, 1.5, 1.5, "FD");
+        this.pdf.doc.text(this.legendLNWL[0], x + 12, y + 3);
+
+        this.pdf.doc.setDrawColor(this.$options.AFDCOLORS[2]);
+        this.pdf.doc.setFillColor(this.$options.AFDCOLORS[2]);
+        this.pdf.doc.roundedRect(x, y + 5, 10, 4, 1.5, 1.5, "FD");
+        this.pdf.doc.text(this.legendLNWL[1], x + 12, y + 8);
+
+        this.pdf.doc.setDrawColor(this.$options.AFDCOLORS[1]);
+        this.pdf.doc.setFillColor(this.$options.AFDCOLORS[1]);
+        this.pdf.doc.roundedRect(x, y + 10, 10, 4, 1.5, 1.5, "FD");
+        this.pdf.doc.text(this.legendLNWL[2], x + 12, y + 13);
 
-      this.pdf.doc.setDrawColor(this.$options.AFDCOLORS[2]);
-      this.pdf.doc.setFillColor(this.$options.AFDCOLORS[2]);
-      this.pdf.doc.roundedRect(x, y + 5, 10, 4, 1.5, 1.5, "FD");
-      this.pdf.doc.text(this.legendLNWL[1], x + 12, y + 8);
+        this.pdf.doc.setDrawColor(this.$options.AFDCOLORS[0]);
+        this.pdf.doc.setFillColor(this.$options.AFDCOLORS[0]);
+        this.pdf.doc.roundedRect(x, y + 15, 10, 4, 1.5, 1.5, "FD");
+        this.pdf.doc.text(this.legendLNWL[3], x + 12, y + 18);
+      } else {
+        this.pdf.doc.setTextColor(color);
+        this.pdf.doc.setDrawColor(this.$options.LWNLCOLORS.LDC);
+        this.pdf.doc.setFillColor(this.$options.LWNLCOLORS.LDC);
+        this.pdf.doc.roundedRect(x, y, 10, 4, 1.5, 1.5, "FD");
+        this.pdf.doc.text(this.legendLNWL[0], x + 12, y + 3);
 
-      this.pdf.doc.setDrawColor(this.$options.AFDCOLORS[1]);
-      this.pdf.doc.setFillColor(this.$options.AFDCOLORS[1]);
-      this.pdf.doc.roundedRect(x, y + 10, 10, 4, 1.5, 1.5, "FD");
-      this.pdf.doc.text(this.legendLNWL[2], x + 12, y + 13);
+        this.pdf.doc.setDrawColor(this.$options.AFDCOLORS[2]);
+        this.pdf.doc.setFillColor(this.$options.AFDCOLORS[2]);
+        this.pdf.doc.roundedRect(x, y + 5, 10, 4, 1.5, 1.5, "FD");
+        this.pdf.doc.text(this.legendLNWL[1], x + 12, y + 8);
 
-      this.pdf.doc.setDrawColor(this.$options.AFDCOLORS[0]);
-      this.pdf.doc.setFillColor(this.$options.AFDCOLORS[0]);
-      this.pdf.doc.roundedRect(x, y + 15, 10, 4, 1.5, 1.5, "FD");
-      this.pdf.doc.text(this.legendLNWL[3], x + 12, y + 18);
+        this.pdf.doc.setDrawColor(this.$options.AFDCOLORS[0]);
+        this.pdf.doc.setFillColor(this.$options.AFDCOLORS[0]);
+        this.pdf.doc.roundedRect(x, y + 10, 10, 4, 1.5, 1.5, "FD");
+        this.pdf.doc.text(this.legendLNWL[2], x + 12, y + 13);
+      }
     },
     close() {
       this.$store.commit("application/paneSetup", "DEFAULT");
--- a/client/src/components/fairway/Fairwayprofile.vue	Wed Jul 17 18:47:42 2019 +0200
+++ b/client/src/components/fairway/Fairwayprofile.vue	Mon Jul 22 15:44:19 2019 +0200
@@ -411,15 +411,17 @@
       svg.attr("height", "100%");
       const width = dimensions.width;
       const height = dimensions.mainHeight;
+      const offsetY = 15;
       const currentData = this.currentData;
       const additionalData = this.additionalData;
       const { xScale, yScaleRight, graph } = this.generateScalesAndGraph({
         svg,
         height,
         width,
-        dimensions
+        dimensions,
+        offsetY
       });
-      this.drawWaterlevel({ graph, xScale, yScaleRight, height });
+      this.drawWaterlevel({ graph, xScale, yScaleRight, height, offsetY });
       this.drawLabels({ graph, dimensions });
       if (currentData) {
         this.drawProfile({
@@ -430,7 +432,8 @@
           height,
           color: GROUND_COLOR,
           strokeColor: "black",
-          opacity: 1
+          opacity: 1,
+          offsetY
         });
       }
       if (additionalData) {
@@ -442,12 +445,13 @@
           height,
           color: GROUND_COLOR,
           strokeColor: "#943007",
-          opacity: 0.6
+          opacity: 0.6,
+          offsetY
         });
       }
-      this.drawFairway({ graph, xScale, yScaleRight });
+      this.drawFairway({ graph, xScale, yScaleRight, offsetY });
     },
-    drawFairway({ graph, xScale, yScaleRight }) {
+    drawFairway({ graph, xScale, yScaleRight, offsetY }) {
       if (this.fairwayData === undefined) {
         return;
       }
@@ -487,7 +491,8 @@
             )
             .attr("stroke-opacity", this.getLayerStyle(data.los).strokeOpacity)
             .attr("stroke-dasharray", this.getLayerStyle(data.los).strokeDash)
-            .attr("d", fairwayArea);
+            .attr("d", fairwayArea)
+            .attr("transform", `translate(0 ${-offsetY})`);
         });
       }
     },
@@ -510,22 +515,18 @@
         .text("Waterlevel [m]");
       graph
         .append("text")
-        .attr("y", 30)
+        .attr("y", 0)
         .attr("x", 0)
         .attr("dy", "1em")
         .attr("fill", "black")
         .style("text-anchor", "middle")
         .attr("transform", [
-          "translate(" +
-            dimensions.width / 2 +
-            "," +
-            dimensions.mainHeight +
-            ")",
+          `translate(${dimensions.width / 2} ${dimensions.mainHeight})`,
           "rotate(0)"
         ])
         .text("Width [m]");
     },
-    generateScalesAndGraph({ svg, height, width, dimensions }) {
+    generateScalesAndGraph({ svg, height, width, dimensions, offsetY }) {
       let xScale = d3
         .scaleLinear()
         .domain([0, this.totalLength])
@@ -568,7 +569,7 @@
         );
       graph
         .append("g")
-        .attr("transform", "translate(0," + height + ")")
+        .attr("transform", `translate(0 ${height - offsetY})`)
         .call(xAxis)
         .selectAll(".tick text")
         .attr("fill", "black")
@@ -579,7 +580,7 @@
         .attr("stroke", "black");
       graph
         .append("g")
-        .attr("transform", "translate(" + width + ",0)")
+        .attr("transform", `translate(${width} ${-offsetY})`)
         .call(yAxisRight)
         .selectAll(".tick text")
         .attr("fill", "black")
@@ -590,7 +591,7 @@
         .attr("stroke", "black");
       graph
         .append("g")
-        .attr("transform", "translate(0 0)")
+        .attr("transform", `translate(0 ${-offsetY})`)
         .call(yAxisLeft)
         .selectAll(".tick text")
         .attr("fill", "black")
@@ -603,7 +604,7 @@
       graph.selectAll(".domain").attr("stroke", "black");
       return { xScale, yScaleRight, graph };
     },
-    drawWaterlevel({ graph, xScale, yScaleRight, height }) {
+    drawWaterlevel({ graph, xScale, yScaleRight, height, offsetY }) {
       let waterArea = d3
         .area()
         .x(function(d) {
@@ -619,7 +620,8 @@
         .attr("fill-opacity", 0.65)
         .attr("fill", WATER_COLOR)
         .attr("stroke", "transparent")
-        .attr("d", waterArea);
+        .attr("d", waterArea)
+        .attr("transform", `translate(0 ${-offsetY})`);
     },
     drawProfile({
       graph,
@@ -629,7 +631,8 @@
       height,
       color,
       strokeColor,
-      opacity
+      opacity,
+      offsetY
     }) {
       for (let part of currentData) {
         let profileLine = d3
@@ -659,7 +662,8 @@
           .attr("fill", color)
           .attr("stroke", "transparent")
           .attr("fill-opacity", opacity)
-          .attr("d", profileArea);
+          .attr("d", profileArea)
+          .attr("transform", `translate(0 ${-offsetY})`);
         graph
           .append("path")
           .datum(part)
@@ -670,7 +674,8 @@
           .attr("stroke-width", 3)
           .attr("stroke-opacity", opacity)
           .attr("fill-opacity", 0)
-          .attr("d", profileLine);
+          .attr("d", profileLine)
+          .attr("transform", `translate(0 ${-offsetY})`);
       }
     },
     scaleFairwayProfile() {
--- a/client/src/components/layers/Layerselect.vue	Wed Jul 17 18:47:42 2019 +0200
+++ b/client/src/components/layers/Layerselect.vue	Mon Jul 22 15:44:19 2019 +0200
@@ -59,6 +59,7 @@
       "differencesLegendImgDataURL"
     ]),
     ...mapGetters("map", ["openLayersMap"]),
+    ...mapState("bottlenecks", ["selectedSurvey"]),
     layer() {
       return this.openLayersMap().getLayer(this.layerId);
     },
@@ -69,6 +70,18 @@
   methods: {
     toggle(map) {
       if (map) {
+        if (
+          (this.layerId === "BOTTLENECKISOLINE" ||
+            this.layerId === "DIFFERENCES") &&
+          !this.selectedSurvey
+        ) {
+          map
+            .getLayer(this.layerId)
+            .getSource()
+            .updateParams({
+              cql_filter: `bottleneck_id='NO_BOTTLENECK_SELECTED'`
+            });
+        }
         map
           .getLayer(this.layerId)
           .setVisible(!map.getLayer(this.layerId).getVisible());
--- a/client/src/components/map/Map.vue	Wed Jul 17 18:47:42 2019 +0200
+++ b/client/src/components/map/Map.vue	Mon Jul 22 15:44:19 2019 +0200
@@ -145,8 +145,8 @@
   },
   methods: {
     updateBottleneckFilter(bottleneck_id, datestr) {
+      if (!bottleneck_id) return;
       const exists = bottleneck_id != "does_not_exist";
-
       if (exists) {
         this.layers
           .get("BOTTLENECKISOLINE")
--- a/client/src/lib/mixins.js	Wed Jul 17 18:47:42 2019 +0200
+++ b/client/src/lib/mixins.js	Mon Jul 22 15:44:19 2019 +0200
@@ -18,6 +18,18 @@
 import locale2 from "locale2";
 import { mapState } from "vuex";
 import { HTTP } from "@/lib/http";
+import * as d3 from "d3";
+
+/*eslint no-unused-vars: ["error", { "varsIgnorePattern": "debugSVG" }]*/
+
+const debugSVG = ({ svg, svgWidth, svgHeight }) => {
+  d3.select(svg)
+    .append("rect")
+    .attr("width", svgWidth)
+    .attr("height", svgHeight)
+    .attr("fill-opacity", 0)
+    .attr("stroke", "#ff0000");
+};
 
 export const sortTable = {
   data() {
@@ -337,10 +349,7 @@
         x = this.pdf.width - offset.x - width;
       }
       if (["bottomright", "bottomleft"].indexOf(position) !== -1) {
-        y =
-          this.pdf.height -
-          offset.y * 0.3 -
-          this.getTextHeight(textLines.length);
+        y = this.pdf.height - offset.y - this.getTextHeight(textLines.length);
       }
       this.pdf.doc.text(textLines, x, y, { baseline: "hanging" });
     },
@@ -468,14 +477,25 @@
         background,
         brcolor
       );
-      this.addText(
-        position,
-        { x: offset.x + padding, y: offset.y + padding * 2 },
-        textWidth,
-        fontSize,
-        color,
-        text
-      );
+      if (["bottomright", "bottomleft"].indexOf(position) !== -1) {
+        this.addText(
+          position,
+          { x: offset.x + padding, y: offset.y - padding / 2 },
+          textWidth,
+          fontSize,
+          color,
+          text
+        );
+      } else {
+        this.addText(
+          position,
+          { x: offset.x + padding, y: offset.y + padding * 2 },
+          textWidth,
+          fontSize,
+          color,
+          text
+        );
+      }
     }
   }
 };
--- a/client/src/store/fairwayavailability.js	Wed Jul 17 18:47:42 2019 +0200
+++ b/client/src/store/fairwayavailability.js	Mon Jul 22 15:44:19 2019 +0200
@@ -102,17 +102,31 @@
 const transformAFD = csv => {
   return csv.map(e => {
     const result = e.split(",");
-    let [label, _, ldc, lower, middle, highestLevel] = result;
-    let levelsWithSum = [
+    let label, _, ldc, lower, middle, highestLevel;
+    let levelsWithSum;
+    if (result.length == 6) {
+      [label, _, ldc, lower, middle, highestLevel] = result;
+      levelsWithSum = [
+        {
+          height: Number(lower),
+          translateY: Number(middle)
+        },
+        {
+          height: Number(middle),
+          translateY: 0
+        }
+      ];
+    } else {
       {
-        height: Number(lower),
-        translateY: Number(middle)
-      },
-      {
-        height: Number(middle),
-        translateY: 0
+        [label, _, ldc, middle, highestLevel] = result;
+        levelsWithSum = [
+          {
+            height: Number(middle),
+            translateY: 0
+          }
+        ];
       }
-    ];
+    }
     return {
       label: label,
       ldc: ldc,
@@ -298,13 +312,25 @@
         data.shift(); // remove header line
         data = data.map(d => {
           let columns = d.split(",");
-          return {
-            date: columns[0],
-            ldc: Number(columns[2]),
-            below: Number(columns[3]),
-            between: Number(columns[4]),
-            above: Number(columns[5])
-          };
+          let result;
+          if (columns.length === 6) {
+            result = {
+              date: columns[0],
+              ldc: Number(columns[2]),
+              below: Number(columns[3]),
+              between: Number(columns[4]),
+              above: Number(columns[5])
+            };
+          } else {
+            result = {
+              date: columns[0],
+              ldc: Number(columns[2]),
+              below: Number(columns[3]),
+              between: null,
+              above: Number(columns[4])
+            };
+          }
+          return result;
         });
         commit("setFwLNWLData", data);
         return data;
--- a/client/src/store/gauges.js	Wed Jul 17 18:47:42 2019 +0200
+++ b/client/src/store/gauges.js	Mon Jul 22 15:44:19 2019 +0200
@@ -13,7 +13,7 @@
  */
 import { HTTP } from "@/lib/http";
 import { WFS } from "ol/format";
-import { isPast } from "date-fns";
+import { isPast, startOfDay, endOfDay, format } from "date-fns";
 
 let dateFrom = new Date();
 dateFrom.setDate(dateFrom.getDate() - 30);
@@ -136,16 +136,12 @@
       });
     },
     loadWaterlevels({ state, commit }) {
-      // include the last day
-      let dateTo = new Date(state.dateTo.getTime() + 86400);
-
       return new Promise((resolve, reject) => {
         HTTP.get(
-          `/data/waterlevels/${
-            state.selectedGaugeISRS
-          }?from=${state.dateFrom
-            .toISOString()
-            .substr(0, 23)}&to=${dateTo.toISOString().substr(0, 23)}`,
+          `/data/waterlevels/${state.selectedGaugeISRS}?from=${format(
+            startOfDay(state.dateFrom),
+            "YYYY-MM-DDTHH:mm:ss.SSS"
+          )}&to=${format(endOfDay(state.dateTo), "YYYY-MM-DDTHH:mm:ss.SSS")}`,
           {
             headers: { "X-Gemma-Auth": localStorage.getItem("token") }
           }
--- a/docker/Dockerfile.spa	Wed Jul 17 18:47:42 2019 +0200
+++ b/docker/Dockerfile.spa	Mon Jul 22 15:44:19 2019 +0200
@@ -7,7 +7,7 @@
 
 RUN apt-get update &&\
     apt-get -y install --no-install-recommends \
-            curl ca-certificates gnupg nodejs make mercurial
+            curl ca-certificates gnupg nodejs make mercurial git
 
 # Install yarn
 RUN curl https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - &&\
--- a/schema/gemma.sql	Wed Jul 17 18:47:42 2019 +0200
+++ b/schema/gemma.sql	Mon Jul 22 15:44:19 2019 +0200
@@ -477,8 +477,11 @@
         date_info timestamp with time zone NOT NULL,
         source_organization varchar NOT NULL, -- "originator" from NtS response
         staging_done boolean NOT NULL DEFAULT false,
-        UNIQUE (location, measure_date, staging_done)
+        UNIQUE (measure_date, location, staging_done)
     )
+    -- For fast retrieval of newest measurement per location:
+    CREATE INDEX gauge_measurements_location_measure_date_desc
+        ON waterway.gauge_measurements (location, measure_date DESC)
 
     CREATE TABLE gauge_predictions (
         location isrs NOT NULL,
@@ -498,7 +501,7 @@
             CHECK (conf_interval @> CAST(water_level AS numeric)),
         date_info timestamp with time zone NOT NULL,
         source_organization varchar NOT NULL, -- "originator" from NtS response
-        PRIMARY KEY (location, measure_date, date_issue)
+        PRIMARY KEY (measure_date, location, date_issue)
     )
 
     CREATE TABLE waterway_axis (
--- a/schema/geoserver_views.sql	Wed Jul 17 18:47:42 2019 +0200
+++ b/schema/geoserver_views.sql	Mon Jul 22 15:44:19 2019 +0200
@@ -94,16 +94,6 @@
     FROM waterway.distance_marks;
 
 CREATE OR REPLACE VIEW waterway.bottlenecks_geoserver AS
-    WITH
-    fairway_availability_latest AS (
-        SELECT DISTINCT ON (bottleneck_id) bottleneck_id, date_info, critical
-            FROM waterway.fairway_availability
-            ORDER BY bottleneck_id, date_info DESC),
-    sounding_result_latest AS (
-        SELECT DISTINCT ON (bottleneck_id) bottleneck_id, max(date_info) AS date_max
-            FROM waterway.sounding_results
-            GROUP BY bottleneck_id
-            ORDER BY bottleneck_id DESC)
     SELECT
         b.id,
         b.bottleneck_id,
@@ -132,9 +122,16 @@
     FROM waterway.bottlenecks b
         LEFT JOIN waterway.gauges_base_view g
             ON b.gauge_location = g.location AND b.gauge_validity = g.validity
-        LEFT JOIN fairway_availability_latest fal
+        LEFT JOIN (SELECT DISTINCT ON (bottleneck_id)
+                    bottleneck_id, date_info, critical
+                FROM waterway.fairway_availability
+                ORDER BY bottleneck_id, date_info DESC) AS fal
             ON b.id = fal.bottleneck_id
-        LEFT JOIN sounding_result_latest srl
+        LEFT JOIN (SELECT DISTINCT ON (bottleneck_id)
+                    bottleneck_id, max(date_info) AS date_max
+                FROM waterway.sounding_results
+                GROUP BY bottleneck_id
+                ORDER BY bottleneck_id DESC) AS srl
             ON b.bottleneck_id = srl.bottleneck_id
     WHERE b.validity @> current_timestamp;
 
--- a/schema/search_functions.sql	Wed Jul 17 18:47:42 2019 +0200
+++ b/schema/search_functions.sql	Mon Jul 22 15:44:19 2019 +0200
@@ -55,6 +55,7 @@
                  'bottleneck' AS type
             FROM waterway.bottlenecks
             WHERE objnam ILIKE '%' || search_string || '%'
+              AND validity @> now()
           ORDER BY name) r;
   RETURN _result;
 END;
--- a/schema/update-db.sh	Wed Jul 17 18:47:42 2019 +0200
+++ b/schema/update-db.sh	Mon Jul 22 15:44:19 2019 +0200
@@ -103,9 +103,9 @@
   if [ -d "$d" ] && [ "$new_ver" -gt $current_ver ] ; then
     echo "Running updates for $new_ver ..."
 
-    sql=$( cat `echo "$d/"* | sort -n` )
-    sql+="INSERT INTO gemma_schema_version(version) VALUES ($new_ver);"
+    psql -1qv ON_ERROR_STOP= -p "$port" -d "$db" \
+      $(find "$d" -type f -printf ' -f %p') \
+      -c "INSERT INTO gemma_schema_version(version) VALUES ($new_ver)"
 
-    psql -1 -q -p "$port" -d "$db" -c "$sql"
   fi
 done
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/schema/updates/1002/01.replace-ctes.sql	Mon Jul 22 15:44:19 2019 +0200
@@ -0,0 +1,1 @@
+\ir ../../geoserver_views.sql
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/schema/updates/1003/01.add-indexes.sql	Mon Jul 22 15:44:19 2019 +0200
@@ -0,0 +1,5 @@
+CREATE INDEX gauge_measurements_measure_date
+    ON waterway.gauge_measurements (measure_date);
+
+CREATE INDEX gauge_predictions_measure_date
+    ON waterway.gauge_predictions (measure_date)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/schema/updates/1004/01.optimize_index-setup.sql	Mon Jul 22 15:44:19 2019 +0200
@@ -0,0 +1,16 @@
+ALTER TABLE waterway.gauge_measurements
+    DROP CONSTRAINT gauge_measurements_location_measure_date_staging_done_key;
+ALTER TABLE waterway.gauge_measurements
+    ADD CONSTRAINT gauge_measurements_measure_date_location_staging_done_key
+    UNIQUE (measure_date, location, staging_done);
+DROP INDEX waterway.gauge_measurements_measure_date;
+
+ALTER TABLE waterway.gauge_predictions
+    DROP CONSTRAINT gauge_predictions_pkey;
+ALTER TABLE waterway.gauge_predictions
+    ADD CONSTRAINT gauge_predictions_pkey
+    PRIMARY KEY (measure_date, location, date_issue);
+DROP INDEX waterway.gauge_predictions_measure_date;
+
+CREATE INDEX gauge_measurements_location_measure_date_desc
+    ON waterway.gauge_measurements (location, measure_date DESC)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/schema/updates/1005/01.search-only-valid-bottleneck.sql	Mon Jul 22 15:44:19 2019 +0200
@@ -0,0 +1,18 @@
+CREATE OR REPLACE FUNCTION search_bottlenecks(search_string text) RETURNS jsonb
+  LANGUAGE plpgsql STABLE PARALLEL SAFE
+  AS $$
+DECLARE
+  _result jsonb;
+BEGIN
+  SELECT COALESCE(json_agg(r),'[]')
+    INTO _result
+    FROM (SELECT objnam AS name,
+                 ST_AsGeoJSON(ST_Envelope(area::geometry))::json AS geom,
+                 'bottleneck' AS type
+            FROM waterway.bottlenecks
+            WHERE objnam ILIKE '%' || search_string || '%'
+              AND validity @> 'now'::timestamptz
+          ORDER BY name) r;
+  RETURN _result;
+END;
+$$;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/schema/updates/1006/01.fix-search-only-valid-bottleneck.sql	Mon Jul 22 15:44:19 2019 +0200
@@ -0,0 +1,18 @@
+CREATE OR REPLACE FUNCTION search_bottlenecks(search_string text) RETURNS jsonb
+  LANGUAGE plpgsql STABLE PARALLEL SAFE
+  AS $$
+DECLARE
+  _result jsonb;
+BEGIN
+  SELECT COALESCE(json_agg(r),'[]')
+    INTO _result
+    FROM (SELECT objnam AS name,
+                 ST_AsGeoJSON(ST_Envelope(area::geometry))::json AS geom,
+                 'bottleneck' AS type
+            FROM waterway.bottlenecks
+            WHERE objnam ILIKE '%' || search_string || '%'
+              AND validity @> now()
+          ORDER BY name) r;
+  RETURN _result;
+END;
+$$;
--- a/schema/version.sql	Wed Jul 17 18:47:42 2019 +0200
+++ b/schema/version.sql	Mon Jul 22 15:44:19 2019 +0200
@@ -1,1 +1,1 @@
-INSERT INTO gemma_schema_version(version) VALUES (1001);
+INSERT INTO gemma_schema_version(version) VALUES (1006);