view client/src/components/ImportSoundingresults.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 bb5286acfee2
children c6fba10926cc
line wrap: on
line source

<template>
  <div class="main d-flex flex-column">
    <div class="d-flex flex-row">
      <Spacer></Spacer>
      <div class="card shadow-xs mt-2 mr-2 w-100 h-100">
        <UIBoxHeader icon="upload" title="Import Soundingresults" />
        <div v-if="editState">
          <div
            v-for="(message, index) in messages"
            :key="index"
            class="alert alert-warning small rounded-0"
          >
            {{ message }}
          </div>
          <div class="container">
            <div class="row">
              <div class="col-5">
                <small class="text-muted">
                  <translate>Bottleneck</translate>
                </small>
                <select v-model="bottleneck" class="custom-select">
                  <option
                    v-for="bottleneck in availableBottlenecks"
                    :value="bottleneck"
                    :key="bottleneck.properties.objnam"
                  >
                    {{ bottleneck.properties.objnam }}
                  </option>
                </select>
                <span class="text-danger">
                  <small v-if="!bottleneck">
                    <translate>Please select a bottleneck</translate>
                  </small>
                </span>
              </div>
              <div class="col-2">
                <small class="text-muted">
                  <translate>Projection</translate>&nbsp;(EPSG)
                </small>
                <input
                  class="form-control"
                  v-model="projection"
                  value="4326"
                  placeholder="e.g. 4326"
                  type="number"
                />
                <span class="text-left text-danger">
                  <small v-if="!projection">
                    <translate>Please enter a projection</translate>
                  </small>
                </span>
              </div>
              <div class="col-2">
                <small class="text-muted">
                  <translate>Depthreference</translate>
                </small>
                <select
                  v-model="depthReference"
                  class="custom-select"
                  id="depthreference"
                >
                  <option
                    v-for="option in this.depthReferenceOptions"
                    :key="option"
                    >{{ option }}</option
                  >
                </select>
                <span class="text-left text-danger">
                  <small v-if="!depthReference">
                    <translate>Please enter a reference</translate>
                  </small>
                </span>
              </div>
              <div class="col-3">
                <small class="text-muted"> <translate>Date</translate> </small>
                <input
                  id="importdate"
                  type="date"
                  class="form-control"
                  placeholder="Date of import"
                  aria-label="bottleneck"
                  aria-describedby="bottlenecklabel"
                  v-model="importDate"
                />
                <span class="text-left text-danger">
                  <small v-if="!importDate">
                    <translate>Please enter a date</translate>
                  </small>
                </span>
              </div>
            </div>
            <div class="row"></div>
          </div>
        </div>
        <div class="container py-5">
          <div v-if="uploadState" class="input-group">
            <div class="custom-file">
              <input
                accept=".zip"
                type="file"
                @change="fileSelected"
                class="custom-file-input"
                id="uploadFile"
              />
              <label class="pointer custom-file-label" for="uploadFile">
                {{ uploadLabel }}
              </label>
            </div>
          </div>
          <div class="d-flex justify-content-between" v-if="editState">
            <a
              download="meta.json"
              :href="dataLink"
              :class="[
                'btn btn-outline-info',
                { disabled: !bottleneck || !importDate || !depthReference }
              ]"
            >
              <translate>Download Meta.json</translate>
            </a>
            <span>
              <button
                @click="deleteTempData"
                class="btn btn-danger"
                type="button"
              >
                <translate>Cancel Upload</translate>
              </button>
              <button
                :disabled="disableUploadButton"
                @click="confirm"
                class="btn btn-info ml-2"
                type="button"
              >
                <translate>Confirm</translate>
              </button>
            </span>
          </div>
        </div>
      </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 by via donau
 *   – Österreichische Wasserstraßen-Gesellschaft mbH
 * Software engineering by Intevation GmbH
 *
 * Author(s):
 * Thomas Junk <thomas.junk@intevation.de>
 * Markus Kottländer <markus.kottlaender@intevation.de>
 */
import { HTTP } from "@/lib/http";
import { displayError, displayInfo } from "@/lib/errors.js";
import { mapState } from "vuex";
import Spacer from "./Spacer";

const IMPORTSTATE = { UPLOAD: "UPLOAD", EDIT: "EDIT" };

