changeset 4390:acd0cf98fd44 stretches-for-responsibility

Merged default into stretches-for-responsibility branch.
author Sascha Wilde <wilde@intevation.de>
date Thu, 12 Sep 2019 18:15:22 +0200
parents 5e38667f740c (current diff) eca3afe766d7 (diff)
children d9088bb96ce1
files pkg/imports/stsh.go
diffstat 14 files changed, 266 insertions(+), 137 deletions(-) [+]
line wrap: on
line diff
--- a/client/docs/dev-translations.md	Thu Sep 12 18:13:47 2019 +0200
+++ b/client/docs/dev-translations.md	Thu Sep 12 18:15:22 2019 +0200
@@ -26,7 +26,12 @@
 - `gettext` must be called only in javascript part. For html part we use `<translate>` and `<v-translate>` to make sure that `makemessages` marks the strings correctly
 - passing a value with \`\` to `gettext` leads to break up the translation process (e.g. gettext(\` text to translate ${value} \`))
 - passing html element (e.g. `<div>`) to gettext is interpreted as string.
--  The strings to translate have to be included in the source code and not directly in `.po` files.
+- The strings to translate have to be included in the source code and not directly in `.po` files.
+- Check if the development work does not ruin the translation process:
+  Call `make makemessages` --> if `.po` files were generated --> everything is ok.
+  **Notice**: To avoid merge conflicts we push `.po` files into the repository after we synchronize it with the new translations on `weblate`.
+  We do this one time weekly, so you do not have to do this yourself.
+  If it is required to merge the new strings instantly please contact one of the translation managers.
 
 ## Why was gettext chosen?
 
--- a/client/src/components/Contextbox.vue	Thu Sep 12 18:13:47 2019 +0200
+++ b/client/src/components/Contextbox.vue	Thu Sep 12 18:15:22 2019 +0200
@@ -59,6 +59,7 @@
       this.$store.commit("map/mapPopupEnabled", true);
       this.$store.commit("application/searchQuery", "");
       this.$store.commit("application/showContextBox", false);
+      this.$store.commit("map/reviewActive", false);
       this.$store.commit(
         "application/showSearchbar",
         this.showSearchbarLastState
--- a/client/src/components/fairway/AvailableFairwayDepth.vue	Thu Sep 12 18:13:47 2019 +0200
+++ b/client/src/components/fairway/AvailableFairwayDepth.vue	Thu Sep 12 18:15:22 2019 +0200
@@ -100,6 +100,14 @@
   },
   data() {
     return {
+      frequencyD: null,
+      selectedFairwayAvailabilityFeatureD: null,
+      fromDate: null,
+      toDate: null,
+      depthlimit1D: null,
+      depthlimit2D: null,
+      widthlimit1D: null,
+      widthlimit2D: null,
       containerId: "availablefairwaydepth-diagram-container",
       resizeListenerFunction: null,
       loading: false,
@@ -136,7 +144,7 @@
     // not guarantee that the DOM is not only updated but also re-painted on the
     // screen.
     setTimeout(this.drawDiagram, 150);
-
+    this.initDiagramValues();
     this.templates[0] = this.defaultTemplate;
     this.form.template = this.templates[0];
     this.templateData = this.form.template;
@@ -176,12 +184,12 @@
       "widthlimit2"
     ]),
     legend() {
-      const d = [this.depthlimit1, this.depthlimit2].sort();
-      const w = [this.widthlimit1, this.widthlimit2].sort();
+      const d = [this.depthlimit1D, this.depthlimit2D].sort();
+      const w = [this.widthlimit1D, this.widthlimit2D].sort();
       const lowerBound = [d[0], w[0]].filter(x => x).join(", ");
       const upperBound = [d[1], w[1]].filter(x => x).join(", ");
       let result;
-      if (this.depthlimit1 !== this.depthlimit2) {
+      if (this.depthlimit1D !== this._depthlimit2D) {
         result = [
           `> LDC`,
           `>= ${upperBound}`,
@@ -197,42 +205,49 @@
       return `data:text/csv;charset=utf-8, ${encodeURIComponent(this.csv)}`;
     },
     csvFileName() {
+      if (!this.frequencyD) return;
       return `${this.$gettext("fairwayavailability")}-${
         this.featureName
       }-${filters.surveyDate(this.fromDate)}-${filters.surveyDate(
         this.toDate
-      )}-${this.$gettext(this.frequency)}-.csv`;
+      )}-${this.$gettext(this.frequencyD)}-.csv`;
     },
     frequencyToRange() {
+      if (!this.frequencyD) return;
       const frequencies = {
         [FREQUENCIES.MONTHLY]: [-33, 33],
         [FREQUENCIES.QUARTERLY]: [-93, 93],
         [FREQUENCIES.YEARLY]: [-370, 370]
       };
-      return frequencies[this.frequency];
-    },
-    fromDate() {
-      return this.from;
-    },
-    toDate() {
-      return this.to;
+      return frequencies[this.frequencyD];
     },
     availability() {
       return this.plainAvailability;
     },
     title() {
+      if (!this.frequencyD) return;
       return `Available Fairway Depth: ${
         this.featureName
       } (${filters.surveyDate(this.fromDate)} - ${filters.surveyDate(
         this.toDate
-      )}) ${this.$gettext(this.frequency)}`;
+      )}) ${this.$gettext(this.frequencyD)}`;
     },
     featureName() {
-      if (this.selectedFairwayAvailabilityFeature == null) return "";
-      return this.selectedFairwayAvailabilityFeature.properties.name;
+      if (this.selectedFairwayAvailabilityFeatureD == null) return "";
+      return this.selectedFairwayAvailabilityFeatureD.properties.name;
     }
   },
   methods: {
+    initDiagramValues() {
+      this.selectedFairwayAvailabilityFeatureD = this.selectedFairwayAvailabilityFeature;
+      this.fromDate = this.from;
+      this.toDate = this.to;
+      this.depthlimit1D = this.depthlimit1;
+      this.depthlimit2D = this.depthlimit2;
+      this.widthlimit1D = this.widthlimit1;
+      this.widthlimit2D = this.widthlimit2;
+      this.frequencyD = this.frequency;
+    },
     applyChange() {
       if (this.form.template.hasOwnProperty("properties")) {
         this.templateData = this.defaultTemplate;
@@ -322,7 +337,7 @@
       }
     },
     legendStyle(index) {
-      if (this.depthlimit1 === this.depthlimit2) {
+      if (this._depthlimit1 === this._depthlimit2) {
         let result = [
           `background-color: ${this.$options.COLORS.LDC};`,
           `background-color: ${this.$options.COLORS.HIGHEST};`
@@ -702,6 +717,7 @@
   },
   watch: {
     fwData() {
+      this.initDiagramValues();
       this.drawDiagram();
     },
     showNumbers() {
--- a/client/src/components/fairway/AvailableFairwayDepthLNWL.vue	Thu Sep 12 18:13:47 2019 +0200
+++ b/client/src/components/fairway/AvailableFairwayDepthLNWL.vue	Thu Sep 12 18:15:22 2019 +0200
@@ -99,6 +99,14 @@
   },
   data() {
     return {
+      frequencyD: null,
+      selectedFairwayAvailabilityFeatureD: null,
+      fromDate: null,
+      toDate: null,
+      depthlimit1D: null,
+      depthlimit2D: null,
+      widthlimit1D: null,
+      widthlimit2D: null,
       containerId: "availablefairwaydepthlnwl-diagram-container",
       resizeListenerFunction: null,
       loading: false,
@@ -135,7 +143,7 @@
     // not guarantee that the DOM is not only updated but also re-painted on the
     // screen.
     setTimeout(this.drawDiagram, 150);
-
+    this.initDiagramValues();
     this.templates[0] = this.defaultTemplate;
     this.form.template = this.templates[0];
     this.templateData = this.form.template;
@@ -175,12 +183,12 @@
       "widthlimit2"
     ]),
     legendLNWL() {
-      const d = [this.depthlimit1, this.depthlimit2].sort();
-      const w = [this.widthlimit1, this.widthlimit2].sort();
+      const d = [this.depthlimit1D, this.depthlimit2D].sort();
+      const w = [this.widthlimit1D, this.widthlimit2D].sort();
       const lowerBound = [d[0], w[0]].filter(x => x).join(", ");
       const upperBound = [d[1], w[1]].filter(x => x).join(", ");
       let result;
-      if (this.depthlimit1 !== this.depthlimit2) {
+      if (this.depthlimit1D !== this.depthlimit2D) {
         result = [
           `> LDC`,
           `< ${lowerBound}`,
@@ -196,34 +204,40 @@
       return `data:text/csv;charset=utf-8, ${encodeURIComponent(this.csv)}`;
     },
     csvFileName() {
+      if (!this.frequencyD) return;
       return `${this.$gettext("fairwayavailabilityLNWL")}-${
         this.featureName
       }-${filters.surveyDate(this.fromDate)}-${filters.surveyDate(
         this.toDate
-      )}-${this.$gettext(this.frequency)}-.csv`;
-    },
-    fromDate() {
-      return this.from;
-    },
-    toDate() {
-      return this.to;
+      )}-${this.$gettext(this.frequencyD)}-.csv`;
     },
     availability() {
       return this.plainAvailability;
     },
     title() {
+      if (!this.frequencyD) return;
       return `Available Fairway Depth vs LNWL: ${
         this.featureName
       } (${filters.surveyDate(this.fromDate)} - ${filters.surveyDate(
         this.toDate
-      )}) ${this.$gettext(this.frequency)}`;
+      )}) ${this.$gettext(this.frequencyD)}`;
     },
     featureName() {
-      if (this.selectedFairwayAvailabilityFeature == null) return "";
-      return this.selectedFairwayAvailabilityFeature.properties.name;
+      if (this.selectedFairwayAvailabilityFeatureD == null) return "";
+      return this.selectedFairwayAvailabilityFeatureD.properties.name;
     }
   },
   methods: {
+    initDiagramValues() {
+      this.selectedFairwayAvailabilityFeatureD = this.selectedFairwayAvailabilityFeature;
+      this.fromDate = this.from;
+      this.toDate = this.to;
+      this.depthlimit1D = this.depthlimit1;
+      this.depthlimit2D = this.depthlimit2;
+      this.widthlimit1D = this.widthlimit1;
+      this.widthlimit2D = this.widthlimit2;
+      this.frequencyD = this.frequency;
+    },
     legendStyle(index) {
       let style;
       if (this.depthlimit1 !== this.depthlimit2) {
@@ -645,6 +659,7 @@
   },
   watch: {
     fwLNWLData() {
+      this.initDiagramValues();
       this.drawDiagram();
     },
     showNumbers() {
--- a/client/src/components/gauge/HydrologicalConditions.vue	Thu Sep 12 18:13:47 2019 +0200
+++ b/client/src/components/gauge/HydrologicalConditions.vue	Thu Sep 12 18:15:22 2019 +0200
@@ -12,7 +12,7 @@
           <span
             style="background-color: red; width: 20px; height: 20px;"
           ></span>
-          {{ yearCompare }}
+          {{ yearCompareD }}
         </div>
         <div class="legend">
           <span
@@ -116,6 +116,9 @@
   },
   data() {
     return {
+      selectedGaugeD: null,
+      longtermIntervalD: null,
+      yearCompareD: null,
       zoomStore: null,
       containerId: "hydrologicalconditions-diagram-container",
       resizeListenerFunction: null,
@@ -143,19 +146,22 @@
     ]),
     ...mapGetters("gauges", ["selectedGauge"]),
     title() {
-      return `${this.selectedGauge.properties.objname}: ${this.$gettext(
+      if (!this.selectedGaugeD || !this.longtermIntervalD) return;
+      return `${this.selectedGaugeD.properties.objname}: ${this.$gettext(
         "Hydrological Conditions"
-      )} (${this.longtermInterval.join(" - ")})`;
+      )} (${this.longtermIntervalD.join(" - ")})`;
     },
     csvLink() {
       return "data:text/csv;charset=utf-8," + encodeURIComponent(this.csvData);
     },
     csvFileName() {
+      if (!this.selectedGaugeD || !this.longtermIntervalD) return;
       return `${this.$gettext("hydrological-conditions")}-${
-        this.selectedGauge.properties.objname
-      }-${this.longtermInterval.join(" - ")}.csv`;
+        this.selectedGaugeD.properties.objname
+      }-${this.longtermIntervalD.join(" - ")}.csv`;
     },
     csvData() {
+      if (!this.longtermIntervalD || !this.yearCompareD) return;
       // We cannot directly use the csv data provided by the backend because the
       // diagram uses data from two endpoints, longterm- and yearWaterlevels.
       // So we need to merge them here to have them in one csv export.
@@ -165,31 +171,41 @@
           let yearData = this.yearWaterlevels.find(y => {
             return d.date.getTime() === y.date.getTime();
           });
-          d[this.yearCompare] = yearData ? yearData.mean : "";
+          d[this.yearCompareD] = yearData ? yearData.mean : "";
           return `${d.date.getMonth() + 1}-${d.date.getDate()};${d.min};${
             d.max
-          };${d.mean};${d.median};${d.q25};${d.q75};${d[this.yearCompare]}`;
+          };${d.mean};${d.median};${d.q25};${d.q75};${d[this.yearCompareD]}`;
         })
         .join("\n");
-      return `#Interval: ${this.longtermInterval.join(
+      return `#Interval: ${this.longtermIntervalD.join(
         " - "
       )}\n#date;#min;#max;#mean;#median;#q25;#q75;#${
-        this.yearCompare
+        this.yearCompareD
       }\n${merged}`;
     }
   },
   watch: {
     paneSetup() {
-      this.$nextTick(() => this.drawDiagram());
+      this.$nextTick(() => {
+        this.initialDiagramValues();
+        this.drawDiagram();
+      });
     },
     longtermWaterlevels() {
+      this.initialDiagramValues();
       this.drawDiagram();
     },
     yearWaterlevels() {
+      this.initialDiagramValues();
       this.drawDiagram();
     }
   },
   methods: {
+    initialDiagramValues() {
+      this.selectedGaugeD = this.selectedGauge;
+      this.longtermIntervalD = this.longtermInterval;
+      this.yearCompareD = this.yearCompare;
+    },
     close() {
       this.$store.commit(
         "application/paneSetup",
@@ -200,9 +216,9 @@
     },
     downloadPDF() {
       let diagramTitle =
-        this.gaugeInfo(this.selectedGauge) +
+        this.gaugeInfo(this.selectedGaugeD) +
         ": Hydrological Conditions " +
-        this.longtermInterval.join(" - ");
+        this.longtermIntervalD.join(" - ");
 
       this.generatePDF({
         templateData: this.templateData,
@@ -210,7 +226,7 @@
       });
 
       this.pdf.doc.save(
-        this.selectedGauge.properties.objname +
+        this.selectedGaugeD.properties.objname +
           " Hydrological-condition Diagram.pdf"
       );
     },
@@ -242,6 +258,7 @@
     },
     // Diagram legend
     addDiagramLegend(position, offset, color) {
+      if (!this.yearCompareD) return;
       let x = offset.x + 2, // 2 is the radius of the circle
         y = offset.y,
         padding = 3;
@@ -263,7 +280,7 @@
       this.pdf.doc.setDrawColor("white");
       this.pdf.doc.setFillColor("red");
       this.pdf.doc.circle(x, y, 2, "FD");
-      this.pdf.doc.text(x + padding, y + 1, "" + this.yearCompare);
+      this.pdf.doc.text(x + padding, y + 1, "" + this.yearCompareD);
       this.pdf.doc.setFillColor("orange");
       this.pdf.doc.circle(x, y + 5, 2, "FD");
       this.pdf.doc.text(x + padding, y + 6, "Q25%");
@@ -297,7 +314,7 @@
       // remove old diagram
       d3.select("#" + this.containerId + " svg").remove();
       const el = document.querySelector("#" + this.containerId);
-      if (!this.selectedGauge || !this.longtermWaterlevels.length || !el)
+      if (!this.selectedGaugeD || !this.longtermWaterlevels.length || !el)
         return;
       const svgWidth = el.clientWidth;
       const svgHeight = el.clientHeight;
@@ -316,7 +333,7 @@
 
       // HDC/LDC/MW for the selected gauge
       const refWaterLevels = JSON.parse(
-        this.selectedGauge.properties.reference_water_levels
+        this.selectedGaugeD.properties.reference_water_levels
       );
 
       // dimensions (widths, heights, margins)
@@ -962,6 +979,7 @@
             ywl => ywl.date.getTime() === d.date.getTime()
           );
           if (dYear) {
+            if (!this.yearCompareD) return;
             tooltipText
               .append("tspan")
               .attr("x", 0)
@@ -969,7 +987,7 @@
               .attr("dy", "7.4em")
               .attr("dominant-baseline", "hanging")
               .attr("text-anchor", "middle")
-              .text(`${this.yearCompare}: ${dYear.mean.toFixed(1)} cm`);
+              .text(`${this.yearCompareD}: ${dYear.mean.toFixed(1)} cm`);
           }
 
           // get text dimensions
@@ -1016,7 +1034,7 @@
     // not guarantee that the DOM is not only updated but also re-painted on the
     // screen.
     setTimeout(this.drawDiagram, 150);
-
+    this.initialDiagramValues();
     this.templates[0] = this.defaultTemplate;
     this.form.template = this.templates[0];
     this.templateData = this.form.template;
--- a/client/src/components/gauge/Waterlevel.vue	Thu Sep 12 18:13:47 2019 +0200
+++ b/client/src/components/gauge/Waterlevel.vue	Thu Sep 12 18:15:22 2019 +0200
@@ -70,6 +70,17 @@
           </button>
           -->
         </div>
+        <div class="btn-group-toggle w-100 mt-2">
+          <label
+            class="btn btn-outline-secondary btn-sm"
+            :class="{ active: showNSC }"
+            ><input
+              type="checkbox"
+              v-model="showNSC"
+              autocomplete="off"
+            />Nash-Sutcliffe
+          </label>
+        </div>
       </DiagramLegend>
       <div
         class="d-flex flex-fill justify-content-center align-items-center"
@@ -114,6 +125,8 @@
 // d3-line-chunked plugin: https://github.com/pbeshai/d3-line-chunked
 const d3 = Object.assign(d3Base, { lineChunked });
 
+let temp = null;
+
 export default {
   mixins: [diagram, pdfgen, templateLoader, refwaterlevels],
   components: {
@@ -121,6 +134,9 @@
   },
   data() {
     return {
+      dateFromD: null,
+      dateToD: null,
+      selectedGaugeD: null,
       containerId: "waterlevel-diagram-container",
       resizeListenerFunction: null,
       form: {
@@ -134,32 +150,25 @@
         height: 297
       },
       templateData: null,
-      zoomStore: null
+      zoomStore: null,
+      showNSC: true
     };
   },
   computed: {
     ...mapState("application", ["paneSetup"]),
     ...mapState("gauges", [
       "dateFrom",
+      "dateTo",
       "waterlevels",
       "waterlevelsCSV",
       "nashSutcliffe"
     ]),
     ...mapGetters("gauges", ["selectedGauge"]),
     title() {
-      return `${this.selectedGauge.properties.objname}: ${this.$gettext(
+      if (!this.selectedGaugeD) return;
+      return `${this.selectedGaugeD.properties.objname}: ${this.$gettext(
         "Waterlevel"
-      )} (${this.dateFrom.toLocaleDateString()} - ${this.dateTo.toLocaleDateString()})`;
-    },
-    dateFrom: {
-      get() {
-        return this.$store.state.gauges.dateFrom;
-      }
-    },
-    dateTo: {
-      get() {
-        return this.$store.state.gauges.dateTo;
-      }
+      )} (${this.dateFromD.toLocaleDateString()} - ${this.dateToD.toLocaleDateString()})`;
     },
     csvLink() {
       return (
@@ -169,8 +178,8 @@
     csvFileName() {
       return `${this.$gettext("waterlevels")}-${
         this.selectedGauge.properties.objname
-      }-${this.dateFrom.toISOString().split("T")[0]}-${
-        this.dateTo.toISOString().split("T")[0]
+      }-${this.dateFromD.toISOString().split("T")[0]}-${
+        this.dateToD.toISOString().split("T")[0]
       }.csv`;
     },
     hasPredictions() {
@@ -178,14 +187,23 @@
     }
   },
   watch: {
+    showNSC() {
+      this.drawDiagram({ ...this.zoomStore });
+    },
     paneSetup() {
       this.$nextTick(() => this.drawDiagram());
     },
     waterlevels() {
+      this.initialDiagramValues();
       this.drawDiagram();
     }
   },
   methods: {
+    initialDiagramValues() {
+      this.dateFromD = this.dateFrom;
+      this.dateToD = this.dateTo;
+      this.selectedGaugeD = this.selectedGauge;
+    },
     close() {
       this.$store.commit(
         "application/paneSetup",
@@ -206,9 +224,9 @@
       let diagramTitle =
         this.gaugeInfo(this.selectedGauge) +
         ": Waterlevel " +
-        this.dateFrom.toLocaleDateString() +
+        this.dateFromD.toLocaleDateString() +
         " - " +
-        this.dateTo.toLocaleDateString();
+        this.dateToD.toLocaleDateString();
       this.generatePDF({
         templateData: this.templateData,
         diagramTitle: diagramTitle
@@ -291,7 +309,7 @@
         }
       };
     },
-    drawDiagram() {
+    drawDiagram(zoom) {
       // remove old diagram and exit if necessary data is missing
       d3.select("#" + this.containerId + " svg").remove();
       const elem = document.querySelector("#" + this.containerId);
@@ -305,7 +323,8 @@
           svgWidth: svgWidth,
           svgHeight: svgHeight,
           ...layout
-        })
+        }),
+        zoomLevel: zoom ? zoom : null
       });
     },
     renderTo({ element, dimensions, zoomLevel }) {
@@ -398,16 +417,17 @@
       // static, don't need updater
       this.drawNavigationChart({ scale, navigation });
       this.drawRefLines({ refWaterLevels, diagram, scale, dimensions, extent });
-
-      updaters.push(
-        this.drawNashSutcliffe({ hours: 72, diagram, scale, dimensions })
-      );
-      updaters.push(
-        this.drawNashSutcliffe({ hours: 48, diagram, scale, dimensions })
-      );
-      updaters.push(
-        this.drawNashSutcliffe({ hours: 24, diagram, scale, dimensions })
-      );
+      if (this.showNSC) {
+        updaters.push(
+          this.drawNashSutcliffe({ hours: 72, diagram, scale, dimensions })
+        );
+        updaters.push(
+          this.drawNashSutcliffe({ hours: 48, diagram, scale, dimensions })
+        );
+        updaters.push(
+          this.drawNashSutcliffe({ hours: 24, diagram, scale, dimensions })
+        );
+      }
 
       // INTERACTIONS
 
@@ -955,14 +975,19 @@
           .call(brush.move, scale.x.range().map(t.invertX, t));
       };
       zoom.on("zoom", () => {
-        if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush")
+        if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") {
           return; // ignore zoom-by-brush
+        }
         let t = d3.event.transform;
+        // set the zoom to the passed zoom level.
         if (zoomLevel) {
-          const tx = (zoomLevel.x * dimensions.width) / zoomLevel.width;
-          const k = zoomLevel.k;
-          const ty = zoomLevel.y;
+          let tx = (zoomLevel.x * dimensions.width) / zoomLevel.width;
+          let k = zoomLevel.k;
+          let ty = zoomLevel.y;
           t = d3.zoomIdentity.translate(tx, ty).scale(k);
+          zoomLevel = null; // avoid to stuck at same zoom level after setting the zoom by subsequent zooming.
+        } else {
+          temp = { ...d3.event.transform, width: dimensions.width };
         }
         scaleForZoom(t);
       });
