view client/src/components/identify/Identify.vue @ 2455:54c9fe587fe6

Subdivide SQL function to prepare for improved error handling The context of an error (e.g. the function in which it occured) can be inferred by the database client. Not doing all in one statement will render the context more meaningful.
author Tom Gottfried <tom@intevation.de>
date Fri, 01 Mar 2019 18:38:02 +0100
parents 9de710bdb535
children bb5286acfee2
line wrap: on
line source

<template>
  <div
    :class="[
      'box ui-element rounded bg-white text-nowrap',
      { expanded: showIdentify }
    ]"
  >
    <div style="width: 18rem">
      <UIBoxHeader icon="info" title="Identified" :closeCallback="close" />
      <div class="features flex-grow-1 text-left">
        <div v-if="currentMeasurement">
          <small class="d-block bg-dark text-light text-center px-2 py-1">
            {{ $gettext("Measurement") }}
          </small>
          <small class="d-flex justify-content-between px-2">
            <b>
              {{ currentMeasurement.quantity }}
            </b>
            {{ currentMeasurement.value }} {{ currentMeasurement.unitSymbol }}
          </small>
        </div>
        <div
          v-for="feature of filteredIdentifiedFeatures"
          :key="feature.getId()"
        >
          <small class="d-block bg-dark text-light text-center px-2 py-1">
            {{ $gettext(featureLabel(feature)) }}
          </small>
          <small
            v-for="prop in featureProps(feature)"
            :key="prop.key"
            v-if="prop.val"
            class="d-flex justify-content-between px-2"
          >
            <b>{{ $gettext(prop.key) }}</b>
            {{ prop.val }}
          </small>
        </div>
        <div
          v-if="!currentMeasurement && !filteredIdentifiedFeatures.length"
          class="text-muted small text-center my-auto py-3 px-2"
        >
          <translate>No features identified.</translate>
        </div>
      </div>
      <div class="versioninfo border-top p-3 text-left">
        <span v-translate="{ license: 'AGPL-3.0-or-later' }">
          This app uses <i>gemma</i>, which is Free Software under <br />
          %{ license } without warranty, see docs for details.
        </span>
        <br />
        <a href="https://hg.intevation.de/gemma/file/tip">
          <translate>source-code</translate>
        </a>
        {{ versionStr }} <br />© via donau. &#x24D4; Intevation. <br />
        <span v-translate="{ name: 'OpenSteetMap' }"
          >Some data ©
          <a href="https://www.openstreetmap.org/copyright">%{ name }</a>
          contributors.
        </span>
        <br />
        <span v-translate="{ geoLicense: 'CC-BY-4.0' }">
          Uses
          <a href="https://download.geonames.org/export/dump/readme.txt"
            >GeoNames</a
          >
          under %{ geoLicense }.
        </span>
        <translate>Generated PDFs use font: </translate>
        <a href="http://libertine-fonts.org">LinBiolinum</a>
      </div>
    </div>
  </div>
</template>

<style lang="scss" scoped>
.features {
  max-height: 19rem;
  overflow-y: auto;
  small {
    &:nth-child(even) {
      background: #f8f8f8;
    }
    &:hover {
      background: #eeeeee;
    }
  }
}

.versioninfo {
  font-size: 60%;
  white-space: normal;
}
</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, 2019 by via donau
 *   – Österreichische Wasserstraßen-Gesellschaft mbH
 * Software engineering by Intevation GmbH
 *
 * Author(s):
 * Thomas Junk <thomas.junk@intevation.de>
 * Bernhard E. Reiter <bernhard.reiter@intevation.de>
 * Markus Kottländer <markus.kottlaender@intevation.de>
 */
import { mapState, mapGetters } from "vuex";
import { formatter } from "./formatter";

export default {
  name: "identify",
  computed: {
    ...mapGetters("application", ["versionStr"]),
    ...mapState("application", ["showIdentify"]),
    ...mapGetters("map", ["filteredIdentifiedFeatures"]),
    ...mapState("map", ["currentMeasurement"])
  },
  methods: {
    close() {
      this.$store.commit("application/showIdentify", false);
    },
    featureId(feature) {
      // cut away everything from the last . to the end
      return feature.getId().replace(/[.][^.]*$/, "");
    },
    featureLabel(feature) {
      if (formatter.hasOwnProperty(this.featureId(feature))) {
        return formatter[this.featureId(feature)].label;
      }
      return this.featureId(feature);
    },
    featureProps(feature) {
      let featureId = this.featureId(feature);

      // create array with {key, val} objects
      let propsArray = [];
      Object.keys(feature.getProperties()).forEach(key => {
        // skip geometry (would lead to cyclic object error)
        if (key !== feature.getGeometryName()) {
          let val = feature.getProperties()[key];

          // if val is a valid json object string, spread its values into the array
          let jsonObj = this.getObjectFromString(val);
          if (jsonObj) {
            Object.keys(jsonObj).forEach(key => {
              propsArray.push({ key, val: jsonObj[key] });
            });
          } else {
            // otherwise just put the key value pair into the array
            propsArray.push({ key, val });
          }
        }
      });

      // change labels and remove unneeded properties
      // for all features
      propsArray = propsArray.map(formatter.all);
      // feature specific
      if (
        formatter.hasOwnProperty(featureId) &&
        formatter[featureId].hasOwnProperty("props")
      ) {
        propsArray = propsArray.map(formatter[featureId].props);
      }
      // remove empty entries
      propsArray = propsArray.filter(p => p);

      // remove underscores in labels that where not previously changed already
      propsArray = propsArray.map(prop => {
        return { key: prop.key.replace(/_/g, " "), val: prop.val };
      });

      return propsArray;
    },
    getObjectFromString(val) {
      // JSON.parse() accepts integers and null as valid json. So to be sure to
      // get an object, we cannot just try JSON.parse() but we need to check if
      // the given value is a string and starts with a {.
      if (
        Object.prototype.toString.call(val) === "[object String]" &&
        val[0] === "{"
      ) {
        try {
          return JSON.parse(val);
        } catch (e) {
          return null;
        }
      }
      return null;
    }
  }
};
</script>