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