changeset 3320:1473e9e7cd0c

merge
author Thomas Junk <thomas.junk@intevation.de>
date Mon, 20 May 2019 09:41:32 +0200
parents 9dc7d803e51f (current diff) 753e7c8e8c95 (diff)
children bd1385c00b59
files client/src/components/fairway/AvailableFairwayDepthDialogue.vue
diffstat 18 files changed, 691 insertions(+), 513 deletions(-) [+]
line wrap: on
line diff
--- a/client/src/components/Sidebar.vue	Mon May 20 09:39:45 2019 +0200
+++ b/client/src/components/Sidebar.vue	Mon May 20 09:41:32 2019 +0200
@@ -12,21 +12,17 @@
       </div>
       <div class="menu text-nowrap text-left">
         <router-link to="/">
-          <font-awesome-icon
-            class="fa-fw mr-2"
-            fixed-width
-            icon="map-marked-alt"
-          />
+          <font-awesome-icon class="mr-2" fixed-width icon="map-marked-alt" />
           <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 class="mr-2" fixed-width icon="ship" />
           <span class="fix-trans-space" v-translate>Bottlenecks</span>
         </router-link>
         <div v-if="isWaterwayAdmin">
           <router-link to="/imports/overview" class="position-relative">
             <font-awesome-icon
-              class="fa-fw mr-2"
+              class="mr-2"
               fixed-width
               icon="clipboard-check"
             />
@@ -38,13 +34,19 @@
         </div>
         <div v-if="isSysAdmin">
           <router-link to="/stretches">
-            <font-awesome-icon class="fa-fw mr-2" fixed-width icon="road" />
+            <font-awesome-icon class="mr-2" fixed-width icon="road" />
             <span class="fix-trans-space" v-translate>Define stretches</span>
           </router-link>
         </div>
         <div v-if="isWaterwayAdmin">
+          <router-link to="/sections">
+            <font-awesome-icon class="mr-2" fixed-width icon="road" />
+            <span class="fix-trans-space" v-translate>Define sections</span>
+          </router-link>
+        </div>
+        <div v-if="isWaterwayAdmin">
           <router-link to="/imports/configuration">
-            <font-awesome-icon class="fa-fw mr-2" fixed-width icon="clock" />
+            <font-awesome-icon class="mr-2" fixed-width icon="clock" />
             <translate class="fix-trans-space">Imports</translate>
           </router-link>
           <small class="text-muted pl-3">
@@ -54,29 +56,25 @@
         </div>
         <div v-if="isSysAdmin">
           <router-link to="/usermanagement">
-            <font-awesome-icon
-              class="fa-fw mr-2"
-              fixed-width
-              icon="users-cog"
-            />
+            <font-awesome-icon class="mr-2" fixed-width icon="users-cog" />
             <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 class="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 class="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 class="mr-2" fixed-width icon="power-off" />
           <span class="fix-trans-space" v-translate>Logout</span> {{ user }}
         </a>
       </div>
--- a/client/src/components/fairway/AvailableFairwayDepthDialogue.vue	Mon May 20 09:39:45 2019 +0200
+++ b/client/src/components/fairway/AvailableFairwayDepthDialogue.vue	Mon May 20 09:41:32 2019 +0200
@@ -9,9 +9,7 @@
       <UIBoxHeader icon="chart-line" :title="label" :closeCallback="close" />
       <div class="box-body">
         <UISpinnerOverlay v-if="loading" />
-        <div
-          class="mb-3 d-flex flex-row justify-content-between align-items-center"
-        >
+        <div class="mb-2 d-flex justify-content-between align-items-center">
           <div>
             <input :value="$options.BOTTLENECKS" type="radio" v-model="type" />
             <small class="ml-1 text-muted">
@@ -31,111 +29,120 @@
             </small>
           </div>
         </div>
-        <div class="d-flex flex-column">
-          <select
-            :key="1"
-            v-if="type === $options.BOTTLENECKS"
-            @change="entrySelected"
-            class="form-control form-control-sm font-weight-bold"
-            v-model="selectedEntry"
+        <select
+          v-if="type === $options.BOTTLENECKS"
+          @change="entrySelected"
+          class="form-control font-weight-bold"
+          v-model="selectedEntry"
+        >
+          <option :value="null">{{ placeholder }}</option>
+          <optgroup
+            v-for="(bottlenecksForCountry, cc) in orderedBottlenecks"
+            :key="cc"
+            :label="cc"
           >
-            <option :value="null">{{ empty }}</option>
-            <optgroup
-              v-for="(bottlenecksForCountry, cc) in orderedBottlenecks"
-              :key="cc"
-              :label="cc"
+            <option
+              v-for="bn in bottlenecksForCountry"
+              :key="bn.properties.name"
+              :value="bn"
+            >
+              {{ bn.properties.name }}
+            </option>
+          </optgroup>
+        </select>
+        <select
+          v-else-if="type === $options.STRETCHES"
+          @change="entrySelected"
+          class="form-control font-weight-bold"
+          v-model="selectedEntry"
+        >
+          <option :value="null">{{ placeholder }}</option>
+          <option
+            v-for="stretch in stretches"
+            :value="stretch"
+            :key="stretch.id"
+          >
+            {{ stretch.properties.name }}
+          </option>
+        </select>
+        <select
+          v-else-if="type === $options.SECTIONS"
+          @change="entrySelected"
+          class="form-control font-weight-bold"
+          v-model="selectedEntry"
+        >
+          <option :value="null">{{ placeholder }}</option>
+          <option
+            v-for="section in sections"
+            :value="section"
+            :key="section.id"
+          >
+            {{ section.properties.name }}
+          </option>
+        </select>
+        <div class="d-flex mt-2">
+          <div class="d-flex flex-column w-50 mr-1">
+            <small class="my-auto text-muted">
+              <translate>Type</translate>
+            </small>
+            <select
+              v-model="selectedFrequency"
+              class="form-control form-control-sm"
             >
               <option
-                v-for="bn in bottlenecksForCountry"
-                :key="bn.properties.name"
-                :value="bn"
-              >
-                {{ bn.properties.name }}
-              </option>
-            </optgroup>
-          </select>
-          <select
-            :key="2"
-            v-else
-            @change="entrySelected"
-            class="form-control form-control-sm font-weight-bold"
-            v-model="selectedEntry"
-          >
-            <option :value="null">{{ empty }}</option>
-            <option
-              v-for="(entry, index) in entries"
-              :value="entry"
-              :key="index"
-              >{{ entry.properties.name }}</option
-            >
-          </select>
-        </div>
-        <div class="d-flex flex-column mt-3">
-          <div class="d-flex flex-row w-100">
-            <div class="d-flex flex-column mb-3 w-50 mr-1">
-              <small class="my-auto text-muted"
-                ><translate>Type</translate></small
-              >
-              <select
-                v-model="selectedFrequency"
-                class="form-control form-control-sm"
+                v-for="(option, index) in $options.FREQUENCIES"
+                :value="option"
+                :key="index"
               >
