Mercurial > gemma
view client/src/fairway/Fairwayprofile.vue @ 1146:74e180ad3d6b
fairway profile UI improvements
splitscreen button position at top of profile container
bottleneck name and survey date as headline in profile container
moved logout button to sidebar menu to avoid unnecessary overlapping
author | Markus Kottlaender <markus@intevation.de> |
---|---|
date | Tue, 13 Nov 2018 11:12:12 +0100 |
parents | dc3f0277628a |
children | fb5c83d4ea1d |
line wrap: on
line source
<template> <div :class="['position-relative', {show: showSplitscreen}]" v-if="Object.keys(currentProfile).length"> <button class="rounded bg-white border-0 position-absolute splitscreen-toggle shadow" @click="$store.commit('application/showSplitscreen', false)" v-if="showSplitscreen"> <i class="fa fa-angle-down"></i> </button> <button class="rounded bg-white border-0 position-absolute clear-selection shadow" @click="$store.dispatch('fairwayprofile/clearSelection');" v-if="showSplitscreen"> <i class="fa fa-times text-danger"></i> </button> <div class="profile d-flex flex-column"> <h5 class="mb-0 mt-2">{{ selectedBottleneck }} ({{ selectedSurvey.date_info }})</h5> <div class="d-flex flex-fill"> <div class="fairwayprofile flex-fill"></div> <div class="additionalsurveys d-flex flex-column"> <small class="label">Available Additional Surveys</small> <select v-model="additionalSurvey"> <option value="">None</option> <option v-for="survey in additionalSurveys" :key="survey.date_info" :value="survey" >{{survey.date_info}}</option> </select> <small class="mt-2"> <b>Start:</b> <br> Lat: {{ startPoint[1] }} <br> Lon: {{ startPoint[0] }} <br> <b>End:</b> <br> Lat: {{ endPoint[1] }} <br> Lon: {{ endPoint[0] }} <br> </small> </div> </div> </div> </div> </template> <style lang="sass" scoped> .profile background-color: white width: 100vw height: 0 overflow: hidden position: relative z-index: 2 .splitscreen-toggle, .clear-selection top: -$icon-height right: $icon-width + $offset + $offset width: $icon-width height: $icon-height margin-top: 2px z-index: 1 outline: none .clear-selection right: $offset .show .profile height: 50vh .label margin-bottom: $small-offset .waterlevelselection margin-top: $large-offset margin-right: $large-offset .additionalsurveys width: 200px margin-top: $large-offset margin-bottom: auto margin-right: $large-offset margin-left: auto .additionalsurveys input margin-right: $small-offset .fairwayprofile background-color: white margin: $offset margin-top: 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): * Thomas Junk <thomas.junk@intevation.de> */ import * as d3 from "d3"; import { mapState } from "vuex"; import { displayError } from "../application/lib/errors.js"; const GROUND_COLOR = "#4A2F06"; export default { name: "fairwayprofile", props: [ "width", "height", "xScale", "yScaleLeft", "yScaleRight", "margin", "additionalSurveys" ], computed: { ...mapState("application", ["showSplitscreen"]), ...mapState("fairwayprofile", [ "startPoint", "endPoint", "currentProfile", "minAlt", "maxAlt", "totalLength", "fairwayCoordinates", "waterLevels", "selectedWaterLevel" ]), ...mapState("bottlenecks", ["selectedBottleneck", "selectedSurvey"]), additionalSurvey: { get() { return this.$store.getters["fairwayprofile/additionalSurvey"]; }, set(value) { this.$store.commit("fairwayprofile/setAdditionalSurvey", value); this.selectAdditionalSurveyData(); } }, currentData() { if ( !this.selectedSurvey || !this.currentProfile.hasOwnProperty(this.selectedSurvey.date_info) ) return []; return this.currentProfile[this.selectedSurvey.date_info]; }, additionalData() { if ( !this.additionalSurvey || !this.currentProfile.hasOwnProperty(this.additionalSurvey.date_info) ) return []; return this.currentProfile[this.additionalSurvey.date_info]; }, waterColor() { const result = this.waterLevels.find( x => x.level === this.selectedWaterLevel ); return result.color; } }, data() { return { wait: false }; }, watch: { showSplitscreen() { this.drawDiagram(); }, currentData() { this.drawDiagram(); }, width() { this.drawDiagram(); }, height() { this.drawDiagram(); }, waterLevels() { this.drawDiagram(); }, selectedWaterLevel() { this.drawDiagram(); }, fairwayCoordinates() { this.drawDiagram(); } }, methods: { selectAdditionalSurveyData() { if ( !this.additionalSurvey || this.wait || this.currentProfile[this.additionalSurvey.date_info] ) { this.drawDiagram(); return; } this.$store .dispatch("fairwayprofile/loadProfile", this.additionalSurvey) .then(() => { this.wait = false; }) .catch(error => { this.wait = false; let status = "ERROR"; let data = error; const response = error.response; if (response) { status = response.status; data = response.data; } displayError({ title: "Backend Error", message: `${status}: ${data.message || data}` }); }); }, drawDiagram() { const chartDiv = document.querySelector(".fairwayprofile"); d3.select("svg").remove(); let svg = d3.select(chartDiv).append("svg"); svg.attr("width", this.width); svg.attr("height", this.height); const width = this.width - this.margin.right - 1.5 * this.margin.left; const height = this.height - this.margin.top - 2 * this.margin.bottom; const currentData = this.currentData; const additionalData = this.additionalData; const { xScale, yScaleRight, yScaleLeft, graph } = this.generateCoordinates(svg, height, width); this.drawWaterlevel({ graph, xScale, yScaleRight, height, width }); if (currentData) { this.drawProfile({ graph, xScale, yScaleRight, currentData, height, width, color: GROUND_COLOR, strokeColor: "black", opacity: 1 }); } if (additionalData) { this.drawProfile({ graph, xScale, yScaleRight, currentData: additionalData, height, width, color: GROUND_COLOR, strokeColor: "#943007", opacity: 0.6 }); } this.drawLabels({ graph, xScale, yScaleLeft, currentData, height, width }); this.drawFairway({ graph, xScale, yScaleRight, currentData, height, width }); }, drawFairway({ graph, xScale, yScaleRight }) { for (let coordinates of this.fairwayCoordinates) { const [startPoint, endPoint, depth] = coordinates; let fairwayArea = d3 .area() .x(function(d) { return xScale(d.x); }) .y0(yScaleRight(0)) .y1(function(d) { return yScaleRight(d.y); }); graph .append("path") .datum([{ x: startPoint, y: depth }, { x: endPoint, y: depth }]) .attr("fill", "#002AFF") .attr("stroke-opacity", 0.65) .attr("fill-opacity", 0.65) .attr("stroke", "#FFD20D") .attr("d", fairwayArea); } }, drawLabels({ graph, height }) { graph .append("text") .attr("transform", ["rotate(-90)"]) .attr("y", this.width - 60) .attr("x", -(this.height - this.margin.top - this.margin.bottom) / 2) .attr("dy", "1em") .attr("fill", "black") .style("text-anchor", "middle") .text("Depth [m]"); graph .append("text") .attr("y", 0 - this.margin.left) .attr("x", 0 - height / 4) .attr("dy", "1em") .attr("fill", "black") .style("text-anchor", "middle") .attr("transform", [ "translate(" + this.width / 2 + "," + this.height + ")", "rotate(0)" ]) .text("Width [m]"); }, generateCoordinates(svg, height, width) { let xScale = d3 .scaleLinear() .domain(this.xScale) .rangeRound([0, width]); xScale.ticks(5); let yScaleLeft = d3 .scaleLinear() .domain(this.yScaleLeft) .rangeRound([height, 0]); let yScaleRight = d3 .scaleLinear() .domain(this.yScaleRight) .rangeRound([height, 0]); let xAxis = d3.axisBottom(xScale); let yAxis2 = d3.axisRight(yScaleRight); let graph = svg .append("g") .attr( "transform", "translate(" + this.margin.left + "," + this.margin.top + ")" ); graph .append("g") .attr("transform", "translate(0," + height + ")") .call(xAxis.ticks(5)); graph .append("g") .attr("transform", "translate(" + width + ",0)") .call(yAxis2); return { xScale, yScaleLeft, yScaleRight, graph }; }, drawWaterlevel({ graph, xScale, yScaleRight, height }) { let waterArea = d3 .area() .x(function(d) { return xScale(d.x); }) .y0(height) .y1(function(d) { return yScaleRight(d.y); }); graph .append("path") .datum([{ x: 0, y: 0 }, { x: this.totalLength, y: 0 }]) .attr("fill", this.waterColor) .attr("stroke", this.waterColor) .attr("d", waterArea); }, drawProfile({ graph, xScale, yScaleRight, currentData, height, color, strokeColor, opacity }) { for (let part of currentData) { let profileLine = d3 .line() .x(d => { return xScale(d.x); }) .y(d => { return yScaleRight(d.y); }); let profileArea = d3 .area() .x(function(d) { return xScale(d.x); }) .y0(height) .y1(function(d) { return yScaleRight(d.y); }); graph .append("path") .datum(part) .attr("fill", color) .attr("stroke", color) .attr("stroke-width", 3) .attr("stroke-opacity", opacity) .attr("fill-opacity", opacity) .attr("d", profileArea); graph .append("path") .datum(part) .attr("fill", "none") .attr("stroke", strokeColor) .attr("stroke-linejoin", "round") .attr("stroke-linecap", "round") .attr("stroke-width", 3) .attr("stroke-opacity", opacity) .attr("fill-opacity", opacity) .attr("d", profileLine); } } }, mounted() { this.drawDiagram(); } }; </script>