protected override CompleteNode GetNextCompleteNode(CompleteNode currentNode, List <Node> incompleteNodes, DateTime when)
        {
            var queue      = _priorityQueues[Thread.CurrentThread];
            var candidates = new ConcurrentBag <CompleteNode>();

            Parallel.Invoke(
                () =>
            {
                GetRouteCandidates(currentNode, incompleteNodes, when, null).ForEach(c => candidates.Add(c));
            },
                () =>
            {
                GetTransferCandidates(currentNode, incompleteNodes, when).ForEach(c => candidates.Add(c));
            });

            foreach (var cnode in candidates)
            {
                var qnode = queue.GetNode(cnode.stopId);

                if ((qnode == null) || (qnode.departureTime > cnode.departureTime))
                {
                    queue.UpdateNode(cnode);
                }
            }

            return(queue.Pop());
        }
示例#2
0
        public CompleteNode(CompleteNode node)
        {
            if (node == null)
            {
                throw new ArgumentNullException();
            }

            if (node.departureTime != null)
            {
                departureTime = new TimeSpan(node.departureTime.Ticks);
            }

            if (node.viaNode != null)
            {
                viaNode = new CompleteNode(node.viaNode);
            }

            if (node.usedEdges != null)
            {
                usedEdges = new Stack<Edge>();
                node.usedEdges.Reverse().ToList().ForEach(n => usedEdges.Push(n.Clone()));
            }

            if (node.usedRouteIds != null)
            {
                usedRouteIds = new Stack<int>(node.usedRouteIds.Reverse());
            }

            stopId = node.stopId;
        }
示例#3
0
        public CompleteNode(CompleteNode node)
        {
            if (node == null)
            {
                throw new ArgumentNullException();
            }

            if (node.departureTime != null)
            {
                departureTime = new TimeSpan(node.departureTime.Ticks);
            }

            if (node.viaNode != null)
            {
                viaNode = new CompleteNode(node.viaNode);
            }

            if (node.usedEdges != null)
            {
                usedEdges = new Stack <Edge>();
                node.usedEdges.Reverse().ToList().ForEach(n => usedEdges.Push(n.Clone()));
            }

            if (node.usedRouteIds != null)
            {
                usedRouteIds = new Stack <int>(node.usedRouteIds.Reverse());
            }

            stopId = node.stopId;
        }
示例#4
0
        /// <summary>
        /// currentNode-ból gyaloglással elérhető csomópontok és azok költsége (incompleteNodes-ból válogatva)
        /// </summary>
        /// <param name="currentNode"></param>
        /// <param name="incompleteNodes"></param>
        /// <param name="when"></param>
        /// <returns></returns>
        protected List <CompleteNode> GetTransferCandidates(CompleteNode currentNode, List <Node> incompleteNodes, DateTime when)
        {
            var candidates = new List <CompleteNode>();

            if ((currentNode.usedEdges.Count > 0) && (currentNode.usedEdges.Peek() is TransferEdge))
            {
                // Ha az előző művelet átszállás volt, akkor ne szálljunk át mégegyszer
                return(candidates);
            }

            int?groupId = GetStop(currentNode.stopId).GroupId;

            if (groupId != null)
            {
                var stopsInGroup = GetStopsInGroup(groupId);
                if (stopsInGroup == null)
                {
                    // Ha árva vagyok, akkor üreset adok vissza
                    return(candidates);
                }

                // Szomszédok vizsgálata
                foreach (var stop in stopsInGroup)
                {
                    if (stop.DbId == currentNode.stopId)
                    {
                        // Saját magunkkal nem foglalkozunk
                        continue;
                    }

                    if (!incompleteNodes.Exists(ic => ic.stopId == stop.DbId))
                    {
                        // Csak akkor foglalkozzunk vele, ha még nem dolgoztuk fel
                        continue;
                    }

                    var edge     = new TransferEdge(stop.DbId, currentNode.departureTime, this.GetStop(currentNode.stopId), this.GetStop(stop.DbId));
                    var edgeCost = edge.GetCost();

                    if (edgeCost != null)
                    {
                        var usedEdges = new Stack <Edge>(currentNode.usedEdges.Reverse());
                        usedEdges.Push(edge);

                        candidates.Add(new CompleteNode
                        {
                            stopId        = stop.DbId,
                            viaNode       = currentNode,
                            departureTime = currentNode.departureTime.Add(TimeSpan.FromMinutes((int)edgeCost)),
                            usedEdges     = usedEdges,
                            usedRouteIds  = new Stack <int>(currentNode.usedRouteIds.Reverse())
                        });
                    }
                }
            }

            return(candidates);
        }
        public void Push(CompleteNode node)
        {
            if (_idMap.ContainsKey(node.stopId))
            {
                UpdateNode(node);
                return;
            }

            var insertedIndex = _storage.Count;
            _storage.Add(node);
            _idMap.Add(node.stopId, insertedIndex);
            BubbleUp(insertedIndex);
        }
        public void Push(CompleteNode node)
        {
            if (_idMap.ContainsKey(node.stopId))
            {
                UpdateNode(node);
                return;
            }

            var insertedIndex = _storage.Count;

            _storage.Add(node);
            _idMap.Add(node.stopId, insertedIndex);
            BubbleUp(insertedIndex);
        }