-                <option
-                  v-for="(option, index) in $options.FREQUENCIES"
-                  :value="option"
-                  :key="index"
-                  ><translate>{{ option }}</translate></option
-                >
-              </select>
-            </div>
-            <div class="d-flex flex-column mb-3 w-50 ml-1">
-              <small class="my-auto text-muted"
-                ><translate>LOS</translate></small
-              >
-              <select v-model="los" class="form-control form-control-sm">
-                <option value="1">1</option>
-                <option value="2">2</option>
-                <option value="3">3</option>
-              </select>
-            </div>
+                <translate>{{ option }}</translate>
+              </option>
+            </select>
+          </div>
+          <div class="d-flex flex-column w-50 ml-1">
+            <small class="my-auto text-muted"><translate>LOS</translate></small>
+            <select v-model="los" class="form-control form-control-sm">
+              <option value="1">1</option>
+              <option value="2">2</option>
+              <option value="3">3</option>
+            </select>
           </div>
-          <div class="d-flex flex-row w-100">
-            <div class="d-flex flex-column w-50 mr-1">
-              <small for="from" class="my-auto text-muted"
-                ><translate>Date from</translate></small
-              ><input
-                id="from"
-                v-model="fromDate"
-                class="form-control form-control-sm"
-                type="date"
-              />
-            </div>
-            <div class="d-flex flex-column w-50 ml-1">
-              <small for="to" class="my-auto text-muted"
-                ><translate>Date to</translate></small
-              ><input
-                id="to"
-                v-model="toDate"
-                class="form-control form-control-sm"
-                type="date"
-              />
-            </div>
+        </div>
+        <div class="d-flex mt-2">
+          <div class="d-flex flex-column w-50 mr-1">
+            <small for="from" class="my-auto text-muted">
+              <translate>Date from</translate>
+            </small>
+            <input
+              id="from"
+              v-model="fromDate"
+              class="form-control form-control-sm"
+              type="date"
+            />
+          </div>
+          <div class="d-flex flex-column w-50 ml-1">
+            <small for="to" class="my-auto text-muted">
+              <translate>Date to</translate>
+            </small>
+            <input
+              id="to"
+              v-model="toDate"
+              class="form-control form-control-sm"
+              type="date"
+            />
           </div>
         </div>
         <div class="mt-3">
           <button
-            @click="openFairwaydepth"
-            :disabled="isComplete"
-            class="btn btn-info btn-sm w-100"
+            @click="openFairwaydepthDiagram"
+            :disabled="!isComplete"
+            class="btn btn-info btn-sm d-block w-100"
           >
             <translate>Available fairway depth</translate>
           </button>
-        </div>
-        <div class="mt-3">
           <button
-            @click="openFairwaydepthLNWL"
-            :disabled="isInComplete"
-            class="btn btn-info btn-sm w-100"
+            @click="openFairwaydepthLNWLDiagram"
+            :disabled="!isComplete"
+            class="btn btn-info btn-sm d-block w-100 mt-2"
           >
             <translate>Available fairway depth vs LNWL</translate>
           </button>
@@ -178,106 +185,6 @@
       loading: false
     };
   },
-  methods: {
-    openFairwaydepthLNWL() {
-      this.loading = true;
-      this.$store
-        .dispatch("fairwayavailability/loadAvailableFairwayDepth", {
-          feature: this.selectedFairwayAvailabilityFeature,
-          from: this.from,
-          to: this.to,
-          frequency: this.frequency,
-          LOS: this.los
-        })
-        .then(() => {
-          this.$store.commit(
-            "application/paneSetup",
-            "AVAILABLEFAIRWAYDEPTHLNWL"
-          );
-        })
-        .catch(error => {
-          console.log(error);
-          const { status, data } = error.response;
-          displayError({
-            title: this.$gettext("Backend Error"),
-            message: `${status}: ${data.message || data}`
-          });
-        })
-        .finally(() => {
-          this.loading = false;
-        });
-    },
-    openFairwaydepth() {
-      this.loading = true;
-      this.$store
-        .dispatch("fairwayavailability/loadAvailableFairwayDepth", {
-          feature: this.selectedFairwayAvailabilityFeature,
-          from: this.from,
-          to: this.to,
-          frequency: this.frequency,
-          LOS: this.los
-        })
-        .then(() => {
-          this.$store.commit("application/paneSetup", "AVAILABLEFAIRWAYDEPTH");
-        })
-        .catch(error => {
-          console.log(error);
-          const { status, data } = error.response;
-          displayError({
-            title: this.$gettext("Backend Error"),
-            message: `${status}: ${data.message || data}`
-          });
-        })
-        .finally(() => {
-          this.loading = false;
-        });
-    },
-    close() {
-      this.$store.commit("application/showFairwayDepth", false);
-      this.$store.commit("application/showFairwayDepthLNWL", false);
-    },
-    entrySelected() {
-      if (this.type === this.$options.BOTTLENECKS) {
-        this.openLayersMap()
-          .getLayer("BOTTLENECKS")
-          .setVisible(true);
-        if (this.showProfiles) {
-          this.$store.dispatch(
-            "bottlenecks/setSelectedBottleneck",
-            this.selectedFairwayAvailabilityFeature.properties.name
-          );
-        }
-      }
-      if (this.type === this.$options.STRETCHES) {
-        this.openLayersMap()
-          .getLayer("STRETCHES")
-          .setVisible(true);
-      }
-      if (this.selectedFairwayAvailabilityFeature) {
-        this.$store.dispatch("map/moveToFeauture", {
-          feature: this.selectedFairwayAvailabilityFeature,
-          zoom: 17,
-          preventZoomOut: true
-        });
-      }
-    },
-    setSelectedBottleneck() {
-      const bn = this.bottlenecksList.filter(
-        x => x.properties.name === this.selectedBottleneck
-      )[0];
-      this.$store.commit(
-        "fairwayavailability/setSelectedFairwayAvailability",
-        bn
-      );
-    },
-    setSelectedStretch() {
-      const stretch = this.stretches.find(x => x.id === this.selectedStretchId);
-      this.$store.commit(
-        "fairwayavailability/setSelectedFairwayAvailability",
-        stretch
-      );
-    }
-  },
   computed: {
     ...mapState("application", [
       "showFairwayDepth",
@@ -291,17 +198,22 @@
       "frequency",
       "LOS"
     ]),
-    ...mapState("imports", ["stretches", "selectedStretchId"]),
+    ...mapState("imports", [
+      "stretches",
+      "sections",
+      "selectedStretchId",
+      "selectedSectionId"
+    ]),
     ...mapState("bottlenecks", ["bottlenecksList", "selectedBottleneck"]),
     ...mapGetters("map", ["openLayersMap"]),
     ...mapGetters("bottlenecks", ["orderedBottlenecks"]),
-    isInComplete() {
+    isComplete() {
       return (
-        this.from == null ||
-        this.to == null ||
-        this.frequency == null ||
-        this.los == null ||
-        this.selectedFairwayAvailabilityFeature == null
+        this.from !== null &&
+        this.to !== null &&
+        this.frequency !== null &&
+        this.los !== null &&
+        this.selectedFairwayAvailabilityFeature !== null
       );
     },
     type: {
@@ -355,15 +267,10 @@
         );
       }
     },
