changeset 2970:149a8f81f99e unified_import

merge with default
author Thomas Junk <thomas.junk@intevation.de>
date Fri, 05 Apr 2019 10:04:45 +0200
parents b92a8d088d8a (current diff) 78affd725ba5 (diff)
children 7a51fdfead2d
files client/src/components/Contextbox.vue client/src/components/importoverview/AdditionalDetail.vue client/src/components/importoverview/FairwayDimension.vue
diffstat 74 files changed, 1158 insertions(+), 942 deletions(-) [+]
line wrap: on
line diff
--- a/client/public/index.html	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/public/index.html	Fri Apr 05 10:04:45 2019 +0200
@@ -5,7 +5,7 @@
   SPDX-License-Identifier: AGPL-3.0-or-later
   License-Filename: LICENSES/AGPL-3.0.txt
 
-  Copyright (C) 2018 by via donau
+  Copyright (C) 2018, 2019 by via donau
    – Österreichische Wasserstraßen-Gesellschaft mbH
   Software engineering by Intevation GmbH
 -->
--- a/client/src/assets/application.scss	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/assets/application.scss	Fri Apr 05 10:04:45 2019 +0200
@@ -107,7 +107,6 @@
 
 .box-control {
   display: inline-block;
-  margin-left: 3px;
   color: #888;
   padding: 3px 7px;
   border-radius: 0.25rem;
@@ -205,10 +204,6 @@
   font-size: 0.75rem;
 }
 
-.empty {
-  margin-right: 1.25rem;
-}
-
 .truncate {
   white-space: nowrap;
   overflow: hidden;
--- a/client/src/assets/tooltip.scss	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/assets/tooltip.scss	Fri Apr 05 10:04:45 2019 +0200
@@ -17,10 +17,12 @@
 }
 
 .tooltip .tooltip-inner {
-  background: black;
-  color: white;
-  border-radius: 16px;
+  background: white;
+  box-shadow: 0 0.1rem 0.5rem rgba(0, 0, 0, 0.2);
+  color: #666;
+  border-radius: 0.25rem;
   padding: 5px 10px 4px;
+  font-size: 0.8rem;
 }
 
 .tooltip .tooltip-arrow {
@@ -29,7 +31,7 @@
   border-style: solid;
   position: absolute;
   margin: 5px;
-  border-color: black;
+  border-color: white;
   z-index: 1;
 }
 
@@ -95,7 +97,7 @@
 
 .tooltip.popover .popover-inner {
   background: #f9f9f9;
-  color: black;
+  color: white;
   padding: 24px;
   border-radius: 5px;
   box-shadow: 0 5px 30px rgba(black, 0.1);
--- a/client/src/components/Bottlenecks.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/Bottlenecks.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -47,31 +47,12 @@
           }}
         </div>
         <div class="table-cell center" style="width: 30px">
-          <a
-            class="text-info"
+          <UISpinnerButton
             @click="loadSurveys(bottleneck)"
+            :loading="loading === bottleneck"
+            :state="bottleneck === openBottleneck"
             v-if="bottleneck.properties.current"
-          >
-            <font-awesome-icon
-              class="pointer"
-              icon="spinner"
-              fixed-width
-              spin
-              v-if="loading === bottleneck"
-            ></font-awesome-icon>
-            <font-awesome-icon
-              class="pointer"
-              icon="angle-down"
-              fixed-width
-              v-if="loading !== bottleneck && openBottleneck !== bottleneck"
-            ></font-awesome-icon>
-            <font-awesome-icon
-              class="pointer"
-              icon="angle-up"
-              fixed-width
-              v-if="loading !== bottleneck && openBottleneck === bottleneck"
-            ></font-awesome-icon>
-          </a>
+          />
         </div>
       </template>
       <template v-slot:expand="{ item: bottleneck }">
--- a/client/src/components/Contextbox.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/Contextbox.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -88,6 +88,7 @@
 
 .contextboxextended {
   max-width: 660px;
+  max-height: 95vh;
 }
 
 .close-contextbox {
--- a/client/src/components/ImportApprovedGaugeMeasurement.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/ImportApprovedGaugeMeasurement.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -1,6 +1,6 @@
 <template>
   <div class="d-flex flex-row">
-    <Spacer></Spacer>
+    <Spacer />
     <div class="card sysconfig mt-2 shadow-xs w-100 h-100 mr-2">
       <UIBoxHeader icon="upload" :title="importGaugmeasurmentLabel" />
       <div class="card-body stretches-card">
@@ -45,11 +45,7 @@
             class="btn btn-info mt-4"
             type="button"
           >
-            <font-awesome-icon
-              class="fa-fw mr-2"
-              fixed-width
-              icon="play"
-            ></font-awesome-icon>
+            <font-awesome-icon class="fa-fw mr-2" fixed-width icon="play" />
             <translate>Trigger import</translate>
           </button>
         </div>
--- a/client/src/components/ImportSoundingresults.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/ImportSoundingresults.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -1,7 +1,7 @@
 <template>
   <div class="main d-flex flex-column">
     <div class="d-flex flex-row">
-      <Spacer></Spacer>
+      <Spacer />
       <div class="card shadow-xs mt-2 mr-2 w-100 h-100">
         <UIBoxHeader icon="upload" :title="importSoundingresultsLabel" />
         <div v-if="editState">
@@ -160,14 +160,13 @@
 import { HTTP } from "@/lib/http";
 import { displayError, displayInfo } from "@/lib/errors.js";
 import { mapState } from "vuex";
-import Spacer from "./Spacer";
 
 const IMPORTSTATE = { UPLOAD: "UPLOAD", EDIT: "EDIT" };
 
 export default {
   name: "imports",
   components: {
-    Spacer
+    Spacer: () => import("@/components/Spacer")
   },
   data() {
     return {
--- a/client/src/components/ImportStretches.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/ImportStretches.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -5,263 +5,281 @@
       :title="defineStretchesLabel"
       :closeCallback="$parent.close"
     />
-    <div v-if="!edit" class="mb-3">
-      <UITableHeader
-        :columns="[
-          { id: 'properties.name', title: `${nameLabel}`, class: 'col-4' },
-          { id: 'properties.date_info', title: `${dateLabel}`, class: 'col-2' },
-          {
-            id: 'properties.source_organization',
-            title: `${sourceorganizationLabel}`,
-            class: 'col-3'
-          }
-        ]"
-      />
-      <UITableBody
-        :data="filteredStretches() | sortTable(sortColumn, sortDirection)"
-      >
-        <template v-slot:row="{ item: stretch }">
-          <div class="py-1 col-4 ">
-            <a
-              class="linkto text-info"
-              v-if="isInStaging(stretch.properties.name)"
-              @click="gotoStaging(getStagingLink(stretch.properties.name))"
-            >
-              {{ stretch.properties.name
-              }}<font-awesome-icon
-                class="ml-1 text-danger"
-                icon="exclamation-triangle"
-                fixed-width
-              ></font-awesome-icon
-              ><small class="ml-1">review</small>
-            </a>
-            <a v-else @click="moveMapToStretch(stretch)" href="#">{{
-              stretch.properties.name
-            }}</a>
-          </div>
-          <div class="py-1 col-2">
-            {{ stretch.properties.date_info | surveyDate }}
-          </div>
-          <div class="py-1 col-3">
-            {{ stretch.properties.source_organization }}
-          </div>
-          <div class="py-1 col text-right">
-            <button
-              class="btn btn-xs btn-dark mr-1"
-              @click="editStretch(stretch)"
-            >
-              <font-awesome-icon icon="pencil-alt" fixed-width />
-            </button>
-            <button class="btn btn-xs btn-dark" @click="deleteStretch(stretch)">
-              <font-awesome-icon icon="trash" fixed-width />
-            </button>
-          </div>
-        </template>
-      </UITableBody>
-    </div>
-    <div v-if="edit">
-      <div class="ml-3 mr-3">
-        <div class="d-flex flex-row justify-content-between">
-          <div class="mt-2 w-50 mr-2 text-left">
-            <small class="text-muted"> <translate>ID</translate> </small>
-            <input
-              id="id"
-              type="text"
-              class="form-control"
-              placeholder="AT_Section_12"
-              aria-label="id"
-              v-model="id"
-              :disabled="editExistingStretch"
-            />
-            <span class="text-left text-danger">
-              <small v-if="idError && !id">
-                <translate>Please enter an id</translate>
-              </small>
-            </span>
-          </div>
-          <div class="mt-2 w-50 ml-2 text-left">
-            <div>
-              <small class="text-muted">
-                <translate>Countrycode</translate>
-              </small>
+    <div class="position-relative">
+      <UISpinnerOverlay v-if="loading" />
+      <div v-if="!edit" class="mb-3">
+        <UITableHeader
+          :columns="[
+            { id: 'properties.name', title: `${nameLabel}`, class: 'col-4' },
+            {
+              id: 'properties.date_info',
+              title: `${dateLabel}`,
+              class: 'col-2'
+            },
+            {
+              id: 'properties.source_organization',
+              title: `${sourceorganizationLabel}`,
+              class: 'col-3'
+            }
+          ]"
+        />
+        <UITableBody
+          :data="filteredStretches() | sortTable(sortColumn, sortDirection)"
+        >
+          <template v-slot:row="{ item: stretch }">
+            <div class="py-1 col-4 ">
+              <a
+                class="linkto text-info"
+                v-if="isInStaging(stretch.properties.name)"
+                @click="gotoStaging(getStagingLink(stretch.properties.name))"
+              >
+                {{ stretch.properties.name
+                }}<font-awesome-icon
+                  class="ml-1 text-danger"
+                  icon="exclamation-triangle"
+                  fixed-width
+                ></font-awesome-icon
+                ><small class="ml-1">review</small>
+              </a>
+              <a v-else @click="moveMapToStretch(stretch)" href="#">{{
+                stretch.properties.name
+              }}</a>
+            </div>
+            <div class="py-1 col-2">
+              {{ stretch.properties.date_info | surveyDate }}
+            </div>
+            <div class="py-1 col-3">
+              {{ stretch.properties.source_organization }}
+            </div>
+            <div class="py-1 col text-right">
+              <button
+                class="btn btn-xs btn-dark mr-1"
+                @click="editStretch(stretch)"
+              >
+                <font-awesome-icon icon="pencil-alt" fixed-width />
+              </button>
+              <button
+                class="btn btn-xs btn-dark"
+                @click="deleteStretch(stretch)"
+              >
+                <font-awesome-icon icon="trash" fixed-width />
+              </button>
+            </div>
+          </template>
+        </UITableBody>
+      </div>
+      <div v-if="edit">
+        <div class="ml-3 mr-3">
+          <div class="d-flex flex-row justify-content-between">
+            <div class="mt-2 w-50 mr-2 text-left">
+              <small class="text-muted"> <translate>ID</translate> </small>
               <input
-                id="countryCode"
+                id="id"
                 type="text"
                 class="form-control"
-                placeholder="AT"
+                placeholder="AT_Section_12"
                 aria-label="id"
-                v-model="countryCode"
+                v-model="id"
+                :disabled="editExistingStretch"
               />
               <span class="text-left text-danger">
-                <small v-if="countryCodeError && !countryCode">
-                  <translate>Please enter a countrycode </translate>
+                <small v-if="idError && !id">
+                  <translate>Please enter an id</translate>
+                </small>
+              </span>
+            </div>
+            <div class="mt-2 w-50 ml-2 text-left">
+              <div>
+                <small class="text-muted">
+                  <translate>Countrycode</translate>
+                </small>
+                <input
+                  id="countryCode"
+                  type="text"
+                  class="form-control"
+                  placeholder="AT"
+                  aria-label="id"
+                  v-model="countryCode"
+                />
+                <span class="text-left text-danger">
+                  <small v-if="countryCodeError && !countryCode">
+                    <translate>Please enter a countrycode </translate>
+                  </small>
+                </span>
+              </div>
+              <div class="w-50 ml-2"></div>
+            </div>
+          </div>
+          <div class="d-flex flex-column  justify-content-between">
+            <div class="mt-2 text-left">
+              <small class="text-muted">
+                <translate>Start rhm</translate>
+              </small>
+              <div class="d-flex flex-row">
+                <input
+                  id="startrhm"
+                  type="text"
+                  class="form-control"
+                  placeholder="e.g. ATXXX000010000019900"
+                  aria-label="startrhm"
+                  v-model="startrhm"
+                />
+                <span class="input-group-text">
+                  <font-awesome-icon
+                    @click="togglePipette('start')"
+                    :class="{ 'text-info': pipetteStart }"
+                    icon="bullseye"
+                  />
+                </span>
+              </div>
+              <span class="text-left text-danger">
+                <small v-if="startrhmError && !startrhm">
+                  <translate>Please enter a start point</translate>
                 </small>
               </span>
             </div>
-            <div class="w-50 ml-2"></div>
-          </div>
-        </div>
-        <div class="d-flex flex-column  justify-content-between">
-          <div class="mt-2 text-left">
-            <small class="text-muted"> <translate>Start rhm</translate> </small>
-            <div class="d-flex flex-row">
-              <input
-                id="startrhm"
-                type="text"
-                class="form-control"
-                placeholder="e.g. ATXXX000010000019900"
-                aria-label="startrhm"
-                v-model="startrhm"
-              />
-              <span class="input-group-text">
-                <font-awesome-icon
-                  @click="togglePipette('start')"
-                  :class="{ 'text-info': pipetteStart }"
-                  icon="bullseye"
-                ></font-awesome-icon>
+            <div class="mt-2 text-left">
+              <small class="text-muted"> <translate>End rhm</translate> </small>
+              <div class="d-flex flex-row">
+                <input
+                  id="endrhm"
+                  type="text"
+                  class="form-control"
+                  placeholder="e.g. ATXXX000010000019900"
+                  aria-label="endrhm"
+                  v-model="endrhm"
+                />
+                <span class="input-group-text">
+                  <font-awesome-icon
+                    @click="togglePipette('end')"
+                    :class="{ 'text-info': pipetteEnd }"
+                    icon="bullseye"
+                  />
+                </span>
+              </div>
+              <span class="text-left text-danger">
+                <small v-if="endrhmError && !endrhm">
+                  <translate>Please enter an end point</translate>
+                </small>
               </span>
             </div>
-            <span class="text-left text-danger">
-              <small v-if="startrhmError && !startrhm">
-                <translate>Please enter a start point</translate>
-              </small>
-            </span>
           </div>
-          <div class="mt-2 text-left">
-            <small class="text-muted"> <translate>End rhm</translate> </small>
-            <div class="d-flex flex-row">
+          <div
+            v-if="!editExistingStretch"
+            class="d-flex flex-row justify-content-between"
+          >
+            <div class="mt-2  mr-2 w-50  text-left">
+              <small class="text-muted">
+                <translate
+                  >Tolerance for snapping of waterway axis [m]</translate
+                >
+              </small>
               <input
-                id="endrhm"
+                class="form-control"
+                v-model.number="tolerance"
+                placeholder=""
+                type="number"
+                min="0"
+                step="any"
+                aria-label="tolerance"
+                id="tolerance"
+              />
+              <span class="text-left text-danger">
+                <small v-if="toleranceError && !tolerance">
+                  <translate>Please enter a tolerance value</translate>
+                </small>
+              </span>
+            </div>
+          </div>
+          <div class="d-flex flex-row justify-content-between">
+            <div class="mt-2  mr-2 w-50  text-left">
+              <small class="text-muted">
+                <translate>Object name</translate>
+              </small>
+              <input
+                id="objbn"
                 type="text"
                 class="form-control"
-                placeholder="e.g. ATXXX000010000019900"
-                aria-label="endrhm"
-                v-model="endrhm"
+                placeholder=""
+                aria-label="objbn"
+                v-model="objbn"
               />
-              <span class="input-group-text">
-                <font-awesome-icon
-                  @click="togglePipette('end')"
-                  :class="{ 'text-info': pipetteEnd }"
-                  icon="bullseye"
-                ></font-awesome-icon>
+              <span class="text-left text-danger">
+                <small v-if="objbnError && !objbn">
+                  <translate>Please enter an objectname</translate>
+                </small>
               </span>
             </div>
-            <span class="text-left text-danger">
-              <small v-if="endrhmError && !endrhm">
-                <translate>Please enter an end point</translate>
+            <div class="mt-2  ml-2 w-50  text-left">
+              <small class="text-muted">
+                <translate>National Object name</translate>
               </small>
-            </span>
+              <input
+                id="nobjbn"
+                type="text"
+                class="form-control"
+                placeholder=""
+                aria-label="nobjbn"
+                v-model="nobjbn"
+              />
+            </div>
           </div>
-        </div>
-        <div
-          v-if="!editExistingStretch"
-          class="d-flex flex-row justify-content-between"
-        >
-          <div class="mt-2  mr-2 w-50  text-left">
-            <small class="text-muted">
-              <translate>Tolerance for snapping of waterway axis [m]</translate>
-            </small>
-            <input
-              class="form-control"
-              v-model.number="tolerance"
-              placeholder=""
-              type="number"
-              min="0"
-              step="any"
-              aria-label="tolerance"
-              id="tolerance"
-            />
-            <span class="text-left text-danger">
-              <small v-if="toleranceError && !tolerance">
-                <translate>Please enter a tolerance value</translate>
+          <div class="d-flex flex-row justify-content-between">
+            <div class="mt-2 mr-2 w-50 text-left">
+              <small class="text-muted">
+                <translate>Date info</translate>
               </small>
-            </span>
+              <input
+                id="date_info"
+                type="date"
+                class="form-control"
+                placeholder="date_info"
+                aria-label="date_info"
+                v-model="date_info"
+              />
+              <span class="text-left text-danger">
+                <small v-if="date_infoError && !date_info">
+                  <translate>Please enter a date</translate>
+                </small>
+              </span>
+            </div>
+            <div class="mt-2 ml-2 w-50 text-left">
+              <small class="text-muted"> <translate>Source</translate> </small>
+              <input
+                id="source"
+                type="text"
+                class="form-control"
+                placeholder="source"
+                aria-label="source"
+                v-model="source"
+              />
+              <span class="text-left text-danger">
+                <small v-if="sourceError && !source">
+                  <translate>Please enter a source</translate>
+                </small>
+              </span>
+            </div>
           </div>
         </div>
-        <div class="d-flex flex-row justify-content-between">
-          <div class="mt-2  mr-2 w-50  text-left">
-            <small class="text-muted">
-              <translate>Object name</translate>
-            </small>
-            <input
-              id="objbn"
-              type="text"
-              class="form-control"
-              placeholder=""
-              aria-label="objbn"
-              v-model="objbn"
-            />
-            <span class="text-left text-danger">
-              <small v-if="objbnError && !objbn">
-                <translate>Please enter an objectname</translate>
-              </small>
-            </span>
-          </div>
-          <div class="mt-2  ml-2 w-50  text-left">
-            <small class="text-muted">
-              <translate>National Object name</translate>
-            </small>
-            <input
-              id="nobjbn"
-              type="text"
-              class="form-control"
-              placeholder=""
-              aria-label="nobjbn"
-              v-model="nobjbn"
-            />
-          </div>
-        </div>
-        <div class="d-flex flex-row justify-content-between">
-          <div class="mt-2 mr-2 w-50 text-left">
-            <small class="text-muted"> <translate>Date info</translate> </small>
-            <input
-              id="date_info"
-              type="date"
-              class="form-control"
-              placeholder="date_info"
-              aria-label="date_info"
-              v-model="date_info"
-            />
-            <span class="text-left text-danger">
-              <small v-if="date_infoError && !date_info">
-                <translate>Please enter a date</translate>
-              </small>
-            </span>
-          </div>
-          <div class="mt-2 ml-2 w-50 text-left">
-            <small class="text-muted"> <translate>Source</translate> </small>
-            <input
-              id="source"
-              type="text"
-              class="form-control"
-              placeholder="source"
-              aria-label="source"
-              v-model="source"
-            />
-            <span class="text-left text-danger">
-              <small v-if="sourceError && !source">
-                <translate>Please enter a source</translate>
-              </small>
-            </span>
-          </div>
+        <div class="text-right mt-2 mr-3 mb-3">
+          <button @click="edit = false" class="btn btn-warning mr-2">
+            Back
+          </button>
+          <button
+            @click="save"
+            type="submit"
+            class="shadow-sm btn btn-info submit-button"
+          >
+            <translate>Submit</translate>
+          </button>
         </div>
       </div>
-      <div class="text-right mt-2 mr-3 mb-3">
-        <button @click="edit = false" class="btn btn-warning mr-2">Back</button>
-        <button
-          @click="save"
-          type="submit"
-          class="shadow-sm btn btn-info submit-button"
-        >
-          <translate>Submit</translate>
+      <div class="text-right mr-3">
+        <button v-if="!edit" @click="startEdit()" class="btn btn-info">
+          <translate>New stretch</translate>
         </button>
       </div>
     </div>
-    <div class="text-right mr-3">
-      <button v-if="!edit" @click="startEdit()" class="btn btn-info">
-        <translate>New stretch</translate>
-      </button>
-    </div>
   </div>
 </template>
 
@@ -285,7 +303,6 @@
 import { LAYERS } from "@/store/map";
 import { HTTP } from "@/lib/http";
 import { sortTable } from "@/lib/mixins";
-import { Stroke, Style, Fill } from "ol/style.js";
 
 export default {
   name: "importstretches",
@@ -316,7 +333,8 @@
       nobjbnError: false,
       date_infoError: false,
       sourceError: false,
-      countryCodeError: false
+      countryCodeError: false,
+      loading: false
     };
   },
   computed: {
@@ -441,62 +459,14 @@
       });
     },
     moveMapToStretch(stretch) {
-      //define new style for the selected stretch
-      var newStyle = new Style({
-        stroke: new Stroke({
-          color: "rgba(250, 240, 10, .9)",
-          width: 5
-        }),
-        fill: new Fill({
-          color: "rgba(250, 240, 0, .7)"
-        })
-      });
-      let stretchesLayer = this.getLayerByName(LAYERS.STRETCHES);
-      let oldStyle = stretchesLayer.data.getStyle();
+      this.$store.commit("imports/selectedStretchId", stretch.id);
       this.$store.commit("map/setLayerVisible", LAYERS.STRETCHES);
-      var stretches = stretchesLayer.data.getSource().getFeatures();
-      // change the style when the the stretches are already loaded
-      if (stretches.length) {
-        this.setStretchStyle(stretches, stretch, oldStyle, newStyle);
-      } else {
-        // by choosing stretch first time wating to load the features then change the style
-        stretchesLayer.data.getSource().once("change", () => {
-          this.setStretchStyle(
-            stretchesLayer.data.getSource().getFeatures(),
-            stretch,
-            oldStyle,
-            newStyle
-          );
-        });
-      }
       this.$store.commit("map/moveToExtent", {
         feature: stretch,
         zoom: 17,
         preventZoomOut: true
       });
     },
