view client/src/components/ImportStretches.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 8d767359fddb
children 4bcb26542767
line wrap: on
line source

<template>
  <div class="d-flex flex-column mb-3">
    <UIBoxHeader
      icon="road"
      title="Define Stretches"
      :closeCallback="$parent.close"
    />
    <div v-if="!edit" class="mb-3">
      <UITableHeader
        :columns="[
          { id: 'name', title: 'Name', class: 'col-4' },
          { id: 'date', title: 'Date', class: 'col-2' },
          { id: 'srcorg', title: 'Source organization', class: 'col-3' }
        ]"
        :sortable="false"
      />
      <UITableBody :data="stretches" v-slot="{ item: stretch }">
        <div class="py-2 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-2 col-2">
          {{ stretch.properties["date_info"] | surveyDate }}
        </div>
        <div class="py-2 col-3">
          {{ stretch.properties["source_organization"] }}
        </div>
        <div class="py-2 col text-right">
          <button
            class="btn btn-sm btn-dark mr-1"
            @click="editStretch(stretch)"
          >
            <font-awesome-icon icon="pencil-alt" fixed-width />
          </button>
          <button class="btn btn-sm btn-dark" @click="deleteStretch(stretch)">
            <font-awesome-icon icon="trash" fixed-width />
          </button>
        </div>
      </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>
              <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"
                ></font-awesome-icon>
              </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">
              <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"
                ></font-awesome-icon>
              </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="!pointsValid">
              <translate>Startpoint is not before endpoint.</translate>
            </small>
          </span>
        </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>
              </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=""
              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>
      </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 mr-3">
      <button v-if="!edit" @click="startEdit()" class="btn btn-info">
        <translate>New stretch</translate>
      </button>
    </div>
  </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, 2019 by via donau
 *   – Österreichische Wasserstraßen-Gesellschaft mbH
 * Software engineering by Intevation GmbH
 *
 * Author(s):
 * Thomas Junk <thomas.junk@intevation.de>
 * Tom Gottfried <tom.gottfried@intevation.de>
 */
import { mapState, mapGetters } from "vuex";
import { displayError, displayInfo } from "@/lib/errors.js";
import { LAYERS } from "@/store/map.js";

