view client/src/components/Bottlenecks.vue @ 2624:9dbaf69c7a66

Improve geoserver config to better calculate bounding boxes * Disable the use of estimated extents for the postgis storage configuration for geoserver, which is set via the gemma middleware. This way we are able to get better bounding boxes for many layers where the postgis function `ST_EstimatedExtent()` would be far off.
author Bernhard Reiter <bernhard@intevation.de>
date Wed, 13 Mar 2019 16:18:39 +0100
parents 468c8dc796cf
children add2d47c2567
line wrap: on
line source

<template>
  <div>
    <UIBoxHeader
      icon="ship"
      title="Bottlenecks"
      :closeCallback="$parent.close"
    />
    <UITableHeader
      :columns="[
        { id: 'name', title: 'Name', class: 'col-4' },
        {
          id: 'latestMeasurement',
          title: 'Latest Measurement',
          class: 'col-3'
        },
        { id: 'chainage', title: 'Chainage', class: 'col-3' }
      ]"
      @sortingChanged="sortBy"
    />
    <UITableBody
      :data="filteredAndSortedBottlenecks()"
      :maxHeight="(showSplitscreen ? 18 : 35) + 'rem'"
      :active="openBottleneck"
      v-slot="{ item: bottleneck }"
    >
      <div class="col-4 py-2 text-left">
        <a href="#" @click="selectBottleneck(bottleneck)">{{
          bottleneck.properties.name
        }}</a>
      </div>
      <div class="col-3 py-2">
        {{ bottleneck.properties.current | surveyDate }}
      </div>
      <div class="col-3 py-2">
        {{
          displayCurrentChainage(
            bottleneck.properties.from,
            bottleneck.properties.to
          )
        }}
      </div>
      <div class="col-2 pr-0 text-right d-flex flex-column">
        <a
          class="text-info mt-auto mb-auto mr-2"
          @click="loadSurveys(bottleneck)"
          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>
      <div
        :class="[
          'col-12 p-0',
          'surveys',
          { open: openBottleneck === bottleneck }
        ]"
      >
        <a
          href="#"
          class="d-inline-block px-3 py-2"
          v-for="(survey, index) in openBottleneckSurveys"
          :key="index"
          @click="selectSurvey(survey, bottleneck)"
        >
          {{ survey.date_info | surveyDate }}
        </a>
      </div>
    </UITableBody>
  </div>
</template>

<style lang="sass" scoped>
.table-body
  .row
    > div:not(:last-child)
      transition: background-color 0.3s, color 0.3s
    &.active
      > div:not(:last-child)
        background-color: $color-info
        color: #fff
        a
          color: #fff !important
      .surveys
        border-bottom: solid 1px $color-info
    .surveys
      overflow: hidden
      max-height: 0
      &.open
        overflow-y: auto
        max-height: 5rem
</style>

<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>
 */
import { mapState } from "vuex";
import { HTTP } from "@/lib/http";
import { displayError } from "@/lib/errors.js";

export default {
  name: "bottlenecks",
  data() {
    return {
      sortColumn: "name",
      sortDirection: "ASC",
      openBottleneck: null,
      openBottleneckSurveys: null,
      loading: null
    };
  },
  computed: {
    ...mapState("application", [
      "searchQuery",
      "showSearchbarLastState",
      "showSplitscreen"
    ]),
    ...mapState("bottlenecks", ["bottlenecksList"]),
    sortIcon() {
      return this.sortDirection === "ASC"
        ? "sort-amount-down"
        : "sort-amount-up";
    }
  },
  methods: {
    filteredAndSortedBottlenecks() {
      return this.bottlenecksList
        .filter(bn => {
          return bn.properties.name
            .toLowerCase()
            .includes(this.searchQuery.toLowerCase());
        })
        .sort((bnA, bnB) => {
          switch (this.sortColumn) {
            case "name":
              if (
                bnA.properties.name.toLowerCase() <
                bnB.properties.name.toLowerCase()
              )
                return this.sortDirection === "ASC" ? -1 : 1;
              if (
                bnA.properties.name.toLowerCase() >
                bnB.properties.name.toLowerCase()
              )
                return this.sortDirection === "ASC" ? 1 : -1;
              return 0;

            case "latestMeasurement": {
              if (
                (bnA.properties.current || "") < (bnB.properties.current || "")
              )
                return this.sortDirection === "ASC" ? -1 : 1;
              if (
                (bnA.properties.current || "") > (bnB.properties.current || "")
              )
                return this.sortDirection === "ASC" ? 1 : -1;
              return 0;
            }

            case "chainage":
              if (bnA.properties.from < bnB.properties.from)
                return this.sortDirection === "ASC" ? -1 : 1;
              if (bnA.properties.from > bnB.properties.from)
                return this.sortDirection === "ASC" ? 1 : -1;
              return 0;

            default:
              return 0;
          }
        });
    },
    selectSurvey(survey, bottleneck) {
      this.$store
        .dispatch(
          "bottlenecks/setSelectedBottleneck",
          bottleneck.properties.name
        )
        .then(() => {
          this.$store.commit("bottlenecks/selectedSurvey", survey);
        })
        .then(() => {
          this.$store.commit("map/moveToExtent", {
            feature: bottleneck,
            zoom: 17,
            preventZoomOut: true
          });
        });
    },
    selectBottleneck(bottleneck) {
      this.$store
        .dispatch(
          "bottlenecks/setSelectedBottleneck",
          bottleneck.properties.name
        )
        .then(() => {
          this.$store.commit("bottlenecks/setFirstSurveySelected");
        })
        .then(() => {
          this.$store.commit("map/moveToExtent", {
            feature: bottleneck,
            zoom: 17,
            preventZoomOut: true
          });
        });
    },
    sortBy(sorting) {
      this.sortColumn = sorting.sortColumn;
      this.sortDirection = sorting.sortDirection;
    },
    loadSurveys(bottleneck) {
      if (bottleneck === this.openBottleneck) {
        this.openBottleneck = null;
        this.openBottleneckSurveys = null;
      } else {
        this.loading = bottleneck;

        HTTP.get("/surveys/" + bottleneck.properties.name, {
          headers: {
            "X-Gemma-Auth": localStorage.getItem("token"),
            "Content-type": "text/xml; charset=UTF-8"
          }
        })
          .then(response => {
            this.openBottleneckSurveys = response.data.surveys.sort((a, b) => {
              return a.date_info < b.date_info ? 1 : -1;
            });
            this.openBottleneck = bottleneck;
          })
          .catch(error => {
            const { status, data } = error.response;
            displayError({
              title: this.$gettext("Backend Error"),
              message: `${status}: ${data.message || data}`
            });
          })
          .finally(() => (this.loading = null));
      }
    },
    displayCurrentChainage(from, to) {
      return from / 10 + " - " + to / 10;
    }
  },
  mounted() {
    this.$store.dispatch("bottlenecks/loadBottlenecksList");
  }
};
</script>