-    // give the selected stretch new style and set the orginal style to the others
-    setStretchStyle(features, stretch, oldStyle, newStyle) {
-      features.forEach(f => {
-        if (f.id_ === stretch.id) {
-          f.setStyle(newStyle);
-        } else {
-          f.setStyle(oldStyle);
-        }
-      });
-    },
-    loadStretches() {
-      return new Promise((resolve, reject) => {
-        this.$store
-          .dispatch("imports/loadStretches")
-          .then(response => {
-            resolve(response);
-          })
-          .catch(error => {
-            reject(error);
-          });
-      });
-    },
     clean() {
       this.id = "";
       this.edit = false;
@@ -594,7 +564,7 @@
             message: this.$gettext("Starting import of stretch")
           });
           this.clean();
-          this.loadStretches().then(() => {
+          this.$store.dispatch("imports/loadStretches").then(() => {
             this.edit = false;
           });
         })
@@ -609,13 +579,17 @@
   },
   mounted() {
     this.edit = false;
-    this.$store.dispatch("imports/loadStretches").catch(error => {
-      const { status, data } = error.response;
-      displayError({
-        title: this.$gettext("Backend Error"),
-        message: `${status}: ${data.message || data}`
-      });
-    });
+    this.loading = true;
+    this.$store
+      .dispatch("imports/loadStretches")
+      .catch(error => {
+        const { status, data } = error.response;
+        displayError({
+          title: this.$gettext("Backend Error"),
+          message: `${status}: ${data.message || data}`
+        });
+      })
+      .finally(() => (this.loading = false));
     this.loadStagingData().catch(error => {
       const { status, data } = error.response;
       displayError({
--- a/client/src/components/ImportWaterwayProfiles.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/ImportWaterwayProfiles.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -1,6 +1,6 @@
 <template>
   <div class="d-flex flex-row">
-    <Spacer></Spacer>
+    <Spacer />
     <div class="card sysconfig mt-2 shadow-xs w-100 h-100 mr-2">
       <UIBoxHeader icon="upload" :title="importWaterwayProfilesLabel" />
       <div class="card-body stretches-card">
@@ -87,11 +87,7 @@
               class="btn btn-info mt-4"
               type="button"
             >
-              <font-awesome-icon
-                class="fa-fw mr-2"
-                fixed-width
-                icon="play"
-              ></font-awesome-icon>
+              <font-awesome-icon class="fa-fw mr-2" fixed-width icon="play" />
               <translate>Trigger import</translate>
             </button>
           </div>
@@ -121,6 +117,9 @@
 
 export default {
   name: "importwaterwayprofiles",
+  components: {
+    Spacer: () => import("./Spacer")
+  },
   data() {
     return {
       url: "https://service.d4d-portal.info/wamos/wfs/",
@@ -176,9 +175,6 @@
           });
         });
     }
-  },
-  components: {
-    Spacer: () => import("./Spacer")
   }
 };
 </script>
--- a/client/src/components/Logs.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/Logs.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -1,7 +1,7 @@
 <template>
   <div class="main d-flex flex-column">
     <div class="d-flex flex-row">
-      <Spacer></Spacer>
+      <Spacer />
       <div class="card logs shadow-xs mt-2 mr-2">
         <UIBoxHeader icon="book" title="Logs" />
         <div class="logoutput text-left bg-white">
@@ -14,8 +14,11 @@
             <ul class="nav nav-pills">
               <li class="nav-item">
                 <a
+                  id="accesslog"
                   :class="accesslogStyle"
-                  @click="fetch('system/log/apache2/access.log', 'accesslog')"
+                  @click.prevent="
+                    fetch('system/log/apache2/access.log', 'accesslog')
+                  "
                   href="#"
                 >
                   <translate>Accesslog</translate>
@@ -23,8 +26,11 @@
               </li>
               <li class="nav-item">
                 <a
+                  id="errorlog"
                   :class="errorlogStyle"
-                  @click="fetch('system/log/apache2/error.log', 'errorlog')"
+                  @click.prevent="
+                    fetch('system/log/apache2/error.log', 'errorlog')
+                  "
                   href="#"
                 >
                   <translate>Errorlog</translate>
@@ -65,6 +71,7 @@
 
 .logs {
   height: 85vh;
+  width: 100vw;
 }
 
 #code {
@@ -117,6 +124,7 @@
 import "../../node_modules/highlight.js/styles/paraiso-dark.css";
 import Vue from "vue";
 import VueHighlightJS from "vue-highlightjs";
+import { displayError } from "@/lib/errors.js";
 Vue.use(VueHighlightJS);
 
 const ACCESSLOG = "accesslog";
@@ -149,7 +157,13 @@
           this.refreshed = new Date().toLocaleString();
           this.currentFile = file;
         })
-        .catch();
+        .catch(e => {
+          const { status, data } = e.response;
+          displayError({
+            title: this.$gettext("Backend Error"),
+            message: `${status} ${data.message || data}`
+          });
+        });
     },
     disallow(e) {
       e.target.blur();
--- a/client/src/components/Maplayer.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/Maplayer.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -67,6 +67,7 @@
     ]),
     ...mapState("bottlenecks", ["selectedSurvey"]),
     ...mapState("application", ["showSplitscreen"]),
+    ...mapState("imports", ["selectedStretchId"]),
     hasActiveInteractions() {
       return (
         (this.lineTool && this.lineTool.getActive()) ||
@@ -76,7 +77,12 @@
     }
   },
   methods: {
-    buildVectorLoader(featureRequestOptions, endpoint, vectorSource) {
+    buildVectorLoader(
+      featureRequestOptions,
+      endpoint,
+      vectorSource,
+      featurePostProcessor
+    ) {
       // build a function to be used for VectorSource.setLoader()
       // make use of WFS().writeGetFeature to build the request
       // and use our HTTP library to actually do it
@@ -103,6 +109,9 @@
             var features = new GeoJSON().readFeatures(
               JSON.stringify(response.data)
             );
+            if (featurePostProcessor) {
+              features.map(f => featurePostProcessor(f));
+            }
             vectorSource.addFeatures(features);
             // console.log(
             //   "loaded",
@@ -164,6 +173,16 @@
       } else {
         this.updateBottleneckFilter("does_not_exist", "1999-10-01");
       }
+    },
+    selectedStretchId(id) {
+      this.getVSourceByName(LAYERS.STRETCHES)
+        .getFeatures()
+        .forEach(f => {
+          f.set("highlighted", false);
+          if (id === f.getId()) {
+            f.set("highlighted", true);
+          }
+        });
     }
   },
   mounted() {
@@ -369,7 +388,13 @@
           geometryName: "area"
         },
         "/internal/wfs",
-        layer.data.getSource()
+        layer.data.getSource(),
+        f => {
+          if (f.getId() === this.selectedStretchId) {
+            f.set("highlighted", true);
+          }
+          return f;
+        }
       )
     );
     layer.data.setVisible(layer.isVisible);
--- a/client/src/components/PageNotFound.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/PageNotFound.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -1,10 +1,10 @@
 <template>
   <div class="main d-flex flex-row" style="position: relative;">
-    <Spacer></Spacer>
+    <Spacer />
     <div class="my-auto mx-auto">
       <h1>
-        <font-awesome-icon icon="frown-open" fixed-width></font-awesome-icon>We
-        are sorry. The ressource you requested could not be found.
+        <font-awesome-icon icon="frown-open" fixed-width />
+        We are sorry. The ressource you requested could not be found.
       </h1>
     </div>
   </div>
--- a/client/src/components/Search.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/Search.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -2,7 +2,7 @@
   <div :class="searchbarContainerStyle">
     <div class="input-group-prepend m-0 d-print-none">
       <span @click="toggleSearchbar" :class="searchButtonStyle" for="search">
-        <font-awesome-icon icon="search"></font-awesome-icon>
+        <font-awesome-icon icon="search" />
       </span>
     </div>
     <div
--- a/client/src/components/Sidebar.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/Sidebar.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -8,7 +8,7 @@
         @click="$store.commit('application/showSidebar', !showSidebar)"
         class="menubutton ui-element d-print-none p-2 bg-white rounded position-absolute d-flex justify-content-center"
       >
