< Summary

Class:Itinero.Snapping.SnapPointExtensions
Assembly:Itinero
File(s):/home/runner/work/routing2/routing2/src/Itinero/Snapping/SnapPointExtensions.cs
Covered lines:47
Uncovered lines:38
Coverable lines:85
Total lines:206
Line coverage:55.2% (47 of 85)
Covered branches:21
Total branches:44
Branch coverage:47.7% (21 of 44)
Tag:224_14471318300

Metrics

MethodBranch coverage Cyclomatic complexity Line coverage
LocationOnNetwork(...)100%1100%
Direction(...)100%1100%
Direction(...)100%10%
DirectionFromAngle(...)62.5%2464%
Angle(...)42.85%1458.97%
GetVertex(...)0%60%

File(s)

/home/runner/work/routing2/routing2/src/Itinero/Snapping/SnapPointExtensions.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using Itinero.Geo;
 4using Itinero.Geo.Directions;
 5using Itinero.Network;
 6using Itinero.Network.Enumerators.Edges;
 7using Itinero.Network.Search;
 8using Itinero.Profiles;
 9
 10namespace Itinero.Snapping;
 11
 12/// <summary>
 13/// Extension methods related to snapping and snap points.
 14/// </summary>
 15public static class SnapPointExtensions
 16{
 17    /// <summary>
 18    /// Returns the location on the given edge using the given offset.
 19    /// </summary>
 20    /// <param name="routerDb">The router db.</param>
 21    /// <param name="snapPoint">The snap point.</param>
 22    /// <returns>The location on the network.</returns>
 23    public static (double longitude, double latitude, float? e) LocationOnNetwork(this SnapPoint snapPoint,
 24        RoutingNetwork routerDb)
 925    {
 926        var enumerator = routerDb.GetEdgeEnumerator();
 927        enumerator.MoveTo(snapPoint.EdgeId);
 28
 929        return enumerator.LocationOnEdge(snapPoint.Offset);
 930    }
 31
 32    /// <summary>
 33    /// Returns the edge direction that best aligns with the direction based on the degrees with the meridian clockwise.
 34    /// </summary>
 35    /// <param name="snapPoint">The snap point.</param>
 36    /// <param name="routerDb">The router db.</param>
 37    /// <param name="direction">The direction.</param>
 38    /// <param name="distance">The distance to average the edge angle over in the range of ]0,∞[.</param>
 39    /// <param name="tolerance">The tolerance in range of ]0,90], default 90, determining what is considered forward or 
 40    /// If the difference in angle is too big null is returned.</param>
 41    /// <returns>The direction on the edge at the location of the snap point that best matches the given direction.
 42    /// Returns null if the difference is too big relative to the tolerance or the edge is too small to properly calcula
 43    public static bool? Direction(this SnapPoint snapPoint, RoutingNetwork routerDb, DirectionEnum direction,
 44        double distance = 10, double tolerance = 90)
 545    {
 546        return snapPoint.DirectionFromAngle(routerDb, (double)direction, out _, distance, tolerance);
 547    }
 48
 49    /// <summary>
 50    /// Returns the edge direction that best aligns with the direction based on the degrees with the meridian clockwise.
 51    /// </summary>
 52    /// <param name="snapPoint">The snap point.</param>
 53    /// <param name="routerDb">The router db.</param>
 54    /// <param name="direction">The direction.</param>
 55    /// <param name="difference">The difference in degrees in the range of ]-180,180].</param>
 56    /// <param name="distance">The distance to average the edge angle over in the range of ]0,∞[.</param>
 57    /// <param name="tolerance">The tolerance in range of ]0,90], default 90, determining what is considered forward or 
 58    /// If the difference in angle is too big null is returned.</param>
 59    /// <returns>The direction on the edge at the location of the snap point that best matches the given direction.
 60    /// Returns null if the difference is too big relative to the tolerance or the edge is too small to properly calcula
 61    public static bool? Direction(this SnapPoint snapPoint, RoutingNetwork routerDb, DirectionEnum direction,
 62        out double difference, double distance = 10, double tolerance = 90)
 063    {
 064        return snapPoint.DirectionFromAngle(routerDb, (double)direction, out difference, distance, tolerance);
 065    }
 66
 67    /// <summary>
 68    /// Returns the edge direction that best aligns with the direction based on the degrees with the meridian clockwise.
 69    /// </summary>
 70    /// <param name="snapPoint">The snap point.</param>
 71    /// <param name="routerDb">The router db.</param>
 72    /// <param name="degreesMeridian">The angle in degrees with the meridian clockwise.</param>
 73    /// <param name="difference">The difference in degrees in the range of ]-180,180].</param>
 74    /// <param name="distance">The distance to average the edge angle over in the range of ]0,∞[.</param>
 75    /// <param name="tolerance">The tolerance in range of ]0,90], default 90, determining what is considered forward or 
 76    /// If the difference in angle is too big null is returned.</param>
 77    /// <returns>The direction on the edge at the location of the snap point that best matches the given direction.
 78    /// Returns null if the difference is too big relative to the tolerance or the edge is too small to properly calcula
 79    public static bool? DirectionFromAngle(this SnapPoint snapPoint, RoutingNetwork routerDb,
 80        double degreesMeridian, out double difference, double distance = 10,
 81        double tolerance = 90)
 582    {
 583        if (tolerance <= 0 || tolerance > 90)
 084        {
 085            throw new ArgumentOutOfRangeException(nameof(tolerance), "The tolerance has to be in range of ]0,90]");
 86        }
 87
 588        var angle = snapPoint.Angle(routerDb, distance);
 589        if (!angle.HasValue)
 090        {
 091            difference = 0;
 092            return null;
 93        }
 94
 595        difference = degreesMeridian - angle.Value;
 596        if (difference > 180)
 097        {
 098            difference -= 360;
 099        }
 100
 5101        if ((difference >= 0 && difference <= tolerance) ||
 5102            (difference < 0 && difference >= -tolerance))
 4103        {
 104            // forward, according to the tolerance.
 4105            return true;
 106        }
 107
 1108        var reverseTolerance = 180 - tolerance;
 1109        if ((difference >= 0 && difference >= reverseTolerance) ||
 1110            (difference < 0 && difference <= reverseTolerance))
 1111        {
 112            // backward, according to the tolerance.
 1113            return false;
 114        }
 115
 0116        return null;
 5117    }
 118
 119    /// <summary>
 120    /// Returns the angle in degrees at the given snap point over a given distance.
 121    /// </summary>
 122    /// <param name="snapPoint">The snap point.</param>
 123    /// <param name="routerDb">The router db.</param>
 124    /// <param name="distance">The distance to average the edge angle over in the range of ]0,∞[.</param>
 125    /// <returns>The angle in degrees with the meridian clockwise.</returns>
 126    public static double? Angle(this SnapPoint snapPoint, RoutingNetwork routerDb, double distance = 10)
 5127    {
 5128        if (distance <= 0)
 0129        {
 0130            throw new ArgumentOutOfRangeException(nameof(distance), "The distance has to be in the range ]0,∞[");
 131        }
 132
 5133        var edgeEnumerator = routerDb.GetEdgeEnumerator();
 5134        if (!edgeEnumerator.MoveTo(snapPoint.EdgeId, true))
 0135        {
 0136            throw new ArgumentException($"Cannot find edge in {nameof(SnapPoint)}: {snapPoint}");
 137        }
 138
 139        // determine the first and last point on the edge
 140        // to calculate the angle for.
 5141        var edgeLength = edgeEnumerator.EdgeLength();
 5142        var distanceOffset = distance / edgeLength * ushort.MaxValue;
 5143        var offset1 = (ushort)0;
 5144        var offset2 = ushort.MaxValue;
 5145        if (distanceOffset <= ushort.MaxValue)
 5146        {
 147            // not the entire edge.
 148            // round offsets to beginning/end of edge.
 5149            offset1 = (ushort)Math.Max(0,
 5150                snapPoint.Offset - distanceOffset);
 5151            offset2 = (ushort)Math.Min(ushort.MaxValue,
 5152                snapPoint.Offset + distanceOffset);
 153
 154            // if both are at the same location make sure to at least
 155            // convert the smallest possible section of the edge.
 5156            if (offset2 - offset1 == 0)
 0157            {
 0158                if (offset1 > 0)
 0159                {
 0160                    offset1--;
 0161                }
 162
 0163                if (offset2 < ushort.MaxValue)
 0164                {
 0165                    offset2++;
 0166                }
 0167            }
 5168        }
 169
 170        // calculate the locations.
 5171        var location1 = edgeEnumerator.LocationOnEdge(offset1);
 5172        var location2 = edgeEnumerator.LocationOnEdge(offset2);
 173
 5174        if (location1.DistanceEstimateInMeter(location2) < .1)
 0175        { // distance too small, edge to short.
 0176            return null;
 177        }
 178
 179        // calculate and return angle.
 5180        var toNorth = (location1.longitude, location1.latitude + 0.001f, (float?)null);
 5181        var angleRadians = DirectionCalculator.Angle(location2, location1, toNorth);
 5182        return angleRadians.ToDegrees().NormalizeDegrees();
 5183    }
 184
 185    /// <summary>
 186    /// Gets the vertex for the snap point, but only if it represents an exact vertex.
 187    /// </summary>
 188    /// <param name="snapPoint">The snap point, representing a vertex.</param>
 189    /// <param name="routingNetwork">The routing network.</param>
 190    /// <returns>The vertex.</returns>
 191    public static VertexId GetVertex(this SnapPoint snapPoint, RoutingNetwork routingNetwork)
 0192    {
 0193        if (!snapPoint.IsVertex) throw new ArgumentOutOfRangeException(nameof(snapPoint), "Snap point is not a vertex");
 194
 0195        var enumerator = routingNetwork.GetEdgeEnumerator();
 0196        if (!enumerator.MoveTo(snapPoint.EdgeId))
 0197            throw new Exception("Edge not found, has snap point been created on the given network?");
 198
 0199        if (snapPoint.Offset == 0)
 0200        {
 0201            return enumerator.Tail;
 202        }
 203
 0204        return enumerator.Head;
 0205    }
 206}