QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
GeoMapVehicleItem.qml
Go to the documentation of this file.
1/****************************************************************************
2 *
3 * (c) 2009-2024 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
4 *
5 * QGroundControl is licensed according to the terms in the file
6 * COPYING.md in the root of the source code directory.
7 *
8 ****************************************************************************/
9
10import QtQuick
11import QtQuick3D
12import QtPositioning
13
14import QGroundControl
15import QGroundControl.Controls
16import QGroundControl.GeoMap
17
18/// Vehicle marker for the GeoMap: 2D top-down icon that crossfades into a
19/// paper-airplane 3D model (heading/roll/pitch attitude) during the 2D<->3D
20/// terrain-scale transition. Anchored at true AMSL altitude, bias-corrected
21/// by the DEM-vs-home-AMSL offset so a landed vehicle sits exactly on the
22/// rendered mesh despite DEM/GPS disagreement.
23GeoMapItem {
24 id: root
25
26 property var vehicle
27 property real size: ScreenTools.defaultFontPixelHeight * 3
28
29 width: size
30 height: size
31 anchorPoint: Qt.point(width / 2, height / 2)
32 altitudeMode: GeoMapItem.Absolute
33 // coordinate.altitude (GLOBAL_POSITION_INT), not the altitudeAMSL fact:
34 // on PX4 the fact switches to the baro-frame ALTITUDE message, which
35 // disagrees with the GPS frame used by the trajectory and HOME_POSITION
36 coordinate: vehicle ? QtPositioning.coordinate(vehicle.coordinate.latitude,
37 vehicle.coordinate.longitude,
38 vehicle.coordinate.altitude + _homeTerrainBias)
39 : QtPositioning.coordinate()
40 visible: vehicle ? vehicle.coordinate.isValid : false
41
42 readonly property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle
43 readonly property real _dimming: vehicle && vehicle === _activeVehicle ? 1.0 : 0.5
44 readonly property real _heading: vehicle && !isNaN(vehicle.heading.value) ? vehicle.heading.value : 0
45 readonly property real _roll: vehicle && !isNaN(vehicle.roll.value) ? vehicle.roll.value : 0
46 readonly property real _pitch: vehicle && !isNaN(vehicle.pitch.value) ? vehicle.pitch.value : 0
47 readonly property var _camera: scene ? scene.camera : null
48
49 // DEM height minus vehicle-reported AMSL, sampled at home. terrainHeightAt
50 // is not a binding dependency, so re-sampled explicitly below as terrain
51 // tiles arrive and when home moves.
52 property real _homeTerrainBias: 0
53
54 function _updateHomeTerrainBias() {
55 if (surfaceModel && vehicle && vehicle.homePosition.isValid && !isNaN(vehicle.homePosition.altitude)) {
56 _homeTerrainBias = surfaceModel.terrainHeightAt(vehicle.homePosition) - vehicle.homePosition.altitude
57 } else {
58 _homeTerrainBias = 0
59 }
60 }
61
62 onVehicleChanged: _updateHomeTerrainBias()
63 onSurfaceModelChanged: _updateHomeTerrainBias()
64
65 // Constant apparent size: scene units per screen pixel at the camera's
66 // look-at distance. Built-in QtQuick3D meshes are 100 units across.
67 readonly property real _modelScale: {
68 if (!_camera || _camera.viewportSize.height <= 0) {
69 return 1
70 }
71 const unitsPerPixel = 2 * _camera.distance * Math.tan(_camera.fieldOfView * Math.PI / 360) / _camera.viewportSize.height
72 return root.size * unitsPerPixel / 100
73 }
74
75 Image {
76 id: vehicleIcon
77
78 anchors.fill: parent
79 source: root.vehicle ? root.vehicle.vehicleImageOpaque : ""
80 mipmap: true
81 sourceSize.width: root.size
82 fillMode: Image.PreserveAspectFit
83 opacity: root.contentOpacity2D * root._dimming
84 // Camera heading rotates map north on screen; the icon follows
85 rotation: root._heading + (root._camera ? root._camera.heading : 0)
86 }
87
88 delegate3D: Component {
89 // Paper-airplane dart matching the 2D icon. Scene axes are x east,
90 // y north, z up; heading is CW from north while positive z-rotation
91 // is CCW, hence the negation.
92 Node {
93 eulerRotation.z: -root._heading
94 scale: Qt.vector3d(root._modelScale, root._modelScale, root._modelScale)
95
96 Model {
97 eulerRotation.x: root._pitch
98 eulerRotation.y: root._roll
99 geometry: PaperPlaneGeometry {}
100 opacity: root._dimming
101
102 materials: PrincipledMaterial {
103 vertexColorsEnabled: true
104 cullMode: Material.NoCulling
105 }
106 }
107 }
108 }
109
110 Connections {
111 target: root.surfaceModel
112
113 function onTerrainHeightsChanged() {
114 root._updateHomeTerrainBias()
115 }
116 }
117
118 Connections {
119 target: root.vehicle
120
121 function onHomePositionChanged() {
122 root._updateHomeTerrainBias()
123 }
124 }
125}