-        <font-awesome-icon class="fa-fw" icon="bars"></font-awesome-icon>
+        <font-awesome-icon class="fa-fw" icon="bars" />
       </div>
       <div class="menu text-nowrap text-left">
         <router-link to="/">
@@ -16,15 +16,11 @@
             class="fa-fw mr-2"
             fixed-width
             icon="map-marked-alt"
-          ></font-awesome-icon>
+          />
           <span class="fix-trans-space" v-translate>Map</span>
         </router-link>
         <router-link to="/bottlenecks">
-          <font-awesome-icon
-            class="fa-fw mr-2"
-            fixed-width
-            icon="ship"
-          ></font-awesome-icon>
+          <font-awesome-icon class="fa-fw mr-2" fixed-width icon="ship" />
           <span class="fix-trans-space" v-translate>Bottlenecks</span>
         </router-link>
         <div v-if="isWaterwayAdmin">
@@ -33,7 +29,7 @@
               class="fa-fw mr-2"
               fixed-width
               icon="clipboard-check"
-            ></font-awesome-icon>
+            />
             <span class="fix-trans-space" v-translate>Import review</span>
             <span class="indicator" v-if="showSidebar && stagingNotifications">
               {{ stagingNotifications }}
@@ -42,11 +38,7 @@
         </div>
         <div v-if="isSysAdmin">
           <router-link to="/stretches">
-            <font-awesome-icon
-              class="fa-fw mr-2"
-              fixed-width
-              icon="road"
-            ></font-awesome-icon>
+            <font-awesome-icon class="fa-fw mr-2" fixed-width icon="road" />
             <span class="fix-trans-space" v-translate>Define stretches</span>
           </router-link>
         </div>
@@ -54,37 +46,21 @@
           <small class="text-muted pl-3"> <translate>Import</translate> </small>
           <hr class="m-0" />
           <router-link to="/importsoundingresults">
-            <font-awesome-icon
-              class="fa-fw mr-2"
-              fixed-width
-              icon="upload"
-            ></font-awesome-icon>
+            <font-awesome-icon class="fa-fw mr-2" fixed-width icon="upload" />
             <span class="fix-trans-space" v-translate>Soundingresults</span>
           </router-link>
           <router-link to="/importapprovedgaugemeasurement">
-            <font-awesome-icon
-              class="fa-fw mr-2"
-              fixed-width
-              icon="upload"
-            ></font-awesome-icon>
+            <font-awesome-icon class="fa-fw mr-2" fixed-width icon="upload" />
             <span class="fix-trans-space" v-translate
               >Approved Gaugemeasurements</span
             >
           </router-link>
           <router-link to="/importwaterwayprofiles">
-            <font-awesome-icon
-              class="fa-fw mr-2"
-              fixed-width
-              icon="upload"
-            ></font-awesome-icon>
+            <font-awesome-icon class="fa-fw mr-2" fixed-width icon="upload" />
             <span class="fix-trans-space" v-translate>Waterway Profiles</span>
           </router-link>
           <router-link to="/importschedule">
-            <font-awesome-icon
-              class="fa-fw mr-2"
-              fixed-width
-              icon="clock"
-            ></font-awesome-icon>
+            <font-awesome-icon class="fa-fw mr-2" fixed-width icon="clock" />
             <translate class="fix-trans-space">Imports</translate>
           </router-link>
           <small class="text-muted pl-3">
@@ -98,37 +74,25 @@
               class="fa-fw mr-2"
               fixed-width
               icon="users-cog"
-            ></font-awesome-icon>
+            />
             <span class="fix-trans-space" v-translate>Users</span>
           </router-link>
         </div>
         <div v-if="isWaterwayAdmin">
           <router-link to="/systemconfiguration">
-            <font-awesome-icon
-              class="fa-fw mr-2"
-              fixed-width
-              icon="wrench"
-            ></font-awesome-icon>
+            <font-awesome-icon class="fa-fw mr-2" fixed-width icon="wrench" />
             <span class="fix-trans-space" v-translate>Configuration</span>
           </router-link>
         </div>
         <div v-if="isSysAdmin">
           <router-link to="/logs">
-            <font-awesome-icon
-              class="fa-fw mr-2"
-              fixed-width
-              icon="book"
-            ></font-awesome-icon>
+            <font-awesome-icon class="fa-fw mr-2" fixed-width icon="book" />
             <span class="fix-trans-space" v-translate>Logs</span>
           </router-link>
         </div>
         <hr class="m-0" />
         <a @click="logoff" href="#" class="logout">
-          <font-awesome-icon
-            class="fa-fw mr-2"
-            fixed-width
-            icon="power-off"
-          ></font-awesome-icon>
+          <font-awesome-icon class="fa-fw mr-2" fixed-width icon="power-off" />
           <span class="fix-trans-space" v-translate>Logout</span> {{ user }}
         </a>
       </div>
--- a/client/src/components/Zoom.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/Zoom.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -4,19 +4,19 @@
       class="zoom-button border-0 bg-white rounded-left ui-element"
       @click="zoomOut"
     >
-      <font-awesome-icon icon="minus"></font-awesome-icon>
+      <font-awesome-icon icon="minus" />
     </button>
     <button
       class="zoom-button border-0 bg-white ui-element border-right"
       @click="refreshMap"
     >
-      <font-awesome-icon icon="redo"></font-awesome-icon>
+      <font-awesome-icon icon="redo" />
     </button>
     <button
       class="zoom-button border-0 bg-white rounded-right ui-element border-right"
       @click="zoomIn"
     >
-      <font-awesome-icon icon="plus"></font-awesome-icon>
+      <font-awesome-icon icon="plus" />
     </button>
   </div>
 </template>
--- a/client/src/components/fairway/Profiles.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/fairway/Profiles.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -12,7 +12,7 @@
         :closeCallback="close"
       />
       <div class="box-body">
-        <SpinnerOverlay
+        <UISpinnerOverlay
           v-if="surveysLoading || profileLoading || differencesLoading"
         />
         <select
@@ -187,7 +187,7 @@
               <button class="btn btn-info btn-sm w-100" @click="toggleCutTool">
                 <font-awesome-icon
                   :icon="cutTool && cutTool.getActive() ? 'times' : 'plus'"
-                ></font-awesome-icon>
+                />
                 {{ cutTool && cutTool.getActive() ? "Cancel" : "New" }}
               </button>
             </div>
--- a/client/src/components/gauge/Gauges.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/gauge/Gauges.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -12,7 +12,7 @@
         :closeCallback="close"
       />
       <div class="box-body">
-        <SpinnerOverlay v-if="loading" />
+        <UISpinnerOverlay v-if="loading" />
         <select
           @change="moveToGauge"
           v-model="selectedGaugeISRS"
--- a/client/src/components/importoverview/AdditionalDetail.vue	Tue Apr 02 10:07:48 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-<template>
-  <div>
-    <FairwayDimensionDetail
-      :entry="entry"
-      :details="details"
-      v-if="isFairwayDimension"
-    ></FairwayDimensionDetail>
-    <ApprovedGaugeMeasurementDetail
-      :entry="entry"
-      :details="details"
-      v-if="isApprovedGaugeMeasurement"
-    ></ApprovedGaugeMeasurementDetail>
-    <BottleneckDetail
-      :details="details"
-      :entry="entry"
-      v-if="isBottleneck"
-    ></BottleneckDetail>
-  </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):
- * Thomas Junk <thomas.junk@intevation.de>
- */
-import { mapState } from "vuex";
-
-export default {
-  name: "additionaldetail",
-  props: ["entry"],
-  components: {
-    BottleneckDetail: () => import("./BottleneckDetail.vue"),
-    ApprovedGaugeMeasurementDetail: () =>
-      import("./ApprovedGaugeMeasurementDetail.vue"),
-    FairwayDimensionDetail: () => import("./FairwayDimension.vue")
-  },
-  computed: {
-    ...mapState("imports", ["showLogs", "details"]),
-    kind() {
-      return this.entry.kind.toUpperCase();
-    },
-    isFairwayDimension() {
-      return this.kind === "FD";
-    },
-    isApprovedGaugeMeasurement() {
-      return this.kind === "AGM";
-    },
-    isBottleneck() {
-      return this.kind === "BN" || this.kind === "UBN";
-    }
-  }
-};
-</script>
-
-<style lang="scss" scoped></style>
--- a/client/src/components/importoverview/ApprovedGaugeMeasurementDetail.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/importoverview/ApprovedGaugeMeasurementDetail.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -7,23 +7,13 @@
     }"
   >
     <div v-for="(result, index) in details.summary" :key="index">
-      <div class="pl-2 d-flex flex-row">
-        <div
-          @click="toggleDiff(index)"
-          class="small mt-auto mb-auto text-info text-left"
-        >
-          <font-awesome-icon
-            class="pointer"
-            v-if="showDiff == index"
-            icon="angle-down"
-            fixed-width
-          ></font-awesome-icon>
-          <font-awesome-icon
-            class="pointer"
-            v-if="showDiff != index"
-            icon="angle-right"
-            fixed-width
-          ></font-awesome-icon>
+      <div class="pl-2 d-flex">
+        <div @click="toggleDiff(index)" class="my-auto text-left">
+          <UISpinnerButton
+            :state="showDiff === index"
+            :icons="['angle-right', 'angle-down']"
+            classes="text-info"
+          />
         </div>
         <span v-if="result.versions.length == 1" class="agmcode text-left"
           ><div>
@@ -37,9 +27,9 @@
           ><div>{{ result["measure-date"] | dateTime }}</div></span
         >
       </div>
-      <div v-if="showDiff == index" class="pl-3 d-flex flex-row">
+      <div v-if="showDiff === index" class="pl-3 d-flex flex-row">
         <div class="w-100">
-          <div class="d-flex flex-row pl-3 text-left">
+          <div class="d-flex flex-row pl-3 text-left w-95">
             <div class="header border-bottom agmdetailskeys">
               <small><translate>Value</translate></small>
             </div>
@@ -54,7 +44,7 @@
             </div>
           </div>
           <div
-            class="d-flex flex-row pl-3 text-left"
+            class="line d-flex flex-row pl-3 text-left w-95"
             v-for="(entry, index) in Object.keys(result.versions[0])"
             :key="index"
           >
@@ -95,8 +85,14 @@
 <style lang="sass" scoped>
 .diffs
   width: 100%
-  max-height: 20vh
   overflow-y: auto
+  > div
+    border-top: dashed 1px #dee2e6
+    &:first-child
+      border-top: none
+
+.line:nth-child(odd)
+  background: #f8f8f8
 
 .agmcode
   width: 35%
@@ -135,7 +131,7 @@
   props: ["entry"],
   data() {
     return {
-      showDiff: false
+      showDiff: 0 // open first item by default
     };
   },
   computed: {
--- a/client/src/components/importoverview/BottleneckDetail.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/importoverview/BottleneckDetail.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -9,55 +9,57 @@
     <div
       v-for="(bottleneck, index) in bottlenecks"
       :key="index"
-      class="d-flex flex-row"
+      class="d-flex flex-column w-100"
     >
-      <div class="d-flex flex-column">
-        <div class="d-flex flex-row">
-          <div
-            @click="showBottleneckDetails(index)"
-            class="mt-auto mb-auto text-info text-left"
+      <div class="d-flex flex-row pl-2">
+        <div
+          @click="showBottleneckDetails(index)"
+          class="mt-auto mb-auto text-info text-left"
+        >
+          <UISpinnerButton
+            :state="showBottleneckDetail === index"
+            :icons="['angle-right', 'angle-down']"
+            class="text-info"
+          />
+        </div>
+        <a @click="moveToBottleneck(index)" href="#">
+          {{ bottleneck.properties.objnam }}
+        </a>
+      </div>
+
+      <div class="ml-3 d-flex flex-row" v-if="showBottleneckDetail === index">
+        <table>
+          <tr
+            v-for="(info, index) in Object.keys(bottleneck.properties)"
+            :key="index"
+            class="mr-1 condensed  text-muted"
           >
-            <font-awesome-icon
-              class="pointer"
-              v-if="showBottleneckDetail === index"
-              icon="angle-down"
-              fixed-width
-            ></font-awesome-icon>
-            <font-awesome-icon
-              class="pointer"
-              v-if="!(showBottleneckDetail === index)"
-              icon="angle-right"
-              fixed-width
-            ></font-awesome-icon>
-          </div>
-          <a @click="moveToBottleneck(index)" href="#">
-            {{ bottleneck.properties.objnam }}
-          </a>
-        </div>
-
-        <div class="ml-3 d-flex flex-row" v-if="showBottleneckDetail === index">
-          <table>
-            <tr
-              v-for="(info, index) in Object.keys(bottleneck.properties)"
-              :key="index"
-              class="mr-1 condensed  text-muted"
-            >
-              <td class="text-left">{{ info }}</td>
-              <td class="pl-3 text-left">
-                {{ bottleneck.properties[info] }}
-              </td>
-            </tr>
-          </table>
-        </div>
+            <td class="text-left">{{ info }}</td>
+            <td class="pl-3 text-left">
+              {{ bottleneck.properties[info] }}
+            </td>
+          </tr>
+        </table>
       </div>
     </div>
   </div>
 </template>
 
 <style lang="sass" scoped>
+
+table
+  width: 100%
+
+tr:nth-child(even)
+  background: #f8f8f8
+
 .bottleneckdetails
   width: 100%
   overflow-y: auto
+  > div
+    border-top: dashed 1px #dee2e6
+    &:first-child
+      border-top: none
 
 .split
   max-height: 35vh
@@ -88,15 +90,13 @@
 import { displayError } from "@/lib/errors.js";
 import { mapState } from "vuex";
 
-const NO_BOTTLENECK = -1;
-
 export default {
   name: "bottleneckdetails",
   props: ["entry"],
   data() {
     return {
       bottlenecks: [],
-      showBottleneckDetail: NO_BOTTLENECK
+      showBottleneckDetail: null
     };
   },
   mounted() {
@@ -160,11 +160,11 @@
       });
     },
     showBottleneckDetails(index) {
-      if (index == this.showBottleneckDetail) {
-        this.showBottleneckDetail = false;
-        return;
+      if (index === this.showBottleneckDetail) {
+        this.showBottleneckDetail = null;
+      } else {
+        this.showBottleneckDetail = index;
       }
-      this.showBottleneckDetail = index;
     }
   }
 };
--- a/client/src/components/importoverview/FairwayDimension.vue	Tue Apr 02 10:07:48 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-<template>
-  <div>Fairwaydimension</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):
- * Thomas Junk <thomas.junk@intevation.de>
- */
-export default {
-  name: "fairwaydimensiondetails"
-};
-</script>
-
-<style></style>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/components/importoverview/FairwayDimensionDetail.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -0,0 +1,20 @@
+<template>
+  <div>Fairwaydimension</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):
+ * Thomas Junk <thomas.junk@intevation.de>
+ */
+export default {};
+</script>
--- a/client/src/components/importoverview/ImportOverview.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/importoverview/ImportOverview.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -7,7 +7,7 @@
       :actions="[{ callback: loadUpdatedLogs, icon: 'redo' }]"
     />
     <div class="position-relative">
-      <SpinnerOverlay v-if="loading" />
+      <UISpinnerOverlay v-if="loading" />
       <div class="border-bottom p-2 d-flex justify-content-between">
         <Filters></Filters>
         <button
@@ -78,7 +78,7 @@
       <UITableBody
         :data="filteredImports() | sortTable(sortColumn, sortDirection)"
         :isActive="item => item.id === this.show"
-        maxHeight="73vh"
+        maxHeight="70vh"
       >
         <template v-slot:row="{ item: entry }">
           <LogEntry :entry="entry"></LogEntry>