-    entries() {
-      if (this.type === this.$options.BOTTLENECKS) return this.bottlenecksList;
-      if (this.type === this.$options.STRETCHES) return this.stretches;
-      return [];
-    },
     label() {
       return this.$gettext("Available fairway depth");
     },
-    empty() {
+    placeholder() {
       if (this.type === this.$options.BOTTLENECKS)
         return this.$gettext("Select bottleneck");
       if (this.type === this.$options.STRETCHES)
@@ -376,34 +283,162 @@
       this.type = this.$options.BOTTLENECKS;
       this.setSelectedBottleneck();
     },
-    type() {
-      if (this.type === this.$options.BOTTLENECKS && this.selectedBottleneck) {
+    selectedStretchId() {
+      this.type = this.$options.STRETCHES;
+      this.setSelectedStretch();
+    },
+    selectedSectionId() {
+      this.type = this.$options.SECTIONS;
+      this.setSelectedSection();
+    },
+    type(type) {
+      if (type === this.$options.BOTTLENECKS && this.selectedBottleneck) {
+        this.openLayersMap()
+          .getLayer("BOTTLENECKS")
+          .setVisible(true);
         this.setSelectedBottleneck();
-      } else if (
-        this.type === this.$options.STRETCHES &&
-        this.selectedStretchId
-      ) {
+      } else if (type === this.$options.STRETCHES && this.selectedStretchId) {
+        this.openLayersMap()
+          .getLayer("STRETCHES")
+          .setVisible(true);
         this.setSelectedStretch();
+      } else if (type === this.$options.SECTIONS && this.selectedSectionId) {
+        this.openLayersMap()
+          .getLayer("SECTIONS")
+          .setVisible(true);
+        this.setSelectedSection();
       } else {
         this.$store.commit(
           "fairwayavailability/setSelectedFairwayAvailability",
           null
         );
       }
-      this.openLayersMap()
-        .getLayer("STRETCHES")
-        .setVisible(true);
     },
     showFairwayDepth() {
       if (this.showFairwayDepth) {
         this.loading = true;
-        this.$store.dispatch("bottlenecks/loadBottlenecksList").then(() => {
-          this.$store.dispatch("imports/loadStretches").then(() => {
-            this.loading = false;
+        Promise.all([
+          this.$store.dispatch("bottlenecks/loadBottlenecksList"),
+          this.$store.dispatch("imports/loadStretches"),
+          this.$store.dispatch("imports/loadSections")
+        ])
+          .then(() => {
             if (this.selectedBottleneck) this.setSelectedBottleneck();
+          })
+          .finally(() => (this.loading = false));
+      }
+    }
+  },
+  methods: {
+    openFairwaydepthLNWLDiagram() {
+      this.loading = true;
+      this.$store
+        .dispatch("fairwayavailability/loadAvailableFairwayDepth", {
+          feature: this.selectedFairwayAvailabilityFeature,
+          from: this.from,
+          to: this.to,
+          frequency: this.frequency,
+          LOS: this.los
+        })
+        .then(() => {
+          this.$store.commit(
+            "application/paneSetup",
+            "AVAILABLEFAIRWAYDEPTHLNWL"
+          );
+        })
+        .catch(error => {
+          console.log(error);
+          const { status, data } = error.response;
+          displayError({
+            title: this.$gettext("Backend Error"),
+            message: `${status}: ${data.message || data}`
           });
+        })
+        .finally(() => {
+          this.loading = false;
+        });
+    },
+    openFairwaydepthDiagram() {
+      this.loading = true;
+      this.$store
+        .dispatch("fairwayavailability/loadAvailableFairwayDepth", {
+          feature: this.selectedFairwayAvailabilityFeature,
+          from: this.from,
+          to: this.to,
+          frequency: this.frequency,
+          LOS: this.los
+        })
+        .then(() => {
+          this.$store.commit("application/paneSetup", "AVAILABLEFAIRWAYDEPTH");
+        })
+        .catch(error => {
+          console.log(error);
+          const { status, data } = error.response;
+          displayError({
+            title: this.$gettext("Backend Error"),
+            message: `${status}: ${data.message || data}`
+          });
+        })
+        .finally(() => {
+          this.loading = false;
+        });
+    },
+    close() {
+      this.$store.commit("application/showFairwayDepth", false);
+      this.$store.commit("application/showFairwayDepthLNWL", false);
+    },
+    entrySelected() {
+      if (this.type === this.$options.BOTTLENECKS) {
+        this.openLayersMap()
+          .getLayer("BOTTLENECKS")
+          .setVisible(true);
+        if (this.showProfiles) {
+          this.$store.dispatch(
+            "bottlenecks/setSelectedBottleneck",
+            this.selectedFairwayAvailabilityFeature.properties.name
+          );
+        }
+      }
+      if (this.type === this.$options.STRETCHES) {
+        this.openLayersMap()
+          .getLayer("STRETCHES")
+          .setVisible(true);
+      }
+      if (this.type === this.$options.SECTIONS) {
+        this.openLayersMap()
+          .getLayer("SECTIONS")
+          .setVisible(true);
+      }
+      if (this.selectedFairwayAvailabilityFeature) {
+        this.$store.dispatch("map/moveToFeauture", {
+          feature: this.selectedFairwayAvailabilityFeature,
+          zoom: 17,
+          preventZoomOut: true
         });
       }
+    },
+    setSelectedBottleneck() {
+      const bn = this.bottlenecksList.filter(
+        x => x.properties.name === this.selectedBottleneck
+      )[0];
+      this.$store.commit(
+        "fairwayavailability/setSelectedFairwayAvailability",
+        bn
+      );
+    },
+    setSelectedStretch() {
+      const stretch = this.stretches.find(x => x.id === this.selectedStretchId);
+      this.$store.commit(
+        "fairwayavailability/setSelectedFairwayAvailability",
+        stretch
+      );
+    },
+    setSelectedSection() {
+      const section = this.sections.find(x => x.id === this.selectedSectionId);
+      this.$store.commit(
+        "fairwayavailability/setSelectedFairwayAvailability",
+        section
+      );
     }
   },
   BOTTLENECKS: "bottlenecks",
--- a/client/src/components/gauge/Waterlevel.vue	Mon May 20 09:39:45 2019 +0200
+++ b/client/src/components/gauge/Waterlevel.vue	Mon May 20 09:41:32 2019 +0200
@@ -55,7 +55,7 @@
     </div>
     <div
       id="pdfContainer"
-      style="position: absolute; z-index: -1; top: 600px;"
+      style="position: absolute; z-index: -1; top: -9999px;"
     ></div>
   </div>
 </template>
@@ -1191,7 +1191,15 @@
     window.addEventListener("resize", debounce(this.drawDiagram), 100);
   },
   mounted() {
-    this.drawDiagram();
+    // Nasty but necessary if we don't want to use the updated hook to re-draw
+    // the diagram because this would re-draw it also for irrelevant reasons.
+    // In this case we need to wait for the child component (DiagramLegend) to
+    // render. According to the docs (https://vuejs.org/v2/api/#mounted) this
+    // should be possible with $nextTick() but it doesn't work because it does
+    // not guarantee that the DOM is not only updated but also re-painted on the
+    // screen.
+    setTimeout(this.drawDiagram, 50);
+
     this.templates[0] = this.defaultTemplate;
     this.form.template = this.templates[0];
     this.templateData = this.form.template;
--- a/client/src/components/identify/formatter.js	Mon May 20 09:39:45 2019 +0200
+++ b/client/src/components/identify/formatter.js	Mon May 20 09:41:32 2019 +0200
@@ -50,6 +50,9 @@
   stretches_geoserver: {
     label: "Stretch"
   },
+  sections_geoserver: {
+    label: "Section"
+  },
   gauges_geoserver: {
     label: "Gauge"
   }
--- a/client/src/components/layers/Layers.vue	Mon May 20 09:39:45 2019 +0200
+++ b/client/src/components/layers/Layers.vue	Mon May 20 09:41:32 2019 +0200
@@ -22,6 +22,7 @@
         <Layerselect layerId="INLANDECDIS" />
         <Layerselect layerId="WATERWAYAREA" />
         <Layerselect layerId="STRETCHES" />
+        <Layerselect layerId="SECTIONS" />
         <Layerselect layerId="FAIRWAYDIMENSIONSLOS3" />
         <Layerselect layerId="FAIRWAYDIMENSIONSLOS2" />
         <Layerselect layerId="FAIRWAYDIMENSIONSLOS1" />
--- a/client/src/components/map/Map.vue	Mon May 20 09:39:45 2019 +0200
+++ b/client/src/components/map/Map.vue	Mon May 20 09:41:32 2019 +0200
@@ -61,7 +61,7 @@
     ...mapState("bottlenecks", ["selectedSurvey"]),
     ...mapState("fairwayprofile", ["additionalSurvey"]),
     ...mapState("application", ["paneSetup", "paneRotate"]),
-    ...mapState("imports", ["selectedStretchId"]),
+    ...mapState("imports", ["selectedStretchId", "selectedSectionId"]),
     layers() {
       return layers();
     },
@@ -129,6 +129,18 @@
             f.set("highlighted", true);
           }
         });
+    },
+    selectedSectionId(id) {
+      this.layers
+        .get("SECTIONS")
+        .getSource()
+        .getFeatures()
+        .forEach(f => {
+          f.set("highlighted", false);
+          if (id === f.getId()) {
+            f.set("highlighted", true);
+          }
+        });
     }
   },
   methods: {
--- a/client/src/components/map/MapPopup.vue	Mon May 20 09:39:45 2019 +0200
+++ b/client/src/components/map/MapPopup.vue	Mon May 20 09:41:32 2019 +0200
@@ -81,6 +81,7 @@
         <button
           class="btn btn-xs btn-info"
           v-tooltip="fairwayAvailabilityLabel"
+          @click="openFairwayAvailabilityForSection(section)"
         >
           <font-awesome-icon icon="chart-line" fixed-width />
         </button>
@@ -229,6 +230,15 @@
         zoom: 17
       });
       this.openFairwayAvailability();
