changeset 1064:907321455f39 crossprofile

merge with default
author Thomas Junk <thomas.junk@intevation.de>
date Fri, 26 Oct 2018 08:45:46 +0200
parents 51d412a79e4f (current diff) 7ec2133c6404 (diff)
children cdee23f3ee4c
files client/src/application/Main.vue client/src/map/Maplayer.vue client/src/morphtool/Morphtool.vue
diffstat 12 files changed, 283 insertions(+), 85 deletions(-) [+]
line wrap: on
line diff
--- a/client/src/App.vue	Thu Oct 25 17:19:50 2018 +0200
+++ b/client/src/App.vue	Fri Oct 26 08:45:46 2018 +0200
@@ -7,6 +7,7 @@
             <div class="midcontainer d-flex flex-row">
                 <div class="leftcontainer">
                     <Sidebar></Sidebar>
+                    <Bottlenecks></Bottlenecks>
                 </div>
                 <div class="middle">
 
@@ -99,8 +100,10 @@
  * 
  * Author(s):
  * Thomas Junk <thomas.junk@intevation.de>
+ * Markus Kottländer <markus.kottlaender@intevation.de>
  */
 import Sidebar from "./application/Sidebar";
+import Bottlenecks from "./bottlenecks/Bottlenecks";
 import Topbar from "./application/Topbar";
 import { mapGetters } from "vuex";
 import Userbar from "./application/Userbar";
@@ -119,6 +122,7 @@
   },
   components: {
     Sidebar,
+    Bottlenecks,
     Topbar,
     Userbar,
     Linetool,
--- a/client/src/application/Main.vue	Thu Oct 25 17:19:50 2018 +0200
+++ b/client/src/application/Main.vue	Fri Oct 26 08:45:46 2018 +0200
@@ -1,7 +1,6 @@
 <template>
     <div class="main d-flex flex-column">
         <Maplayer
-            :drawMode="drawMode"
             :split="isSplitscreen"
             :lat="6155376"
             :long="1819178"
@@ -68,7 +67,7 @@
     };
   },
   computed: {
-    ...mapGetters("application", ["isSplitscreen", "drawMode"]),
+    ...mapGetters("application", ["isSplitscreen"]),
     ...mapState("fairwayprofile", [
       "currentProfile",
       "minAlt",
--- a/client/src/application/Sidebar.vue	Thu Oct 25 17:19:50 2018 +0200
+++ b/client/src/application/Sidebar.vue	Fri Oct 26 08:45:46 2018 +0200
@@ -4,6 +4,9 @@
             <div class="menupoints" v-if="!this.sidebarCollapsed">
                 <router-link to="/" class="text-body d-flex flex-row nav-link">
                     <i class="fa fa-map-o align-self-center navicon"></i>Riverbed Morphology</router-link>
+                <a href="#" class="text-body d-flex flex-row nav-link" @click="toggleBottlenecks">
+                    Bottlenecks
+                </a>
                 <div v-if="isSysAdmin">
                     <hr />
                     <div class="nav-link d-flex menupadding text-muted">Administration</div>
@@ -36,6 +39,7 @@
  * 
  * Author(s):
  * Thomas Junk <thomas.junk@intevation.de>
+ * Markus Kottländer <markus.kottlaender@intevation.de>
  */
 import { mapGetters } from "vuex";
 
@@ -62,6 +66,12 @@
         "d-print-none": true
       };
     }
+  },
+  methods: {
+    toggleBottlenecks() {
+      this.$store.commit("application/toggleBottlenecks");
+      this.$store.commit("application/toggleSidebar");
+    }
   }
 };
 </script>