--- a/client/src/components/importoverview/LogDetail.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/importoverview/LogDetail.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -1,78 +1,53 @@
 <template>
   <div>
     <div
-      class="d-flex fex-row"
+      class="d-flex border-bottom"
       style="padding-left: 3px;"
-      v-if="hasAdditionalInfo || isStretch || isSoundingResult"
+      v-if="hasAdditionalInfo || isST || isSR"
     >
       <div v-if="hasAdditionalInfo">
-        <font-awesome-icon
-          v-if="entry.id === showAdditional"
-          @click="toggleAdditionalInfo"
-          class="my-auto mr-1 text-info pointer"
-          icon="angle-down"
-          fixed-width
-        ></font-awesome-icon>
-        <font-awesome-icon
-          v-if="entry.id !== showAdditional"
+        <UISpinnerButton
           @click="toggleAdditionalInfo"
-          class="my-auto mr-1 text-info pointer"
-          icon="angle-right"
-          fixed-width
-        ></font-awesome-icon>
+          :state="entry.id === showAdditional"
+          :icons="['angle-right', 'angle-down']"
+          class="text-info d-inline-block"
+        />
         <span class="text-info"><translate>Additional Info</translate></span>
+        <span class="text-info" v-if="isAGM && details.summary">
+          ({{ details.summary.length }})
+        </span>
         <span
-          class="text-info"
-          v-if="isApprovedGaugeMeasurement && details.summary"
-        >
-          ({{ details.summary.length }})</span
-        >
-        <span
-          v-if="isBottleneck && details.summary && details.summary.bottlenecks"
+          v-if="isBN && details.summary && details.summary.bottlenecks"
           class="text-info text-left"
         >
-          ({{ details.summary.bottlenecks.length }})</span
-        >
-        <span class="text-left" v-if="isFairwayDimension"
-          >{{ details.summary["source-organization"] }} (LOS:
-          {{ details.summary.los }})</span
-        >
+          ({{ details.summary.bottlenecks.length }})
+        </span>
+        <span class="text-left" v-if="isFD">
+          {{ details.summary["source-organization"] }}
+          (LOS: {{ details.summary.los }})
+        </span>
       </div>
-      <StretchDetail
-        v-if="isStretch && isPending"
-        :entry="entry"
-      ></StretchDetail>
-      <SoundingResultDetail
-        :entry="entry"
-        v-if="isSoundingResult && isPending"
-      ></SoundingResultDetail>
+      <StretchDetail v-if="isST && isPending" :entry="entry" />
+      <SoundingResultDetail :entry="entry" v-if="isSR && isPending" />
     </div>
-    <AdditionalDetail
-      v-if="entry.id === showAdditional && isPending"
-      class="ml-2 d-flex flex-row"
-      :entry="entry"
-    ></AdditionalDetail>
-    <div class="d-flex fex-row" style="padding-left: 3px;">
-      <font-awesome-icon
-        v-if="entry.id === showLogs"
+    <div
+      v-if="entry.id === showAdditional && isPending && (isFD || isAGM || isBN)"
+      class="d-flex border-bottom"
+    >
+      <FairwayDimensionDetail :entry="entry" v-if="isFD" />
+      <ApprovedGaugeMeasurementDetail :entry="entry" v-if="isAGM" />
+      <BottleneckDetail :entry="entry" v-if="isBN" />
+    </div>
+    <div class="d-flex" style="padding-left: 3px;">
+      <UISpinnerButton
         @click="toggleAdditionalLogging"
-        class="my-auto mr-1 text-info pointer"
-        icon="angle-down"
-        fixed-width
-      ></font-awesome-icon>
-      <font-awesome-icon
-        v-if="entry.id !== showLogs"
-        @click="toggleAdditionalLogging"
-        class="my-auto mr-1 text-info pointer"
-        icon="angle-right"
-        fixed-width
-      ></font-awesome-icon>
+        :state="entry.id === showLogs"
+        :icons="['angle-right', 'angle-down']"
+        classes="text-info"
+      />
       <span class="text-info"><translate>Logs</translate></span>
     </div>
-    <AdditionalLog
-      v-if="entry.id === showLogs"
-      class="d-flex flex-row"
-    ></AdditionalLog>
+    <AdditionalLog v-if="entry.id === showLogs" class="d-flex flex-row" />
   </div>
 </template>
 
@@ -97,7 +72,10 @@
   components: {
     SoundingResultDetail: () => import("./SoundingResultDetail.vue"),
     StretchDetail: () => import("./StretchDetails.vue"),
-    AdditionalDetail: () => import("./AdditionalDetail.vue"),
+    FairwayDimensionDetail: () => import("./FairwayDimensionDetail.vue"),
+    ApprovedGaugeMeasurementDetail: () =>
+      import("./ApprovedGaugeMeasurementDetail.vue"),
+    BottleneckDetail: () => import("./BottleneckDetail.vue"),
     AdditionalLog: () => import("./AdditionalLog.vue")
   },
   props: ["entry"],
@@ -110,23 +88,21 @@
       return this.entry.state == "pending";
     },
     hasAdditionalInfo() {
-      return (
-        this.isPending && (this.isApprovedGaugeMeasurement || this.isBottleneck)
-      );
+      return this.isPending && (this.isAGM || this.isBN);
     },
-    isFairwayDimension() {
+    isFD() {
       return this.kind === "FD";
     },
-    isApprovedGaugeMeasurement() {
+    isAGM() {
       return this.kind === "AGM";
     },
-    isBottleneck() {
+    isBN() {
       return this.kind === "BN" || this.kind === "UBN";
     },
-    isStretch() {
+    isST() {
       return this.kind === "ST";
     },
-    isSoundingResult() {
+    isSR() {
       return this.kind === "SR";
     }
   },
--- a/client/src/components/importoverview/LogEntry.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/importoverview/LogEntry.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -1,12 +1,12 @@
 <template>
   <div class="row w-100 no-gutters text-left">
     <div style="width: 79px;" class="table-cell d-flex justify-content-between">
-      <font-awesome-icon
+      <UISpinnerButton
         @click="toggleDetails"
-        :class="'pointer ' + (entry.id === show ? 'text-white' : 'text-info')"
-        :icon="entry.id === show ? 'angle-down' : 'angle-right'"
-        fixed-width
-      ></font-awesome-icon>
+        :loading="loading"
+        :state="entry.id === show"
+        :icons="['angle-right', 'angle-down']"
+      />
       {{ entry.id }}
     </div>
     <div style="width: 53px;" class="table-cell center">
@@ -33,7 +33,7 @@
         class="text-warning"
         icon="exclamation-triangle"
         fixed-width
-      ></font-awesome-icon>
+      />
     </div>
     <div style="flex-grow: 1; padding: 0;" class="table-cell text-right">
       <button
@@ -41,20 +41,14 @@
         @click="toggleApproval($options.STATES.APPROVED)"
         v-if="entry.state === 'pending'"
       >
-        <font-awesome-icon
-          class="small pointer"
-          icon="check"
-        ></font-awesome-icon>
+        <font-awesome-icon class="small pointer" icon="check" />
       </button>
       <button
         :class="['action rejected', { active: isRejected }]"
         @click="toggleApproval($options.STATES.REJECTED)"
         v-if="entry.state === 'pending'"
       >
-        <font-awesome-icon
-          icon="times"
-          class="small pointer"
-        ></font-awesome-icon>
+        <font-awesome-icon icon="times" class="small pointer" />
       </button>
     </div>
   </div>
@@ -109,6 +103,11 @@
 export default {
   STATES,
   props: ["entry"],
+  data() {
+    return {
+      loading: false
+    };
+  },
   computed: {
     ...mapState("imports", ["show"]),
     needsApproval() {
@@ -135,6 +134,7 @@
         this.$store.commit("imports/hideAdditionalInfo");
         this.$store.commit("imports/hideAdditionalLogs");
       } else {
+        this.loading = true;
         HTTP.get("/imports/" + this.entry.id, {
           headers: { "X-Gemma-Auth": localStorage.getItem("token") }
         })
@@ -148,7 +148,8 @@
               title: this.$gettext("Backend Error"),
               message: `${status}: ${data.message || data}`
             });
-          });
+          })
+          .finally(() => (this.loading = false));
       }
     }
   }
--- a/client/src/components/importoverview/SoundingResultDetail.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/importoverview/SoundingResultDetail.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -1,6 +1,5 @@
 <template>
   <div>
-    <span class="empty"></span>
     <a @click="zoomTo()" class="text-info pointer">
       {{ details.summary.bottleneck }}
     </a>
--- a/client/src/components/importoverview/StretchDetails.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/importoverview/StretchDetails.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -1,6 +1,5 @@
 <template>
   <div>
-    <span class="empty">&nbsp;</span>
     <a @click="zoomToStretch()" class="text-info pointer">{{
       details.summary.stretch
     }}</a>
--- a/client/src/components/importschedule/Importschedule.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/importschedule/Importschedule.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -1,6 +1,6 @@
 <template>
   <div class="d-flex flex-row">
-    <Spacer></Spacer>
+    <Spacer />
     <div class="mt-2 w-100">
       <div class="card flex-grow-1 schedulecard shadow-xs">
         <UIBoxHeader icon="clock" :title="importScheduleLabel" />
@@ -8,7 +8,7 @@
           <div class="searchgroup input-group">
             <div class="input-group-prepend">
               <span class="input-group-text" id="search">
-                <font-awesome-icon icon="search"></font-awesome-icon>
+                <font-awesome-icon icon="search" />
               </span>
             </div>
             <input
@@ -47,7 +47,7 @@
                 class="fa-fw mr-2"
                 fixed-width
                 icon="check"
-              ></font-awesome-icon>
+              />
             </div>
             <div class="table-cell col justify-content-end">
               <button
@@ -55,24 +55,21 @@
                 class="btn btn-xs btn-dark mr-1"
                 :disabled="importScheduleDetailVisible"
               >
-                <font-awesome-icon
-                  icon="pencil-alt"
-                  fixed-width
-                ></font-awesome-icon>
+                <font-awesome-icon icon="pencil-alt" fixed-width />
               </button>
               <button
                 @click="deleteSchedule(schedule)"
                 class="btn btn-xs btn-dark mr-1"
                 :disabled="importScheduleDetailVisible"
               >
-                <font-awesome-icon icon="trash" fixed-width></font-awesome-icon>
+                <font-awesome-icon icon="trash" fixed-width />
               </button>
               <button
                 @click="triggerManualImport(schedule.id)"
                 class="btn btn-xs btn-dark"
                 :disabled="importScheduleDetailVisible"
               >
-                <font-awesome-icon icon="play" fixed-width></font-awesome-icon>
+                <font-awesome-icon icon="play" fixed-width />
               </button>
             </div>
           </template>
@@ -88,7 +85,7 @@
         </div>
       </div>
     </div>
-    <Importscheduledetail></Importscheduledetail>
+    <Importscheduledetail />
   </div>
 </template>
 
--- a/client/src/components/importschedule/Importscheduledetail.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/importschedule/Importscheduledetail.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -15,33 +15,33 @@
                 </small>
               </div>
               <select v-model="import_" class="custom-select" id="importtype">
-                <option :value="$options.IMPORTTYPES.BOTTLENECK"
-                  ><translate>Bottlenecks</translate></option
-                >
-                <option :value="$options.IMPORTTYPES.WATERWAYAXIS"
-                  ><translate>Waterway axis</translate></option
-                >
-                <option :value="$options.IMPORTTYPES.GAUGEMEASUREMENT"
-                  ><translate>Gauge measurement</translate></option
-                >
-                <option :value="$options.IMPORTTYPES.FAIRWAYAVAILABILITY"
-                  ><translate>Available fairway depths</translate></option
-                >
-                <option :value="$options.IMPORTTYPES.WATERWAYAREA"
-                  ><translate>Waterway area</translate></option
-                >
-                <option :value="$options.IMPORTTYPES.FAIRWAYDIMENSION"
-                  ><translate>Fairway dimension</translate></option
-                >
-                <option :value="$options.IMPORTTYPES.WATERWAYGAUGES"
-                  ><translate>Waterway gauges</translate></option
-                >
-                <option :value="$options.IMPORTTYPES.DISTANCEMARKSVIRTUAL"
-                  ><translate>Distance marks virtual</translate></option
-                >
-                <option :value="$options.IMPORTTYPES.DISTANCEMARKSASHORE"
-                  ><translate>Distance marks ashore</translate></option
-                >
+                <option :value="$options.IMPORTTYPES.BOTTLENECK">
+                  <translate>Bottlenecks</translate>
+                </option>
+                <option :value="$options.IMPORTTYPES.WATERWAYAXIS">
+                  <translate>Waterway axis</translate>
+                </option>
+                <option :value="$options.IMPORTTYPES.GAUGEMEASUREMENT">
+                  <translate>Gauge measurement</translate>
+                </option>
+                <option :value="$options.IMPORTTYPES.FAIRWAYAVAILABILITY">
+                  <translate>Available fairway depths</translate>
+                </option>
+                <option :value="$options.IMPORTTYPES.WATERWAYAREA">
+                  <translate>Waterway area</translate>
+                </option>
+                <option :value="$options.IMPORTTYPES.FAIRWAYDIMENSION">
+                  <translate>Fairway dimension</translate>
+                </option>
+                <option :value="$options.IMPORTTYPES.WATERWAYGAUGES">
+                  <translate>Waterway gauges</translate>
+                </option>
+                <option :value="$options.IMPORTTYPES.DISTANCEMARKSVIRTUAL">
+                  <translate>Distance marks virtual</translate>
+                </option>
+                <option :value="$options.IMPORTTYPES.DISTANCEMARKSASHORE">
+                  <translate>Distance marks ashore</translate>
+                </option>
               </select>
             </div>
             <div class="flex-column ml-4">
@@ -95,7 +95,7 @@
             "
             @urlChanged="setUrl"
             :url="url"
-          ></Availablefairwaydepth>
+          />
           <Bottleneck
             v-if="import_ == $options.IMPORTTYPES.BOTTLENECK"
             @urlChanged="setUrl"
@@ -103,7 +103,7 @@
             :url="url"
             :tolerance="tolerance"
             :directImport="directImport"
-          ></Bottleneck>
+          />
           <Distancemarksvirtual
             v-if="import_ == $options.IMPORTTYPES.DISTANCEMARKSVIRTUAL"
             @urlChanged="setUrl"
@@ -112,7 +112,7 @@
             :url="url"
             :username="username"
             :password="password"
-          ></Distancemarksvirtual>
+          />
           <Distancemarksashore
             v-if="import_ == $options.IMPORTTYPES.DISTANCEMARKSASHORE"
             @urlChanged="setUrl"
@@ -121,7 +121,7 @@
             :url="url"
             :featureType="featureType"
             :sortBy="sortBy"
-          ></Distancemarksashore>
+          />
           <Faiwaydimensions
             v-if="import_ == $options.IMPORTTYPES.FAIRWAYDIMENSION"
             @urlChanged="setUrl"
@@ -140,14 +140,14 @@
             :maxWidth="maxWidth"
             :sourceOrganization="sourceOrganization"
             :depth="depth"
-          ></Faiwaydimensions>
+          />
           <Gaugemeasurement
             v-if="
               import_ == $options.IMPORTTYPES.GAUGEMEASUREMENT && !directImport
             "
             @urlChanged="setUrl"
             :url="url"
-          ></Gaugemeasurement>
+          />
           <Waterwayarea
             v-if="import_ == $options.IMPORTTYPES.WATERWAYAREA"
             @urlChanged="setUrl"