+    },
+    openFairwayAvailabilityForSection(section) {
+      this.$store.commit("fairwayavailability/type", "sections");
+      this.$store.commit("imports/selectedSectionId", section.getId());
+      this.$store.dispatch("map/moveToFeauture", {
+        feature: section,
+        zoom: 17
+      });
+      this.openFairwayAvailability();
     }
   },
   mounted() {
--- a/client/src/components/map/layers.js	Mon May 20 09:39:45 2019 +0200
+++ b/client/src/components/map/layers.js	Mon May 20 09:41:32 2019 +0200
@@ -232,6 +232,32 @@
         });
       })(),
       (function() {
+        const source = new VectorSource({ strategy: bboxStrategy });
+        source.setLoader(
+          buildVectorLoader(
+            {
+              featureTypes: ["sections_geoserver"],
+              geometryName: "area"
+            },
+            source,
+            true,
+            (f, store) => {
+              if (f.getId() === store.state.imports.selectedSectionId) {
+                f.set("highlighted", true);
+              }
+              return f;
+            }
+          )
+        );
+        return new VectorLayer({
+          id: "SECTIONS",
+          label: "Sections",
+          visible: false,
+          style: styles.sections,
+          source
+        });
+      })(),
+      (function() {
         const source = new VectorSource();
         source.setLoader(
           buildVectorLoader(
--- a/client/src/components/map/styles.js	Mon May 20 09:39:45 2019 +0200
+++ b/client/src/components/map/styles.js	Mon May 20 09:41:32 2019 +0200
@@ -61,6 +61,24 @@
       color: "rgba(250, 240, 0, .7)"
     })
   }),
