예제 #1
0
        public void Preprocess()
        {
            // Filter out nodes with links (probably nodes for the vehicle network)
            _taxiNodes = _nodeDict.Values.Where(v => v.IncomingEdges.Count > 0);

            // Filter out parkings with operation type none.
            _parkings = _parkings.Where(p => p.Operation != OperationType.None).ToList();

            // With unneeded nodes gone, parse the lat/lon string and convert the values to radians
            foreach (TaxiNode v in _taxiNodes)
            {
                v.ComputeLonLat();
            }

            // Compute distance and bearing of each edge
            foreach (TaxiEdge edge in _edges)
            {
                edge.Compute();
            }

            Dictionary <XPlaneAircraftCategory, int> numberOfParkingsPerCategory = new Dictionary <XPlaneAircraftCategory, int>();

            for (XPlaneAircraftCategory cat = XPlaneAircraftCategory.A; cat < XPlaneAircraftCategory.Max; cat++)
            {
                numberOfParkingsPerCategory[cat] = 0;
            }

            Dictionary <WorldTrafficAircraftType, int> numberOfParkingsPerWTType = new Dictionary <WorldTrafficAircraftType, int>();

            for (WorldTrafficAircraftType cat = WorldTrafficAircraftType.Fighter; cat <= WorldTrafficAircraftType.Max; cat++)
            {
                numberOfParkingsPerWTType[cat] = 0;
            }

            foreach (Parking parking in _parkings)
            {
                parking.DetermineWtTypes();
                parking.DetermineTaxiOutLocation(_taxiNodes); // Move this to first if we need pushback info in the parking def
                numberOfParkingsPerCategory[parking.MaxSize]++;

                foreach (XPlaneAircraftType wtt in parking.XpTypes)
                {
                    WorldTrafficAircraftType t = AircraftTypeConverter.WTTypeFromXPlaneTypeAndCat(parking.MaxSize, wtt);
                    numberOfParkingsPerWTType[t]++;
                }
                //parking.FindNearestLine(_lines);
            }

            Log($"Parkings by Category: {string.Join(" ", numberOfParkingsPerCategory.Select(kvp => kvp.Key.ToString() + ": " + kvp.Value.ToString()))}");
            Log($"Parkings by WorldTraffic type:\n\t{string.Join("\n\t", numberOfParkingsPerWTType.Select(kvp => $"{kvp.Key.ToString(),-15}: {kvp.Value.ToString()}"))}".Replace("Max", "Undefined"));

            StringBuilder sb = new StringBuilder();

            _flows.Analyze(sb);

            foreach (Runway r in _runways)
            {
                r.Analyze(_taxiNodes, _edges);
            }
        }
예제 #2
0
        public ResultRoute(XPlaneAircraftCategory size)
        {
            Parkings = new List <Parking>();

            Distance = double.MaxValue;

            MaxSize = size;
            MinSize = 0;
        }
