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 ****************************************************************************/
14import QGroundControl.Controls
16/// General flight-path direction indicators for the GeoMap: one arrow per
17/// entry in MissionController.directionArrows (added to the second flight
18/// path segment and every 5 segments thereafter, regardless of simple vs
19/// complex item — see MissionController::_recalcFlightPathSegments),
20/// mirroring the MapLineArrow Repeater in src/FlightMap/MapItems/PlanMapItems.qml.
24 property var missionController
26 property var surfaceModel
27 property real homeTerrainBias: 0 ///< See GeoMapWaypointItem.homeTerrainBias
30 model: root.missionController ? root.missionController.directionArrows : 0
35 surfaceModel: root.surfaceModel
38 return _terrainLeg ? _drapedPoint(0.75 - _halfChordFraction)
39 : QtPositioning.coordinate(object.coordinate1.latitude, object.coordinate1.longitude,
40 object.coord1AMSLAlt + root.homeTerrainBias)
44 return _terrainLeg ? _drapedPoint(0.75 + _halfChordFraction)
45 : QtPositioning.coordinate(object.coordinate2.latitude, object.coordinate2.longitude,
46 object.coord2AMSLAlt + root.homeTerrainBias)
48 fraction: _terrainLeg ? 0.5 : 0.75
50 // Terrain-frame legs are draped over the DEM (see
51 // GeoMapMissionPath), so anchor the arrow to a short draped chord
52 // around the 0.75 mark rather than the straight AMSL chord: the
53 // chord midpoint puts the arrow on the ribbon and the chord ends
54 // give it the local tangent heading
55 readonly property bool _terrainLeg: !!root.surfaceModel && object.segmentType === FlightPathSegment.SegmentTypeTerrainFrame
57 // Cap the chord at 50 m so it stays local to the ribbon on long
58 // legs (a 10% chord averages away terrain curvature), while short
59 // legs keep enough screen separation for a stable heading
60 readonly property real _halfChordFraction: {
61 const len = object.coordinate1.distanceTo(object.coordinate2)
62 return len > 0 ? Math.min(0.05, 25 / len) : 0.05
65 // terrainHeightAt is not NOTIFY-reactive; read in the coordinate
66 // bindings above so they re-sample as DEM patches stream in
67 property int _terrainRev: 0
69 // Same AGL-ramp draping as GeoMapMissionPath._appendTerrainLeg
70 function _drapedPoint(f) {
71 const c1 = object.coordinate1
72 const c2 = object.coordinate2
73 const point = c1.atDistanceAndAzimuth(c1.distanceTo(c2) * f, c1.azimuthTo(c2))
74 const agl1 = object.coord1AMSLAlt - root.surfaceModel.terrainHeightAt(c1)
75 const agl2 = object.coord2AMSLAlt - root.surfaceModel.terrainHeightAt(c2)
76 return QtPositioning.coordinate(point.latitude, point.longitude,
77 root.surfaceModel.terrainHeightAt(point) + agl1 + ((agl2 - agl1) * f) + root.homeTerrainBias)
81 target: root.surfaceModel
82 enabled: arrow._terrainLeg
83 function onTerrainHeightsChanged() { arrow._terrainRev++ }