Mercurial > gemma
view client/src/components/fairway/Fairwayprofile.vue @ 3968:ea4e1ea04e44 diagram-cleanup
client: fairway profile diagram: inlined scaleFairwayProfile method to be more in line with other diagrams
author | Markus Kottlaender <markus@intevation.de> |
---|---|
date | Fri, 12 Jul 2019 16:18:49 +0200 |
parents | 2aaa1948b525 |
children | 2f024d6189ca |
line wrap: on
line source
<template> <div class="d-flex flex-column flex-fill"> <UIBoxHeader icon="chart-area" :title="title" :closeCallback="close" /> <div class="d-flex flex-fill" v-if="openLayersMap()"> <DiagramLegend> <div class="legend"> <span style="background-color: #5995ff; width: 20px; height: 20px;" ></span> Water </div> <div class="legend"> <span :style=" 'width: 16px; height: 16px; background-color: rgba(' + this.getLayerStyle(1).fillColor.join(',') + ',' + this.getLayerStyle(1).fillOpacity + '); border: dotted 2px rgba(' + this.getLayerStyle(1).strokeColor.join(',') + ',' + this.getLayerStyle(1).strokeOpacity + '); background-clip: padding-box; box-sizing: content-box;' " ></span> Fairway (LOS 1) </div> <div class="legend"> <span :style=" 'width: 16px; height: 16px; background-color: rgba(' + this.getLayerStyle(2).fillColor.join(',') + ',' + this.getLayerStyle(2).fillOpacity + '); border: dashed 2px rgba(' + this.getLayerStyle(2).strokeColor.join(',') + ',' + this.getLayerStyle(2).strokeOpacity + '); background-clip: padding-box; box-sizing: content-box;' " ></span> Fairway (LOS 2) </div> <div class="legend"> <span :style=" 'width: 16px; height: 16px; background-color: rgba(' + this.getLayerStyle(3).fillColor.join(',') + ',' + this.getLayerStyle(3).fillOpacity + '); border: solid 2px rgba(' + this.getLayerStyle(3).strokeColor.join(',') + ',' + this.getLayerStyle(3).strokeOpacity + '); background-clip: padding-box; box-sizing: content-box;' " ></span> Fairway (LOS 3) </div> <div class="legend"> <span style="width: 14px; height: 14px; background-color: #4a2f06; border: solid 3px black; background-clip: padding-box; box-sizing: content-box;" ></span> Sediment </div> <div class="legend"> <span style="width: 14px; height: 14px; background-color: rgba(74, 47, 6, 0.6); border: solid 3px #943007; background-clip: padding-box; box-sizing: content-box;" ></span> Sediment (Compare) </div> <div> <select v-model="form.template" @change="applyChange" class="form-control d-block custom-select-sm w-100" > <option v-for="template in templates" :value="template" :key="template.name" > {{ template.name }} </option> </select> <button @click="downloadPDF" type="button" class="btn btn-sm btn-info d-block w-100 mt-2" > <translate>Export to PDF</translate> </button> </div> </DiagramLegend> <div class="d-flex flex-fill justify-content-center align-items-center position-relative" :id="containerId" > <div class="direction-indicator"></div> <div v-if="!fairwayData.length"> <translate>No data available.</translate> </div> </div> </div> </div> </template> <style lang="sass" scoped> .direction-indicator width: 70px height: 0 border-top: dashed 2px #333 position: absolute bottom: 20px left: 115px margin-left: -35px &::after content: "" width: 0 height: 0 border-width: 10px border-top-width: 5px border-bottom-width: 5px border-style: solid border-color: transparent border-left-color: #333 position: absolute right: -20px top: -6px </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> * Markus Kottländer <markus.kottlaender@intevation.de> * Fadi Abbud <fadi.abbud@intevation.de> */ import * as d3 from "d3"; import { mapState, mapGetters } from "vuex"; import { diagram, pdfgen, templateLoader } from "@/lib/mixins"; const GROUND_COLOR = "#4A2F06"; const WATER_COLOR = "#005DFF"; export default { mixins: [diagram, pdfgen, templateLoader], data() { return { containerId: "fairwayprofile-diagram-container" }; }, computed: { ...mapGetters("map", ["openLayersMap"]), ...mapGetters("fairwayprofile", ["totalLength"]), ...mapState("fairwayprofile", [ "additionalSurvey", "currentProfile", "startPoint", "endPoint", "fairwayData", "maxAlt", "selectedWaterLevel" ]), ...mapState("bottlenecks", ["selectedSurvey", "selectedBottleneck"]), ...mapState("application", ["paneSetup"]), title() { let dates = [this.selectedSurvey.date_info]; let waterlevelLabel = this.selectedWaterLevel === "ref" ? this.selectedSurvey.depth_reference : "Current"; if (this.additionalSurvey) dates.push(this.additionalSurvey.date_info); dates.map(d => this.$options.filters.dateTime(d, true)); return `${this.$gettext("Fairwayprofile")}: ${ this.selectedBottleneck } (${dates.join( ", " )}) WL: ${waterlevelLabel} (${this.$options.filters.waterlevel( this.waterlevel )} m)`; }, 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; }, bottleneck() { return this.openLayersMap() .getLayer("BOTTLENECKS") .getSource() .getFeatures() .find(f => f.get("objnam") === this.selectedBottleneck); }, waterlevel() { return this.selectedWaterLevel === "ref" ? this.refWaterlevel : this.bottleneck.get("gm_waterlevel"); }, refWaterlevel() { return this.selectedSurvey.waterlevel_value; } }, watch: { currentData() { this.drawDiagram(); }, additionalData() { this.drawDiagram(); }, width() { this.drawDiagram(); }, height() { this.drawDiagram(); }, waterLevels() { this.drawDiagram(); }, selectedWaterLevel() { this.drawDiagram(); }, fairwayData() { this.drawDiagram(); }, selectedBottleneck() { this.$store.commit("application/paneSetup", "DEFAULT"); } }, methods: { close() { this.$store.commit( "application/paneSetup", this.paneSetup === "COMPARESURVEYS_FAIRWAYPROFILE" ? "COMPARESURVEYS" : "DEFAULT" ); this.$store.dispatch("fairwayprofile/clearCurrentProfile"); }, getLayerStyle(los) { let style = this.openLayersMap() .getLayer("FAIRWAYDIMENSIONSLOS" + los) .getStyle()()[0]; // use spread operator to clone arrays let fillColor = [...style.getFill().getColor()]; let fillOpacity = fillColor.pop(); let strokeColor = [...style.getStroke().getColor()]; let strokeOpacity = strokeColor.pop(); let strokeDash = style.getStroke().getLineDash(); return { fillColor, fillOpacity, strokeColor, strokeOpacity, strokeDash }; }, // Diagram legend addDiagramLegend(position, offset, color) { let x = offset.x, y = offset.y; this.pdf.doc.setFontSize(10); let width = (this.pdf.doc.getStringUnitWidth("Sediment (Compare)") * 10) / (72 / 25.6) + 4; if (["topright", "bottomright"].indexOf(position) !== -1) { x = this.pdf.width - offset.x - width; } if (["bottomright", "bottomleft"].indexOf(position) !== -1) { y = this.pdf.height - offset.y - this.getTextHeight(8); } this.pdf.doc.setTextColor(color); this.pdf.doc.setDrawColor("#5995ff"); this.pdf.doc.setFillColor("#5995ff"); this.pdf.doc.circle(x, y, 2, "FD"); this.pdf.doc.text(x + 3, y + 1, "Water"); this.pdf.doc.setLineDashPattern([0.8], 0); this.pdf.doc.setDrawColor("#0000ff"); this.pdf.doc.setFillColor("#fcfacc"); this.pdf.doc.circle(x, y + 5, 2, "FD"); this.pdf.doc.text(x + 3, y + 6, "Fairway (LOS 1)"); this.pdf.doc.setLineDashPattern([1.8], 0); this.pdf.doc.setFillColor("#fdfce5"); this.pdf.doc.circle(x, y + 10, 2, "FD"); this.pdf.doc.text(x + 3, y + 11, "Fairway (LOS 2)"); this.pdf.doc.setLineDashPattern([], 0); this.pdf.doc.setFillColor("#ffffff"); this.pdf.doc.circle(x, y + 15, 2, "FD"); this.pdf.doc.text(x + 3, y + 16, "Fairway (LOS 3)"); this.pdf.doc.setDrawColor("black"); this.pdf.doc.setFillColor("#4a2e06"); this.pdf.doc.circle(x, y + 20, 2, "FD"); this.pdf.doc.text(x + 3, y + 21, "Sediment"); this.pdf.doc.setDrawColor("#943007"); this.pdf.doc.setFillColor("#928269"); this.pdf.doc.circle(x, y + 25, 2, "FD"); this.pdf.doc.text(x + 3, y + 26, "Sediment (Compare)"); }, getPrintLayout() { return { main: { top: 20, right: 80, bottom: 60, left: 80 } }; }, drawDiagram() { // remove old diagram and exit if necessary data is missing d3.select("#" + this.containerId + " svg").remove(); const elem = document.querySelector("#" + this.containerId); const svgWidth = elem.clientWidth; const svgHeight = elem.clientHeight; const layout = this.getPrintLayout(); this.renderTo({ element: "#" + this.containerId, dimensions: this.getDimensions({ svgWidth: svgWidth, svgHeight: svgHeight, ...layout }) }); }, renderTo({ element, dimensions }) { let svg = d3.select(element).append("svg"); svg.attr("width", "100%"); svg.attr("height", "100%"); const width = dimensions.width; const height = dimensions.mainHeight; const currentData = this.currentData; const additionalData = this.additionalData; const { xScale, yScaleRight, graph } = this.generateScalesAndGraph({ svg, height, width, dimensions }); this.drawWaterlevel({ graph, xScale, yScaleRight, height }); this.drawLabels({ graph, dimensions }); 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 }); } this.drawFairway({ graph, xScale, yScaleRight }); }, drawFairway({ graph, xScale, yScaleRight }) { if (this.fairwayData === undefined) { return; } for (let data of this.fairwayData) { data.coordinates.forEach(coordinates => { 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", `#${this.getLayerStyle(data.los) .fillColor.map(x => { if (x < 10) return "0" + x.toString(16); return x.toString(16); }) .join("")}` ) .attr("fill-opacity", this.getLayerStyle(data.los).fillOpacity) .attr( "stroke", `#${this.getLayerStyle(data.los) .strokeColor.map(x => { if (x < 10) return "0" + x.toString(16); return x.toString(16); }) .join("")}` ) .attr("stroke-opacity", this.getLayerStyle(data.los).strokeOpacity) .attr("stroke-dasharray", this.getLayerStyle(data.los).strokeDash) .attr("d", fairwayArea); }); } }, drawLabels({ graph, dimensions }) { graph .append("text") .attr("transform", ["rotate(-90)"]) .attr("y", dimensions.width + 45) .attr("x", -dimensions.mainHeight / 2) .attr("fill", "black") .style("text-anchor", "middle") .text("Depth [m]"); graph .append("text") .attr("transform", ["rotate(-90)"]) .attr("y", -50) .attr("x", -dimensions.mainHeight / 2) .attr("fill", "black") .style("text-anchor", "middle") .text("Waterlevel [m]"); graph .append("text") .attr("y", 30) .attr("x", 0) .attr("dy", "1em") .attr("fill", "black") .style("text-anchor", "middle") .attr("transform", [ "translate(" + dimensions.width / 2 + "," + dimensions.mainHeight + ")", "rotate(0)" ]) .text("Width [m]"); }, generateScalesAndGraph({ svg, height, width, dimensions }) { let xScale = d3 .scaleLinear() .domain([0, this.totalLength]) .rangeRound([0, width]); let yScaleRight = d3 .scaleLinear() .domain([ this.maxAlt * 1.1 + Math.abs(this.waterlevel - this.refWaterlevel) / 100, -(this.maxAlt * 0.1) ]) .rangeRound([height, 0]); let yScaleLeft = d3 .scaleLinear() .domain([ this.waterlevel - (this.maxAlt * 100 + Math.abs(this.waterlevel - this.refWaterlevel)), this.waterlevel + this.maxAlt * 0.1 * 100 ]) .rangeRound([height, 0]); let xAxis = d3.axisBottom(xScale).ticks(5); let yAxisRight = d3.axisRight(yScaleRight); let yAxisLeft = d3 .axisLeft(yScaleLeft) .tickFormat(d => this.$options.filters.waterlevel(d)); let graph = svg .append("g") .attr( "transform", "translate(" + dimensions.mainMargin.left + "," + dimensions.mainMargin.top + ")" ); graph .append("g") .attr("transform", "translate(0," + height + ")") .call(xAxis) .selectAll(".tick text") .attr("fill", "black") .select(function() { return this.parentNode; }) .selectAll(".tick line") .attr("stroke", "black"); graph .append("g") .attr("transform", "translate(" + width + ",0)") .call(yAxisRight) .selectAll(".tick text") .attr("fill", "black") .select(function() { return this.parentNode; }) .selectAll(".tick line") .attr("stroke", "black"); graph .append("g") .attr("transform", "translate(0 0)") .call(yAxisLeft) .selectAll(".tick text") .attr("fill", "black") .select(function() { return this.parentNode; }) .selectAll(".tick line") .attr("stroke", "black"); graph.selectAll(".domain").attr("stroke", "black"); return { xScale, 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", WATER_COLOR) .attr("stroke", "transparent") .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 => yScaleRight( d.y + Math.abs(this.waterlevel - this.refWaterlevel) / 100 ) ); let profileArea = d3 .area() .x(function(d) { return xScale(d.x); }) .y0(height) .y1(d => yScaleRight( d.y + Math.abs(this.waterlevel - this.refWaterlevel) / 100 ) ); graph .append("path") .datum(part) .attr("fill", color) .attr("stroke", "transparent") .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", 0) .attr("d", profileLine); } } } }; </script>