< Summary

Class:Itinero.Network.Search.Islands.RoutingNetworkIslandManager
Assembly:Itinero
File(s):/home/runner/work/routing2/routing2/src/Itinero/Network/Search/Islands/RoutingNetworkIslandManager.cs
Covered lines:120
Uncovered lines:14
Coverable lines:134
Total lines:257
Line coverage:89.5% (120 of 134)
Covered branches:24
Total branches:28
Branch coverage:85.7% (24 of 28)
Tag:263_26948838820

Metrics

MethodBranch coverage Cyclomatic complexity Line coverage
.ctor(...)100%1100%
.ctor(...)100%1100%
IsEdgeOnIsland(...)100%1100%
IsEdgeOnIsland(...)62.5%881.25%
GetOrCreateDirectedGraph(...)100%2100%
get_MaxIslandSize()100%1100%
TryGetIslandsFor(...)100%10%
GetIslandsFor(...)100%2100%
IsMainN(...)100%10100%
BuildForTileAsync()100%4100%
Clone()50%276.92%

File(s)

/home/runner/work/routing2/routing2/src/Itinero/Network/Search/Islands/RoutingNetworkIslandManager.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Diagnostics.CodeAnalysis;
 4using System.Threading;
 5using System.Threading.Tasks;
 6using Itinero.Profiles;
 7
 8namespace Itinero.Network.Search.Islands;
 9
 10internal class RoutingNetworkIslandManager
 11{
 59212    private readonly Dictionary<(string profile, uint tile), Task> _tilesInProgress = new();
 59213    private readonly ReaderWriterLockSlim _tilesInProgressLock = new();
 14    private readonly Dictionary<string, Islands> _islands;
 59215    private readonly Dictionary<(string profile, IslandKind kind), IslandDirectedGraph> _directedGraphs = new();
 59216    private readonly ReaderWriterLockSlim _islandsLock = new();
 17
 34318    internal RoutingNetworkIslandManager(int maxIslandSize)
 34319    {
 34320        this.MaxIslandSize = maxIslandSize;
 34321        _islands = new();
 34322    }
 23
 24924    private RoutingNetworkIslandManager(int maxIslandSize, Dictionary<string, Islands> islands)
 24925    {
 24926        this.MaxIslandSize = maxIslandSize;
 24927        _islands = islands;
 24928    }
 29
 30    /// <summary>
 31    /// Checks if an edge is on an island using the directed graph.
 32    /// Returns true if island, false if not island, null if inconclusive.
 33    /// </summary>
 34    internal bool? IsEdgeOnIsland(Profile profile, EdgeId edgeId) =>
 735        this.IsEdgeOnIsland(profile.Name, edgeId);
 36
 37    internal bool? IsEdgeOnIsland(string profileName, EdgeId edgeId)
 738    {
 39        try
 740        {
 741            _islandsLock.EnterReadLock();
 42
 43            // Snapping is a Full-classification concern, so the existing
 44            // single-DG semantics route through the Full DG.
 745            if (!_directedGraphs.TryGetValue((profileName, IslandKind.Full), out var dg))
 546                return null;
 47
 248            if (!_islands.TryGetValue(profileName, out var profileIslands))
 049                return null;
 250            if (profileIslands.IsEdgeOnIsland(edgeId))
 051                return true;
 52
 253            if (dg.IsNotIsland(edgeId))
 054                return false;
 55
 256            return null;
 57        }
 58        finally
 759        {
 760            _islandsLock.ExitReadLock();
 761        }
 762    }
 63
 64    internal IslandDirectedGraph GetOrCreateDirectedGraph(Profile profile, IslandKind kind = IslandKind.Full)
 323865    {
 323866        var key = (profile.Name, kind);
 67        try
 323868        {
 323869            _islandsLock.EnterUpgradeableReadLock();
 70
 639971            if (_directedGraphs.TryGetValue(key, out var dg)) return dg;
 72
 73            try
 7774            {
 7775                _islandsLock.EnterWriteLock();
 76
 7777                dg = new IslandDirectedGraph();
 7778                _directedGraphs[key] = dg;
 7779                return dg;
 80            }
 81            finally
 7782            {
 7783                _islandsLock.ExitWriteLock();
 7784            }
 85        }
 86        finally
 323887        {
 323888            _islandsLock.ExitUpgradeableReadLock();
 323889        }
 323890    }
 91
 256692    internal int MaxIslandSize { get; }
 93
 94    internal bool TryGetIslandsFor(string profileName, out Islands islands)
 095    {
 96        try
 097        {
 098            _islandsLock.EnterReadLock();
 99
 0100            return _islands.TryGetValue(profileName, out islands);
 101        }
 102        finally
 0103        {
 0104            _islandsLock.ExitReadLock();
 0105        }
 0106    }
 107
 108    internal Islands GetIslandsFor(Profile profile)
 2176109    {
 110        try
 2176111        {
 2176112            _islandsLock.EnterUpgradeableReadLock();
 113
 4309114            if (_islands.TryGetValue(profile.Name, out var islands)) return islands;
 115
 116            try
 43117            {
 43118                _islandsLock.EnterWriteLock();
 119
 43120                islands = new Islands();
 43121                _islands[profile.Name] = islands;
 43122                return islands;
 123            }
 124            finally
 43125            {
 43126                _islandsLock.ExitWriteLock();
 43127            }
 128        }
 129        finally
 2176130        {
 2176131            _islandsLock.ExitUpgradeableReadLock();
 2176132        }
 2176133    }
 134
 135    /// <summary>
 136    /// Returns whether the edge is in the profile's main-N component — the
 137    /// dominant SCC of the N-only subgraph, i.e. the "mainland" without
 138    /// L-edges.
 139    ///
 140    /// <list type="bullet">
 141    /// <item><c>true</c>: edge is in main-N. Default for any edge in a done tile
 142    /// that is neither L-tagged, on an island, nor a non-main-N pocket member.</item>
 143    /// <item><c>false</c>: edge is not in main-N. Either L-tagged (passed in via
 144    /// <paramref name="isLocalAccess"/>), on an island (unreachable in Full), or
 145    /// recorded as a local edge (non-main-N pocket).</item>
 146    /// <item><c>null</c>: classification has not yet produced a verdict for this
 147    /// tile.</item>
 148    /// </list>
 149    ///
 150    /// The L-tag check is tag-driven and resolved by the caller (typically via
 151    /// the cost function's <c>localAccess</c> field on the result of <c>Get</c>),
 152    /// then passed in. The manager itself does not consult any tag storage.
 153    /// </summary>
 154    internal bool? IsMainN(Profile profile, EdgeId edgeId, bool isLocalAccess)
 788377155    {
 156        // L-tagged edge — never main-N, no storage lookup needed.
 788379157        if (isLocalAccess) return false;
 158
 159        try
 788375160        {
 788375161            _islandsLock.EnterReadLock();
 162
 1568830163            if (!_islands.TryGetValue(profile.Name, out var islands)) return null;
 164
 165            // Unreachable in the Full classification → not in main-N.
 7921166            if (islands.IsEdgeOnIsland(edgeId)) return false;
 167
 168            // Non-main-N pocket → not in main-N.
 7920169            if (islands.IsEdgeLocal(edgeId)) return false;
 170
 171            // Tile finished classifying and the edge is in neither set → main-N.
 172            // Otherwise we don't yet know.
 7918173            return islands.GetTileDone(edgeId.TileId) ? true : null;
 174        }
 175        finally
 788375176        {
 788375177            _islandsLock.ExitReadLock();
 788375178        }
 788377179    }
 180
 181    internal async Task BuildForTileAsync(RoutingNetwork network, Profile profile, uint tileId,
 182        CancellationToken cancellationToken)
 7183    {
 184        // queue task, if not done yet.
 185        Task task;
 186        try
 7187        {
 7188            _tilesInProgressLock.EnterUpgradeableReadLock();
 189
 7190            if (!_tilesInProgress.TryGetValue((profile.Name, tileId), out task))
 7191            {
 192                try
 7193                {
 7194                    _tilesInProgressLock.EnterWriteLock();
 195
 7196                    task = IslandClassifier.BuildForTileAsync(network, profile, tileId, cancellationToken);
 7197                    _tilesInProgress[(profile.Name, tileId)] = task;
 7198                }
 199                finally
 7200                {
 7201                    _tilesInProgressLock.ExitWriteLock();
 7202                }
 7203            }
 7204        }
 205        finally
 7206        {
 7207            _tilesInProgressLock.ExitUpgradeableReadLock();
 7208        }
 209
 210        // await the task.
 7211        await task;
 212
 213        // remove from the queue.
 214        try
 7215        {
 7216            _tilesInProgressLock.EnterUpgradeableReadLock();
 217
 7218            if (_tilesInProgress.ContainsKey((profile.Name, tileId)))
 7219            {
 220                try
 7221                {
 7222                    _tilesInProgressLock.EnterWriteLock();
 223
 7224                    _tilesInProgress.Remove((profile.Name, tileId));
 7225                }
 226                finally
 7227                {
 7228                    _tilesInProgressLock.ExitWriteLock();
 7229                }
 7230            }
 7231        }
 232        finally
 7233        {
 7234            _tilesInProgressLock.ExitUpgradeableReadLock();
 7235        }
 7236    }
 237
 238    internal RoutingNetworkIslandManager Clone()
 249239    {
 240        try
 249241        {
 249242            _islandsLock.EnterReadLock();
 243
 249244            var islands = new Dictionary<string, Islands>();
 747245            foreach (var (profileName, profileIslands) in _islands)
 0246            {
 0247                islands[profileName] = profileIslands.Clone();
 0248            }
 249
 249250            return new RoutingNetworkIslandManager(this.MaxIslandSize, islands);
 251        }
 252        finally
 249253        {
 249254            _islandsLock.ExitReadLock();
 249255        }
 249256    }
 257}