changeset 1090:dbf0221b1cf1

bottleneck list uses different wfs endpoint now to reduce own server-side-code the bottleneck list is now retreived as a feature collection and includes more details.
author Markus Kottlaender <markus@intevation.de>
date Tue, 30 Oct 2018 11:35:36 +0100
parents 8ae18a8d000e
children 4489e96fb2b4
files client/src/bottlenecks/Bottlenecks.vue client/src/bottlenecks/store.js
diffstat 2 files changed, 142 insertions(+), 105 deletions(-) [+]
line wrap: on
line diff
--- a/client/src/bottlenecks/Bottlenecks.vue	Mon Oct 29 15:36:11 2018 +0100
+++ b/client/src/bottlenecks/Bottlenecks.vue	Tue Oct 30 11:35:36 2018 +0100
@@ -7,38 +7,36 @@
         <h4>Bottlenecks</h4>
         <hr class="mb-0">
         <input type="text" v-model="search" placeholder="Search Bottleneck..." class="border-0 w-100 p-2" />
-        <div class="bottleneck-table d-flex flex-column">
-            <div class="d-flex flex-row p-2 border-top">
-                <div>
-                    <a href="#" @click="sortBy('name')" class="sort-link">Name</a>
-                    <i :class="sortClass" v-if="sortColumn === 'name'"></i>
-                </div>
-                <!--<div>
-                    <a href="#" @click="sortBy('latestMeasurement')">Latest Measurement</a>
-                    <i :class="sortClass" v-if="sortColumn === 'latestMeasurement'"></i>
-                </div>-->
-                <div></div>
+        <div class="row p-2 border-top">
+            <div class="col-5">
+                <a href="#" @click="sortBy('name')" class="sort-link">Name</a>
+                <i :class="sortClass" v-if="sortColumn === 'name'"></i>
+            </div>
+            <div class="col-5">
+                <a href="#" @click="sortBy('latestMeasurement')" class="sort-link">Latest Measurement</a>
+                <i :class="sortClass" v-if="sortColumn === 'latestMeasurement'"></i>
             </div>
-            <div class="d-flex flex-column bottleneck-list">
-                <div v-for="bottleneck in filteredAndSortedBottlenecks" :key="bottleneck.name" class="border-top">
-                    <div class="d-flex flex-row justify-content-between p-2">
-                        <div>
-                            <a href="#" class="d-block" @click="moveToBottleneck(bottleneck)">
-                                {{ bottleneck.name }}
-                            </a>
-                        </div>
-                        <!--<td>2018-10-25</td>-->
-                        <div class="text-right">
-                            <button type="button" class="btn btn-sm btn-outline-secondary" @click="queryBottleneck(bottleneck)">
-                                <i class="fa fa-angle-down"></i>
-                            </button>
-                        </div>
-                    </div>
-                    <div :class="['surveys', {open: selectedBottleneck === bottleneck}]">
-                       <a href="#" class="d-block p-2 border-top" v-for="(survey, index) in surveys[bottleneck.name]" :key="index" @click="selectSurvey(survey)">
-                          {{ survey.date_info }}
-                        </a>
-                    </div>
+            <div class="col-2"></div>
+        </div>
+        <div class="bottleneck-list">
+            <div v-for="bottleneck in filteredAndSortedBottlenecks()" :key="bottleneck.properties.name" class="border-top row mx-0 py-2">
+                <div class="col-5 text-left">
+                    <a href="#" class="d-block" @click="moveToBottleneck(bottleneck)">
+                        {{ bottleneck.properties.name }}
+                    </a>
+                </div>
+                <div class="col-5">
+                    {{ displayCurrentSurvey(bottleneck) }}
+                </div>
+                <div class="col-2 text-right">
+                    <button type="button" class="btn btn-sm btn-outline-secondary" @click="toggleBottleneck(bottleneck)">
+                        <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)">
+                      {{ survey.date_info }}
+                    </a>
                 </div>
             </div>
         </div>
@@ -49,14 +47,14 @@
 /*
  * 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 
+ *
+ * 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>
  */
@@ -91,24 +89,6 @@
         shadow: true
       };
     },
