changeset 3079:57255fda7594

client: compare surveys on map The compare survey is now displayed in a second map.
author Markus Kottlaender <markus@intevation.de>
date Thu, 18 Apr 2019 12:40:42 +0200
parents 71129566acdf
children 22857a12ed9e
files client/src/components/fairway/Profiles.vue client/src/components/layers/Layerselect.vue client/src/components/map/Map.vue client/src/lib/mixins.js client/src/store/application.js client/src/store/fairway.js client/src/store/map.js
diffstat 7 files changed, 204 insertions(+), 180 deletions(-) [+]
line wrap: on
line diff
--- a/client/src/components/fairway/Profiles.vue	Thu Apr 18 10:17:36 2019 +0200
+++ b/client/src/components/fairway/Profiles.vue	Thu Apr 18 12:40:42 2019 +0200
@@ -94,13 +94,17 @@
               </select>
             </div>
           </div>
-          <div class="mt-3">
+          <div class="mt-3" v-if="additionalSurvey">
             <button
-              :disabled="!additionalSurvey"
               class="btn btn-info btn-sm w-100"
-              @click="showSurveyDiffences"
+              @click="differencesVisible ? showSurvey() : showDifferences()"
             >
-              <translate>Show differences</translate>
+              <translate v-if="differencesVisible" key="showsurvey"
+                >Show survey</translate
+              >
+              <translate v-else key="showdifferences"
+                >Show differences</translate
+              >
             </button>
           </div>
           <hr class="w-100 mb-0" />
@@ -375,6 +379,16 @@
         .map(coord => parseFloat(coord.trim()))
         .filter(c => Number(c) === c);
       return coordinates.length === 4;
+    },
+    differencesVisible() {
+      return (
+        !this.openLayersMap("compare-sounding-results")
+          .getLayer("BOTTLENECKISOLINE")
+          .getVisible() &&
+        this.openLayersMap("compare-sounding-results")
+          .getLayer("DIFFERENCES")
+          .getVisible()
+      );
     }
   },
   watch: {
@@ -387,6 +401,20 @@
       this.loadProfile(survey);
     },
     additionalSurvey(survey) {
+      if (survey) {
+        this.loadDifferences();
+        this.$store.commit("application/addPane", {
+          id: "compare-sounding-results",
+          component: "Map"
+        });
+        this.$store.commit("application/paneRotate", 4);
+      } else {
+        this.$store.commit(
+          "application/removePane",
+          "compare-sounding-results"
+        );
+        this.$store.commit("application/paneRotate", 1);
+      }
       this.loadProfile(survey);
     },
     selectedCut(cut) {
@@ -396,7 +424,7 @@
     }
   },
   methods: {
-    showSurveyDiffences() {
+    loadDifferences() {
       this.$store.commit("fairwayprofile/setDifferencesLoading", true);
       HTTP.post(
         "/diff",
@@ -411,24 +439,6 @@
           }
         }
       )
-        .then(() => {
-          this.openLayersMap
-            .getLayer("DIFFERENCES")
-            .getSource()
-            .updateParams({
-              cql_filter:
-                "objnam='" +
-                this.selectedBottleneck +
-                "' AND " +
-                "minuend='" +
-                this.selectedSurvey.date_info +
-                "' AND subtrahend='" +
-                this.additionalSurvey.date_info +
-                "'"
-            });
-          this.openLayersMap.getLayer("BOTTLENECKISOLINE").setVisible(false);
-          this.openLayersMap.getLayer("DIFFERENCES").setVisible(true);
-        })
         .catch(error => {
           const { status, data } = error.response;
           displayError({
@@ -440,6 +450,22 @@
           this.$store.commit("fairwayprofile/setDifferencesLoading", false);
         });
     },
+    showDifferences() {
+      this.openLayersMap("compare-sounding-results")
+        .getLayer("BOTTLENECKISOLINE")
+        .setVisible(false);
+      this.openLayersMap("compare-sounding-results")
+        .getLayer("DIFFERENCES")
+        .setVisible(true);
+    },
+    showSurvey() {
+      this.openLayersMap("compare-sounding-results")
+        .getLayer("BOTTLENECKISOLINE")
+        .setVisible(true);
+      this.openLayersMap("compare-sounding-results")
+        .getLayer("DIFFERENCES")
+        .setVisible(false);
+    },
     close() {
       this.$store.commit("application/showProfiles", false);
     },
--- a/client/src/components/layers/Layerselect.vue	Thu Apr 18 10:17:36 2019 +0200
+++ b/client/src/components/layers/Layerselect.vue	Thu Apr 18 12:40:42 2019 +0200
@@ -60,7 +60,7 @@
     ]),
     ...mapGetters("map", ["openLayersMap"]),
     layer() {
-      return this.openLayersMap.getLayer(this.layerId);
+      return this.openLayersMap().getLayer(this.layerId);
     },
     label() {
       return this.$gettext(this.layer.get("label"));
--- a/client/src/components/map/Map.vue	Thu Apr 18 10:17:36 2019 +0200
+++ b/client/src/components/map/Map.vue	Thu Apr 18 12:40:42 2019 +0200
@@ -1,24 +1,18 @@
 <template>
   <div
-    :id="'map-' + uuid"
-    :class="[
-      'map',
-      {
-        splitscreen: this.splitscreen,
-        nocursor: this.hasActiveInteractions
-      }
-    ]"
-  ></div>
+    :id="'map-' + paneId"
+    :class="['map', { nocursor: this.hasActiveInteractions }]"
+  />
 </template>
 
 <style lang="sass" scoped>
 .map
   width: 100%
   height: 100%