예제 #3
0
        /// <summary>
        /// Add a resulting route for a specific runway exit and size
        /// </summary>
        /// <param name="maxSizeCurrentResult">The maximum size allowed on the current route</param>
        /// <param name="parkingNode">The runway node for this exit</param>
        public void AddResult(XPlaneAircraftCategory maxSizeCurrentResult, TaxiNode parkingNode, Parking parking, TaxiNode entryGroupNode, EntryPoint entryPoint)
        {
            if (!_results.ContainsKey(parkingNode))
            {
                _results[parkingNode] = new Dictionary <XPlaneAircraftCategory, ResultRoute>();
            }

            Dictionary <XPlaneAircraftCategory, ResultRoute> originResults = _results[parkingNode];

            // If no results yet for this node, just add the current route
            if (originResults.Count == 0)
            {
                ResultRoute theRoute = ResultRoute.ExtractRoute(_edges, parkingNode, maxSizeCurrentResult);
                if (!theRoute.HasNode(entryPoint.OnRunwayNode))
                {
                    originResults.Add(maxSizeCurrentResult, theRoute);
                    originResults[maxSizeCurrentResult].RunwayEntryPoint      = entryPoint;
                    originResults[maxSizeCurrentResult].AvailableRunwayLength = entryPoint.TakeoffLengthRemaining;
                }
            }
            else
            {
                XPlaneAircraftCategory minSize = originResults.Min(or => or.Key);
                if (originResults[minSize].Distance > parkingNode.DistanceToTarget)
                {
                    if (minSize > maxSizeCurrentResult)
                    {
                        ResultRoute theRoute = ResultRoute.ExtractRoute(_edges, parkingNode, maxSizeCurrentResult);
                        if (!theRoute.HasNode(entryPoint.OnRunwayNode))
                        {
                            originResults.Add(maxSizeCurrentResult, theRoute);
                            originResults[maxSizeCurrentResult].RunwayEntryPoint      = entryPoint;
                            originResults[maxSizeCurrentResult].AvailableRunwayLength = entryPoint.TakeoffLengthRemaining;

                            originResults[minSize].MinSize = (maxSizeCurrentResult + 1);
                        }
                    }
                    else if (minSize == maxSizeCurrentResult)
                    {
                        ResultRoute theRoute = ResultRoute.ExtractRoute(_edges, parkingNode, maxSizeCurrentResult);
                        if (!theRoute.HasNode(entryPoint.OnRunwayNode))
                        {
                            originResults[minSize] = theRoute;
                            originResults[minSize].RunwayEntryPoint      = entryPoint;
                            originResults[minSize].AvailableRunwayLength = entryPoint.TakeoffLengthRemaining;
                        }
                    }
                }
            }

            // Nasty overkill to make sure parkings with the same 'nearest' node will have routes generated
            foreach (KeyValuePair <XPlaneAircraftCategory, ResultRoute> result in originResults)
            {
                result.Value.AddParking(parking);
            }
        }
예제 #4
0
        public Parking(Airport airport)
            : base()
        {
            _airport = airport;
            AlternateAfterPushBack = null;
            PushBackLatitude       = 0;
            PushBackLongitude      = 0;

            MaxSize   = XPlaneAircraftCategory.F;
            Operation = OperationType.Airline;
            Operators = Enumerable.Empty <string>();
        }
예제 #5
0
        public TaxiEdge(TaxiNode startNode, TaxiNode endNode, bool isRunway, XPlaneAircraftCategory maxSize, string linkName)
        {
            StartNode = startNode;
            EndNode   = endNode;

            IsRunway    = isRunway;
            MaxCategory = maxSize;
            LinkName    = linkName;

            ActiveForRunways = new List <string>();
            ActiveZone       = false;

            ReverseEdge = null;
        }