示例#7
0
        /// <summary>
        /// Új kifejtett csomópont választása
        /// </summary>
        /// <param name="currentNode"></param>
        /// <param name="incompleteNodes"></param>
        /// <param name="when"></param>
        /// <returns></returns>
        protected virtual CompleteNode GetNextCompleteNode(CompleteNode currentNode, List <Node> incompleteNodes, DateTime when)
        {
            try
            {
                var candidates = new List <CompleteNode>();
                candidates.AddRange(GetRouteCandidates(currentNode, incompleteNodes, when));
                candidates.AddRange(GetTransferCandidates(currentNode, incompleteNodes, when));

                #region A legolcsóbb továbblépés megkeresése
                {
                    var minimalDeparture = candidates.Min(c => c.departureTime);
                    return(candidates.First(c => c.departureTime == minimalDeparture));
                }
                #endregion
            }
            catch
            {
                return(null);
            }
        }
        public void UpdateNode(CompleteNode node)
        {
            if (!_idMap.ContainsKey(node.stopId))
            {
                Push(node);
                return;
            }

            int i = _idMap[node.stopId];
            var originalNode = _storage[i];
            _storage[i] = new CompleteNode(node);

            if (originalNode.departureTime > node.departureTime)
            {
                BubbleUp(i);
            }
            else
            {
                PushDown(i);
            }
        }
        public void UpdateNode(CompleteNode node)
        {
            if (!_idMap.ContainsKey(node.stopId))
            {
                Push(node);
                return;
            }

            int i            = _idMap[node.stopId];
            var originalNode = _storage[i];

            _storage[i] = new CompleteNode(node);

            if (originalNode.departureTime > node.departureTime)
            {
                BubbleUp(i);
            }
            else
            {
                PushDown(i);
            }
        }
示例#10
0
        protected override CompleteNode GetNextCompleteNode(CompleteNode currentNode, List<Node> incompleteNodes, DateTime when)
        {
            var queue = _priorityQueues[Thread.CurrentThread];
            var candidates = new ConcurrentBag<CompleteNode>();

            Parallel.Invoke(
            () =>
            {
                GetRouteCandidates(currentNode, incompleteNodes, when, null).ForEach(c => candidates.Add(c));
            },
            () =>
            {
                GetTransferCandidates(currentNode, incompleteNodes, when).ForEach(c => candidates.Add(c));
            });

            foreach (var cnode in candidates)
            {
                var qnode = queue.GetNode(cnode.stopId);

                if ((qnode == null) || (qnode.departureTime > cnode.departureTime))
                {
                    queue.UpdateNode(cnode);
                }
            }

            return queue.Pop();
        }
