comparison client/src/components/gauge/Waterlevel.vue @ 2684:c4da269238a4

client: waterlevel diagram: visualize data gaps
author Markus Kottlaender <markus@intevation.de>
date Fri, 15 Mar 2019 12:43:30 +0100
parents 7fd47d9641ac
children 8f919fe629f9
comparison
equal deleted inserted replaced
2680:bd615f978164 2684:c4da269238a4
3 </template> 3 </template>
4 4
5 <style lang="sass" scoped> 5 <style lang="sass" scoped>
6 .diagram-container 6 .diagram-container
7 /deep/ 7 /deep/
8 .line 8 .main
9 stroke: steelblue 9 .line
10 stroke-width: 2 10 clip-path: url(#clip)
11 fill: transparent 11 .nav
12 clip-path: url(#clip) 12 .line
13 stroke: steelblue
14 stroke-width: 2
15 fill: transparent
16 clip-path: url(#clip)
13 17
14 .hdc-line, 18 .hdc-line,
15 .ldc-line, 19 .ldc-line,
16 .mw-line 20 .mw-line
17 stroke-width: 1 21 stroke-width: 1
61 * Author(s): 65 * Author(s):
62 * Markus Kottländer <markus.kottlaender@intevation.de> 66 * Markus Kottländer <markus.kottlaender@intevation.de>
63 */ 67 */
64 68
65 import { mapState, mapGetters } from "vuex"; 69 import { mapState, mapGetters } from "vuex";
66 import * as d3 from "d3"; 70 import * as d3Base from "d3";
71 import { lineChunked } from "d3-line-chunked";
67 import debounce from "debounce"; 72 import debounce from "debounce";
73
74 // we should load only d3 modules we need but for now we'll go with the lazy way
75 // https://www.giacomodebidda.com/how-to-import-d3-plugins-with-webpack/
76 const d3 = Object.assign(d3Base, { lineChunked });
68 77
69 export default { 78 export default {
70 computed: { 79 computed: {
71 ...mapState("gauges", ["waterlevels"]), 80 ...mapState("gauges", ["waterlevels"]),
72 ...mapGetters("gauges", ["selectedGauge"]) 81 ...mapGetters("gauges", ["selectedGauge"])
143 .tickSizeOuter(0); 152 .tickSizeOuter(0);
144 153
145 // PREPARING CHART FUNCTIONS 154 // PREPARING CHART FUNCTIONS
146 155
147 // waterlevel line in big chart 156 // waterlevel line in big chart
148 let mainLineChart = d3 157 // d3-line-chunked plugin: https://github.com/pbeshai/d3-line-chunked
149 .line() 158 var mainLineChart = d3
150 .curve(d3.curveMonotoneX) 159 .lineChunked()
151 .x(d => x(d.date)) 160 .x(d => x(d.date))
152 .y(d => y(d.waterlevel)); 161 .y(d => y(d.waterlevel))
162 .curve(d3.curveLinear)
163 .isNext((prev, current) => {
164 // points are "next to each other" when they are exactly 15 minutes apart
165 return current.date - prev.date === 15 * 60 * 1000;
166 })
167 .lineStyles({ stroke: "steelblue" })
168 .gapStyles({
169 stroke: "steelblue",
170 "stroke-opacity": 1,
171 "stroke-dasharray": "3 3",
172 "stroke-width": 1
173 });
153 // waterlevel line in small chart 174 // waterlevel line in small chart
154 let navLineChart = d3 175 let navLineChart = d3
155 .line() 176 .line()
156 .curve(d3.curveMonotoneX) 177 .curve(d3.curveMonotoneX)
157 .x(d => x2(d.date)) 178 .x(d => x2(d.date))
174 .attr("width", width) 195 .attr("width", width)
175 .attr("height", mainHeight); 196 .attr("height", mainHeight);
176 197
177 let mainChart = svg 198 let mainChart = svg
178 .append("g") 199 .append("g")
200 .attr("class", "main")
179 .attr("transform", `translate(${mainMargin.left}, ${mainMargin.top})`); 201 .attr("transform", `translate(${mainMargin.left}, ${mainMargin.top})`);
180 202
181 // axes 203 // axes
182 mainChart 204 mainChart
183 .append("g") 205 .append("g")
254 .attr("x", x(lastDate) - 20) 276 .attr("x", x(lastDate) - 20)
255 .attr("y", y(refWaterLevels.MW) - 3); 277 .attr("y", y(refWaterLevels.MW) - 3);
256 278
257 // waterlevel chart 279 // waterlevel chart
258 mainChart 280 mainChart
259 .append("path") 281 .append("g")
282 .attr("class", "line")
260 .datum(this.waterlevels) 283 .datum(this.waterlevels)
261 .attr("class", "line") 284 .transition()
262 .attr("d", mainLineChart); 285 .duration(1000)
286 .call(mainLineChart);
263 287
264 // DRAWING NAVCHART 288 // DRAWING NAVCHART
265 289
266 let navChart = svg 290 let navChart = svg
267 .append("g") 291 .append("g")
292 .attr("class", "nav")
268 .attr("transform", `translate(${navMargin.left}, ${navMargin.top})`); 293 .attr("transform", `translate(${navMargin.left}, ${navMargin.top})`);
269 294
270 // axis (nav chart only has y-axis) 295 // axis (nav chart only has y-axis)
271 navChart 296 navChart
272 .append("g") 297 .append("g")
291 .on("brush end", () => { 316 .on("brush end", () => {
292 if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") 317 if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom")
293 return; // ignore brush-by-zoom 318 return; // ignore brush-by-zoom
294 let s = d3.event.selection || x2.range(); 319 let s = d3.event.selection || x2.range();
295 x.domain(s.map(x2.invert, x2)); 320 x.domain(s.map(x2.invert, x2));
296 mainChart.select(".line").attr("d", mainLineChart); 321 mainChart.select(".line").call(mainLineChart);
297 mainChart 322 mainChart
298 .select(".axis--x") 323 .select(".axis--x")
299 .call(xAxis) 324 .call(xAxis)
300 .selectAll(".tick text") 325 .selectAll(".tick text")
301 .attr("y", 15); 326 .attr("y", 15);
316 .on("zoom", () => { 341 .on("zoom", () => {
317 if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") 342 if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush")
318 return; // ignore zoom-by-brush 343 return; // ignore zoom-by-brush
319 let t = d3.event.transform; 344 let t = d3.event.transform;
320 x.domain(t.rescaleX(x2).domain()); 345 x.domain(t.rescaleX(x2).domain());
321 mainChart.select(".line").attr("d", mainLineChart); 346 mainChart.select(".line").call(mainLineChart);
322 mainChart 347 mainChart
323 .select(".axis--x") 348 .select(".axis--x")
324 .call(xAxis) 349 .call(xAxis)
325 .selectAll(".tick text") 350 .selectAll(".tick text")
326 .attr("y", 15); 351 .attr("y", 15);