/**************************************************************************** * * (c) 2009-2016 QGROUNDCONTROL PROJECT * * QGroundControl is licensed according to the terms in the file * COPYING.md in the root of the source code directory. * ****************************************************************************/ import QtQuick 2.3 import QtQuick.Controls 1.2 import QtLocation 5.3 import QtPositioning 5.3 import QtQuick.Dialogs 1.2 import QGroundControl 1.0 import QGroundControl.ScreenTools 1.0 import QGroundControl.Palette 1.0 import QGroundControl.Controls 1.0 import QGroundControl.FlightMap 1.0 /// QGCMapPolygon map visuals Item { id: _root property var qgcView ///< QGCView for popping dialogs property var mapControl ///< Map control to place item in property var mapPolygon ///< QGCMapPolygon object property bool interactive: mapPolygon.interactive property color interiorColor: "transparent" property real interiorOpacity: 1 property int borderWidth: 0 property color borderColor: "black" property var _polygonComponent property var _dragHandlesComponent property var _splitHandlesComponent property var _centerDragHandleComponent property bool _circle: false property real _circleRadius property real _zorderDragHandle: QGroundControl.zOrderMapItems + 3 // Highest to prevent splitting when items overlap property real _zorderSplitHandle: QGroundControl.zOrderMapItems + 2 property real _zorderCenterHandle: QGroundControl.zOrderMapItems + 1 // Lowest such that drag or split takes precedence function addVisuals() { _polygonComponent = polygonComponent.createObject(mapControl) mapControl.addMapItem(_polygonComponent) } function removeVisuals() { _polygonComponent.destroy() } function addHandles() { if (!_dragHandlesComponent) { _dragHandlesComponent = dragHandlesComponent.createObject(mapControl) _splitHandlesComponent = splitHandlesComponent.createObject(mapControl) _centerDragHandleComponent = centerDragHandleComponent.createObject(mapControl) } } function removeHandles() { if (_dragHandlesComponent) { _dragHandlesComponent.destroy() _dragHandlesComponent = undefined } if (_splitHandlesComponent) { _splitHandlesComponent.destroy() _splitHandlesComponent = undefined } if (_centerDragHandleComponent) { _centerDragHandleComponent.destroy() _centerDragHandleComponent = undefined } } /// Calculate the default/initial 4 sided polygon function defaultPolygonVertices() { // Initial polygon is inset to take 2/3rds space var rect = Qt.rect(map.centerViewport.x, map.centerViewport.y, map.centerViewport.width, map.centerViewport.height) rect.x += (rect.width * 0.25) / 2 rect.y += (rect.height * 0.25) / 2 rect.width *= 0.75 rect.height *= 0.75 var centerCoord = map.toCoordinate(Qt.point(rect.x + (rect.width / 2), rect.y + (rect.height / 2)), false /* clipToViewPort */) var topLeftCoord = map.toCoordinate(Qt.point(rect.x, rect.y), false /* clipToViewPort */) var topRightCoord = map.toCoordinate(Qt.point(rect.x + rect.width, rect.y), false /* clipToViewPort */) var bottomLeftCoord = map.toCoordinate(Qt.point(rect.x, rect.y + rect.height), false /* clipToViewPort */) var bottomRightCoord = map.toCoordinate(Qt.point(rect.x + rect.width, rect.y + rect.height), false /* clipToViewPort */) // Initial polygon has max width and height of 3000 meters var halfWidthMeters = Math.min(topLeftCoord.distanceTo(topRightCoord), 3000) / 2 var halfHeightMeters = Math.min(topLeftCoord.distanceTo(bottomLeftCoord), 3000) / 2 topLeftCoord = centerCoord.atDistanceAndAzimuth(halfWidthMeters, -90).atDistanceAndAzimuth(halfHeightMeters, 0) topRightCoord = centerCoord.atDistanceAndAzimuth(halfWidthMeters, 90).atDistanceAndAzimuth(halfHeightMeters, 0) bottomLeftCoord = centerCoord.atDistanceAndAzimuth(halfWidthMeters, -90).atDistanceAndAzimuth(halfHeightMeters, 180) bottomRightCoord = centerCoord.atDistanceAndAzimuth(halfWidthMeters, 90).atDistanceAndAzimuth(halfHeightMeters, 180) return [ topLeftCoord, topRightCoord, bottomRightCoord, bottomLeftCoord, centerCoord ] } /// Add an initial 4 sided polygon function addInitialPolygon() { if (mapPolygon.count < 3) { initialVertices = defaultPolygonVertices() mapPolygon.appendVertex(initialVertices[0]) mapPolygon.appendVertex(initialVertices[1]) mapPolygon.appendVertex(initialVertices[2]) mapPolygon.appendVertex(initialVertices[3]) } } /// Reset polygon back to initial default function resetPolygon() { var initialVertices = defaultPolygonVertices() mapPolygon.clear() for (var i=0; i<4; i++) { mapPolygon.appendVertex(initialVertices[i]) } _circle = false } /// Reset polygon to a circle which fits within initial polygon function setCircleRadius(center, radius) { var unboundCenter = center.atDistanceAndAzimuth(0, 0) _circleRadius = radius var segments = 16 var angleIncrement = 360 / segments var angle = 0 mapPolygon.clear() for (var i=0; i _vertices.length - 1) { nextIndex = 0 } var distance = _vertices[index].distanceTo(_vertices[nextIndex]) var azimuth = _vertices[index].azimuthTo(_vertices[nextIndex]) _splitHandle.coordinate = _vertices[index].atDistanceAndAzimuth(distance / 2, azimuth) } Component.onCompleted: { _splitHandle = splitHandleComponent.createObject(mapControl) _splitHandle.vertexIndex = index _setHandlePosition() mapControl.addMapItem(_splitHandle) } Component.onDestruction: { if (_splitHandle) { _splitHandle.destroy() } } } } } // Control which is used to drag polygon vertices Component { id: dragAreaComponent MissionItemIndicatorDrag { id: dragArea z: _zorderDragHandle visible: !_circle property int polygonVertex property bool _creationComplete: false Component.onCompleted: _creationComplete = true onItemCoordinateChanged: { if (_creationComplete) { // During component creation some bad coordinate values got through which screws up draw mapPolygon.adjustVertex(polygonVertex, itemCoordinate) } } onClicked: mapPolygon.removeVertex(polygonVertex) } } Component { id: centerDragHandle MapQuickItem { id: mapQuickItem anchorPoint.x: dragHandle.width / 2 anchorPoint.y: dragHandle.height / 2 z: _zorderDragHandle sourceItem: Rectangle { id: dragHandle width: ScreenTools.defaultFontPixelHeight * 1.5 height: width radius: width / 2 color: "white" opacity: .90 QGCLabel { anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter text: "..." color: "black" } } } } Component { id: dragHandleComponent MapQuickItem { id: mapQuickItem anchorPoint.x: dragHandle.width / 2 anchorPoint.y: dragHandle.height / 2 z: _zorderDragHandle visible: !_circle property int polygonVertex sourceItem: Rectangle { id: dragHandle width: ScreenTools.defaultFontPixelHeight * 1.5 height: width radius: width / 2 color: "white" opacity: .90 } } } // Add all polygon vertex drag handles to the map Component { id: dragHandlesComponent Repeater { model: mapPolygon.pathModel delegate: Item { property var _visuals: [ ] Component.onCompleted: { var dragHandle = dragHandleComponent.createObject(mapControl) dragHandle.coordinate = Qt.binding(function() { return object.coordinate }) dragHandle.polygonVertex = Qt.binding(function() { return index }) mapControl.addMapItem(dragHandle) var dragArea = dragAreaComponent.createObject(mapControl, { "itemIndicator": dragHandle, "itemCoordinate": object.coordinate }) dragArea.polygonVertex = Qt.binding(function() { return index }) _visuals.push(dragHandle) _visuals.push(dragArea) } Component.onDestruction: { for (var i=0; i<_visuals.length; i++) { _visuals[i].destroy() } _visuals = [ ] } } } } Component { id: editPositionDialog EditPositionDialog { coordinate: mapPolygon.center onCoordinateChanged: mapPolygon.center = coordinate } } Component { id: centerDragAreaComponent MissionItemIndicatorDrag { z: _zorderCenterHandle onItemCoordinateChanged: mapPolygon.center = itemCoordinate onDragStart: mapPolygon.centerDrag = true onDragStop: mapPolygon.centerDrag = false onClicked: menu.popup() function setRadiusFromDialog() { setCircleRadius(mapPolygon.center, radiusField.text) radiusDialog.visible = false } Menu { id: menu MenuItem { text: qsTr("Circle" ) onTriggered: resetCircle() } MenuItem { text: qsTr("Polygon") onTriggered: resetPolygon() } MenuItem { text: qsTr("Set radius..." ) enabled: _circle onTriggered: radiusDialog.visible = true } MenuItem { text: qsTr("Edit position..." ) enabled: _circle onTriggered: qgcView.showDialog(editPositionDialog, qsTr("Edit Position"), qgcView.showDialogDefaultWidth, StandardButton.Cancel) } MenuItem { text: qsTr("Load KML...") onTriggered: kmlLoadDialog.openForLoad() } } Rectangle { id: radiusDialog anchors.margins: _margin anchors.left: parent.right width: radiusColumn.width + (_margin *2) height: radiusColumn.height + (_margin *2) color: qgcPal.window border.color: qgcPal.text visible: false Column { id: radiusColumn anchors.margins: _margin anchors.left: parent.left anchors.top: parent.top spacing: _margin QGCLabel { text: qsTr("Radius:") } QGCTextField { id: radiusField text: _circleRadius.toFixed(2) onEditingFinished: setRadiusFromDialog() } } QGCLabel { anchors.right: radiusColumn.right anchors.top: radiusColumn.top text: "X" QGCMouseArea { fillItem: parent onClicked: setRadiusFromDialog() } } } } } Component { id: centerDragHandleComponent Item { property var dragHandle property var dragArea Component.onCompleted: { dragHandle = centerDragHandle.createObject(mapControl) dragHandle.coordinate = Qt.binding(function() { return mapPolygon.center }) mapControl.addMapItem(dragHandle) dragArea = centerDragAreaComponent.createObject(mapControl, { "itemIndicator": dragHandle, "itemCoordinate": mapPolygon.center }) } Component.onDestruction: { dragHandle.destroy() dragArea.destroy() } } } }