--- a/client/src/application/stores/application.js	Thu Oct 25 17:19:50 2018 +0200
+++ b/client/src/application/stores/application.js	Fri Oct 26 08:45:46 2018 +0200
@@ -1,16 +1,17 @@
-/*
- * This is Free Software under GNU Affero General Public License v >= 3.0
+/* This is Free Software under GNU Affero General Public License v >= 3.0
  * without warranty, see README.md and license for details.
- * 
+ *
  * SPDX-License-Identifier: AGPL-3.0-or-later
  * License-Filename: LICENSES/AGPL-3.0.txt
- * 
+ *
  * Copyright (C) 2018 by via donau 
  *   – Österreichische Wasserstraßen-Gesellschaft mbH
  * Software engineering by Intevation GmbH
- * 
+ *
  * Author(s):
- * Thomas Junk <thomas.junk@intevation.de>
+ *   Thomas Junk <thomas.junk@intevation.de>
+ *   Markus Kottländer <markus.kottlaender@intevation.de>
+ *   Bernhard E. Reiter <bernhard.reiter@intevation.de>
  */
 
 import { version } from "../../../package.json";
@@ -24,10 +25,6 @@
   };
 };
 
-const DRAWMODES = {
-  LINE: "LineString"
-};
-
 const Application = {
   namespaced: true,
   state: {
@@ -36,11 +33,13 @@
     sidebar: {
       iscollapsed: defaultCollapseState
     },
+    bottlenecksCollapsed: true,
     splitsceen: initializeSplitScreen(),
     usermenu: {
       iscollapsed: defaultCollapseState
     },
     countries: ["AT", "SK", "HU", "HR", "RS", "BiH", "BG", "RO", "UA"],
+    // there are three states of drawMode: null, "LineString", "Polygon"
     drawMode: null,
     version
   },
@@ -66,9 +65,6 @@
     splitMode: state => {
       return state.splitsceen.mode;
     },
-    drawMode: state => {
-      return state.drawMode;
-    },
     versionStr: state => {
       // version number from package.json
       let versionStr = "v" + state.version;
@@ -89,6 +85,9 @@
     toggleSidebar: state => {
       state.sidebar.iscollapsed = !state.sidebar.iscollapsed;
     },
+    toggleBottlenecks: state => {
+      state.bottlenecksCollapsed = !state.bottlenecksCollapsed;
+    },
     toggleUserMenu: state => {
       state.usermenu.iscollapsed = !state.usermenu.iscollapsed;
     },
@@ -117,11 +116,14 @@
       state.splitsceen = initializeSplitScreen();
     },
     toggleDrawModeLine: state => {
-      if (state.drawMode && state.drawMode === DRAWMODES.LINE) {
+      if (state.drawMode) {
         state.drawMode = null;
-        return;
+      } else {
+        state.drawMode = "LineString";
       }
-      state.drawMode = DRAWMODES.LINE;
+    },
+    activateDrawModePolygon: state => {
+      state.drawMode = "Polygon";
     }
   },
   actions: {}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/bottlenecks/Bottlenecks.vue	Fri Oct 26 08:45:46 2018 +0200
@@ -0,0 +1,125 @@
+<template>
+    <div :class="bottlenecksStyle">
+        <div @click="$store.commit('application/toggleBottlenecks');" class="ui-element close-bottlenecks">
+            <i class="fa fa-close"></i>
+        </div>
+
+        <div v-if="!this.bottlenecksCollapsed">
+            <h4>Bottlenecks</h4>
+            <hr class="mb-0">
+            <div style="max-height: 500px; overflow-y: scroll">
+                <table class="table text-left mb-0" style="margin-top: -1px;">
+                    <tr v-for="(bottleneck) in bottlenecks"
+                        :key="bottleneck.name">
+                        <td>
+                            <a href="#" class="d-block" @click="moveToBottleneck(bottleneck)">
+                                {{ bottleneck.name }}
+                            </a>
+                        </td>
+                    </tr>
+                </table>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+/*
+ * This is Free Software under GNU Affero General Public License v >= 3.0
+ * without warranty, see README.md and license for details.
+ * 
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ * License-Filename: LICENSES/AGPL-3.0.txt
+ * 
+ * Copyright (C) 2018 by via donau 
+ *   – Österreichische Wasserstraßen-Gesellschaft mbH
+ * Software engineering by Intevation GmbH
+ * 
+ * Author(s):
+ * Markus Kottländer <markus.kottlaender@intevation.de>
+ */
+import { mapState } from "vuex";
+import { fromLonLat } from "ol/proj";
+
+export default {
+  name: "bottlenecks",
+  computed: {
+    ...mapState("application", ["bottlenecksCollapsed"]),
+    ...mapState("bottlenecks", ["bottlenecks"]),
+    ...mapState("mapstore", ["openLayersMap"]),
+    bottlenecksStyle() {
+      return {
+        "ui-element": true,
+        bottlenecks: true,
+        overlay: true,
+        bottleneckscollapsed: this.bottlenecksCollapsed,
+        bottlenecksextended: !this.bottlenecksCollapsed,
+        shadow: true
+      };
+    }
+  },
+  methods: {
+    // TODO: make this central, duplicates code from application/Topbar.vue
+    moveToBottleneck(bottleneck) {
+      if (bottleneck.geom.type == "Point") {
+        let view = this.openLayersMap.getView();
+        const currentZoom = view.getZoom();
+        const newZoom = Math.max(17, currentZoom);
+        view.animate(
+          {
+            zoom: newZoom,
+            center: fromLonLat(
+              bottleneck.geom.coordinates,
+              view.getProjection()
+            )
+          },
+          700
+        );
+      }
+      this.$store.commit("application/toggleBottlenecks");
+    }
+  },
+  mounted() {
+    this.$store.dispatch("bottlenecks/loadBottlenecks");
+  }
+};
+</script>
+
+<style lang="scss">
+.bottlenecks {
+  position: absolute;
+  z-index: -2;
+  top: $offset;
+  left: 64px;
+  background-color: #ffffff;
+  padding-top: $offset;
+  opacity: $slight-transparent;
+  border-radius: $border-radius;
+}
+
+.bottleneckscollapsed {
+  width: 0;
+  height: 0;
+  transition: $transition-fast;
+}
+
+.bottlenecksextended {
+  min-height: $sidebar-height;
+  width: 500px;
+}
+
+.close-bottlenecks {
+  position: absolute;
+  z-index: 2;
+  right: 0;
+  top: 7px;
+  border-radius: $border-radius;
+  height: $icon-width;
+  width: $icon-height;
+  display: none;
+}
+
+.bottlenecksextended .close-bottlenecks {
+  display: block;
+}
+</style>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/bottlenecks/store.js	Fri Oct 26 08:45:46 2018 +0200
@@ -0,0 +1,45 @@
+/*
+ * This is Free Software under GNU Affero General Public License v >= 3.0
+ * without warranty, see README.md and license for details.
+ * 
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ * License-Filename: LICENSES/AGPL-3.0.txt
+ * 
+ * Copyright (C) 2018 by via donau 
+ *   – Österreichische Wasserstraßen-Gesellschaft mbH
+ * Software engineering by Intevation GmbH
+ * 
+ * Author(s):
+ * Markus Kottländer <markuks.kottlaender@intevation.de>
+ */
+import { HTTP } from "../application/lib/http";
+
+const Bottlenecks = {
+  namespaced: true,
+  state: {
+    bottlenecks: []
+  },
+  mutations: {
+    setBottlenecks: (state, bottlenecks) => {
+      state.bottlenecks = bottlenecks;
+    }
+  },
+  actions: {
+    loadBottlenecks({ commit }) {
+      return new Promise((resolve, reject) => {
+        HTTP.get("/bottlenecks", {
+          headers: { "X-Gemma-Auth": localStorage.getItem("token") }
+        })
+          .then(response => {
+            commit("setBottlenecks", response.data);
+            resolve(response);
+          })
+          .catch(error => {
+            reject(error);
+          });
+      });
+    }
+  }
+};
+
+export default Bottlenecks;
--- a/client/src/identify/Identify.vue	Thu Oct 25 17:19:50 2018 +0200
+++ b/client/src/identify/Identify.vue	Fri Oct 26 08:45:46 2018 +0200
@@ -78,19 +78,19 @@
 </style>
 
 <script>
-/*
- * This is Free Software under GNU Affero General Public License v >= 3.0
+/* This is Free Software under GNU Affero General Public License v >= 3.0
  * without warranty, see README.md and license for details.
- * 
+ *
  * SPDX-License-Identifier: AGPL-3.0-or-later
  * License-Filename: LICENSES/AGPL-3.0.txt
- * 
+ *
  * Copyright (C) 2018 by via donau 
  *   – Österreichische Wasserstraßen-Gesellschaft mbH
  * Software engineering by Intevation GmbH
- * 
+ *
  * Author(s):
- * Thomas Junk <thomas.junk@intevation.de>
+ *   Thomas Junk <thomas.junk@intevation.de>
+ *   Bernhard E. Reiter <bernhard.reiter@intevation.de>
  */
 import { mapState } from "vuex";
 import { mapGetters } from "vuex";
--- a/client/src/linetool/Linetool.vue	Thu Oct 25 17:19:50 2018 +0200
+++ b/client/src/linetool/Linetool.vue	Fri Oct 26 08:45:46 2018 +0200
@@ -26,40 +26,44 @@
 </style>
 
 <script>
-/*
- * This is Free Software under GNU Affero General Public License v >= 3.0
+/* This is Free Software under GNU Affero General Public License v >= 3.0
  * without warranty, see README.md and license for details.
- * 
+ *
  * SPDX-License-Identifier: AGPL-3.0-or-later
  * License-Filename: LICENSES/AGPL-3.0.txt
- * 
+ *
  * Copyright (C) 2018 by via donau 
  *   – Österreichische Wasserstraßen-Gesellschaft mbH
  * Software engineering by Intevation GmbH
- * 
+ *
  * Author(s):
  * Thomas Junk <thomas.junk@intevation.de>
  */
-import { mapGetters, mapState } from "vuex";
+import { mapState } from "vuex";
 
 export default {
   name: "linetool",
   methods: {
     drawLine() {
-      this.$store.commit("application/toggleDrawModeLine");
+      if (!this.selectedMorph && this.drawMode === "LineString") {
+        this.$store.commit("application/activateDrawModePolygon");
+      } else {
+        this.$store.commit("application/toggleDrawModeLine");
+      }
     }
   },
   computed: {
-    ...mapGetters("application", ["drawMode"]),
+    ...mapState("application", ["drawMode"]),
     ...mapState("identifystore", ["identifiedFeatures"]),
     ...mapState("fairwayprofile", ["selectedMorph"]),
     icon() {
       return {
         fa: true,
         "fa-pencil": !this.selectedMorph,
-        "fa-pencil inverted": !this.selectedMorph && this.drawMode,
+        "fa-pencil inverted":
+          !this.selectedMorph && this.drawMode === "LineString",
         "fa-area-chart": this.selectedMorph,
-        "fa-area-chart inverted": this.selectedMorph && this.drawMode
+        "fa-edit inverted": this.drawMode === "Polygon"
       };
     }
   }
--- a/client/src/login/Login.vue	Thu Oct 25 17:19:50 2018 +0200
+++ b/client/src/login/Login.vue	Fri Oct 26 08:45:46 2018 +0200
@@ -46,7 +46,6 @@
 
             <!-- bottom logo section -->
             <div class="mb-3 secondary-logo"><img :src="secondaryLogo"></div>
-            <div id="versioninfo"><small>Version: {{version}}</small></div>
         </div>
     </div>
 </template>)
@@ -59,12 +58,6 @@
   @extend %fully-centered;
 }
 
