Mercurial > gemma
view client/src/components/fairway/Fairwayprofile.vue @ 2350:ed6b0a43d42f
staging: open detailsview for UBN too
author | Thomas Junk <thomas.junk@intevation.de> |
---|---|
date | Wed, 20 Feb 2019 10:58:32 +0100 |
parents | 940ae7c20326 |
children | da75385887b9 |
line wrap: on
line source
<template> <div :class="['position-relative', { show: showSplitscreen }]"> <button class="rounded-bottom bg-white border-0 position-absolute splitscreen-toggle" @click="$store.commit('application/showSplitscreen', false)" v-if="showSplitscreen" > <font-awesome-icon icon="angle-down" /> </button> <button class="rounded-bottom bg-white border-0 position-absolute clear-selection" @click="$store.dispatch('fairwayprofile/clearSelection')" v-if="showSplitscreen" > <font-awesome-icon icon="times" class="pointer" /> </button> <div class="profile bg-white position-relative d-flex flex-column"> <h5 class="headline border-bottom mb-0 py-2" v-if="selectedBottleneck && selectedSurvey" > {{ selectedBottleneck }} ({{ selectedSurvey.date_info }}) </h5> <div class="d-flex flex-fill"> <div class="loading d-flex justify-content-center align-items-center" v-if="surveysLoading || profileLoading" > <font-awesome-icon icon="spinner" spin /> </div> <div class="fairwayprofile m-3 mt-0 bg-white flex-grow-1"></div> </div> </div> </div> </template> <style lang="scss" scoped> .profile { width: 100vw; height: 0; overflow: hidden; z-index: 2; } .splitscreen-toggle, .clear-selection { width: 2rem; height: 2rem; margin-top: 8px; z-index: 3; outline: none; } .splitscreen-toggle svg path, .clear-selection svg path { fill: #666; } .splitscreen-toggle { right: 2.5rem; } .clear-selection { right: 0.5rem; } .show .profile { height: 50vh; } .loading { background: rgba(255, 255, 255, 0.96); position: absolute; z-index: 99; top: 0; right: 0; bottom: 0; left: 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, mapGetters } from "vuex"; import debounce from "debounce"; const GROUND_COLOR = "#4A2F06"; export default { name: "fairwayprofile", data() { return { coordinatesInput: "", coordinatesSelect: null, cutLabel: "", showLabelInput: false, width: null, height: null, margin: { top: 20, right: 40, bottom: 30, left: 40 } }; }, computed: { ...mapGetters("fairwayprofile", ["totalLength"]), ...mapState("application", ["showSplitscreen"]), ...mapState("fairwayprofile", [ "startPoint", "endPoint", "currentProfile", "additionalSurvey", "minAlt", "maxAlt", "fairwayData", "waterLevels", "selectedWaterLevel", "profileLoading" ]), ...mapState("bottlenecks", [ "selectedBottleneck", "selectedSurvey", "surveysLoading" ]), currentData() { if ( !this.selectedSurvey || !this.currentProfile.hasOwnProperty(this.selectedSurvey.date_info) ) return []; return this.currentProfile[this.selectedSurvey.date_info].points; }, additionalData() { if ( !this.additionalSurvey || !this.currentProfile.hasOwnProperty(this.additionalSurvey.date_info) ) return []; return this.currentProfile[this.additionalSurvey.date_info].points; }, waterColor() { const result = this.waterLevels.find( x => x.level === this.selectedWaterLevel ); return result.color; }, xScale() { return [0, this.totalLength]; }, yScaleLeft() { const hi = Math.max(this.maxAlt, this.selectedWaterLevel); return [this.minAlt, hi]; }, yScaleRight() { const DELTA = this.maxAlt * 1.1 - this.maxAlt; return [this.maxAlt * 1 + DELTA, -DELTA]; } }, watch: { currentData() { this.drawDiagram(); }, additionalData() { this.drawDiagram(); }, width() { this.drawDiagram(); }, height() { this.drawDiagram(); }, waterLevels() { this.drawDiagram(); }, selectedWaterLevel() { this.drawDiagram(); }, fairwayData() { this.drawDiagram(); } }, methods: { drawDiagram() { this.coordinatesSelect = null; const chartDiv = document.querySelector(".fairwayprofile"); d3.select(".fairwayprofile svg").remove(); this.scaleFairwayProfile(); 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, graph } = this.generateCoordinates( svg, height, width ); if (!this.height || !this.width) return; // do not try to render when height and width are unknown this.drawWaterlevel({ graph, xScale, yScaleRight, height }); this.drawLabels({ graph, height }); this.drawFairway({ graph, xScale, yScaleRight }); if (currentData) { this.drawProfile({ graph, xScale, yScaleRight, currentData, height, color: GROUND_COLOR, strokeColor: "black", opacity: 1 }); } if (additionalData) { this.drawProfile({ graph, xScale, yScaleRight, currentData: additionalData, height, color: GROUND_COLOR, strokeColor: "#943007", opacity: 0.6 }); } }, drawFairway({ graph, xScale, yScaleRight }) { if (this.fairwayData === undefined) { return; } for (let data of this.fairwayData) { const [startPoint, endPoint, depth] = data.coordinates[0]; const style = data.style(); 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("fill-opacity", 0.65) .attr("stroke", style[0].getStroke().getColor()) .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-opacity", 0.65) .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); } }, scaleFairwayProfile() { if (!document.querySelector(".fairwayprofile")) return; const clientHeight = document.querySelector(".fairwayprofile") .clientHeight; const clientWidth = document.querySelector(".fairwayprofile").clientWidth; if (!clientHeight || !clientWidth) return; this.height = clientHeight; this.width = clientWidth; } }, created() { window.addEventListener("resize", debounce(this.drawDiagram), 100); }, mounted() { this.drawDiagram(); }, updated() { this.scaleFairwayProfile(); }, destroyed() { window.removeEventListener("resize", debounce(this.drawDiagram)); } }; </script>