示例#11
0
        public override List <Edge> GetShortestRoute(int sourceStopId, int destinationStopId, DateTime when)
        {
            Console.WriteLine("Looking for route between '" + GetStop(sourceStopId).StopName + "' and '" + GetStop(destinationStopId).StopName + "'");

            var result           = new List <Edge>();
            var completeNodes    = new List <CompleteNode>();
            var inclompleteNodes = new List <Node>();

            bool traceComplete = false;

            // Kezdetben csak a forráspont van kész, nem vezet hozzá semmi
            completeNodes.Add(new CompleteNode
            {
                stopId        = sourceStopId,
                departureTime = when.TimeOfDay,
                viaNode       = null,
                usedEdges     = new Stack <Edge>(),
                usedRouteIds  = new Stack <int>()
            });

            // Mindenki mást pedig várakozik a kifejtésre
            foreach (var stop in AllStops.Where(s => s.DbId != sourceStopId))
            {
                inclompleteNodes.Add(new Node
                {
                    stopId = stop.DbId
                });
            }

            // Az útvonalkereső hurok
            while ((!traceComplete) && (inclompleteNodes.Count > 0))
            {
                Console.WriteLine("Iteration: " + completeNodes.Count + " cnodes, " + inclompleteNodes.Count + " icnodes");
                CompleteNode nextNode = null;

                var lck     = new Object();
                var garbage = new ConcurrentBag <CompleteNode>();

                #region Következő csomópont keresése
                {
                    completeNodes.AsParallel().ForAll(
                        currentNode =>
                    {
                        var candidate = GetNextCompleteNode(currentNode, inclompleteNodes, when);

                        if (candidate != null)
                        {
                            // van kimenő él

                            // atomi művelet!!
                            lock (lck)
                            {
                                if ((nextNode == null) || (candidate.departureTime < nextNode.departureTime))
                                {
                                    nextNode = candidate;
                                }
                            }
                        }
                        else
                        {
                            garbage.Add(currentNode);
                        }
                    });
                }
                #endregion

                if (nextNode == null)
                {
                    Console.WriteLine("No route between '" + GetStop(sourceStopId).StopName + "' and '" + GetStop(destinationStopId).StopName + "'");
                    break;
                }

                // Takarítás
                garbage.ToList().ForEach(g => completeNodes.Remove(g));
                garbage = new ConcurrentBag <CompleteNode>();

                // Áthelyezés a kész csomópontok közé
                completeNodes.Add(nextNode);
                var removedNode = inclompleteNodes.Single(n => n.stopId == nextNode.stopId);
                inclompleteNodes.Remove(removedNode);

                // Ha megtaláltuk a célt, akkor leállunk
                if (nextNode.stopId == destinationStopId)
                {
                    traceComplete = true;
                    Console.WriteLine("Trace complete between '" + GetStop(sourceStopId).StopName + "' and '" + GetStop(destinationStopId).StopName + "'");
                    Console.WriteLine("Shortest path Found. # of used routes: " + nextNode.usedRouteIds.Count + " | arrival: " + nextNode.departureTime.ToString());

                    result.AddRange(nextNode.usedEdges.Reverse());
                    result.ToList().ForEach(
                        e =>
                    {
                        Console.WriteLine(e.GetType().Name + " " + e.ToString());
                    });
                }
            }

            return(result);
        }
示例#12
0
        /// <summary>
        /// currentNode-ból járművel elérhető csomópontok és azok költsége (incompleteNodes-ból válogatva)
        /// </summary>
        /// <param name="currentNode"></param>
        /// <param name="incompleteNodes"></param>
        /// <param name="when"></param>
        /// <returns></returns>
        protected List <CompleteNode> GetRouteCandidates(CompleteNode currentNode, List <Node> incompleteNodes, DateTime when, int?limitOfRoutes = 5)
        {
            var candidates = new List <CompleteNode>();
            var routes     = GetRoutes(currentNode.stopId);

            if (routes == null)
            {
                return(candidates);
            }

            foreach (var routeId in routes)
            {
                bool isKnownRouteId = currentNode.usedRouteIds.Contains(routeId);
                if (!(isKnownRouteId && (currentNode.usedRouteIds.Peek() != routeId)))
                {
                    // A járatok nem újrafelhasználhatóak!

                    foreach (var stop in GetEndpoints(currentNode.stopId, routeId))
                    {
                        if (!incompleteNodes.Exists(ic => ic.stopId == stop.DbId))
                        {
                            // Csak akkor foglalkozzunk vele, ha még nem dolgoztuk fel
                            continue;
                        }

                        var edge     = new RouteEdge(stop.DbId, routeId, when, currentNode.departureTime);
                        var edgeCost = edge.GetCost();

                        if (edgeCost != null)
                        {
                            var usedEdges = new Stack <Edge>(currentNode.usedEdges.Reverse());

                            #region Check for hidden transfers
                            {
                                // Ha ugyanannál a megállónál száll át...
                                if ((usedEdges.Count > 0) && (usedEdges.Peek() is RouteEdge))
                                {
                                    if (((RouteEdge)usedEdges.Peek()).RouteId != edge.RouteId)
                                    {
                                        var stp = GetStop(currentNode.stopId);
                                        usedEdges.Push(new TransferEdge(stp.DbId, currentNode.departureTime, stp, stp));
                                        edgeCost += usedEdges.Peek().GetCost();
                                    }
                                }
                            }
                            #endregion

                            usedEdges.Push(edge);

                            var usedRouteIds = new Stack <int>(currentNode.usedRouteIds.Reverse());
                            if (!isKnownRouteId)
                            {
                                usedRouteIds.Push(routeId);
                            }

                            if ((limitOfRoutes == null) || (usedRouteIds.Count <= limitOfRoutes))
                            {
                                candidates.Add(new CompleteNode
                                {
                                    stopId        = stop.DbId,
                                    viaNode       = currentNode,
                                    departureTime = currentNode.departureTime.Add(TimeSpan.FromMinutes((int)edgeCost)),
                                    usedEdges     = usedEdges,
                                    usedRouteIds  = new Stack <int>(usedRouteIds.Reverse())
                                });
                            }
                        }
                    }
                }
            }

            return(candidates);
        }
