< 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:0
Uncovered lines:121
Coverable lines:121
Total lines:193
Line coverage:0% (0 of 121)
Covered branches:0
Total branches:58
Branch coverage:0% (0 of 58)
Tag:251_23667616543

Metrics

MethodBranch coverage Cyclomatic complexity Line coverage
AddStandaloneTile(...)0%240%
TryResolveRestriction(...)0%160%

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)
 023    {
 24        // add the tile without boundary crossings.
 025        writer.AddTile(tile.NetworkTile);
 26
 27        // register all GlobalEdgeId → EdgeId mappings from the tile's internal edges.
 028        var tileEnumerator = new NetworkTileEnumerator();
 029        tileEnumerator.MoveTo(tile.NetworkTile);
 030        var tileId = tile.TileId;
 031        for (uint v = 0; v < tile.NetworkTile.VertexCount; v++)
 032        {
 033            var vertexId = new VertexId(tileId, v);
 034            if (!tileEnumerator.MoveTo(vertexId)) continue;
 35
 036            while (tileEnumerator.MoveNext())
 037            {
 38                // only process forward edges to avoid double registration.
 039                if (!tileEnumerator.Forward) continue;
 40
 041                var globalEdgeId = tileEnumerator.GlobalEdgeId;
 042                if (globalEdgeId != null)
 043                {
 044                    globalIdSet.EdgeIdSet.Set(globalEdgeId.Value, tileEnumerator.EdgeId);
 045                }
 046            }
 047        }
 48
 49        // process boundary crossings.
 050        foreach (var (isIncoming, globalEdgeId, vertex, attributes, edgeTypeId) in tile.GetBoundaryCrossings())
 051        {
 052            if (globalIdSet.PendingBoundaryCrossings.TryGetValue(globalEdgeId, out var pending))
 053            {
 54                // match found - the other tile already loaded its half, create the boundary edge.
 55                EdgeId newEdge;
 056                if (isIncoming)
 057                {
 58                    // isIncoming=true: vertex is at way tail, pending.vertex is at way head.
 059                    var length = writer.ComputeEdgeLength(vertex, pending.vertex);
 060                    newEdge = writer.AddEdge(vertex, pending.vertex, null, attributes, edgeTypeId, length, globalEdgeId)
 061                }
 62                else
 063                {
 64                    // isIncoming=false: vertex is at way head, pending.vertex is at way tail.
 065                    var length = writer.ComputeEdgeLength(pending.vertex, vertex);
 066                    newEdge = writer.AddEdge(pending.vertex, vertex, null, attributes, edgeTypeId, length, globalEdgeId)
 067                }
 68
 69                // register boundary edge's GlobalEdgeId → EdgeId mapping.
 070                globalIdSet.EdgeIdSet.Set(globalEdgeId, newEdge);
 071                globalIdSet.PendingBoundaryCrossings.Remove(globalEdgeId);
 072            }
 73            else
 074            {
 75                // no match yet - store as pending (materialize attributes).
 076                globalIdSet.PendingBoundaryCrossings[globalEdgeId] =
 077                    (vertex, attributes.ToArray(), edgeTypeId, isIncoming);
 078            }
 079        }
 80
 81        // resolve global restrictions from this tile.
 082        foreach (var (edges, isProhibitory, turnCostTypeId, restrictionAttributes) in tile.GetGlobalRestrictions())
 083        {
 084            var globalRestriction = new GlobalRestriction(
 085                edges.Select(e => e.globalEdgeId),
 086                isProhibitory,
 087                restrictionAttributes.ToArray());
 88
 089            if (!TryResolveRestriction(globalRestriction, globalIdSet, writer))
 090            {
 091                globalIdSet.PendingRestrictions.Add(globalRestriction);
 092            }
 093        }
 94
 95        // retry pending restrictions with newly available edges.
 096        for (var i = globalIdSet.PendingRestrictions.Count - 1; i >= 0; i--)
 097        {
 098            if (TryResolveRestriction(globalIdSet.PendingRestrictions[i], globalIdSet, writer))
 099            {
 0100                globalIdSet.PendingRestrictions.RemoveAt(i);
 0101            }
 0102        }
 0103    }
 104
 105    private static bool TryResolveRestriction(GlobalRestriction globalRestriction,
 106        GlobalNetworkManager globalIdSet, RoutingNetworkWriter writer)
 0107    {
 108        // try to resolve all GlobalEdgeIds to EdgeIds.
 0109        if (!globalRestriction.TryBuildNetworkRestriction(GetEdge, out var networkRestriction))
 0110            return false;
 111
 0112        if (networkRestriction!.Count < 2) return true;
 113
 114        // get last edge and determine turn cost vertex.
 0115        var last = networkRestriction[^1];
 0116        var edgeEnumerator = writer.GetEdgeEnumerator();
 0117        if (!edgeEnumerator.MoveTo(last.edge, last.forward))
 0118            return false;
 0119        var turnCostVertex = edgeEnumerator.Tail;
 120
 0121        var secondToLast = networkRestriction[^2];
 122
 0123        if (networkRestriction.IsProhibitory)
 0124        {
 125            // prohibitory: add a single cost entry forbidding this specific turn.
 0126            var costs = new uint[,] { { 0, 1 }, { 0, 0 } };
 0127            writer.AddTurnCosts(turnCostVertex, networkRestriction.Attributes,
 0128                [secondToLast.edge, last.edge], costs,
 0129                networkRestriction.Take(networkRestriction.Count - 2).Select(x => x.edge));
 130
 0131        }
 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
 0152        return true;
 153
 154        (EdgeId edge, bool forward)? GetEdge(GlobalEdgeId geid)
 0155        {
 0156            if (globalIdSet.EdgeIdSet.TryGet(geid, out var edgeId))
 0157                return (edgeId, true);
 0158            if (globalIdSet.EdgeIdSet.TryGet(geid.GetInverted(), out edgeId))
 0159                return (edgeId, false);
 160
 161            // exact match not found — search for a subsection sharing the
 162            // endpoint closest to the restricted vertex.
 163            // for tail->head: keep head stable, search tail from head-1 toward 0
 164            // for inverted head->tail: keep tail stable, search head from tail+1 toward max
 0165            if (geid.Tail < geid.Head)
 0166            {
 167                // forward direction: head is the restricted vertex, search toward it
 0168                for (var t = geid.Head - 1; t > geid.Tail; t--)
 0169                {
 0170                    var sub = GlobalEdgeId.Create(geid.EdgeId, t, geid.Head);
 0171                    if (globalIdSet.EdgeIdSet.TryGet(sub, out edgeId))
 0172                        return (edgeId, true);
 0173                    if (globalIdSet.EdgeIdSet.TryGet(sub.GetInverted(), out edgeId))
 0174                        return (edgeId, false);
 0175                }
 0176            }
 177            else
 0178            {
 179                // reversed direction: tail is the restricted vertex, search away from it
 0180                for (var h = geid.Tail - 1; h > geid.Head; h--)
 0181                {
 0182                    var sub = GlobalEdgeId.Create(geid.EdgeId, geid.Tail, h);
 0183                    if (globalIdSet.EdgeIdSet.TryGet(sub, out edgeId))
 0184                        return (edgeId, true);
 0185                    if (globalIdSet.EdgeIdSet.TryGet(sub.GetInverted(), out edgeId))
 0186                        return (edgeId, false);
 0187                }
 0188            }
 189
 0190            return null;
 0191        }
 0192    }
 193}