@@ -971,11 +996,13 @@
         svg.select(".chart-tooltip").style("opacity", 0);
       });
       // store the zoom level after zomming is ended
-      if (!zoomLevel) {
-        zoom.on("end", () => {
-          this.zoomStore = { ...d3.event.transform, width: dimensions.width };
-        });
-      }
+      zoom.on("end", () => {
+        if (!zoomLevel) {
+          this.zoomStore = temp
+            ? temp
+            : { ...d3.event.transform, width: dimensions.width };
+        }
+      });
 
       navigation
         .append("g")
@@ -1154,7 +1181,7 @@
     // not guarantee that the DOM is not only updated but also re-painted on the
     // screen.
     setTimeout(this.drawDiagram, 150);
-
+    this.initialDiagramValues();
     this.templates[0] = this.defaultTemplate;
     this.form.template = this.templates[0];
     this.templateData = this.form.template;
--- a/client/src/components/importoverview/FairwayDimensionDetail.vue	Thu Sep 12 18:13:47 2019 +0200
+++ b/client/src/components/importoverview/FairwayDimensionDetail.vue	Thu Sep 12 18:15:22 2019 +0200
@@ -26,14 +26,14 @@
  * Author(s):
  * Thomas Junk <thomas.junk@intevation.de>
  */