@@ -156,7 +156,7 @@
             :url="url"
             :featureType="featureType"
             :sortBy="sortBy"
-          ></Waterwayarea>
+          />
           <Waterwaygauges
             v-if="import_ == $options.IMPORTTYPES.WATERWAYGAUGES"
             @urlChanged="setUrl"
@@ -165,7 +165,7 @@
             :url="url"
             :username="username"
             :password="password"
-          ></Waterwaygauges>
+          />
           <Waterwayaxis
             v-if="import_ == $options.IMPORTTYPES.WATERWAYAXIS"
             @urlChanged="setUrl"
@@ -174,7 +174,7 @@
             :url="url"
             :featureType="featureType"
             :sortBy="sortBy"
-          ></Waterwayaxis>
+          />
 
           <template v-if="!directImport || !directImportAvailable">
             <div class="d-flex flex-row">
@@ -411,12 +411,8 @@
             class="shadow-sm btn btn-outline-info trigger"
             :disabled="!triggerActive || !isValid"
           >
-            <font-awesome-icon
-              class="fa-fw mr-2"
-              fixed-width
-              icon="play"
-            ></font-awesome-icon
-            ><translate>Trigger import</translate>
+            <font-awesome-icon class="fa-fw mr-2" fixed-width icon="play" />
+            <translate>Trigger import</translate>
           </button>
         </form>
       </div>
--- a/client/src/components/importschedule/importtypes/Distancemarksvirtual.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/importschedule/importtypes/Distancemarksvirtual.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -56,9 +56,7 @@
             class="input-group-text ml-2"
             @click="passwordVisible = !passwordVisible"
           >
-            <font-awesome-icon
-              :icon="passwordVisible ? 'eye-slash' : 'eye'"
-            ></font-awesome-icon>
+            <font-awesome-icon :icon="passwordVisible ? 'eye-slash' : 'eye'" />
           </span>
         </div>
         <div v-if="!password" class="d-flex flex-row">
--- a/client/src/components/importschedule/importtypes/Waterwaygauges.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/importschedule/importtypes/Waterwaygauges.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -56,9 +56,7 @@
             class="input-group-text ml-2"
             @click="passwordVisible = !passwordVisible"
           >
-            <font-awesome-icon
-              :icon="passwordVisible ? 'eye-slash' : 'eye'"
-            ></font-awesome-icon>
+            <font-awesome-icon :icon="passwordVisible ? 'eye-slash' : 'eye'" />
           </span>
         </div>
         <div v-if="!password" class="d-flex flex-row">
--- a/client/src/components/layers/Layerselect.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/layers/Layerselect.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -21,6 +21,9 @@
     <div v-if="isVisible && isBottleneckIsolineLayer">
       <img class="rounded my-1 d-block" :src="isolinesLegendImgDataURL" />
     </div>
+    <div v-if="isVisible && isBottleneckDifferences">
+      <img class="rounded my-1 d-block" :src="differencesLegendImgDataURL" />
+    </div>
   </div>
 </template>
 
@@ -51,6 +54,7 @@
 import { HTTP } from "@/lib/http";
 import { mapState } from "vuex";
 import { LAYERS } from "@/store/map.js";
+
 export default {
   props: ["layername", "layerindex", "isVisible"],
   name: "layerselect",
@@ -58,9 +62,15 @@
     LegendElement: () => import("./LegendElement.vue")
   },
   computed: {
-    ...mapState("map", ["isolinesLegendImgDataURL"]),
+    ...mapState("map", [
+      "isolinesLegendImgDataURL",
+      "differencesLegendImgDataURL"
+    ]),
     isBottleneckIsolineLayer() {
       return this.layername == LAYERS.BOTTLENECKISOLINE;
+    },
+    isBottleneckDifferences() {
+      return this.layername == LAYERS.DIFFERENCES;
     }
   },
   methods: {
@@ -89,6 +99,24 @@
         reader.readAsDataURL(response.data);
       });
     }
+    if (this.isBottleneckDifferences) {
+      const src =
+        "/internal/wms?REQUEST=GetLegendGraphic&VERSION=1.0.0&FORMAT=image/png&WIDTH=20&HEIGHT=20&LAYER=sounding_differences&legend_options=columns:4;fontAntiAliasing:true";
+      HTTP.get(src, {
+        headers: {
+          Accept: "image/png",
+          "X-Gemma-Auth": localStorage.getItem("token")
+        },
+        responseType: "blob"
+      }).then(response => {
+        var that = this;
+        const reader = new FileReader();
+        reader.onload = function() {
+          that.$store.commit("map/differencesLegendImgDataURL", this.result);
+        };
+        reader.readAsDataURL(response.data);
+      });
+    }
   }
 };
 </script>
--- a/client/src/components/splitscreen/Splitscreen.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/splitscreen/Splitscreen.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -14,7 +14,7 @@
         v-if="activeSplitscreen"
       />
       <div class="d-flex flex-fill">
-        <SpinnerOverlay v-if="splitscreenLoading" />
+        <UISpinnerOverlay v-if="splitscreenLoading" />
         <transition name="fade" mode="out-in">
           <component
             :is="activeSplitscreen.component"
--- a/client/src/components/systemconfiguration/ColorSettings.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/systemconfiguration/ColorSettings.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -43,9 +43,7 @@
  * Bernhard Reiter <bernhard@intevation.de>
  * Markus Kottländer <markus@intevation.de>
  */
-import { Chrome } from "vue-color";
-import { Compact } from "vue-color";
-
+import { Chrome, Compact } from "vue-color";
 import { HTTP } from "@/lib/http";
 import { displayError } from "@/lib/errors.js";
 
--- a/client/src/components/systemconfiguration/Systemconfiguration.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/systemconfiguration/Systemconfiguration.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -1,6 +1,6 @@
 <template>
   <div class="d-flex flex-row">
-    <Spacer></Spacer>
+    <Spacer />
     <div class="card sysconfig mt-2 shadow-xs">
       <UIBoxHeader icon="wrench" :title="systemconfigurationLabel" />
       <div class="card-body text-left">
--- a/client/src/components/toolbar/Gauges.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/toolbar/Gauges.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -2,11 +2,12 @@
   <div
     @click="$store.commit('application/showGauges', !showGauges)"
     class="toolbar-button"
+    v-tooltip.right="label"
   >
     <font-awesome-icon
       icon="ruler-vertical"
       :class="{ 'text-info': showGauges }"
-    ></font-awesome-icon>
+    />
   </div>
 </template>
 
@@ -28,7 +29,10 @@
 
 export default {
   computed: {
-    ...mapState("application", ["showGauges"])
+    ...mapState("application", ["showGauges"]),
+    label() {
+      return this.$gettext("Gauges");
+    }
   }
 };
 </script>
--- a/client/src/components/toolbar/Identify.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/toolbar/Identify.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -2,11 +2,9 @@
   <div
     @click="$store.commit('application/showIdentify', !showIdentify)"
     class="toolbar-button"
+    v-tooltip.right="label"
   >
-    <font-awesome-icon
-      icon="info"
-      :class="{ 'text-info': showIdentify }"
-    ></font-awesome-icon>
+    <font-awesome-icon icon="info" :class="{ 'text-info': showIdentify }" />
     <span
       :class="[
         'indicator',
@@ -46,6 +44,9 @@
     ...mapGetters("map", ["filteredIdentifiedFeatures"]),
     badgeCount() {
       return this.filteredIdentifiedFeatures.length + !!this.currentMeasurement;
+    },
+    label() {
+      return this.$gettext("Identified Features");
     }
   }
 };
--- a/client/src/components/toolbar/Layers.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/toolbar/Layers.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -2,11 +2,12 @@
   <div
     @click="$store.commit('application/showLayers', !showLayers)"
     class="toolbar-button"
+    v-tooltip.right="label"
   >
     <font-awesome-icon
       icon="layer-group"
       :class="{ 'text-info': showLayers }"
-    ></font-awesome-icon>
+    />
   </div>
 </template>
 
@@ -29,7 +30,10 @@
 export default {
   name: "layers",
   computed: {
-    ...mapState("application", ["showLayers"])
+    ...mapState("application", ["showLayers"]),
+    label() {
+      return this.$gettext("Map Layers");
+    }
   }
 };
 </script>
--- a/client/src/components/toolbar/Linetool.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/toolbar/Linetool.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -1,9 +1,9 @@
 <template>
-  <div @click="toggleLineTool" class="toolbar-button">
+  <div @click="toggleLineTool" class="toolbar-button" v-tooltip.right="label">
     <font-awesome-icon
       icon="ruler"
       :class="{ 'text-info': lineTool && lineTool.getActive() }"
-    ></font-awesome-icon>
+    />
   </div>
 </template>
 