예제 #6
0
        public int FindOutboundRoutes(bool normalOutput, ProgressBar progress)
        {
            string outputPath = normalOutput ? Path.Combine(Settings.WorldTrafficGroundRoutes, "Departure") : Settings.DepartureFolderKML;

            outputPath = Path.Combine(outputPath, ICAO);
            Settings.DeleteDirectoryContents(outputPath);

            int count = 0;

            progress.Minimum = 0;
            progress.Maximum = _runways.Sum(r => r.EntryGroups.Count) * (int)XPlaneAircraftCategory.Max;
            progress.Value   = 0;

            // for each runway
            foreach (Runway runway in _runways)
            {
                // for each takeoff spot
                foreach (KeyValuePair <TaxiNode, List <EntryPoint> > entryGroup in runway.EntryGroups)
                {
                    OutboundResults or = new OutboundResults(_edges, runway);
                    // for each size
                    for (XPlaneAircraftCategory size = XPlaneAircraftCategory.F; size >= XPlaneAircraftCategory.A; size--)
                    {
                        foreach (EntryPoint ep in entryGroup.Value)
                        {
                            // find shortest path from each parking to each takeoff spot considering each entrypoint
                            FindShortestPaths(_taxiNodes, ep.OffRunwayNode, size);
                            foreach (Parking parking in _parkings)
                            {
                                or.AddResult(size, parking.NearestNode, parking, entryGroup.Key, ep);
                            }
                        }
                        progress.Value++;
                        progress.Update();
                    }
                    count += or.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);
        }
예제 #7
0
        /// <summary>
        /// Add a resulting route for a specific runway exit and size
        /// </summary>
        /// <param name="maxSizeCurrentResult">The maximum size allowed on the current route</param>
        /// <param name="runwayExitNode">The runway node for this exit</param>
        /// <param name="pathStartNode">The frist node 'departing' the runway</param>
        /// <param name="r">The runway it self</param>
        public void AddResult(XPlaneAircraftCategory maxSizeCurrentResult, TaxiNode runwayExitNode, TaxiNode pathStartNode, Runway r, double availableRunwayLength)
        {
            if (!_results.ContainsKey(runwayExitNode))
            {
                _results[runwayExitNode] = new Dictionary <XPlaneAircraftCategory, ResultRoute>();
            }

            Dictionary <XPlaneAircraftCategory, ResultRoute> originResults = _results[runwayExitNode];

            // If no results yet for this node, just add the current route
            if (originResults.Count == 0)
            {
                originResults.Add(maxSizeCurrentResult, ResultRoute.ExtractRoute(_edges, pathStartNode, maxSizeCurrentResult));
                originResults[maxSizeCurrentResult].Runway = r;
                originResults[maxSizeCurrentResult].AvailableRunwayLength = availableRunwayLength;
            }
            else
            {
                XPlaneAircraftCategory minSize = originResults.Min(or => or.Key);
                if (originResults[minSize].Distance > pathStartNode.DistanceToTarget)
                {
                    if (minSize > maxSizeCurrentResult)
                    {
                        originResults.Add(maxSizeCurrentResult, ResultRoute.ExtractRoute(_edges, pathStartNode, maxSizeCurrentResult));
                        originResults[maxSizeCurrentResult].Runway = r;
                        originResults[maxSizeCurrentResult].AvailableRunwayLength = availableRunwayLength;
                        originResults[minSize].MinSize = (maxSizeCurrentResult + 1);
                    }
                    else if (minSize == maxSizeCurrentResult)
                    {
                        originResults[minSize]        = ResultRoute.ExtractRoute(_edges, pathStartNode, maxSizeCurrentResult);
                        originResults[minSize].Runway = r;
                        originResults[minSize].AvailableRunwayLength = availableRunwayLength;
                    }
                }
            }
        }
예제 #8
0
        public static WorldTrafficAircraftType WTTypeFromXPlaneTypeAndCat(XPlaneAircraftCategory category, XPlaneAircraftType xpType)
        {
            switch (category)
            {
            case XPlaneAircraftCategory.A:
                switch (xpType)
                {
                case XPlaneAircraftType.Helo:
                    return(WorldTrafficAircraftType.Helo);

                case XPlaneAircraftType.Fighter:
                    return(WorldTrafficAircraftType.Fighter);

                case XPlaneAircraftType.Prop:
                case XPlaneAircraftType.TurboProp:
                    return(WorldTrafficAircraftType.LightProp);

                case XPlaneAircraftType.Jet:
                    return(WorldTrafficAircraftType.LightJet);

                default:
                    break;
                }
                break;

            case XPlaneAircraftCategory.B:
                switch (xpType)
                {
                case XPlaneAircraftType.TurboProp:
                    return(WorldTrafficAircraftType.MediumProp);

                case XPlaneAircraftType.Jet:
                    return(WorldTrafficAircraftType.MediumJet);

                default:
                    break;
                }
                break;

            case XPlaneAircraftCategory.C:
                switch (xpType)
                {
                case XPlaneAircraftType.TurboProp:
                    return(WorldTrafficAircraftType.MediumProp);

                case XPlaneAircraftType.Jet:
                    return(WorldTrafficAircraftType.LargeJet);

                default:
                    break;
                }
                break;

            case XPlaneAircraftCategory.D:
                switch (xpType)
                {
                case XPlaneAircraftType.TurboProp:
                    return(WorldTrafficAircraftType.LargeProp);

                case XPlaneAircraftType.HeavyJet:
                    return(WorldTrafficAircraftType.HeavyJet);

                default:
                    break;
                }
                break;

            case XPlaneAircraftCategory.E:
                switch (xpType)
                {
                case XPlaneAircraftType.HeavyJet:
                    return(WorldTrafficAircraftType.HeavyJet);

                default:
                    break;
                }
                break;

            case XPlaneAircraftCategory.F:
                switch (xpType)
                {
                case XPlaneAircraftType.HeavyJet:
                    return(WorldTrafficAircraftType.SuperHeavy);

                default:
                    break;
                }
                break;
            }
            return(WorldTrafficAircraftType.Max);
        }
예제 #9
0
        public static IEnumerable <WorldTrafficAircraftType> WTTypesFromXPlaneLimits(XPlaneAircraftCategory minCategory, XPlaneAircraftCategory maxCategory, OperationType operationType)
        {
            List <WorldTrafficAircraftType> wtTypes = new List <WorldTrafficAircraftType>()
            {
                WorldTrafficAircraftType.Fighter
            };

            for (XPlaneAircraftCategory cat = minCategory; cat <= maxCategory; cat++)
            {
                switch (cat)
                {
                case XPlaneAircraftCategory.A:
                    switch (operationType)
                    {
                    case OperationType.Airline:
                    case OperationType.Cargo:
                    case OperationType.GeneralAviation:
                        wtTypes.AddRange(new WorldTrafficAircraftType[] { WorldTrafficAircraftType.LightJet, WorldTrafficAircraftType.LightProp });
                        break;

                    case OperationType.Military:
                        wtTypes.AddRange(new WorldTrafficAircraftType[] { WorldTrafficAircraftType.LightJet, WorldTrafficAircraftType.LightProp, WorldTrafficAircraftType.Fighter });
                        break;

                    case OperationType.None:
                    default:
                        break;
                    }
                    break;

                case XPlaneAircraftCategory.B:
                    switch (operationType)
                    {
                    case OperationType.Airline:
                    case OperationType.Cargo:
                    case OperationType.GeneralAviation:
                        wtTypes.AddRange(new WorldTrafficAircraftType[] { WorldTrafficAircraftType.MediumJet, WorldTrafficAircraftType.MediumProp, WorldTrafficAircraftType.LightJet, WorldTrafficAircraftType.LightProp });
                        break;

                    case OperationType.Military:
                        wtTypes.AddRange(new WorldTrafficAircraftType[] { WorldTrafficAircraftType.MediumJet, WorldTrafficAircraftType.MediumProp, WorldTrafficAircraftType.LightJet, WorldTrafficAircraftType.LightProp, WorldTrafficAircraftType.Fighter });
                        break;

                    case OperationType.None:
                    default:
                        break;
                    }
                    break;

                case XPlaneAircraftCategory.C:
                    switch (operationType)
                    {
                    case OperationType.Airline:
                    case OperationType.Cargo:
                        wtTypes.AddRange(new WorldTrafficAircraftType[] { WorldTrafficAircraftType.LargeJet, WorldTrafficAircraftType.LargeProp });
                        break;

                    case OperationType.GeneralAviation:
                    case OperationType.Military:
                        wtTypes.AddRange(new WorldTrafficAircraftType[] { WorldTrafficAircraftType.MediumJet, WorldTrafficAircraftType.MediumProp, WorldTrafficAircraftType.LargeJet, WorldTrafficAircraftType.LargeProp });
                        break;

                    case OperationType.None:
                    default:
                        break;
                    }
                    break;

                case XPlaneAircraftCategory.D:
                    switch (operationType)
                    {
                    case OperationType.Airline:
                    case OperationType.Cargo:
                    case OperationType.GeneralAviation:
                    case OperationType.Military:
                        wtTypes.AddRange(new WorldTrafficAircraftType[] { WorldTrafficAircraftType.LargeJet, WorldTrafficAircraftType.HeavyJet, WorldTrafficAircraftType.LargeProp });
                        break;

                    case OperationType.None:
                    default:
                        break;
                    }
                    break;

                case XPlaneAircraftCategory.E:
                    switch (operationType)
                    {
                    case OperationType.Airline:
                    case OperationType.Cargo:
                    case OperationType.GeneralAviation:
                    case OperationType.Military:
                        wtTypes.AddRange(new WorldTrafficAircraftType[] { WorldTrafficAircraftType.HeavyJet, WorldTrafficAircraftType.LargeProp });
                        break;

                    case OperationType.None:
                    default:
                        break;
                    }
                    break;

                case XPlaneAircraftCategory.F:
                    switch (operationType)
                    {
                    case OperationType.Airline:
                    case OperationType.Cargo:
                    case OperationType.GeneralAviation:
                    case OperationType.Military:
                        wtTypes.AddRange(new WorldTrafficAircraftType[] { WorldTrafficAircraftType.HeavyJet, WorldTrafficAircraftType.SuperHeavy });
                        break;

                    case OperationType.None:
                    default:
                        break;
                    }
                    break;
                }
            }
            return(wtTypes.Distinct());
        }
예제 #10
0
 internal void SetMetaData(XPlaneAircraftCategory maxSize, string operation, IEnumerable <string> operators)
 {
     MaxSize   = maxSize;
     Operation = OperationTypeConverter.FromString(operation);
     Operators = operators;
 }
예제 #11
0
        /// <summary>
        /// Dijkstra... goes through the full network finding shortest path from every node to the target so
        /// that afterwards we can cherrypick the starting nodes we are actually interested in.
        /// </summary>
        /// <param name="nodes">The node network</param>
        /// <param name="targetNode">Here do we go now</param>
        /// <param name="targetCategory">Minimum Cat (A-F) that needs to be supported by the route</param>
        private static void FindShortestPaths(IEnumerable <TaxiNode> nodes, TaxiNode targetNode, XPlaneAircraftCategory targetCategory)
        {
            List <TaxiNode> untouchedNodes = nodes.ToList();
            List <TaxiNode> touchedNodes   = new List <TaxiNode>();

            // Reset previously found paths
            foreach (TaxiNode node in nodes)
            {
                node.DistanceToTarget = double.MaxValue;
                node.NextNodeToTarget = null;
            }

            // Setup the targetnode
            targetNode.DistanceToTarget = 0;
            targetNode.NextNodeToTarget = null;

            // Assign distances to all incoming edges of the target
            foreach (TaxiEdge incoming in targetNode.IncomingEdges)
            {
                // Skip taxiways that are too small
                if (targetCategory > incoming.MaxCategory)
                {
                    continue;
                }

                // Mark the other side of the incoming edge as touched...
                if (untouchedNodes.Contains(incoming.StartNode) && !touchedNodes.Contains(incoming.StartNode))
                {
                    untouchedNodes.Remove(incoming.StartNode);
                    touchedNodes.Add(incoming.StartNode);
                }

                // And set the properties of the path
                incoming.StartNode.DistanceToTarget = incoming.DistanceKM;
                incoming.StartNode.NextNodeToTarget = targetNode;
                incoming.StartNode.NameToTarget     = incoming.LinkName;
                incoming.StartNode.PathIsRunway     = incoming.IsRunway;
                incoming.StartNode.BearingToTarget  = VortexMath.BearingRadians(incoming.StartNode, targetNode);
            }

            // Remove the target node completely, it's done.
            untouchedNodes.Remove(targetNode);

            // instantiate the comparer for taxinode list sorting
            ShortestPathComparer spc = new ShortestPathComparer();

            // Now rinse and repeat will we still have 'touched nodes' (unprocessed nodes with a path to the target)
            while (touchedNodes.Count() > 0)
            {
                // Pick the next( touched but not finished) node to process, that is: the one which currently has the shortest path to the target
                touchedNodes.Sort(spc);
                TaxiNode currentNode = touchedNodes.First();

                // And set the distances for the nodes with link towards the current node
                foreach (TaxiEdge incoming in currentNode.IncomingEdges)
                {
                    // Skip taxiways that are too small
                    if (targetCategory > incoming.MaxCategory)
                    {
                        continue;
                    }

                    // Avoid runways unless they are the only option.
                    double distanceToCurrent = incoming.DistanceKM;
                    if (incoming.IsRunway)
                    {
                        distanceToCurrent += 2.0;
                    }

                    // If the incoming link + the distance from the current node to the target is smaller
                    // than the so far shortest distance from the node on the otherside of the incoming link to
                    // the target... reroute the path from the otherside through the current node.
                    if ((distanceToCurrent + currentNode.DistanceToTarget) < incoming.StartNode.DistanceToTarget)
                    {
                        if (untouchedNodes.Contains(incoming.StartNode) && !touchedNodes.Contains(incoming.StartNode))
                        {
                            // The 'otherside' node is now ready to be processed
                            untouchedNodes.Remove(incoming.StartNode);
                            touchedNodes.Add(incoming.StartNode);
                        }

                        // Update the path properties
                        incoming.StartNode.DistanceToTarget = (currentNode.DistanceToTarget + distanceToCurrent);
                        incoming.StartNode.NextNodeToTarget = currentNode;
                        incoming.StartNode.NameToTarget     = incoming.LinkName;
                        incoming.StartNode.PathIsRunway     = incoming.IsRunway;
                        incoming.StartNode.BearingToTarget  = incoming.Bearing;
                    }
                }

                // And the current is done.
                touchedNodes.Remove(currentNode);
            }
        }
예제 #12
0
        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);
        }
