< Summary

Class:Itinero.Instructions.Types.Generators.FollowBendGenerator
Assembly:Itinero.Instructions
File(s):/home/runner/work/routing2/routing2/src/Itinero.Instructions/Types/Generators/FollowBendGenerator.cs
Covered lines:52
Uncovered lines:15
Coverable lines:67
Total lines:137
Line coverage:77.6% (52 of 67)
Covered branches:19
Total branches:28
Branch coverage:67.8% (19 of 28)
Tag:224_14471318300

Metrics

MethodBranch coverage Cyclomatic complexity Line coverage
get_Name()100%1100%
DoesFollowBend(...)75%880%
Generate(...)65%2076.08%

File(s)

/home/runner/work/routing2/routing2/src/Itinero.Instructions/Types/Generators/FollowBendGenerator.cs

#LineLine coverage
 1using System;
 2using Itinero.Geo;
 3using Itinero.Network.Attributes;
 4
 5namespace Itinero.Instructions.Types.Generators;
 6
 7internal class FollowBendGenerator : IInstructionGenerator
 8{
 109    public string Name { get; } = "followbend";
 10
 11    private static bool DoesFollowBend(IndexedRoute route, int shapeI, double dAngle, int angleSign)
 2212    {
 13        // We aren't allowed to have branches on the inner side, to avoid confusing situations
 14
 2215        if (shapeI >= route.Branches.Count)
 016        {
 017            return true;
 18        }
 19
 6920        foreach (var branch in route.Branches[shapeI])
 221        {
 22            // What is the angle-difference of the branch?
 23            // This resembles the route.DirectionChangeAt-definition
 224            var selfAngle = route.ArrivingDirectionAt(shapeI);
 225            var branchAngle = route.Shape[shapeI].AngleWithMeridian(branch.Coordinate);
 226            var dBranchAngle = (selfAngle - branchAngle).NormalizeDegrees();
 27
 28            // With the angle in hand, we can ask ourselves: lies it on the inner side?
 229            if (Math.Sign(dBranchAngle) != angleSign)
 130            {
 31                // It lies on the other side; this branch doesn't pose a problem
 132                continue;
 33            }
 34
 35            // We know the signs are the same; so we pretend both are going left (aka positive)
 136            var dAngleAbs = Math.Abs(dAngle);
 137            var dBranchAbs = Math.Abs(dBranchAngle);
 38
 39            // If the turning angle of the route is bigger, then the branch lies on the outer side
 140            if (dBranchAbs < dAngleAbs)
 041            {
 042                continue;
 43            }
 44
 45            // At this point, we know the branch lies on _the inner side_
 46            // We cannot issue a simple follow bend
 147            return false;
 48        }
 49
 2150        return true;
 2251    }
 52
 53
 54    public BaseInstruction? Generate(IndexedRoute route, int offset)
 755    {
 756        if (offset == 0 || offset == route.Last)
 057        {
 58            // We never have a bend at first or as last...
 059            return null;
 60        }
 61        // Okay folks!
 62        // We will be walking forward - as long as we are turning in one direction, it is fine!
 63
 64
 765        var angleDiff = route.DirectionChangeAt(offset);
 766        var angleSign = Math.Sign(angleDiff);
 767        var usedShapes = 1;
 768        route.Meta[offset].Attributes.TryGetValue("name", out var name);
 69
 70
 771        var totalDistance = route.DistanceToNextPoint(offset);
 72        // We walk forward and detect a true gentle bend:
 2873        while (offset + usedShapes < route.Last)
 2474        {
 2475            var distance = route.DistanceToNextPoint(offset + usedShapes);
 2476            if (distance > 35)
 277            {
 78                // a gentle bend must have pieces that are not too long at a time
 279                break;
 80            }
 81
 2282            var dAngle = route.DirectionChangeAt(offset + usedShapes);
 2283            if (Math.Sign(route.DirectionChangeAt(offset + usedShapes)) != angleSign)
 084            {
 85                // The gentle bend should turn in the same direction as the first angle
 86                // Here, it doesn't have that...
 087                break;
 88            }
 89
 90
 2291            if (!DoesFollowBend(route, offset + usedShapes, dAngle, angleSign))
 192            {
 93                //
 194                break;
 95            }
 96
 2197            route.Meta[offset + usedShapes].Attributes.TryGetValue("name", out var newName);
 2198            if (name != newName)
 099            {
 100                // Different street
 0101                break;
 102            }
 103
 21104            totalDistance += distance;
 21105            angleDiff += dAngle;
 106            // We keep the total angle too; as it might turn more then 180°
 107            // We do NOT normalize the angle
 21108            usedShapes++;
 21109        }
 110
 111
 7112        if (usedShapes <= 2)
 2113        {
 114            // A 'bend' isn't a bend if there is only one point, otherwise it is a turn...
 2115            return null;
 116        }
 117
 118
 119        // A gentle bend also does turn, at least a few degrees per meter
 5120        if (Math.Abs(angleDiff) < 45)
 0121        {
 122            // There is little change - does it at least turn a bit?
 0123            if (Math.Abs(angleDiff) / totalDistance < 2.5)
 0124            {
 125                // Nope, we turn only 2.5° per meter - that isn't a lot
 0126                return null;
 127            }
 0128        }
 129
 5130        return new FollowBendInstruction(
 5131            route,
 5132            offset,
 5133            offset + usedShapes,
 5134            angleDiff
 5135        );
 7136    }
 137}