< Summary

Class:Itinero.Routes.Builders.RouteBuilder
Assembly:Itinero
File(s):/home/runner/work/routing2/routing2/src/Itinero/Routes/Builders/RouteBuilder.cs
Covered lines:117
Uncovered lines:20
Coverable lines:137
Total lines:236
Line coverage:85.4% (117 of 137)
Covered branches:31
Total branches:36
Branch coverage:86.1% (31 of 36)
Tag:224_14471318300

Metrics

MethodBranch coverage Cyclomatic complexity Line coverage
.cctor()100%1100%
get_Default()100%1100%
.ctor(...)100%1100%
Build(...)84.37%3281.65%
AddBranches(...)100%4100%

File(s)

/home/runner/work/routing2/routing2/src/Itinero/Routes/Builders/RouteBuilder.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Linq;
 4using System.Threading;
 5using Itinero.Network;
 6using Itinero.Network.Enumerators.Edges;
 7using Itinero.Profiles;
 8using Itinero.Routes.Paths;
 9
 10namespace Itinero.Routes.Builders;
 11
 12/// <summary>
 13///     Default route builder implementation.
 14/// </summary>
 15public class RouteBuilder : IRouteBuilder
 16{
 17    private readonly Func<IEnumerable<(string, string)>, bool, double, RoutingNetworkEdgeEnumerator, IEnumerable<(string
 418    private static readonly ThreadLocal<RouteBuilder> DefaultLazy = new(() => new RouteBuilder());
 19
 20    /// <summary>
 21    ///     Gets the default instance (for the local thread).
 22    /// </summary>
 2123    public static RouteBuilder Default => DefaultLazy.Value;
 24
 25    /// <summary>
 26    /// </summary>
 27    /// <param name="calculateExtraAttributes">
 28    /// If this callback is given, it will be executed on every segment to inject extra attributes into the ShapeMeta.
 29    /// The parameters are:
 30    /// - attributes:be a reference to the attributes which the edgeEnumerator gave
 31    /// - direction: if we are travelling in forward direction over the edge
 32    /// - distance: the length we are travelling on the edge
 33    /// - edgeEnumerator: the current edgeEnumerator
 34    /// </param>
 335    public RouteBuilder(Func<IEnumerable<(string, string)>, bool, double, RoutingNetworkEdgeEnumerator, IEnumerable<(str
 336    {
 337        _calculateExtraAttributes = calculateExtraAttributes;
 338    }
 39
 40    /// <inheritdoc />
 41    public Result<Route> Build(RoutingNetwork routingNetwork, Profile profile, Path path)
 2142    {
 2143        var edgeEnumerator = routingNetwork.GetEdgeEnumerator();
 2144        var profileCached = routingNetwork.GetCachedProfile(profile);
 45
 46        // SpareEnumerator is used in the branch-calculation. IT offers no guarantees about the state
 2147        var spareEnumerator = routingNetwork.GetEdgeEnumerator();
 2148        var route = new Route { Profile = profile.Name };
 2149        var branches = new List<Route.Branch>();
 2150        var seenEdges = 0;
 51
 2152        var allEdgeIds = new HashSet<EdgeId>();
 12353        foreach (var edge in path)
 3054        {
 3055            allEdgeIds.Add(edge.edge);
 3056        }
 57
 12358        foreach (var (edge, direction, offset1, offset2) in path)
 3059        {
 3060            if (route.Shape.Count == 0)
 2161            {
 62                // This is the first edge of the route - we have to check for branches at the start loction
 63                bool firstEdgeIsFullyContained;
 2164                if (direction)
 1365                {
 66                    // Forward: we look to offset1
 1367                    firstEdgeIsFullyContained = offset1 == 0;
 1368                }
 69                else
 870                {
 871                    firstEdgeIsFullyContained = offset2 == ushort.MaxValue;
 872                }
 73
 2174                if (firstEdgeIsFullyContained)
 2175                {
 76                    // We check for branches
 2177                    edgeEnumerator.MoveTo(edge, direction);
 2178                    this.AddBranches(edgeEnumerator.Tail, edgeEnumerator, spareEnumerator, 0, branches, allEdgeIds);
 2179                }
 2180            }
 81
 3082            edgeEnumerator.MoveTo(edge, direction);
 83
 3084            var attributes = edgeEnumerator.Attributes;
 3085            var factor = profileCached.Factor(edgeEnumerator);
 3086            var distance = edgeEnumerator.EdgeLength();
 3087            distance = (offset2 - offset1) / (double)ushort.MaxValue * distance;
 3088            route.TotalDistance += distance;
 89
 3090            if (_calculateExtraAttributes != null)
 091            {
 092                attributes = attributes.Concat(_calculateExtraAttributes(attributes, direction, distance, edgeEnumerator
 093            }
 3094            var speed = factor.ForwardSpeedMeterPerSecond;
 3095            if (speed <= 0)
 096            {
 097                speed = 10;
 98                // Something is wrong here
 99                // throw new ArgumentException("Speed is zero! Route is not possible with the given profile.");
 0100            }
 101
 30102            var time = distance / speed;
 30103            route.TotalTime += time;
 104
 105
 106            // add shape points to route.
 30107            using var shapeBetween = edgeEnumerator.GetShapeBetween(offset1, offset2).GetEnumerator();
 108            // skip first coordinate if there are already previous edges.
 30109            if (route.Shape.Count > 0 && offset1 == 0)
 9110            {
 9111                shapeBetween.MoveNext();
 9112            }
 113
 114
 113115            while (shapeBetween.MoveNext())
 83116            {
 83117                route.Shape.Add(shapeBetween.Current);
 83118            }
 119
 30120            if (route.Shape.Count == 0)
 0121            {
 0122                route.Shape.Add(edgeEnumerator.LocationOnEdge(offset2));
 0123            }
 124
 30125            if (offset1 != 0 ||
 30126                offset2 != ushort.MaxValue)
 0127            {
 0128                attributes = attributes.Concat(new (string key, string value)[]
 0129                {
 0130                        ("_segment_offset1",
 0131                            (offset1 / (double)ushort.MaxValue).ToString(System.Globalization.CultureInfo
 0132                                .InvariantCulture)),
 0133                        ("_segment_offset2",
 0134                            (offset2 / (double)ushort.MaxValue).ToString(System.Globalization.CultureInfo
 0135                                .InvariantCulture)),
 0136                });
 0137            }
 138
 30139            attributes = attributes.Concat(new (string key, string value)[]
 30140            {
 30141                    ("_segment_forward", direction.ToString())
 30142            });
 143
 30144            route.ShapeMeta.Add(new Route.Meta
 30145            {
 30146                Shape = route.Shape.Count - 1,
 30147                Attributes = attributes,
 30148                AttributesAreForward = direction,
 30149                Distance = distance,
 30150                Profile = profile.Name,
 30151                Time = time
 30152            });
 153
 154            // Intermediate points of an edge will never have branches
 155            // So, to calculate branches, it is enough to only look at the last point of the edge
 156            // (and the first point of the first edge - a true edge case)
 157            // (Also note that the first and last edge might not be needed entirely, so that means we possible can ignor
 158
 159            // What is the end vertex? Add its branches...
 30160            if (seenEdges + 1 == path.Count)
 21161            {
 162                // Hmm, this is the last edge
 163                // We should add the branches of it, but only if the edge is completely contained
 164                bool lastEdgeIsFullyContained;
 21165                if (!direction)
 8166                {
 167                    // Backward: we look to offset1
 8168                    lastEdgeIsFullyContained = offset1 == 0;
 8169                }
 170                else
 13171                {
 13172                    lastEdgeIsFullyContained = offset2 == ushort.MaxValue;
 13173                }
 174
 21175                if (lastEdgeIsFullyContained)
 21176                {
 21177                    this.AddBranches(edgeEnumerator.Head,
 21178                        edgeEnumerator, spareEnumerator, route.Shape.Count - 1, branches, allEdgeIds);
 21179                }
 21180            }
 181            else
 9182            {
 9183                this.AddBranches(edgeEnumerator.Head, edgeEnumerator, spareEnumerator, route.Shape.Count - 1, branches,
 9184                    allEdgeIds);
 9185            }
 186
 30187            seenEdges++;
 30188        }
 189
 21190        route.Branches = branches.ToArray();
 191
 21192        return route;
 21193    }
 194
 195    /// <summary>
 196    ///     Calculate all the branches of 'centralVertexid'
 197    /// </summary>
 198    /// <param name="edgeEnumerator">The edge enumerator, moved to the point of which the branches should be added</para
 199    /// <param name="spareEnumerator">An extra enumerator to use. Will be moved during the call</param>
 200    /// <param name="centralVertexId">The vertex id of the point under consideration</param>
 201    /// <param name="shapeIndex">The index of the shape of the current vertex</param>
 202    /// <param name="addTo">All the branches are collected into this collection</param>
 203    /// <param name="branchBlacklist">Edges in this list won't be used as branches</param>
 204    private void AddBranches(VertexId centralVertexId, RoutingNetworkEdgeEnumerator edgeEnumerator,
 205        RoutingNetworkEdgeEnumerator spareEnumerator, int shapeIndex,
 206        ICollection<Route.Branch> addTo, HashSet<EdgeId> branchBlacklist)
 51207    {
 51208        edgeEnumerator.MoveTo(centralVertexId);
 119209        while (edgeEnumerator.MoveNext())
 68210        {
 211            // Iterates over all edges of the endvertex
 212
 68213            if (branchBlacklist.Contains(edgeEnumerator.EdgeId))
 60214            {
 215                // We make sure not to pick the current nor the next edge of the path
 216                // This is simple as we have a hashset containing _all_ the edge ids ¯\_(ツ)_/¯
 60217                continue;
 218            }
 219
 220            // If the vertex of the crossroads are the same as the from, we would walk forward over the branch if we wou
 8221            var isForward = edgeEnumerator.Forward;
 8222            spareEnumerator.MoveTo(edgeEnumerator.EdgeId, isForward);
 8223            using var shapeEnum = spareEnumerator.GetShapeBetween(includeVertices: false).GetEnumerator();
 8224            shapeEnum.MoveNext(); // Init enumerator at first value
 8225            shapeEnum.MoveNext();
 8226            var branch = new Route.Branch
 8227            {
 8228                Attributes = edgeEnumerator.Attributes,
 8229                Shape = shapeIndex,
 8230                AttributesAreForward = isForward,
 8231                Coordinate = shapeEnum.Current
 8232            };
 8233            addTo.Add(branch);
 8234        }
 51235    }
 236}