< Summary

Class:Itinero.Network.Tiles.Standalone.Writer.RoutingNetworkWriterExtensions
Assembly:Itinero
File(s):/home/runner/work/routing2/routing2/src/Itinero/Network/Tiles/Standalone/Writer/RoutingNetworkWriterExtensions.cs
Covered lines:85
Uncovered lines:16
Coverable lines:101
Total lines:169
Line coverage:84.1% (85 of 101)
Covered branches:33
Total branches:44
Branch coverage:75% (33 of 44)
Tag:263_26948838820

Metrics

MethodBranch coverage Cyclomatic complexity Line coverage
AddStandaloneTile(...)100%24100%
TryResolveRestriction(...)31.25%1652.94%

File(s)

/home/runner/work/routing2/routing2/src/Itinero/Network/Tiles/Standalone/Writer/RoutingNetworkWriterExtensions.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Linq;
 4using Itinero.Data;
 5using Itinero.Network.Tiles.Standalone.Global;
 6using Itinero.Network.Writer;
 7
 8namespace Itinero.Network.Tiles.Standalone.Writer;
 9
 10/// <summary>
 11/// Extension methods related to writing standalone tiles to a network.
 12/// </summary>
 13public static class RoutingNetworkWriterExtensions
 14{
 15    /// <summary>
 16    /// Adds a tile in the form of a standalone tile to the network.
 17    /// </summary>
 18    /// <param name="writer">The writer to write to.</param>
 19    /// <param name="tile">The tile to add.</param>
 20    /// <param name="globalIdSet">The global id set.</param>
 21    public static void AddStandaloneTile(this RoutingNetworkWriter writer, StandaloneNetworkTile tile,
 22        GlobalNetworkManager globalIdSet)
 2323    {
 24        // add the tile without boundary crossings.
 2325        writer.AddTile(tile.NetworkTile);
 26
 27        // register all GlobalEdgeId → EdgeId mappings from the tile's internal edges.
 2328        var tileEnumerator = new NetworkTileEnumerator();
 2329        tileEnumerator.MoveTo(tile.NetworkTile);
 2330        var tileId = tile.TileId;
 17431        for (uint v = 0; v < tile.NetworkTile.VertexCount; v++)
 6432        {
 6433            var vertexId = new VertexId(tileId, v);
 6434            if (!tileEnumerator.MoveTo(vertexId)) continue;
 35
 14636            while (tileEnumerator.MoveNext())
 8237            {
 38                // only process forward edges to avoid double registration.
 12339                if (!tileEnumerator.Forward) continue;
 40
 4141                var globalEdgeId = tileEnumerator.GlobalEdgeId;
 4142                if (globalEdgeId != null)
 4143                {
 4144                    globalIdSet.EdgeIdSet.Set(globalEdgeId.Value, tileEnumerator.EdgeId);
 4145                }
 4146            }
 6447        }
 48
 49        // process boundary crossings.
 10150        foreach (var (isIncoming, globalEdgeId, vertex, attributes, edgeTypeId) in tile.GetBoundaryCrossings())
 1651        {
 1652            if (globalIdSet.PendingBoundaryCrossings.TryGetValue(globalEdgeId, out var pending))
 853            {
 54                // match found - the other tile already loaded its half, create the boundary edge.
 55                EdgeId newEdge;
 856                if (isIncoming)
 457                {
 58                    // isIncoming=true: vertex is at way tail, pending.vertex is at way head.
 459                    var length = writer.ComputeEdgeLength(vertex, pending.vertex);
 460                    newEdge = writer.AddEdge(vertex, pending.vertex, null, attributes, edgeTypeId, length, globalEdgeId)
 461                }
 62                else
 463                {
 64                    // isIncoming=false: vertex is at way head, pending.vertex is at way tail.
 465                    var length = writer.ComputeEdgeLength(pending.vertex, vertex);
 466                    newEdge = writer.AddEdge(pending.vertex, vertex, null, attributes, edgeTypeId, length, globalEdgeId)
 467                }
 68
 69                // register boundary edge's GlobalEdgeId → EdgeId mapping.
 870                globalIdSet.EdgeIdSet.Set(globalEdgeId, newEdge);
 871                globalIdSet.PendingBoundaryCrossings.Remove(globalEdgeId);
 872            }
 73            else
 874            {
 75                // no match yet - store as pending (materialize attributes).
 876                globalIdSet.PendingBoundaryCrossings[globalEdgeId] =
 877                    (vertex, attributes.ToArray(), edgeTypeId, isIncoming);
 878            }
 1679        }
 80
 81        // resolve global restrictions from this tile.
 9382        foreach (var (edges, isProhibitory, turnCostTypeId, restrictionAttributes) in tile.GetGlobalRestrictions())
 1283        {
 1284            var globalRestriction = new GlobalRestriction(
 2485                edges.Select(e => e.globalEdgeId),
 1286                isProhibitory,
 1287                restrictionAttributes.ToArray());
 88
 1289            if (!TryResolveRestriction(globalRestriction, globalIdSet, writer))
 890            {
 891                globalIdSet.PendingRestrictions.Add(globalRestriction);
 892            }
 1293        }
 94
 95        // retry pending restrictions with newly available edges.
 7896        for (var i = globalIdSet.PendingRestrictions.Count - 1; i >= 0; i--)
 1697        {
 1698            if (TryResolveRestriction(globalIdSet.PendingRestrictions[i], globalIdSet, writer))
 899            {
 8100                globalIdSet.PendingRestrictions.RemoveAt(i);
 8101            }
 16102        }
 23103    }
 104
 105    private static bool TryResolveRestriction(GlobalRestriction globalRestriction,
 106        GlobalNetworkManager globalIdSet, RoutingNetworkWriter writer)
 28107    {
 108        // try to resolve all GlobalEdgeIds to EdgeIds.
 28109        if (!globalRestriction.TryBuildNetworkRestriction(GetEdge, out var networkRestriction))
 16110            return false;
 111
 12112        if (networkRestriction!.Count < 2) return true;
 113
 114        // get last edge and determine turn cost vertex.
 12115        var last = networkRestriction[^1];
 12116        var edgeEnumerator = writer.GetEdgeEnumerator();
 12117        if (!edgeEnumerator.MoveTo(last.edge, last.forward))
 0118            return false;
 12119        var turnCostVertex = edgeEnumerator.Tail;
 120
 12121        var secondToLast = networkRestriction[^2];
 122
 12123        if (networkRestriction.IsProhibitory)
 12124        {
 125            // prohibitory: add a single cost entry forbidding this specific turn.
 12126            var costs = new uint[,] { { 0, 1 }, { 0, 0 } };
 12127            writer.AddTurnCosts(turnCostVertex, networkRestriction.Attributes,
 12128                [secondToLast.edge, last.edge], costs,
 12129                networkRestriction.Take(networkRestriction.Count - 2).Select(x => x.edge));
 130
 12131        }
 132        else
 0133        {
 134            // mandatory: add cost for every *other* edge at the vertex.
 0135            if (!edgeEnumerator.MoveTo(secondToLast.edge, secondToLast.forward))
 0136                return false;
 0137            var to = edgeEnumerator.Head;
 138
 0139            edgeEnumerator.MoveTo(to);
 0140            while (edgeEnumerator.MoveNext())
 0141            {
 0142                if (edgeEnumerator.EdgeId == secondToLast.edge ||
 0143                    edgeEnumerator.EdgeId == last.edge) continue;
 144
 0145                var costs = new uint[,] { { 0, 1 }, { 0, 0 } };
 0146                writer.AddTurnCosts(turnCostVertex, networkRestriction.Attributes,
 0147                    [secondToLast.edge, edgeEnumerator.EdgeId], costs,
 0148                    networkRestriction.Take(networkRestriction.Count - 2).Select(x => x.edge));
 0149            }
 0150        }
 151
 12152        return true;
 153
 154        (EdgeId edge, bool forward)? GetEdge(GlobalEdgeId geid, bool isFirst)
 48155        {
 156            // exact match short-circuit (cheap check before walking).
 48157            if (globalIdSet.EdgeIdSet.TryGet(geid, out var edgeId))
 11158                return (edgeId, true);
 37159            if (globalIdSet.EdgeIdSet.TryGet(geid.GetInverted(), out edgeId))
 13160                return (edgeId, false);
 161
 162            // walk from the chain-anchor end; first edge anchors at Head, every
 163            // subsequent edge anchors at Tail (chain invariant: previous.Head ==
 164            // current.Tail).
 24165            return GlobalRestrictionExtensions.WalkFromAnchor(geid, isFirst,
 24166                globalIdSet.EdgeIdSet.TryGet);
 48167        }
 28168    }
 169}