< Summary

Class:Itinero.Routes.RouteEnumerator
Assembly:Itinero
File(s):/home/runner/work/routing2/routing2/src/Itinero/Routes/Route.cs
Covered lines:0
Uncovered lines:18
Coverable lines:18
Total lines:693
Line coverage:0% (0 of 18)
Covered branches:0
Total branches:2
Branch coverage:0% (0 of 2)
Tag:224_14471318300

Metrics

MethodBranch coverage Cyclomatic complexity Line coverage
.ctor(...)100%10%
Reset()100%10%
get_Current()100%10%
System.Collections.IEnumerator.get_Current()100%10%
MoveNext()0%20%
Dispose()100%10%

File(s)

/home/runner/work/routing2/routing2/src/Itinero/Routes/Route.cs

#LineLine coverage
 1using System;
 2using System.Collections;
 3using System.Collections.Generic;
 4using System.Linq;
 5using Itinero.Geo.Directions;
 6using Itinero.Network.Attributes;
 7
 8namespace Itinero.Routes;
 9
 10/// <summary>
 11/// A route is the result of routeplanning and represents how a vehicle might travel over the routing graph.
 12/// Apart from the coordinates, it has metadata about stops, branches and the segments
 13/// </summary>
 14public partial class Route : IEnumerable<RoutePosition>
 15{
 16    /// <summary>
 17    /// The geometry of the route, including startpoint, endpoint and (if present) intermediate waypoints
 18    /// </summary>
 19    public List<(double longitude, double latitude, float? e)> Shape { get; set; } = new();
 20
 21    /// <summary>
 22    /// Metadata about the entire route; might contain total length, total duration, the vehicle profile the route has b
 23    /// </summary>
 24    public IEnumerable<(string key, string value)> Attributes { get; set; } =
 25        ArraySegment<(string key, string value)>.Empty;
 26
 27    /// <summary>
 28    /// Metadata about the Stops, such as departure point, arrival point and intermediate destinations
 29    /// </summary>
 30    public List<Stop> Stops { get; set; } = new();
 31
 32    /// <summary>
 33    /// Metadata about individual segments.
 34    /// <remarks>
 35    /// ShapeMetaObject at `this.ShapeMeta[i]` describes the segment up to (and including) `this.Shape[this.ShapeMeta[i]
 36    /// </remarks>
 37    /// </summary>
 38    public List<Meta> ShapeMeta { get; set; } = new();
 39
 40    /// <summary>
 41    /// A stop is the coordinate where the traveller would like to start, to end or has an intermediate destination.
 42    /// <remarks>
 43    /// In general, this is the coordinate of the point not yet matched to the road network, but it has a certain distan
 44    /// </remarks>
 45    /// </summary>
 46    public class Stop
 47    {
 48        /// <summary>
 49        /// The shape index of the closes point in the route
 50        /// </summary>
 51        /// <remarks>
 52        /// This will generally be either '0' or 'route.Coordinates.Length - 1', for the start- and end-positions respec
 53        /// </remarks>
 54        public int Shape { get; set; }
 55
 56        /// <summary>
 57        /// The coordinates of the actual point
 58        /// </summary>
 59        public (double longitude, double latitude, float? e) Coordinate { get; set; }
 60
 61        /// <summary>
 62        /// Meta information about the point
 63        /// </summary>
 64        public IEnumerable<(string key, string value)> Attributes { get; set; } =
 65            ArraySegment<(string key, string value)>.Empty;
 66
 67        /// <summary>
 68        /// Creates a clone.
 69        /// </summary>
 70        /// <returns></returns>
 71        public Stop Clone()
 72        {
 73            var attributes = new List<(string key, string value)>(this.Attributes);
 74
 75            return new Stop
 76            {
 77                Attributes = attributes,
 78                Shape = this.Shape,
 79                Coordinate = this.Coordinate,
 80                Distance = this.Distance,
 81                Time = this.Time
 82            };
 83        }
 84
 85        /// <summary>
 86        /// The distance (in meter) travelled along the route when reaching this stop.
 87        /// </summary>
 88        public double Distance { get; set; }
 89
 90        /// <summary>
 91        /// The time (in seconds) travelled along the route when reaching this stop
 92        /// </summary>
 93        public double Time { get; set; }
 94
 95        /// <inheritdoc/>
 96        public override string ToString()
 97        {
 98            return $"{this.Attributes}@{this.Coordinate} ({this.Distance}m {this.Time}s)";
 99        }
 100    }
 101
 102    /// <summary>
 103    /// Represents meta-data about a part of this route.
 104    /// </summary>
 105    public class Meta
 106    {
 107        /// <summary>
 108        /// The index of the last Shape where this Meta-information is valid
 109        /// </summary>
 110        /// <remarks>
 111        /// A route has a list of coordinates named 'Shape'.
 112        /// This meta-object gives information about a part of the route,
 113        /// namely from the previous shapeMeta till 'Shape' (shape is included)
 114        /// </remarks>
 115        public int Shape { get; set; }
 116
 117        /// <summary>
 118        /// The attributes of the described segment from the underlying datasource.
 119        /// </summary>
 120        /// <remarks>
 121        /// These are thus the OpenStreetMap-tags (if the underlying data source is OSM)
 122        /// </remarks>
 123        public IEnumerable<(string key, string value)> Attributes { get; set; } = ArraySegment<(string key, string value
 124
 125        /// <summary>
 126        /// Gets or sets the relative direction flag of the attributes.
 127        /// </summary>
 128        /// <remarks>
 129        /// One can travel in two directions over an edge; forward and backward.
 130        /// The attributes however are always defined in the forward direction of the edge.
 131        /// If this flag is false, some attributes (such as oneway) might have to be interpreted differently
 132        /// </remarks>
 133        public bool AttributesAreForward { get; set; }
 134
 135        /// <summary>
 136        /// The name of the profile used for route planning this segment.
 137        /// </summary>
 138        /// <remarks>In most cases, this will always be identical for the entire route, but might be different for diffe
 139        public string Profile { get; set; } = string.Empty;
 140
 141        /// <summary>
 142        /// Creates a clone of this meta-object.
 143        /// </summary>
 144        /// <returns></returns>
 145        public Meta Clone()
 146        {
 147            var attributes = new List<(string key, string value)>(this.Attributes);
 148
 149            return new Meta
 150            {
 151                Attributes = attributes,
 152                Shape = this.Shape,
 153                Distance = this.Distance,
 154                Time = this.Time,
 155                Profile = this.Profile
 156            };
 157        }
 158
 159        /// <summary>
 160        /// The length of the described segment in meter
 161        /// </summary>
 162        public double Distance { get; set; }
 163
 164        /// <summary>
 165        /// The time needed to travel over this segment in seconds.
 166        /// The speed on this segment can be calculated by using this.Distance/this.Time
 167        /// </summary>
 168        public double Time { get; set; }
 169    }
 170
 171    /// <summary>
 172    /// The list of other edges (intersections) where the traveller passes by.
 173    /// <remarks>
 174    ///See <see cref="Branch"/> for more information
 175    /// </remarks>
 176    /// </summary>
 177    public Branch[] Branches { get; set; } = Array.Empty<Branch>();
 178
 179    /// <summary>
 180    /// A branch is an edge which the traveller passes by when following the route.
 181    /// <remarks>
 182    /// These are thus roads "branching" from the main route, e.g. in intersections, crossed streets, ...
 183    /// </remarks>
 184    /// </summary>
 185    public class Branch
 186    {
 187        /// <summary>
 188        /// The index of the coordinate where this branch branches of
 189        /// </summary>
 190        public int Shape { get; set; }
 191
 192        /// <summary>
 193        /// The end-coordinate of the branch.
 194        /// </summary>
 195        ///  /// <remarks>
 196        /// The start-coordinate of the branch can be obtained with route.Shape[this.Shape]
 197        /// </remarks>
 198        public (double longitude, double latitude, float? e) Coordinate { get; set; }
 199
 200        /// <summary>
 201        /// The attributes of the branch as fetched from the underlying data source
 202        /// </summary>
 203        public IEnumerable<(string key, string value)> Attributes { get; set; } = ArraySegment<(string key, string value
 204
 205        /// <summary>
 206        /// If walking from the route onto the branch, the attributes are interpreted in a forward manner if this flag i
 207        /// </summary>
 208        public bool AttributesAreForward { get; set; }
 209
 210        /// <summary>
 211        /// Creates a clone of this object.
 212        /// </summary>
 213        public Branch Clone()
 214        {
 215            var attributes = new List<(string key, string value)>(this.Attributes);
 216
 217            return new Branch { Attributes = attributes, Shape = this.Shape, Coordinate = this.Coordinate };
 218        }
 219    }
 220
 221    /// <summary>
 222    /// The distance in hectometer.
 223    /// </summary>
 224    public double TotalDistance { get; set; }
 225
 226    /// <summary>
 227    /// The time in hecto seconds.
 228    /// </summary>
 229    public double TotalTime { get; set; }
 230
 231    /// <summary>
 232    /// Gets or sets the profile.
 233    /// </summary>
 234    public string Profile { get; set; } = string.Empty;
 235
 236    /// <summary>
 237    /// Gets the enumerator.
 238    /// </summary>
 239    /// <returns></returns>
 240    public IEnumerator<RoutePosition> GetEnumerator()
 241    {
 242        return new RouteEnumerator(this);
 243    }
 244
 245    /// <summary>
 246    /// Gets the enumerator.
 247    /// </summary>
 248    /// <returns></returns>
 249    IEnumerator IEnumerable.GetEnumerator()
 250    {
 251        return new RouteEnumerator(this);
 252    }
 253}
 254
 255/// <summary>
 256/// Represents a route enumerator.
 257/// </summary>
 258internal class RouteEnumerator : IEnumerator<RoutePosition>
 259{
 260    private readonly Route _route;
 261
 262    /// <summary>
 263    /// Creates a new route enumerator.
 264    /// </summary>
 0265    internal RouteEnumerator(Route route)
 0266    {
 0267        _route = route;
 0268    }
 269
 270    private RoutePosition _current;
 271
 272    /// <summary>
 273    /// Resets this enumerator.
 274    /// </summary>
 275    public void Reset()
 0276    {
 0277        _current = new RoutePosition(_route,
 0278            -1, -1, -1, -1);
 0279    }
 280
 281    /// <summary>
 282    /// Returns the current object.
 283    /// </summary>
 0284    public RoutePosition Current => _current;
 285
 286    /// <summary>
 287    /// Returns the current object.
 288    /// </summary>
 0289    object IEnumerator.Current => _current;
 290
 291    /// <summary>
 292    /// Move next.
 293    /// </summary>
 294    public bool MoveNext()
 0295    {
 0296        if (_current.Route == null)
 0297        {
 0298            this.Reset();
 0299        }
 300
 0301        return _current.MoveNext();
 0302    }
 303
 304    /// <summary>
 305    /// Disposes native resources associated with this enumerator.
 306    /// </summary>
 0307    public void Dispose() { }
 308}
 309
 310/// <summary>
 311/// Abstract representation of a route position.
 312/// </summary>
 313public struct RoutePosition
 314{
 315    /// <summary>
 316    /// Creates a new route position.
 317    /// </summary>
 318    public RoutePosition(Route route, int shape, int stopIndex,
 319        int metaIndex, int branchIndex)
 320    {
 321        this.Route = route;
 322        this.Shape = shape;
 323        this.StopIndex = stopIndex;
 324        this.MetaIndex = metaIndex;
 325        this.BranchIndex = branchIndex;
 326    }
 327
 328    /// <summary>
 329    /// Gets the route.
 330    /// </summary>
 331    public Route Route { get; private set; }
 332
 333    /// <summary>
 334    /// Gets the shape index.
 335    /// </summary>
 336    public int Shape { get; private set; }
 337
 338    /// <summary>
 339    /// Gets the stop index.
 340    /// </summary>
 341    public int StopIndex { get; private set; }
 342
 343    /// <summary>
 344    /// Gets the meta index.
 345    /// </summary>
 346    public int MetaIndex { get; private set; }
 347
 348    /// <summary>
 349    /// Gets the branch index.
 350    /// </summary>
 351    public int BranchIndex { get; private set; }
 352
 353    /// <summary>
 354    /// Move to the next position.
 355    /// </summary>
 356    public bool MoveNext()
 357    {
 358        this.Shape++;
 359        if (this.Route.Shape == null ||
 360            this.Shape >= this.Route.Shape.Count)
 361        {
 362            return false;
 363        }
 364
 365        if (this.Route.Stops != null)
 366        {
 367            if (this.StopIndex == -1)
 368            {
 369                this.StopIndex = 0;
 370            }
 371            else
 372            {
 373                while (this.StopIndex < this.Route.Stops.Count &&
 374                       this.Route.Stops[this.StopIndex].Shape < this.Shape)
 375                {
 376                    this.StopIndex++;
 377                }
 378            }
 379        }
 380
 381        if (this.Route.ShapeMeta != null)
 382        {
 383            if (this.MetaIndex == -1)
 384            {
 385                this.MetaIndex = 0;
 386            }
 387            else
 388            {
 389                while (this.MetaIndex < this.Route.ShapeMeta.Count &&
 390                       this.Route.ShapeMeta[this.MetaIndex].Shape < this.Shape)
 391                {
 392                    this.MetaIndex++;
 393                }
 394            }
 395        }
 396
 397        if (this.Route.Branches != null)
 398        {
 399            if (this.BranchIndex == -1)
 400            {
 401                this.BranchIndex = 0;
 402            }
 403            else
 404            {
 405                while (this.BranchIndex < this.Route.Branches.Length &&
 406                       this.Route.Branches[this.BranchIndex].Shape < this.Shape)
 407                {
 408                    this.BranchIndex++;
 409                }
 410            }
 411        }
 412
 413        return true;
 414    }
 415
 416    /// <summary>
 417    /// Move to the next position.
 418    /// </summary>
 419    public bool MovePrevious()
 420    {
 421        this.Shape--;
 422        if (this.Route.Shape == null ||
 423            this.Shape < 0 ||
 424            this.Shape >= this.Route.Shape.Count)
 425        {
 426            return false;
 427        }
 428
 429        while (this.Route.Stops != null &&
 430               this.StopIndex > 0 &&
 431               this.StopIndex < this.Route.Stops.Count &&
 432               this.Route.Stops[this.StopIndex].Shape > this.Shape)
 433        {
 434            this.StopIndex--;
 435        }
 436
 437        while (this.Route.ShapeMeta != null &&
 438               this.MetaIndex > 0 &&
 439               this.MetaIndex < this.Route.ShapeMeta.Count &&
 440               this.Route.ShapeMeta[this.MetaIndex].Shape > this.Shape)
 441        {
 442            this.MetaIndex--;
 443        }
 444
 445        while (this.Route.Branches != null &&
 446               this.BranchIndex > 0 &&
 447               this.BranchIndex < this.Route.Branches.Length &&
 448               this.Route.Branches[this.BranchIndex].Shape > this.Shape)
 449        {
 450            this.BranchIndex--;
 451        }
 452
 453        return true;
 454    }
 455}
 456
 457/// <summary>
 458/// Extension methods for the IRoutePosition-interface.
 459/// </summary>
 460public static class IRoutePositionExtensions
 461{
 462    /// <summary>
 463    /// Returns true if this position has stops.
 464    /// </summary>
 465    public static bool HasStops(this RoutePosition position)
 466    {
 467        return position.Route.Stops != null &&
 468               position.Route.Stops.Count > position.StopIndex &&
 469               position.Route.Stops[position.StopIndex].Shape == position.Shape;
 470    }
 471
 472    /// <summary>
 473    /// Returns the stops at this position.
 474    /// </summary>
 475    public static IEnumerable<Route.Stop> Stops(this RoutePosition position)
 476    {
 477        throw new NotImplementedException();
 478    }
 479
 480    /// <summary>
 481    /// Returns true if this position has branches.
 482    /// </summary>
 483    public static bool HasBranches(this RoutePosition position)
 484    {
 485        return position.Route.Branches != null &&
 486               position.Route.Branches.Length > position.BranchIndex &&
 487               position.Route.Branches[position.BranchIndex].Shape == position.Shape;
 488    }
 489
 490    /// <summary>
 491    /// Returns the branches at this position.
 492    /// </summary>
 493    public static IEnumerable<Route.Branch> Branches(this RoutePosition position)
 494    {
 495        var branches = new List<Route.Branch>();
 496        if (position.Route.Branches != null &&
 497            position.Route.Branches.Length > position.BranchIndex &&
 498            position.Route.Branches[position.BranchIndex].Shape == position.Shape)
 499        {
 500            var branchIndex = position.BranchIndex;
 501            while (position.Route.Branches.Length > branchIndex &&
 502                   position.Route.Branches[branchIndex].Shape == position.Shape)
 503            {
 504                branches.Add(position.Route.Branches[branchIndex]);
 505                branchIndex++;
 506            }
 507        }
 508
 509        return branches;
 510    }
 511
 512    /// <summary>
 513    /// Returns true if this position has current meta.
 514    /// </summary>
 515    public static bool HasCurrentMeta(this RoutePosition position)
 516    {
 517        return position.Route.ShapeMeta != null &&
 518               position.Route.ShapeMeta.Count > position.MetaIndex &&
 519               position.Route.ShapeMeta[position.MetaIndex].Shape == position.Shape;
 520    }
 521
 522    /// <summary>
 523    /// Returns the current meta.
 524    /// </summary>
 525    public static Route.Meta? CurrentMeta(this RoutePosition position)
 526    {
 527        if (position.HasCurrentMeta())
 528        {
 529            return position.Route.ShapeMeta[position.MetaIndex];
 530        }
 531
 532        return null;
 533    }
 534
 535    /// <summary>
 536    /// Returns the meta that applies to this position.
 537    /// </summary>
 538    public static Route.Meta? Meta(this RoutePosition position)
 539    {
 540        if (position.Route.ShapeMeta != null &&
 541            position.Route.ShapeMeta.Count > position.MetaIndex)
 542        {
 543            return position.Route.ShapeMeta[position.MetaIndex];
 544        }
 545
 546        return null;
 547    }
 548
 549    /// <summary>
 550    /// Returns true if this position is the first position.
 551    /// </summary>
 552    public static bool IsFirst(this RoutePosition position)
 553    {
 554        return position.Shape == 0;
 555    }
 556
 557    /// <summary>
 558    /// Returns true if this position is the last position.
 559    /// </summary>
 560    public static bool IsLast(this RoutePosition position)
 561    {
 562        return position.Route.Shape.Count - 1 == position.Shape;
 563    }
 564
 565    /// <summary>
 566    /// Gets the previous location.
 567    /// </summary>
 568    public static (double longitude, double latitude, float? e) PreviousLocation(this RoutePosition position)
 569    {
 570        return position.Route.Shape[position.Shape - 1];
 571    }
 572
 573    /// <summary>
 574    /// Gets the next location.
 575    /// </summary>
 576    public static (double longitude, double latitude, float? e) NextLocation(this RoutePosition position)
 577    {
 578        return position.Route.Shape[position.Shape + 1];
 579    }
 580
 581    /// <summary>
 582    /// Gets the location.
 583    /// </summary>
 584    public static (double longitude, double latitude, float? e) Location(this RoutePosition position)
 585    {
 586        return position.Route.Shape[position.Shape];
 587    }
 588
 589    /// <summary>
 590    /// Gets the direction at this position.
 591    /// </summary>
 592    public static DirectionEnum Direction(this RoutePosition position)
 593    {
 594        return DirectionCalculator.Calculate(position.Location(), position.NextLocation());
 595    }
 596
 597    /// <summary>
 598    /// Gets the meta attribute for route at the current position.
 599    /// </summary>
 600    public static string GetMetaAttribute(this RoutePosition position, string key)
 601    {
 602        var meta = position.Meta();
 603        if (meta?.Attributes == null)
 604        {
 605            return string.Empty;
 606        }
 607
 608        if (!meta.Attributes.TryGetValue(key, out var value))
 609        {
 610            return string.Empty;
 611        }
 612
 613        return value;
 614    }
 615
 616    /// <summary>
 617    /// Returns true if the meta attribute for the route at the current position contains the given attribute.
 618    /// </summary>
 619    public static bool ContainsMetaAttribute(this RoutePosition position, string key, string value)
 620    {
 621        var meta = position.Meta();
 622        if (meta?.Attributes == null)
 623        {
 624            return false;
 625        }
 626
 627        return meta.Attributes.Contains((key, value));
 628    }
 629
 630    /// <summary>
 631    /// Gets the next route position.
 632    /// </summary>
 633    public static RoutePosition? Next(this RoutePosition position)
 634    {
 635        if (position.MoveNext())
 636        {
 637            return position;
 638        }
 639
 640        return null;
 641    }
 642
 643    /// <summary>
 644    /// Gets the previous route position.
 645    /// </summary>
 646    public static RoutePosition? Previous(this RoutePosition position)
 647    {
 648        if (position.MovePrevious())
 649        {
 650            return position;
 651        }
 652
 653        return null;
 654    }
 655
 656    /// <summary>
 657    /// Gets the next position until a given stop condition is met.
 658    /// </summary>
 659    public static RoutePosition? GetNextUntil(this RoutePosition position, Func<RoutePosition, bool> stopHere)
 660    {
 661        var next = position.Next();
 662        while (next != null)
 663        {
 664            if (stopHere(next.Value))
 665            {
 666                return next;
 667            }
 668
 669            next = position.Next();
 670        }
 671
 672        return null;
 673    }
 674
 675    /// <summary>
 676    /// Gets the previous position until a given stop condition is met.
 677    /// </summary>
 678    public static RoutePosition? GetPreviousUntil(this RoutePosition position, Func<RoutePosition, bool> stopHere)
 679    {
 680        var next = position.Previous();
 681        while (next != null)
 682        {
 683            if (stopHere(next.Value))
 684            {
 685                return next;
 686            }
 687
 688            next = position.Previous();
 689        }
 690
 691        return null;
 692    }
 693}