-import { HTTP } from "@/lib/http";
-import { WFS } from "ol/format";
 import { or as orFilter, equalTo as equalToFilter } from "ol/format/filter";
 //import { displayError } from "@/lib/errors";
 import { mapGetters } from "vuex";
-import Feature from "ol/Feature";
-import Polygon from "ol/geom/Polygon";
-import { transform } from "ol/proj";
+import VectorSource from "ol/source/Vector";
+import { buildVectorLoader } from "@/components/map/layers.js";
+import { bbox as bboxStrategy } from "ol/loadingstrategy";
+import { WFS } from "ol/format";
+import { HTTP } from "@/lib/http";
 
 const getFromWFS = (type, filter) => {
   return new Promise((resolve, reject) => {
@@ -76,33 +76,31 @@
     const ids = this.fairWayDimensionIDs.map(id => {
       return equalToFilter("id", id);
     });
-    getFromWFS("fairway_dimensions", orFilter(...ids)).then(response => {
-      let { features } = response.data;
-      if (features.length < 0) {
-        const {
-          level_of_service,
-          source_organization
-        } = features[0].properties;
-        this.LOS = level_of_service;
-        this.organization = source_organization;
-        const fairwaydimensionLayer = this.openLayersMap().getLayer(
-          "FDREVIEWLAYER"
-        );
-        features = features.map(f => {
-          let result = new Feature({
-            geometry: new Polygon([
-              f.geometry.coordinates[0].map(c =>
-                transform(c, "EPSG:4326", "EPSG:3857")
-              )
-            ])
-          });
-          result.setId(f.id);
-          return result;
-        });
-        this.$store.commit("map/reviewActive", true);
-        fairwaydimensionLayer.setVisible(true);
-        fairwaydimensionLayer.getSource().addFeatures(features);
-      }
+    const fairwaydimensionLayer = this.openLayersMap().getLayer(
+      "FDREVIEWLAYER"
+    );
+    const source = new VectorSource({ strategy: bboxStrategy });
+    this.$store.commit("map/reviewActive", true);
+    fairwaydimensionLayer.setVisible(true);
+    source.setLoader(
+      buildVectorLoader(
+        {
+          geometryName: "area",
+          featureTypes: ["fairway_dimensions"],
+          filter: orFilter(...ids)
+        },
+        source,
+        false
+      )
+    );
+    fairwaydimensionLayer.setSource(source);
+    getFromWFS("fairway_dimensions", ids[0]).then(response => {
+      const {
+        level_of_service,
+        source_organization
+      } = response.data.features[0].properties;
+      this.LOS = level_of_service;
+      this.organization = source_organization;
     });
   },
   computed: {
--- a/client/src/components/importoverview/ImportOverview.vue	Thu Sep 12 18:13:47 2019 +0200
+++ b/client/src/components/importoverview/ImportOverview.vue	Thu Sep 12 18:15:22 2019 +0200
@@ -419,6 +419,7 @@
                 this.$store.dispatch("imports/loadStagingNotifications");
                 this.$store.dispatch("imports/loadStretches");
                 this.$store.dispatch("imports/loadSections");
+                this.$store.commit("map/reviewActive", false);
                 const messages = response.data
                   .map(x => {
                     if (x.message) return x.message;
--- a/client/src/components/layers/Layers.vue	Thu Sep 12 18:13:47 2019 +0200
+++ b/client/src/components/layers/Layers.vue	Thu Sep 12 18:15:22 2019 +0200
@@ -59,7 +59,7 @@
  * Thomas Junk <thomas.junk@intevation.de>
  * Markus Kottländer <markus.kottlaender@intevation.de>
  */
-import { mapState } from "vuex";
+import { mapState, mapGetters } from "vuex";
 
 export default {
   components: {
@@ -68,6 +68,7 @@
   computed: {
     ...mapState("application", ["showLayers"]),
     ...mapState("map", ["openLayersMaps", "reviewActive"]),
+    ...mapGetters("map", ["openLayersMap"]),
     label() {
       return this.$gettext("Map Layers");
     },
@@ -82,6 +83,16 @@
       return counter;
     }
   },
+  watch: {
+    reviewActive() {
+      if (!this.reviewActive) {
+        const fairwaydimensionLayer = this.openLayersMap().getLayer(
+          "FDREVIEWLAYER"
+        );
+        fairwaydimensionLayer.setVisible(false);
+      }
+    }
+  },
   methods: {
     close() {
       this.$store.commit("application/showLayers", false);
--- a/client/src/components/map/layers.js	Thu Sep 12 18:13:47 2019 +0200
+++ b/client/src/components/map/layers.js	Thu Sep 12 18:15:22 2019 +0200
@@ -11,12 +11,12 @@
 import { bbox as bboxStrategy } from "ol/loadingstrategy";
 import { WFS, GeoJSON } from "ol/format";
 import OSM from "ol/source/OSM";
-import { equalTo } from "ol/format/filter";
+import { equalTo, and as andFilter } from "ol/format/filter";
 import { HTTP } from "@/lib/http";
 import { styleFactory } from "./styles";
 import store from "@/store/index";
 
-const buildVectorLoader = (
+export const buildVectorLoader = (
   featureRequestOptions,
   vectorSource,
   bboxStrategyDisabled,
@@ -331,7 +331,10 @@
               {
                 geometryName: "area",
                 featureTypes: ["fairway_dimensions"],
-                filter: equalTo("level_of_service", 1)
+                filter: andFilter(
+                  equalTo("level_of_service", 1),
+                  equalTo("staging_done", true)
+                )
               },
               source,
               false
@@ -354,7 +357,10 @@
               {
                 geometryName: "area",
                 featureTypes: ["fairway_dimensions"],
-                filter: equalTo("level_of_service", 2)
+                filter: andFilter(
+                  equalTo("level_of_service", 2),
+                  equalTo("staging_done", true)
+                )
               },
               source,
               false
@@ -377,7 +383,10 @@
               {
                 geometryName: "area",
                 featureTypes: ["fairway_dimensions"],
-                filter: equalTo("level_of_service", 3)
+                filter: andFilter(
+                  equalTo("level_of_service", 3),
+                  equalTo("staging_done", true)
+                )
               },
               source,
               false
--- a/client/src/components/map/styles.js	Thu Sep 12 18:13:47 2019 +0200
+++ b/client/src/components/map/styles.js	Thu Sep 12 18:15:22 2019 +0200
@@ -221,20 +221,24 @@
       }
       if (feature.get("fa_critical") && feature.get("fa_data")) {
         let data = feature.get("fa_data");
-        const heightInPixel = 82;
+        const heightInPixel = 80;
         const relativeHeightInPercent = heightInPixel / 100;
         let lnwlHeight = relativeHeightInPercent * data.ldc;
         let belowThresholdHeight = relativeHeightInPercent * data.below;
         let betweenThresholdHeight = relativeHeightInPercent * data.between;
         let aboveThresholdHeight = relativeHeightInPercent * data.above;
-        let lnwl = `<rect x='2' y='${heightInPixel -
+        let lnwl = `<rect x='2' y='${2 +
+          heightInPixel -
           lnwlHeight}' width='10' height='${lnwlHeight}' stroke-width='0' fill='aqua'/>`;
-        let above = `<rect x='12' y='${heightInPixel -
+        let above = `<rect x='12' y='${2 +
+          heightInPixel -
           aboveThresholdHeight}' width='18' height='${aboveThresholdHeight}' stroke-width='0' fill='blue'/>`;
-        let between = `<rect x='12' y='${heightInPixel -
+        let between = `<rect x='12' y='${2 +
+          heightInPixel -
           aboveThresholdHeight -
           betweenThresholdHeight}' width='18' height='${betweenThresholdHeight}' stroke-width='0' fill='darksalmon'/>`;
-        let below = `<rect x='12' y='${heightInPixel -
+        let below = `<rect x='12' y='${2 +
+          heightInPixel -
           aboveThresholdHeight -
           betweenThresholdHeight -
           belowThresholdHeight}' width='18' height='${belowThresholdHeight}' stroke-width='0' fill='hotpink'/>`;
--- a/client/src/components/systemconfiguration/MorphologyClassbreaks.vue	Thu Sep 12 18:13:47 2019 +0200
+++ b/client/src/components/systemconfiguration/MorphologyClassbreaks.vue	Thu Sep 12 18:15:22 2019 +0200
@@ -104,6 +104,9 @@
       <span class="text-danger" v-if="!checkClassbreaks">
         {{ validationMessage }}
       </span>
+      <span class="text-secondary fix-trans-space" v-translate>
+        Changes need a map reload. Consider informing your users.
+      </span>
     </div>
     <div class="px-3">
       <h6 class="font-weight-bold">
@@ -211,6 +214,10 @@
       <span class="text-danger" v-if="!checkClassbreaksCompare">
         {{ validationMessageForCompare }}
       </span>
+      <span class="text-secondary fix-trans-space" v-translate>
+        Colour changes need a map reload. Value changes need a de- and re-select
+        of a difference calculation. Inform your users!
+      </span>
     </div>
   </div>
 </template>
--- a/client/src/lib/session.js	Thu Sep 12 18:13:47 2019 +0200
+++ b/client/src/lib/session.js	Thu Sep 12 18:15:22 2019 +0200
@@ -14,13 +14,30 @@
 
 import app from "@/main";
 import { unsetLayerConfigs } from "@/components/map/layers";
+import { HTTP } from "@/lib/http";
+import { displayError } from "@/lib/errors";
 
 const logOff = () => {
-  app.$snotify.clear();
-  app.$store.commit("reset");
-  app.$store.commit("user/clearAuth");
-  app.$router.push("/login");
-  unsetLayerConfigs();
+  HTTP.get("/logout", {
+    headers: {
+      "X-Gemma-Auth": localStorage.getItem("token"),
+      "Content-type": "text/xml; charset=UTF-8"
+    }
+  })
+    .then(() => {
+      app.$snotify.clear();
+      app.$store.commit("reset");
+      app.$store.commit("user/clearAuth");
+      app.$router.push("/login");
+      unsetLayerConfigs();
+    })
+    .catch(error => {
+      const { status, data } = error.response;
+      displayError({
+        title: this.$gettext("Backend Error"),
+        message: `${status}: ${data.message || data}`
+      });
+    });
 };
 
 /**
--- a/pkg/imports/stsh.go	Thu Sep 12 18:13:47 2019 +0200
+++ b/pkg/imports/stsh.go	Thu Sep 12 18:15:22 2019 +0200
@@ -319,7 +319,7 @@
 
 		// Store the countries
 		for _, country := range countries {
-			if _, err := insCountryStmt.ExecContext(ctx, country); err != nil {
+			if _, err := insCountryStmt.ExecContext(ctx, id, country); err != nil {
 				return nil, err
 			}
 		}