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>