Mercurial > gemma
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 } |