8import QGroundControl.Controls
9import QGroundControl.FlightMap
10import QGroundControl.PlanView
12/// QGCMapPolyline map visuals
16 property var mapControl ///< Map control to place item in
17 property var mapPolyline ///< QGCMapPolyline object
18 property bool interactive: mapPolyline.interactive
19 property int lineWidth: 3
20 property color lineColor: "#be781c"
22 property var _dragHandlesComponent
23 property var _splitHandlesComponent
24 property string _instructionText: _corridorToolsText
25 property real _zorderDragHandle: QGroundControl.zOrderMapItems + 3 // Highest to prevent splitting when items overlap
26 property real _zorderSplitHandle: QGroundControl.zOrderMapItems + 2
27 property var _savedVertices: [ ]
28 property bool _isVertexBeingDragged: false
29 property bool dragging: _isVertexBeingDragged
31 readonly property string _corridorToolsText: qsTr("Polyline Tools")
32 readonly property string _traceText: qsTr("Click in the map to add vertices. Click 'Done Tracing' when finished.")
34 function _addCommonVisuals() {
35 if (_objMgrCommonVisuals.empty) {
36 _objMgrCommonVisuals.createObject(polylineComponent, mapControl, true)
40 function _addInteractiveVisuals() {
41 if (_objMgrInteractiveVisuals.empty) {
42 _objMgrInteractiveVisuals.createObjects([ dragHandlesComponent, splitHandlesComponent, edgeLengthHandlesComponent, toolbarComponent ], mapControl)
46 /// Calculate the default/initial polyline
47 function _defaultPolylineVertices() {
48 var x = mapControl.centerViewport.x + (mapControl.centerViewport.width / 2)
49 var yInset = mapControl.centerViewport.height / 4
50 var topPointCoord = mapControl.toCoordinate(Qt.point(x, mapControl.centerViewport.y + yInset), false /* clipToViewPort */)
51 var bottomPointCoord = mapControl.toCoordinate(Qt.point(x, mapControl.centerViewport.y + mapControl.centerViewport.height - yInset), false /* clipToViewPort */)
52 return [ topPointCoord, bottomPointCoord ]
55 /// Reset polyline back to initial default
56 function _resetPolyline() {
57 mapPolyline.beginReset()
59 var initialVertices = _defaultPolylineVertices()
60 mapPolyline.appendVertex(initialVertices[0])
61 mapPolyline.appendVertex(initialVertices[1])
62 mapPolyline.endReset()
65 function _saveCurrentVertices() {
67 for (var i=0; i<mapPolyline.count; i++) {
68 _savedVertices.push(mapPolyline.vertexCoordinate(i))
72 function _restorePreviousVertices() {
73 mapPolyline.beginReset()
75 for (var i=0; i<_savedVertices.length; i++) {
76 mapPolyline.appendVertex(_savedVertices[i])
78 mapPolyline.endReset()
81 onInteractiveChanged: {
83 _addInteractiveVisuals()
85 _objMgrInteractiveVisuals.destroyObjects()
91 function onTraceModeChanged(traceMode) {
93 _instructionText = _traceText
94 _objMgrTraceVisuals.createObject(traceMouseAreaComponent, mapControl, false)
96 _instructionText = _corridorToolsText
97 _objMgrTraceVisuals.destroyObjects()
102 Component.onCompleted: {
105 _addInteractiveVisuals()
108 Component.onDestruction: mapPolyline.traceMode = false
110 QGCDynamicObjectManager { id: _objMgrCommonVisuals }
111 QGCDynamicObjectManager { id: _objMgrInteractiveVisuals }
112 QGCDynamicObjectManager { id: _objMgrTraceVisuals }
114 QGCPalette { id: qgcPal }
117 id: kmlOrSHPLoadDialog
118 title: qsTr("Select Polyline File")
120 onAcceptedForLoad: (file) => {
121 mapPolyline.loadKMLOrSHPFile(file)
122 mapFitFunctions.fitMapViewportToMissionItems()
129 property int _removeVertexIndex
131 function popUpWithIndex(curIndex) {
132 _removeVertexIndex = curIndex
133 removeVertexItem.visible = mapPolyline.count > 2
139 text: qsTr("Remove vertex" )
140 onTriggered: mapPolyline.removeVertex(menu._removeVertexIndex)
144 text: qsTr("Edit position..." )
145 onTriggered: editPositionDialogFactory.open({ coordinate: mapPolyline.path[menu._removeVertexIndex] })
149 QGCPopupDialogFactory {
150 id: editPositionDialogFactory
152 dialogComponent: editPositionDialog
156 id: editPositionDialog
159 onCoordinateChanged: mapPolyline.adjustVertex(menu._removeVertexIndex,coordinate)
164 id: polylineComponent
167 line.width: mapPolyline.vertexDrag ? 3 : lineWidth
168 line.color: mapPolyline.vertexDrag ? "orange" : lineColor
169 path: mapPolyline.vertexDrag ? mapPolyline.dragPath : mapPolyline.path
170 visible: _root.visible
171 opacity: _root.opacity
176 id: splitHandleComponent
180 anchorPoint.x: sourceItem.width / 2
181 anchorPoint.y: sourceItem.height / 2
182 z: _zorderSplitHandle
183 opacity: _root.opacity
184 visible: !mapPolyline.vertexDrag
186 property int vertexIndex
188 sourceItem: SplitIndicator {
189 onClicked: mapPolyline.splitSegment(mapQuickItem.vertexIndex)
195 id: splitHandlesComponent
198 model: mapPolyline.path
201 property var _splitHandle
202 property var _vertices: mapPolyline.path
204 opacity: _root.opacity
206 function _setHandlePosition() {
207 var nextIndex = index + 1
208 var distance = _vertices[index].distanceTo(_vertices[nextIndex])
209 var azimuth = _vertices[index].azimuthTo(_vertices[nextIndex])
210 _splitHandle.coordinate = _vertices[index].atDistanceAndAzimuth(distance / 2, azimuth)
213 Component.onCompleted: {
214 if (index + 1 <= _vertices.length - 1) {
215 _splitHandle = splitHandleComponent.createObject(mapControl)
216 _splitHandle.vertexIndex = index
218 mapControl.addMapItem(_splitHandle)
222 Component.onDestruction: {
224 _splitHandle.destroy()
232 id: edgeLengthHandleComponent
235 id: edgeLengthMapItem
236 anchorPoint.x: sourceItem.width / 2
237 anchorPoint.y: sourceItem.height / 2
239 property int vertexIndex
240 property real distance
242 property var _unitsConversion: QGroundControl.unitsConversion
245 text: _unitsConversion.metersToAppSettingsHorizontalDistanceUnits(distance).toFixed(1) + " " +
246 _unitsConversion.appSettingsHorizontalDistanceUnitsString
253 id: edgeLengthHandlesComponent
256 model: _isVertexBeingDragged ? mapPolyline.path : undefined
259 property var _edgeLengthHandle
261 function _setHandlePosition() {
262 var vertices = mapPolyline.vertexDrag ? mapPolyline.dragPath : mapPolyline.path
263 var nextIndex = index + 1
264 if (nextIndex > vertices.length - 1) {
267 var distance = vertices[index].distanceTo(vertices[nextIndex])
268 var azimuth = vertices[index].azimuthTo(vertices[nextIndex])
269 _edgeLengthHandle.coordinate = vertices[index].atDistanceAndAzimuth(distance / 2, azimuth)
270 _edgeLengthHandle.distance = distance
275 function onDragPathChanged() { _setHandlePosition() }
278 Component.onCompleted: {
279 if (index + 1 <= mapPolyline.path.length - 1) {
280 _edgeLengthHandle = edgeLengthHandleComponent.createObject(mapControl)
281 _edgeLengthHandle.vertexIndex = index
283 mapControl.addMapItem(_edgeLengthHandle)
287 Component.onDestruction: {
288 if (_edgeLengthHandle) {
289 _edgeLengthHandle.destroy()
296 // Control which is used to drag polygon vertices
298 id: dragAreaComponent
300 MissionItemIndicatorDrag {
301 mapControl: _root.mapControl
304 opacity: _root.opacity
305 onDragStart: { _isVertexBeingDragged = true; mapPolyline.vertexDrag = true }
306 onDragStop: { _isVertexBeingDragged = false; mapPolyline.vertexDrag = false }
308 property int polylineVertex
310 property bool _creationComplete: false
312 Component.onCompleted: _creationComplete = true
314 onItemCoordinateChanged: {
315 if (_creationComplete) {
316 // During component creation some bad coordinate values got through which screws up draw
317 mapPolyline.adjustVertex(polylineVertex, itemCoordinate)
322 menu.popUpWithIndex(polylineVertex)
329 id: dragHandleComponent
333 anchorPoint.x: dragHandle.width / 2
334 anchorPoint.y: dragHandle.height / 2
336 opacity: _root.opacity
338 property int polylineVertex
340 sourceItem: Rectangle {
342 width: ScreenTools.defaultFontPixelHeight * 1.5
345 color: Qt.rgba(1,1,1,0.8)
346 border.color: Qt.rgba(0,0,0,0.25)
352 // Add all polygon vertex drag handles to the map
354 id: dragHandlesComponent
357 model: mapPolyline.pathModel
360 property var _visuals: [ ]
362 opacity: _root.opacity
364 Component.onCompleted: {
365 var dragHandle = dragHandleComponent.createObject(mapControl)
366 dragHandle.coordinate = Qt.binding(function() { return object.coordinate })
367 dragHandle.polylineVertex = Qt.binding(function() { return index })
368 mapControl.addMapItem(dragHandle)
369 var dragArea = dragAreaComponent.createObject(mapControl, { "itemIndicator": dragHandle, "itemCoordinate": object.coordinate })
370 dragArea.polylineVertex = Qt.binding(function() { return index })
371 _visuals.push(dragHandle)
372 _visuals.push(dragArea)
375 Component.onDestruction: {
376 for (var i=0; i<_visuals.length; i++) {
377 _visuals[i].destroy()
389 anchors.horizontalCenter: mapControl.left
390 anchors.horizontalCenterOffset: mapControl.centerViewport.left + (mapControl.centerViewport.width / 2)
391 y: mapControl.centerViewport.top
392 z: QGroundControl.zOrderMapItems + 2
393 availableWidth: mapControl.centerViewport.width
396 _horizontalPadding: 0
398 visible: !mapPolyline.traceMode
399 onClicked: _resetPolyline()
403 _horizontalPadding: 0
404 text: mapPolyline.traceMode ? qsTr("Done Tracing") : qsTr("Trace")
406 if (mapPolyline.traceMode) {
407 if (mapPolyline.count < 2) {
408 _restorePreviousVertices()
410 mapPolyline.traceMode = false
412 _saveCurrentVertices()
413 mapPolyline.traceMode = true
420 _horizontalPadding: 0
421 text: qsTr("Load KML/SHP...")
422 onClicked: kmlOrSHPLoadDialog.openForLoad()
423 visible: !mapPolyline.traceMode
428 // Mouse area to capture clicks for tracing a polyline
430 id: traceMouseAreaComponent
433 anchors.fill: mapControl
434 preventStealing: true
435 z: QGroundControl.zOrderMapItems + 1 // Over item indicators
437 onClicked: (mouse) => {
438 if (mouse.button === Qt.LeftButton && _root.interactive) {
439 mapPolyline.appendVertex(mapControl.toCoordinate(Qt.point(mouse.x, mouse.y), false /* clipToViewPort */))