+  orange1: new Style({
+    stroke: new Stroke({
+      color: "rgba(255, 150, 10, .8)",
+      width: 2
+    }),
+    fill: new Fill({
+      color: "rgba(255, 150, 0, .3)"
+    })
+  }),
+  orange2: new Style({
+    stroke: new Stroke({
+      color: "rgba(255, 166, 10, .9)",
+      width: 5
+    }),
+    fill: new Fill({
+      color: "rgba(255, 166, 0, .7)"
+    })
+  }),
   red1: new Style({
     stroke: new Stroke({
       color: "rgba(255, 0, 0, 1)",
@@ -117,6 +135,13 @@
     }
     return style;
   },
+  sections(feature) {
+    let style = styles.orange1;
+    if (feature.get("highlighted")) {
+      style = styles.orange2;
+    }
+    return style;
+  },
   fwd1() {
     return [styles.blue1, styles.textFW1];
   },
--- a/client/src/store/map.js	Mon May 20 09:39:45 2019 +0200
+++ b/client/src/store/map.js	Mon May 20 09:41:32 2019 +0200
@@ -375,14 +375,33 @@
                 root: true
               });
             } else {
-              commit("application/showFairwayDepth", true, { root: true });
-              commit("fairwayavailability/type", "stretches", { root: true });
               commit("imports/selectedStretchId", stretches[0].getId(), {
                 root: true
               });
+              commit("fairwayavailability/type", "stretches", { root: true });
+              commit("application/showFairwayDepth", true, { root: true });
               dispatch("moveToFeauture", { feature: stretches[0], zoom: 17 });
             }
           }
+          if (
+            sections.length === 1 &&
+            !stretches.length &&
+            !bottlenecks.length &&
+            !gauges.length
+          ) {
+            if (rootState.imports.selectedSectionId === sections[0].getId()) {
+              commit("imports/selectedSectionId", null, {
+                root: true
+              });
+            } else {
+              commit("imports/selectedSectionId", sections[0].getId(), {
+                root: true
+              });
+              commit("fairwayavailability/type", "sections", { root: true });
+              commit("application/showFairwayDepth", true, { root: true });
+              dispatch("moveToFeauture", { feature: sections[0], zoom: 17 });
+            }
+          }
         }
 
         // DEBUG output and example how to remove the GeometryName
--- a/pkg/controllers/bottlenecks.go	Mon May 20 09:39:45 2019 +0200
+++ b/pkg/controllers/bottlenecks.go	Mon May 20 09:41:32 2019 +0200
@@ -177,7 +177,8 @@
 			// log.Println("complete outside")
 			continue pairs
 
-		case p1.when.After(from) && p2.when.Before(to):
+		case (p1.when.Before(from) || p1.when.Equal(from)) && (to.Before(p2.when) || to.Equal(p2.when)):
+			//case !p1.when.After(from) && !p2.when.Before(to):
 			// (from-to) is complete inside segment.
 			// invalid += p1.when.Sub(from)
 			// invalid += to.Sub(p2.when)
@@ -186,7 +187,7 @@
 			f, _ := v(from)
 			t, _ := v(to)
 			classify(common.InterpolateTimeByValue(from, f, to, t))
-			start, end = p1.when, p2.when
+			start, end = from, to
 
 		case p1.when.After(from):
 			// from is inside segment
@@ -218,8 +219,10 @@
 				p2.when, access(p2),
 			))
 			start, end = p1.when, p2.when
+
 		default:
-			log.Println("warn: unexpected case. That should not happen.")
+			log.Printf("warn: unexpected case. That should not happen. %v - %v, %v - %v\n",
+				p1.when, p2.when, from, to)
 			continue pairs
 		}
 
--- a/pkg/geoserver/boot.go	Mon May 20 09:39:45 2019 +0200
+++ b/pkg/geoserver/boot.go	Mon May 20 09:41:32 2019 +0200
@@ -264,11 +264,20 @@
 		hasFeature = func(string) bool { return false }
 	}
 
+	var already []string
+
+	defer func() {
+		if len(already) > 0 {
+			log.Printf("info: already having featuretypes: %s\n",
+				strings.Join(already, ", "))
+		}
+	}()
+
 	for i := range tables {
 		table := tables[i].Name
 
 		if hasFeature(table) {
-			log.Printf("info: featuretype %s already exists.\n", table)
+			already = append(already, table)
 			continue
 		}
 
@@ -529,10 +538,19 @@
 			models.IntWMS,
 			models.IntWithStyle))
 
