changeset 1125:dbc663b74724

merged store-refactoring into default
author Markus Kottlaender <markus@intevation.de>
date Tue, 06 Nov 2018 13:54:10 +0100
parents c316fcf94ea9 (current diff) 86ed7a56e9f1 (diff)
children a047a2735b9c 71ba4a66ec95
files client/src/store/identify.js
diffstat 28 files changed, 402 insertions(+), 522 deletions(-) [+]
line wrap: on
line diff
--- a/client/src/App.vue	Tue Nov 06 10:02:19 2018 +0100
+++ b/client/src/App.vue	Tue Nov 06 13:54:10 2018 +0100
@@ -99,12 +99,12 @@
  * Thomas Junk <thomas.junk@intevation.de>
  * Markus Kottländer <markus.kottlaender@intevation.de>
  */
-import { mapGetters } from "vuex";
+import { mapState } from "vuex";
 
 export default {
   name: "app",
   computed: {
-    ...mapGetters("user", ["isAuthenticated"]),
+    ...mapState("user", ["isAuthenticated"]),
     routeName() {
       const routeName = this.$route.name;
       return routeName;
--- a/client/src/application/Main.vue	Tue Nov 06 10:02:19 2018 +0100
+++ b/client/src/application/Main.vue	Tue Nov 06 13:54:10 2018 +0100
@@ -1,7 +1,7 @@
 <template>
     <div class="main d-flex flex-column">
-        <Maplayer :split="isSplitscreen" :lat="6155376" :long="1819178" :zoom="11"></Maplayer>
-        <div v-if="isSplitscreen" class="profile d-flex flex-row">
+        <Maplayer :split="showSplitscreen" :lat="6155376" :long="1819178" :zoom="11"></Maplayer>
+        <div v-if="showSplitscreen" class="profile d-flex flex-row">
             <FairwayProfile
                 :additionalSurveys="additionalSurveys"
                 :height="height"
@@ -62,7 +62,7 @@
     };
   },
   computed: {
-    ...mapGetters("application", ["isSplitscreen"]),
+    ...mapState("application", ["showSplitscreen"]),
     ...mapState("fairwayprofile", [
       "currentProfile",
       "minAlt",
@@ -70,14 +70,14 @@
       "totalLength",
       "waterLevels",
       "fairwayCoordinates",
-      "selectedWaterLevel",
-      "availableSurveys",
-      "selectedMorph"
+      "selectedWaterLevel"
     ]),
+    ...mapState("bottlenecks", ["surveys", "selectedSurvey"]),
     additionalSurveys() {
-      if (!this.availableSurveys) return [];
-      return this.availableSurveys.surveys.filter(x => {
-        return x.date_info !== this.selectedMorph.date_info;
+      if (!this.surveys) return [];
+      if (!this.selectedSurvey) return this.surveys;
+      return this.surveys.filter(survey => {
+        return survey.date_info !== this.selectedSurvey.date_info;
       });
     },
     xAxis() {
--- a/client/src/application/Sidebar.vue	Tue Nov 06 10:02:19 2018 +0100
+++ b/client/src/application/Sidebar.vue	Tue Nov 06 13:54:10 2018 +0100
@@ -1,10 +1,13 @@
 <template>
     <div :class="sidebarStyle">
         <div :class="menuStyle">
-            <div class="menupoints" v-if="!this.sidebarCollapsed">
+            <div class="menupoints" v-if="this.showSidebar">
                 <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 v-if="routeName == 'mainview'" href="#" class="text-body d-flex flex-row nav-link" @click="$store.commit('application/toggleBottlenecks');">
+                <a v-if="routeName == 'mainview'"
+                   href="#"
+                   class="text-body d-flex flex-row nav-link"
+                   @click="$store.commit('application/showBottlenecks', !showBottlenecks);">
                     Bottlenecks
                 </a>
                 <div v-if="isSysAdmin">
@@ -41,14 +44,14 @@
  * Thomas Junk <thomas.junk@intevation.de>
  * Markus Kottländer <markus.kottlaender@intevation.de>
  */
-import { mapGetters } from "vuex";
+import { mapGetters, mapState } from "vuex";
 
 export default {
   name: "sidebar",
   props: ["routeName"],
   computed: {
     ...mapGetters("user", ["isSysAdmin"]),
-    ...mapGetters("application", ["sidebarCollapsed"]),
+    ...mapState("application", ["showSidebar", "showBottlenecks"]),
     menuStyle() {
       return {
         menu: true,
@@ -61,8 +64,8 @@
         "ui-element": true,
         sidebar: true,
         overlay: true,
-        sidebarcollapsed: this.sidebarCollapsed,
-        sidebarextended: !this.sidebarCollapsed,
+        sidebarcollapsed: !this.showSidebar,
+        sidebarextended: this.showSidebar,
         shadow: true,
         "d-print-none": true
       };
--- a/client/src/application/Topbar.vue	Tue Nov 06 10:02:19 2018 +0100
+++ b/client/src/application/Topbar.vue	Tue Nov 06 13:54:10 2018 +0100
@@ -1,6 +1,6 @@
 <template>
     <div class="topbar d-flex flex-row">
-        <div @click="toggleSidebar">
+        <div @click="$store.commit('application/showSidebar', !showSidebar)">
             <i class="ui-element menubutton d-print-none fa fa-bars"></i>
         </div>
         <div v-if="routeName == 'mainview'" :class="searchbarContainerStyle">
@@ -10,8 +10,8 @@
                 </span>
             </div>
             <div class="searchgroup">
-                <input @keyup.enter="takeFirstSearchresult" v-if="!searchbarCollapsed" id="search" v-model="searchQuery" type="text" class="form-control ui-element search searchbar d-print-none">
-                <ul v-if="!searchbarCollapsed && searchResults !== null " class="list-group d-print-none">
+                <input @keyup.enter="takeFirstSearchresult" v-if="showSearchbar" id="search" v-model="searchQuery" type="text" class="form-control ui-element search searchbar d-print-none">
+                <ul v-if="showSearchbar && searchResults !== null " class="list-group d-print-none">
                     <li v-for="entry of searchResults" :key="entry.name" class="list-group-item">
                         <a href="#" @click.prevent="moveToSearchResult(entry)">{{entry.name}}</a>
                     </li>
@@ -19,7 +19,7 @@
             </div>
         </div>
         <div v-if="routeName == 'mainview' && Object.keys(currentProfile).length" class="splitbutton">
-            <i @click="splitScreen" class="ui-element splitscreen fa fa-window-restore shadow"></i>
+            <i @click="$store.commit('application/showSplitscreen', !showSplitscreen)" class="ui-element splitscreen fa fa-window-restore shadow"></i>
         </div>
         <div class="">
             <Layers v-if="routeName == 'mainview'"></Layers>
@@ -132,7 +132,6 @@
   },
   data() {
     return {
-      searchbarCollapsed: true,
       searchQuery: "",
       searchQueryIsDirty: false,
       searchResults: null,
@@ -140,7 +139,8 @@
     };
   },
   computed: {
-    ...mapState("mapstore", ["openLayersMap"]),
+    ...mapState("application", ["showSidebar", "showSplitscreen", "showSearchbar"]),
+    ...mapState("map", ["openLayersMap"]),
     ...mapState("fairwayprofile", ["currentProfile"]),
     searchIndicator: function() {
       if (this.isSearching) {
@@ -155,8 +155,8 @@
       return {
         "input-group": true,
         searchcontainer: true,
-        "searchbar-collapsed": this.searchbarCollapsed,
-        "searchbar-expanded": !this.searchbarCollapsed
+        "searchbar-collapsed": !this.showSearchbar,
+        "searchbar-expanded": this.showSearchbar
       };
     }
   },
@@ -235,17 +235,10 @@
       this.toggleSearchbar();
     },
     toggleSearchbar() {
-      if (this.searchbarCollapsed) {
+      if (!this.showSearchbar) {
         setTimeout(setFocus, 300);
       }
-      this.searchbarCollapsed = !this.searchbarCollapsed;
-    },
-    toggleSidebar() {
-      this.$store.commit("application/toggleSidebar");
-    },
-    splitScreen() {
-      if (Object.keys(this.currentProfile).length == 0) return;
-      this.$store.commit("application/toggleSplitScreen");
+      this.$store.commit("application/showSearchbar", !this.showSearchbar)
     }
   }
 };
--- a/client/src/application/Userbar.vue	Tue Nov 06 10:02:19 2018 +0100
+++ b/client/src/application/Userbar.vue	Tue Nov 06 13:54:10 2018 +0100
@@ -1,9 +1,9 @@
 <template>
     <div>
-        <img @click="extendUserMenu" class="ui-element userpic shadow" src="../application/assets/user.png">
+        <img @click="$store.commit('application/showUsermenu', !showUsermenu)" class="ui-element userpic shadow" src="../application/assets/user.png">
         <div :class="userManagementStyle">
-            <span v-if="!isUsermenuCollapsed" class="username align-self-center">{{ userinfo }}</span>
-            <span v-if="!isUsermenuCollapsed" class="logout align-self-center" @click="logoff">
+            <span v-if="showUsermenu" class="username align-self-center">{{ user }}</span>
+            <span v-if="showUsermenu" class="logout align-self-center" @click="logoff">
                 <i class="fa fa-power-off"></i>
             </span>
         </div>
@@ -59,27 +59,24 @@
  * Author(s):
  * Thomas Junk <thomas.junk@intevation.de>
  */
-import { mapGetters } from "vuex";
+import { mapGetters, mapState } from "vuex";
 export default {
   name: "user",
   data() {
     return {};
   },
   methods: {
-    extendUserMenu() {
-      this.$store.commit("application/toggleUserMenu");
-    },
     logoff() {
-      this.$store.commit("user/clear_auth");
-      this.$store.commit("application/resetSidebar");
-      this.$store.commit("application/resetUserMenu");
-      this.$store.commit("application/resetSplitScreen");
+      this.$store.commit("user/clearAuth");
+      this.$store.commit("application/showSidebar", false);
+      this.$store.commit("application/showUsermenu", false);
+      this.$store.commit("application/showSplitscreen", false);
       this.$router.push("/login");
     }
   },
   computed: {
-    ...mapGetters("user", ["userinfo"]),
-    ...mapGetters("application", ["isUsermenuCollapsed"]),
+    ...mapState("user", ["user"]),
+    ...mapState("application", ["showUsermenu"]),
     userManagementStyle() {
       return {
         "ui-element": true,
@@ -87,8 +84,8 @@
         "flex-row": true,
         "justify-content-around": true,
         usermanagement: true,
-        usermanagementcollapsed: this.isUsermenuCollapsed,
-        usermanagementexpanded: !this.isUsermenuCollapsed,
+        usermanagementcollapsed: !this.showUsermenu,
+        usermanagementexpanded: this.showUsermenu,
         shadow: true
       };
     }
--- a/client/src/bottlenecks/Bottlenecks.vue	Tue Nov 06 10:02:19 2018 +0100
+++ b/client/src/bottlenecks/Bottlenecks.vue	Tue Nov 06 13:54:10 2018 +0100
@@ -1,6 +1,6 @@
 <template>
-    <div :class="bottlenecksStyle" :style="'left: ' + (sidebarCollapsed ? '64px' : '17rem')">
-        <div @click="$store.commit('application/toggleBottlenecks');" class="ui-element close-bottlenecks">
+    <div :class="bottlenecksStyle" :style="'left: ' + (showSidebar ? '17rem' : '64px')">
+        <div @click="$store.commit('application/showBottlenecks', !showBottlenecks);" class="ui-element close-bottlenecks">
             <i class="fa fa-close"></i>
         </div>
 
@@ -30,18 +30,18 @@
                     </a>
                 </div>
                 <div class="col-2">
-                    {{ displayCurrentSurvey(bottleneck) }}
+                    {{ displayCurrentSurvey(bottleneck.properties.current) }}
                 </div>
                 <div class="col-3">
-                    {{ displayCurrentChainage(bottleneck) }}
+                    {{ displayCurrentChainage(bottleneck.properties.from, bottleneck.properties.from) }}
                 </div>
                 <div class="col-2 text-right">
-                    <button type="button" class="btn btn-sm btn-outline-secondary" @click="toggleBottleneck(bottleneck)">
+                    <button type="button" class="btn btn-sm btn-outline-secondary" @click="toggleBottleneck(bottleneck.properties.name)">
                         <i class="fa fa-angle-down"></i>
                     </button>
                 </div>
-                <div :class="['col-12', 'surveys', {open: selectedBottleneck === bottleneck}]">
-                   <a href="#" class="d-block p-2" v-for="(survey, index) in surveys[bottleneck.properties.name]" :key="index" @click="selectSurvey(survey, bottleneck)">
+                <div :class="['col-12', 'surveys', {open: openBottleneck === bottleneck.properties.name}]">
+                   <a href="#" class="d-block p-2" v-for="(survey, index) in openBottleneckSurveys" :key="index" @click="selectSurvey(survey, bottleneck)">
                       {{ survey.date_info }}
                     </a>
                 </div>
@@ -77,22 +77,21 @@
       search: "",
       sortColumn: "name",
       sortDirection: "ASC",
-      selectedBottleneck: null,
-      surveys: {}
+      openBottleneck: null,
+      openBottleneckSurveys: null
     };
   },
   computed: {
-    ...mapState("application", ["bottlenecksCollapsed"]),
+    ...mapState("application", ["showBottlenecks", "showSidebar"]),
     ...mapState("bottlenecks", ["bottlenecks"]),
-    ...mapState("mapstore", ["openLayersMap"]),
-    ...mapGetters("application", ["sidebarCollapsed"]),
+    ...mapState("map", ["openLayersMap"]),
     bottlenecksStyle() {
       return {
         "ui-element": true,
         bottlenecks: true,
         overlay: true,
-        bottleneckscollapsed: this.bottlenecksCollapsed,
-        bottlenecksextended: !this.bottlenecksCollapsed,
+        bottleneckscollapsed: !this.showBottlenecks,
+        bottlenecksextended: this.showBottlenecks,
         shadow: true
       };
     },
@@ -153,10 +152,8 @@
         });
     },
     selectSurvey(survey, bottleneck) {
-      this.$store.commit("fairwayprofile/setSelectedMorph", survey);
-      this.$store.commit("fairwayprofile/setAvailableSurveys", {
-        surveys: this.surveys[bottleneck.properties.name]
-      });
+      this.$store.dispatch("bottlenecks/setSelectedBottleneck", bottleneck.properties.name);
+      this.$store.commit("bottlenecks/setSelectedSurvey", survey);
       this.moveToBottleneck(bottleneck);
     },
     moveToBottleneck(bottleneck) {
@@ -179,19 +176,21 @@
       this.sortColumn = column;
       this.sortDirection = this.sortDirection === "ASC" ? "DESC" : "ASC";
     },
-    toggleBottleneck(bottleneck) {
-      if (bottleneck === this.selectedBottleneck) {
-        this.selectedBottleneck = null;
+    toggleBottleneck(name) {
+      this.openBottleneckSurveys = null;
+      if (name === this.openBottleneck) {
+        this.openBottleneck = null;
       } else {
-        HTTP.get("/surveys/" + bottleneck.properties.name, {
+        this.openBottleneck = name;
+        
+        HTTP.get("/surveys/" + name, {
           headers: {
             "X-Gemma-Auth": localStorage.getItem("token"),
             "Content-type": "text/xml; charset=UTF-8"
           }
         })
           .then(response => {
-            this.surveys[bottleneck.properties.name] = response.data.surveys;
-            this.selectedBottleneck = bottleneck;
+            this.openBottleneckSurveys = response.data.surveys;
           })
           .catch(error => {
             const { status, data } = error.response;
@@ -202,13 +201,12 @@
           });
       }
     },
-    displayCurrentSurvey(bottleneck) {
-      const current = bottleneck.properties.current;
+    displayCurrentSurvey(current) {
       return current ? current.substr(0, current.length - 1) : "";
     },
-    displayCurrentChainage(bottleneck) {
+    displayCurrentChainage(from, to) {
       return (
-        bottleneck.properties.from / 10 + " - " + bottleneck.properties.to / 10
+        from / 10 + " - " + to / 10
       );
     }
   },
--- a/client/src/fairway/Fairwayprofile.vue	Tue Nov 06 10:02:19 2018 +0100
+++ b/client/src/fairway/Fairwayprofile.vue	Tue Nov 06 13:54:10 2018 +0100
@@ -8,6 +8,7 @@
                 <option
                     v-for="survey in additionalSurveys"
                     :key="survey.date_info"
+                    :value="survey"
                 >{{survey.date_info}}</option>
             </select>
             <small class="mt-2">
@@ -99,7 +100,6 @@
       "startPoint",
       "endPoint",
       "currentProfile",
-      "selectedMorph",
       "minAlt",
       "maxAlt",
       "totalLength",
@@ -107,6 +107,7 @@
       "waterLevels",
       "selectedWaterLevel"
     ]),
+    ...mapState("bottlenecks", ["selectedSurvey"]),
     additionalSurvey: {
       get() {
         return this.$store.getters["fairwayprofile/additionalSurvey"];
@@ -117,11 +118,18 @@
       }
     },
     currentData() {
-      const currentSurveyDate = this.selectedMorph.date_info;
-      return this.currentProfile[currentSurveyDate];
+      if (
+        !this.selectedSurvey ||
+        !this.currentProfile.hasOwnProperty(this.selectedSurvey.date_info)
+      ) return [];
+      return this.currentProfile[this.selectedSurvey.date_info];
     },
     additionalData() {
-      return this.currentProfile[this.additionalSurvey];
+      if (
+        !this.additionalSurvey ||
+        !this.currentProfile.hasOwnProperty(this.additionalSurvey.date_info)
+      ) return [];
+      return this.currentProfile[this.additionalSurvey.date_info];
     },
     waterColor() {
       const result = this.waterLevels.find(
@@ -160,7 +168,7 @@
       if (
         !this.additionalSurvey ||
         this.wait ||
-        this.currentProfile[this.additionalSurvey]
+        this.currentProfile[this.additionalSurvey.date_info]
       ) {
         this.drawDiagram();
         return;
--- a/client/src/identify/Identify.vue	Tue Nov 06 10:02:19 2018 +0100
+++ b/client/src/identify/Identify.vue	Tue Nov 06 13:54:10 2018 +0100
@@ -1,12 +1,12 @@
 <template>
     <div class="identifymenu">
-        <div @click="collapse" class="d-flex flex-column ui-element minimizer">
+        <div @click="$store.commit('application/showIdentify', !showIdentify)" class="d-flex flex-column ui-element minimizer">
             <div :class="infoStyle">
                 <i class="fa fa-info"></i>
             </div>
         </div>
         <div :class="identifyStyle">
-            <div v-if="!collapsed" class="card-body">
+            <div v-if="showIdentify" class="card-body">
                 <div class="headline">
                     <h4 class="card-title">Identified</h4>
                 </div>
@@ -97,27 +97,22 @@
  *   Thomas Junk <thomas.junk@intevation.de>
  *   Bernhard E. Reiter <bernhard.reiter@intevation.de>
  */
-import { mapState } from "vuex";
-import { mapGetters } from "vuex";
+import { mapState, mapGetters } from "vuex";
 
 export default {
   name: "identify",
-  data() {
-    return {
-      collapsed: true
-    };
-  },
   computed: {
     ...mapGetters("application", ["versionStr"]),
-    ...mapState("identifystore", ["identifiedFeatures", "currentMeasurement"]),
+    ...mapState("application", ["showIdentify"]),
+    ...mapState("map", ["identifiedFeatures", "currentMeasurement"]),
     identifyStyle() {
       return {
         "ui-element": true,
         card: true,
         identify: true,
         shadow: true,
-        identifyexpanded: !this.collapsed,
-        identifycollapsed: this.collapsed
+        identifyexpanded: this.showIdentify,
+        identifycollapsed: !this.showIdentify
       };
     },
     infoStyle() {
@@ -129,9 +124,6 @@
     }
   },
   methods: {
-    collapse() {
-      this.collapsed = !this.collapsed;
-    },
     prepareProperties(feature) {
       // return dict object with propertyname:plainvalue prepared for display
       var properties = feature.getProperties();
--- a/client/src/layers/Layers.vue	Tue Nov 06 10:02:19 2018 +0100
+++ b/client/src/layers/Layers.vue	Tue Nov 06 13:54:10 2018 +0100
@@ -1,18 +1,18 @@
 <template>
     <div class="layerselectmenu">
-        <div @click="collapse" class="d-flex flex-column ui-element minimizer">
+        <div @click="$store.commit('application/showLayers', !showLayers)" class="d-flex flex-column ui-element minimizer">
             <div>
                 <i class="fa fa-th-list"></i>
             </div>
         </div>
         <div :class="layerSelectStyle">
-            <div v-if="!collapsed" class="card-body layers">
+            <div v-if="showLayers" class="card-body layers">
                 <div class="headline">
                     <h4 class="card-title">Layers</h4>
                 </div>
                 <hr>
                 <div class="d-flex flex-column">
-                    <Layerselect :layerindex="index" :layername="layer.name" v-for="(layer, index) in layers" :key="layer.name" :isVisible="layer.isVisible" @visibilityToggled="visibilityToggled"></Layerselect>
+                    <Layerselect :layerindex="index" :layername="layer.name" v-for="(layer, index) in layersForLegend" :key="layer.name" :isVisible="layer.isVisible" @visibilityToggled="visibilityToggled"></Layerselect>
                 </div>
             </div>
         </div>
@@ -68,36 +68,29 @@
  * Thomas Junk <thomas.junk@intevation.de>
  */
 import Layerselect from "./Layerselect";
-import { mapGetters } from "vuex";
+import { mapGetters, mapState } from "vuex";
 export default {
   name: "layers",
-  data() {
-    return {
-      collapsed: false
-    };
-  },
   components: {
     Layerselect
   },
   computed: {
-    ...mapGetters("mapstore", ["layers"]),
+    ...mapGetters("map", ["layersForLegend"]),
+    ...mapState("application", ["showLayers"]),
     layerSelectStyle() {
       return {
         "ui-element": true,
         card: true,
         layerselection: true,
         shadow: true,
-        layerselectionexpanded: !this.collapsed,
-        layerselectioncollapsed: this.collapsed
+        layerselectionexpanded: this.showLayers,
+        layerselectioncollapsed: !this.showLayers
       };
     }
   },
   methods: {
-    collapse() {
-      this.collapsed = !this.collapsed;
-    },
     visibilityToggled(layer) {
-      this.$store.commit("mapstore/toggleVisibility", layer);
+      this.$store.commit("map/toggleVisibility", layer);
     }
   }
 };
--- a/client/src/layers/LegendElement.vue	Tue Nov 06 10:02:19 2018 +0100
+++ b/client/src/layers/LegendElement.vue	Tue Nov 06 13:54:10 2018 +0100
@@ -37,7 +37,7 @@
     };
   },
   computed: {
-    ...mapGetters("mapstore", ["getLayerByName"]),
+    ...mapGetters("map", ["getLayerByName"]),
     id() {
       return "legendelement" + this.layerindex;
     },
--- a/client/src/linetool/Linetool.vue	Tue Nov 06 10:02:19 2018 +0100
+++ b/client/src/linetool/Linetool.vue	Tue Nov 06 13:54:10 2018 +0100
@@ -1,5 +1,5 @@
 <template>
-    <div @click="drawLine" class="ui-element d-flex shadow drawtool">
+    <div @click="cycleDrawMode" class="ui-element d-flex shadow drawtool">
         <i :class="icon"></i>
     </div>
 </template>
@@ -44,24 +44,23 @@
 export default {
   name: "linetool",
   methods: {
-    drawLine() {
-      if (!this.selectedMorph && this.drawMode === "LineString") {
-        this.$store.commit("application/activateDrawModePolygon");
+    cycleDrawMode() {
+      if (!this.selectedSurvey && this.drawMode === "LineString") {
+        this.$store.commit("map/activateDrawModePolygon");
       } else {
-        this.$store.commit("application/toggleDrawModeLine");
+        this.$store.commit("map/toggleDrawModeLine");
       }
     }
   },
   computed: {
-    ...mapState("application", ["drawMode"]),
-    ...mapState("identifystore", ["identifiedFeatures"]),
-    ...mapState("fairwayprofile", ["selectedMorph"]),
+    ...mapState("map", ["identifiedFeatures", "drawMode"]),
+    ...mapState("bottlenecks", ["selectedSurvey"]),
     icon() {
       return {
         fa: true,
-        "fa-area-chart": this.selectedMorph,
-        "fa-edit": !this.selectedMorph && this.drawMode === "Polygon",
-        "fa-pencil": !this.selectedMorph && this.drawMode !== "Polygon",
+        "fa-area-chart": this.selectedSurvey,
+        "fa-edit": !this.selectedSurvey && this.drawMode === "Polygon",
+        "fa-pencil": !this.selectedSurvey && this.drawMode !== "Polygon",
         inverted: this.drawMode
       };
     }
--- a/client/src/login/Login.vue	Tue Nov 06 10:02:19 2018 +0100
+++ b/client/src/login/Login.vue	Tue Nov 06 13:54:10 2018 +0100
@@ -94,7 +94,7 @@
 </style>
 
 <script>
-import { mapGetters } from "vuex";
+import { mapState } from "vuex";
 import { HTTP } from "../application/lib/http.js";
 import { displayError } from "../application/lib/errors.js";
 
@@ -150,7 +150,7 @@
       }
       return result;
     },
-    ...mapGetters("application", ["appTitle", "secondaryLogo"])
+    ...mapState("application", ["appTitle", "secondaryLogo"])
   },
   methods: {
     login() {
--- a/client/src/logs/logs.vue	Tue Nov 06 10:02:19 2018 +0100
+++ b/client/src/logs/logs.vue	Tue Nov 06 13:54:10 2018 +0100
@@ -103,7 +103,7 @@
  * Author(s):
  * Thomas Junk <thomas.junk@intevation.de>
  */
-import { mapGetters } from "vuex";
+import { mapState } from "vuex";
 import { HTTP } from "../application/lib/http.js";
 import "../../node_modules/highlight.js/styles/paraiso-dark.css";
 import Vue from "vue";
@@ -144,7 +144,7 @@
     }
   },
   computed: {
-    ...mapGetters("application", ["sidebarCollapsed", "isUsermenuCollapsed"]),
+    ...mapState("application", ["showSidebar"]),
     accesslogStyle() {
       return {
         active: this.currentLog == ACCESSLOG,
@@ -160,8 +160,8 @@
     spacer() {
       return {
         spacer: true,
-        "spacer-expanded": !this.sidebarCollapsed,
-        "spacer-collapsed": this.sidebarCollapsed
+        "spacer-expanded": this.showSidebar,
+        "spacer-collapsed": !this.showSidebar
       };
     }
   }
--- a/client/src/map/Maplayer.vue	Tue Nov 06 10:02:19 2018 +0100
+++ b/client/src/map/Maplayer.vue	Tue Nov 06 13:54:10 2018 +0100
@@ -45,12 +45,11 @@
 import { Map, View } from "ol";
 import { WFS, GeoJSON } from "ol/format.js";
 import LineString from "ol/geom/LineString.js";
-import Point from "ol/geom/Point.js";
 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, getArea } from "ol/sphere.js";
-import { Icon, Stroke, Style, Fill } from "ol/style.js";
+import { Stroke, Style, Fill } from "ol/style.js";
 
 import { displayError } from "../application/lib/errors.js";
 import { calculateFairwayCoordinates } from "../application/lib/geo.js";
@@ -65,16 +64,13 @@
   data() {
     return {
       projection: "EPSG:3857",
-      interaction: null,
-      vectorLayer: null,
-      vectorSource: null
+      interaction: null
     };
   },
   computed: {
-    ...mapGetters("mapstore", ["layers", "getLayerByName"]),
-    ...mapState("application", ["drawMode"]),
-    ...mapState("mapstore", ["openLayersMap"]),
-    ...mapState("fairwayprofile", ["selectedMorph"]),
+    ...mapGetters("map", ["getLayerByName"]),
+    ...mapState("map", ["layers", "openLayersMap", "drawMode"]),
+    ...mapState("bottlenecks", ["selectedSurvey"]),
     mapStyle() {
       return {
         mapfull: !this.split,
@@ -83,62 +79,23 @@
     }
   },
   methods: {
-    drawStyleFunction(feature) {
-      // adapted from OpenLayer's LineString Arrow Example
-      var geometry = feature.getGeometry();
-      var styles = [
-        // linestring
-        new Style({
-          stroke: new Stroke({
-            color: "#369aca",
-            width: 2
-          })
-        })
-      ];
-
-      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;
-    },
     removeCurrentInteraction() {
-      this.$store.commit("identifystore/setCurrentMeasurement", null);
-      this.vectorSource.clear();
+      this.$store.commit("map/setCurrentMeasurement", null);
+      this.getLayerByName("Draw Tool").data.getSource().clear();
       this.openLayersMap.removeInteraction(this.interaction);
       this.interaction = null;
     },
     createInteraction(drawMode) {
-      this.vectorSource.clear();
+      const drawVectorSrc = this.getLayerByName("Draw Tool").data.getSource();
+      drawVectorSrc.clear();
       var draw = new Draw({
-        source: this.vectorSource,
+        source: drawVectorSrc,
         type: drawMode,
         maxPoints: drawMode === "LineString" ? 2 : 50
       });
       draw.on("drawstart", () => {
-        this.vectorSource.clear();
-        this.$store.commit("identifystore/setCurrentMeasurement", null);
+        drawVectorSrc.clear();
+        this.$store.commit("map/setCurrentMeasurement", null);
         // we are not setting an id here, to avoid the regular identify to
         // pick it up
         // event.feature.setId("drawn.1"); // unique id for new feature
@@ -152,14 +109,14 @@
         // also place the a rounded areaSize in a property,
         // so identify will show it
         if (areaSize > 100000) {
-          this.$store.commit("identifystore/setCurrentMeasurement", {
+          this.$store.commit("map/setCurrentMeasurement", {
             quantity: "Area",
             unitSymbol: "km²",
             // convert into 1 km² == 1000*1000 m² and round to 1000 m²
             value: Math.round(areaSize / 1000) / 1000
           });
         } else {
-          this.$store.commit("identifystore/setCurrentMeasurement", {
+          this.$store.commit("map/setCurrentMeasurement", {
             quantity: "Area",
             unitSymbol: "m²",
             value: Math.round(areaSize)
@@ -168,7 +125,7 @@
       }
       if (this.drawMode === "LineString") {
         const length = getLength(event.feature.getGeometry());
-        this.$store.commit("identifystore/setCurrentMeasurement", {
+        this.$store.commit("map/setCurrentMeasurement", {
           quantity: "Length",
           unitSymbol: "m",
           value: Math.round(length * 10) / 10
@@ -178,9 +135,9 @@
       // if a survey has been selected, request a profile
       // TODO an improvement could be to check if the line intersects
       // with the bottleneck area's polygon before trying the server request
-      if (this.selectedMorph) {
+      if (this.selectedSurvey) {
         this.$store.commit("fairwayprofile/clearCurrentProfile");
-        console.log("requesting profile for", this.selectedMorph);
+        console.log("requesting profile for", this.selectedSurvey);
         const inputLineString = event.feature.getGeometry().clone();
         inputLineString.transform("EPSG:3857", "EPSG:4326");
         const [start, end] = inputLineString
@@ -190,7 +147,7 @@
         this.$store.commit("fairwayprofile/setEndPoint", end);
         const profileLine = new LineString([start, end]);
         this.$store
-          .dispatch("fairwayprofile/loadProfile", this.selectedMorph.date_info)
+          .dispatch("fairwayprofile/loadProfile", this.selectedSurvey)
           .then(() => {
             var vectorSource = this.getLayerByName(
               "Fairway Dimensions"
@@ -198,7 +155,7 @@
             this.calculateIntersection(vectorSource, profileLine);
           })
           .then(() => {
-            this.$store.commit("application/openSplitScreen");
+            this.$store.commit("application/showSplitscreen", true);
           })
           .catch(error => {
             const { status, data } = error.response;
@@ -242,10 +199,21 @@
       this.openLayersMap.addInteraction(interaction);
     },
     identify(coordinate, pixel) {
-      this.$store.commit("identifystore/setIdentifiedFeatures", []);
+      this.$store.commit("map/setIdentifiedFeatures", []);
       // checking our WFS layers
       var features = this.openLayersMap.getFeaturesAtPixel(pixel);
-      this.$store.commit("identifystore/setIdentifiedFeatures", features);
+      if (features) {
+        this.$store.commit("map/setIdentifiedFeatures", features);
+        
+        // get selected bottleneck from identified features
+        for (let feature of features) {
+          let id = feature.getId();
+          // RegExp.prototype.test() works with number, str and undefined
+          if (/^bottlenecks\./.test(id)) {
+            this.$store.dispatch("bottlenecks/setSelectedBottleneck", feature.get("objnam"));
+          }
+        }
+      }
 
       // DEBUG output and example how to remove the GeometryName
       /*
@@ -384,11 +352,11 @@
         map.updateSize();
       });
     },
-    selectedMorph(newSelectedMorph) {
-      if (newSelectedMorph) {
+    selectedSurvey(newSelectedSurvey) {
+      if (newSelectedSurvey) {
         this.updateBottleneckFilter(
-          newSelectedMorph.bottleneck_id,
-          newSelectedMorph.date_info
+          newSelectedSurvey.bottleneck_id,
+          newSelectedSurvey.date_info
         );
       } else {
         this.updateBottleneckFilter("does_not_exist", "1999-10-01");
@@ -396,13 +364,8 @@
     }
   },
   mounted() {
-    this.vectorSource = new VectorSource({ wrapX: false });
-    this.vectorLayer = new VectorLayer({
-      source: this.vectorSource,
-      style: this.drawStyleFunction
-    });
     let map = new Map({
-      layers: [...this.layers.map(x => x.data), this.vectorLayer],
+      layers: [...this.layers.map(x => x.data)],
       target: "map",
       controls: [],
       view: new View({
@@ -411,7 +374,7 @@
         projection: this.projection
       })
     });
-    this.$store.commit("mapstore/setOpenLayersMap", map);
+    this.$store.commit("map/setOpenLayersMap", map);
 
     // TODO make display of layers more dynamic, e.g. from a list
 
--- a/client/src/morphtool/Morphtool.vue	Tue Nov 06 10:02:19 2018 +0100
+++ b/client/src/morphtool/Morphtool.vue	Tue Nov 06 13:54:10 2018 +0100
@@ -1,36 +1,34 @@
 <template>
     <div class="morphcontainer">
-        <div v-if="selectedBottleneck">
-            <div v-if="surveyList && !drawMode" class="ui-element card card-body shadow">
-                <div class="headline">
-                    <h4>{{bottleneckName}}</h4>
-                    <hr>
-                    <div
-                        @click="clearSelection"
-                        class="float-left ui-element d-flex morphtoolminus"
-                    >
-                        <i class="fa fa-close morphtoolsminus"></i>
-                    </div>
+        <div v-if="selectedBottleneck && surveys && !selectedSurvey" class="ui-element card card-body shadow">
+            <div class="headline">
+                <h4>{{ selectedBottleneck }}</h4>
+                <hr>
+                <div
+                    @click="clearSelection"
+                    class="float-left ui-element d-flex morphtoolminus"
+                >
+                    <i class="fa fa-close morphtoolsminus"></i>
                 </div>
-                <ul class="list-group surveylist">
-                    <li
-                        v-for="survey of surveyList.surveys"
-                        :key="survey.data_info"
-                        class="list-group-item"
-                        @click.prevent="selectSurvey(survey)"
-                    >
-                        <a href="#" @click.prevent>{{survey.date_info}}</a>
-                    </li>
-                </ul>
             </div>
+            <ul class="list-group surveylist">
+                <li
+                    v-for="survey of surveys"
+                    :key="survey.data_info"
+                    class="list-group-item"
+                    @click.prevent="$store.commit('bottlenecks/setSelectedSurvey', survey)"
+                >
+                    <a href="#" @click.prevent>{{ survey.date_info }}</a>
+                </li>
+            </ul>
         </div>
-        <div v-if="selectedMorph" @click="clearSelection" class="ui-element shadow morphtool">
+        <div v-if="selectedSurvey" @click="clearSelection" class="ui-element shadow morphtool">
             <div class="d-flex flex-row justify-content-between">
                 <i class="fa fa-close text-danger"></i>
                 <small>Bottleneck:&nbsp;</small>
                 <h6>
-                    {{bottleneckName}}
-                    <small>( {{selectedMorph.date_info}} )</small>
+                    {{ selectedBottleneck }}
+                    <small>( {{ selectedSurvey.date_info }} )</small>
                 </h6>
             </div>
         </div>
@@ -108,83 +106,22 @@
  */
 import { mapState } from "vuex";
 
-import { displayError } from "../application/lib/errors.js";
-import { HTTP } from "../application/lib/http";
-
 export default {
   name: "morphtool",
-  data() {
-    return {
-      surveyList: null,
-      bottleneckName: ""
-    };
-  },
   computed: {
-    ...mapState("application", ["drawMode"]),
-    ...mapState("identifystore", ["identifiedFeatures"]),
-    ...mapState("fairwayprofile", ["selectedMorph"]),
-    selectedBottleneck: function() {
-      if (this.identifiedFeatures && !this.drawMode) {
-        for (let feature of this.identifiedFeatures) {
-          let id = feature.getId();
-          // RegExp.prototype.test() works with number, str and undefined
-          if (/^bottlenecks\./.test(id)) {
-            this.$store.commit("fairwayprofile/setSelectedMorph", null);
-            return feature;
-          }
-        }
-      }
-      return null;
-    }
-  },
-  watch: {
-    selectedBottleneck: function(bottleneckFeature) {
-      if (bottleneckFeature) {
-        let bottleneckName = bottleneckFeature.get("objnam");
-        if (bottleneckName) {
-          this.bottleneckName = bottleneckName;
-          this.queryBottleneck(bottleneckName);
-        }
-      }
-    }
+    ...mapState("map", ["drawMode"]),
+    ...mapState("bottlenecks", [
+      "selectedBottleneck",
+      "surveys",
+      "selectedSurvey"
+    ])
   },
   methods: {
-    queryBottleneck(name) {
-      // DEBUG console.log("starting to query bottleneck", name);
-      HTTP.get("/surveys/" + name, {
-        headers: {
-          "X-Gemma-Auth": localStorage.getItem("token"),
-          "Content-type": "text/xml; charset=UTF-8"
-        }
-      })
-        .then(response => {
-          this.surveyList = response.data;
-          this.$store.commit(
-            "fairwayprofile/setAvailableSurveys",
-            response.data
-          );
-        })
-        .catch(error => {
-          this.surveyList = null;
-          const { status, data } = error.response;
-          displayError({
-            title: "Backend Error",
-            message: `${status}: ${data.message || data}`
-          });
-        });
-    },
-    selectSurvey(survey) {
-      this.$store.commit("fairwayprofile/setSelectedMorph", survey);
-      this.surveyList = null;
-    },
     clearSelection() {
-      this.$store.commit("identifystore/setIdentifiedFeatures", []);
-      this.$store.commit("fairwayprofile/setSelectedMorph", null);
-      this.$store.commit("fairwayprofile/clearCurrentProfile");
-      this.$store.commit("application/closeSplitScreen");
-      this.surveyList = null;
+      this.$store.dispatch("bottlenecks/setSelectedBottleneck", null);
+      this.$store.commit("application/showSplitscreen", false);
       if (this.drawMode) {
-        this.$store.commit("application/toggleDrawModeLine");
+        this.$store.commit("map/toggleDrawModeLine");
       }
     }
   }
--- a/client/src/pdftool/Pdftool.vue	Tue Nov 06 10:02:19 2018 +0100
+++ b/client/src/pdftool/Pdftool.vue	Tue Nov 06 13:54:10 2018 +0100
@@ -1,10 +1,10 @@
 <template>
     <div class="pdftool">
-        <div @click="collapse" class="d-flex flex-column ui-element minimizer">
-            <i :class="['fa', 'mt-1', {'fa-file-pdf-o': collapsed}, {'fa-close': !collapsed}]"></i>
+        <div @click="$store.commit('application/showPdfTool', !showPdfTool)" class="d-flex flex-column ui-element minimizer">
+            <i :class="['fa', 'mt-1', {'fa-file-pdf-o': !showPdfTool}, {'fa-close': showPdfTool}]"></i>
         </div>
         <div :class="style">
-            <div v-if="!collapsed" class="card-body">
+            <div v-if="showPdfTool" class="card-body">
                 <div class="headline">
                     <h4 class="card-title">Generate PDF</h4>
                 </div>
@@ -102,7 +102,6 @@
   name: "pdftool",
   data() {
     return {
-      collapsed: true,
       form: {
         format: "landscape",
         downloadType: "download"
@@ -110,22 +109,19 @@
     };
   },
   computed: {
-    ...mapState("application", ["showPrintDialog"]),
+    ...mapState("application", ["showPdfTool"]),
     style() {
       return {
         "ui-element": true,
         card: true,
         inner: true,
         shadow: true,
-        pdftoolexpanded: !this.collapsed,
-        pdftoolcollapsed: this.collapsed
+        pdftoolexpanded: this.showPdfTool,
+        pdftoolcollapsed: !this.showPdfTool
       };
     }
   },
   methods: {
-    collapse() {
-      this.collapsed = !this.collapsed;
-    },
     download() {
       // generate PDF and open it
       // TODO: replace this src with an API reponse after actually generating PDFs
--- a/client/src/router.js	Tue Nov 06 10:02:19 2018 +0100
+++ b/client/src/router.js	Tue Nov 06 13:54:10 2018 +0100
@@ -97,12 +97,12 @@
           localStorage.getItem("expires")
         );
         if (sessionStillActive(expiresFromPastSession)) {
-          store.commit("user/set_user", localStorage.getItem("user"));
-          store.commit("user/set_expires", expiresFromPastSession);
-          store.commit("user/set_roles", localStorage.getItem("roles"));
-          store.commit("user/set_authenticate", true);
+          store.commit("user/setUser", localStorage.getItem("user"));
+          store.commit("user/setExpires", expiresFromPastSession);
+          store.commit("user/setRoles", localStorage.getItem("roles"));
+          store.commit("user/setIsAuthenticate", true);
         } else {
-          store.commit("user/clear_auth");
+          store.commit("user/clearAuth");
         }
         next();
       }
@@ -116,7 +116,7 @@
 
 router.beforeEach((to, from, next) => {
   const requiresAuth = to.matched.some(record => record.meta.requiresAuth);
-  const loggedIn = store.getters["user/isAuthenticated"];
+  const loggedIn = store.state.user.isAuthenticated;
   const expiresFromPastSession = toMillisFromString(
     localStorage.getItem("expires")
   );
--- a/client/src/store.js	Tue Nov 06 10:02:19 2018 +0100
+++ b/client/src/store.js	Tue Nov 06 13:54:10 2018 +0100
@@ -16,24 +16,22 @@
 
 import Vue from "vue";
 import Vuex from "vuex";
-import Application from "./store/application";
+import application from "./store/application";
 import user from "./store/user";
 import usermanagement from "./store/usermanagement";
-import mapstore from "./store/map";
-import FairwayProfile from "./store/fairway";
-import IdentifyStore from "./store/identify";
-import Bottlenecks from "./store/bottlenecks";
+import map from "./store/map";
+import fairwayprofile from "./store/fairway";
+import bottlenecks from "./store/bottlenecks";
 
 Vue.use(Vuex);
 
 export default new Vuex.Store({
   modules: {
-    application: Application,
-    fairwayprofile: FairwayProfile,
-    identifystore: IdentifyStore,
-    bottlenecks: Bottlenecks,
-    mapstore: mapstore,
-    user: user,
-    usermanagement: usermanagement
+    application,
+    fairwayprofile,
+    bottlenecks,
+    map,
+    user,
+    usermanagement
   }
 });
--- a/client/src/store/application.js	Tue Nov 06 10:02:19 2018 +0100
+++ b/client/src/store/application.js	Tue Nov 06 13:54:10 2018 +0100
@@ -16,55 +16,23 @@
 
 import { version } from "../../package.json";
 
-const defaultCollapseState = true;
-
-const initializeSplitScreen = () => {
-  return {
-    active: false,
-    mode: "v"
-  };
-};
-
-const Application = {
+export default {
   namespaced: true,
   state: {
     appTitle: process.env.VUE_APP_TITLE,
     secondaryLogo: process.env.VUE_APP_SECONDARY_LOGO_URL,
-    sidebar: {
-      iscollapsed: defaultCollapseState
-    },
-    bottlenecksCollapsed: true,
-    splitsceen: initializeSplitScreen(),
-    usermenu: {
-      iscollapsed: defaultCollapseState
-    },
+    showSidebar: false,
+    showUsermenu: false,
+    showBottlenecks: false,
+    showSplitscreen: false,
+    showSearchbar: false,
+    showIdentify: false,
+    showLayers: true,
+    showPdfTool: false,
     countries: ["AT", "SK", "HU", "HR", "RS", "BiH", "BG", "RO", "UA"],
-    // there are three states of drawMode: null, "LineString", "Polygon"
-    drawMode: null,
     version
   },
   getters: {
-    countries: state => {
-      return state.countries;
-    },
-    sidebarCollapsed: state => {
-      return state.sidebar.iscollapsed;
-    },
-    isUsermenuCollapsed: state => {
-      return state.usermenu.iscollapsed;
-    },
-    appTitle: state => {
-      return state.appTitle;
-    },
-    secondaryLogo: state => {
-      return state.secondaryLogo;
-    },
-    isSplitscreen: state => {
-      return state.splitsceen.active;
-    },
-    splitMode: state => {
-      return state.splitsceen.mode;
-    },
     versionStr: state => {
       // version number from package.json
       let versionStr = "v" + state.version;
@@ -82,51 +50,29 @@
     }
   },
   mutations: {
-    toggleSidebar: state => {
-      state.sidebar.iscollapsed = !state.sidebar.iscollapsed;
-    },
-    toggleBottlenecks: state => {
-      state.bottlenecksCollapsed = !state.bottlenecksCollapsed;
+    showSidebar: (state, show) => {
+      state.showSidebar = show;
     },
-    toggleUserMenu: state => {
-      state.usermenu.iscollapsed = !state.usermenu.iscollapsed;
-    },
-    toggleSplitScreen: state => {
-      state.splitsceen.active = !state.splitsceen.active;
+    showBottlenecks: (state, show) => {
+      state.showBottlenecks = show;
     },
-    openSplitScreen: state => {
-      state.splitsceen.active = true;
-    },
-    closeSplitScreen: state => {
-      state.splitsceen.active = false;
+    showSplitscreen: (state, show) => {
+      state.showSplitscreen = show;
     },
-    resetSidebar: state => {
-      state.sidebar.iscollapsed = defaultCollapseState;
-    },
-    collapseSidebar: state => {
-      state.sidebar.iscollapsed = true;
+    showUsermenu: (state, show) => {
+      state.showUsermenu = show;
     },
-    resetUserMenu: state => {
-      state.usermenu.iscollapsed = defaultCollapseState;
+    showSearchbar: (state, show) => {
+      state.showSearchbar = show;
     },
-    collapseUserMenu: state => {
-      state.usermenu.iscollapsed = true;
-    },
-    resetSplitScreen: state => {
-      state.splitsceen = initializeSplitScreen();
+    showIdentify: (state, show) => {
+      state.showIdentify = show;
     },
-    toggleDrawModeLine: state => {
-      if (state.drawMode) {
-        state.drawMode = null;
-      } else {
-        state.drawMode = "LineString";
-      }
+    showLayers: (state, show) => {
+      state.showLayers = show;
     },
-    activateDrawModePolygon: state => {
-      state.drawMode = "Polygon";
+    showPdfTool: (state, show) => {
+      state.showPdfTool = show;
     }
-  },
-  actions: {}
+  }
 };
-
-export default Application;
--- a/client/src/store/bottlenecks.js	Tue Nov 06 10:02:19 2018 +0100
+++ b/client/src/store/bottlenecks.js	Tue Nov 06 13:54:10 2018 +0100
@@ -14,18 +14,39 @@
  */
 import { HTTP } from "../application/lib/http";
 import { WFS } from "ol/format.js";
+import { displayError } from "../application/lib/errors.js";
 
-const Bottlenecks = {
+export default {
   namespaced: true,
   state: {
-    bottlenecks: []
+    bottlenecks: [],
+    selectedBottleneck: null,
+    surveys: [],
+    selectedSurvey: null
   },
   mutations: {
     setBottlenecks: (state, bottlenecks) => {
       state.bottlenecks = bottlenecks;
+    },
+    setSelectedBottleneck: (state, name) => {
+      state.selectedBottleneck = name;
+    },
+    setSurveys(state, surveys) {
+      state.surveys = surveys;
+    },
+    setSelectedSurvey(state, survey) {
+      state.selectedSurvey = survey;
     }
   },
   actions: {
+    setSelectedBottleneck({ state, commit, dispatch }, name) {
+      if (name !== state.selectedBottleneck) {
+        commit("setSelectedSurvey", null);
+        commit("fairwayprofile/clearCurrentProfile", null, { root: true });
+      }
+      commit("setSelectedBottleneck", name);
+      dispatch("querySurveys", name);
+    },
     loadBottlenecks({ commit }) {
       var bottleneckFeatureCollectionRequest = new WFS().writeGetFeature({
         srsName: "EPSG:4326",
@@ -49,8 +70,29 @@
       ).then(response => {
         commit("setBottlenecks", response.data.features);
       });
+    },
+    querySurveys({ commit }, name) {
+      if (name) {
+        HTTP.get("/surveys/" + name, {
+          headers: {
+            "X-Gemma-Auth": localStorage.getItem("token"),
+            "Content-type": "text/xml; charset=UTF-8"
+          }
+        })
+          .then(response => {
+            commit("setSurveys", response.data.surveys);
+          })
+          .catch(error => {
+            commit("setSurveys", []);
+            const { status, data } = error.response;
+            displayError({
+              title: "Backend Error",
+              message: `${status}: ${data.message || data}`
+            });
+          });
+      } else {
+        commit("setSurveys", []);
+      }
     }
   }
 };
-
-export default Bottlenecks;
--- a/client/src/store/fairway.js	Tue Nov 06 10:02:19 2018 +0100
+++ b/client/src/store/fairway.js	Tue Nov 06 13:54:10 2018 +0100
@@ -21,11 +21,10 @@
 
 const DEMOLEVEL = 149.345;
 
-const FairwayProfile = {
+export default {
   namespaced: true,
   state: {
     additionalSurvey: "",
-    availableSurveys: null,
     totalLength: 0,
     minAlt: 0,
     maxAlt: 0,
@@ -34,8 +33,7 @@
     selectedWaterLevel: DEMOLEVEL,
     fairwayCoordinates: [],
     startPoint: null,
-    endPoint: null,
-    selectedMorph: null
+    endPoint: null
   },
   getters: {
     length: state => {
@@ -49,12 +47,6 @@
     setAdditionalSurvey: (state, additionalSurvey) => {
       state.additionalSurvey = additionalSurvey;
     },
-    setSelectedMorph: (state, selectedMorph) => {
-      state.selectedMorph = selectedMorph;
-    },
-    setAvailableSurveys: (state, surveys) => {
-      state.availableSurveys = surveys;
-    },
     setSelectedWaterLevel: (state, level) => {
       state.selectedWaterLevel = level;
     },
@@ -101,13 +93,13 @@
     }
   },
   actions: {
-    loadProfile({ commit, state }, date_info) {
+    loadProfile({ commit, state }, survey) {
       return new Promise((resolve, reject) => {
         const profileLine = new LineString([state.startPoint, state.endPoint]);
         const geoJSON = generateFeatureRequest(
           profileLine,
-          state.selectedMorph.bottleneck_id,
-          date_info
+          survey.bottleneck_id,
+          survey.date_info
         );
         HTTP.post("/cross", geoJSON, {
           headers: { "X-Gemma-Auth": localStorage.getItem("token") }
@@ -115,7 +107,7 @@
           .then(response => {
             commit("profileLoaded", {
               response: response,
-              surveyDate: date_info
+              surveyDate: survey.date_info
             });
             resolve(response);
           })
@@ -126,5 +118,3 @@
     }
   }
 };
-
-export default FairwayProfile;
--- a/client/src/store/identify.js	Tue Nov 06 10:02:19 2018 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,43 +0,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>
- */
-
-// note that some identified features may not have an id
-// especially related to drawing in our own vector layer
-
-const IndentifyStore = {
-  namespaced: true,
-  state: {
-    identifiedFeatures: [],
-    currentMeasurement: null
-  },
-  getters: {
-    identifiedFeatures: state => {
-      return state.identifiedFeatures;
-    },
-    currentMeasurement: state => {
-      return state.currentMeasurement;
-    }
-  },
-  mutations: {
-    setIdentifiedFeatures: (state, identifiedFeatures) => {
-      state.identifiedFeatures = identifiedFeatures;
-    },
-    setCurrentMeasurement: (state, measurement) => {
-      state.currentMeasurement = measurement;
-    }
-  }
-};
-
-export default IndentifyStore;
--- a/client/src/store/map.js	Tue Nov 06 10:02:19 2018 +0100
+++ b/client/src/store/map.js	Tue Nov 06 13:54:10 2018 +0100
@@ -18,22 +18,35 @@
 import TileWMS from "ol/source/TileWMS.js";
 import { Tile as TileLayer, Vector as VectorLayer } from "ol/layer.js";
 import OSM from "ol/source/OSM";
-import { Stroke, Style, Fill, Text, Circle as CircleStyle } from "ol/style.js";
+import {
+  Icon,
+  Stroke,
+  Style,
+  Fill,
+  Text,
+  Circle as CircleStyle
+} from "ol/style.js";
 import VectorSource from "ol/source/Vector.js";
+import Point from "ol/geom/Point.js";
 import { bbox as bboxStrategy } from "ol/loadingstrategy";
 import { HTTP } from "../application/lib/http";
 
-const MapStore = {
+export default {
   namespaced: true,
   state: {
     openLayersMap: null,
+    identifiedFeatures: [],
+    currentMeasurement: null,
+    // there are three states of drawMode: null, "LineString", "Polygon"
+    drawMode: null,
     layers: [
       {
         name: "Open Streetmap",
         data: new TileLayer({
           source: new OSM()
         }),
-        isVisible: true
+        isVisible: true,
+        showInLegend: true
       },
       {
         name: "Inland ECDIS chart Danube",
@@ -44,7 +57,8 @@
             params: { LAYERS: "d4d", VERSION: "1.1.1", TILED: true }
           })
         }),
-        isVisible: true
+        isVisible: true,
+        showInLegend: true
       },
       {
         name: "Fairway Dimensions",
@@ -72,7 +86,8 @@
             ];
           }
         }),
-        isVisible: true
+        isVisible: true,
+        showInLegend: true
       },
       {
         name: "Waterway Area, named",
@@ -87,7 +102,8 @@
             })
           })
         }),
-        isVisible: false
+        isVisible: false,
+        showInLegend: true
       },
       {
         name: "Waterway Area",
@@ -102,7 +118,8 @@
             })
           })
         }),
-        isVisible: true
+        isVisible: true,
+        showInLegend: true
       },
       {
         name: "Waterway Axis",
@@ -118,7 +135,8 @@
             })
           })
         }),
-        isVisible: true
+        isVisible: true,
+        showInLegend: true
       },
       {
         name: "Distance marks",
@@ -128,7 +146,8 @@
             strategy: bboxStrategy
           })
         }),
-        isVisible: false
+        isVisible: false,
+        showInLegend: true
       },
       {
         name: "Bottlenecks",
@@ -146,7 +165,8 @@
             })
           })
         }),
-        isVisible: true
+        isVisible: true,
+        showInLegend: true
       },
       {
         name: "Bottleneck isolines",
@@ -173,7 +193,8 @@
             } // TODO  tile.setState(TileState.ERROR);
           })
         }),
-        isVisible: false
+        isVisible: false,
+        showInLegend: true
       },
       {
         name: "Distance marks, Axis",
@@ -209,13 +230,62 @@
             }
           }
         }),
-        isVisible: true
+        isVisible: true,
+        showInLegend: true
+      },
+      {
+        name: "Draw Tool",
+        data: new VectorLayer({
+          source: new VectorSource({ wrapX: false }),
+          style: function(feature) {
+            // adapted from OpenLayer's LineString Arrow Example
+            var geometry = feature.getGeometry();
+            var styles = [
+              // linestring
+              new Style({
+                stroke: new Stroke({
+                  color: "#369aca",
+                  width: 2
+                })
+              })
+            ];
+
+            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;
+          }
+        }),
+        isVisible: true,
+        showInLegend: false
       }
     ]
   },
   getters: {
-    layers: state => {
-      return state.layers;
+    layersForLegend: state => {
+      return state.layers.filter(layer => layer.showInLegend);
     },
     getLayerByName: state => name => {
       return state.layers.find(layer => layer.name === name);
@@ -228,8 +298,22 @@
     },
     setOpenLayersMap: (state, map) => {
       state.openLayersMap = map;
+    },
+    setIdentifiedFeatures: (state, identifiedFeatures) => {
+      state.identifiedFeatures = identifiedFeatures;
+    },
+    setCurrentMeasurement: (state, measurement) => {
+      state.currentMeasurement = measurement;
+    },
+    toggleDrawModeLine: state => {
+      if (state.drawMode) {
+        state.drawMode = null;
+      } else {
+        state.drawMode = "LineString";
+      }
+    },
+    activateDrawModePolygon: state => {
+      state.drawMode = "Polygon";
     }
   }
 };
-
-export default MapStore;
--- a/client/src/store/user.js	Tue Nov 06 10:02:19 2018 +0100
+++ b/client/src/store/user.js	Tue Nov 06 13:54:10 2018 +0100
@@ -15,27 +15,15 @@
 
 import { HTTP } from "../application/lib/http";
 
-const User = {
+export default {
   namespaced: true,
   state: {
-    authenticated: false,
+    isAuthenticated: false,
     expires: null,
     roles: [],
     user: ""
   },
   getters: {
-    isAuthenticated: state => {
-      return state.authenticated;
-    },
-    userinfo: state => {
-      return state.user;
-    },
-    roles: state => {
-      return state.roles;
-    },
-    expires: state => {
-      return state.expires;
-    },
     isWaterwayAdmin: state => {
       return state.roles.includes("waterway_admin");
     },
@@ -44,7 +32,7 @@
     }
   },
   mutations: {
-    auth_success: (state, data) => {
+    authSuccess: (state, data) => {
       const { token, user, expires, roles } = data;
       localStorage.setItem("expires", expires);
       localStorage.setItem("roles", roles);
@@ -55,24 +43,24 @@
       state.user = user;
       state.authenticated = true;
     },
-    clear_auth: state => {
+    clearAuth: state => {
       state.authenticated = false;
       state.expires = null;
       state.roles = [];
       state.user = "";
       localStorage.clear();
     },
-    set_user: (state, name) => {
+    setUser: (state, name) => {
       state.user = name;
     },
-    set_roles: (state, roles) => {
+    setRoles: (state, roles) => {
       state.roles = roles;
     },
-    set_expires: (state, expires) => {
+    setExpires: (state, expires) => {
       state.expires = expires;
     },
-    set_authenticate: state => {
-      state.authenticated = true;
+    setIsAuthenticate: state => {
+      state.isAuthenticated = true;
     }
   },
   actions: {
@@ -81,16 +69,14 @@
       return new Promise((resolve, reject) => {
         HTTP.post("/login", user)
           .then(response => {
-            commit("auth_success", response.data);
+            commit("authSuccess", response.data);
             resolve(response);
           })
           .catch(error => {
-            commit("clear_auth");
+            commit("clearAuth");
             reject(error);
           });
       });
     }
   }
 };
-
-export default User;
--- a/client/src/store/usermanagement.js	Tue Nov 06 10:02:19 2018 +0100
+++ b/client/src/store/usermanagement.js	Tue Nov 06 13:54:10 2018 +0100
@@ -27,7 +27,7 @@
   };
 };
 
-const UserManagement = {
+export default {
   namespaced: true,
   state: {
     users: null,
@@ -145,5 +145,3 @@
     }
   }
 };
-
-export default UserManagement;
--- a/client/src/usermanagement/Userdetail.vue	Tue Nov 06 10:02:19 2018 +0100
+++ b/client/src/usermanagement/Userdetail.vue	Tue Nov 06 13:54:10 2018 +0100
@@ -107,7 +107,7 @@
  */
 import { HTTP } from "../application/lib/http";
 import { displayError } from "../application/lib/errors.js";
-import { mapGetters } from "vuex";
+import { mapState } from "vuex";
 import PasswordField from "./Passwordfield";
 
 const emptyErrormessages = () => {
@@ -189,7 +189,7 @@
       if (this.currentUser.isNew) return "N.N";
       return "";
     },
-    ...mapGetters("application", ["countries"]),
+    ...mapState("application", ["countries"]),
     user() {
       return this.$store.getters["usermanagement/currentUser"];
     },
--- a/client/src/usermanagement/Usermanagement.vue	Tue Nov 06 10:02:19 2018 +0100
+++ b/client/src/usermanagement/Usermanagement.vue	Tue Nov 06 13:54:10 2018 +0100
@@ -193,7 +193,7 @@
  */
 import Userdetail from "./Userdetail";
 import store from "../store";
-import { mapGetters } from "vuex";
+import { mapGetters, mapState } from "vuex";
 import { displayError } from "../application/lib/errors.js";
 
 export default {
@@ -210,12 +210,12 @@
   },
   computed: {
     ...mapGetters("usermanagement", ["isUserDetailsVisible"]),
-    ...mapGetters("application", ["sidebarCollapsed", "isUsermenuCollapsed"]),
+    ...mapState("application", ["showSidebar", "showUsermenu"]),
     spacerStyle() {
       return {
         spacer: true,
-        "spacer-expanded": !(this.isUsermenuCollapsed && this.sidebarCollapsed),
-        "spacer-collapsed": this.isUsermenuCollapsed && this.sidebarCollapsed
+        "spacer-expanded": (this.showUsermenu && this.showSidebar),
+        "spacer-collapsed": !this.showUsermenu && this.showSidebar
       };
     },
     users() {
--- a/client/src/zoom/zoom.vue	Tue Nov 06 10:02:19 2018 +0100
+++ b/client/src/zoom/zoom.vue	Tue Nov 06 13:54:10 2018 +0100
@@ -30,7 +30,7 @@
 export default {
   name: "zoom",
   computed: {
-    ...mapState("mapstore", ["openLayersMap"]),
+    ...mapState("map", ["openLayersMap"]),
     zoomLevel: {
       get() {
         return this.openLayersMap.getView().getZoom();