comparison 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
comparison
equal deleted inserted replaced
2603:8d767359fddb 2604:85f9bf4a6eba
2 <div class="flex-fill diagram-container"></div> 2 <div class="flex-fill diagram-container"></div>
3 </template> 3 </template>
4 4
5 <style lang="sass" scoped> 5 <style lang="sass" scoped>
6 .diagram-container 6 .diagram-container
7 /deep/ .area 7 /deep/
8 stroke: steelblue 8 .area
9 stroke-width: 2 9 stroke: steelblue
10 fill: transparent 10 stroke-width: 2
11 clip-path: url(#clip) 11 fill: transparent
12 12 clip-path: url(#clip)
13 /deep/ .zoom 13
14 cursor: move 14 .zoom
15 fill: none 15 cursor: move
16 pointer-events: all 16 fill: none
17 pointer-events: all
18
19 .hdc-line,
20 .ldc-line,
21 .mw-line
22 stroke-width: 1
23 fill: transparent
24 clip-path: url(#clip)
25 .hdc-line
26 stroke: red
27 .ldc-line
28 stroke: green
29 .mw-line
30 stroke: grey
17 </style> 31 </style>
18 32
19 <script> 33 <script>
20 /* This is Free Software under GNU Affero General Public License v >= 3.0 34 /* This is Free Software under GNU Affero General Public License v >= 3.0
21 * without warranty, see README.md and license for details. 35 * without warranty, see README.md and license for details.
29 * 43 *
30 * Author(s): 44 * Author(s):
31 * Markus Kottländer <markus.kottlaender@intevation.de> 45 * Markus Kottländer <markus.kottlaender@intevation.de>
32 */ 46 */
33 47
34 import { mapState } from "vuex"; 48 import { mapState, mapGetters } from "vuex";
35 import * as d3 from "d3"; 49 import * as d3 from "d3";
36 import debounce from "debounce"; 50 import debounce from "debounce";
37 51
38 export default { 52 export default {
39 computed: { 53 computed: {
40 ...mapState("gauges", ["selectedGauge", "waterlevels"]) 54 ...mapState("gauges", ["waterlevels"]),
55 ...mapGetters("gauges", ["selectedGauge"])
41 }, 56 },
42 watch: { 57 watch: {
43 waterlevels() { 58 waterlevels() {
44 this.drawDiagram(); 59 this.drawDiagram();
45 } 60 }
46 }, 61 },
47 methods: { 62 methods: {
48 drawDiagram() { 63 drawDiagram() {
49 var svgWidth = document.querySelector(".diagram-container").clientWidth; 64 if (!this.selectedGauge || !this.waterlevels.length) return;
50 var svgHeight = document.querySelector(".diagram-container").clientHeight; 65
66 let refWaterLevels = JSON.parse(
67 this.selectedGauge.properties.reference_water_levels
68 );
69
70 let svgWidth = document.querySelector(".diagram-container").clientWidth;
71 let svgHeight = document.querySelector(".diagram-container").clientHeight;
51 d3.select(".diagram-container svg").remove(); 72 d3.select(".diagram-container svg").remove();
52 var svg = d3.select(".diagram-container").append("svg"); 73 let svg = d3.select(".diagram-container").append("svg");
53 svg.attr("width", "100%").attr("height", "100%"); 74 svg.attr("width", "100%").attr("height", "100%");
54 let margin = { top: 20, right: 20, bottom: 110, left: 40 }, 75 let margin = { top: 20, right: 20, bottom: 110, left: 40 },
55 margin2 = { 76 margin2 = {
56 top: svgHeight - margin.top - 50, 77 top: svgHeight - margin.top - 50,
57 right: 20, 78 right: 20,
60 }, 81 },
61 width = +svgWidth - margin.left - margin.right, 82 width = +svgWidth - margin.left - margin.right,
62 height = +svgHeight - margin.top - margin.bottom, 83 height = +svgHeight - margin.top - margin.bottom,
63 height2 = +svgHeight - margin2.top - margin2.bottom; 84 height2 = +svgHeight - margin2.top - margin2.bottom;
64 85
65 var x = d3.scaleTime().range([0, width]), 86 let x = d3.scaleTime().range([0, width]),
66 x2 = d3.scaleTime().range([0, width]), 87 x2 = d3.scaleTime().range([0, width]),
67 y = d3.scaleLinear().range([height, 0]), 88 y = d3.scaleLinear().range([height, 0]),
68 y2 = d3.scaleLinear().range([height2, 0]); 89 y2 = d3.scaleLinear().range([height2, 0]);
69 90
70 var xAxis = d3.axisBottom(x), 91 let xAxis = d3.axisBottom(x),
71 xAxis2 = d3.axisBottom(x2), 92 xAxis2 = d3.axisBottom(x2),
72 yAxis = d3.axisLeft(y); 93 yAxis = d3.axisLeft(y);
73 94
74 var brush = d3 95 // find min/max values for the waterlevel axis
96 // based on hdc/ldc +/- 100 cm
97 let xMinMax = d3.extent(
98 [
99 ...this.waterlevels,
100 { waterlevel: refWaterLevels.HDC + 100 },
101 { waterlevel: Math.max(refWaterLevels.LDC - 100, 0) }
102 ],
103 function(d) {
104 return d.waterlevel;
105 }
106 );
107
108 x.domain(
109 d3.extent(this.waterlevels, function(d) {
110 return d.date;
111 })
112 );
113 y.domain(xMinMax);
114 x2.domain(x.domain());
115 y2.domain(y.domain());
116
117 let brush = d3
75 .brushX() 118 .brushX()
119 .handleSize(4)
76 .extent([[0, 0], [width, height2]]) 120 .extent([[0, 0], [width, height2]])
77 .on("brush end", brushed); 121 .on("brush end", brushed);
78 122
79 var zoom = d3 123 let zoom = d3
80 .zoom() 124 .zoom()
81 .scaleExtent([1, Infinity]) 125 .scaleExtent([1, Infinity])
82 .translateExtent([[0, 0], [width, height]]) 126 .translateExtent([[0, 0], [width, height]])
83 .extent([[0, 0], [width, height]]) 127 .extent([[0, 0], [width, height]])
84 .on("zoom", zoomed); 128 .on("zoom", zoomed);
85 129
86 var area = d3 130 // waterlevel line in big chart
131 let area = d3
87 .line() 132 .line()
88 .curve(d3.curveMonotoneX) 133 .curve(d3.curveMonotoneX)
89 .x(function(d) { 134 .x(function(d) {
90 return x(d.date); 135 return x(d.date);
91 }) 136 })
92 .y(function(d) { 137 .y(function(d) {
93 return y(d.waterlevel); 138 return y(d.waterlevel);
94 }); 139 });
95 140
96 var area2 = d3 141 // waterlevel line in small chart
142 let area2 = d3
97 .line() 143 .line()
98 .curve(d3.curveMonotoneX) 144 .curve(d3.curveMonotoneX)
99 .x(function(d) { 145 .x(function(d) {
100 return x2(d.date); 146 return x2(d.date);
101 }) 147 })
102 .y(function(d) { 148 .y(function(d) {
103 return y2(d.waterlevel); 149 return y2(d.waterlevel);
104 }); 150 });
105 151
152 let refWaterlevelLine = d3
153 .line()
154 .x(d => x(d.x))
155 .y(d => y(d.y));
156
106 svg 157 svg
107 .append("defs") 158 .append("defs")
108 .append("clipPath") 159 .append("clipPath")
109 .attr("id", "clip") 160 .attr("id", "clip")
110 .append("rect") 161 .append("rect")
111 .attr("width", width) 162 .attr("width", width)
112 .attr("height", height); 163 .attr("height", height);
113 164
114 var focus = svg 165 let focus = svg
115 .append("g") 166 .append("g")
116 .attr("class", "focus") 167 .attr("class", "focus")
117 .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 168 .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
118 169
119 var context = svg 170 let context = svg
120 .append("g") 171 .append("g")
121 .attr("class", "context") 172 .attr("class", "context")
122 .attr( 173 .attr(
123 "transform", 174 "transform",
124 "translate(" + margin2.left + "," + margin2.top + ")" 175 "translate(" + margin2.left + "," + margin2.top + ")"
125 ); 176 );
126 177
127 x.domain(
128 d3.extent(this.waterlevels, function(d) {
129 return d.date;
130 })
131 );
132 y.domain([0, 5]);
133 x2.domain(x.domain());
134 y2.domain(y.domain());
135
136 focus 178 focus
137 .append("path") 179 .append("path")
138 .datum(this.waterlevels) 180 .datum(this.waterlevels)
139 .attr("class", "area") 181 .attr("class", "area")
140 .attr("d", area); 182 .attr("d", area);
183
184 focus
185 .append("path")
186 .datum([
187 { x: 0, y: refWaterLevels.HDC },
188 {
189 x: this.waterlevels[this.waterlevels.length - 1].date,
190 y: refWaterLevels.HDC
191 }
192 ])
193 .attr("class", "hdc-line")
194 .attr("d", refWaterlevelLine);
195
196 focus
197 .append("path")
198 .datum([
199 { x: 0, y: refWaterLevels.LDC },
200 {
201 x: this.waterlevels[this.waterlevels.length - 1].date,
202 y: refWaterLevels.LDC
203 }
204 ])
205 .attr("class", "ldc-line")
206 .attr("d", refWaterlevelLine);
207
208 focus
209 .append("path")
210 .datum([
211 { x: 0, y: refWaterLevels.MW },
212 {
213 x: this.waterlevels[this.waterlevels.length - 1].date,
214 y: refWaterLevels.MW
215 }
216 ])
217 .attr("class", "mw-line")
218 .attr("d", refWaterlevelLine);
141 219
142 focus 220 focus
143 .append("g") 221 .append("g")
144 .attr("class", "axis axis--x") 222 .attr("class", "axis axis--x")
145 .attr("transform", "translate(0," + height + ")") 223 .attr("transform", "translate(0," + height + ")")
177 .call(zoom); 255 .call(zoom);
178 256
179 function brushed() { 257 function brushed() {
180 if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") 258 if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom")
181 return; // ignore brush-by-zoom 259 return; // ignore brush-by-zoom
182 var s = d3.event.selection || x2.range(); 260 let s = d3.event.selection || x2.range();
183 x.domain(s.map(x2.invert, x2)); 261 x.domain(s.map(x2.invert, x2));
184 focus.select(".area").attr("d", area); 262 focus.select(".area").attr("d", area);
185 focus.select(".axis--x").call(xAxis); 263 focus.select(".axis--x").call(xAxis);
186 svg 264 svg
187 .select(".zoom") 265 .select(".zoom")
192 } 270 }
193 271
194 function zoomed() { 272 function zoomed() {
195 if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") 273 if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush")
196 return; // ignore zoom-by-brush 274 return; // ignore zoom-by-brush
197 var t = d3.event.transform; 275 let t = d3.event.transform;
198 x.domain(t.rescaleX(x2).domain()); 276 x.domain(t.rescaleX(x2).domain());
199 focus.select(".area").attr("d", area); 277 focus.select(".area").attr("d", area);
200 focus.select(".axis--x").call(xAxis); 278 focus.select(".axis--x").call(xAxis);
201 context.select(".brush").call(brush.move, x.range().map(t.invertX, t)); 279 context.select(".brush").call(brush.move, x.range().map(t.invertX, t));
202 } 280 }