export default {
  name: "imports",
  components: {
    Spacer
  },
  data() {
    return {
      importState: IMPORTSTATE.UPLOAD,
      depthReference: "",
      bottleneck: "",
      projection: "",
      importDate: "",
      uploadLabel: this.$gettext("choose .zip- file"),
      uploadFile: null,
      disableUpload: false,
      token: null,
      messages: []
    };
  },
  methods: {
    initialState() {
      this.importState = IMPORTSTATE.UPLOAD;
      this.depthReference = "";
      this.bottleneck = null;
      this.projection = "";
      this.importDate = "";
      this.uploadLabel = this.$gettext("choose .zip- file");
      this.uploadFile = null;
      this.disableUpload = false;
      this.token = null;
      this.messages = [];
    },
    fileSelected(e) {
      const files = e.target.files || e.dataTransfer.files;
      if (!files) return;
      this.uploadLabel = files[0].name;
      this.uploadFile = files[0];
      this.upload();
    },
    deleteTempData() {
      HTTP.delete("/imports/sr-upload/" + this.token, {
        headers: {
          "X-Gemma-Auth": localStorage.getItem("token")
        }
      })
        .then(() => {
          this.initialState();
        })
        .catch(error => {
          const { status, data } = error.response;
          displayError({
            title: this.$gettext("Backend Error"),
            message: `${status}: ${data.message || data}`
          });
        });
    },
    upload() {
      let formData = new FormData();
      formData.append("soundingresult", this.uploadFile);
      HTTP.post("/imports/sr-upload", formData, {
        headers: {
          "X-Gemma-Auth": localStorage.getItem("token"),
          "Content-Type": "multipart/form-data"
        }
      })
        .then(response => {
          if (response.data.meta) {
            const { bottleneck, date, epsg } = response.data.meta;
            const depthReference = response.data.meta["depth-reference"];
            this.bottleneck = this.bottlenecks.find(
              bn => bn.properties.objnam === bottleneck
            );
            this.depthReference = depthReference;
            this.importDate = new Date(date).toISOString().split("T")[0];
            this.projection = epsg;
          }
          this.importState = IMPORTSTATE.EDIT;
          this.token = response.data.token;
          this.messages = response.data.messages;
        })
        .catch(error => {
          const { status, data } = error.response;
          const messages = data.messages ? data.messages.join(", ") : "";
          displayError({
            title: this.$gettext("Backend Error"),
            message: `${status}: ${messages}`
          });
        });
    },
    confirm() {
      let formData = new FormData();
      formData.append("token", this.token);
      if (this.bottleneck)
        formData.append("bottleneck", this.bottleneck.properties.objnam);
      if (this.importDate)
        formData.append("date", this.importDate.split("T")[0]);
      if (this.depthReference)
        formData.append("depth-reference", this.depthReference);
      if (this.projection) formData.append("", this.projection);

      HTTP.post("/imports/sr", formData, {
        headers: {
          "X-Gemma-Auth": localStorage.getItem("token"),
          "Content-Type": "multipart/form-data"
        }
      })
        .then(() => {
          displayInfo({
            title: this.$gettext("Import"),
            message:
              this.$gettext("Starting import for ") +
              this.bottleneck.properties.objnam
          });
          this.initialState();
        })
        .catch(error => {
          const { status, data } = error.response;
          displayError({
            title: this.$gettext("Backend Error"),
            message: `${status}: ${data.message || data}`
          });
        });
    }
  },
  mounted() {
    this.$store.dispatch("bottlenecks/loadBottlenecks");
  },
  watch: {
    showContextBox() {
      if (!this.showContextBox && this.token) this.deleteTempData();
    }
  },
  computed: {
    ...mapState("application", ["showContextBox"]),
    ...mapState("bottlenecks", ["bottlenecks"]),
    disableUploadButton() {
      if (this.importState === IMPORTSTATE.UPLOAD) return this.disableUpload;
      if (
        !this.bottleneck ||
        !this.importDate ||
        !this.depthReference ||
        !this.projection
      )
        return true;
      return this.disableUpload;
    },
    availableBottlenecks() {
      return this.bottlenecks;
    },
    editState() {
      return this.importState === IMPORTSTATE.EDIT;
    },
    uploadState() {
      return this.importState === IMPORTSTATE.UPLOAD;
    },
    Upload() {
      return this.$gettext("Upload");
    },
    Confirm() {
      return this.$gettext("Confirm");
    },
    dataLink() {
      if (this.bottleneck && this.depthReference && this.import) {
        return (
          "data:text/json;charset=utf-8," +
          encodeURIComponent(
            JSON.stringify({
              depthReference: this.depthReference,
              bottleneck: this.bottleneck.properties.objnam,
              date: this.importDate
            })
          )
        );
      }
    },
    depthReferenceOptions() {
      if (
        this.bottleneck &&
        this.bottleneck.properties.reference_water_levels
      ) {
        return Object.keys(
          JSON.parse(this.bottleneck.properties.reference_water_levels)
        );
      }
      return [];
    }
  }
};
</script>