< Summary

Class:Itinero.Network.Enumerators.Edges.IEdgeEnumeratorExtensions
Assembly:Itinero
File(s):/home/runner/work/routing2/routing2/src/Itinero/Network/Enumerators/Edges/IEdgeEnumeratorExtensions.cs
Covered lines:90
Uncovered lines:8
Coverable lines:98
Total lines:185
Line coverage:91.8% (90 of 98)
Covered branches:37
Total branches:42
Branch coverage:88% (37 of 42)
Tag:267_28791001112

Metrics

MethodBranch coverage Cyclomatic complexity Line coverage
GetCompleteShape()100%2100%
EdgeLength(...)50%275%
GetShapeBetween()93.33%3093.44%
LocationOnEdge(...)75%887.5%

File(s)

/home/runner/work/routing2/routing2/src/Itinero/Network/Enumerators/Edges/IEdgeEnumeratorExtensions.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Linq;
 4using Itinero.Geo;
 5
 6namespace Itinero.Network.Enumerators.Edges;
 7
 8/// <summary>
 9/// Contains edge enumerator extensions.
 10/// </summary>
 11public static class IEdgeEnumeratorExtensions
 12{
 13    /// <summary>
 14    /// Gets the complete shape, including start end end vertices.
 15    /// </summary>
 16    /// <param name="enumerator">The enumerator.</param>
 17    /// <returns>The complete shape.</returns>
 18    public static IEnumerable<(double longitude, double latitude, float? e)> GetCompleteShape(
 19        this IEdgeEnumerator enumerator)
 10515720    {
 10515721        yield return enumerator.TailLocation;
 22
 8243923        var shape = enumerator.Shape;
 57720024        foreach (var s in shape)
 16577625        {
 16577626            yield return s;
 16410727        }
 28
 8077029        yield return enumerator.HeadLocation;
 7851730    }
 31
 32    /// <summary>
 33    /// Gets the length of an edge in centimeters.
 34    /// </summary>
 35    /// <param name="enumerator">The enumerator.</param>
 36    /// <returns>The length in meters.</returns>
 37    public static double EdgeLength(this IEdgeEnumerator enumerator)
 75040738    {
 150081439        if (enumerator.Length != null) return enumerator.Length.Value / 100.0;
 40
 041        return enumerator.GetCompleteShape().DistanceEstimateInMeter();
 75040742    }
 43
 44    /// <summary>
 45    /// Returns the part of the edge between the two offsets not including the vertices at the start or the end.
 46    /// </summary>
 47    /// <param name="enumerator">The enumerator.</param>
 48    /// <param name="offset1">The start offset.</param>
 49    /// <param name="offset2">The end offset.</param>
 50    /// <param name="includeVertices">Include vertices in case the range start at min offset or ends at max.</param>
 51    /// <returns>The shape points between the given offsets. Includes the vertices by default when offsets at min/max.</
 52    public static IEnumerable<(double longitude, double latitude, float? e)> GetShapeBetween(
 53        this IEdgeEnumerator enumerator,
 54        ushort offset1 = 0, ushort offset2 = ushort.MaxValue, bool includeVertices = true)
 4453455    {
 4453456        if (offset1 > offset2)
 057        {
 058            throw new ArgumentException($"{nameof(offset1)} has to smaller than or equal to {nameof(offset2)}");
 59        }
 60
 61        // return the entire edge if requested.
 4453462        if (offset1 == 0 && offset2 == ushort.MaxValue)
 4448963        {
 48979864            foreach (var s in enumerator.GetCompleteShape())
 17852165            {
 17852166                yield return s;
 17781067            }
 68
 4377869            yield break;
 70        }
 71
 72        // get edge and shape details.
 4573        var shape = enumerator.Shape.ToList();
 74
 75        // calculate offsets in meters.
 4576        var edgeLength = enumerator.EdgeLength();
 4577        var offset1Length = offset1 / (double)ushort.MaxValue * edgeLength;
 4578        var offset2Length = offset2 / (double)ushort.MaxValue * edgeLength;
 79
 80        // TODO: can we make this easier with the complete shape enumeration?
 81        // calculate coordinate shape.
 4582        var before = offset1 > 0; // when there is a start offset.
 4583        var length = 0.0;
 4584        var previous = enumerator.TailLocation;
 4585        if (offset1 == 0 && includeVertices)
 2286        {
 2287            yield return previous;
 2288        }
 89
 29090        for (var i = 0; i < shape.Count + 1; i++)
 12791        {
 92            (double longitude, double latitude, float? e) next;
 12793            if (i < shape.Count)
 9594            {
 95                // the
 9596                next = shape[i];
 9597            }
 98            else
 3299            {
 100                // the last location.
 32101                next = enumerator.HeadLocation;
 32102            }
 103
 127104            var segmentLength = previous.DistanceEstimateInMeter(next);
 127105            if (before)
 41106            {
 107                // check if offset1 length has exceeded.
 41108                if (segmentLength + length >= offset1Length &&
 41109                    offset1 > 0)
 23110                {
 111                    // we are before, but now we have moved to after.
 23112                    var segmentOffset = offset1Length - length;
 23113                    var location = (previous, next).PositionAlongLine(segmentOffset / segmentLength);
 114                    // *don't* advance `previous` here, offset2 could also fall inside the same (previous → next) segmen
 23115                    before = false;
 23116                    yield return location;
 23117                }
 41118            }
 119
 127120            if (!before)
 109121            {
 122                // check if offset2 length has exceeded.
 109123                if (segmentLength + length > offset2Length &&
 109124                    offset2 < ushort.MaxValue)
 27125                {
 126                    // we are after but now we are after.
 27127                    var segmentOffset = offset2Length - length;
 27128                    var location = (previous, next).PositionAlongLine(segmentOffset / segmentLength);
 27129                    yield return location;
 27130                    yield break;
 131                }
 132
 133                // the case where include vertices is false.
 82134                if (i == shape.Count && !includeVertices)
 0135                {
 0136                    yield break;
 137                }
 138
 82139                yield return next;
 82140            }
 141
 142            // move to the next segment.
 100143            previous = next;
 100144            length += segmentLength;
 100145        }
 18146    }
 147
 148    /// <summary>
 149    /// Returns the location on the given edge using the given offset.
 150    /// </summary>
 151    /// <param name="enumerator">The enumerator.</param>
 152    /// <param name="offset">The offset.</param>
 153    /// <returns>The location on the network.</returns>
 154    internal static (double longitude, double latitude, float? e) LocationOnEdge(this IEdgeEnumerator enumerator,
 155        in ushort offset)
 43193156    {
 157        // TODO: this can be optimized, build a performance test.
 43193158        var shape = enumerator.GetShapeBetween().ToList();
 43193159        var length = enumerator.EdgeLength();
 43193160        var currentLength = 0.0;
 43193161        var targetLength = length * (offset / (double)ushort.MaxValue);
 176058162        for (var i = 1; i < shape.Count; i++)
 85155163        {
 85155164            var segmentLength = shape[i - 1].DistanceEstimateInMeter(shape[i]);
 85155165            if (segmentLength + currentLength > targetLength)
 40319166            {
 40319167                var segmentOffsetLength = segmentLength + currentLength - targetLength;
 40319168                var segmentOffset = 1 - (segmentOffsetLength / segmentLength);
 40319169                float? e = null;
 40319170                if (shape[i - 1].e.HasValue &&
 40319171                    shape[i].e.HasValue)
 0172                {
 0173                    e = (float)(shape[i - 1].e.Value + (segmentOffset * (shape[i].e.Value - shape[i - 1].e.Value)));
 0174                }
 175
 40319176                return (shape[i - 1].longitude + (segmentOffset * (shape[i].longitude - shape[i - 1].longitude)),
 40319177                    shape[i - 1].latitude + (segmentOffset * (shape[i].latitude - shape[i - 1].latitude)), e);
 178            }
 179
 44836180            currentLength += segmentLength;
 44836181        }
 182
 2874183        return shape[^1];
 43193184    }
 185}