< Summary

Class:Itinero.Routes.Paths.Path
Assembly:Itinero
File(s):/home/runner/work/routing2/routing2/src/Itinero/Routes/Paths/Path.cs
Covered lines:78
Uncovered lines:80
Coverable lines:158
Total lines:306
Line coverage:49.3% (78 of 158)
Covered branches:24
Total branches:60
Branch coverage:40% (24 of 60)
Tag:232_15462506344

Metrics

MethodBranch coverage Cyclomatic complexity Line coverage
.ctor(...)100%1100%
.ctor(...)100%10%
get_RoutingNetwork()100%10%
get_Offset1()100%1100%
get_Offset2()100%1100%
get_First()100%1100%
get_Last()100%1100%
RemoveFirst()50%271.42%
RemoveLast()50%271.42%
get_Count()100%1100%
Append(...)60%1092.85%
Prepend(...)60%1092.85%
Clone()100%10%
AppendInternal(...)100%1100%
PrependInternal(...)100%1100%
GetEnumerator()100%6100%
System.Collections.IEnumerable.GetEnumerator()100%10%
ToString()0%260%
get_Item(...)100%4100%

File(s)

/home/runner/work/routing2/routing2/src/Itinero/Routes/Paths/Path.cs

#LineLine coverage
 1using System;
 2using System.Collections;
 3using System.Collections.Generic;
 4using System.Text;
 5using Itinero.Network;
 6using Itinero.Network.Enumerators.Edges;
 7
 8namespace Itinero.Routes.Paths;
 9
 10/// <summary>
 11/// Represents a path in a graph as a collection of edges.
 12/// </summary>
 13public sealed class Path : IReadOnlyList<(EdgeId edge, bool forward, ushort offset1, ushort offset2)>
 14{
 15    private readonly List<(EdgeId edge, bool forward)> _edges;
 16    private readonly RoutingNetworkEdgeEnumerator _edgeEnumerator;
 17    private readonly RoutingNetwork _network;
 18
 19    /// <summary>
 20    /// Creates a new empty path.
 21    /// </summary>
 22    /// <param name="network"></param>
 7323    public Path(RoutingNetwork network)
 7324    {
 7325        _network = network;
 7326        _edgeEnumerator = network.GetEdgeEnumerator();
 27
 7328        _edges = new List<(EdgeId edge, bool forward)>();
 7329    }
 30
 031    internal Path(Path pathToClone)
 032    {
 033        _network = pathToClone._network;
 034        _edgeEnumerator = _network.GetEdgeEnumerator();
 35
 036        _edges = new List<(EdgeId edge, bool forward)>(pathToClone._edges);
 37
 038        this.Offset1 = pathToClone.Offset1;
 039        this.Offset2 = pathToClone.Offset2;
 040    }
 41
 42    /// <summary>
 43    /// Gets the network.
 44    /// </summary>
 045    public RoutingNetwork RoutingNetwork => _network;
 46
 47    /// <summary>
 48    /// Gets the offset at the start of the path.
 49    /// </summary>
 50    /// <remarks>
 51    /// This is independent of the the direction of the first edge:
 52    /// - 0   : means the edge is fully included.
 53    /// - max : means the edge is not included.
 54    /// </remarks>
 26955    public ushort Offset1 { get; set; } = 0;
 56
 57    /// <summary>
 58    /// Gets the offset at the end of the path, relative to the direction of the edge.
 59    /// </summary>
 60    /// <remarks>
 61    /// This is independent of the the direction of the last edge:
 62    /// - 0   : means the edge is not included.
 63    /// - max : means the edge is fully included.
 64    /// </remarks>
 27265    public ushort Offset2 { get; set; } = ushort.MaxValue;
 66
 67    /// <summary>
 68    /// Gets the first edge.
 69    /// </summary>
 3970    public (EdgeId edge, bool direction) First => _edges[0];
 71
 72    /// <summary>
 73    /// Gets the last edge.
 74    /// </summary>
 3575    public (EdgeId edge, bool direction) Last => _edges[this.Count - 1];
 76
 77    /// <summary>
 78    /// Remove the first edge.
 79    /// </summary>
 80    /// <exception cref="InvalidOperationException"></exception>
 81    public void RemoveFirst()
 482    {
 483        if (_edges.Count == 0)
 084        {
 085            throw new InvalidOperationException("Cannot remove first from an already empty path.");
 86        }
 87
 488        _edges.RemoveAt(0);
 489        this.Offset1 = 0;
 490    }
 91
 92    /// <summary>
 93    /// Remove the last edge.
 94    /// </summary>
 95    /// <exception cref="InvalidOperationException"></exception>
 96    public void RemoveLast()
 497    {
 498        if (_edges.Count == 0)
 099        {
 0100            throw new InvalidOperationException("Cannot remove last from an already empty path.");
 101        }
 102
 4103        _edges.RemoveAt(_edges.Count - 1);
 4104        this.Offset2 = ushort.MaxValue;
 4105    }
 106
 107    /// <summary>
 108    /// Returns the number of edges.
 109    /// </summary>
 476110    public int Count => _edges.Count;
 111
 112    /// <summary>
 113    /// Appends the given edge and calculates the proper direction.
 114    /// </summary>
 115    /// <param name="edge">The edge.</param>
 116    /// <param name="forward">The direction of the edge.</param>
 117    public void Append(EdgeId edge, bool forward)
 53118    {
 53119        if (!_edgeEnumerator.MoveTo(edge, forward)) throw new Exception($"Edge does not exist.");
 120
 121#if DEBUG
 53122        if (_edges.Count > 0)
 18123        {
 18124            var edgeEnumerator = _network.GetEdgeEnumerator();
 18125            if (!edgeEnumerator.MoveTo(edge, forward)) throw new Exception($"Edge does not exist.");
 18126            var tail = edgeEnumerator.Tail;
 18127            var previous = _edges[^1];
 36128            if (!edgeEnumerator.MoveTo(previous.edge, previous.forward)) throw new Exception($"Edge does not exist."); ;
 18129            if (edgeEnumerator.Head != tail)
 0130                throw new Exception("Cannot append edge, previous edge head does not match tail");
 18131        }
 132#endif
 133
 53134        this.AppendInternal(edge, forward);
 53135    }
 136
 137    /// <summary>
 138    /// Prepends the given edge and calculates the proper direction.
 139    /// </summary>
 140    /// <param name="edge">The edge.</param>
 141    /// <param name="forward">The direction of the edge.</param>
 142    public void Prepend(EdgeId edge, bool forward)
 72143    {
 72144        if (!_edgeEnumerator.MoveTo(edge, forward)) throw new Exception($"Edge does not exist.");
 145
 146#if DEBUG
 72147        if (_edges.Count > 0)
 35148        {
 35149            var edgeEnumerator = _network.GetEdgeEnumerator();
 35150            if (!edgeEnumerator.MoveTo(edge, forward)) throw new Exception($"Edge does not exist.");
 35151            var head = edgeEnumerator.Head;
 35152            var next = _edges[0];
 35153            if (!edgeEnumerator.MoveTo(next.edge, next.forward)) throw new Exception($"Edge does not exist.");
 35154            if (edgeEnumerator.Tail != head)
 0155                throw new Exception("Cannot append edge, next edge tail does not match head");
 35156        }
 157#endif
 158
 72159        this.PrependInternal(edge, forward);
 72160    }
 161
 162    /// <summary>
 163    /// Returns a copy of this path.
 164    /// </summary>
 165    /// <returns>A copy.</returns>
 166    public Path Clone()
 0167    {
 0168        return new Path(this);
 0169    }
 170
 171    internal void AppendInternal(EdgeId edge, bool forward)
 53172    {
 53173        _edges.Add((edge, forward));
 53174    }
 175
 176    internal void PrependInternal(EdgeId edge, bool forward)
 72177    {
 72178        _edges.Insert(0, (edge, forward));
 72179    }
 180
 181    public IEnumerator<(EdgeId edge, bool forward, ushort offset1, ushort offset2)> GetEnumerator()
 83182    {
 422183        for (var i = 0; i < this.Count; i++)
 131184        {
 131185            var edge = _edges[i];
 131186            var offset1 = (ushort)0;
 131187            var offset2 = ushort.MaxValue;
 188
 131189            if (i == 0)
 83190            {
 83191                offset1 = this.Offset1;
 83192            }
 193
 131194            if (i == this.Count - 1)
 83195            {
 83196                offset2 = this.Offset2;
 83197            }
 198
 131199            yield return (edge.edge, edge.forward, offset1, offset2);
 128200        }
 80201    }
 202
 203    IEnumerator IEnumerable.GetEnumerator()
 0204    {
 0205        return this.GetEnumerator();
 0206    }
 207
 208    /// <summary>
 209    /// Returns a description of this path.
 210    /// </summary>
 211    public override string ToString()
 0212    {
 213        // 1 edge without offsets:         [0]->21543F->[4]
 214        // 1 edge with offsets:            [0]->10%-21543F-20%->[4]
 0215        var builder = new StringBuilder();
 216
 0217        if (_edges.Count > 0)
 0218        { // there is a first edge.
 0219            var first = _edges[0];
 0220            _edgeEnumerator.MoveTo(first.edge, first.forward);
 0221            builder.Append($"[{_edgeEnumerator.Tail}]");
 0222            builder.Append("->");
 0223            if (this.Offset1 != 0)
 0224            {
 0225                builder.Append(OffsetPer(this.Offset1));
 0226                builder.Append("-");
 0227            }
 228
 0229            builder.Append($"{first.edge}");
 0230            builder.Append(first.forward ? "F" : "B");
 231
 0232            if (_edges.Count == 1)
 0233            {
 0234                if ((first.forward && this.Offset2 != ushort.MaxValue) ||
 0235                    (!first.forward && this.Offset2 != 0))
 0236                {
 0237                    builder.Append("-");
 0238                    builder.Append(OffsetPer(this.Offset2));
 0239                }
 240
 0241                builder.Append("->");
 0242                builder.Append($"[{_edgeEnumerator.Head}]");
 0243                return builder.ToString();
 244            }
 245
 0246            builder.Append("->");
 0247            builder.Append($"[{_edgeEnumerator.Head}]");
 0248        }
 249
 0250        for (var e = 1; e < _edges.Count - 1; e++)
 0251        {
 0252            var edgeAndDirection = _edges[e];
 0253            _edgeEnumerator.MoveTo(edgeAndDirection.edge, edgeAndDirection.forward);
 0254            builder.Append("->");
 0255            builder.Append($"{edgeAndDirection.edge}");
 0256            builder.Append(edgeAndDirection.forward ? "F" : "B");
 0257            builder.Append("->");
 0258            builder.Append($"[{_edgeEnumerator.Head}]");
 0259        }
 260
 0261        if (_edges.Count > 0)
 0262        { // there is a last edge.
 0263            var last = _edges[^1];
 0264            builder.Append("->");
 0265            builder.Append($"{last.edge}");
 0266            builder.Append(last.forward ? "F" : "B");
 0267            if (this.Offset2 != ushort.MaxValue)
 0268            {
 0269                builder.Append("-");
 0270                builder.Append(OffsetPer(this.Offset2));
 0271            }
 272
 0273            _edgeEnumerator.MoveTo(last.edge, last.forward);
 0274            builder.Append("->");
 0275            builder.Append($"[{_edgeEnumerator.Head}]");
 0276            return builder.ToString();
 277        }
 278
 0279        return builder.ToString();
 280
 281        // Declare a local function.
 282        static string OffsetPer(ushort offset)
 0283        {
 0284            return $"{(double)offset / ushort.MaxValue * 100:F1}%";
 0285        }
 0286    }
 287
 288    /// <summary>
 289    /// Gets the path segment at the given index.
 290    /// </summary>
 291    /// <param name="index"></param>
 292    public (EdgeId edge, bool forward, ushort offset1, ushort offset2) this[int index]
 293    {
 294        get
 14295        {
 14296            ushort offset1 = 0;
 14297            ushort offset2 = ushort.MaxValue;
 25298            if (index == 0) offset1 = this.Offset1;
 25299            if (index == this.Count - 1) offset2 = this.Offset2;
 300
 14301            var s = _edges[index];
 302
 14303            return (s.edge, s.forward, offset1, offset2);
 14304        }
 305    }
 306}