Skip to content

Commit fb78961

Browse files
authored
fix(flight-data): stop duplicated vehicle overlay markers (#27)
* fix(flight-data): scope vehicle marker to its point * test(flight-data): prevent overlay marker duplication * docs(porting): record vehicle overlay rendering handoff * test(flight-data): exercise live overlay composition * docs(porting): refresh vehicle overlay verification
1 parent 5a1c11e commit fb78961

3 files changed

Lines changed: 111 additions & 12 deletions

File tree

Controls/MapView.cs

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,6 @@ public MapView() {
165165
map.Layers.Add(_movingBase);
166166
map.Layers.Add(_cameraTarget);
167167
map.Layers.Add(_otherVehicles);
168-
_vehicle.Style = MavMarker.Vehicle(0);
169168
map.Layers.Add(_vehicle);
170169

171170
map.Navigator.Limiter = new Mapsui.Limiting.ViewportLimiterKeepWithinExtent();
@@ -336,12 +335,7 @@ private void UpdateVehicle() {
336335

337336
var (x, y) = SphericalMercator.FromLonLat(cs.lng, cs.lat);
338337
var pt = new MPoint(x, y);
339-
_vehicle.Style = MavMarker.Vehicle(cs.yaw);
340-
_vehicle.Clear();
341-
_vehicle.Add(new PointFeature(pt));
342-
343-
DrawBearingOverlays(mav, pt);
344-
_vehicle.DataHasChanged();
338+
PopulateVehicleLayer(mav, pt, Map.Navigator.Viewport.Resolution);
345339

346340
UpdateMapRotation(cs);
347341

@@ -1120,9 +1114,14 @@ private void ClearGimbalTarget() {
11201114
Line = new Pen(new Color(255, 105, 180), 2),
11211115
};
11221116

1123-
private void DrawBearingOverlays(MAVState mav, MPoint pt) {
1117+
internal void PopulateVehicleLayer(MAVState mav, MPoint point, double resolution) {
1118+
SetVehicleMarker(point, mav.cs.yaw);
1119+
DrawBearingOverlays(mav, point, resolution);
1120+
_vehicle.DataHasChanged();
1121+
}
1122+
1123+
private void DrawBearingOverlays(MAVState mav, MPoint pt, double resMpp) {
11241124
MissionPlanner.CurrentState cs = mav.cs;
1125-
double resMpp = Map.Navigator.Viewport.Resolution;
11261125
if (resMpp <= 0) {
11271126
return;
11281127
}
@@ -1165,6 +1164,16 @@ private void AddBearingLine(MPoint pt, double bearingDeg, double len, VectorStyl
11651164
_vehicle.Add(line);
11661165
}
11671166

1167+
private void SetVehicleMarker(MPoint point, double headingDeg) {
1168+
// Keep the symbol on the point feature. A layer-level symbol is also applied to the
1169+
// bearing-line and turn-radius geometries, which renders duplicate aircraft along them.
1170+
_vehicle.Style = null;
1171+
_vehicle.Clear();
1172+
var marker = new PointFeature(point);
1173+
marker.Styles.Add(MavMarker.Vehicle(headingDeg));
1174+
_vehicle.Add(marker);
1175+
}
1176+
11681177
private void AddRadiusArc(MPoint pt, double cogDeg, double radius, double resMpp) {
11691178
if (Math.Abs(radius) <= 1) {
11701179
return;
@@ -1254,9 +1263,7 @@ public void ShowSampleMarker(double lat, double lng) {
12541263
}
12551264
var (x, y) = SphericalMercator.FromLonLat(lng, lat);
12561265
var pt = new MPoint(x, y);
1257-
_vehicle.Style = MavMarker.Vehicle(0);
1258-
_vehicle.Clear();
1259-
_vehicle.Add(new PointFeature(pt));
1266+
SetVehicleMarker(pt, 0);
12601267
_vehicle.DataHasChanged();
12611268
Map.Navigator.CenterOn(pt);
12621269
}

MissionPlannerTests/Avalonia/MissionPlanner.Tests/FlightMapOverlayTests.cs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,71 @@ public void Flight_map_contains_operational_overlay_layers() {
5555
Assert.Contains("Other vehicles", names);
5656
}
5757

58+
[AvaloniaFact]
59+
public void Flight_map_scopes_vehicle_symbol_to_the_point_feature() {
60+
var map = new MapView();
61+
62+
map.ShowSampleMarker(34, 33);
63+
64+
Mapsui.Layers.WritableLayer layer = Assert.IsType<Mapsui.Layers.WritableLayer>(
65+
Assert.Single(map.Map.Layers, candidate => candidate.Name == "Vehicle"));
66+
Assert.Null(layer.Style);
67+
Mapsui.Layers.PointFeature marker = Assert.Single(
68+
layer.GetFeatures().OfType<Mapsui.Layers.PointFeature>());
69+
Mapsui.Styles.SymbolStyle style = Assert.Single(
70+
marker.Styles.OfType<Mapsui.Styles.SymbolStyle>());
71+
Assert.Equal(Mapsui.Styles.SymbolType.Triangle, style.SymbolType);
72+
}
73+
74+
[AvaloniaFact]
75+
public void Live_vehicle_bearing_and_radius_overlays_do_not_duplicate_the_symbol() {
76+
string[] settingKeys = {
77+
"GMapMarkerBase_DisplayHeading",
78+
"GMapMarkerBase_DisplayNavBearing",
79+
"GMapMarkerBase_DisplayCOG",
80+
"GMapMarkerBase_DisplayTarget",
81+
"GMapMarkerBase_DisplayRadius",
82+
};
83+
var saved = settingKeys.ToDictionary(key => key, key => Utilities.Settings.Instance[key]);
84+
try {
85+
foreach (string key in settingKeys) {
86+
Utilities.Settings.Instance[key] = bool.TrueString;
87+
}
88+
89+
using var link = new MAVLinkInterface();
90+
MAVState mav = link.MAV;
91+
mav.aptype = MAVLink.MAV_TYPE.FIXED_WING;
92+
mav.cs.yaw = 10;
93+
mav.cs.nav_bearing = 20;
94+
mav.cs.groundcourse = 30;
95+
mav.cs.target_bearing = 40;
96+
mav.cs.groundspeed = 20;
97+
mav.cs.roll = 20;
98+
var map = new MapView();
99+
100+
map.PopulateVehicleLayer(mav, new Mapsui.MPoint(1000, 2000), resolution: 2);
101+
102+
Mapsui.Layers.WritableLayer layer = Assert.IsType<Mapsui.Layers.WritableLayer>(
103+
Assert.Single(map.Map.Layers, candidate => candidate.Name == "Vehicle"));
104+
Assert.Null(layer.Style);
105+
Mapsui.IFeature[] features = layer.GetFeatures().ToArray();
106+
Mapsui.Layers.PointFeature marker = Assert.Single(
107+
features.OfType<Mapsui.Layers.PointFeature>());
108+
Assert.Single(marker.Styles.OfType<Mapsui.Styles.SymbolStyle>());
109+
Mapsui.Nts.GeometryFeature[] overlays =
110+
features.OfType<Mapsui.Nts.GeometryFeature>().ToArray();
111+
Assert.Equal(5, overlays.Length);
112+
Assert.All(overlays, overlay => {
113+
Assert.Contains(overlay.Styles, style => style is Mapsui.Styles.VectorStyle);
114+
Assert.DoesNotContain(overlay.Styles, style => style is Mapsui.Styles.SymbolStyle);
115+
});
116+
} finally {
117+
foreach ((string key, string? value) in saved) {
118+
Utilities.Settings.Instance[key] = value;
119+
}
120+
}
121+
}
122+
58123
[Theory]
59124
[InlineData(Firmwares.ArduCopter2, 1, 2, 150, true)]
60125
[InlineData(Firmwares.ArduCopter2, 1, 3, 150, true)]

Porting/STATUS.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,33 @@
22

33
Updated: **2026-08-31**.
44

5+
## Flight Data vehicle-overlay marker rendering
6+
7+
- Dedicated branch `fix/flight-data-vehicle-overlay-rendering` starts from pulled `master`
8+
`5a1c11ea3`. Commit `7379e6dde` scopes the active aircraft triangle to its point feature;
9+
commit `109872c5e` adds the rendering regression test, and reviewer follow-up `a4f218da0`
10+
exercises the complete live fixed-wing layer with all four bearing lines and its radius arc.
11+
- Flight Data previously assigned `MavMarker.Vehicle` as the style of the complete `Vehicle`
12+
layer, then added heading, course, navigation-bearing, target-bearing and turn-radius geometries
13+
to that same layer. Mapsui consequently painted the aircraft symbol on those geometries too,
14+
producing duplicated arrowheads and a striped fan along the turn-radius arc. The layer now has
15+
no shared symbol style, while its aircraft point owns the triangle style; bearing and radius
16+
features retain only their vector styles. Log Browse sample markers use the same safe path.
17+
- `dotnet build MissionPlanner.csproj -c Release -m:1 --no-restore` succeeds with **0 warnings / 0
18+
errors**, and the focused `FlightMapOverlayTests` pass **28/28**. The complete Avalonia suite ran
19+
**1555** cases: **1554 passed** and only the existing environment-sensitive
20+
`VideoSourceResolverTests.NormalizesCommonStreamSources` `/dev/video0` case failed. Neither the
21+
resolver nor its test changes on this branch.
22+
- The worktree still contains the user's five pre-existing modifications in
23+
`Drivers/inf2cat.bat`, `Drivers/uninstall_drivers.bat`, `ExtLibs/Mavlink/regenerate.bat`,
24+
`ExtLibs/Mavlink/updatexmls.bat` and `graphs/updatexmls.bat`; none is staged or included in these
25+
commits. Claude remains disabled.
26+
- No code blocker remains. The next executable acceptance step is a fixed-wing SITL flight with
27+
bearing and turn-radius overlays enabled: confirm exactly one aircraft triangle is rendered and
28+
the colored guide lines and radius arc remain clean. The separately observed Flight Planner
29+
viewport jump between a distant persisted home and new waypoints is intentionally excluded from
30+
this branch and should be handled after this fix lands.
31+
532
## Flight Data Auto Pan settings parity
633

734
- Dedicated branch `fix/flight-data-autopan-settings` carries four granular code/test commits:

0 commit comments

Comments
 (0)