Mercurial > gemma
diff client/src/components/gauge/Waterlevel.vue @ 2590:1686ec185155
client: added gauge waterlevel example diagram
author | Markus Kottlaender <markus@intevation.de> |
---|---|
date | Tue, 12 Mar 2019 08:37:09 +0100 |
parents | |
children | 8774054959a7 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/src/components/gauge/Waterlevel.vue Tue Mar 12 08:37:09 2019 +0100 @@ -0,0 +1,222 @@ +<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) + + /deep/ .zoom + cursor: move + fill: none + pointer-events: all +</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 } from "vuex"; +import * as d3 from "d3"; +import debounce from "debounce"; + +export default { + computed: { + ...mapState("gauges", ["selectedGauge"]), + waterlevels() { + let data = []; + let waterlevel = 2.5; + for (let i = 1; i <= 365; i++) { + let date = new Date(); + date.setFullYear(2018); + date.setDate(date.getDate() + i); + waterlevel *= Math.random() * (1.02 - 0.98) + 0.98; + data.push({ date, waterlevel }); + } + return data; + } + }, + methods: { + drawDiagram() { + var svgWidth = document.querySelector(".diagram-container").clientWidth; + var svgHeight = document.querySelector(".diagram-container").clientHeight; + d3.select(".diagram-container svg").remove(); + var 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; + + var x = d3.scaleTime().range([0, width]), + x2 = d3.scaleTime().range([0, width]), + y = d3.scaleLinear().range([height, 0]), + y2 = d3.scaleLinear().range([height2, 0]); + + var xAxis = d3.axisBottom(x), + xAxis2 = d3.axisBottom(x2), + yAxis = d3.axisLeft(y); + + var brush = d3 + .brushX() + .extent([[0, 0], [width, height2]]) + .on("brush end", brushed); + + var zoom = d3 + .zoom() + .scaleExtent([1, Infinity]) + .translateExtent([[0, 0], [width, height]]) + .extent([[0, 0], [width, height]]) + .on("zoom", zoomed); + + var area = d3 + .line() + .curve(d3.curveMonotoneX) + .x(function(d) { + return x(d.date); + }) + .y(function(d) { + return y(d.waterlevel); + }); + + var area2 = d3 + .line() + .curve(d3.curveMonotoneX) + .x(function(d) { + return x2(d.date); + }) + .y(function(d) { + return y2(d.waterlevel); + }); + + svg + .append("defs") + .append("clipPath") + .attr("id", "clip") + .append("rect") + .attr("width", width) + .attr("height", height); + + var focus = svg + .append("g") + .attr("class", "focus") + .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); + + var context = svg + .append("g") + .attr("class", "context") + .attr( + "transform", + "translate(" + margin2.left + "," + margin2.top + ")" + ); + + x.domain( + d3.extent(this.waterlevels, function(d) { + return d.date; + }) + ); + y.domain([0, 5]); + x2.domain(x.domain()); + y2.domain(y.domain()); + + focus + .append("path") + .datum(this.waterlevels) + .attr("class", "area") + .attr("d", area); + + 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 + var 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 + var 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>