view client/src/components/ImportStretches.vue @ 2956:974122125a76

Let it be an error if closest points of DISMARs on axis are equal This might be the case e.g. if both distance marks are very far away from the available axis geometries. Instead of returning a point in such a case, which would likely be an unexpected result, raise an exception by means of STRICT.
author Tom Gottfried <tom@intevation.de>
date Mon, 08 Apr 2019 14:53:09 +0200
parents 6c5364ff0abb
children b74ebeb2bdc8
line wrap: on
line source

<template>
  <div class="d-flex flex-column mb-3">
    <UIBoxHeader
      icon="road"
      :title="defineStretchesLabel"
      :closeCallback="$parent.close"
    />
    <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="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"
                  />
                </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"
                  />
                </span>
              </div>
              <span class="text-left text-danger">
                <small v-if="endrhmError && !endrhm">
                  <translate>Please enter an end point</translate>
                </small>
              </span>
            </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>
                </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>
  </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";
import { LAYERS } from "@/store/map";
import { HTTP } from "@/lib/http";
import { sortTable } from "@/lib/mixins";

export default {
  name: "importstretches",
  mixins: [sortTable],
  data() {
    return {
      staging: [],
      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,
      loading: false
    };
  },
  computed: {
    ...mapState("application", ["searchQuery"]),
    ...mapState("map", ["identifiedFeatures", "currentMeasurement"]),
    ...mapGetters("map", ["getVSourceByName", "getLayerByName"]),
    ...mapGetters("user", ["isSysAdmin"]),
    ...mapState("imports", ["stretches"]),
    defineStretchesLabel() {
      return this.$gettext("Define Stretches");
    },
    nameLabel() {
      return this.$gettext("Name");
    },
    dateLabel() {
      return this.$gettext("Date");
    },
    sourceorganizationLabel() {
      return this.$gettext("Source organization");
    },
    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;
    }
  },
  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;
      }
    }
  },
  methods: {
    filteredStretches() {
      return this.stretches.filter(s => {
        return (s.properties.name + s.properties.source_organization)
          .toLowerCase()
          .includes(this.searchQuery.toLowerCase());
      });
    },
    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) => {
        HTTP.get("/imports?states=pending", {
          headers: { "X-Gemma-Auth": localStorage.getItem("token") }
        })
          .then(response => {
            const { imports } = response.data;
            this.staging = imports;
            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("imports/selectedStretchId", stretch.id);
      this.$store.commit("map/setLayerVisible", LAYERS.STRETCHES);
      this.$store.commit("map/moveToExtent", {
        feature: stretch,
        zoom: 17,
        preventZoomOut: true
      });
    },
    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.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.$store.dispatch("imports/loadStretches").then(() => {
            this.edit = false;
          });
        })
        .catch(error => {
          const { status, data } = error.response;
          displayError({
            title: this.$gettext("Backend Error"),
            message: `${status}: ${data.message || data}`
          });
        });
    }
  },
  mounted() {
    this.edit = false;
    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({
        title: this.$gettext("Backend Error"),
        message: `${status}: ${data.message || data}`
      });
    });
  }
};
</script>

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