Mercurial > gemma
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: </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();