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
15import QGroundControl.GeoMap
17/// Draped ground shadow under a floating marker's drop line: GeoMapCircle
18/// clamps each rim point to the terrain mesh, so the shadow follows slopes
19/// instead of half-burying like a flat disc. Instantiate as a direct child
20/// of the marker's GeoMapItem — the item repositions itself in viewport
21/// coordinates and this undoes that offset (screen-space geometry).
22/// Also owns the marker-to-ground drop computation (dropLength /
23/// dropLineScale) shared with the delegate3D's GeoMapDropLine, since the
24/// shadow already samples the terrain under the marker.
28 property var geoItem ///< Owning GeoMapItem (the marker)
29 property real indicatorSize: 0 ///< Marker diameter in pixels; sets the shadow radius
31 /// Scene-space length of the drop line from marker to the rendered
32 /// terrain (tracks terrainScale, so it flattens in lockstep with the
33 /// anchor z); 0 for clamped markers, so they show no line
34 readonly property real dropLength: {
35 if (!scene || !geoItem || geoItem.altitudeMode !== GeoMapItem.Absolute || isNaN(geoItem.coordinate.altitude)) {
38 const drop = (geoItem.coordinate.altitude - _groundHeight) * scene.verticalScale * scene.terrainScale
39 return isFinite(drop) ? Math.max(0, drop) : 0
42 /// Drop line diameter: constant apparent width, like the marker
43 /// (built-in QtQuick3D meshes are 100 units across)
44 readonly property real dropLineScale: _camera
45 ? (ScreenTools.defaultFontPixelHeight / 8) * _camera.distance * _camera.unitsPerPixelAtUnitDistance / 100
48 readonly property var _camera: scene ? scene.camera : null
50 // Rendered-terrain height (true meters) under the marker; terrainHeightAt
51 // is not a binding dependency, so re-sampled explicitly as terrain tiles
52 // arrive and when the marker moves. Keyed on lat/lon so altitude-only
53 // coordinate changes (e.g. terrain-bias updates) skip the resample.
54 property real _groundHeight: 0
55 readonly property real _latitude: geoItem ? geoItem.coordinate.latitude : NaN
56 readonly property real _longitude: geoItem ? geoItem.coordinate.longitude : NaN
58 function _updateGroundHeight() {
59 _groundHeight = (surfaceModel && geoItem && geoItem.coordinate.isValid)
60 ? surfaceModel.terrainHeightAt(geoItem.coordinate) : 0
63 Component.onCompleted: _updateGroundHeight()
64 onSurfaceModelChanged: _updateGroundHeight()
65 on_LatitudeChanged: _updateGroundHeight()
66 on_LongitudeChanged: _updateGroundHeight()
69 target: root.surfaceModel
70 function onTerrainHeightsChanged() { root._updateGroundHeight() }
73 x: geoItem ? -geoItem.x : 0
74 y: geoItem ? -geoItem.y : 0
75 visible: dropLength > 0
76 opacity: scene ? scene.terrainScale : 0
77 scene: geoItem ? geoItem.scene : null
78 surfaceModel: geoItem ? geoItem.surfaceModel : null
79 center: geoItem ? geoItem.coordinate : QtPositioning.coordinate()
81 ? (indicatorSize / 2) * _camera.distance * _camera.unitsPerPixelAtUnitDistance
83 strokeColor: "transparent"
85 // Translucent black (ADS-B shadow convention) so the colored drop line
86 // stays visible where it meets the shadow
87 fillColor: Qt.alpha("black", 0.35)