Mercurial > gemma
comparison client/src/drawtool/Drawtool.vue @ 1140:2e06bc53b002
separating line/polygon/cut tools in UI
Measurements can now be made while a bottleneck and sounding data is selected.
The open layers interaction object(s) are now in the vuex store to disable them from other components (Morphtool.vue).
Line and Polygon are now to separate buttons.
author | Markus Kottlaender <markus@intevation.de> |
---|---|
date | Mon, 12 Nov 2018 14:45:07 +0100 |
parents | |
children | 5f98d0c9d738 |
comparison
equal
deleted
inserted
replaced
1139:2fda33d55d81 | 1140:2e06bc53b002 |
---|---|
1 <template> | |
2 <div class="d-flex flex-column"> | |
3 <div @click="toggleLineMode" class="ui-element d-flex shadow drawtool"> | |
4 <i :class="['fa fa-pencil', {inverted: drawMode === 'LineString'}]"></i> | |
5 </div> | |
6 <div @click="togglePolygonMode" class="ui-element d-flex shadow drawtool"> | |
7 <i :class="['fa fa-edit', {inverted: drawMode === 'Polygon'}]"></i> | |
8 </div> | |
9 <div @click="toggleCutMode" class="ui-element d-flex shadow drawtool" v-if="selectedSurvey"> | |
10 <i :class="['fa fa-area-chart', {inverted: cutMode}]"></i> | |
11 </div> | |
12 </div> | |
13 </template> | |
14 | |
15 <style lang="sass" scoped> | |
16 .drawtool | |
17 background-color: white | |
18 padding: $small-offset | |
19 border-radius: $border-radius | |
20 margin-left: $offset | |
21 height: $icon-width | |
22 width: $icon-height | |
23 margin-bottom: $offset | |
24 margin-right: $offset | |
25 z-index: 2 | |
26 | |
27 .inverted | |
28 color: #0077ff | |
29 </style> | |
30 | |
31 <script> | |
32 /* This is Free Software under GNU Affero General Public License v >= 3.0 | |
33 * without warranty, see README.md and license for details. | |
34 * | |
35 * SPDX-License-Identifier: AGPL-3.0-or-later | |
36 * License-Filename: LICENSES/AGPL-3.0.txt | |
37 * | |
38 * Copyright (C) 2018 by via donau | |
39 * – Österreichische Wasserstraßen-Gesellschaft mbH | |
40 * Software engineering by Intevation GmbH | |
41 * | |
42 * Author(s): | |
43 * Thomas Junk <thomas.junk@intevation.de> | |
44 * Markus Kottländer <markus.kottlaender@intevation.de> | |
45 */ | |
46 import { mapState, mapGetters } from "vuex"; | |
47 import { getLength, getArea } from "ol/sphere.js"; | |
48 import LineString from "ol/geom/LineString.js"; | |
49 import Draw from "ol/interaction/Draw.js"; | |
50 import { displayError } from "../application/lib/errors.js"; | |
51 import { calculateFairwayCoordinates } from "../application/lib/geo.js"; | |
52 | |
53 const DEMODATA = 2.5; | |
54 | |
55 export default { | |
56 name: "drawtool", | |
57 computed: { | |
58 ...mapGetters("map", ["getLayerByName"]), | |
59 ...mapState("map", ["drawMode", "drawTool", "cutMode", "cutTool", "openLayersMap"]), | |
60 ...mapState("bottlenecks", ["selectedSurvey"]) | |
61 }, | |
62 methods: { | |
63 toggleLineMode() { | |
64 this.disableDrawTool(); | |
65 this.disableCutTool(); | |
66 this.$store.commit("map/drawMode", this.drawMode !== "LineString" ? "LineString" : null); | |
67 this.$store.commit("map/cutMode", null); | |
68 if (this.drawMode) this.enableDrawTool(); | |
69 }, | |
70 togglePolygonMode() { | |
71 this.disableDrawTool(); | |
72 this.disableCutTool(); | |
73 this.$store.commit("map/drawMode", this.drawMode !== "Polygon" ? "Polygon" : null); | |
74 this.$store.commit("map/cutMode", null); | |
75 if (this.drawMode) this.enableDrawTool(); | |
76 }, | |
77 toggleCutMode() { | |
78 this.disableCutTool(); | |
79 this.disableDrawTool(); | |
80 this.$store.commit('map/cutMode', !this.cutMode); | |
81 this.$store.commit("map/drawMode", null); | |
82 if (this.cutMode) this.enableCutTool(); | |
83 }, | |
84 enableDrawTool() { | |
85 const drawVectorSrc = this.getLayerByName("Draw Tool").data.getSource(); | |
86 drawVectorSrc.clear(); | |
87 const drawTool = new Draw({ | |
88 source: drawVectorSrc, | |
89 type: this.drawMode, | |
90 maxPoints: this.drawMode === "LineString" ? 2 : 50 | |
91 }); | |
92 drawTool.on("drawstart", () => { | |
93 drawVectorSrc.clear(); | |
94 this.$store.commit("map/setCurrentMeasurement", null); | |
95 // we are not setting an id here, to avoid the regular identify to | |
96 // pick it up | |
97 // event.feature.setId("drawn.1"); // unique id for new feature | |
98 }); | |
99 drawTool.on("drawend", this.drawEnd); | |
100 this.$store.commit("map/drawTool", drawTool); | |
101 this.openLayersMap.addInteraction(drawTool); | |
102 }, | |
103 disableDrawTool() { | |
104 this.$store.commit("map/setCurrentMeasurement", null); | |
105 this.getLayerByName("Draw Tool").data.getSource().clear(); | |
106 this.openLayersMap.removeInteraction(this.drawTool); | |
107 this.$store.commit("map/drawTool", null); | |
108 }, | |
109 drawEnd(event) { | |
110 if (this.drawMode === "Polygon") { | |
111 const areaSize = getArea(event.feature.getGeometry()); | |
112 // also place the a rounded areaSize in a property, | |
113 // so identify will show it | |
114 this.$store.commit("map/setCurrentMeasurement", { | |
115 quantity: "Area", | |
116 unitSymbol: areaSize > 100000 ? "km²" : "m²", | |
117 value: areaSize > 100000 | |
118 ? Math.round(areaSize / 1000) / 1000 // convert into 1 km² == 1000*1000 m² and round to 1000 m² | |
119 : Math.round(areaSize) | |
120 }); | |
121 } | |
122 if (this.drawMode === "LineString") { | |
123 const length = getLength(event.feature.getGeometry()); | |
124 this.$store.commit("map/setCurrentMeasurement", { | |
125 quantity: "Length", | |
126 unitSymbol: "m", | |
127 value: Math.round(length * 10) / 10 | |
128 }); | |
129 } | |
130 }, | |
131 enableCutTool() { | |
132 const cutVectorSrc = this.getLayerByName("Cut Tool").data.getSource(); | |
133 cutVectorSrc.clear(); | |
134 const cutTool = new Draw({ | |
135 source: cutVectorSrc, | |
136 type: "LineString", | |
137 maxPoints: 2 | |
138 }); | |
139 cutTool.on("drawstart", () => { | |
140 cutVectorSrc.clear(); | |
141 // we are not setting an id here, to avoid the regular identify to | |
142 // pick it up | |
143 // event.feature.setId("drawn.1"); // unique id for new feature | |
144 }); | |
145 cutTool.on("drawend", this.cutEnd); | |
146 this.$store.commit("map/cutTool", cutTool); | |
147 this.openLayersMap.addInteraction(cutTool); | |
148 }, | |
149 disableCutTool() { | |
150 this.$store.commit("map/setCurrentMeasurement", null); | |
151 this.getLayerByName("Cut Tool").data.getSource().clear(); | |
152 this.openLayersMap.removeInteraction(this.cutTool); | |
153 this.$store.commit("map/cutTool", null); | |
154 }, | |
155 cutEnd(event) { | |
156 const length = getLength(event.feature.getGeometry()); | |
157 this.$store.commit("map/setCurrentMeasurement", { | |
158 quantity: "Length", | |
159 unitSymbol: "m", | |
160 value: Math.round(length * 10) / 10 | |
161 }); | |
162 | |
163 // if a survey has been selected, request a profile | |
164 // TODO an improvement could be to check if the line intersects | |
165 // with the bottleneck area's polygon before trying the server request | |
166 if (this.selectedSurvey) { | |
167 this.$store.commit("fairwayprofile/clearCurrentProfile"); | |
168 console.log("requesting profile for", this.selectedSurvey); | |
169 const inputLineString = event.feature.getGeometry().clone(); | |
170 inputLineString.transform("EPSG:3857", "EPSG:4326"); | |
171 const [start, end] = inputLineString | |
172 .getCoordinates() | |
173 .map(coords => coords.map(coord => parseFloat(coord.toFixed(8)))); | |
174 this.$store.commit("fairwayprofile/setStartPoint", start); | |
175 this.$store.commit("fairwayprofile/setEndPoint", end); | |
176 const profileLine = new LineString([start, end]); | |
177 this.$store | |
178 .dispatch("fairwayprofile/loadProfile", this.selectedSurvey) | |
179 .then(() => { | |
180 var vectorSource = this.getLayerByName( | |
181 "Fairway Dimensions" | |
182 ).data.getSource(); | |
183 this.calculateIntersection(vectorSource, profileLine); | |
184 }) | |
185 .then(() => { | |
186 this.$store.commit("application/showSplitscreen", true); | |
187 }) | |
188 .catch(error => { | |
189 const { status, data } = error.response; | |
190 displayError({ | |
191 title: "Backend Error", | |
192 message: `${status}: ${data.message || data}` | |
193 }); | |
194 }); | |
195 } | |
196 }, | |
197 calculateIntersection(vectorSource, profileLine) { | |
198 const transformedLine = profileLine | |
199 .clone() | |
200 .transform("EPSG:4326", "EPSG:3857") | |
201 .getExtent(); | |
202 const featureCallback = feature => { | |
203 // transform back to prepare for usage | |
204 var intersectingPolygon = feature | |
205 .getGeometry() | |
206 .clone() | |
207 .transform("EPSG:3857", "EPSG:4326"); | |
208 const fairwayCoordinates = calculateFairwayCoordinates( | |
209 profileLine, | |
210 intersectingPolygon, | |
211 DEMODATA | |
212 ); | |
213 this.$store.commit( | |
214 "fairwayprofile/setFairwayCoordinates", | |
215 fairwayCoordinates | |
216 ); | |
217 }; | |
218 vectorSource.forEachFeatureIntersectingExtent( | |
219 // need to use EPSG:3857 which is the proj of vectorSource | |
220 transformedLine, | |
221 featureCallback | |
222 ); | |
223 }, | |
224 } | |
225 }; | |
226 </script> |