7import QGroundControl.Controls
8import QGroundControl.FlyView
12 objectName: "proximityRadarMapView"
13 visible: proximityValues.telemetryAvailable && coordinate.isValid
15 property var vehicle /// Vehicle object, undefined for ADSB vehicle
17 property double heading: vehicle ? vehicle.heading.value : Number.NaN ///< Vehicle heading, NAN for none
19 anchorPoint.x: vehicleItem.width / 2
20 anchorPoint.y: vehicleItem.height / 2
22 property real _ratio: 1
24 // Density-independent stroke width for the sensor arcs and limit circle
25 readonly property real _strokeWidth: ScreenTools.defaultFontPixelHeight / 4
27 // Each of the 8 sensor sectors covers 45 degrees, centered on its rotation direction.
28 // Sector 0 is vehicle-forward, which is -90 degrees in PathAngleArc coordinates.
29 readonly property real _sectorSweepAngle: 360 / 8
30 readonly property real _firstSectorStartAngle: -90 - (_sectorSweepAngle / 2)
32 // Cap the rendered circle size to keep item geometry sane at deep zoom levels. The clamp only
33 // engages once the circle's rim is more than two viewport-widths from the vehicle, so the
34 // clamped (wrong-sized) portion is offscreen and never visible.
35 readonly property real _maximumDiameter: Math.max(map.width, map.height) * 4
38 var scaleLinePixelLength = 100
39 var leftCoord = map.toCoordinate(Qt.point(0, 0), false /* clipToViewPort */)
40 var rightCoord = map.toCoordinate(Qt.point(scaleLinePixelLength, 0), false /* clipToViewPort */)
41 var scaleLineMeters = leftCoord.distanceTo(rightCoord)
42 if (!isFinite(scaleLineMeters) || scaleLineMeters <= 0) {
46 _ratio = scaleLinePixelLength / scaleLineMeters
49 function _clampedDiameter(requestedDiameter) {
50 if (!isFinite(requestedDiameter) || requestedDiameter < 0) {
53 return Math.min(requestedDiameter, _maximumDiameter)
56 function _sectorRadius(sectorIndex) {
57 var sectorDistance = proximityValues.rgRotationValues[sectorIndex]
58 // Clamp for the same reason as _clampedDiameter: keep geometry coordinates sane at deep zoom
59 return isNaN(sectorDistance) ? 0 : _clampedDiameter(sectorDistance * _ratio * 2) / 2
62 function _sectorColor(sectorIndex) {
63 return isNaN(proximityValues.rgRotationValues[sectorIndex]) ? Qt.rgba(0, 0, 0, 0) : Qt.rgba(1, 0, 0, 1)
66 function _sectorStartAngle(sectorIndex) {
67 return _firstSectorStartAngle + (sectorIndex * _sectorSweepAngle)
70 ProximityRadarValues {
72 vehicle: _root.vehicle
77 function onWidthChanged() { scaleTimer.restart() }
78 function onHeightChanged() { scaleTimer.restart() }
79 function onZoomLevelChanged() { scaleTimer.restart() }
87 onTriggered: calcSize()
92 width: detectionLimitCircle.width
93 height: detectionLimitCircle.height
96 Component.onCompleted: calcSize()
98 // Sensor arcs are drawn with Shape rather than Canvas since Shape renders as scene graph
99 // geometry and doesn't require a backing store allocation which scales with item size.
100 // Each 45 degree sector is centered on its rotation direction: sector 0 is vehicle-forward.
103 anchors.fill: detectionLimitCircle
105 transform: Rotation {
106 origin.x: detectionLimitCircle.width / 2
107 origin.y: detectionLimitCircle.height / 2
108 angle: isNaN(heading) ? 0 : heading
111 // ShapePath is not an Item so a Repeater can't be used; an Instantiator which appends
112 // to the Shape's data list creates the equivalent of one ShapePath per sensor sector.
116 delegate: ShapePath {
117 required property int index
119 strokeColor: _sectorColor(index)
120 strokeWidth: _strokeWidth
121 fillColor: "transparent"
124 centerX: vehicleSensors.width / 2
125 centerY: vehicleSensors.height / 2
126 radiusX: _sectorRadius(index)
128 startAngle: _sectorStartAngle(index)
129 sweepAngle: _sectorSweepAngle
133 onObjectAdded: (index, object) => vehicleSensors.data.push(object)
138 id: detectionLimitCircle
139 width: _clampedDiameter(proximityValues.maxDistance * 2 * _ratio)
141 color: Qt.rgba(1,1,1,0)
142 border.color: Qt.rgba(1,1,1,1)
143 border.width: _strokeWidth
146 transform: Rotation {
147 origin.x: detectionLimitCircle.width / 2
148 origin.y: detectionLimitCircle.height / 2
149 angle: isNaN(heading) ? 0 : heading