< Summary

Class:Itinero.Geo.Directions.DirectionCalculator
Assembly:Itinero
File(s):/home/runner/work/routing2/routing2/src/Itinero/Geo/Directions/DirectionCalculator.cs
Covered lines:28
Uncovered lines:107
Coverable lines:135
Total lines:207
Line coverage:20.7% (28 of 135)
Covered branches:7
Total branches:86
Branch coverage:8.1% (7 of 86)
Tag:224_14471318300

Metrics

MethodBranch coverage Cyclomatic complexity Line coverage
Angle(...)27.77%1840.81%
Calculate(...)0%320%
Calculate(...)0%320%
ToDegrees(...)100%1100%
NormalizeDegrees(...)50%438.46%

File(s)

/home/runner/work/routing2/routing2/src/Itinero/Geo/Directions/DirectionCalculator.cs

#LineLine coverage
 1using System;
 2
 3namespace Itinero.Geo.Directions;
 4
 5internal static class DirectionCalculator
 6{
 7    /// <summary>
 8    /// Calculates the angle in radians at coordinate2.
 9    /// </summary>
 10    public static double Angle((double longitude, double latitude, float? e) coordinate1,
 11        (double longitude, double latitude, float? e) coordinate2,
 12        (double longitude, double latitude, float? e) coordinate3)
 513    {
 514        var v11 = coordinate1.latitude - coordinate2.latitude;
 515        var v10 = coordinate1.longitude - coordinate2.longitude;
 16
 517        var v21 = coordinate3.latitude - coordinate2.latitude;
 518        var v20 = coordinate3.longitude - coordinate2.longitude;
 19
 520        var v1Size = Math.Sqrt((v11 * v11) + (v10 * v10));
 521        var v2Size = Math.Sqrt((v21 * v21) + (v20 * v20));
 22
 523        if (v1Size == 0 || v2Size == 0)
 024        {
 025            return double.NaN;
 26        }
 27
 528        var dot = (double)((v11 * v21) + (v10 * v20));
 529        var cross = (double)((v10 * v21) - (v11 * v20));
 30
 31        // split per quadrant.
 32        double angle;
 533        if (dot > 0)
 034        { // dot > 0
 035            if (cross > 0)
 036            { // dot > 0 and cross > 0
 37              // Quadrant 1
 038                angle = (double)Math.Asin(cross / (v1Size * v2Size));
 039                if (angle < Math.PI / 4f)
 040                { // use cosine.
 041                    angle = (double)Math.Acos(dot / (v1Size * v2Size));
 042                }
 43
 44                // angle is ok here for quadrant 1.
 045            }
 46            else
 047            { // dot > 0 and cross <= 0
 48              // Quadrant 4
 049                angle = (double)(Math.PI * 2.0f) + (double)Math.Asin(cross / (v1Size * v2Size));
 050                if (angle > (double)(Math.PI * 2.0f) - (Math.PI / 4f))
 051                { // use cosine.
 052                    angle = (double)(Math.PI * 2.0f) - (double)Math.Acos(dot / (v1Size * v2Size));
 053                }
 54
 55                // angle is ok here for quadrant 1.
 056            }
 057        }
 58        else
 559        { // dot <= 0
 560            if (cross > 0)
 561            { // dot > 0 and cross > 0
 62              // Quadrant 2
 563                angle = (double)Math.PI - (double)Math.Asin(cross / (v1Size * v2Size));
 564                if (angle > (Math.PI / 2f) + (Math.PI / 4f))
 065                { // use cosine.
 066                    angle = (double)Math.Acos(dot / (v1Size * v2Size));
 067                }
 68
 69                // angle is ok here for quadrant 2.
 570            }
 71            else
 072            { // dot > 0 and cross <= 0
 73              // Quadrant 3
 074                angle = -(-(double)Math.PI + (double)Math.Asin(cross / (v1Size * v2Size)));
 075                if (angle < Math.PI + (Math.PI / 4f))
 076                { // use cosine.
 077                    angle = (double)(Math.PI * 2.0f) - (double)Math.Acos(dot / (v1Size * v2Size));
 078                }
 79
 80                // angle is ok here for quadrant 3.
 081            }
 582        }
 83
 584        return angle;
 585    }
 86
 87    public static RelativeDirection Calculate((double longitude, double latitude, float? e) coordinate1,
 88        (double longitude, double latitude, float? e) coordinate2,
 89        (double longitude, double latitude, float? e) coordinate3)
 090    {
 091        var direction = new RelativeDirection();
 92
 93        const double margin = 65.0;
 94        const double straightOn = 10.0;
 95        const double turnBack = 5.0;
 96
 097        var angle = Angle(coordinate1, coordinate2, coordinate3).ToDegrees();
 98
 099        angle = angle.NormalizeDegrees();
 100
 0101        if (angle >= 360 - turnBack || angle < turnBack)
 0102        {
 0103            direction.Direction = RelativeDirectionEnum.TurnBack;
 0104        }
 0105        else if (angle >= turnBack && angle < 90 - margin)
 0106        {
 0107            direction.Direction = RelativeDirectionEnum.SharpRight;
 0108        }
 0109        else if (angle >= 90 - margin && angle < 90 + margin)
 0110        {
 0111            direction.Direction = RelativeDirectionEnum.Right;
 0112        }
 0113        else if (angle >= 90 + margin && angle < 180 - straightOn)
 0114        {
 0115            direction.Direction = RelativeDirectionEnum.SlightlyRight;
 0116        }
 0117        else if (angle >= 180 - straightOn && angle < 180 + straightOn)
 0118        {
 0119            direction.Direction = RelativeDirectionEnum.StraightOn;
 0120        }
 0121        else if (angle >= 180 + straightOn && angle < 270 - margin)
 0122        {
 0123            direction.Direction = RelativeDirectionEnum.SlightlyLeft;
 0124        }
 0125        else if (angle >= 270 - margin && angle < 270 + margin)
 0126        {
 0127            direction.Direction = RelativeDirectionEnum.Left;
 0128        }
 0129        else if (angle >= 270 + margin && angle < 360 - turnBack)
 0130        {
 0131            direction.Direction = RelativeDirectionEnum.SharpLeft;
 0132        }
 133
 0134        direction.Angle = angle;
 135
 0136        return direction;
 0137    }
 138
 139    /// <summary>
 140    /// Calculates the direction of a segment.
 141    /// </summary>
 142    public static DirectionEnum Calculate((double longitude, double latitude, float? e) coordinate1,
 143        (double longitude, double latitude, float? e) coordinate2)
 0144    {
 0145        var angle = (double)Angle((coordinate1.latitude + 0.01f, coordinate1.longitude, null),
 0146            coordinate1, coordinate2);
 147
 0148        angle = angle.ToDegrees();
 0149        angle = angle.NormalizeDegrees();
 150
 0151        if (angle < 22.5 || angle >= 360 - 22.5)
 0152        { // north
 0153            return DirectionEnum.North;
 154        }
 0155        else if (angle >= 22.5 && angle < 90 - 22.5)
 0156        { // north-east.
 0157            return DirectionEnum.NorthWest;
 158        }
 0159        else if (angle >= 90 - 22.5 && angle < 90 + 22.5)
 0160        { // east.
 0161            return DirectionEnum.West;
 162        }
 0163        else if (angle >= 90 + 22.5 && angle < 180 - 22.5)
 0164        { // south-east.
 0165            return DirectionEnum.SouthWest;
 166        }
 0167        else if (angle >= 180 - 22.5 && angle < 180 + 22.5)
 0168        { // south
 0169            return DirectionEnum.South;
 170        }
 0171        else if (angle >= 180 + 22.5 && angle < 270 - 22.5)
 0172        { // south-west.
 0173            return DirectionEnum.SouthEast;
 174        }
 0175        else if (angle >= 270 - 22.5 && angle < 270 + 22.5)
 0176        { // south-west.
 0177            return DirectionEnum.East;
 178        }
 0179        else if (angle >= 270 + 22.5 && angle < 360 - 22.5)
 0180        { // south-west.
 0181            return DirectionEnum.NorthEast;
 182        }
 183
 0184        return DirectionEnum.North;
 0185    }
 186
 187    internal static double ToDegrees(this double radians)
 5188    {
 5189        return radians / Math.PI * 180d;
 5190    }
 191
 192    internal static double NormalizeDegrees(this double degrees)
 5193    {
 5194        if (degrees >= 360)
 0195        {
 0196            var count = Math.Floor(degrees / 360.0);
 0197            degrees -= 360.0 * count;
 0198        }
 5199        else if (degrees < 0)
 0200        {
 0201            var count = Math.Floor(-degrees / 360.0) + 1;
 0202            degrees += 360.0 * count;
 0203        }
 204
 5205        return degrees;
 5206    }
 207}