예제 #13
0
        public int WriteRoutes(string outputPath, bool kml)
        {
            int count = 0;

            foreach (KeyValuePair <TaxiNode, Dictionary <XPlaneAircraftCategory, ResultRoute> > sizeRoutes in _results)
            {
                for (XPlaneAircraftCategory size = XPlaneAircraftCategory.Max - 1; size >= XPlaneAircraftCategory.A; size--)
                {
                    if (sizeRoutes.Value.ContainsKey(size))
                    {
                        ResultRoute route = sizeRoutes.Value[size];
                        if (route.TargetNode == null)
                        {
                            continue;
                        }

                        if (route.AvailableRunwayLength < VortexMath.Feet3000Km)
                        {
                            continue;
                        }

                        foreach (Parking currentParking in route.Parkings)
                        {
                            IEnumerable <WorldTrafficAircraftType> wtTypes = AircraftTypeConverter.WTTypesFromXPlaneLimits(XPlaneAircraftCategory.A, route.MaxSize, currentParking.Operation);

                            if (route.AvailableRunwayLength < VortexMath.Feet9000Km)
                            {
                                WorldTrafficAircraftType[] big = { WorldTrafficAircraftType.SuperHeavy, WorldTrafficAircraftType.HeavyJet };
                                wtTypes = wtTypes.Except(big);
                            }
                            if (route.AvailableRunwayLength < VortexMath.Feet6500Km)
                            {
                                WorldTrafficAircraftType[] big = { WorldTrafficAircraftType.LargeJet };
                                wtTypes = wtTypes.Except(big);
                            }
                            if (route.AvailableRunwayLength < VortexMath.Feet5000Km)
                            {
                                WorldTrafficAircraftType[] big = { WorldTrafficAircraftType.MediumJet, WorldTrafficAircraftType.LightJet };
                                wtTypes = wtTypes.Except(big);
                            }
                            if (route.AvailableRunwayLength < VortexMath.Feet4000Km)
                            {
                                WorldTrafficAircraftType[] big = { WorldTrafficAircraftType.LargeProp, WorldTrafficAircraftType.MediumProp };
                                wtTypes = wtTypes.Except(big);
                            }

                            if (wtTypes.Count() == 0)
                            {
                                continue;
                            }

                            IEnumerable <SteerPoint> steerPoints = BuildSteerPoints(currentParking, route);

                            if (steerPoints.Count() <= Settings.MaxSteerpoints)
                            {
                                string allSizes = string.Join(" ", wtTypes.Select(w => (int)w).OrderBy(w => w));
                                string sizeName = (wtTypes.Count() == 10) ? "all" : allSizes.Replace(" ", "");
                                string fileName = Path.Combine(outputPath, $"{currentParking.FileNameSafeName}_to_{Runway.Designator}-{route.AvailableRunwayLength * VortexMath.KmToFoot:00000}_{sizeName}");

                                int military = (currentParking.Operation == OperationType.Military) ? 1 : 0;
                                int cargo    = (currentParking.Operation == OperationType.Cargo) ? 1 : 0;

                                using (RouteWriter sw = RouteWriter.Create(kml ? 0 : 1, fileName, allSizes, cargo, military, Runway.Designator, "NOSEWHEEL"))
                                {
                                    count++;

                                    foreach (SteerPoint steerPoint in steerPoints)
                                    {
                                        sw.Write(steerPoint);
                                    }
                                }
                            }
                            else
                            {
                                Logger.Log($"Route from <{currentParking.FileNameSafeName}> to {Runway.Designator} not written. Too many steerpoints ({steerPoints.Count()} vs {Settings.MaxSteerpoints})");
                            }
                        }
                    }
                }
            }

            return(count);
        }
