Mercurial > gemma
view client/src/components/systemconfiguration/MorphologyClassbreaks.vue @ 5095:e21cbb9768a2
Prevent duplicate fairway areas
In principal, there can be only one or no fairway area at each point
on the map. Since polygons from real data will often be topologically
inexact, just disallow equal geometries. This will also help to
avoid importing duplicates with concurrent imports, once the history
of fairway dimensions will be preserved.
author | Tom Gottfried <tom@intevation.de> |
---|---|
date | Wed, 25 Mar 2020 18:10:02 +0100 |
parents | 2e19ed576c68 |
children | 84d01a536bec |
line wrap: on
line source
<template> <div class="d-flex flex-column py-4"> <div class="px-3"> <h6 class="font-weight-bold"><translate>Sounding Result</translate></h6> <div class="d-flex flex-wrap"> <div class="input-group mb-3 mr-2 classbreak" v-for="(value, i) in morphologyClassbreaks" :key="i" > <div class="input-group-prepend"> <button :class="[ 'btn btn-sm btn-outline-secondary', { hasColor: morphologyClassbreaks[i][1] !== '#ffffff' } ]" :style=" 'width: 28px; background-color: ' + (morphologyClassbreaks[i][1] || 'transparent') " type="button" @click="showColorPicker('sounding-' + i)" > <font-awesome-icon icon="tint" /> </button> <div class="color-picker card shadow-sm" v-if="activeColorPicker === 'sounding-' + i" > <UIBoxHeader :title="colorPickerTitle" icon="paint-brush" :actions="[ { callback: () => { morphologyClassbreaks[i][1] = '#ffffff'; activeColorPicker = null; }, icon: 'trash' }, { callback: () => { activeColorPicker = null; }, icon: 'check' } ]" /> <chrome-picker v-model="morphologyClassbreaks[i][1]" @input="color => (morphologyClassbreaks[i][1] = color.hex)" /> </div> </div> <form id="novalidatedform" /> <input v-model.number="morphologyClassbreaks[i][0]" type="number" min="0" step="0.01" class="form-control form-control-sm numfield" /> <div class="input-group-append"> <button class="btn btn-sm btn-outline-secondary" type="button" @click="morphologyClassbreaks.splice(i, 1)" > <font-awesome-icon icon="times" /> </button> <button class="btn btn-sm btn-outline-secondary" @click="addClassbreak(morphologyClassbreaks, i)" > <font-awesome-icon icon="plus" /> </button> </div> </div> <button v-if="!morphologyClassbreaks.length" class="btn btn-sm btn-success mb-3" @click="addClassbreak(morphologyClassbreaks, 0)" > <font-awesome-icon icon="plus" /> </button> </div> </div> <div class="mb-4 px-3"> <a @click.prevent="submitClassbreaks" :class="[ 'btn btn-info btn-sm text-white', { disabled: !checkClassbreaks } ]" > <translate>Send</translate> </a> <a @click.prevent="resetClassbreaks" class="btn btn-outline-info btn-sm ml-2" > <translate>Reset to defaults</translate> </a> <span class="text-danger" v-if="!checkClassbreaks"> {{ validationMessage }} </span> <span class="text-secondary fix-trans-space" v-translate> Changes need a map reload. Consider informing your users. </span> </div> <div class="px-3"> <h6 class="font-weight-bold"> <translate>Sounding Result Comparison</translate> </h6> <div class="d-flex flex-wrap"> <div class="input-group mb-3 mr-2 classbreak" v-for="(value, i) in morphologyClassbreaksCompare" :key="i" > <div class="input-group-prepend"> <button :class="[ 'btn btn-sm btn-outline-secondary', { hasColor: morphologyClassbreaksCompare[i][1] !== '#ffffff' } ]" :style=" 'width: 28px; background-color: ' + (morphologyClassbreaksCompare[i][1] || 'transparent') " type="button" @click="showColorPicker('compare-' + i)" > <font-awesome-icon icon="tint" /> </button> <div class="color-picker card shadow-sm" v-if="activeColorPicker === 'compare-' + i" > <UIBoxHeader :title="colorPickerTitle" icon="paint-brush" :actions="[ { callback: () => { morphologyClassbreaksCompare[i][1] = '#ffffff'; activeColorPicker = null; }, icon: 'trash' }, { callback: () => { activeColorPicker = null; }, icon: 'check' } ]" /> <chrome-picker v-model="morphologyClassbreaksCompare[i][1]" @input=" color => (morphologyClassbreaksCompare[i][1] = color.hex) " /> </div> </div> <input v-model.number="morphologyClassbreaksCompare[i][0]" type="number" step="0.01" class="form-control form-control-sm numfield" /> <div class="input-group-append"> <button class="btn btn-sm btn-outline-secondary" type="button" @click="morphologyClassbreaksCompare.splice(i, 1)" > <font-awesome-icon icon="times" /> </button> <button class="btn btn-sm btn-outline-secondary" @click="addClassbreak(morphologyClassbreaksCompare, i)" > <font-awesome-icon icon="plus" /> </button> </div> </div> <button v-if="!morphologyClassbreaksCompare.length" class="btn btn-sm btn-success mb-3" @click="addClassbreak(morphologyClassbreaksCompare, 0)" > <font-awesome-icon icon="plus" /> </button> </div> </div> <div class="px-3"> <a @click.prevent="submitClassbreaksCompare" :class="[ 'btn btn-info btn-sm text-white', { disabled: !checkClassbreaksCompare } ]" > <translate>Send</translate> </a> <a @click.prevent="resetClassbreaksCompare" class="btn btn-outline-info btn-sm ml-2" > <translate>Reset to defaults</translate> </a> <span class="text-danger" v-if="!checkClassbreaksCompare"> {{ validationMessageForCompare }} </span> <span class="text-secondary fix-trans-space" v-translate> Colour changes need a map reload. Value changes need a de- and re-select of a difference calculation. Inform your users! </span> </div> </div> </template> <style lang="sass" scoped> .numfield:invalid border: 2px solid border-color: #ff0000 .classbreak width: 154px .btn-outline-secondary border-color: #ccc color: #ccc &:hover:not(.hasColor) background: #eee !important .input-group-prepend .btn-outline-secondary.hasColor color: rgba(255, 255, 255, 0.5) .input-group-append .btn-outline-secondary &:hover color: #dc3545 &:last-child:hover color: #28a745 .color-picker position: absolute top: -4px left: 19px z-index: 9 overflow: hidden border-top-left-radius: 0 !important .btn border-radius: 0 !important .vc-chrome box-shadow: none /deep/ .vc-chrome-alpha-wrap display: none !important .vc-chrome-hue-wrap margin-top: 10px .vc-chrome-saturation-wrap border-radius: 0 </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@intevation.de> */ import { mapState } from "vuex"; import { Chrome } from "vue-color"; import defaults from "./defaults"; export default { components: { "chrome-picker": Chrome }, data() { return { morphologyClassbreaks: [], morphologyClassbreaksCompare: [], activeColorPicker: null, closeColorPickerListener: null, validationMessage: "", validationMessageForCompare: "" }; }, computed: { ...mapState("application", ["config"]), colorPickerTitle() { return this.$gettext("Choose color"); }, checkClassbreaks() { return ( this.valuesAreValid(this.morphologyClassbreaks) && !this.hasDoublettes(this.morphologyClassbreaks) ); }, checkClassbreaksCompare() { return ( this.valuesAreValid(this.morphologyClassbreaksCompare, "compare") && !this.hasDoublettes(this.morphologyClassbreaksCompare, "compare") ); } }, methods: { // check if the same value is used for more than one field. hasDoublettes(m, compare) { const errorMessage = this.$gettext( "Same value is used in multiple fields." ); let values = []; for (let i = 0; i < m.length; i++) { values[i] = Number(m[i][0]); } if (new Set(values).size !== values.length) { // determine which message to change if (compare !== "compare") { this.validationMessage = errorMessage; } else { this.validationMessageForCompare = errorMessage; } return true; } return false; }, valuesAreValid(m, compare) { const errorMessage = this.$gettext( "There are invalid classbreak values." ); let values = m.map(e => { const element = e[0]; if (!isNaN(element)) { if (element === "") return false; // check if the field is empty if (!isNaN(element)) { const numberParts = String(element).split("."); // check number of decimal places return numberParts.length == 2 ? numberParts[1].length < 3 : true; } } return false; }); if (values.every(e => e === true)) { return true; } if (compare !== "compare") { this.validationMessage = errorMessage; } else { this.validationMessageForCompare = errorMessage; } return false; }, addClassbreak(classbreaks, i) { classbreaks.splice( i, 0, classbreaks.length > i ? [classbreaks[i][0], classbreaks[i][1]] // create new array! : [1, "#ffffff"] ); }, showColorPicker(id) { this.activeColorPicker = this.activeColorPicker === id ? null : id; }, submitClassbreaks() { this.$store .dispatch("application/saveConfig", { morphology_classbreaks: this.morphologyClassbreaks .sort((a, b) => (a[0] < b[0] ? -1 : 1)) .map(cb => (cb[1] === "#ffffff" ? cb[0] : cb.join(":"))) .join(",") }) .finally(() => this.$store.dispatch("application/loadConfig")); }, submitClassbreaksCompare() { this.$store .dispatch("application/saveConfig", { morphology_classbreaks_compare: this.morphologyClassbreaksCompare .sort((a, b) => (a[0] < b[0] ? -1 : 1)) .map(cb => (cb[1] === "#ffffff" ? cb[0] : cb.join(":"))) .join(",") }) .finally(() => this.$store.dispatch("application/loadConfig")); }, resetClassbreaks() { this.morphologyClassbreaks = this.parseClassbreakString( defaults.morphology_classbreaks ); }, resetClassbreaksCompare() { this.morphologyClassbreaksCompare = this.parseClassbreakString( defaults.morphology_classbreaks_compare ); }, parseClassbreakString(str) { return str .split(",") .map(cb => cb.split(":")) .map(cb => { cb[0] = Number(cb[0]); cb[1] = cb[1] || "#ffffff"; return cb; }); } }, mounted() { this.morphologyClassbreaks = this.parseClassbreakString( this.config.morphology_classbreaks ); this.morphologyClassbreaksCompare = this.parseClassbreakString( this.config.morphology_classbreaks_compare ); this.closeColorPickerListener = e => { // Escape if (e.keyCode === 27) { this.activeColorPicker = null; } }; window.addEventListener("keydown", this.closeColorPickerListener); }, destroyed() { window.removeEventListener("keydown", this.closeColorPickerListener); } }; </script>