Mercurial > gemma
view client/src/components/gauge/Waterlevel.vue @ 2604:85f9bf4a6eba
client: gauge waterlevel diagram: draw reference waterlevels
author | Markus Kottlaender <markus@intevation.de> |
---|---|
date | Tue, 12 Mar 2019 17:08:07 +0100 |
parents | 8774054959a7 |
children | ee60096c7eb6 |
line wrap: on
line source
<template> <div class="flex-fill diagram-container"></div> </template> <style lang="sass" scoped> .diagram-container /deep/ .area stroke: steelblue stroke-width: 2 fill: transparent clip-path: url(#clip) .zoom cursor: move fill: none pointer-events: all .hdc-line, .ldc-line, .mw-line stroke-width: 1 fill: transparent clip-path: url(#clip) .hdc-line stroke: red .ldc-line stroke: green .mw-line stroke: grey </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> */ import { mapState, mapGetters } from "vuex"; import * as d3 from "d3"; import debounce from "debounce"; export default { computed: { ...mapState("gauges", ["waterlevels"]), ...mapGetters("gauges", ["selectedGauge"]) }, watch: { waterlevels() { this.drawDiagram(); } }, methods: { drawDiagram() { if (!this.selectedGauge || !this.waterlevels.length) return; let refWaterLevels = JSON.parse( this.selectedGauge.properties.reference_water_levels ); let svgWidth = document.querySelector(".diagram-container").clientWidth; let svgHeight = document.querySelector(".diagram-container").clientHeight; d3.select(".diagram-container svg").remove(); let svg = d3.select(".diagram-container").append("svg"); svg.attr("width", "100%").attr("height", "100%"); let margin = { top: 20, right: 20, bottom: 110, left: 40 }, margin2 = { top: svgHeight - margin.top - 50, right: 20, bottom: 30, left: 40 }, width = +svgWidth - margin.left - margin.right, height = +svgHeight - margin.top - margin.bottom, height2 = +svgHeight - margin2.top - margin2.bottom; let x = d3.scaleTime().range([0, width]), x2 = d3.scaleTime().range([0, width]), y = d3.scaleLinear().range([height, 0]), y2 = d3.scaleLinear().range([height2, 0]); let xAxis = d3.axisBottom(x), xAxis2 = d3.axisBottom(x2), yAxis = d3.axisLeft(y); // find min/max values for the waterlevel axis // based on hdc/ldc +/- 100 cm let xMinMax = d3.extent( [ ...this.waterlevels, { waterlevel: refWaterLevels.HDC + 100 }, { waterlevel: Math.max(refWaterLevels.LDC - 100, 0) } ], function(d) { return d.waterlevel; } ); x.domain( d3.extent(this.waterlevels, function(d) { return d.date; }) ); y.domain(xMinMax); x2.domain(x.domain()); y2.domain(y.domain()); let brush = d3 .brushX() .handleSize(4) .extent([[0, 0], [width, height2]]) .on("brush end", brushed); let zoom = d3 .zoom() .scaleExtent([1, Infinity]) .translateExtent([[0, 0], [width, height]]) .extent([[0, 0], [width, height]]) .on("zoom", zoomed); // waterlevel line in big chart let area = d3 .line() .curve(d3.curveMonotoneX) .x(function(d) { return x(d.date); }) .y(function(d) { return y(d.waterlevel); }); // waterlevel line in small chart let area2 = d3 .line() .curve(d3.curveMonotoneX) .x(function(d) { return x2(d.date); }) .y(function(d) { return y2(d.waterlevel); }); let refWaterlevelLine = d3 .line() .x(d => x(d.x)) .y(d => y(d.y)); svg .append("defs") .append("clipPath") .attr("id", "clip") .append("rect") .attr("width", width) .attr("height", height); let focus = svg .append("g") .attr("class", "focus") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); let context = svg .append("g") .attr("class", "context") .attr( "transform", "translate(" + margin2.left + "," + margin2.top + ")" ); focus .append("path") .datum(this.waterlevels) .attr("class", "area") .attr("d", area); focus .append("path") .datum([ { x: 0, y: refWaterLevels.HDC }, { x: this.waterlevels[this.waterlevels.length - 1].date, y: refWaterLevels.HDC } ]) .attr("class", "hdc-line") .attr("d", refWaterlevelLine); focus .append("path") .datum([ { x: 0, y: refWaterLevels.LDC }, { x: this.waterlevels[this.waterlevels.length - 1].date, y: refWaterLevels.LDC } ]) .attr("class", "ldc-line") .attr("d", refWaterlevelLine); focus .append("path") .datum([ { x: 0, y: refWaterLevels.MW }, { x: this.waterlevels[this.waterlevels.length - 1].date, y: refWaterLevels.MW } ]) .attr("class", "mw-line") .attr("d", refWaterlevelLine); focus .append("g") .attr("class", "axis axis--x") .attr("transform", "translate(0," + height + ")") .call(xAxis); focus .append("g") .attr("class", "axis axis--y") .call(yAxis); context .append("path") .datum(this.waterlevels) .attr("class", "area") .attr("d", area2); context .append("g") .attr("class", "axis axis--x") .attr("transform", "translate(0," + height2 + ")") .call(xAxis2); context .append("g") .attr("class", "brush") .call(brush) .call(brush.move, x.range()); svg .append("rect") .attr("class", "zoom") .attr("width", width) .attr("height", height) .attr("transform", "translate(" + margin.left + "," + margin.top + ")") .call(zoom); function brushed() { if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") return; // ignore brush-by-zoom let s = d3.event.selection || x2.range(); x.domain(s.map(x2.invert, x2)); focus.select(".area").attr("d", area); focus.select(".axis--x").call(xAxis); svg .select(".zoom") .call( zoom.transform, d3.zoomIdentity.scale(width / (s[1] - s[0])).translate(-s[0], 0) ); } function zoomed() { if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") return; // ignore zoom-by-brush let t = d3.event.transform; x.domain(t.rescaleX(x2).domain()); focus.select(".area").attr("d", area); focus.select(".axis--x").call(xAxis); context.select(".brush").call(brush.move, x.range().map(t.invertX, t)); } } }, created() { window.addEventListener("resize", debounce(this.drawDiagram), 100); }, mounted() { this.drawDiagram(); }, updated() { this.drawDiagram(); } }; </script>