private void FindExits() { ExitGroups.Clear(); TaxiNode groupStartNode = null; // First, group all nodes foreach (TaxiNode node in RunwayNodes) { foreach (TaxiEdge edge in node.IncomingEdges) { if (edge.IsRunway) { continue; } if (edge.ReverseEdge == null) { continue; } TaxiEdge actualEdge = edge.ReverseEdge; double exitAngle = VortexMath.TurnAngle(actualEdge.Bearing, Bearing); if (Math.Abs(exitAngle) > VortexMath.Deg135Rad) { continue; } if (groupStartNode == null) { groupStartNode = node; ExitGroups.Add(node, new List <ExitPoint>()); } double landingLengthUsed = VortexMath.DistanceKM(DisplacedNode, node); // 'distance used' actually if (VortexMath.DistanceKM(groupStartNode, node) < 0.200) { ExitGroups[groupStartNode].Add(new ExitPoint() { OffRunwayNode = actualEdge.EndNode, OnRunwayNode = node, LandingLengthUsed = landingLengthUsed, TurnAngle = exitAngle }); } else { // add to new group groupStartNode = node; ExitGroups.Add(node, new List <ExitPoint>()); ExitGroups[groupStartNode].Add(new ExitPoint() { OffRunwayNode = actualEdge.EndNode, OnRunwayNode = node, LandingLengthUsed = landingLengthUsed, TurnAngle = exitAngle }); } } } if (ExitGroups.Count == 0) { return; // todo: add warning } // Then pick groups based upon distance List <ExitPoint> minimumExit = null; List <ExitPoint> mediumExit = null; List <ExitPoint> longExit = null; List <ExitPoint> maxExit = null; foreach (KeyValuePair <TaxiNode, List <ExitPoint> > exitGroup in ExitGroups.OrderBy(eg => eg.Value.First().LandingLengthUsed)) { if (minimumExit == null || minimumExit.First().LandingLengthUsed < VortexMath.Feet4000Km) { minimumExit = exitGroup.Value; } else if (mediumExit == null || mediumExit.First().LandingLengthUsed < VortexMath.Feet5000Km) { mediumExit = exitGroup.Value; } else if (longExit == null || longExit.First().LandingLengthUsed < VortexMath.Feet6500Km) { longExit = exitGroup.Value; } else { maxExit = exitGroup.Value; } } ExitGroups.Clear(); if (minimumExit != null) { ExitGroups.Add(minimumExit.First().OnRunwayNode, minimumExit); } if (mediumExit != null) { ExitGroups.Add(mediumExit.First().OnRunwayNode, mediumExit); } if (longExit != null) { ExitGroups.Add(longExit.First().OnRunwayNode, longExit); } if (maxExit != null) { ExitGroups.Add(maxExit.First().OnRunwayNode, maxExit); } foreach (var result in ExitGroups) { Logger.Log($"{Designator} Group: {result.Key.Id}"); ExitPoint right = result.Value.Where(ep => ep.TurnAngle > 0).OrderBy(ep => ep.TurnAngle).FirstOrDefault(); ExitPoint left = result.Value.Where(ep => ep.TurnAngle < 0).OrderByDescending(ep => ep.TurnAngle).FirstOrDefault(); ExitGroups[result.Key].Clear(); if (right != null) { Logger.Log($" Right Exit: {right.OnRunwayNode.Id}->{right.OffRunwayNode.Id} {right.TurnAngle * VortexMath.Rad2Deg:0.0} {right.LandingLengthUsed * VortexMath.KmToFoot:0}ft"); ExitGroups[result.Key].Add(right); } if (left != null) { Logger.Log($" Left Exit: {left.OnRunwayNode.Id}->{left.OffRunwayNode.Id} {left.TurnAngle * VortexMath.Rad2Deg:0.0} {left.LandingLengthUsed * VortexMath.KmToFoot:0}ft"); ExitGroups[result.Key].Add(left); } } }
public int FindInboundRoutes(bool normalOutput, ProgressBar progress) { string outputPath = normalOutput ? Path.Combine(Settings.WorldTrafficGroundRoutes, "Arrival") : Settings.ArrivalFolderKML; outputPath = Path.Combine(outputPath, ICAO); Settings.DeleteDirectoryContents(outputPath); int count = 0; progress.Minimum = 0; progress.Maximum = _parkings.Count * (int)XPlaneAircraftCategory.Max; progress.Value = 0; foreach (Parking parking in _parkings) { InboundResults ir = new InboundResults(_edges, parking); progress.Value += (XPlaneAircraftCategory.Max - (parking.MaxSize + 1)); for (XPlaneAircraftCategory size = parking.MaxSize; size >= XPlaneAircraftCategory.A; size--) { // Nearest node should become 'closest to computed pushback point' FindShortestPaths(_taxiNodes, parking.NearestNode, size); // Pick the runway exit points for the selected size foreach (Runway r in _runways) { foreach (KeyValuePair <TaxiNode, List <ExitPoint> > exit in r.ExitGroups) { double bestDistance = double.MaxValue; double bestTurnAngle = double.MaxValue; ExitPoint bestExit = null; foreach (ExitPoint ep in exit.Value) { if (ep.OffRunwayNode.NextNodeToTarget != ep.OnRunwayNode) { if ((ep.OffRunwayNode.DistanceToTarget < bestDistance / 1.2) || (bestTurnAngle - Math.Abs(ep.TurnAngle) > VortexMath.Deg020Rad) || (ep.OffRunwayNode.DistanceToTarget < bestDistance && Math.Abs(ep.TurnAngle) < bestTurnAngle)) { bestExit = ep; bestDistance = ep.OffRunwayNode.DistanceToTarget; bestTurnAngle = Math.Abs(ep.TurnAngle); } } } if (bestExit != null) { ir.AddResult(size, bestExit.OnRunwayNode, bestExit.OffRunwayNode, r, bestExit.LandingLengthUsed); } } } progress.Value++; progress.Update(); } count += ir.WriteRoutes(outputPath, !normalOutput); } progress.Maximum++; progress.Value++; progress.Maximum--; progress.Value = progress.Maximum; // Work around for a side effect caused by windows animating the progress bar progress.Update(); return(count); }