changeset 1063:7ec2133c6404

client: add area measurement. simpify code * Add a third draw mode which can only be activated when no morphology is selected and we are already in LineString mode. It adds an area calculation. Because the Polygon drawMode ends on a double click, there needs to be an extra callback for this to run identify so that the area calculation is shown all times. * Add Bernhard as author to some files and also simplify copyright note. * Remove DRAWMODES in the code to simplify as this is just one indirection used once in stores/application.js. * Use mapState instead mapGetters to get the drawMode at all places to save some code lines.
author Bernhard Reiter <bernhard@intevation.de>
date Thu, 25 Oct 2018 23:16:53 +0200
parents d3bdad8ed8d3
children 907321455f39 4372a489c9ad 3f14b73414e2
files client/src/application/stores/application.js client/src/identify/Identify.vue client/src/linetool/Linetool.vue client/src/map/Maplayer.vue client/src/morphtool/Morphtool.vue
diffstat 5 files changed, 83 insertions(+), 69 deletions(-) [+]
line wrap: on
line diff
--- a/client/src/application/stores/application.js	Thu Oct 25 21:10:23 2018 +0200
+++ b/client/src/application/stores/application.js	Thu Oct 25 23:16:53 2018 +0200
@@ -1,17 +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>
- * Markus Kottländer <markus.kottlaender@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";
@@ -25,10 +25,6 @@
   };
 };
 
-const DRAWMODES = {
-  LINE: "LineString"
-};
-
 const Application = {
   namespaced: true,
   state: {
@@ -43,6 +39,7 @@
       iscollapsed: defaultCollapseState
     },
     countries: ["AT", "SK", "HU", "HR", "RS", "BiH", "BG", "RO", "UA"],
+    // there are three states of drawMode: null, "LineString", "Polygon"
     drawMode: null,
     version
   },
@@ -68,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;
@@ -122,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: {}
--- a/client/src/identify/Identify.vue	Thu Oct 25 21:10:23 2018 +0200
+++ b/client/src/identify/Identify.vue	Thu Oct 25 23:16:53 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 21:10:23 2018 +0200
+++ b/client/src/linetool/Linetool.vue	Thu Oct 25 23:16:53 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/map/Maplayer.vue	Thu Oct 25 21:10:23 2018 +0200
+++ b/client/src/map/Maplayer.vue	Thu Oct 25 23:16:53 2018 +0200
@@ -49,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";
@@ -105,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() {
@@ -141,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();
@@ -157,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 (m)", 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
@@ -229,7 +239,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);
       });
     },
@@ -362,10 +374,11 @@
     }
   },
   watch: {
-    drawMode() {
+    drawMode(newValue) {
       if (this.interaction) {
         this.removeCurrentInteraction();
-      } else {
+      }
+      if (newValue) {
         this.activateInteraction();
       }
     },
--- a/client/src/morphtool/Morphtool.vue	Thu Oct 25 21:10:23 2018 +0200
+++ b/client/src/morphtool/Morphtool.vue	Thu Oct 25 23:16:53 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() {