< Summary

Class:Itinero.Routes.Builders.RouteBuilder
Assembly:Itinero
File(s):/home/runner/work/routing2/routing2/src/Itinero/Routes/Builders/RouteBuilder.cs
Covered lines:128
Uncovered lines:9
Coverable lines:137
Total lines:236
Line coverage:93.4% (128 of 137)
Covered branches:33
Total branches:36
Branch coverage:91.6% (33 of 36)
Tag:263_26948838820

Metrics

MethodBranch coverage Cyclomatic complexity Line coverage
.cctor()100%1100%
get_Default()100%1100%
.ctor(...)100%1100%
Build(...)90.62%3291.74%
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
 2318    private static readonly ThreadLocal<RouteBuilder> DefaultLazy = new(() => new RouteBuilder());
 19
 20    /// <summary>
 21    ///     Gets the default instance (for the local thread).
 22    /// </summary>
 1164923    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>
 2235    public RouteBuilder(Func<IEnumerable<(string, string)>, bool, double, RoutingNetworkEdgeEnumerator, IEnumerable<(str
 2236    {
 2237        _calculateExtraAttributes = calculateExtraAttributes;
 2238    }
 39
 40    /// <inheritdoc />
 41    public Result<Route> Build(RoutingNetwork routingNetwork, Profile profile, Path path)
 5442    {
 5443        var edgeEnumerator = routingNetwork.GetEdgeEnumerator();
 5444        var profileCached = routingNetwork.GetCachedProfile(profile);
 45
 46        // SpareEnumerator is used in the branch-calculation. IT offers no guarantees about the state
 5447        var spareEnumerator = routingNetwork.GetEdgeEnumerator();
 5448        var route = new Route { Profile = profile.Name };
 5449        var branches = new List<Route.Branch>();
 5450        var seenEdges = 0;
 51
 5452        var allEdgeIds = new HashSet<EdgeId>();
 140853        foreach (var edge in path)
 62354        {
 62355            allEdgeIds.Add(edge.edge);
 62356        }
 57
 140858        foreach (var (edge, direction, offset1, offset2) in path)
 62359        {
 62360            if (route.Shape.Count == 0)
 5461            {
 62                // This is the first edge of the route - we have to check for branches at the start loction
 63                bool firstEdgeIsFullyContained;
 5464                if (direction)
 3965                {
 66                    // Forward: we look to offset1
 3967                    firstEdgeIsFullyContained = offset1 == 0;
 3968                }
 69                else
 1570                {
 1571                    firstEdgeIsFullyContained = offset2 == ushort.MaxValue;
 1572                }
 73
 5474                if (firstEdgeIsFullyContained)
 4075                {
 76                    // We check for branches
 4077                    edgeEnumerator.MoveTo(edge, direction);
 4078                    this.AddBranches(edgeEnumerator.Tail, edgeEnumerator, spareEnumerator, 0, branches, allEdgeIds);
 4079                }
 5480            }
 81
 62382            edgeEnumerator.MoveTo(edge, direction);
 83
 62384            var attributes = edgeEnumerator.Attributes;
 62385            var factor = profileCached.Factor(edgeEnumerator);
 62386            var distance = edgeEnumerator.EdgeLength();
 62387            distance = (offset2 - offset1) / (double)ushort.MaxValue * distance;
 62388            route.TotalDistance += distance;
 89
 62390            if (_calculateExtraAttributes != null)
 091            {
 092                attributes = attributes.Concat(_calculateExtraAttributes(attributes, direction, distance, edgeEnumerator
 093            }
 62394            var speed = factor.ForwardSpeedMeterPerSecond;
 62395            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
 623102            var time = distance / speed;
 623103            route.TotalTime += time;
 104
 105
 106            // add shape points to route.
 623107            using var shapeBetween = edgeEnumerator.GetShapeBetween(offset1, offset2).GetEnumerator();
 108            // skip first coordinate if there are already previous edges.
 623109            if (route.Shape.Count > 0 && offset1 == 0)
 569110            {
 569111                shapeBetween.MoveNext();
 569112            }
 113
 114
 2203115            while (shapeBetween.MoveNext())
 1580116            {
 1580117                route.Shape.Add(shapeBetween.Current);
 1580118            }
 119
 623120            if (route.Shape.Count == 0)
 0121            {
 0122                route.Shape.Add(edgeEnumerator.LocationOnEdge(offset2));
 0123            }
 124
 623125            if (offset1 != 0 ||
 623126                offset2 != ushort.MaxValue)
 39127            {
 39128                attributes = attributes.Concat(new (string key, string value)[]
 39129                {
 39130                        ("_segment_offset1",
 39131                            (offset1 / (double)ushort.MaxValue).ToString(System.Globalization.CultureInfo
 39132                                .InvariantCulture)),
 39133                        ("_segment_offset2",
 39134                            (offset2 / (double)ushort.MaxValue).ToString(System.Globalization.CultureInfo
 39135                                .InvariantCulture)),
 39136                });
 39137            }
 138
 623139            attributes = attributes.Concat(new (string key, string value)[]
 623140            {
 623141                    ("_segment_forward", direction.ToString())
 623142            });
 143
 623144            route.ShapeMeta.Add(new Route.Meta
 623145            {
 623146                Shape = route.Shape.Count - 1,
 623147                Attributes = attributes,
 623148                AttributesAreForward = direction,
 623149                Distance = distance,
 623150                Profile = profile.Name,
 623151                Time = time
 623152            });
 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...
 623160            if (seenEdges + 1 == path.Count)
 54161            {
 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;
 54165                if (!direction)
 12166                {
 167                    // Backward: we look to offset1
 12168                    lastEdgeIsFullyContained = offset1 == 0;
 12169                }
 170                else
 42171                {
 42172                    lastEdgeIsFullyContained = offset2 == ushort.MaxValue;
 42173                }
 174
 54175                if (lastEdgeIsFullyContained)
 34176                {
 34177                    this.AddBranches(edgeEnumerator.Head,
 34178                        edgeEnumerator, spareEnumerator, route.Shape.Count - 1, branches, allEdgeIds);
 34179                }
 54180            }
 181            else
 569182            {
 569183                this.AddBranches(edgeEnumerator.Head, edgeEnumerator, spareEnumerator, route.Shape.Count - 1, branches,
 569184                    allEdgeIds);
 569185            }
 186
 623187            seenEdges++;
 623188        }
 189
 54190        route.Branches = branches.ToArray();
 191
 54192        return route;
 54193    }
 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)
 643207    {
 643208        edgeEnumerator.MoveTo(centralVertexId);
 2583209        while (edgeEnumerator.MoveNext())
 1940210        {
 211            // Iterates over all edges of the endvertex
 212
 1940213            if (branchBlacklist.Contains(edgeEnumerator.EdgeId))
 1229214            {
 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 ¯\_(ツ)_/¯
 1229217                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
 711221            var isForward = edgeEnumerator.Forward;
 711222            spareEnumerator.MoveTo(edgeEnumerator.EdgeId, isForward);
 711223            using var shapeEnum = spareEnumerator.GetShapeBetween(includeVertices: false).GetEnumerator();
 711224            shapeEnum.MoveNext(); // Init enumerator at first value
 711225            shapeEnum.MoveNext();
 711226            var branch = new Route.Branch
 711227            {
 711228                Attributes = edgeEnumerator.Attributes,
 711229                Shape = shapeIndex,
 711230                AttributesAreForward = isForward,
 711231                Coordinate = shapeEnum.Current
 711232            };
 711233            addTo.Add(branch);
 711234        }
 643235    }
 236}