示例#13
0
        public override List <Edge> GetShortestRoute(int sourceStopId, int destinationStopId, DateTime when)
        {
            if (!_priorityQueues.ContainsKey(Thread.CurrentThread))
            {
                var storage = new BinaryHeapPriorityQueue();
                _priorityQueues.Add(Thread.CurrentThread, storage);
            }

            var result = new List <Edge>();

            #region Dijkstra
            {
                int iterationCount = 0;

                Console.WriteLine("Looking for route between '" + GetStop(sourceStopId).StopName + "' and '" + GetStop(destinationStopId).StopName + "'");

                var  inclompleteNodes = new List <Node>();
                bool traceComplete    = false;

                // Kezdetben csak a forráspont van kész, nem vezet hozzá semmi
                var currentNode = new CompleteNode
                {
                    stopId        = sourceStopId,
                    departureTime = when.TimeOfDay,
                    viaNode       = null,
                    usedEdges     = new Stack <Edge>(),
                    usedRouteIds  = new Stack <int>()
                };

                // Mindenki mást pedig várakozik a kifejtésre
                foreach (var stop in AllStops.Where(s => s.DbId != sourceStopId))
                {
                    inclompleteNodes.Add(new Node
                    {
                        stopId = stop.DbId
                    });
                }

                // Az útvonalkereső hurok
                while ((!traceComplete) && (inclompleteNodes.Count > 0))
                {
                    if ((iterationCount % 25) == 0)
                    {
                        Console.WriteLine("Iteration: " + ((iterationCount / 25) + 1) + " x 25 | with " + inclompleteNodes.Count + " icnodes");
                        iterationCount++;
                    }

                    currentNode = GetNextCompleteNode(currentNode, inclompleteNodes, when);

                    if (currentNode == null)
                    {
                        Console.WriteLine("No route between '" + GetStop(sourceStopId).StopName + "' and '" + GetStop(destinationStopId).StopName + "'");
                        break;
                    }

                    // Áthelyezés a kész csomópontok közé
                    {
                        var removedNode = inclompleteNodes.Single(n => n.stopId == currentNode.stopId);
                        inclompleteNodes.Remove(removedNode);
                    }

                    // Ha megtaláltuk a célt, akkor leállunk
                    if (currentNode.stopId == destinationStopId)
                    {
                        traceComplete = true;
                        Console.WriteLine("Trace complete between '" + GetStop(sourceStopId).StopName + "' and '" + GetStop(destinationStopId).StopName + "'");
                        Console.WriteLine("Shortest path Found. # of used routes: " + currentNode.usedRouteIds.Count + " | arrival: " + currentNode.departureTime.ToString());

                        result.AddRange(currentNode.usedEdges.Reverse());
                        result.ForEach(
                            e =>
                        {
                            Console.WriteLine(e.GetType().Name + " " + e.ToString());
                        });
                    }
                }
            }
            #endregion

            if (_priorityQueues.ContainsKey(Thread.CurrentThread))
            {
                _priorityQueues.Remove(Thread.CurrentThread);
            }

            return(result);
        }