+	var already []string
+
+	defer func() {
+		if len(already) > 0 {
+			log.Printf("info: already having styles: %s\n",
+				strings.Join(already, ", "))
+		}
+	}()
+
 	for i := range entries {
 		entry := &entries[i]
 		if stls.hasStyle(entry.Name) {
-			log.Printf("warn: already has style for %s\n", entry.Name)
+			already = append(already, entry.Name)
 			continue
 		}
 		if err := updateStyle(entry, true); err != nil {
--- a/pkg/imports/errors.go	Mon May 20 09:39:45 2019 +0200
+++ b/pkg/imports/errors.go	Mon May 20 09:41:32 2019 +0200
@@ -29,6 +29,7 @@
 
 // Handle PostgreSQL error codes
 const (
+	notNullViolation    = "23502"
 	foreignKeyViolation = "23503"
 	noDataFound         = "P0002"
 )
@@ -37,6 +38,21 @@
 
 func (err dbError) Error() string {
 	switch err.Code {
+	case notNullViolation:
+		switch err.SchemaName {
+		case "waterway":
+			switch err.TableName {
+			case "gauges":
+				switch err.ColumnName {
+				case "objname":
+					return "Missing objname"
+				case "geom":
+					return "Missing lat/lon"
+				case "zero_point":
+					return "Missing zeropoint"
+				}
+			}
+		}
 	case foreignKeyViolation:
 		switch err.SchemaName {
 		case "waterway":
--- a/pkg/imports/sec.go	Mon May 20 09:39:45 2019 +0200
+++ b/pkg/imports/sec.go	Mon May 20 09:41:32 2019 +0200
@@ -101,7 +101,7 @@
   SELECT ISRSrange_axis((SELECT r FROM r), $16::double precision) AS axs)
 INSERT INTO waterway.sections (
   name,
-  stretch,
+  section,
   area,
   objnam,
   nobjnam,
@@ -121,7 +121,7 @@
 RETURNING id`
 )
 
-// StageDone moves the imported stretch out of the staging area.
+// StageDone moves the imported section out of the staging area.
 func (secJobCreator) StageDone(
 	ctx context.Context,
 	tx *sql.Tx,
@@ -134,10 +134,10 @@
 	return err
 }
 
-// CleanUp of a stretch import is a NOP.
+// CleanUp of a section import is a NOP.
 func (*Section) CleanUp() error { return nil }
 
-// Do executes the actual stretch import.
+// Do executes the actual section import.
 func (sec *Section) Do(
 	ctx context.Context,
 	importID int64,
--- a/pkg/imports/wg.go	Mon May 20 09:39:45 2019 +0200
+++ b/pkg/imports/wg.go	Mon May 20 09:41:32 2019 +0200
@@ -65,6 +65,12 @@
 func (*WaterwayGauge) CleanUp() error { return nil }
 
 const (
+	eraseObsoleteGaugesSQL = `
+UPDATE waterway.gauges SET erased = true
+WHERE NOT erased AND isrs_astext(location) <> ALL($1)
+RETURNING isrs_astext(location)
+`
+
 	eraseGaugeSQL = `
 UPDATE waterway.gauges SET
   erased = true,
@@ -172,58 +178,6 @@
 		return nil, err
 	}
 
-	var ignored int
-
-	type idxCode struct {
-		jdx  int
-		idx  int
-		code *models.Isrs
-	}
-
-	var gauges []idxCode
-
-	for j, data := range responseData {
-		for i, dr := range data.RisdataReturn {
-			if dr.RisidxCode == nil {
-				ignored++
-				continue
-			}
-			code, err := models.IsrsFromString(string(*dr.RisidxCode))
-			if err != nil {
-				feedback.Warn("invalid ISRS code %v", err)
-				ignored++
-				continue
-			}
-
-			if dr.Objname.Loc == nil {
-				feedback.Warn("missing objname: %s", code)
-				ignored++
-				continue
-			}
-
-			if dr.Lat == nil || dr.Lon == nil {
-				feedback.Warn("missing lat/lon: %s", code)
-				ignored++
-				continue
-			}
-
-			if dr.Zeropoint == nil {
-				feedback.Warn("missing zeropoint: %s", code)
-				ignored++
-				continue
-			}
-
-			gauges = append(gauges, idxCode{jdx: j, idx: i, code: code})
-		}
-	}
-	feedback.Info("Ignored gauges: %d", ignored)
-	feedback.Info("Further process %d gauges", len(gauges))
-
-	if len(gauges) == 0 {
-		return nil, UnchangedError("Nothing to do")
-	}
-
-	// insert/update the gauges
 	var eraseGaugeStmt, insertStmt, updateStmt,
 		deleteReferenceWaterLevelsStmt,
 		isNtSDepthRefStmt, insertWaterLevelStmt *sql.Stmt
@@ -245,257 +199,291 @@
 		defer (*x.stmt).Close()
 	}
 
+	var gauges []string
 	var unchanged int
 
-	for i := range gauges {
-		ic := &gauges[i]
-		dr := responseData[ic.jdx].RisdataReturn[ic.idx]
-
-		feedback.Info("Processing %s", ic.code)
+	for _, data := range responseData {
+		for _, dr := range data.RisdataReturn {
 
-		var from, to sql.NullInt64
-
-		if dr.Applicabilityfromkm != nil {
-			from = sql.NullInt64{
-				Int64: int64(*dr.Applicabilityfromkm),
-				Valid: true,
+			isrs := string(*dr.RisidxCode)
+			code, err := models.IsrsFromString(isrs)
+			if err != nil {
+				feedback.Warn("Invalid ISRS code '%s': %v", isrs, err)
+				continue
 			}
-		}
-		if dr.Applicabilitytokm != nil {
-			to = sql.NullInt64{
-				Int64: int64(*dr.Applicabilitytokm),
-				Valid: true,
-			}
-		}
+			gauges = append(gauges, isrs)
+			feedback.Info("Processing %s", code)
+
+			var from, to sql.NullInt64
 
-		var tfrom, tto, dateInfo pgtype.Timestamptz
-
-		if dr.Startdate != nil {
-			tfrom = pgtype.Timestamptz{
-				Time:   time.Time(*dr.Startdate),
-				Status: pgtype.Present,
-			}
-		} else {
-			tfrom = pgtype.Timestamptz{
-				Status: pgtype.Null,
+			if dr.Applicabilityfromkm != nil {
+				from = sql.NullInt64{
+					Int64: int64(*dr.Applicabilityfromkm),
+					Valid: true,
+				}
 			}
-		}
+			if dr.Applicabilitytokm != nil {
+				to = sql.NullInt64{
+					Int64: int64(*dr.Applicabilitytokm),
+					Valid: true,
+				}
+			}
 
-		if dr.Enddate != nil {
-			tto = pgtype.Timestamptz{
-				Time:   time.Time(*dr.Enddate),
-				Status: pgtype.Present,
-			}
-		} else {
-			tto = pgtype.Timestamptz{
-				Status: pgtype.Null,
-			}
-		}
+			var tfrom, tto, dateInfo pgtype.Timestamptz
 
-		validity := pgtype.Tstzrange{
-			Lower:     tfrom,
-			Upper:     tto,
-			LowerType: pgtype.Inclusive,
-			UpperType: pgtype.Exclusive,
-			Status:    pgtype.Present,
-		}
+			if dr.Startdate != nil {
+				tfrom = pgtype.Timestamptz{
+					Time:   time.Time(*dr.Startdate),
+					Status: pgtype.Present,
+				}
+			} else {
+				tfrom = pgtype.Timestamptz{
+					Status: pgtype.Null,
+				}
+			}
 
-		if dr.Infodate != nil {
-			dateInfo = pgtype.Timestamptz{
-				Time:   time.Time(*dr.Infodate),
-				Status: pgtype.Present,
+			if dr.Enddate != nil {
+				tto = pgtype.Timestamptz{
+					Time:   time.Time(*dr.Enddate),
+					Status: pgtype.Present,
+				}
+			} else {
+				tto = pgtype.Timestamptz{
+					Status: pgtype.Null,
+				}
 			}
-		} else {
-			dateInfo = pgtype.Timestamptz{
-				Status: pgtype.Null,
-			}
-		}
 
-		var geodref sql.NullString
-		if dr.Geodref != nil {
-			geodref = sql.NullString{
-				String: string(*dr.Geodref),
-				Valid:  true,
-			}
-		}
-
-		var source sql.NullString
-		if dr.Source != nil {
-			source = sql.NullString{
-				String: string(*dr.Source),
-				Valid:  true,
+			validity := pgtype.Tstzrange{
+				Lower:     tfrom,
+				Upper:     tto,
+				LowerType: pgtype.Inclusive,
+				UpperType: pgtype.Exclusive,
+				Status:    pgtype.Present,
 			}
-		}
-
-		tx, err := conn.BeginTx(ctx, nil)
-		if err != nil {
-			return nil, err
-		}
-		defer tx.Rollback()
 
-		// Mark old entries of gauge as erased, if applicable
-		if _, err := tx.StmtContext(ctx, eraseGaugeStmt).ExecContext(ctx,
-			ic.code.String(),
-			validity,
-		); err != nil {
-			feedback.Warn(handleError(err).Error())
-			if err2 := tx.Rollback(); err2 != nil {
-				return nil, err2
+			if dr.Infodate != nil {
+				dateInfo = pgtype.Timestamptz{
+					Time:   time.Time(*dr.Infodate),
+					Status: pgtype.Present,
+				}
+			} else {
+				dateInfo = pgtype.Timestamptz{
+					Status: pgtype.Null,
+				}
+			}
+
+			var geodref sql.NullString
+			if dr.Geodref != nil {
+				geodref = sql.NullString{
+					String: string(*dr.Geodref),
+					Valid:  true,
+				}
 			}
-			unchanged++
-			continue
-		}
+
+			var source sql.NullString
+			if dr.Source != nil {
+				source = sql.NullString{
+					String: string(*dr.Source),
+					Valid:  true,
+				}
+			}
+
+			tx, err := conn.BeginTx(ctx, nil)
+			if err != nil {
+				return nil, err
+			}
+			defer tx.Rollback()
 
-		// Try to insert gauge entry
-		var dummy int
-		err = tx.StmtContext(ctx, insertStmt).QueryRowContext(ctx,
-			ic.code.CountryCode,
-			ic.code.LoCode,
-			ic.code.FairwaySection,
-			ic.code.Orc,
-			ic.code.Hectometre,
-			string(*dr.Objname.Loc),
-			float64(*dr.Lon), float64(*dr.Lat),
-			from,
-			to,
-			&validity,
-			float64(*dr.Zeropoint),
-			geodref,
-			&dateInfo,
-			source,
-			time.Time(*dr.Lastupdate),
-		).Scan(&dummy)
-		switch {
-		case err == sql.ErrNoRows:
-			// Assume constraint conflict, try to update
-			err2 := tx.StmtContext(ctx, updateStmt).QueryRowContext(ctx,
-				ic.code.CountryCode,
-				ic.code.LoCode,
-				ic.code.FairwaySection,
-				ic.code.Orc,
-				ic.code.Hectometre,
-				string(*dr.Objname.Loc),
-				float64(*dr.Lon), float64(*dr.Lat),
+			// Mark old entries of gauge as erased, if applicable
+			if _, err := tx.StmtContext(ctx, eraseGaugeStmt).ExecContext(ctx,
+				code.String(),
+				validity,
+			); err != nil {
+				feedback.Warn(handleError(err).Error())
+				if err2 := tx.Rollback(); err2 != nil {
+					return nil, err2
+				}
+				unchanged++
+				continue
+			}
+
+			// Try to insert gauge entry
+			var dummy int
+			err = tx.StmtContext(ctx, insertStmt).QueryRowContext(ctx,
+				code.CountryCode,
+				code.LoCode,
+				code.FairwaySection,
+				code.Orc,
+				code.Hectometre,
+				dr.Objname.Loc,
+				dr.Lon, dr.Lat,
 				from,
 				to,
-				float64(*dr.Zeropoint),
+				&validity,
+				dr.Zeropoint,
 				geodref,
 				&dateInfo,
 				source,
 				time.Time(*dr.Lastupdate),
 			).Scan(&dummy)
 			switch {
-			case err2 == sql.ErrNoRows:
-				feedback.Info("unchanged")
-				if err3 := tx.Rollback(); err3 != nil {
-					return nil, err3
+			case err == sql.ErrNoRows:
+				// Assume constraint conflict, try to update
+				err2 := tx.StmtContext(ctx, updateStmt).QueryRowContext(ctx,
+					code.CountryCode,
+					code.LoCode,
+					code.FairwaySection,
+					code.Orc,
+					code.Hectometre,
+					dr.Objname.Loc,
+					dr.Lon, dr.Lat,
+					from,
+					to,
+					dr.Zeropoint,
+					geodref,
+					&dateInfo,
+					source,
+					time.Time(*dr.Lastupdate),
+				).Scan(&dummy)
+				switch {
+				case err2 == sql.ErrNoRows:
+					feedback.Info("unchanged")
+					if err3 := tx.Rollback(); err3 != nil {
+						return nil, err3
+					}
+					unchanged++
+					continue
+				case err2 != nil:
+					feedback.Warn(handleError(err2).Error())
+					if err3 := tx.Rollback(); err3 != nil {
+						return nil, err3
+					}
+					unchanged++
+					continue
+				default:
+					feedback.Info("update")
 				}
-				unchanged++
-				continue
-			case err2 != nil:
-				feedback.Warn(handleError(err2).Error())
-				if err3 := tx.Rollback(); err3 != nil {
-					return nil, err3
+
+				// Remove obsolete reference water levels
+				var currLevels pgtype.VarcharArray
+				currLevels.Set([]string{
+					string(*dr.Reflevel1code),
+					string(*dr.Reflevel2code),
+					string(*dr.Reflevel3code),
+				})
+				rwls, err := tx.StmtContext(ctx,
+					deleteReferenceWaterLevelsStmt).QueryContext(ctx,
+					code.String(),
+					&validity,
+					&currLevels,
+				)
+				if err != nil {
+					return nil, err
+				}
+				defer rwls.Close()
+				for rwls.Next() {
+					var delRef string
+					if err := rwls.Scan(&delRef); err != nil {
+						return nil, err
+					}
+					feedback.Warn("Removed reference water level %s from %s",
+						delRef, code)
+				}
+				if err := rwls.Err(); err != nil {
+					return nil, err
+				}
+			case err != nil:
+				feedback.Warn(handleError(err).Error())
+				if err2 := tx.Rollback(); err2 != nil {
+					return nil, err2
 				}
 				unchanged++
 				continue
 			default:
-				feedback.Info("update")
+				feedback.Info("insert new version")
 			}
 
-			// Remove obsolete reference water levels
-			var currLevels pgtype.VarcharArray
-			currLevels.Set([]string{
-				string(*dr.Reflevel1code),
-				string(*dr.Reflevel2code),
-				string(*dr.Reflevel3code),
-			})
-			rwls, err2 := tx.StmtContext(ctx,
-				deleteReferenceWaterLevelsStmt).QueryContext(ctx,
-				ic.code.String(),
-				&validity,
-				&currLevels,
-			)
-			if err2 != nil {
-				return nil, err2
-			}
-			defer rwls.Close()
-			for rwls.Next() {
-				var delRef string
-				if err2 = rwls.Scan(&delRef); err2 != nil {
-					return nil, err2
+			// "Upsert" reference water levels
+			for _, wl := range []struct {
+				level **erdms.RisreflevelcodeType
+				value **erdms.RisreflevelvalueType
+			}{
+				{&dr.Reflevel1code, &dr.Reflevel1value},
+				{&dr.Reflevel2code, &dr.Reflevel2value},
+				{&dr.Reflevel3code, &dr.Reflevel3value},
+			} {
+				if *wl.level == nil || *wl.value == nil {
+					continue
+				}
+
+				var isNtSDepthRef bool
+				if err := tx.StmtContext(
+					ctx, isNtSDepthRefStmt).QueryRowContext(ctx,
+					string(**wl.level),
+				).Scan(
+					&isNtSDepthRef,
+				); err != nil {
+					return nil, err
 				}
-				feedback.Warn("Removed reference water level %s from %s",
-					delRef, ic.code)
-			}
-		case err != nil:
-			feedback.Warn(handleError(err).Error())
-			if err2 := tx.Rollback(); err2 != nil {
-				return nil, err2
-			}
-			unchanged++
-			continue
-		default:
-			feedback.Info("insert new version")
-		}
+				if !isNtSDepthRef {
+					feedback.Warn(
+						"Reference level code '%s' is not in line "+
+							"with the NtS reference_code table",
+						string(**wl.level))
+				}
 
-		// "Upsert" reference water levels
-		for _, wl := range []struct {
-			level **erdms.RisreflevelcodeType
-			value **erdms.RisreflevelvalueType
-		}{
-			{&dr.Reflevel1code, &dr.Reflevel1value},
-			{&dr.Reflevel2code, &dr.Reflevel2value},
-			{&dr.Reflevel3code, &dr.Reflevel3value},
-		} {
-			if *wl.level == nil || *wl.value == nil {
-				continue
+				if _, err := tx.StmtContext(
+					ctx, insertWaterLevelStmt).ExecContext(ctx,
+					code.CountryCode,
+					code.LoCode,
+					code.FairwaySection,
+					code.Orc,
+					code.Hectometre,
+					&validity,
+					string(**wl.level),
+					int64(**wl.value),
+				); err != nil {
+					feedback.Warn(handleError(err).Error())
+					tx.Rollback()
+					continue
+				}
 			}
 
-			var isNtSDepthRef bool
-			if err := tx.StmtContext(ctx, isNtSDepthRefStmt).QueryRowContext(
-				ctx,
-				string(**wl.level),
-			).Scan(
-				&isNtSDepthRef,
-			); err != nil {
+			if err = tx.Commit(); err != nil {
 				return nil, err
 			}
-			if !isNtSDepthRef {
-				feedback.Warn(
-					"Reference level code '%s' is not in line "+
-						"with the NtS reference_code table",
-					string(**wl.level))
-			}
+		}
+	}
+
+	if len(gauges) == 0 {
+		return nil, UnchangedError("No gauges returned from ERDMS")
+	}
 
-			if _, err := tx.StmtContext(ctx, insertWaterLevelStmt).ExecContext(
-				ctx,
-				ic.code.CountryCode,
-				ic.code.LoCode,
-				ic.code.FairwaySection,
-				ic.code.Orc,
-				ic.code.Hectometre,
-				&validity,
-				string(**wl.level),
-				int64(**wl.value),
-			); err != nil {
-				feedback.Warn(handleError(err).Error())
-				tx.Rollback()
-				continue
-			}
-		}
-
-		if err = tx.Commit(); err != nil {
+	var pgGauges pgtype.VarcharArray
+	pgGauges.Set(gauges)
+	obsGauges, err := conn.QueryContext(ctx, eraseObsoleteGaugesSQL, &pgGauges)
+	if err != nil {
+		return nil, err
+	}
+	defer obsGauges.Close()
+	for obsGauges.Next() {
+		var isrs string
+		if err := obsGauges.Scan(&isrs); err != nil {
 			return nil, err
 		}
+		feedback.Info("Erased %s", isrs)
+		unchanged--
+	}
+	if err := obsGauges.Err(); err != nil {
+		return nil, err
+	}
+
+	if unchanged == len(gauges) {
+		return nil, UnchangedError("All gauges unchanged")
 	}
 
 	feedback.Info("Importing gauges took %s",
 		time.Since(start))
 
-	if unchanged == len(gauges) {
-		return nil, UnchangedError("All gauges unchanged")
-	}
-
 	return nil, err
 }
--- a/schema/demo-data/published_services.sql	Mon May 20 09:39:45 2019 +0200
+++ b/schema/demo-data/published_services.sql	Mon May 20 09:41:32 2019 +0200
@@ -12,6 +12,7 @@
 --  * Tom Gottfried <tom@intevation.de>
 
 INSERT INTO sys_admin.published_services (name) VALUES
+    ('waterway.sections_geoserver'),
     ('waterway.stretches_geoserver'),
     ('waterway.fairway_dimensions'),
     ('waterway.gauges_geoserver'),
--- a/schema/gemma.sql	Mon May 20 09:39:45 2019 +0200
+++ b/schema/gemma.sql	Mon May 20 09:41:32 2019 +0200
@@ -424,7 +424,7 @@
     CREATE TABLE sections (
         id int PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
         name varchar NOT NULL,
-        stretch isrsrange NOT NULL,
+        section isrsrange NOT NULL,
         area geography(MULTIPOLYGON, 4326) NOT NULL
             CHECK(ST_IsValid(CAST(area AS geometry))),
         objnam varchar NOT NULL,
@@ -616,7 +616,8 @@
   ('waterway', 'distance_marks_geoserver', 'location_code'),
   ('waterway', 'distance_marks_ashore_geoserver', 'id'),
   ('waterway', 'bottlenecks_geoserver', 'id'),
-  ('waterway', 'stretches_geoserver', 'id');
+  ('waterway', 'stretches_geoserver', 'id'),
+  ('waterway', 'sections_geoserver', 'id');
 
 --
 -- Import queue and respective logging
--- a/schema/geoserver_views.sql	Mon May 20 09:39:45 2019 +0200
+++ b/schema/geoserver_views.sql	Mon May 20 09:41:32 2019 +0200
@@ -100,6 +100,20 @@
         staging_done
     FROM waterway.stretches;
 
+CREATE OR REPLACE VIEW waterway.sections_geoserver AS
+    SELECT
+        id,
+        name,
+        (section).lower::varchar as lower,
+        (section).upper::varchar as upper,
+        area::Geometry(MULTIPOLYGON, 4326),
+        objnam,
+        nobjnam,
+        date_info,
+        source_organization,
+        staging_done
+    FROM waterway.sections;
+
 CREATE OR REPLACE VIEW waterway.sounding_results_contour_lines_geoserver AS
     SELECT bottleneck_id,
         date_info,