예제 #14
0
        /// <summary>
        /// Extract the route that starts at TaxiNode 'startNode'
        /// </summary>
        /// <param name="edges">A list of all available edges</param>
        /// <param name="startNode">The first node of the route</param>
        /// <param name="size">The maximum size for which this route is valid</param>
        /// <returns>The route as a linked list of nodes with additional informationthat will be needed when writing the route to a file</returns>
        public static ResultRoute ExtractRoute(IEnumerable <TaxiEdge> edges, TaxiNode startNode, XPlaneAircraftCategory size)
        {
            ResultRoute extracted = new ResultRoute(size);

            extracted.Runway    = null;
            extracted.StartNode = startNode;
            ulong node1 = extracted.StartNode.Id;

            extracted.Distance = startNode.DistanceToTarget;

            TaxiNode pathNode;

            pathNode = startNode.NextNodeToTarget;

            TaxiEdge sneakEdge = null;

            if (pathNode != null)
            {
                sneakEdge = edges.SingleOrDefault(e => e.StartNode.Id == node1 && e.EndNode.Id == pathNode.Id);
            }

            // Set up the first link
            extracted.RouteStart = new LinkedNode()
            {
                Node = startNode.NextNodeToTarget,
                Next = null,
                Edge = sneakEdge
            };

            LinkedNode currentLink = extracted.RouteStart;

            // And follow the path...
            while (pathNode != null)
            {
                double   currentBearing = currentLink.Node.BearingToTarget;
                ulong    node2          = pathNode.Id;
                TaxiEdge edge           = edges.Single(e => e.StartNode.Id == node1 && e.EndNode.Id == node2);

                if (pathNode.NextNodeToTarget != null && pathNode.NextNodeToTarget.DistanceToTarget > 0)
                {
                    double nextBearing = pathNode.NextNodeToTarget.BearingToTarget;
                    double turn        = VortexMath.AbsTurnAngle(currentBearing, nextBearing);

                    // This filters out very sharp turns if an alternate exists in exchange for a longer route:
                    // todo: parameters. Now => if more than 120 degrees and alternate < 45 exists use alternate
                    if (turn > VortexMath.Deg120Rad)
                    {
                        IEnumerable <TaxiEdge> altEdges = edges.Where(e => e.StartNode.Id == pathNode.NextNodeToTarget.Id &&
                                                                      e.EndNode.Id != pathNode.NextNodeToTarget.NextNodeToTarget.Id &&
                                                                      e.EndNode.Id != pathNode.Id);

                        foreach (TaxiEdge te in altEdges)
                        {
                            if (te.EndNode.DistanceToTarget < double.MaxValue)
                            {
                                double newTurn = VortexMath.AbsTurnAngle(currentBearing, te.EndNode.BearingToTarget);
                                if (newTurn < VortexMath.Deg100Rad)
                                {
                                    // Fiddling with Dijkstra results like this may generate a loop in the route
                                    // So scan it before actually using the reroute
                                    if (!hasLoop(te.EndNode, pathNode))
                                    {
                                        pathNode.NextNodeToTarget.OverrideToTarget = te.EndNode;
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    else if (turn > VortexMath.Deg005Rad)   // Any turn larger than 5 degrees: if going straight does not lead to more than 250m extra distance... go straight.
                    {
                        IEnumerable <TaxiEdge> altEdges = edges.Where(e => e.StartNode.Id == pathNode.NextNodeToTarget.Id &&
                                                                      e.EndNode.Id != pathNode.NextNodeToTarget.NextNodeToTarget.Id &&
                                                                      e.EndNode.Id != pathNode.Id);

                        foreach (TaxiEdge te in altEdges)
                        {
                            if (te.EndNode.DistanceToTarget < (pathNode.NextNodeToTarget.NextNodeToTarget.DistanceToTarget + 0.250))
                            {
                                double newTurn = VortexMath.AbsTurnAngle(currentBearing, te.EndNode.BearingToTarget);
                                if (newTurn < VortexMath.Deg005Rad)
                                {
                                    // Fiddling with Dijkstra results like this may generate a loop in the route
                                    // So scan it before actually using the reroute
                                    if (!hasLoop(te.EndNode, pathNode))
                                    {
                                        pathNode.NextNodeToTarget.OverrideToTarget = te.EndNode;
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }

                TaxiNode nextNode = (pathNode.OverrideToTarget != null) ? pathNode.OverrideToTarget : pathNode.NextNodeToTarget;

                currentLink.Next = new LinkedNode()
                {
                    Node = nextNode,
                    Next = null,
                };
                node1 = node2;

                currentLink.Edge = edge;

                currentLink          = currentLink.Next;
                extracted.TargetNode = pathNode;

                pathNode.OverrideToTarget = null;
                pathNode = nextNode;
            }

            return(extracted);
        }
예제 #15
0
        public int WriteRoutes(string outputPath, bool kml)
        {
            int count = 0;

            foreach (KeyValuePair <TaxiNode, Dictionary <XPlaneAircraftCategory, ResultRoute> > sizeRoutes in _results)
            {
                for (XPlaneAircraftCategory size = Parking.MaxSize; size >= XPlaneAircraftCategory.A; size--)
                {
                    if (sizeRoutes.Value.ContainsKey(size))
                    {
                        ResultRoute route = sizeRoutes.Value[size];

                        if (route.TargetNode == null)
                        {
                            continue;
                        }

                        if (Parking.MaxSize < route.MinSize)
                        {
                            continue;
                        }

                        XPlaneAircraftCategory validMax = (XPlaneAircraftCategory)Math.Min((int)route.MaxSize, (int)Parking.MaxSize);
                        IEnumerable <WorldTrafficAircraftType> wtTypes = AircraftTypeConverter.WTTypesFromXPlaneLimits(route.MinSize, validMax, Parking.Operation);
                        if (wtTypes.Count() == 0)
                        {
                            Logger.Log($"WARN {Parking.Name} (Max)Cat {Parking.MaxSize} Types: {string.Join(" ", Parking.XpTypes)} does not map to any WT types.");
                        }

                        if (route.AvailableRunwayLength < VortexMath.Feet5000Km)
                        {
                            WorldTrafficAircraftType[] big = { WorldTrafficAircraftType.SuperHeavy, WorldTrafficAircraftType.HeavyJet, WorldTrafficAircraftType.LargeJet, WorldTrafficAircraftType.LargeProp, WorldTrafficAircraftType.LightJet };
                            wtTypes = wtTypes.Except(big);
                        }
                        else if (route.AvailableRunwayLength < VortexMath.Feet6500Km)
                        {
                            WorldTrafficAircraftType[] big = { WorldTrafficAircraftType.SuperHeavy, WorldTrafficAircraftType.HeavyJet };
                            wtTypes = wtTypes.Except(big);
                        }
                        else if (route.AvailableRunwayLength > VortexMath.Feet8000Km)
                        {
                            WorldTrafficAircraftType[] small = { WorldTrafficAircraftType.LightProp, WorldTrafficAircraftType.LightJet, WorldTrafficAircraftType.MediumProp };
                            wtTypes = wtTypes.Except(small);
                        }

                        if (wtTypes.Count() == 0)
                        {
                            continue;
                        }

                        IEnumerable <SteerPoint> steerPoints = BuildSteerPoints(route, sizeRoutes.Key);
                        if (steerPoints.Count() <= Settings.MaxSteerpoints)
                        {
                            string allSizes = string.Join(" ", wtTypes.Select(w => (int)w).OrderBy(w => w));
                            string sizeName = (wtTypes.Count() == 10) ? "all" : allSizes.Replace(" ", "");
                            string fileName = Path.Combine(outputPath, $"{route.Runway.Designator}_to_{Parking.FileNameSafeName}_{route.AvailableRunwayLength * VortexMath.KmToFoot:00000}_{sizeName}");

                            using (RouteWriter sw = RouteWriter.Create(kml ? 0 : 1, fileName, allSizes, -1, -1, route.Runway.Designator, ParkingReferenceConverter.ParkingReference(Settings.ParkingReference)))
                            {
                                count++;

                                foreach (SteerPoint steerPoint in steerPoints)
                                {
                                    sw.Write(steerPoint);
                                }
                            }
                        }
                        else
                        {
                            Logger.Log($"Route from <{route.Runway.Designator}> to {Parking.FileNameSafeName} not written. Too many steerpoints ({steerPoints.Count()} vs {Settings.MaxSteerpoints})");
                        }
                    }
                }
            }
            return(count);
        }