示例#14
0
        public override List<Edge> GetShortestRoute(int sourceStopId, int destinationStopId, DateTime when)
        {
            if (!_priorityQueues.ContainsKey(Thread.CurrentThread))
            {
                var storage = new BinaryHeapPriorityQueue();
                _priorityQueues.Add(Thread.CurrentThread, storage);
            }

            var result = new List<Edge>();

            #region Dijkstra
            {
                int iterationCount = 0;

                Console.WriteLine("Looking for route between '" + GetStop(sourceStopId).StopName + "' and '" + GetStop(destinationStopId).StopName + "'");

                var inclompleteNodes = new List<Node>();
                bool traceComplete = false;

                // Kezdetben csak a forráspont van kész, nem vezet hozzá semmi
                var currentNode = new CompleteNode
                {
                    stopId = sourceStopId,
                    departureTime = when.TimeOfDay,
                    viaNode = null,
                    usedEdges = new Stack<Edge>(),
                    usedRouteIds = new Stack<int>()
                };

                // Mindenki mást pedig várakozik a kifejtésre
                foreach (var stop in AllStops.Where(s => s.DbId != sourceStopId))
                {
                    inclompleteNodes.Add(new Node
                    {
                        stopId = stop.DbId
                    });
                }

                // Az útvonalkereső hurok
                while ((!traceComplete) && (inclompleteNodes.Count > 0))
                {
                    if ((iterationCount % 25) == 0)
                    {
                        Console.WriteLine("Iteration: " + ((iterationCount / 25) + 1) + " x 25 | with " + inclompleteNodes.Count + " icnodes");
                        iterationCount++;
                    }

                    currentNode = GetNextCompleteNode(currentNode, inclompleteNodes, when);

                    if (currentNode == null)
                    {
                        Console.WriteLine("No route between '" + GetStop(sourceStopId).StopName + "' and '" + GetStop(destinationStopId).StopName + "'");
                        break;
                    }

                    // Áthelyezés a kész csomópontok közé
                    {
                        var removedNode = inclompleteNodes.Single(n => n.stopId == currentNode.stopId);
                        inclompleteNodes.Remove(removedNode);
                    }

                    // Ha megtaláltuk a célt, akkor leállunk
                    if (currentNode.stopId == destinationStopId)
                    {
                        traceComplete = true;
                        Console.WriteLine("Trace complete between '" + GetStop(sourceStopId).StopName + "' and '" + GetStop(destinationStopId).StopName + "'");
                        Console.WriteLine("Shortest path Found. # of used routes: " + currentNode.usedRouteIds.Count + " | arrival: " + currentNode.departureTime.ToString());

                        result.AddRange(currentNode.usedEdges.Reverse());
                        result.ForEach(
                            e =>
                            {
                                Console.WriteLine(e.GetType().Name + " " + e.ToString());
                            });
                    }
                }
            }
            #endregion

            if (_priorityQueues.ContainsKey(Thread.CurrentThread))
            {
                _priorityQueues.Remove(Thread.CurrentThread);
            }

            return result;
        }
示例#15
0
        /// <summary>
        /// currentNode-ból gyaloglással elérhető csomópontok és azok költsége (incompleteNodes-ból válogatva)
        /// </summary>
        /// <param name="currentNode"></param>
        /// <param name="incompleteNodes"></param>
        /// <param name="when"></param>
        /// <returns></returns>
        protected List<CompleteNode> GetTransferCandidates(CompleteNode currentNode, List<Node> incompleteNodes, DateTime when)
        {
            var candidates = new List<CompleteNode>();

            if ((currentNode.usedEdges.Count > 0) && (currentNode.usedEdges.Peek() is TransferEdge))
            {
                // Ha az előző művelet átszállás volt, akkor ne szálljunk át mégegyszer
                return candidates;
            }

            int? groupId = GetStop(currentNode.stopId).GroupId;

            if (groupId != null)
            {
                var stopsInGroup = GetStopsInGroup(groupId);
                if (stopsInGroup == null)
                {
                    // Ha árva vagyok, akkor üreset adok vissza
                    return candidates;
                }

                // Szomszédok vizsgálata
                foreach (var stop in stopsInGroup)
                {
                    if (stop.DbId == currentNode.stopId)
                    {
                        // Saját magunkkal nem foglalkozunk
                        continue;
                    }

                    if (!incompleteNodes.Exists(ic => ic.stopId == stop.DbId))
                    {
                        // Csak akkor foglalkozzunk vele, ha még nem dolgoztuk fel
                        continue;
                    }

                    var edge = new TransferEdge(stop.DbId, currentNode.departureTime, this.GetStop(currentNode.stopId), this.GetStop(stop.DbId));
                    var edgeCost = edge.GetCost();

                    if (edgeCost != null)
                    {
                        var usedEdges = new Stack<Edge>(currentNode.usedEdges.Reverse());
                        usedEdges.Push(edge);

                        candidates.Add(new CompleteNode
                        {
                            stopId = stop.DbId,
                            viaNode = currentNode,
                            departureTime = currentNode.departureTime.Add(TimeSpan.FromMinutes((int)edgeCost)),
                            usedEdges = usedEdges,
                            usedRouteIds = new Stack<int>(currentNode.usedRouteIds.Reverse())
                        });
                    }
                }
            }

            return candidates;
        }