-
-  &.splitscreen
-    height: 50%
-
+  background-color: #eee
+  background-image: linear-gradient(45deg, #e8e8e8 25%, transparent 25%, transparent 75%, #e8e8e8 75%, #e8e8e8), linear-gradient(45deg, #e8e8e8 25%, transparent 25%, transparent 75%, #e8e8e8 75%, #e8e8e8)
+  background-size: 20px 20px
+  background-position: 0 0, 10px 10px
   &.nocursor
     cursor: none
 </style>
@@ -43,14 +37,14 @@
 import { Map, View } from "ol";
 import { Stroke, Style, Fill } from "ol/style";
 import { displayError } from "@/lib/errors";
-import { uuid, pane } from "@/lib/mixins";
+import { pane } from "@/lib/mixins";
 import layers from "@/components/map/layers";
 import "ol/ol.css";
 
 /* for the sake of debugging */
 /* eslint-disable no-console */
 export default {
-  mixins: [uuid, pane],
+  mixins: [pane],
   data() {
     return {
       map: null,
@@ -60,27 +54,62 @@
   computed: {
     ...mapState("map", ["initialLoad", "extent", "syncedView"]),
     ...mapState("bottlenecks", ["selectedSurvey"]),
-    ...mapState("application", ["showSplitscreen", "panes", "paneRotate"]),
+    ...mapState("fairwayprofile", ["additionalSurvey"]),
+    ...mapState("application", ["panes", "paneRotate"]),
     ...mapState("imports", ["selectedStretchId"]),
     layers() {
       return layers();
     },
     hasActiveInteractions() {
-      let active = false;
-      if (this.map) {
+      return (
+        this.map &&
         this.map
           .getInteractions()
           .getArray()
-          .filter(i =>
-            ["linetool", "polygontool", "cuttool"].includes(i.get("id"))
-          )
-          .forEach(i => {
-            if (i.getActive()) {
-              active = true;
-            }
-          });
+          .filter(
+            i =>
+              ["linetool", "polygontool", "cuttool"].includes(i.get("id")) &&
+              i.getActive()
+          ).length
+      );
+    }
+  },
+  watch: {
+    panes() {
+      this.$nextTick(() => this.map.updateSize());
+    },
+    paneRotate() {
+      this.$nextTick(() => this.map.updateSize());
+    },
+    selectedSurvey(survey) {
+      if (this.paneId === "main") {
+        if (survey) {
+          this.updateBottleneckFilter(survey.bottleneck_id, survey.date_info);
+        } else {
+          this.updateBottleneckFilter("does_not_exist", "1999-10-01");
+        }
       }
-      return active;
+    },
+    additionalSurvey(survey) {
+      if (this.paneId === "compare-sounding-results") {
+        if (survey) {
+          this.updateBottleneckFilter(survey.bottleneck_id, survey.date_info);
+        } else {
+          this.updateBottleneckFilter("does_not_exist", "1999-10-01");
+        }
+      }
+    },
+    selectedStretchId(id) {
+      this.layers
+        .get("STRETCHES")
+        .getSource()
+        .getFeatures()
+        .forEach(f => {
+          f.set("highlighted", false);
+          if (id === f.getId()) {
+            f.set("highlighted", true);
+          }
+        });
     }
   },
   methods: {
@@ -96,112 +125,88 @@
           });
       }
       this.layers.get("BOTTLENECKISOLINE").setVisible(exists);
-    }
-  },
-  watch: {
-    showSplitscreen(show) {
-      if (show) {
-        setTimeout(() => {
-          this.splitscreen = true;
-        }, 350);
-      } else {
-        this.splitscreen = false;
+    },
+    initMap() {
+      if (!this.syncedView) {
+        this.$store.commit(
+          "map/syncedView",
+          new View({
+            center: [this.extent.lon, this.extent.lat],
+            minZoom: 5, // restrict zooming out to ~size of Europe for width 1000px
+            zoom: this.extent.zoom,
+            projection: "EPSG:3857"
+          })
+        );
       }
-    },
-    panes() {
-      this.$nextTick(() => this.map.updateSize());
-    },
-    paneRotate() {
-      this.$nextTick(() => this.map.updateSize());
-    },
-    splitscreen() {
-      this.$nextTick(() => this.map.updateSize());
-    },
-    selectedSurvey(newSelectedSurvey) {
-      if (newSelectedSurvey) {
-        this.updateBottleneckFilter(
-          newSelectedSurvey.bottleneck_id,
-          newSelectedSurvey.date_info
-        );
-      } else {
-        this.updateBottleneckFilter("does_not_exist", "1999-10-01");
+      this.map = new Map({
+        layers: this.layers.config,
+        target: "map-" + this.paneId,
+        controls: [],
+        view: this.syncedView
+      });
+      this.map.getLayer = id => this.layers.get(id);
+
+      // store map position on every move
+      // will be obsolete once we abandoned the separated admin context
+      this.map.on("moveend", event => {
+        const center = event.map.getView().getCenter();
+        this.$store.commit("map/extent", {
+          lat: center[1],
+          lon: center[0],
+          zoom: event.map.getView().getZoom()
+        });
+      });
+      this.$store.dispatch("map/openLayersMap", this.map);
+
+      // move to user specific default extent if map loads for the first time
+      // checking initialLoad will be obsolete once we abandoned the separated admin context
+      if (this.initialLoad) {
+        this.$store.commit("map/initialLoad", false);
+        var currentUser = this.$store.state.user.user;
+        HTTP.get("/users/" + currentUser, {
+          headers: {
+            "X-Gemma-Auth": localStorage.getItem("token"),
+            "Content-type": "text/xml; charset=UTF-8"
+          }
+        })
+          .then(response => {
+            this.$store.dispatch("map/moveToBoundingBox", {
+              boundingBox: [
+                response.data.extent.x1,
+                response.data.extent.y1,
+                response.data.extent.x2,
+                response.data.extent.y2
+              ],
+              zoom: 17,
+              preventZoomOut: true
+            });
+          })
+          .catch(error => {
+            const { status, data } = error.response;
+            displayError({
+              title: this.$gettext("Backend Error"),
+              message: `${status}: ${data.message || data}`
+            });
+          });
       }
-    },
-    selectedStretchId(id) {
-      this.layers
-        .get("STRETCHES")
-        .getSource()
-        .getFeatures()
-        .forEach(f => {
-          f.set("highlighted", false);
-          if (id === f.getId()) {
-            f.set("highlighted", true);
-          }
-        });
+
+      this.$store.dispatch("map/initIdentifyTool", this.map);
     }
   },
   mounted() {
-    if (!this.syncedView) {
-      this.$store.commit(
-        "map/syncedView",
-        new View({
-          center: [this.extent.lon, this.extent.lat],
-          minZoom: 5, // restrict zooming out to ~size of Europe for width 1000px
-          zoom: this.extent.zoom,
-          projection: "EPSG:3857"
-        })
+    this.initMap();
+
+    if (this.selectedSurvey && this.paneId === "main") {
+      this.updateBottleneckFilter(
+        this.selectedSurvey.bottleneck_id,
+        this.selectedSurvey.date_info
       );
     }
-    this.map = new Map({
-      layers: this.layers.config,
-      target: "map-" + this.uuid,
-      controls: [],
-      view: this.syncedView
-    });
-    this.map.getLayer = id => this.layers.get(id);
-
-    // store map position on every move
-    // will be obsolete once we abandoned the separated admin context
-    this.map.on("moveend", event => {
-      const center = event.map.getView().getCenter();
-      this.$store.commit("map/extent", {
-        lat: center[1],
-        lon: center[0],
-        zoom: event.map.getView().getZoom()
-      });
-    });
-    this.$store.dispatch("map/openLayersMap", this.map);
-
-    // move to user specific default extent if map loads for the first timeout
-    // checking initialLoad will be obsolete once we abandoned the separated admin context
-    if (this.initialLoad) {
-      this.$store.commit("map/initialLoad", false);
-      var currentUser = this.$store.state.user.user;
-      HTTP.get("/users/" + currentUser, {
-        headers: {
-          "X-Gemma-Auth": localStorage.getItem("token"),
-          "Content-type": "text/xml; charset=UTF-8"
-        }
-      })
-        .then(response => {
-          this.$store.dispatch("map/moveToBoundingBox", {
-            boundingBox: [
-              response.data.extent.x1,
-              response.data.extent.y1,
-              response.data.extent.x2,
-              response.data.extent.y2
-            ],
-            zoom: 17,
-            preventZoomOut: true
-          });
-        })
-        .catch(error => {
-          const { status, data } = error.response;
-          displayError({
-            title: this.$gettext("Backend Error"),
-            message: `${status}: ${data.message || data}`
-          });
-        });
+    if (this.additionalSurvey && this.paneId === "compare-sounding-results") {
+      this.updateBottleneckFilter(
+        this.additionalSurvey.bottleneck_id,
+        this.additionalSurvey.date_info
+      );
     }
 
     // load configured bottleneck colors
@@ -233,8 +238,6 @@
       .catch(error => {
         console.log(error);
       });
-
-    this.$store.dispatch("map/initIdentifyTool", this.map);
   },
   destroyed() {
     this.$store.commit("map/removeOpenLayersMap", this.map);
--- a/client/src/lib/mixins.js	Thu Apr 18 10:17:36 2019 +0200
+++ b/client/src/lib/mixins.js	Thu Apr 18 12:40:42 2019 +0200
@@ -11,9 +11,6 @@
  * Author(s):
  * Markus Kottländer <markus.kottlaender@intevation.de>
  */
-
-import * as uuidGen from "uuid";
-
 const sortTable = {
   data() {
     return {
@@ -31,14 +28,6 @@
   }
 };
 
-const uuid = {
-  computed: {
-    uuid() {
-      return uuidGen.v4();
-    }
-  }
-};
-
 const pane = {
   props: {
     paneCreated: Function,
@@ -65,4 +54,4 @@
   }
 };
 
-export { sortTable, uuid, pane };
+export { sortTable, pane };
--- a/client/src/store/application.js	Thu Apr 18 10:17:36 2019 +0200
+++ b/client/src/store/application.js	Thu Apr 18 12:40:42 2019 +0200
@@ -83,8 +83,14 @@
     popup: (state, popup) => {
       state.popup = popup;
     },
-    panes: (state, panes) => {
-      state.panes = panes;
+    addPane: (state, pane) => {
+      state.panes.push(pane);
+    },
+    removePane: (state, paneId) => {
+      const index = state.panes.findIndex(p => p.id === paneId);
+      if (index !== -1) {
+        state.panes.splice(index, 1);
+      }
     },
     paneRotate: (state, rotate) => {
       state.paneRotate = rotate;
--- a/client/src/store/fairway.js	Thu Apr 18 10:17:36 2019 +0200
+++ b/client/src/store/fairway.js	Thu Apr 18 12:40:42 2019 +0200
@@ -219,7 +219,7 @@
           Promise.all(profileLoaders)
             .then(() => {
               commit("map/cutToolEnabled", false, { root: true });
-              const los3 = rootGetters["map/openLayersMap"].getLayer(
+              const los3 = rootGetters["map/openLayersMap"]().getLayer(
                 "FAIRWAYDIMENSIONSLOS3"
               );
               los3.getSource().forEachFeatureIntersectingExtent(
@@ -241,7 +241,7 @@
                   }
                 }
               );
-              const los2 = rootGetters["map/openLayersMap"].getLayer(
+              const los2 = rootGetters["map/openLayersMap"]().getLayer(
                 "FAIRWAYDIMENSIONSLOS2"
               );
               los2.getSource().forEachFeatureIntersectingExtent(
@@ -263,7 +263,7 @@
                   }
                 }
               );
-              const los1 = rootGetters["map/openLayersMap"].getLayer(
+              const los1 = rootGetters["map/openLayersMap"]().getLayer(
                 "FAIRWAYDIMENSIONSLOS1"
               );
               los1.getSource().forEachFeatureIntersectingExtent(
--- a/client/src/store/map.js	Thu Apr 18 10:17:36 2019 +0200
+++ b/client/src/store/map.js	Thu Apr 18 12:40:42 2019 +0200
@@ -52,8 +52,10 @@
   namespaced: true,
   state: init(),
   getters: {
-    openLayersMap: state => {
-      return state.openLayersMaps.length ? state.openLayersMaps[0] : null;
+    openLayersMap: state => id => {
+      return state.openLayersMaps.find(
+        map => map.getTarget() === "map-" + (id || "main")
+      );
     },
     filteredIdentifiedFeatures: state => {
       return state.identifiedFeatures.filter(f => f.getId());
@@ -486,12 +488,11 @@
         }
       });
     },
-    moveToBoundingBox({ getters }, { boundingBox, zoom, preventZoomOut }) {
+    moveToBoundingBox({ state }, { boundingBox, zoom, preventZoomOut }) {
       const extent = transformExtent(boundingBox, "EPSG:4326", "EPSG:3857");
-      let view = getters.openLayersMap.getView();
-      const currentZoom = view.getZoom();
+      const currentZoom = state.syncedView.getZoom();
       zoom = zoom || currentZoom;
-      view.fit(extent, {
+      state.syncedView.fit(extent, {
         maxZoom: preventZoomOut ? Math.max(zoom, currentZoom) : zoom,
         duration: 700
       });
@@ -500,13 +501,12 @@
       const boundingBox = bbox(feature.geometry);
       dispatch("moveToBoundingBox", { boundingBox, zoom, preventZoomOut });
     },
-    moveMap({ getters }, { coordinates, zoom, preventZoomOut }) {
-      let view = getters.openLayersMap.getView();
-      const currentZoom = view.getZoom();
+    moveMap({ state }, { coordinates, zoom, preventZoomOut }) {
+      const currentZoom = state.syncedView.getZoom();
       zoom = zoom || currentZoom;
-      view.animate({
+      state.syncedView.animate({
         zoom: preventZoomOut ? Math.max(zoom, currentZoom) : zoom,
-        center: fromLonLat(coordinates, view.getProjection()),
+        center: fromLonLat(coordinates, state.syncedView.getProjection()),
         duration: 700
       });
     }