@@ -28,7 +28,10 @@
   name: "linetool",
   computed: {
     ...mapGetters("map", ["getVSourceByName"]),
-    ...mapState("map", ["lineTool", "polygonTool", "cutTool"])
+    ...mapState("map", ["lineTool", "polygonTool", "cutTool"]),
+    label() {
+      return this.$gettext("Measure Distance");
+    }
   },
   methods: {
     toggleLineTool() {
--- a/client/src/components/toolbar/Pdftool.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/toolbar/Pdftool.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -2,11 +2,9 @@
   <div
     @click="$store.commit('application/showPdfTool', !showPdfTool)"
     class="toolbar-button"
+    v-tooltip.right="label"
   >
-    <font-awesome-icon
-      icon="file-pdf"
-      :class="{ 'text-info': showPdfTool }"
-    ></font-awesome-icon>
+    <font-awesome-icon icon="file-pdf" :class="{ 'text-info': showPdfTool }" />
   </div>
 </template>
 
@@ -29,7 +27,10 @@
 export default {
   name: "pdftool",
   computed: {
-    ...mapState("application", ["showPdfTool"])
+    ...mapState("application", ["showPdfTool"]),
+    label() {
+      return this.$gettext("Generate PDF");
+    }
   }
 };
 </script>
--- a/client/src/components/toolbar/Polygontool.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/toolbar/Polygontool.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -1,9 +1,13 @@
 <template>
-  <div @click="togglePolygonTool" class="toolbar-button">
+  <div
+    @click="togglePolygonTool"
+    class="toolbar-button"
+    v-tooltip.right="label"
+  >
     <font-awesome-icon
       icon="draw-polygon"
       :class="{ 'text-info': polygonTool && polygonTool.getActive() }"
-    ></font-awesome-icon>
+    />
   </div>
 </template>
 
@@ -28,7 +32,10 @@
   name: "polygontool",
   computed: {
     ...mapGetters("map", ["getVSourceByName"]),
-    ...mapState("map", ["lineTool", "polygonTool", "cutTool"])
+    ...mapState("map", ["lineTool", "polygonTool", "cutTool"]),
+    label() {
+      return this.$gettext("Measure Area");
+    }
   },
   methods: {
     togglePolygonTool() {
--- a/client/src/components/toolbar/Profiles.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/toolbar/Profiles.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -2,11 +2,12 @@
   <div
     @click="$store.commit('application/showProfiles', !showProfiles)"
     class="toolbar-button"
+    v-tooltip.right="label"
   >
     <font-awesome-icon
       icon="chart-area"
       :class="{ 'text-info': showProfiles }"
-    ></font-awesome-icon>
+    />
   </div>
 </template>
 
@@ -29,7 +30,10 @@
 export default {
   name: "profiles",
   computed: {
-    ...mapState("application", ["showProfiles"])
+    ...mapState("application", ["showProfiles"]),
+    label() {
+      return this.$gettext("Profiles");
+    }
   }
 };
 </script>
--- a/client/src/components/toolbar/Toolbar.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/toolbar/Toolbar.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -6,13 +6,13 @@
           (expandToolbar ? 'expanded' : 'collapsed')
       "
     >
-      <Identify class="pointer" />
-      <Layers class="pointer" />
-      <Profiles class="pointer" />
-      <Gauges class="pointer" />
-      <Linetool class="pointer" />
-      <Polygontool class="pointer" />
-      <Pdftool class="pointer" />
+      <Identify />
+      <Layers />
+      <Profiles />
+      <Gauges />
+      <Linetool />
+      <Polygontool />
+      <Pdftool />
     </div>
     <div
       @click="$store.commit('application/expandToolbar', !expandToolbar)"
@@ -21,7 +21,7 @@
       <font-awesome-icon
         class="pointer"
         :icon="expandToolbar ? 'angle-up' : 'angle-down'"
-      ></font-awesome-icon>
+      />
     </div>
   </div>
 </template>
@@ -34,6 +34,7 @@
   overflow: hidden;
   transition: max-height 0.4s;
   margin-bottom: auto;
+  cursor: pointer;
 }
 
 .toolbar-collapsed {
--- a/client/src/components/ui/UIBoxHeader.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/ui/UIBoxHeader.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -58,6 +58,8 @@
     padding-left: 0.25rem
     .box-icon
       margin-right: 0.25rem
+  .box-control
+    margin-left: 3px
 </style>
 
 <script>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/components/ui/UISpinnerButton.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -0,0 +1,56 @@
+<template>
+  <div :class="classesString" @click="$emit('click')">
+    <font-awesome-icon :icon="iconString" :spin="loading" fixed-width />
+    <slot />
+  </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>
+ */
+export default {
+  props: {
+    loading: {
+      type: Boolean,
+      default: false
+    },
+    state: [Number, Boolean],
+    icons: {
+      type: [String, Array],
+      default: () => ["angle-down", "angle-up"]
+    },
+    classes: {
+      type: [String, Array],
+      default: () => ["text-info", "text-white"]
+    }
+  },
+  computed: {
+    classesString() {
+      return (
+        "pointer " +
+        (Array.isArray(this.classes)
+          ? this.classes[Number(this.state)]
+          : this.classes)
+      );
+    },
+    iconString() {
+      return this.loading
+        ? "spinner"
+        : Array.isArray(this.icons)
+        ? this.icons[Number(this.state)]
+        : this.icons;
+    }
+  }
+};
+</script>
--- a/client/src/components/ui/UITableBody.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/ui/UITableBody.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -38,7 +38,7 @@
         &.center
           justify-content: center
     .expand
-      border-bottom: solid 1px $color-info
+      border-bottom: solid 2px $color-info
 
     &.active
       > .row
--- a/client/src/components/usermanagement/Passwordfield.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/usermanagement/Passwordfield.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -12,14 +12,12 @@
         :required="required"
       />
       <span class="input-group-text" @click="showPassword">
-        <font-awesome-icon
-          :icon="readablePassword ? 'eye-slash' : 'eye'"
-        ></font-awesome-icon>
+        <font-awesome-icon :icon="readablePassword ? 'eye-slash' : 'eye'" />
       </span>
     </div>
     <div v-show="passworderrors" class="text-danger">
       <small>
-        <font-awesome-icon icon="exclamation-triangle"></font-awesome-icon>
+        <font-awesome-icon icon="exclamation-triangle" />
         {{ this.passworderrors }}
       </small>
     </div>
--- a/client/src/components/usermanagement/Userdetail.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/usermanagement/Userdetail.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -20,9 +20,7 @@
             />
             <div v-show="errors.user" class="text-danger">
               <small>
-                <font-awesome-icon
-                  icon="exclamation-triangle"
-                ></font-awesome-icon>
+                <font-awesome-icon icon="exclamation-triangle" />
                 {{ errors.user }}
               </small>
             </div>
@@ -46,9 +44,7 @@
             </select>
             <div v-show="errors.country" class="text-danger">
               <small>
-                <font-awesome-icon
-                  icon="exclamation-triangle"
-                ></font-awesome-icon>
+                <font-awesome-icon icon="exclamation-triangle" />
                 {{ errors.country }}
               </small>
             </div>
@@ -65,9 +61,7 @@
             />
             <div v-show="errors.email" class="text-danger">
               <small>
-                <font-awesome-icon
-                  icon="exclamation-triangle"
-                ></font-awesome-icon>
+                <font-awesome-icon icon="exclamation-triangle" />
                 {{ errors.email }}
               </small>
             </div>
@@ -94,9 +88,7 @@
             </select>
             <div v-show="errors.role" class="text-danger">
               <small>
-                <font-awesome-icon
-                  icon="exclamation-triangle"
-                ></font-awesome-icon>
+                <font-awesome-icon icon="exclamation-triangle" />
                 {{ errors.role }}
               </small>
             </div>
@@ -107,7 +99,7 @@
               :placeholder="passwordPlaceholder"
               :label="passwordLabel"
               :passworderrors="errors.password"
-            ></PasswordField>
+            />
           </div>
           <div class="form-group row">
             <PasswordField
@@ -115,7 +107,7 @@
               :placeholder="passwordRePlaceholder"
               :label="passwordReLabel"
               :passworderrors="errors.passwordre"
-            ></PasswordField>
+            />
           </div>
         </div>
         <div>
@@ -124,7 +116,7 @@
             :disabled="submitted"
             class="shadow-sm btn btn-info submit-button"
           >
-            <translate>Submit</translate>
+            <translate>Save</translate>
           </button>
         </div>
       </form>
--- a/client/src/components/usermanagement/Usermanagement.vue	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/components/usermanagement/Usermanagement.vue	Fri Apr 05 10:04:45 2019 +0200
@@ -1,6 +1,6 @@
 <template>
   <div class="main d-flex flex-row" style="position: relative;">
-    <Spacer></Spacer>
+    <Spacer />
     <div class="d-flex content py-2">
       <div :class="userlistStyle">
         <div class="card shadow-xs">
@@ -27,7 +27,7 @@
                   v-tooltip="roleLabel(user.role)"
                   :icon="roleIcon(user.role)"
                   class="fa-lg"
-                ></font-awesome-icon>
+                />
               </div>
               <div class="table-cell col-4" @click="selectUser(user.user)">
                 {{ user.user }}
@@ -68,7 +68,7 @@
                 v-if="this.page !== 1"
                 class="mr-2 btn btn-sm btn-light align-self-center"
               >
-                <font-awesome-icon icon="angle-left"></font-awesome-icon>
+                <font-awesome-icon icon="angle-left" />
               </button>
               {{ this.page }} / {{ this.pages }}
               <button
@@ -76,7 +76,7 @@
                 v-if="this.page !== this.pages"
                 class="ml-2 btn btn-sm btn-light align-self-center"
               >
-                <font-awesome-icon icon="angle-right"></font-awesome-icon>
+                <font-awesome-icon icon="angle-right" />
               </button>
             </div>
             <button @click="addUser" class="btn btn-info addbutton shadow-sm">
@@ -85,7 +85,7 @@
           </div>
         </div>
       </div>
-      <Userdetail v-if="isUserDetailsVisible"></Userdetail>
+      <Userdetail v-if="isUserDetailsVisible" />
     </div>
   </div>
 </template>
@@ -132,14 +132,8 @@
 import { mapGetters, mapState } from "vuex";
 import { displayError, displayInfo } from "@/lib/errors.js";
 import { HTTP } from "@/lib/http";
-import Vue from "vue";
-import { VTooltip, VPopover, VClosePopover } from "v-tooltip";
 import { sortTable } from "@/lib/mixins";
 
-Vue.directive("tooltip", VTooltip);
-Vue.directive("close-popover", VClosePopover);
-Vue.component("v-popover", VPopover);
-
 export default {
   name: "userview",
   mixins: [sortTable],
--- a/client/src/main.js	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/main.js	Fri Apr 05 10:04:45 2019 +0200
@@ -34,6 +34,7 @@
 import UITableHeader from "@/components/ui/UITableHeader";
 import UITableBody from "@/components/ui/UITableBody";
 import UISpinnerOverlay from "@/components/ui/UISpinnerOverlay";
+import UISpinnerButton from "@/components/ui/UISpinnerButton";
 
 // styles
 import "../node_modules/bootstrap/dist/css/bootstrap.min.css";
@@ -180,6 +181,7 @@
 Vue.component("UITableHeader", UITableHeader);
 Vue.component("UITableBody", UITableBody);
 Vue.component("UISpinnerOverlay", UISpinnerOverlay);
+Vue.component("UISpinnerButton", UISpinnerButton);
 
 // register global filters
 for (let name in filters) Vue.filter(name, filters[name]);
--- a/client/src/store/imports.js	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/store/imports.js	Fri Apr 05 10:04:45 2019 +0200
@@ -32,6 +32,7 @@
     declined: false,
     warning: false,
     stretches: [],
+    selectedStretchId: null,
     imports: [],
     reviewed: [],
     show: null,
@@ -128,6 +129,9 @@
     setStretches: (state, stretches) => {
       state.stretches = stretches;
     },
+    selectedStretchId: (state, id) => {
+      state.selectedStretchId = id;
+    },
     setReviewed: (state, reviewed) => {
       state.reviewed = reviewed;
     },
--- a/client/src/store/map.js	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/src/store/map.js	Fri Apr 05 10:04:45 2019 +0200
@@ -80,6 +80,7 @@
     polygonTool: null, // open layers interaction object (Draw)
     cutTool: null, // open layers interaction object (Draw)
     isolinesLegendImgDataURL: "",
+    differencesLegendImgDataURL: "",
     layers: {
       [LAYERS.OPENSTREETMAP]: {
         name: LAYERS.OPENSTREETMAP,
@@ -121,15 +122,30 @@
           source: new VectorSource({
             strategy: bboxStrategy
           }),
-          style: new Style({
-            stroke: new Stroke({
-              color: "rgba(250, 200, 0, .8)",
-              width: 2
-            }),
-            fill: new Fill({
-              color: "rgba(250, 200, 10, .3)"
-            })
-          })
+          style: feature => {
+            let style = new Style({
+              stroke: new Stroke({
+                color: "rgba(250, 200, 0, .8)",
+                width: 2
+              }),
+              fill: new Fill({
+                color: "rgba(250, 200, 10, .3)"
+              })
+            });
+            if (feature.get("highlighted")) {
+              style = new Style({
+                stroke: new Stroke({
+                  color: "rgba(250, 240, 10, .9)",
+                  width: 5
+                }),
+                fill: new Fill({
+                  color: "rgba(250, 240, 0, .7)"
+                })
+              });
+            }
+
+            return style;
+          }
         }),
         isVisible: false
       },
@@ -644,6 +660,9 @@
     },
     isolinesLegendImgDataURL: (state, isolinesLegendImgDataURL) => {
       state.isolinesLegendImgDataURL = isolinesLegendImgDataURL;
+    },
+    differencesLegendImgDataURL: (state, differencesLegendImgDataURL) => {
+      state.differencesLegendImgDataURL = differencesLegendImgDataURL;
     }
   },
   actions: {
@@ -756,7 +775,6 @@
 
               for (let feature of features) {
                 let id = feature.getId();
-
                 // avoid identifying the same feature twice
                 if (
                   identifiedFeatures.findIndex(
@@ -820,6 +838,28 @@
                     });
                   }
                 }
+
+                // get selected stretch
+                if (/^stretches/.test(id)) {
+                  if (rootState.imports.selectedStretchId === feature.getId()) {
+                    commit("imports/selectedStretchId", null, { root: true });
+                  } else {
+                    commit("imports/selectedStretchId", feature.getId(), {
+                      root: true
+                    });
+                    commit("moveMap", {
+                      coordinates: getCenter(
+                        feature
+                          .getGeometry()
+                          .clone()
+                          .transform("EPSG:3857", "EPSG:4326")
+                          .getExtent()
+                      ),
+                      zoom: null,
+                      preventZoomOut: true
+                    });
+                  }
+                }
               }
 
               commit("setIdentifiedFeatures", identifiedFeatures);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/tests/e2e/reports/import.xml	Fri Apr 05 10:04:45 2019 +0200
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<testsuites errors="0"
+            failures="0"
+            tests="6">
+
+  <testsuite name="import"
+    errors="0" failures="0" hostname="" id="" package="import" skipped="6"
+    tests="6" time="0.000" timestamp="Thu, 04 Apr 2019 10:16:03 GMT">
+  
+
+  
+    <system-err>
+        Error while running [Protocols / Loading protocols]:
+
+  TypeError: browser.url(...).waitForElementVisible(...).setValue(...).setValue(...).click(...).pause(...).click(...).pause(...).click(...).pause(...).url.contains is not a function
+    at Object.Loading protocols (/home/thomas/go/src/gemma.intevation.de/gemma/client/tests/e2e/specs/protocols.js:31:12)
+    at Module.call (/home/thomas/go/src/gemma.intevation.de/gemma/client/node_modules/nightwatch/lib/runner/module.js:62:34)
+    at /home/thomas/go/src/gemma.intevation.de/gemma/client/node_modules/nightwatch/lib/runner/testcase.js:70:29
+    at _fulfilled (/home/thomas/go/src/gemma.intevation.de/gemma/client/node_modules/q/q.js:834:54)
+    at self.promiseDispatch.done (/home/thomas/go/src/gemma.intevation.de/gemma/client/node_modules/q/q.js:863:30)
+    at Promise.promise.promiseDispatch (/home/thomas/go/src/gemma.intevation.de/gemma/client/node_modules/q/q.js:796:13)
+    at /home/thomas/go/src/gemma.intevation.de/gemma/client/node_modules/q/q.js:556:49
+    at runSingle (/home/thomas/go/src/gemma.intevation.de/gemma/client/node_modules/q/q.js:137:13)
+    at flush (/home/thomas/go/src/gemma.intevation.de/gemma/client/node_modules/q/q.js:125:13)
+    at _combinedTickCallback (internal/process/next_tick.js:132:7)
+    </system-err>
+  
+
+  
+    
+    <testcase
+      name="Bottleneck import" classname="import">
+      <skipped />
+    </testcase>
+    
+    <testcase
+      name="Available fairwaydepth" classname="import">
+      <skipped />
+    </testcase>
+    
+    <testcase
+      name="Gauge measurement" classname="import">
+      <skipped />
+    </testcase>
+    
+    <testcase
+      name="Import Fairway Dimensions" classname="import">
+      <skipped />
+    </testcase>
+    
+    <testcase
+      name="Import Waterway Axis" classname="import">
+      <skipped />
+    </testcase>
+    
+    <testcase
+      name="Import Waterway Area " classname="import">
+      <skipped />
+    </testcase>
+    
+  
+  </testsuite>
+</testsuites>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/tests/e2e/reports/login.xml	Fri Apr 05 10:04:45 2019 +0200
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<testsuites errors="0"
+            failures="0"
+            tests="5">
+
+  <testsuite name="login"
+    errors="0" failures="0" hostname="" id="" package="login" skipped="5"
+    tests="5" time="0.000" timestamp="Thu, 04 Apr 2019 10:16:03 GMT">
+  
+
+  
+    <system-err>
+        Error while running [Protocols / Loading protocols]:
+
+  TypeError: browser.url(...).waitForElementVisible(...).setValue(...).setValue(...).click(...).pause(...).click(...).pause(...).click(...).pause(...).url.contains is not a function
+    at Object.Loading protocols (/home/thomas/go/src/gemma.intevation.de/gemma/client/tests/e2e/specs/protocols.js:31:12)
+    at Module.call (/home/thomas/go/src/gemma.intevation.de/gemma/client/node_modules/nightwatch/lib/runner/module.js:62:34)
+    at /home/thomas/go/src/gemma.intevation.de/gemma/client/node_modules/nightwatch/lib/runner/testcase.js:70:29
+    at _fulfilled (/home/thomas/go/src/gemma.intevation.de/gemma/client/node_modules/q/q.js:834:54)
+    at self.promiseDispatch.done (/home/thomas/go/src/gemma.intevation.de/gemma/client/node_modules/q/q.js:863:30)
+    at Promise.promise.promiseDispatch (/home/thomas/go/src/gemma.intevation.de/gemma/client/node_modules/q/q.js:796:13)
+    at /home/thomas/go/src/gemma.intevation.de/gemma/client/node_modules/q/q.js:556:49
+    at runSingle (/home/thomas/go/src/gemma.intevation.de/gemma/client/node_modules/q/q.js:137:13)
+    at flush (/home/thomas/go/src/gemma.intevation.de/gemma/client/node_modules/q/q.js:125:13)
+    at _combinedTickCallback (internal/process/next_tick.js:132:7)
+    </system-err>
+  
+
+  
+    
+    <testcase
+      name="Page Load" classname="login">
+      <skipped />
+    </testcase>
+    
+    <testcase
+      name="Login failed" classname="login">
+      <skipped />
+    </testcase>
+    
+    <testcase
+      name="Login oana success" classname="login">
+      <skipped />
+    </testcase>
+    
+    <testcase
+      name="Login oana switch url" classname="login">
+      <skipped />
+    </testcase>
+    
+    <testcase
+      name="Login switch user from oana to sophie" classname="login">
+      <skipped />
+    </testcase>
+    
+  
+  </testsuite>
+</testsuites>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/tests/e2e/reports/protocols.xml	Fri Apr 05 10:04:45 2019 +0200
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<testsuites errors="1"
+            failures="0"
+            tests="1">
+
+  <testsuite name="protocols"
+    errors="1" failures="0" hostname="" id="" package="protocols" skipped="0"
+    tests="1" time="0.005000" timestamp="Thu, 04 Apr 2019 10:16:03 GMT">
+  
+    <testcase name="Loading protocols" classname="protocols" time="0.005000" assertions="0">
+    </testcase>
+  
+
+  
+    <system-err>
+        Error while running [Protocols / Loading protocols]:
+
+  TypeError: browser.url(...).waitForElementVisible(...).setValue(...).setValue(...).click(...).pause(...).click(...).pause(...).click(...).pause(...).url.contains is not a function
+    at Object.Loading protocols (/home/thomas/go/src/gemma.intevation.de/gemma/client/tests/e2e/specs/protocols.js:31:12)
+    at Module.call (/home/thomas/go/src/gemma.intevation.de/gemma/client/node_modules/nightwatch/lib/runner/module.js:62:34)
+    at /home/thomas/go/src/gemma.intevation.de/gemma/client/node_modules/nightwatch/lib/runner/testcase.js:70:29
+    at _fulfilled (/home/thomas/go/src/gemma.intevation.de/gemma/client/node_modules/q/q.js:834:54)
+    at self.promiseDispatch.done (/home/thomas/go/src/gemma.intevation.de/gemma/client/node_modules/q/q.js:863:30)
+    at Promise.promise.promiseDispatch (/home/thomas/go/src/gemma.intevation.de/gemma/client/node_modules/q/q.js:796:13)
+    at /home/thomas/go/src/gemma.intevation.de/gemma/client/node_modules/q/q.js:556:49
+    at runSingle (/home/thomas/go/src/gemma.intevation.de/gemma/client/node_modules/q/q.js:137:13)
+    at flush (/home/thomas/go/src/gemma.intevation.de/gemma/client/node_modules/q/q.js:125:13)
+    at _combinedTickCallback (internal/process/next_tick.js:132:7)
+    </system-err>
+  
+
+  
+  </testsuite>
+</testsuites>
--- a/client/tests/e2e/specs/import.js	Tue Apr 02 10:07:48 2019 +0200
+++ b/client/tests/e2e/specs/import.js	Fri Apr 05 10:04:45 2019 +0200
@@ -16,6 +16,7 @@
 // http://nightwatchjs.org/guide#usage
 
 module.exports = {
+  "@disabled": true,
   "Bottleneck import": browser => {
     browser
       .url(process.env.VUE_DEV_SERVER_URL)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/tests/e2e/specs/protocols.js	Fri Apr 05 10:04:45 2019 +0200
@@ -0,0 +1,37 @@
+/* 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>
+ */
+
+// For authoring Nightwatch tests, see
+// http://nightwatchjs.org/guide#usage
+
+module.exports = {
+  "Loading protocols": browser => {
+    browser
+      .url(process.env.VUE_DEV_SERVER_URL)
+      .waitForElementVisible("#app", 5000)
+      .setValue("input[id='inputUsername']", "sophie")
+      .setValue("input[id='inputPassword']", "so2Phie4")
+      .click("button[type='submit']")
+      .pause(1000)
+      .click(".menubutton")
+      .pause(1000)
+      .click("a[href='#/logs']")
+      .pause(1000)
+      .assert.urlContains("#/logs")
+      .click("#accesslog")
+      .click("#errorlog")
+      .assert.urlContains("#/logs")
+      .end();
+  }
+};
--- a/docker/Dockerfile.backend	Tue Apr 02 10:07:48 2019 +0200
+++ b/docker/Dockerfile.backend	Fri Apr 05 10:04:45 2019 +0200
@@ -23,4 +23,4 @@
 
 EXPOSE 8000
 
-CMD ["/usr/local/bin/gemma", "-c", "/opt/gemma/gemma.toml"]
+CMD ["/usr/local/bin/gemma", "-c", "/opt/gemma/example_conf.toml"]
--- a/docker/Dockerfile.db	Tue Apr 02 10:07:48 2019 +0200
+++ b/docker/Dockerfile.db	Fri Apr 05 10:04:45 2019 +0200
@@ -15,7 +15,6 @@
     apt-key add - &&\
     apt-get update &&\
     apt-get -y install postgresql-11-postgis-2.5 postgresql-11-pgtap
-RUN apt-get -y install --no-install-recommends postgis xsltproc jq
 
 USER postgres
 ENV PGBIN /usr/lib/postgresql/11/bin
--- a/pkg/common/time.go	Tue Apr 02 10:07:48 2019 +0200
+++ b/pkg/common/time.go	Fri Apr 05 10:04:45 2019 +0200
@@ -4,16 +4,22 @@
 // SPDX-License-Identifier: AGPL-3.0-or-later
 // License-Filename: LICENSES/AGPL-3.0.txt
 //
-// Copyright (C) 2018 by via donau
+// Copyright (C) 2018, 2019 by via donau
 //   – Österreichische Wasserstraßen-Gesellschaft mbH
 // Software engineering by Intevation GmbH
 //
 // Author(s):
 //  * Sascha L. Teichmann <sascha.teichmann@intevation.de>
+//  * Bernhard E. Reiter <bernhard.reiter@intevation.de>
 
 package common
 
+import "time"
+
 const (
-	TimeFormat = "2006-01-02T15:04:05"
+	// time.RFC3339 equals "simplified ISO format as defined by ECMA-262"
+	//   https://tc39.github.io/ecma262/#sec-date-time-string-format
+	// and "SHOULD be used in new protocols on the Internet." (RFC section 5.6)
+	TimeFormat = time.RFC3339
 	DateFormat = "2006-01-02"
 )
--- a/pkg/config/config.go	Tue Apr 02 10:07:48 2019 +0200
+++ b/pkg/config/config.go	Fri Apr 05 10:04:45 2019 +0200
@@ -4,7 +4,7 @@
 // SPDX-License-Identifier: AGPL-3.0-or-later
 // License-Filename: LICENSES/AGPL-3.0.txt
 //
-// Copyright (C) 2018 by via donau
+// Copyright (C) 2018, 2019 by via donau
 //   – Österreichische Wasserstraßen-Gesellschaft mbH
 // Software engineering by Intevation GmbH
 //
@@ -140,7 +140,6 @@
 }
 
 // ProxyPrefix is the prefix used in generated URLs by the proxy.
-// It defauls to http://${WebHost}:${WebPort}".
 // You may need to set this if you run gemma behind a proxy
 // on a specific domain.
 func ProxyPrefix() string {
@@ -156,7 +155,6 @@
 }
 
 // ExternalURL is the URL to find this server from the outside.
-// It defauls to http://${WebHost}:${WebPort}".
 func ExternalURL() string {
 	fetchExternal := func() {
 		if externalURL == "" {
@@ -183,7 +181,7 @@
 	return sessionTimeout
 }
 
-// The root directories where to find schema files.
+// SchemaDirs are the root directories where to find schema files.
 func SchemaDirs() string { return viper.GetString("schema-dirs") }
 
 // RootCmd is cobra command to be bound th the cobra/viper infrastructure.
@@ -260,10 +258,10 @@
 	str("proxy-key", "", "signing key for proxy URLs.\n"+
 		"Defaults to random key.")
 	str("proxy-prefix", "", "URL prefix of proxy.\n"+
-		"Defaults to 'http://${web-host}:${web-port}'")
+		"Defaults to 'http://${host}:${port}'")
 
 	str("external-url", "", "URL to find the server from the outside.\n"+
-		"Defaults to 'http://${web-host}:${web-port}'")
+		"Defaults to 'http://${host}:${port}'")
 
 	str("tmp-dir", "", "Temp directory of gemma server.\n"+
 		"Defaults to system temp directory.")
--- a/pkg/controllers/gauges.go	Tue Apr 02 10:07:48 2019 +0200
+++ b/pkg/controllers/gauges.go	Fri Apr 05 10:04:45 2019 +0200
@@ -42,7 +42,7 @@
   water_level
 FROM waterway.gauge_measurements
 WHERE
-  a.fk_gauge_id = (
+  fk_gauge_id = (
     $1::char(2),
     $2::char(3),
     $3::char(5),
--- a/pkg/controllers/user.go	Tue Apr 02 10:07:48 2019 +0200
+++ b/pkg/controllers/user.go	Fri Apr 05 10:04:45 2019 +0200
@@ -403,10 +403,10 @@
 
 	var bodyTmpl *template.Template
 	if userData.Role == "sys_admin" {
-		subject = "Sysadmin Notification TEST"
+		subject = "Gemma: Sysadmin Notification TEST"
 		bodyTmpl = testSysadminNotifyMailTmpl
 	} else if userData.Role == "waterway_admin" {
-		subject = "Waterway Admin Notification TEST"
+		subject = "Gemma: Waterway Admin Notification TEST"
 		bodyTmpl = testWWAdminNotifyMailTmpl
 	} else {
 		err = JSONError{
--- a/pkg/imports/dma.go	Tue Apr 02 10:07:48 2019 +0200
+++ b/pkg/imports/dma.go	Fri Apr 05 10:04:45 2019 +0200
@@ -77,36 +77,28 @@
 const (
 	deleteDistanceMarksSQL = `
 WITH resp AS (
-  SELECT best_utm(area) AS t,
-         ST_Transform(area::geometry, best_utm(area)) AS a
-  FROM users.responsibility_areas
-  WHERE country = users.current_user_country()
+  SELECT users.current_user_area_utm() AS a
 )
 DELETE FROM waterway.distance_marks
-WHERE ST_Covers(
-  (SELECT a FROM resp),
-  ST_Transform(geom::geometry, (SELECT t FROM resp)))
+WHERE pg_has_role('sys_admin', 'MEMBER')
+    OR ST_Covers((SELECT a FROM resp),
+      ST_Transform(geom::geometry, (SELECT ST_SRID(a) FROM resp)))
 `
 	insertDistanceMarksSQL = `
 WITH resp AS (
-  SELECT best_utm(area) AS t,
-         ST_Transform(area::geometry, best_utm(area)) AS a
-  FROM users.responsibility_areas
-  WHERE country = users.current_user_country()
+  SELECT users.current_user_area_utm() AS a
 )
 INSERT INTO waterway.distance_marks (geom, catdis)
-SELECT ST_Transform(clipped.geom, 4326)::geography, $3 FROM (
-    SELECT (ST_Dump(
-       ST_Intersection(
-         (SELECT a FROM resp),
-         ST_Transform(
-           ST_GeomFromWKB($1, $2::integer),
-           (SELECT t FROM resp)
-         )
-       )
-     )).geom AS geom
-  ) AS clipped
-  WHERE clipped.geom IS NOT NULL
+SELECT ST_Transform(new_dma, 4326), $3
+  FROM (SELECT
+      CASE WHEN pg_has_role('sys_admin', 'MEMBER')
+        THEN dma
+        ELSE ST_Intersection((SELECT a FROM resp),
+          ST_Transform(dma, (SELECT ST_SRID(a) FROM resp)))
+        END AS new_dma
+    FROM ST_GeomFromWKB($1, $2::integer) AS dma (dma)) AS new_dma
+  WHERE NOT ST_IsEmpty(new_dma)
+RETURNING id
 `
 )
 
@@ -169,6 +161,7 @@
 		unsupported       = stringCounter{}
 		missingProperties int
 		badProperties     int
+		outside           int
 		features          int
 	)
 
@@ -211,13 +204,19 @@
 				if err := json.Unmarshal(*feature.Geometry.Coordinates, &p); err != nil {
 					return err
 				}
-				if _, err := insertStmt.ExecContext(
+				var dmaid int64
+				err := insertStmt.QueryRowContext(
 					ctx,
 					p.asWKB(),
 					epsg,
 					props.HydroCatdis,
-				); err != nil {
-					feedback.Error("error: %s", err)
+				).Scan(&dmaid)
+				switch {
+				case err == sql.ErrNoRows:
+					outside++
+					// ignore -> filtered by responsibility area
+					continue
+				case err != nil:
 					return err
 				}
 				features++
@@ -242,6 +241,10 @@
 		feedback.Warn("Unsupported types found: %s", unsupported)
 	}
 
+	if outside > 0 {
+		feedback.Info("Features outside responsibility area: %d", outside)
+	}
+
 	if features == 0 {
 		err := errors.New("No features found")
 		feedback.Error("%v", err)
--- a/pkg/imports/email.go	Tue Apr 02 10:07:48 2019 +0200
+++ b/pkg/imports/email.go	Fri Apr 05 10:04:45 2019 +0200
@@ -28,7 +28,7 @@
 const (
 	selectEmailSQL = `SELECT email_address FROM users.list_users WHERE username = $1`
 
-	importNotificationMailSubject = `import notification mail`
+	importNotificationMailSubject = `Gemma: import notification mail`
 )
 
 var (
@@ -38,11 +38,11 @@
 a {{ .Description }} import on server {{ .Server }} triggered
 this email notification.
 
-{{ if eq .State "accepted" }}The imported data were successfully integrated into the database.{{ end -}}
-{{ if eq .State "unchanged" }}The import has not changed any data in the database.{{ end -}}
-{{ if eq .State "failed" }}The import failed for some reasons.{{ end -}}
+{{ if eq .State "accepted" }}The imported data were successfully integrated into the database. {{ end -}}
+{{ if eq .State "unchanged" }}The import has not changed any data in the database. {{ end -}}
+{{ if eq .State "failed" }}The import failed for some reasons. {{ end -}}
 {{ if eq .State "pending" }}The imported data could be integrated into the database
-but your final decision is needed.{{ end -}}
+but your final decision is needed. {{ end -}}
 
 Please follow this link to have a closer look at the details:
 
--- a/pkg/imports/fd.go	Tue Apr 02 10:07:48 2019 +0200
+++ b/pkg/imports/fd.go	Fri Apr 05 10:04:45 2019 +0200
@@ -139,15 +139,12 @@
 
 	deleteFairwayDimensionSQL = `
 WITH resp AS (
-  SELECT best_utm(area) AS t,
-         ST_Transform(area::geometry, best_utm(area)) AS a
-  FROM users.responsibility_areas
-  WHERE country = users.current_user_country()
+  SELECT users.current_user_area_utm() AS a
 )
 DELETE FROM waterway.fairway_dimensions
-WHERE ST_Covers(
-  (SELECT a FROM resp),
-  ST_Transform(area::geometry, (SELECT t FROM resp)))
+WHERE (pg_has_role('sys_admin', 'MEMBER')
+    OR ST_Covers((SELECT a FROM resp),
+      ST_Transform(area::geometry, (SELECT ST_SRID(a) FROM resp))))
   AND staging_done
   AND level_of_service = $1::smallint`
 
@@ -155,24 +152,27 @@
 	// avoid errors due to reprojection.
 	insertFairwayDimensionSQL = `
 WITH resp AS (
-  SELECT best_utm(area) AS t,
-         ST_Transform(area::geometry, best_utm(area)) AS a
-  FROM users.responsibility_areas
-  WHERE country = users.current_user_country()
+  SELECT users.current_user_area_utm() AS a
 )
-INSERT INTO waterway.fairway_dimensions (area, level_of_service, min_width, max_width, min_depth, date_info, source_organization)
-SELECT ST_Transform(clipped.geom, 4326)::geography, $3, $4, $5, $6, $7, $8 FROM (
-    SELECT (ST_Dump(
-       ST_Intersection(
-         (SELECT ST_Buffer(a, -0.0001) FROM resp),
-         ST_MakeValid(ST_Transform(
-           ST_GeomFromWKB($1, $2::integer),
-           (SELECT t FROM resp)
-         ))
-       )
-     )).geom AS geom
-  ) AS clipped
-  WHERE clipped.geom IS NOT NULL
+INSERT INTO waterway.fairway_dimensions (
+  area,
+  level_of_service,
+  min_width,
+  max_width,
+  min_depth,
+  date_info,
+  source_organization)
+SELECT ST_Transform(dmp.geom, 4326), $3, $4, $5, $6, $7, $8
+  FROM ST_GeomFromWKB($1, $2::integer) AS new_fd (new_fd),
+    ST_Dump(
+      CASE WHEN pg_has_role('sys_admin', 'MEMBER')
+        THEN ST_MakeValid(ST_Transform(
+          new_fd, best_utm(ST_Transform(new_fd, 4326))))
+        ELSE ST_CollectionExtract(ST_Intersection(
+            (SELECT ST_Buffer(a, -0.0001) FROM resp),
+            ST_MakeValid(ST_Transform(new_fd, (SELECT ST_SRID(a) FROM resp)))),
+          3)
+        END) AS dmp
 RETURNING id,
   ST_X(ST_Centroid(area::geometry)),
   ST_Y(ST_Centroid(area::geometry))
@@ -259,6 +259,8 @@
 
 		feedback.Info("Using EPSG: %d", epsg)
 
+		savepoint := Savepoint(ctx, tx, "feature")
+
 	features:
 		for _, feature := range rfc.Features {
 			if feature.Geometry.Coordinates == nil {
@@ -287,24 +289,28 @@
 				}
 				var fdid int64
 				var lat, lon float64
-				err = insertStmt.QueryRowContext(
-					ctx,
-					p.asWKB(),
-					epsg,
-					fd.LOS,
-					fd.MinWidth,
-					fd.MaxWidth,
-					fd.Depth,
-					dateInfo,
-					fd.SourceOrganization,
-				).Scan(&fdid, &lat, &lon)
+				err = savepoint(func() error {
+					err := insertStmt.QueryRowContext(
+						ctx,
+						p.asWKB(),
+						epsg,
+						fd.LOS,
+						fd.MinWidth,
+						fd.MaxWidth,
+						fd.Depth,
+						dateInfo,
+						fd.SourceOrganization,
+					).Scan(&fdid, &lat, &lon)
+					return err
+				})
 				switch {
 				case err == sql.ErrNoRows:
 					outside++
 					// ignore -> filtered by responsibility_areas
 					continue features
 				case err != nil:
-					return err
+					feedback.Warn(handleError(err).Error())
+					continue features
 				}
 				// Store for potential later removal.
 				if err = track(ctx, tx, importID, "waterway.fairway_dimensions", fdid); err != nil {
--- a/pkg/imports/st.go	Tue Apr 02 10:07:48 2019 +0200
+++ b/pkg/imports/st.go	Fri Apr 05 10:04:45 2019 +0200
@@ -97,8 +97,9 @@
               $8::char(5),
               $9::char(5),
               $10::int)::isrs)
-    ) AS r
-)
+    ) AS r),
+axs AS (
+  SELECT ISRSrange_axis((SELECT r FROM r), $16::double precision) AS axs)
 INSERT INTO waterway.stretches (
   name,
   stretch,
@@ -110,10 +111,10 @@
 ) VALUES (
   $11,
   (SELECT r FROM r),
-  ISRSrange_area(
-    ISRSrange_axis((SELECT r FROM r), $16::double precision),
-    (SELECT ST_Collect(CAST(area AS geometry))
-      FROM waterway.waterway_area)),
+  ST_Transform(ISRSrange_area(
+      (SELECT axs FROM axs),
+      (SELECT ST_Buffer(axs, 10000) FROM axs)),
+    4326),
   $12,
   $13,
   $14,
--- a/pkg/imports/wa.go	Tue Apr 02 10:07:48 2019 +0200
+++ b/pkg/imports/wa.go	Fri Apr 05 10:04:45 2019 +0200
@@ -81,22 +81,16 @@
 const (
 	deleteWaterwayAreaSQL = `
 WITH resp AS (
-  SELECT best_utm(area) AS t,
-         ST_Transform(area::geometry, best_utm(area)) AS a
-  FROM users.responsibility_areas
-  WHERE country = users.current_user_country()
+  SELECT users.current_user_area_utm() AS a
 )
 DELETE FROM waterway.waterway_area
 WHERE pg_has_role('sys_admin', 'MEMBER')
   OR ST_Covers((SELECT a FROM resp),
-    ST_Transform(area::geometry, (SELECT t FROM resp)))
+    ST_Transform(area::geometry, (SELECT ST_SRID(a) FROM resp)))
 `
 	insertWaterwayAreaSQL = `
 WITH resp AS (
-  SELECT best_utm(area) AS t,
-         ST_Transform(area::geometry, best_utm(area)) AS a
-  FROM users.responsibility_areas
-  WHERE country = users.current_user_country()
+  SELECT users.current_user_area_utm() AS a
 )
 INSERT INTO waterway.waterway_area (area, catccl, dirimp)
 SELECT dmp.geom, $3, $4
@@ -106,9 +100,10 @@
         THEN ST_MakeValid(ST_Transform(new_area,
           best_utm(ST_Transform(new_area, 4326))))
         ELSE ST_Intersection((SELECT a FROM resp),
-          ST_MakeValid(ST_Transform(new_area, (SELECT t FROM resp))))
+          ST_MakeValid(ST_Transform(new_area, (SELECT ST_SRID(a) FROM resp))))
         END,
       3), 4326)) AS dmp
+RETURNING id
 `
 )
 
@@ -175,6 +170,7 @@
 		unsupported       = stringCounter{}
 		missingProperties int
 		badProperties     int
+		outside           int
 		features          int
 	)
 
@@ -233,18 +229,24 @@
 				if err := json.Unmarshal(*feature.Geometry.Coordinates, &p); err != nil {
 					return err
 				}
-				if err := savepoint(func() error {
-					_, err := insertStmt.ExecContext(
+				var waid int64
+				err := savepoint(func() error {
+					err := insertStmt.QueryRowContext(
 						ctx,
 						p.asWKB(),
 						epsg,
 						catccl,
 						dirimp,
-					)
+					).Scan(&waid)
 					return err
-				}); err != nil {
+				})
+				switch {
+				case err == sql.ErrNoRows:
+					outside++
+					// ignore -> filtered by responsibility_areas
+				case err != nil:
 					feedback.Warn(handleError(err).Error())
-				} else {
+				default:
 					features++
 				}
 			default:
@@ -268,6 +270,10 @@
 		feedback.Warn("Unsupported types found: %s", unsupported)
 	}
 
+	if outside > 0 {
+		feedback.Info("Features outside responsibility area: %d", outside)
+	}
+
 	if features == 0 {
 		err := errors.New("No features found")
 		feedback.Error("%v", err)
--- a/pkg/imports/wx.go	Tue Apr 02 10:07:48 2019 +0200
+++ b/pkg/imports/wx.go	Fri Apr 05 10:04:45 2019 +0200
@@ -80,15 +80,12 @@
 const (
 	deleteWaterwayAxisSQL = `
 WITH resp AS (
-  SELECT best_utm(area) AS t,
-         ST_Transform(area::geometry, best_utm(area)) AS a
-  FROM users.responsibility_areas
-  WHERE country = users.current_user_country()
+  SELECT users.current_user_area_utm() AS a
 )
 DELETE FROM waterway.waterway_axis
 WHERE pg_has_role('sys_admin', 'MEMBER')
   OR ST_Covers((SELECT a FROM resp),
-    ST_Transform(wtwaxs::geometry, (SELECT t FROM resp)))
+    ST_Transform(wtwaxs::geometry, (SELECT ST_SRID(a) FROM resp)))
 `
 
 	checkCrossingAxisSQL = `
@@ -100,10 +97,7 @@
 
 	insertWaterwayAxisSQL = `
 WITH resp AS (
-  SELECT best_utm(area) AS t,
-         ST_Transform(area::geometry, best_utm(area)) AS a
-  FROM users.responsibility_areas
-  WHERE country = users.current_user_country()
+  SELECT users.current_user_area_utm() AS a
 )
 INSERT INTO waterway.waterway_axis (wtwaxs, objnam, nobjnam)
 SELECT dmp.geom, $3, $4
@@ -113,7 +107,7 @@
         THEN ST_Node(ST_Transform(new_line,
           best_utm(ST_Transform(new_line, 4326))))
         ELSE ST_Intersection((SELECT a FROM resp),
-          ST_Node(ST_Transform(new_line, (SELECT t FROM resp))))
+          ST_Node(ST_Transform(new_line, (SELECT ST_SRID(a) FROM resp))))
         END,
       2), 4326)) AS dmp
 RETURNING id
@@ -189,6 +183,7 @@
 		unsupported       = stringCounter{}
 		missingProperties int
 		badProperties     int
+		outside           int
 		features          int
 	)
 
@@ -247,11 +242,12 @@
 					epsg,
 					props,
 					nobjnam,
+					&outside,
+					&features,
 					checkCrossingStmt,
 					insertStmt); err != nil {
 					return err
 				}
-				features++
 			case "MultiLineString":
 				var ls []lineSlice
 				if err := json.Unmarshal(*feature.Geometry.Coordinates, &ls); err != nil {
@@ -266,11 +262,12 @@
 						epsg,
 						props,
 						nobjnam,
+						&outside,
+						&features,
 						checkCrossingStmt,
 						insertStmt); err != nil {
 						return err
 					}
-					features++
 				}
 			default:
 				unsupported[feature.Geometry.Type]++
@@ -293,10 +290,12 @@
 		feedback.Warn("Unsupported types found: %s", unsupported)
 	}
 
+	if outside > 0 {
+		feedback.Info("Features outside responsibility area: %d", outside)
+	}
+
 	if features == 0 {
-		err := errors.New("No features found")
-		feedback.Error("%v", err)
-		return nil, err
+		return nil, errors.New("No features found")
 	}
 
 	if err = tx.Commit(); err == nil {
@@ -315,10 +314,11 @@
 	epsg int,
 	props waterwayAxisProperties,
 	nobjnam sql.NullString,
+	outside, features *int,
 	checkCrossingStmt, insertStmt *sql.Stmt,
 ) error {
 	var id int
-	if err := savepoint(func() error {
+	err := savepoint(func() error {
 		err := insertStmt.QueryRowContext(
 			ctx,
 			l.asWKB(),
@@ -327,22 +327,29 @@
 			nobjnam,
 		).Scan(&id)
 		return err
-	}); err != nil {
+	})
+	switch {
+	case err == sql.ErrNoRows:
+		*outside++
+		// ignore -> filtered by responsibility_areas
+		return nil
+	case err != nil:
 		feedback.Warn(handleError(err).Error())
-	}
-
-	var crossing string
-	switch err := checkCrossingStmt.QueryRowContext(
-		ctx,
-		id,
-	).Scan(&crossing); {
-	case err != nil && err != sql.ErrNoRows:
-		return err
-	case err == nil:
-		feedback.Warn(
-			"Linestring %d crosses previously imported linestring near %s. "+
-				"Finding a contiguous axis may not work here",
-			id, crossing)
+	default:
+		*features++
+		var crossing string
+		switch err := checkCrossingStmt.QueryRowContext(
+			ctx,
+			id,
+		).Scan(&crossing); {
+		case err != nil && err != sql.ErrNoRows:
+			return err
+		case err == nil:
+			feedback.Warn(
+				"Linestring %d crosses previously imported linestring near %s. "+
+					"Finding a contiguous axis may not work here",
+				id, crossing)
+		}
 	}
 	return nil
 }
--- a/schema/auth.sql	Tue Apr 02 10:07:48 2019 +0200
+++ b/schema/auth.sql	Fri Apr 05 10:04:45 2019 +0200
@@ -4,7 +4,7 @@
 -- SPDX-License-Identifier: AGPL-3.0-or-later
 -- License-Filename: LICENSES/AGPL-3.0.txt
 
--- Copyright (C) 2018 by via donau
+-- Copyright (C) 2018, 2019 by via donau
 --   – Österreichische Wasserstraßen-Gesellschaft mbH
 -- Software engineering by Intevation GmbH
 
@@ -102,6 +102,8 @@
     LOOP
         EXECUTE format('CREATE POLICY hide_staging ON waterway.%I '
             'FOR SELECT TO waterway_user USING (staging_done)', the_table);
+        EXECUTE format('CREATE POLICY sys_admin ON waterway.%I '
+            'FOR ALL TO sys_admin USING (true)', the_table);
         EXECUTE format('ALTER TABLE waterway.%I ENABLE ROW LEVEL SECURITY',
             the_table);
     END LOOP;
@@ -139,19 +141,15 @@
 
 CREATE POLICY responsibility_area ON waterway.bottlenecks
     FOR ALL TO waterway_admin
-    USING (utm_covers(area));
+    USING (users.utm_covers(area));
 
 CREATE POLICY responsibility_area ON waterway.sounding_results
     FOR ALL TO waterway_admin
-    USING (utm_covers(area));
+    USING (users.utm_covers(area));
 
 CREATE POLICY responsibility_area ON waterway.fairway_dimensions
     FOR ALL TO waterway_admin
-    USING (utm_covers(area));
-
-CREATE POLICY sys_admin ON waterway.stretches
-    FOR ALL TO sys_admin
-    USING (true);
+    USING (users.utm_covers(area));
 
 --
 -- RLS policies for imports and import config
--- a/schema/geo_functions.sql	Tue Apr 02 10:07:48 2019 +0200
+++ b/schema/geo_functions.sql	Fri Apr 05 10:04:45 2019 +0200
@@ -33,20 +33,3 @@
 $$
 LANGUAGE plpgsql
 IMMUTABLE;
-
-CREATE OR REPLACE FUNCTION utm_covers(g geography) RETURNS boolean AS
-$$
-DECLARE
-  user_area geometry;
-  utm integer;
-BEGIN
-    SELECT area::geometry FROM users.responsibility_areas INTO user_area
-    WHERE country = users.current_user_country();
-    SELECT best_utm(user_area) INTO utm;
-    RETURN ST_Covers(
-      ST_Transform(user_area, utm),
-      ST_Transform(g::geometry, utm));
-END;
-$$
-LANGUAGE plpgsql
-STABLE;
--- a/schema/manage_users.sql	Tue Apr 02 10:07:48 2019 +0200
+++ b/schema/manage_users.sql	Fri Apr 05 10:04:45 2019 +0200
@@ -4,7 +4,7 @@
 -- SPDX-License-Identifier: AGPL-3.0-or-later
 -- License-Filename: LICENSES/AGPL-3.0.txt
 
--- Copyright (C) 2018 by via donau
+-- Copyright (C) 2018, 2019 by via donau
 --   – Österreichische Wasserstraßen-Gesellschaft mbH
 -- Software engineering by Intevation GmbH
 
@@ -68,6 +68,31 @@
     STABLE PARALLEL SAFE;
 
 
+CREATE OR REPLACE FUNCTION users.current_user_area_utm()
+    RETURNS geometry
+    AS $$
+        DECLARE utm_area geometry;
+        BEGIN
+            SELECT ST_Transform(area::geometry, best_utm(area))
+                INTO STRICT utm_area
+                FROM users.responsibility_areas
+                WHERE country = users.current_user_country();
+            RETURN utm_area;
+        END;
+    $$
+    LANGUAGE plpgsql
+    STABLE PARALLEL SAFE;
+
+
+CREATE OR REPLACE FUNCTION users.utm_covers(g geography) RETURNS boolean AS
+    $$
+        SELECT ST_Covers(a, ST_Transform(g::geometry, ST_SRID(a)))
+            FROM users.current_user_area_utm() AS a (a)
+    $$
+    LANGUAGE SQL
+    STABLE PARALLEL SAFE;
+
+
 CREATE OR REPLACE FUNCTION internal.create_user() RETURNS trigger
 AS $$
 BEGIN
--- a/schema/manage_users_tests.sql	Tue Apr 02 10:07:48 2019 +0200
+++ b/schema/manage_users_tests.sql	Fri Apr 05 10:04:45 2019 +0200
@@ -4,7 +4,7 @@
 -- SPDX-License-Identifier: AGPL-3.0-or-later
 -- License-Filename: LICENSES/AGPL-3.0.txt
 
--- Copyright (C) 2018 by via donau
+-- Copyright (C) 2018, 2019 by via donau
 --   – Österreichische Wasserstraßen-Gesellschaft mbH
 -- Software engineering by Intevation GmbH
 
@@ -17,10 +17,23 @@
 
 SET search_path TO public, gemma, gemma_waterway, gemma_fairway;
 
+SET SESSION AUTHORIZATION test_user_at;
+--
+-- Utility functions
+--
+SELECT results_eq($$
+    SELECT ST_SRID(users.current_user_area_utm())
+    $$,
+    $$
+    SELECT best_utm(area)
+        FROM users.responsibility_areas
+        WHERE country = users.current_user_country()
+    $$,
+    'Geometry has SRID corresponding to best_utm()');
+
 --
 -- Role listing
 --
-SET SESSION AUTHORIZATION test_user_at;
 SELECT results_eq($$
     SELECT username FROM users.list_users
     $$,
--- a/schema/run_tests.sh	Tue Apr 02 10:07:48 2019 +0200
+++ b/schema/run_tests.sh	Fri Apr 05 10:04:45 2019 +0200
@@ -28,7 +28,7 @@
     -c 'SET client_min_messages TO WARNING' \
     -c "DROP ROLE IF EXISTS $TEST_ROLES" \
     -f tap_tests_data.sql \
-    -c 'SELECT plan(63)' \
+    -c 'SELECT plan(64)' \
     -f gemma_tests.sql \
     -f isrs_tests.sql \
     -f auth_tests.sql \