-#versioninfo {
-  position: absolute;
-  bottom: 0;
-  right: 0;
-}
-
 .loginform {
   max-width: 375px;
   margin-left: auto;
@@ -103,7 +96,6 @@
 import { mapGetters } from "vuex";
 import { HTTP } from "../application/lib/http.js";
 import { displayError } from "../application/lib/errors.js";
-import { version } from "../../package.json";
 
 const UNAUTHORIZED = 401;
 
@@ -118,8 +110,7 @@
       passwordJustResetted: false,
       readablePassword: false,
       showPasswordReset: false,
-      usernameToReset: "",
-      version: version
+      usernameToReset: ""
     };
   },
   computed: {
--- a/client/src/map/Maplayer.vue	Thu Oct 25 17:19:50 2018 +0200
+++ b/client/src/map/Maplayer.vue	Fri Oct 26 08:45:46 2018 +0200
@@ -27,16 +27,17 @@
 /*
  * This is Free Software under GNU Affero General Public License v >= 3.0
  * without warranty, see README.md and license for details.
- * 
+ *
  * SPDX-License-Identifier: AGPL-3.0-or-later
  * License-Filename: LICENSES/AGPL-3.0.txt
- * 
+ *
  * Copyright (C) 2018 by via donau 
  *   – Österreichische Wasserstraßen-Gesellschaft mbH
  * Software engineering by Intevation GmbH
- * 
+ *
  * Author(s):
- * Thomas Junk <thomas.junk@intevation.de>
+ * * Thomas Junk <thomas.junk@intevation.de>
+ * * Bernhard E. Reiter <bernhard.reiter@intevation.de>
  */
 import { HTTP } from "../application/lib/http";
 import { mapGetters, mapState } from "vuex";
@@ -48,7 +49,7 @@
 import Draw from "ol/interaction/Draw.js";
 import { Vector as VectorLayer } from "ol/layer.js";
 import { Vector as VectorSource } from "ol/source.js";
-import { getLength } from "ol/sphere.js";
+import { getLength, getArea } from "ol/sphere.js";
 import { Icon, Stroke, Style, Fill } from "ol/style.js";
 
 import { displayError } from "../application/lib/errors.js";
@@ -60,7 +61,7 @@
 /* eslint-disable no-console */
 export default {
   name: "maplayer",
-  props: ["drawMode", "lat", "long", "zoom", "split"],
+  props: ["lat", "long", "zoom", "split"],
   data() {
     return {
       projection: "EPSG:3857",
@@ -71,6 +72,7 @@
   },
   computed: {
     ...mapGetters("mapstore", ["layers", "getLayerByName"]),
+    ...mapState("application", ["drawMode"]),
     ...mapState("mapstore", ["openLayersMap"]),
     ...mapState("fairwayprofile", ["selectedMorph"]),
     mapStyle() {
@@ -103,30 +105,31 @@
         })
       ];
 
-      geometry.forEachSegment(function(start, end) {
-        var dx = end[0] - start[0];
-        var dy = end[1] - start[1];
-        var rotation = Math.atan2(dy, dx);
-        // arrows
-        styles.push(
-          new Style({
-            geometry: new Point(end),
-            image: new Icon({
-              // we need to make sure the image is loaded by Vue Loader
-              src: require("../application/assets/linestring_arrow.png"),
-              // fiddling with the anchor's y value does not help to
-              // position the image more centered on the line ending, as the
-              // default line style seems to be slightly uncentered in the
-              // anti-aliasing, but the image is not placed with subpixel
-              // precision
-              anchor: [0.75, 0.5],
-              rotateWithView: true,
-              rotation: -rotation
+      if (geometry.getType() === "LineString") {
+        geometry.forEachSegment(function(start, end) {
+          var dx = end[0] - start[0];
+          var dy = end[1] - start[1];
+          var rotation = Math.atan2(dy, dx);
+          // arrows
+          styles.push(
+            new Style({
+              geometry: new Point(end),
+              image: new Icon({
+                // we need to make sure the image is loaded by Vue Loader
+                src: require("../application/assets/linestring_arrow.png"),
+                // fiddling with the anchor's y value does not help to
+                // position the image more centered on the line ending, as the
+                // default line style seems to be slightly uncentered in the
+                // anti-aliasing, but the image is not placed with subpixel
+                // precision
+                anchor: [0.75, 0.5],
+                rotateWithView: true,
+                rotation: -rotation
+              })
             })
-          })
-        );
-      });
-
+          );
+        });
+      }
       return styles;
     },
     createVectorLayer() {
@@ -139,12 +142,12 @@
       this.openLayersMap.removeInteraction(this.interaction);
       this.interaction = null;
     },
-    createInteraction() {
+    createInteraction(drawMode) {
       this.vectorSource.clear();
       var draw = new Draw({
         source: this.vectorSource,
-        type: this.drawMode,
-        maxPoints: 2
+        type: drawMode,
+        maxPoints: drawMode === "LineString" ? 2 : 50
       });
       draw.on("drawstart", event => {
         this.vectorSource.clear();
@@ -155,10 +158,19 @@
       return draw;
     },
     drawEnd(event) {
-      const length = getLength(event.feature.getGeometry());
-      this.$store.commit("identifystore/setCurrentMeasurement", length);
-      // also place the a rounded length in a property, so identify can show it
-      event.feature.set("length", Math.round(length * 10) / 10);
+      if (this.drawMode === "Polygon") {
+        const areaSize = getArea(event.feature.getGeometry());
+        // also place the a rounded areaSize in a property,
+        // so identify will show it
+        event.feature.set("area (km²)", Math.round(areaSize) / 1000);
+      }
+      if (this.drawMode === "LineString") {
+        const length = getLength(event.feature.getGeometry());
+        this.$store.commit("identifystore/setCurrentMeasurement", length);
+        // also place the a rounded length in a property,
+        // so identify will show it
+        event.feature.set("length (m)", Math.round(length * 10) / 10);
+      }
 
       // if a survey has been selected, request a profile
       // TODO an improvement could be to check if the line intersects
@@ -228,7 +240,9 @@
     },
     activateIdentifyMode() {
       this.openLayersMap.on("singleclick", event => {
-        // console.log("single click on map:", event);
+        this.identify(event.coordinate, event.pixel);
+      });
+      this.openLayersMap.on("dblclick", event => {
         this.identify(event.coordinate, event.pixel);
       });
     },
@@ -361,10 +375,11 @@
     }
   },
   watch: {
-    drawMode() {
+    drawMode(newValue) {
       if (this.interaction) {
         this.removeCurrentInteraction();
-      } else {
+      }
+      if (newValue) {
         this.activateInteraction();
       }
     },
--- a/client/src/morphtool/Morphtool.vue	Thu Oct 25 17:19:50 2018 +0200
+++ b/client/src/morphtool/Morphtool.vue	Fri Oct 26 08:45:46 2018 +0200
@@ -106,7 +106,7 @@
  * Author(s):
  * Thomas Junk <thomas.junk@intevation.de>
  */
-import { mapGetters, mapState } from "vuex";
+import { mapState } from "vuex";
 
 import { displayError } from "../application/lib/errors.js";
 import { HTTP } from "../application/lib/http";
@@ -120,7 +120,7 @@
     };
   },
   computed: {
-    ...mapGetters("application", ["drawMode"]),
+    ...mapState("application", ["drawMode"]),
     ...mapState("identifystore", ["identifiedFeatures"]),
     ...mapState("fairwayprofile", ["selectedMorph"]),
     selectedBottleneck: function() {
--- a/client/src/store.js	Thu Oct 25 17:19:50 2018 +0200
+++ b/client/src/store.js	Fri Oct 26 08:45:46 2018 +0200
@@ -11,6 +11,7 @@
  * 
  * Author(s):
  * Thomas Junk <thomas.junk@intevation.de>
+ * Markus Kottländer <markus.kottlaender@intevation.de>
  */
 
 import Vue from "vue";
@@ -21,6 +22,7 @@
 import mapstore from "./map/store";
 import FairwayProfile from "./fairway/store";
 import IdentifyStore from "./identify/store";
+import Bottlenecks from "./bottlenecks/store";
 
 Vue.use(Vuex);
 
@@ -29,6 +31,7 @@
     application: Application,
     fairwayprofile: FairwayProfile,
     identifystore: IdentifyStore,
+    bottlenecks: Bottlenecks,
     mapstore: mapstore,
     user: user,
     usermanagement: usermanagement