-    filteredAndSortedBottlenecks() {
-      return this.bottlenecks
-        .filter(bn => {
-          return bn.name.toLowerCase().includes(this.search.toLowerCase());
-        })
-        .sort((bnA, bnB) => {
-          switch (this.sortColumn) {
-            case "name":
-              if (bnA.name.toLowerCase() < bnB.name.toLowerCase())
-                return this.sortDirection === "ASC" ? -1 : 1;
-              if (bnA.name.toLowerCase() > bnB.name.toLowerCase())
-                return this.sortDirection === "ASC" ? 1 : -1;
-              return 0;
-            default:
-              return 0;
-          }
-        });
-    },
     sortClass() {
       return {
         fa: true,
@@ -119,52 +99,98 @@
     }
   },
   methods: {
-    selectSurvey(survey) {
+    filteredAndSortedBottlenecks() {
+      return this.bottlenecks.filter(bn => {
+        const foundInName = bn.properties.name
+          .toLowerCase()
+          .includes(this.search.toLowerCase());
+        const latestMeasurement = (bn.properties.current || '');
+        const foundInLatestMeasurement = latestMeasurement
+          .substr(0, latestMeasurement.length - 1)
+          .includes(this.search.toLowerCase());
+        return foundInName || foundInLatestMeasurement;
+      }).sort((bnA, bnB) => {
+        switch (this.sortColumn) {
+          case "name":
+            if (
+              bnA.properties.name.toLowerCase() <
+              bnB.properties.name.toLowerCase()
+            )
+              return this.sortDirection === "ASC" ? -1 : 1;
+            if (
+              bnA.properties.name.toLowerCase() >
+              bnB.properties.name.toLowerCase()
+            )
+              return this.sortDirection === "ASC" ? 1 : -1;
+            return 0;
+
+          case "latestMeasurement":
+            const currentA = bnA.properties.current || '';
+            const currentB = bnB.properties.current || '';
+            if (currentA < currentB)
+              return this.sortDirection === "ASC" ? -1 : 1;
+            if (currentA > currentB)
+              return this.sortDirection === "ASC" ? 1 : -1;
+            return 0;
+
+          default:
+            return 0;
+        }
+      });
+    },
+    selectSurvey(survey, bottleneck) {
       this.$store.commit("fairwayprofile/setSelectedMorph", survey);
-      this.$store.commit("fairwayprofile/setAvailableSurveys", {surveys: this.surveys[this.selectedBottleneck.name] });
-      this.moveToBottleneck(this.selectedBottleneck);
+      this.$store.commit("fairwayprofile/setAvailableSurveys", {
+        surveys: this.surveys[bottleneck.properties.name]
+      });
+      this.moveToBottleneck(bottleneck);
     },
     moveToBottleneck(bottleneck) {
       // TODO: make this central, duplicates code from application/Topbar.vue
-      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
-        );
-      }
+      let view = this.openLayersMap.getView();
+      const currentZoom = view.getZoom();
+      const newZoom = Math.max(17, currentZoom);
+      view.animate(
+        {
+          zoom: newZoom,
+          center: fromLonLat(
+            bottleneck.geometry.coordinates,
+            view.getProjection()
+          )
+        },
+        700
+      );
     },
     sortBy(column) {
       this.sortColumn = column;
       this.sortDirection = this.sortDirection === "ASC" ? "DESC" : "ASC";
     },
-    queryBottleneck(bottleneck) {
-      HTTP.get("/surveys/" + bottleneck.name, {
-        headers: {
-          "X-Gemma-Auth": localStorage.getItem("token"),
-          "Content-type": "text/xml; charset=UTF-8"
-        }
-      })
-        .then(response => {
-          this.surveys[bottleneck.name] = response.data.surveys;
-          this.selectedBottleneck =
-            this.selectedBottleneck === bottleneck ? null : bottleneck;
+    toggleBottleneck(bottleneck) {
+      if (bottleneck === this.selectedBottleneck) {
+        this.selectedBottleneck = null;
+      } else {
+        HTTP.get("/surveys/" + bottleneck.properties.name, {
+          headers: {
+            "X-Gemma-Auth": localStorage.getItem("token"),
+            "Content-type": "text/xml; charset=UTF-8"
+          }
         })
-        .catch(error => {
-          const { status, data } = error.response;
-          displayError({
-            title: "Backend Error",
-            message: `${status}: ${data.message || data}`
+          .then(response => {
+            this.surveys[bottleneck.properties.name] = response.data.surveys;
+            this.selectedBottleneck = bottleneck;
+          })
+          .catch(error => {
+            const { status, data } = error.response;
+            displayError({
+              title: "Backend Error",
+              message: `${status}: ${data.message || data}`
+            });
           });
-        });
+      }
+    },
+    displayCurrentSurvey(bottleneck) {
+      const current = bottleneck.properties.current;
+      return current ? current.substr(0, current.length - 1) : "";
     }
   },
   mounted() {
@@ -182,7 +208,7 @@
   padding-top: $offset;
   opacity: $slight-transparent;
   border-radius: $border-radius;
-  transition: left .3s ease;
+  transition: left 0.3s ease;
   overflow: hidden;
   background: #fff;
 }
@@ -194,7 +220,7 @@
 }
 
 .bottlenecksextended {
-  width: 500px;
+  width: 600px;
 }
 
 .close-bottlenecks {
--- a/client/src/bottlenecks/store.js	Mon Oct 29 15:36:11 2018 +0100
+++ b/client/src/bottlenecks/store.js	Tue Oct 30 11:35:36 2018 +0100
@@ -1,18 +1,19 @@
 /*
  * 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 
+ *
+ * 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";
+import { WFS } from "ol/format.js";
 
 const Bottlenecks = {
   namespaced: true,
@@ -26,17 +27,27 @@
   },
   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);
-          });
+      var bottleneckFeatureCollectionRequest = new WFS().writeGetFeature({
+        srsName: "EPSG:4326",
+        featureNS: "gemma",
+        featurePrefix: "gemma",
+        featureTypes: ["bottleneck_overview"],
+        outputFormat: "application/json"
+      });
+
+      HTTP.post(
+        "/internal/wfs",
+        new XMLSerializer().serializeToString(
+          bottleneckFeatureCollectionRequest
+        ),
+        {
+          headers: {
+            "X-Gemma-Auth": localStorage.getItem("token"),
+            "Content-type": "text/xml; charset=UTF-8"
+          }
+        }
+      ).then(response => {
+        commit("setBottlenecks", response.data.features);
       });
     }
   }