public async Task <IActionResult> RouteRequest(string startNode, string endNode, string routeType, TimeSpan startingTime)
        {
            try
            {
                // Get all student routes.
                AllStudentRoutes = await CongestionHelper.GenerateAllStudentRoutes(DissDatabaseContext, Pathfinder, MemoryCache, AppSettings.Accommodations);

                RouteRequestResponse response = new RouteRequestResponse();

                // If start and end are the same, return empty response.
                if (startNode == endNode)
                {
                    return(Ok(response));
                }

                // Get a list of possible start and end nodes that fulfil this request.
                List <string> starts = await RoutingHelper.GetMatchingIds(DissDatabaseContext, startNode, routeType[0].ToString());

                List <string> ends = await RoutingHelper.GetMatchingIds(DissDatabaseContext, endNode, routeType.Last().ToString());

                // Check we have a start location.
                if (starts.Count == 0)
                {
                    return(BadRequest("Could not find start points."));
                }

                // Check we have an end location.
                if (ends.Count == 0)
                {
                    return(BadRequest("Could not find end points"));
                }

                // Calculate all possible routes between all starts and all ends.
                // There is more than one start or end point - probably more efficient to calculate in parallel.
                if (starts.Count > 1 || ends.Count > 1)
                {
                    ConcurrentBag <Route> possibleRoutes         = new ConcurrentBag <Route>();
                    ConcurrentBag <Route> possibleAdjustedRoutes = new ConcurrentBag <Route>();
                    Parallel.ForEach(starts, startPoint =>
                    {
                        Parallel.ForEach(ends, destination =>
                        {
                            // Calculate route and add to possible.
                            possibleRoutes.Add(Pathfinder.BuildAStar(startPoint, destination));
                            possibleAdjustedRoutes.Add(Pathfinder.BuildAStar(startPoint, destination, AllStudentRoutes, startingTime));
                        });
                    });

                    // Find the shortest route from the possible routes.
                    response.NormalRoute   = RoutingHelper.FindTheBestRouteFromPossibleRoutes(possibleRoutes.ToList());
                    response.AdjustedRoute = RoutingHelper.FindTheBestRouteFromPossibleRoutes(possibleAdjustedRoutes.ToList());
                }
                else
                {
                    // Only one start and end point - only need to do this once, so avoid parallel overheads.
                    response.NormalRoute   = Pathfinder.BuildAStar(starts[0], ends[0]);
                    response.AdjustedRoute = Pathfinder.BuildAStar(starts[0], ends[0], AllStudentRoutes, startingTime);
                }

                // Adjust walking times based on congestion values.
                response.NormalRoute.CalculateAdjustedWalkingTime(AllStudentRoutes, startingTime);
                response.AdjustedRoute.CalculateAdjustedWalkingTime(AllStudentRoutes, startingTime);

                if (response.AdjustedRoute.WalkingTimeSeconds == response.NormalRoute.WalkingTimeSeconds)
                {
                    // Both routes are the same, remove the adjusted route - there was likely no congestion.
                    response.AdjustedRoute = new Route();
                }

                return(Ok(response));
            }
            catch (Exception exception)
            {
                return(StatusCode(500, exception.Message));
            }
        }
Exemple #2
0
        /// <summary>
        /// Calculate the adjusted walking time for this route, taking congestion into account.
        /// </summary>
        /// <param name="allStudentRoutes">A collection of all student routes today.</param>
        /// <param name="startingTime">The starting time of this route.</param>
        /// <exception cref="InvalidOperationException"></exception>
        /// <exception cref="AggregateException"></exception>
        public void CalculateAdjustedWalkingTime(StudentRoute[] allStudentRoutes, TimeSpan startingTime)
        {
            double distanceSoFar            = 0;
            double totalAdjustedWalkingTime = 0;

            List <Dictionary <(string entryId, string exitId), int> > allCongestionValues = new List <Dictionary <(string entryId, string exitId), int> >(RouteNodes.Count);

            for (int i = 0; i < RouteNodes.Count - 1; i++)
            {
                // Increment the distance walked so far.
                if (RouteNodes[i].Type == NodeType.Corridor && RouteNodes[i + 1].Type == NodeType.Corridor)
                {
                    distanceSoFar += RouteNodes[i].DistanceInMetersTo(RouteNodes[i + 1]);
                }
                else
                {
                    distanceSoFar += SharedFunctions.NonCorridorDistance;
                }

                // Calculate the time spent getting to this point and get the current time of day from that.
                TimeSpan currentTime = startingTime.Add(TimeSpan.FromSeconds(SharedFunctions.CalculateWalkingTimeNoRounding(distanceSoFar)));

                // Calculate the occupancies at this point.
                Dictionary <(string entryId, string exitId), int> edgeOccupancies = CongestionHelper.CalculateEdgeOccupanciesAtTime(allStudentRoutes, currentTime);

                if (edgeOccupancies.Count > 0)
                {
                    // Add to running count for heatmap congestion calculation later.
                    allCongestionValues.Add(edgeOccupancies);
                }

                // Get the edge object.
                NodeEdge edge = RouteNodes[i].OutgoingEdges.Find(e => e.Node2.NodeId == RouteNodes[i + 1].NodeId);
                if (edge != default)
                {
                    // Increment the actual walking time.
                    totalAdjustedWalkingTime += Pathfinder.CalculateWalkingTimeWithCongestion(edge, edgeOccupancies).timeWithCongestion;
                }
            }

            // Congestion for heatmap display.
            Congestion = new Dictionary <string, float>();
            if (allCongestionValues.Count > 0)
            {
                foreach (Dictionary <(string entryId, string exitId), int> entry in allCongestionValues)
                {
                    foreach (KeyValuePair <(string entryId, string exitId), int> congestedEdge in entry)
                    {
                        string newKey = $"{congestedEdge.Key.entryId},{congestedEdge.Key.exitId}";

                        if (!Congestion.TryAdd(newKey, congestedEdge.Value))
                        {
                            // Already exists, add to it
                            Congestion[newKey] += congestedEdge.Value;
                        }
                    }
                }

                if (Congestion.Count > 0)
                {
                    MaxCongestion = Congestion.Values.Max();
                }
            }

            WalkingTimeSeconds = Convert.ToInt32(Math.Round(totalAdjustedWalkingTime));
        }