Mercurial > gemma
view client/src/components/fairway/AvailableFairwayDepthDialogue.vue @ 5736:55892008ec96 default tip
Fixed a bunch of corner cases in WG import.
author | Sascha Wilde <wilde@sha-bang.de> |
---|---|
date | Wed, 29 May 2024 19:02:42 +0200 |
parents | 84d01a536bec |
children |
line wrap: on
line source
<template> <div :class="[ 'box ui-element rounded bg-white text-nowrap', { expanded: showFairwayDepth } ]" > <div style="width: 17rem"> <UIBoxHeader icon="chart-line" :title="label" :closeCallback="close" /> <div class="box-body"> <UISpinnerOverlay v-if="loading" /> <div class="mb-2 d-flex justify-content-between align-items-center"> <div class="custom-control custom-radio custom-control-inline mr-2"> <input :value="$options.BOTTLENECK" type="radio" v-model="type" id="type-bottleneck" class="custom-control-input" /> <label class="custom-control-label small d-flex align-items-center" for="type-bottleneck" > <translate>Bottlenecks</translate> </label> </div> <div class="custom-control custom-radio custom-control-inline mr-2"> <input :value="$options.STRETCH" type="radio" v-model="type" id="type-stretch" class="custom-control-input" /> <label class="custom-control-label small d-flex align-items-center" for="type-stretch" > <translate>Stretches</translate> </label> </div> <div class="custom-control custom-radio custom-control-inline"> <input :value="$options.SECTION" type="radio" v-model="type" id="type-section" class="custom-control-input" /> <label class="custom-control-label small d-flex align-items-center" for="type-section" > <translate>Sections</translate> </label> </div> </div> <div class="d-flex"> <select v-if="type === $options.BOTTLENECK" class="w-90 form-control font-weight-bold" v-model="selectedEntry" > <option :value="null">{{ placeholder }}</option> <optgroup v-for="(bottlenecksForCountry, cc) in orderedBottlenecks" :key="cc" :label="cc" > <option v-for="bn in bottlenecksForCountry" :key="bn.properties.id" :value="bn" > {{ bn.properties.displayName }} <!-- name for display is okay --> </option> </optgroup> </select> <select v-else-if="type === $options.STRETCH" class="form-control font-weight-bold" v-model="selectedEntry" > <option :value="null">{{ placeholder }}</option> <option v-for="stretch in stretches" :value="stretch" :key="stretch.id" > {{ stretch.properties.name }} </option> </select> <select v-else-if="type === $options.SECTION" class="form-control font-weight-bold" v-model="selectedEntry" > <option :value="null">{{ placeholder }}</option> <option v-for="section in sections" :value="section" :key="section.id" > {{ section.properties.name }} </option> </select> <button @click="takeMeThere" class="btn btn-sm btn-info"> <font-awesome-icon icon="crosshairs" /> </button> </div> <div class="d-flex mt-2"> <div class="d-flex flex-column w-50 mr-1"> <small class="my-auto text-muted"> <translate>Type</translate> </small> <select v-model="selectedFrequency" class="form-control form-control-sm" > <option v-for="(option, index) in $options.FREQUENCIES" :value="index" :key="index" > {{ option }} </option> </select> </div> <div class="d-flex flex-column w-50 ml-1"> <small class="my-auto text-muted"><translate>LOS</translate></small> <select v-model="los" class="form-control form-control-sm"> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> </div> </div> <div class="d-flex mt-2"> <div :key="new Date().getTime()" v-if="selectedFrequency === $options.FREQUENCIES.monthly" class="d-flex flex-column w-50 mr-1" > <small for="from" class="my-auto text-muted"> <translate>Starting Month</translate> </small> <select v-model="startMonth" class="form-control form-control-sm"> <option v-for="month in Object.keys($options.MONTHS)" :key="'start' + month" :value="month" >{{ $options.MONTHS[month] }}</option > </select> </div> <div :key="new Date().getTime()" v-if="selectedFrequency === $options.FREQUENCIES.quarterly" class="d-flex flex-column w-50 mr-1" > <small for="from" class="my-auto text-muted"> <translate>Starting Quarter</translate> </small> <select v-model="startQuarter" class="form-control form-control-sm"> <option v-for="month in [1, 2, 3, 4]" :key="'qstart' + month" :value="month" >Q{{ month }}</option > </select> </div> <div :class="[ 'd-flex', 'flex-column', 'w-50', { 'ml-1': selectedFrequency !== $options.FREQUENCIES.yearly } ]" > <small for="from" class="my-auto text-muted"> <translate>Starting Year</translate> </small> <select v-model="startYear" class="form-control form-control-sm"> <option v-for="year in $options.YEARS" :key="'start' + year" :value="year" >{{ year }}</option > </select> </div> </div> <div class="d-flex mt-2"> <div :key="new Date().getTime()" class="d-flex flex-column w-50 mr-1" v-if="selectedFrequency === $options.FREQUENCIES.monthly" > <small for="to" class="my-auto text-muted"> <translate>Ending Month</translate> </small> <select v-model="endMonth" class="form-control form-control-sm"> <option v-for="month in Object.keys($options.MONTHS)" :key="'end' + month" :value="month" >{{ $options.MONTHS[month] }}</option > </select> </div> <div :key="new Date().getTime()" v-if="selectedFrequency === $options.FREQUENCIES.quarterly" class="d-flex flex-column w-50 mr-1" > <small for="from" class="my-auto text-muted"> <translate>Ending Quarter</translate> </small> <select v-model="endQuarter" class="form-control form-control-sm"> <option v-for="month in [1, 2, 3, 4]" :key="'qend' + month" :value="month" >Q{{ month }}</option > </select> </div> <div :class="[ 'd-flex', 'flex-column', 'w-50', { 'ml-1': selectedFrequency !== $options.FREQUENCIES.yearly } ]" > <small for="to" class="my-auto text-muted"> <translate>Ending Year</translate> </small> <select v-model="endYear" class="form-control form-control-sm"> <option v-for="year in $options.YEARS" :key="'end' + year" :value="year" >{{ year }}</option > </select> </div> </div> <div v-if="depthLimitVisible" class="d-flex mt-2" :key="1"> <div class="d-flex flex-column w-50 mr-1"> <small for="from" class="my-auto text-muted"> <translate>Depthlimit 1 [m]</translate> </small> <input id="depthlimit1" v-model.number="depthLimit1" class="form-control form-control-sm" type="number" min="0" step="0.1" /> </div> <div v-if="depthLimitVisible" class="d-flex flex-column w-50 ml-1" :key="2" > <small for="to" class="my-auto text-muted"> <translate>Depthlimit 2 [m]</translate> </small> <input id="depthlimit2" v-model.number="depthLimit2" class="form-control form-control-sm" type="number" min="0" step="0.1" /> </div> </div> <div v-if="widthLimitVisible" class="d-flex mt-2" :key="3"> <div class="d-flex flex-column w-50 mr-1"> <small for="from" class="my-auto text-muted"> <translate>Widthlimit 1 [m]</translate> </small> <input id="widthLimit" v-model.number="widthLimit1" class="form-control form-control-sm" type="number" min="0" /> </div> <div v-if="widthLimitVisible" class="d-flex flex-column w-50 mr-1" :key="4" > <small for="from" class="my-auto text-muted"> <translate>Widthlimit 2 [m]</translate> </small> <input id="widthLimit" v-model.number="widthLimit2" class="form-control form-control-sm" type="number" min="0" /> </div> </div> <div class="d-flex mt-2"> <div class="d-flex flex-column w-50 ml-1"> <small for="from" class="my-auto text-muted"> <translate>From</translate> {{ fromDate | surveyDate }} </small> </div> <div class="d-flex flex-column w-50 ml-1"> <small for="to" class="my-auto text-muted"> <translate>To</translate> {{ toDate | surveyDate }} </small> </div> </div> <div class="mt-3"> <button @click="openFairwaydepthDiagram" :disabled="!isComplete" class="btn btn-info btn-sm d-block w-100" > <translate>Available fairway depth</translate> </button> <button @click="openFairwaydepthLNWLDiagram" :disabled="!isComplete" class="btn btn-info btn-sm d-block w-100 mt-2" > <translate>Available fairway depth vs LNWL</translate> </button> </div> </div> </div> </div> </template> <style scoped> input, select { font-size: 0.8em; } .custom-control { padding-left: 1.2rem; } .custom-control .custom-control-label::before, .custom-control .custom-control-label::after { left: -1.2rem; } </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.kottlaender@intevation.de> * Thomas Junk <thomas.junk@intevation.de> */ import app from "@/main"; import { displayError } from "@/lib/errors"; import { mapState, mapGetters } from "vuex"; import { LIMITINGFACTORS } from "@/store/fairwayavailability"; import { subYears, getMonth, startOfYear, startOfMonth, endOfMonth, endOfYear, format, getQuarter, startOfQuarter, endOfQuarter, setQuarter, getYear } from "date-fns"; const isoFormat = date => { return format(date, "YYYY-MM-DD"); }; export default { data() { return { loading: false, startMonth: null, endMonth: null, startQuarter: null, endQuarter: null, startYear: null, endYear: null }; }, computed: { ...mapState("application", [ "showFairwayDepth", "paneSetup", "showProfiles" ]), ...mapState("fairwayavailability", [ "selectedFairwayAvailabilityFeature", "from", "to", "frequency", "LOS", "depthlimit1", "depthlimit2", "widthlimit1", "widthlimit2" ]), ...mapState("imports", [ "stretches", "sections", "selectedStretchId", "selectedSectionId" ]), ...mapState("bottlenecks", ["bottlenecksList", "selectedBottleneck"]), ...mapGetters("map", ["openLayersMap"]), ...mapGetters("bottlenecks", [ "orderedBottlenecks", "limitingFactorsPerBottleneck" ]), depthLimitVisible() { if (this.type !== this.$options.BOTTLENECK) return true; if ( this.selectedEntry && this.limitingFactorsPerBottleneck[ this.selectedEntry.properties.bottleneck_id ] == this.$options.LIMITINGFACTORS.DEPTH ) return true; return false; }, widthLimitVisible() { if (this.type !== this.$options.BOTTLENECK) return true; if ( this.selectedEntry && this.limitingFactorsPerBottleneck[ this.selectedEntry.properties.bottleneck_id ] == this.$options.LIMITINGFACTORS.WIDTH ) return true; }, limitingFactor() { if (this.type !== this.$options.BOTTLENECK) return; if (this.selectedEntry) return this.limitingFactorsPerBottleneck[ this.selectedEntry.properties.bottleneck_id ]; }, isComplete() { return this.selectedFairwayAvailabilityFeature !== null; }, type: { get() { return this.$store.state.fairwayavailability.type; }, set(type) { this.$store.commit("fairwayavailability/type", type); } }, los: { get() { return this.LOS; }, set(value) { this.$store.commit("fairwayavailability/setLOS", value); } }, fromDate: { get() { return this.from; }, set(value) { this.$store.commit("fairwayavailability/setFrom", value); } }, toDate: { get() { return this.to; }, set(value) { this.$store.commit("fairwayavailability/setTo", value); } }, depthLimit1: { get() { return this.depthlimit1 / 100; }, set(value) { this.$store.commit( "fairwayavailability/setDepthlimit1", Math.round(value * 100) ); } }, depthLimit2: { get() { return this.depthlimit2 / 100; }, set(value) { this.$store.commit( "fairwayavailability/setDepthlimit2", Math.round(value * 100) ); } }, widthLimit1: { get() { return this.widthlimit1; }, set(value) { this.$store.commit("fairwayavailability/setWidthlimit1", value); } }, widthLimit2: { get() { return this.widthlimit2; }, set(value) { this.$store.commit("fairwayavailability/setWidthlimit2", value); } }, selectedFrequency: { get() { return this.frequency; }, set(value) { this.$store.commit("fairwayavailability/setFrequency", value); } }, selectedEntry: { get() { return this.selectedFairwayAvailabilityFeature; }, set(feature) { this.$store.commit( "fairwayavailability/setSelectedFairwayAvailability", feature ); } }, label() { return this.$gettext("Available fairway depth"); }, placeholder() { if (this.type === this.$options.BOTTLENECK) return this.$gettext("Select bottleneck"); if (this.type === this.$options.STRETCH) return this.$gettext("Select stretch"); return this.$gettext("Select section"); } }, watch: { selectedFrequency() { const now = new Date(); switch (this.selectedFrequency) { case this.$options.FREQUENCIES.monthly: this.startMonth = getMonth(now) + 1; this.endMonth = getMonth(now) + 1; this.startYear = getYear(subYears(now, 1)); this.endYear = getYear(now); this.fromDate = isoFormat(subYears(startOfMonth(now), 1)); this.toDate = isoFormat(endOfMonth(now)); break; case this.$options.FREQUENCIES.quarterly: this.startQuarter = this.dateToQuarter(now); this.endQuarter = this.dateToQuarter(now); this.startYear = getYear(subYears(now, 1)); this.endYear = getYear(now); this.fromDate = isoFormat(subYears(startOfQuarter(now), 1)); this.toDate = this.toDate = isoFormat(endOfQuarter(now)); break; case this.$options.FREQUENCIES.yearly: this.startYear = getYear(subYears(now, 1)); this.endYear = getYear(now); this.fromDate = isoFormat(subYears(startOfYear(now), 1)); this.toDate = isoFormat(endOfYear(now)); break; default: throw new Error("undefined frequency"); } }, startQuarter() { this.fromDate = isoFormat( startOfQuarter(this.quarterToDate(this.startYear, this.startQuarter)) ); }, endQuarter() { this.toDate = isoFormat( endOfQuarter(this.quarterToDate(this.endYear, this.endQuarter)) ); }, startMonth() { this.calcStart(); }, startYear() { this.calcStart(); }, endMonth() { this.calcEnd(); }, endYear() { this.calcEnd(); }, depthLimitVisible() { if (this.depthLimitVisible) { this.depthLimit1 = 2.3; this.depthLimit2 = 2.5; } }, widthLimitVisible() { if (this.widthLimitVisible) { this.widthLimit1 = 80; this.widthLimit2 = 150; } }, selectedBottleneck() { this.type = this.$options.BOTTLENECK; this.setSelectedBottleneck(); }, selectedStretchId() { this.type = this.$options.STRETCH; this.setSelectedStretch(); }, selectedSectionId() { this.type = this.$options.SECTION; this.setSelectedSection(); }, showFairwayDepth() { if (this.showFairwayDepth) { this.loading = true; Promise.all([ this.$store.dispatch("bottlenecks/loadBottlenecks"), this.$store.dispatch("bottlenecks/loadBottlenecksList"), this.$store.dispatch("imports/loadStretches"), this.$store.dispatch("imports/loadSections") ]) .then(() => { if (this.selectedBottleneck) this.setSelectedBottleneck(); }) .finally(() => (this.loading = false)); } } }, methods: { takeMeThere() { if (this.type === this.$options.BOTTLENECK) { this.openLayersMap() .getLayer("BOTTLENECKS") .setVisible(true); this.$store.dispatch( "bottlenecks/setSelectedBottleneck", this.selectedFairwayAvailabilityFeature.properties.bottleneck_id ); this.$store.commit( "bottlenecks/setBottleneckForPrint", this.selectedFairwayAvailabilityFeature.properties.objnam // for printing objnam is okay ); } if (this.type === this.$options.STRETCH) { this.openLayersMap() .getLayer("STRETCHES") .setVisible(true); } if (this.type === this.$options.SECTION) { this.openLayersMap() .getLayer("SECTIONS") .setVisible(true); } if (this.selectedFairwayAvailabilityFeature) { this.$store.dispatch("map/moveToFeauture", { feature: this.selectedFairwayAvailabilityFeature, zoom: 17, preventZoomOut: true }); } }, dateToQuarter(date) { return getQuarter(date); }, quarterToDate(year, quarter) { return setQuarter(new Date(year, 1, 1), quarter); }, calcStart() { switch (this.selectedFrequency) { case this.$options.FREQUENCIES.monthly: this.fromDate = isoFormat( startOfMonth(new Date(this.startYear, this.startMonth - 1, 1)) ); break; case this.$options.FREQUENCIES.quarterly: this.fromDate = isoFormat( startOfQuarter( this.quarterToDate(this.startYear, this.startQuarter) ) ); break; case this.$options.FREQUENCIES.yearly: this.fromDate = isoFormat(new Date(this.startYear, 0, 1)); break; default: throw new Error("undefined frequency"); } }, calcEnd() { switch (this.selectedFrequency) { case this.$options.FREQUENCIES.monthly: this.toDate = isoFormat( endOfMonth(new Date(this.endYear, this.endMonth - 1, 1)) ); break; case this.$options.FREQUENCIES.quarterly: this.toDate = isoFormat( isoFormat( endOfQuarter(this.quarterToDate(this.endYear, this.endQuarter)) ) ); break; case this.$options.FREQUENCIES.yearly: this.toDate = isoFormat(endOfYear(new Date(this.endYear, 11, 31))); break; default: throw new Error("undefined frequency"); } }, initDates() { const endDate = new Date(); const startDate = subYears(new Date(), 1); this.startMonth = getMonth(startDate) + 1; this.endMonth = getMonth(endDate) + 1; this.startYear = getYear(startDate); this.endYear = getYear(endDate); this.fromDate = isoFormat(startOfMonth(startDate)); this.toDate = isoFormat(endOfMonth(endDate)); this.startQuarter = this.dateToQuarter(startDate); this.endQuarter = this.dateToQuarter(endDate); }, openFairwaydepthLNWLDiagram() { this.clearInvisibleFormValues(); this.loading = true; this.$store .dispatch("fairwayavailability/loadAvailableFairwayDepthLNWLDiagram", { feature: this.selectedFairwayAvailabilityFeature, from: this.from, to: this.to, frequency: this.frequency, LOS: this.los, type: this.type, depthLimit1: this.depthlimit1, depthLimit2: this.depthlimit2, widthLimit1: Math.round(this.widthLimit1 * 100), widthLimit2: Math.round(this.widthLimit2 * 100), limitingFactor: this.limitingFactor }) .then(() => { this.$store.commit( "application/paneSetup", "AVAILABLEFAIRWAYDEPTHLNWL" ); }) .catch(error => { let message = "Backend not reachable"; if (error.response) { const { status, data } = error.response; message = `${status}: ${data.message || data}`; } displayError({ title: this.$gettext("Backend Error"), message: message }); }) .finally(() => { this.loading = false; }); }, clearInvisibleFormValues() { if (!this.widthLimitVisible) { this.widthLimit1 = null; this.widthLimit2 = null; } if (!this.depthLimitVisible) { this.depthLimit1 = null; this.depthLimit2 = null; } }, openFairwaydepthDiagram() { this.loading = true; this.clearInvisibleFormValues(); this.$store .dispatch("fairwayavailability/loadAvailableFairwayDepth", { feature: this.selectedFairwayAvailabilityFeature, from: this.from, to: this.to, frequency: this.frequency, LOS: this.los, type: this.type, depthLimit1: this.depthlimit1, depthLimit2: this.depthlimit2, widthLimit1: Math.round(this.widthLimit1 * 100), widthLimit2: Math.round(this.widthLimit2 * 100), limitingFactor: this.limitingFactor }) .then(() => { this.$store.commit("application/paneSetup", "AVAILABLEFAIRWAYDEPTH"); }) .catch(error => { let message = "Backend not reachable"; if (error.response) { const { status, data } = error.response; message = `${status}: ${data.message || data}`; } displayError({ title: this.$gettext("Backend Error"), message: message }); }) .finally(() => { this.loading = false; }); }, close() { this.$store.commit("application/showFairwayDepth", false); this.$store.commit("application/showFairwayDepthLNWL", false); }, setSelectedBottleneck() { const bn = this.bottlenecksList.filter( x => x.properties.bottleneck_id === this.selectedBottleneck )[0]; this.$store.commit( "fairwayavailability/setSelectedFairwayAvailability", bn ); }, setSelectedStretch() { const stretch = this.stretches.find(x => x.id === this.selectedStretchId); this.$store.commit( "fairwayavailability/setSelectedFairwayAvailability", stretch ); }, setSelectedSection() { const section = this.sections.find(x => x.id === this.selectedSectionId); this.$store.commit( "fairwayavailability/setSelectedFairwayAvailability", section ); } }, mounted() { this.initDates(); }, BOTTLENECK: "bottleneck", SECTION: "section", STRETCH: "stretch", AVAILABLEFAIRWAYDEPTH: app.$gettext("Available Fairway Depth"), FREQUENCIES: { monthly: app.$gettext("monthly"), quarterly: app.$gettext("quarterly"), yearly: app.$gettext("yearly") }, YEARS: [ 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025, 2026, 2027, 2028, 2029, 2030, 2031, 2032, 2033, 2034, 2035, 2036, 2037, 2038, 2039, 2040, 2041, 2042, 2043, 2044, 2045, 2046, 2047, 2048, 2049, 2050 ], MONTHS: { 1: app.$gettext("January"), 2: app.$gettext("February"), 3: app.$gettext("March"), 4: app.$gettext("April"), 5: app.$gettext("May"), 6: app.$gettext("June"), 7: app.$gettext("July"), 8: app.$gettext("August"), 9: app.$gettext("September"), 10: app.$gettext("October"), 11: app.$gettext("November"), 12: app.$gettext("December") }, LIMITINGFACTORS: LIMITINGFACTORS }; </script>