export default {
  name: "importstretches",
  data() {
    return {
      edit: false,
      editExistingStretch: false,
      id: "",
      funktion: "",
      startrhm: "",
      endrhm: "",
      tolerance: 5,
      objbn: "",
      nobjbn: "",
      countryCode: "",
      date_info: new Date().toISOString().split("T")[0],
      source: "",
      pipetteStart: false,
      pipetteEnd: false,
      idError: false,
      funktionError: false,
      startrhmError: false,
      endrhmError: false,
      toleranceError: false,
      objbnError: false,
      nobjbnError: false,
      date_infoError: false,
      sourceError: false,
      countryCodeError: false
    };
  },
  mounted() {
    this.edit = false;
    this.loadStretches().catch(error => {
      const { status, data } = error.response;
      displayError({
        title: this.$gettext("Backend Error"),
        message: `${status}: ${data.message || data}`
      });
    });
    this.loadStagingData().catch(error => {
      const { status, data } = error.response;
      displayError({
        title: this.$gettext("Backend Error"),
        message: `${status}: ${data.message || data}`
      });
    });
  },
  methods: {
    gotoStaging(id) {
      this.$router.push("/review/" + id);
    },
    isInStaging(stretchname) {
      for (let s of this.stretchesInStaging) {
        if (s.name == stretchname) return true;
      }
      return false;
    },
    getStagingLink(stretchname) {
      for (let s of this.stretchesInStaging) {
        if (s.name == stretchname) return s.id;
      }
    },
    loadStagingData() {
      return new Promise((resolve, reject) => {
        this.$store
          .dispatch("imports/getStaging")
          .then(response => {
            resolve(response);
          })
          .catch(error => {
            reject(error);
          });
      });
    },
    editStretch(stretch) {
      const properties = stretch.properties;
      this.date_info = properties.date_info.split("T")[0];
      this.id = properties.name;
      this.nobjbn = properties.nobjnam;
      this.objbn = properties.objnam;
      this.countryCode = properties.countries;
      this.source = properties["source_organization"];
      this.edit = true;
      this.startrhm = properties.lower;
      this.endrhm = properties.upper;
      this.editExistingStretch = true;
    },
    deleteStretch(stretch) {
      this.$store.commit("application/popup", {
        icon: "trash",
        title: this.$gettext("Delete Stretch"),
        content:
          this.$gettext("Do you really want to delete this stretch:") +
          `<br>
        <b>${stretch.properties.name}, ${
            stretch.properties.source_organization
          } (${stretch.properties.countries})</b>`,
        confirm: {
          label: this.$gettext("Delete"),
          icon: "trash",
          callback: () => {
            displayInfo({
              title: this.$gettext("Not implemented"),
              message: this.$gettext("Deleting ") + stretch.id
            });
          }
        },
        cancel: {
          label: this.$gettext("Cancel"),
          icon: "times"
        }
      });
    },
    moveMapToStretch(stretch) {
      this.$store.commit("map/setLayerVisible", LAYERS.STRETCHES);
      this.$store.commit("map/moveToExtent", {
        feature: stretch,
        zoom: 17,
        preventZoomOut: true
      });
    },
    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;
      this.editExistingStretch = false;
      this.funktion = "";
      this.startrhm = "";
      this.tolerance = 5;
      this.endrhm = "";
      this.objbn = "";
      this.nobjbn = "";
      this.countryCode = "";
      this.date_info = new Date().toISOString().split("T")[0];
      this.source = "";
      this.pipetteStart = false;
      this.pipetteEnd = false;
      this.idError = false;
      this.funktionError = false;
      this.startrhmError = false;
      this.endrhmError = false;
      this.toleranceError = false;
      this.objbnError = false;
      this.nobjbnError = false;
      this.date_infoError = false;
      this.sourceError = false;
      this.countryCodeError = false;
    },
    startEdit() {
      this.clean();
      this.edit = true;
    },
    togglePipette(t) {
      this.$store.commit("map/setLayerVisible", LAYERS.DISTANCEMARKSAXIS);
      if (t === "start") {
        this.pipetteStart = !this.pipetteStart;
        this.pipetteEnd = false;
      } else {
        this.pipetteEnd = !this.pipetteEnd;
        this.pipetteStart = false;
      }
    },
    validate() {
      const fields = [
        "id",
        "funktion",
        "startrhm",
        "tolerance",
        "endrhm",
        "objbn",
        "nobjbn",
        "countryCode",
        "date_info",
        "source"
      ];
      fields.forEach(field => {
        if (!this[field]) {
          this[field + "Error"] = true;
        } else {
          this[field + "Error"] = false;
        }
      });
    },
    save() {
      this.validate();
      if (!this.pointsValid) return;
      if (
        !this.id ||
        !this.startrhm ||
        !this.endrhm ||
        (!this.tolerance && this.editExistingStretch) ||
        !this.source ||
        !this.date_info ||
        !this.objbn ||
        !this.countryCode
      )
        return;
      const data = {
        name: this.id,
        from: this.startrhm,
        to: this.endrhm,
        "source-organization": this.source,
        "date-info": this.date_info,
        objnam: this.objbn,
        nobjnam: this.nobjbn,
        countries: this.countryCode.split(",").map(x => {
          return x.trim();
        })
      };
      if (!this.editExistingStretch) {
        data["tolerance"] = this.tolerance;
      }
      this.$store
        .dispatch("imports/saveStretch", data)
        .then(() => {
          displayInfo({
            title: this.$gettext("Import"),
            message: this.$gettext("Starting import of stretch")
          });
          this.clean();
          this.loadStretches().then(() => {
            this.edit = false;
          });
        })
        .catch(error => {
          const { status, data } = error.response;
          displayError({
            title: this.$gettext("Backend Error"),
            message: `${status}: ${data.message || data}`
          });
        });
    }
  },
  watch: {
    identifiedFeatures() {
      const filterDistanceMarks = x => {
        return /^distance_marks/.test(x["id_"]);
      };
      const distanceMark = this.identifiedFeatures.filter(filterDistanceMarks);
      if (distanceMark.length > 0) {
        const value = distanceMark[0].getProperties()["location"];
        this.startrhm = this.pipetteStart ? value : this.startrhm;
        this.endrhm = this.pipetteEnd ? value : this.endrhm;
        this.pipetteStart = false;
        this.pipetteEnd = false;
      }
    }
  },
  computed: {
    ...mapState("map", ["identifiedFeatures", "currentMeasurement"]),
    ...mapGetters("user", ["isSysAdmin"]),
    ...mapState("imports", ["stretches", "staging"]),
    stretchesInStaging() {
      const result = [];
      for (let stretch of this.stretches) {
        for (let s of this.staging) {
          if (s.kind == "st" && s.summary.stretch == stretch.properties.name) {
            result.push({ name: s.summary.stretch, id: s.id });
          }
        }
      }
      return result;
    },
    pointsValid() {
      if (!this.startrhm || !this.endrhm) return true;
      const start = this.startrhm.replace(/\D+/g, "") * 1;
      const end = this.endrhm.replace(/\D+/g, "") * 1;
      const result = start < end;
      return result;
    }
  }
};
</script>

<style lang="scss" scoped>
.linkto {
  cursor: pointer;
}
</style>