Mercurial > gemma
comparison client/src/components/fairway/Fairwayprofile.vue @ 1558:0ded4c56978e
refac: component filestructure. remove admin/map hierarchy
author | Thomas Junk <thomas.junk@intevation.de> |
---|---|
date | Wed, 12 Dec 2018 09:22:20 +0100 |
parents | client/src/components/map/fairway/Fairwayprofile.vue@9b81ac91a43e |
children | 2888bfacd331 |
comparison
equal
deleted
inserted
replaced
1557:62171cd9a42b | 1558:0ded4c56978e |
---|---|
1 <template> | |
2 <div :class="['position-relative', { show: showSplitscreen }]"> | |
3 <button | |
4 class="rounded-bottom bg-white border-0 position-absolute splitscreen-toggle" | |
5 @click="$store.commit('application/showSplitscreen', false)" | |
6 v-if="showSplitscreen" | |
7 > | |
8 <font-awesome-icon icon="angle-down" /> | |
9 </button> | |
10 <button | |
11 class="rounded-bottom bg-white border-0 position-absolute clear-selection" | |
12 @click="$store.dispatch('fairwayprofile/clearSelection')" | |
13 v-if="showSplitscreen" | |
14 > | |
15 <font-awesome-icon icon="times" /> | |
16 </button> | |
17 <div class="profile bg-white position-relative d-flex flex-column"> | |
18 <h5 | |
19 class="headline border-bottom mb-0 py-2" | |
20 v-if="selectedBottleneck && selectedSurvey" | |
21 > | |
22 {{ selectedBottleneck }} ({{ selectedSurvey.date_info }}) | |
23 </h5> | |
24 <div class="d-flex flex-fill"> | |
25 <div | |
26 class="loading d-flex justify-content-center align-items-center" | |
27 v-if="surveysLoading || profileLoading" | |
28 > | |
29 <font-awesome-icon icon="spinner" spin /> | |
30 </div> | |
31 <div class="fairwayprofile m-3 mt-0 bg-white flex-grow-1"></div> | |
32 </div> | |
33 </div> | |
34 </div> | |
35 </template> | |
36 | |
37 <style lang="scss" scoped> | |
38 .profile { | |
39 width: 100vw; | |
40 height: 0; | |
41 overflow: hidden; | |
42 z-index: 2; | |
43 } | |
44 | |
45 .splitscreen-toggle, | |
46 .clear-selection { | |
47 width: 2rem; | |
48 height: 2rem; | |
49 margin-top: 8px; | |
50 z-index: 3; | |
51 outline: none; | |
52 } | |
53 | |
54 .splitscreen-toggle svg path, | |
55 .clear-selection svg path { | |
56 fill: #666; | |
57 } | |
58 | |
59 .splitscreen-toggle { | |
60 right: 2.5rem; | |
61 } | |
62 | |
63 .clear-selection { | |
64 right: 0.5rem; | |
65 } | |
66 | |
67 .show .profile { | |
68 height: 50vh; | |
69 } | |
70 | |
71 .loading { | |
72 background: rgba(255, 255, 255, 0.96); | |
73 position: absolute; | |
74 z-index: 99; | |
75 top: 0; | |
76 right: 0; | |
77 bottom: 0; | |
78 left: 0; | |
79 } | |
80 </style> | |
81 | |
82 <script> | |
83 /* This is Free Software under GNU Affero General Public License v >= 3.0 | |
84 * without warranty, see README.md and license for details. | |
85 * | |
86 * SPDX-License-Identifier: AGPL-3.0-or-later | |
87 * License-Filename: LICENSES/AGPL-3.0.txt | |
88 * | |
89 * Copyright (C) 2018 by via donau | |
90 * – Österreichische Wasserstraßen-Gesellschaft mbH | |
91 * Software engineering by Intevation GmbH | |
92 * | |
93 * Author(s): | |
94 * Thomas Junk <thomas.junk@intevation.de> | |
95 */ | |
96 import * as d3 from "d3"; | |
97 import { mapState, mapGetters } from "vuex"; | |
98 import debounce from "debounce"; | |
99 | |
100 const GROUND_COLOR = "#4A2F06"; | |
101 | |
102 export default { | |
103 name: "fairwayprofile", | |
104 data() { | |
105 return { | |
106 coordinatesInput: "", | |
107 coordinatesSelect: null, | |
108 cutLabel: "", | |
109 showLabelInput: false, | |
110 width: null, | |
111 height: null, | |
112 margin: { | |
113 top: 20, | |
114 right: 40, | |
115 bottom: 30, | |
116 left: 40 | |
117 } | |
118 }; | |
119 }, | |
120 computed: { | |
121 ...mapGetters("fairwayprofile", ["totalLength"]), | |
122 ...mapState("application", ["showSplitscreen"]), | |
123 ...mapState("fairwayprofile", [ | |
124 "startPoint", | |
125 "endPoint", | |
126 "currentProfile", | |
127 "additionalSurvey", | |
128 "minAlt", | |
129 "maxAlt", | |
130 "fairwayCoordinates", | |
131 "waterLevels", | |
132 "selectedWaterLevel", | |
133 "profileLoading" | |
134 ]), | |
135 ...mapState("bottlenecks", [ | |
136 "selectedBottleneck", | |
137 "selectedSurvey", | |
138 "surveysLoading" | |
139 ]), | |
140 currentData() { | |
141 if ( | |
142 !this.selectedSurvey || | |
143 !this.currentProfile.hasOwnProperty(this.selectedSurvey.date_info) | |
144 ) | |
145 return []; | |
146 return this.currentProfile[this.selectedSurvey.date_info].points; | |
147 }, | |
148 additionalData() { | |
149 if ( | |
150 !this.additionalSurvey || | |
151 !this.currentProfile.hasOwnProperty(this.additionalSurvey.date_info) | |
152 ) | |
153 return []; | |
154 return this.currentProfile[this.additionalSurvey.date_info].points; | |
155 }, | |
156 waterColor() { | |
157 const result = this.waterLevels.find( | |
158 x => x.level === this.selectedWaterLevel | |
159 ); | |
160 return result.color; | |
161 }, | |
162 xScale() { | |
163 return [0, this.totalLength]; | |
164 }, | |
165 yScaleLeft() { | |
166 const hi = Math.max(this.maxAlt, this.selectedWaterLevel); | |
167 return [this.minAlt, hi]; | |
168 }, | |
169 yScaleRight() { | |
170 const DELTA = this.maxAlt * 1.1 - this.maxAlt; | |
171 return [this.maxAlt * 1 + DELTA, -DELTA]; | |
172 } | |
173 }, | |
174 watch: { | |
175 currentData() { | |
176 this.drawDiagram(); | |
177 }, | |
178 additionalData() { | |
179 this.drawDiagram(); | |
180 }, | |
181 width() { | |
182 this.drawDiagram(); | |
183 }, | |
184 height() { | |
185 this.drawDiagram(); | |
186 }, | |
187 waterLevels() { | |
188 this.drawDiagram(); | |
189 }, | |
190 selectedWaterLevel() { | |
191 this.drawDiagram(); | |
192 }, | |
193 fairwayCoordinates() { | |
194 this.drawDiagram(); | |
195 } | |
196 }, | |
197 methods: { | |
198 drawDiagram() { | |
199 this.coordinatesSelect = null; | |
200 const chartDiv = document.querySelector(".fairwayprofile"); | |
201 d3.select(".fairwayprofile svg").remove(); | |
202 this.scaleFairwayProfile(); | |
203 let svg = d3.select(chartDiv).append("svg"); | |
204 svg.attr("width", this.width); | |
205 svg.attr("height", this.height); | |
206 const width = this.width - this.margin.right - 1.5 * this.margin.left; | |
207 const height = this.height - this.margin.top - 2 * this.margin.bottom; | |
208 const currentData = this.currentData; | |
209 const additionalData = this.additionalData; | |
210 const { xScale, yScaleRight, graph } = this.generateCoordinates( | |
211 svg, | |
212 height, | |
213 width | |
214 ); | |
215 this.drawWaterlevel({ graph, xScale, yScaleRight, height }); | |
216 this.drawLabels({ graph, height }); | |
217 this.drawFairway({ graph, xScale, yScaleRight }); | |
218 if (currentData) { | |
219 this.drawProfile({ | |
220 graph, | |
221 xScale, | |
222 yScaleRight, | |
223 currentData, | |
224 height, | |
225 color: GROUND_COLOR, | |
226 strokeColor: "black", | |
227 opacity: 1 | |
228 }); | |
229 } | |
230 if (additionalData) { | |
231 this.drawProfile({ | |
232 graph, | |
233 xScale, | |
234 yScaleRight, | |
235 currentData: additionalData, | |
236 height, | |
237 color: GROUND_COLOR, | |
238 strokeColor: "#943007", | |
239 opacity: 0.6 | |
240 }); | |
241 } | |
242 }, | |
243 drawFairway({ graph, xScale, yScaleRight }) { | |
244 for (let coordinates of this.fairwayCoordinates) { | |
245 const [startPoint, endPoint, depth] = coordinates; | |
246 let fairwayArea = d3 | |
247 .area() | |
248 .x(function(d) { | |
249 return xScale(d.x); | |
250 }) | |
251 .y0(yScaleRight(0)) | |
252 .y1(function(d) { | |
253 return yScaleRight(d.y); | |
254 }); | |
255 graph | |
256 .append("path") | |
257 .datum([{ x: startPoint, y: depth }, { x: endPoint, y: depth }]) | |
258 .attr("fill", "#002AFF") | |
259 .attr("stroke-opacity", 0.65) | |
260 .attr("fill-opacity", 0.65) | |
261 .attr("stroke", "#FFD20D") | |
262 .attr("d", fairwayArea); | |
263 } | |
264 }, | |
265 drawLabels({ graph, height }) { | |
266 graph | |
267 .append("text") | |
268 .attr("transform", ["rotate(-90)"]) | |
269 .attr("y", this.width - 60) | |
270 .attr("x", -(this.height - this.margin.top - this.margin.bottom) / 2) | |
271 .attr("dy", "1em") | |
272 .attr("fill", "black") | |
273 .style("text-anchor", "middle") | |
274 .text("Depth [m]"); | |
275 graph | |
276 .append("text") | |
277 .attr("y", 0 - this.margin.left) | |
278 .attr("x", 0 - height / 4) | |
279 .attr("dy", "1em") | |
280 .attr("fill", "black") | |
281 .style("text-anchor", "middle") | |
282 .attr("transform", [ | |
283 "translate(" + this.width / 2 + "," + this.height + ")", | |
284 "rotate(0)" | |
285 ]) | |
286 .text("Width [m]"); | |
287 }, | |
288 generateCoordinates(svg, height, width) { | |
289 let xScale = d3 | |
290 .scaleLinear() | |
291 .domain(this.xScale) | |
292 .rangeRound([0, width]); | |
293 | |
294 xScale.ticks(5); | |
295 let yScaleLeft = d3 | |
296 .scaleLinear() | |
297 .domain(this.yScaleLeft) | |
298 .rangeRound([height, 0]); | |
299 | |
300 let yScaleRight = d3 | |
301 .scaleLinear() | |
302 .domain(this.yScaleRight) | |
303 .rangeRound([height, 0]); | |
304 | |
305 let xAxis = d3.axisBottom(xScale); | |
306 let yAxis2 = d3.axisRight(yScaleRight); | |
307 let graph = svg | |
308 .append("g") | |
309 .attr( | |
310 "transform", | |
311 "translate(" + this.margin.left + "," + this.margin.top + ")" | |
312 ); | |
313 graph | |
314 .append("g") | |
315 .attr("transform", "translate(0," + height + ")") | |
316 .call(xAxis.ticks(5)); | |
317 graph | |
318 .append("g") | |
319 .attr("transform", "translate(" + width + ",0)") | |
320 .call(yAxis2); | |
321 return { xScale, yScaleLeft, yScaleRight, graph }; | |
322 }, | |
323 drawWaterlevel({ graph, xScale, yScaleRight, height }) { | |
324 let waterArea = d3 | |
325 .area() | |
326 .x(function(d) { | |
327 return xScale(d.x); | |
328 }) | |
329 .y0(height) | |
330 .y1(function(d) { | |
331 return yScaleRight(d.y); | |
332 }); | |
333 graph | |
334 .append("path") | |
335 .datum([{ x: 0, y: 0 }, { x: this.totalLength, y: 0 }]) | |
336 .attr("fill", this.waterColor) | |
337 .attr("stroke", this.waterColor) | |
338 .attr("d", waterArea); | |
339 }, | |
340 drawProfile({ | |
341 graph, | |
342 xScale, | |
343 yScaleRight, | |
344 currentData, | |
345 height, | |
346 color, | |
347 strokeColor, | |
348 opacity | |
349 }) { | |
350 for (let part of currentData) { | |
351 let profileLine = d3 | |
352 .line() | |
353 .x(d => { | |
354 return xScale(d.x); | |
355 }) | |
356 .y(d => { | |
357 return yScaleRight(d.y); | |
358 }); | |
359 let profileArea = d3 | |
360 .area() | |
361 .x(function(d) { | |
362 return xScale(d.x); | |
363 }) | |
364 .y0(height) | |
365 .y1(function(d) { | |
366 return yScaleRight(d.y); | |
367 }); | |
368 graph | |
369 .append("path") | |
370 .datum(part) | |
371 .attr("fill", color) | |
372 .attr("stroke", color) | |
373 .attr("stroke-width", 3) | |
374 .attr("stroke-opacity", opacity) | |
375 .attr("fill-opacity", opacity) | |
376 .attr("d", profileArea); | |
377 graph | |
378 .append("path") | |
379 .datum(part) | |
380 .attr("fill", "none") | |
381 .attr("stroke", strokeColor) | |
382 .attr("stroke-linejoin", "round") | |
383 .attr("stroke-linecap", "round") | |
384 .attr("stroke-width", 3) | |
385 .attr("stroke-opacity", opacity) | |
386 .attr("fill-opacity", opacity) | |
387 .attr("d", profileLine); | |
388 } | |
389 }, | |
390 scaleFairwayProfile() { | |
391 if (!document.querySelector(".fairwayprofile")) return; | |
392 const clientHeight = document.querySelector(".fairwayprofile") | |
393 .clientHeight; | |
394 const clientWidth = document.querySelector(".fairwayprofile").clientWidth; | |
395 if (!clientHeight || !clientWidth) return; | |
396 this.height = clientHeight; | |
397 this.width = clientWidth; | |
398 } | |
399 }, | |
400 created() { | |
401 window.addEventListener("resize", debounce(this.drawDiagram), 100); | |
402 }, | |
403 mounted() { | |
404 this.drawDiagram(); | |
405 }, | |
406 updated() { | |
407 this.scaleFairwayProfile(); | |
408 }, | |
409 destroyed() { | |
410 window.removeEventListener("resize", debounce(this.drawDiagram)); | |
411 } | |
412 }; | |
413 </script> |