| | 1 | | using System; |
| | 2 | | using System.Collections.Generic; |
| | 3 | | using System.Linq; |
| | 4 | | using System.Net.Http.Headers; |
| | 5 | | using System.Threading; |
| | 6 | | using System.Threading.Tasks; |
| | 7 | | using Itinero.Geo; |
| | 8 | | using Itinero.Network; |
| | 9 | | using Itinero.Routes; |
| | 10 | | using Itinero.Routes.Paths; |
| | 11 | | using Itinero.Routing.Costs; |
| | 12 | | using Itinero.Routing.Flavours.Dijkstra; |
| | 13 | |
|
| | 14 | | namespace Itinero.Routing.Alternatives; |
| | 15 | |
|
| | 16 | | public static class IRouterOneToOneWithAlternativesExtensions |
| | 17 | | { |
| | 18 | | internal static async Task<Result<IReadOnlyList<Path>>> CalculatePathsAsync( |
| | 19 | | this IRouterOneToOneWithAlternatives alternativeRouter, CancellationToken cancellationToken) |
| 0 | 20 | | { |
| 0 | 21 | | var settings = alternativeRouter.Settings; |
| 0 | 22 | | var altSettings = alternativeRouter.AlternativeRouteSettings; |
| 0 | 23 | | var routingNetwork = alternativeRouter.Network; |
| | 24 | |
|
| 0 | 25 | | if (routingNetwork == null) |
| 0 | 26 | | { |
| 0 | 27 | | throw new NullReferenceException( |
| 0 | 28 | | "RoutingNetwork is null, cannot do route planning without a routing network"); |
| | 29 | | } |
| | 30 | |
|
| 0 | 31 | | var profile = settings.Profile; |
| 0 | 32 | | var costFunction = routingNetwork.GetCostFunctionFor(profile); |
| | 33 | |
|
| | 34 | |
|
| 0 | 35 | | var maxBox = settings.MaxBoxFor(routingNetwork, alternativeRouter.Source.sp); |
| | 36 | |
|
| | 37 | | bool CheckMaxDistance(VertexId v) |
| 0 | 38 | | { |
| 0 | 39 | | if (maxBox == null) |
| 0 | 40 | | { |
| 0 | 41 | | return false; |
| | 42 | | } |
| | 43 | |
|
| 0 | 44 | | if (routingNetwork == null) |
| 0 | 45 | | { |
| 0 | 46 | | throw new Exception("Router cannot be null here."); |
| | 47 | | } |
| | 48 | |
|
| 0 | 49 | | var vertex = routingNetwork.GetVertex(v); |
| 0 | 50 | | if (!maxBox.Value.Overlaps(vertex)) |
| 0 | 51 | | { |
| 0 | 52 | | return true; |
| | 53 | | } |
| | 54 | |
|
| 0 | 55 | | return false; |
| 0 | 56 | | } |
| | 57 | |
|
| | 58 | | async Task<(Path? path, double cost)> RunDijkstraAsync(ICostFunction costFunction, CancellationToken cancellatio |
| 0 | 59 | | { |
| 0 | 60 | | var source = alternativeRouter.Source; |
| 0 | 61 | | var target = alternativeRouter.Target; |
| | 62 | |
|
| 0 | 63 | | if (source.direction == null && target.direction == null) |
| 0 | 64 | | { |
| | 65 | | // Run the undirected dijkstra |
| 0 | 66 | | return await Dijkstra.Default.RunAsync(routingNetwork, source.sp, target.sp, |
| 0 | 67 | | costFunction.GetDijkstraWeightFunc(), |
| 0 | 68 | | async v => |
| 0 | 69 | | { |
| 0 | 70 | | await routingNetwork.UsageNotifier.NotifyVertex(routingNetwork, v, cancellationToken); |
| 0 | 71 | | if (cancellationToken.IsCancellationRequested) return false; |
| 0 | 72 | | return CheckMaxDistance(v); |
| 0 | 73 | | }); |
| | 74 | | } |
| | 75 | |
|
| | 76 | | // Run directed dijkstra |
| 0 | 77 | | return await Flavours.Dijkstra.EdgeBased.Dijkstra.Default.RunAsync(routingNetwork, source, target, |
| 0 | 78 | | costFunction.GetDijkstraWeightFunc(), |
| 0 | 79 | | async v => |
| 0 | 80 | | { |
| 0 | 81 | | await routingNetwork.UsageNotifier.NotifyVertex(routingNetwork, v.vertexId, cancellationToken); |
| 0 | 82 | | if (cancellationToken.IsCancellationRequested) return false; |
| 0 | 83 | | return CheckMaxDistance(v.vertexId); |
| 0 | 84 | | }); |
| 0 | 85 | | } |
| | 86 | |
|
| 0 | 87 | | var (initialPath, initialCost) = await RunDijkstraAsync(costFunction, cancellationToken); |
| 0 | 88 | | if (initialPath == null) |
| 0 | 89 | | { |
| 0 | 90 | | return new Result<IReadOnlyList<Path>>("Not a single path found!"); |
| | 91 | | } |
| | 92 | |
|
| 0 | 93 | | var results = new List<Path>(altSettings.MaxNumberOfAlternativeRoutes) { |
| 0 | 94 | | initialPath |
| 0 | 95 | | }; |
| | 96 | |
|
| 0 | 97 | | if (altSettings.MaxNumberOfAlternativeRoutes == 1) return results; |
| | 98 | |
|
| 0 | 99 | | var costThreshold = initialCost * altSettings.MaxWeightIncreasePercentage; |
| | 100 | |
|
| 0 | 101 | | var seenEdges = new Dictionary<EdgeId, int>(); |
| 0 | 102 | | foreach (var (edge, _, _, _) in initialPath) |
| 0 | 103 | | { |
| 0 | 104 | | var count = seenEdges.GetValueOrDefault(edge, 0); |
| 0 | 105 | | seenEdges[edge] = count + 1; |
| 0 | 106 | | } |
| | 107 | |
|
| 0 | 108 | | var maxTries = altSettings.MaxNumberOfAlternativeRoutes * 5; |
| 0 | 109 | | while (results.Count < altSettings.MaxNumberOfAlternativeRoutes && maxTries > 0) |
| 0 | 110 | | { |
| 0 | 111 | | if (cancellationToken.IsCancellationRequested) break; |
| | 112 | |
|
| 0 | 113 | | maxTries--; |
| 0 | 114 | | var altCostFunction = new AlternativeRouteCostFunction(costFunction, seenEdges, |
| 0 | 115 | | altSettings.PenaltyFactor); |
| 0 | 116 | | var (altPath, altCost) = await RunDijkstraAsync(altCostFunction, cancellationToken); |
| | 117 | |
|
| 0 | 118 | | if (altCost > costThreshold) |
| 0 | 119 | | { |
| | 120 | | // No more alternative routes can be found |
| 0 | 121 | | break; |
| | 122 | | } |
| | 123 | |
|
| 0 | 124 | | if (altPath == null) |
| 0 | 125 | | { |
| | 126 | | // No more alternative routes can be found |
| 0 | 127 | | break; |
| | 128 | | } |
| | 129 | |
|
| 0 | 130 | | var totalEdges = 0; |
| 0 | 131 | | var alreadyKnownEdges = 0; |
| | 132 | |
|
| 0 | 133 | | foreach (var (edge, _, _, _) in altPath) |
| 0 | 134 | | { |
| 0 | 135 | | totalEdges++; |
| 0 | 136 | | if (seenEdges.TryGetValue(edge, out var count)) |
| 0 | 137 | | { |
| 0 | 138 | | seenEdges[edge] = count + 1; |
| | 139 | |
|
| | 140 | | // Already seen! |
| 0 | 141 | | alreadyKnownEdges++; |
| 0 | 142 | | } |
| | 143 | | else |
| 0 | 144 | | { |
| 0 | 145 | | seenEdges[edge] = 1; |
| 0 | 146 | | } |
| 0 | 147 | | } |
| | 148 | |
|
| 0 | 149 | | var overlapPercentage = (double)alreadyKnownEdges / totalEdges; |
| | 150 | |
|
| 0 | 151 | | if (overlapPercentage > altSettings.MaxPercentageOfEqualEdges) |
| 0 | 152 | | { |
| 0 | 153 | | continue; |
| | 154 | | } |
| | 155 | |
|
| 0 | 156 | | results.Add(altPath); |
| 0 | 157 | | } |
| | 158 | |
|
| 0 | 159 | | return results; |
| 0 | 160 | | } |
| | 161 | |
|
| | 162 | | public static async Task<Result<IReadOnlyList<Path>>> PathsAsync( |
| | 163 | | this IRouterOneToOneWithAlternatives withAlternatives, CancellationToken cancellationToken = default) |
| 0 | 164 | | { |
| 0 | 165 | | return await withAlternatives.CalculatePathsAsync(cancellationToken); |
| 0 | 166 | | } |
| | 167 | |
|
| | 168 | | public static async Task<Result<IReadOnlyList<Route>>> CalculateAsync(this IRouterOneToOneWithAlternatives withAlter |
| 0 | 169 | | { |
| 0 | 170 | | var paths = await withAlternatives.CalculatePathsAsync(cancellationToken); |
| | 171 | |
|
| 0 | 172 | | if (paths.IsError) |
| 0 | 173 | | { |
| 0 | 174 | | return new Result<IReadOnlyList<Route>>(paths.ErrorMessage); |
| | 175 | | } |
| | 176 | |
|
| | 177 | |
|
| 0 | 178 | | var routes = paths.Value.Select(path => |
| 0 | 179 | | withAlternatives.Settings.RouteBuilder.Build(withAlternatives.Network, |
| 0 | 180 | | withAlternatives.Settings.Profile, path).Value |
| 0 | 181 | | ).ToList(); |
| | 182 | |
|
| 0 | 183 | | return new Result<IReadOnlyList<Route>>(routes); |
| 0 | 184 | | } |
| | 185 | | } |