1/****************************************************************************
3 * (c) 2009-2024 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
5 * QGroundControl is licensed according to the terms in the file
6 * COPYING.md in the root of the source code directory.
8 ****************************************************************************/
15import QGroundControl.Controls
16import QGroundControl.GeoMap
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.
27 property real size: ScreenTools.defaultFontPixelHeight * 3
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
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
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
54 function _updateHomeTerrainBias() {
55 if (surfaceModel && vehicle && vehicle.homePosition.isValid && !isNaN(vehicle.homePosition.altitude)) {
56 _homeTerrainBias = surfaceModel.terrainHeightAt(vehicle.homePosition) - vehicle.homePosition.altitude
62 onVehicleChanged: _updateHomeTerrainBias()
63 onSurfaceModelChanged: _updateHomeTerrainBias()
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) {
71 const unitsPerPixel = 2 * _camera.distance * Math.tan(_camera.fieldOfView * Math.PI / 360) / _camera.viewportSize.height
72 return root.size * unitsPerPixel / 100
79 source: root.vehicle ? root.vehicle.vehicleImageOpaque : ""
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)
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.
93 eulerRotation.z: -root._heading
94 scale: Qt.vector3d(root._modelScale, root._modelScale, root._modelScale)
97 eulerRotation.x: root._pitch
98 eulerRotation.y: root._roll
99 geometry: PaperPlaneGeometry {}
100 opacity: root._dimming
102 materials: PrincipledMaterial {
103 vertexColorsEnabled: true
104 cullMode: Material.NoCulling
111 target: root.surfaceModel
113 function onTerrainHeightsChanged() {
114 root._updateHomeTerrainBias()
121 function onHomePositionChanged() {
122 root._updateHomeTerrainBias()