示例#16
0
        /// <summary>
        /// currentNode-ból járművel elérhető csomópontok és azok költsége (incompleteNodes-ból válogatva)
        /// </summary>
        /// <param name="currentNode"></param>
        /// <param name="incompleteNodes"></param>
        /// <param name="when"></param>
        /// <returns></returns>
        protected List<CompleteNode> GetRouteCandidates(CompleteNode currentNode, List<Node> incompleteNodes, DateTime when, int? limitOfRoutes = 5)
        {
            var candidates = new List<CompleteNode>();
            var routes = GetRoutes(currentNode.stopId);

            if (routes == null)
            {
                return candidates;
            }

            foreach (var routeId in routes)
            {
                bool isKnownRouteId = currentNode.usedRouteIds.Contains(routeId);
                if (!(isKnownRouteId && (currentNode.usedRouteIds.Peek() != routeId)))
                {
                    // A járatok nem újrafelhasználhatóak!

                    foreach (var stop in GetEndpoints(currentNode.stopId, routeId))
                    {
                        if (!incompleteNodes.Exists(ic => ic.stopId == stop.DbId))
                        {
                            // Csak akkor foglalkozzunk vele, ha még nem dolgoztuk fel
                            continue;
                        }

                        var edge = new RouteEdge(stop.DbId, routeId, when, currentNode.departureTime);
                        var edgeCost = edge.GetCost();

                        if (edgeCost != null)
                        {
                            var usedEdges = new Stack<Edge>(currentNode.usedEdges.Reverse());

                            #region Check for hidden transfers
                            {
                                // Ha ugyanannál a megállónál száll át...
                                if ((usedEdges.Count > 0) && (usedEdges.Peek() is RouteEdge))
                                {
                                    if (((RouteEdge)usedEdges.Peek()).RouteId != edge.RouteId)
                                    {
                                        var stp = GetStop(currentNode.stopId);
                                        usedEdges.Push(new TransferEdge(stp.DbId, currentNode.departureTime, stp, stp));
                                        edgeCost += usedEdges.Peek().GetCost();
                                    }
                                }
                            }
                            #endregion

                            usedEdges.Push(edge);

                            var usedRouteIds = new Stack<int>(currentNode.usedRouteIds.Reverse());
                            if (!isKnownRouteId)
                            {
                                usedRouteIds.Push(routeId);
                            }

                            if ((limitOfRoutes == null) || (usedRouteIds.Count <= limitOfRoutes))
                            {
                                candidates.Add(new CompleteNode
                                {
                                    stopId = stop.DbId,
                                    viaNode = currentNode,
                                    departureTime = currentNode.departureTime.Add(TimeSpan.FromMinutes((int)edgeCost)),
                                    usedEdges = usedEdges,
                                    usedRouteIds = new Stack<int>(usedRouteIds.Reverse())
                                });
                            }
                        }
                    }
                }
            }

            return candidates;
        }
示例#17
0
        /// <summary>
        /// Új kifejtett csomópont választása
        /// </summary>
        /// <param name="currentNode"></param>
        /// <param name="incompleteNodes"></param>
        /// <param name="when"></param>
        /// <returns></returns>
        protected virtual CompleteNode GetNextCompleteNode(CompleteNode currentNode, List<Node> incompleteNodes, DateTime when)
        {
            try
            {
                var candidates = new List<CompleteNode>();
                candidates.AddRange(GetRouteCandidates(currentNode, incompleteNodes, when));
                candidates.AddRange(GetTransferCandidates(currentNode, incompleteNodes, when));

                #region A legolcsóbb továbblépés megkeresése
                {
                    var minimalDeparture = candidates.Min(c => c.departureTime);
                    return candidates.First(c => c.departureTime == minimalDeparture);
                }
                #endregion
            }
            catch
            {
                return null;
            }
        }