< Summary

Class:Itinero.Network.Mutation.RoutingNetworkMutator
Assembly:Itinero
File(s):/home/runner/work/routing2/routing2/src/Itinero/Network/Mutation/RoutingNetworkMutator.cs
Covered lines:107
Uncovered lines:49
Coverable lines:156
Total lines:349
Line coverage:68.5% (107 of 156)
Covered branches:32
Total branches:48
Branch coverage:66.6% (32 of 48)
Tag:224_14471318300

Metrics

MethodBranch coverage Cyclomatic complexity Line coverage
.ctor(...)100%1100%
Itinero.Network.Enumerators.Edges.IEdgeEnumerable.GetTileForRead(...)100%1100%
HasTile(...)0%20%
SetTile(...)100%10%
GetTileForRead(...)50%670%
GetEdge(...)100%10%
GetTileForWrite(...)83.33%679.16%
GetEdgeEnumerator()100%1100%
GetTiles()100%4100%
GetTile(...)100%1100%
get_Zoom()100%1100%
get_IslandManager()100%1100%
AddVertex(...)100%1100%
TryGetVertex(...)0%40%
AddEdge(...)83.33%6100%
DeleteEdge(...)66.66%6100%
AddTurnCosts(...)75%480%
Clear()100%10%
ToRoutingNetwork()80%1070.58%
Dispose()100%1100%

File(s)

/home/runner/work/routing2/routing2/src/Itinero/Network/Mutation/RoutingNetworkMutator.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using Itinero.Network.DataStructures;
 4using Itinero.Network.Enumerators.Edges;
 5using Itinero.Network.Search.Islands;
 6using Itinero.Network.Tiles;
 7// ReSharper disable PossibleMultipleEnumeration
 8
 9namespace Itinero.Network.Mutation;
 10
 11/// <summary>
 12/// A routing network mutator. This mutator can be used to change anything except deleting vertices.
 13///
 14/// This mutator can be used to:
 15/// - add new vertices
 16/// - add new edges.
 17/// - delete edges.
 18///
 19/// </summary>
 20public class RoutingNetworkMutator : IDisposable, IEdgeEnumerable
 21{
 22    private readonly SparseArray<bool> _modified;
 23    private readonly SparseArray<NetworkTile?> _tiles;
 24    private readonly IRoutingNetworkMutable _network;
 25    private readonly RoutingNetworkIslandManager _islandManager;
 26
 12227    internal RoutingNetworkMutator(IRoutingNetworkMutable network)
 12228    {
 12229        _network = network;
 30
 12231        _tiles = _network.Tiles.Clone();
 12232        _modified = new SparseArray<bool>(_tiles.Length);
 33
 12234        _islandManager = network.IslandManager.Clone();
 12235    }
 36
 37    NetworkTile? IEdgeEnumerable.GetTileForRead(uint localTileId)
 1738    {
 1739        return this.GetTileForRead(localTileId);
 1740    }
 41
 42    internal bool HasTile(uint localTileId)
 043    {
 044        return _tiles.Length > localTileId &&
 045               _modified[localTileId] == true;
 046    }
 47
 48    internal void SetTile(NetworkTile tile)
 049    {
 050        _tiles[tile.TileId] = tile;
 051        _modified[tile.TileId] = true;
 052    }
 53
 54    private NetworkTile? GetTileForRead(uint localTileId)
 1955    {
 1956        if (_tiles.Length <= localTileId) return null;
 57
 58
 59        // get tile, if any.
 1960        var tile = _tiles[localTileId];
 1961        if (tile == null) return null;
 62
 63        // check edge type map.
 1964        var edgeTypeMap = _network.RouterDb.GetEdgeTypeMap();
 3865        if (tile.EdgeTypeMapId == edgeTypeMap.id) return tile;
 66
 67        // tile.EdgeTypeMapId indicates the version of the used edgeTypeMap
 68        // If the id is different, the loaded tile needs updating; e.g. because a cost function has been changed
 069        tile = tile.CloneForEdgeTypeMap(edgeTypeMap);
 070        _tiles[localTileId] = tile;
 071        return tile;
 1972    }
 73
 74    /// <summary>
 75    /// Gets the edge associated with the given id.
 76    /// </summary>
 77    /// <param name="edgeId">The edge id.</param>
 78    /// <param name="forward">The forward flag.</param>
 79    /// <returns>The edge details.</returns>
 80    public INetworkTileEdge GetEdge(EdgeId edgeId, bool forward)
 081    {
 082        var (tile, _) = this.GetTileForWrite(edgeId.TileId);
 83
 084        var edge = new NetworkTileEnumerator();
 085        edge.MoveTo(tile);
 086        edge.MoveTo(edgeId, forward);
 087        return edge;
 088    }
 89
 90    /// <summary>
 91    /// Gets a tile for writing.
 92    /// </summary>
 93    /// <param name="localTileId">The local tile id.</param>
 94    /// <returns></returns>
 95    /// <remarks>
 96    /// This makes sure tiles are first cloned before being written to.
 97    /// </remarks>
 98    private (NetworkTile tile, Func<IEnumerable<(string key, string value)>, uint> func) GetTileForWrite(
 99        uint localTileId)
 522100    {
 522101        var edgeTypeMap = _network.RouterDb.GetEdgeTypeMap();
 102
 103        // ensure minimum size.
 522104        _tiles.EnsureMinimumSize(localTileId);
 522105        _modified.EnsureMinimumSize(localTileId);
 106
 107        // check if there is already a modified version.
 522108        var tile = _tiles[localTileId];
 522109        if (tile != null)
 409110        {
 111            // if edge types map doesn't match, clone and mark as modified.
 409112            if (tile.EdgeTypeMapId != edgeTypeMap.id)
 0113            {
 0114                tile = tile.CloneForEdgeTypeMap(edgeTypeMap);
 0115                _modified[localTileId] = true;
 0116                _tiles[localTileId] = tile;
 0117                return (tile, edgeTypeMap.func);
 118            }
 119
 120            // if already modified, just return.
 409121            var modified = _modified[localTileId];
 808122            if (modified) return (tile, edgeTypeMap.func);
 123
 124            // make sure all tiles being written to are cloned.
 10125            tile = tile.Clone();
 10126            _modified[localTileId] = true;
 10127            _tiles[localTileId] = tile;
 10128            return (tile, edgeTypeMap.func);
 129        }
 130
 131        // there is no tile, create a new one.
 113132        tile = new NetworkTile(_network.Zoom, localTileId, edgeTypeMap.id);
 133
 134        // store in the local tiles.
 113135        _tiles[localTileId] = tile;
 113136        _modified[localTileId] = true;
 113137        return (tile, edgeTypeMap.func);
 522138    }
 139
 140    /// <summary>
 141    /// Gets the edge enumerator.
 142    /// </summary>
 143    /// <returns></returns>
 144    public RoutingNetworkMutatorEdgeEnumerator GetEdgeEnumerator()
 23145    {
 23146        return new(this);
 23147    }
 148
 149    internal IEnumerable<uint> GetTiles()
 8150    {
 524312151        foreach (var (i, value) in _tiles)
 262144152        {
 262144153            if (value == null)
 262140154            {
 262140155                continue;
 156            }
 157
 4158            yield return (uint)i;
 4159        }
 8160    }
 161
 162    internal NetworkTile? GetTile(uint localTileId)
 2163    {
 2164        return this.GetTileForRead(localTileId);
 2165    }
 166
 167    /// <summary>
 168    /// The zoom level.
 169    /// </summary>
 4170    public int Zoom => _network.Zoom;
 171
 172    /// <summary>
 173    /// The max island size.
 174    /// </summary>
 4175    internal RoutingNetworkIslandManager IslandManager => _network.IslandManager;
 176
 177    /// <summary>
 178    /// Adds a new vertex.
 179    /// </summary>
 180    /// <param name="longitude">The longitude.</param>
 181    /// <param name="latitude">The latitude.</param>
 182    /// <param name="elevation">The elevation.</param>
 183    /// <returns>The vertex id.</returns>
 184    public VertexId AddVertex(double longitude, double latitude, float? elevation = null)
 303185    {
 186        // get the local tile id.
 303187        var (x, y) = TileStatic.WorldToTile(longitude, latitude, _network.Zoom);
 303188        var localTileId = TileStatic.ToLocalId(x, y, _network.Zoom);
 189
 190        // ensure minimum size.
 303191        _tiles.EnsureMinimumSize(localTileId);
 192
 193        // get the tile (or create it).
 303194        var (tile, _) = this.GetTileForWrite(localTileId);
 195
 196        // add the vertex.
 303197        return tile.AddVertex(longitude, latitude, elevation);
 303198    }
 199
 200    internal bool TryGetVertex(VertexId vertex, out double longitude, out double latitude, out float? e)
 0201    {
 0202        var localTileId = vertex.TileId;
 203
 204        // get tile.
 0205        if (_tiles.Length <= localTileId)
 0206        {
 0207            longitude = default;
 0208            latitude = default;
 0209            e = null;
 0210            return false;
 211        }
 212
 0213        var tile = this.GetTileForRead(localTileId);
 0214        if (tile != null) return tile.TryGetVertex(vertex, out longitude, out latitude, out e);
 215
 216        // no tile, no vertex.
 0217        longitude = default;
 0218        latitude = default;
 0219        e = null;
 0220        return false;
 0221    }
 222
 223    /// <summary>
 224    /// Adds a new edge.
 225    /// </summary>
 226    /// <param name="tail">The tail vertex.</param>
 227    /// <param name="head">The head vertex.</param>
 228    /// <param name="shape">The shape points, if any.</param>
 229    /// <param name="attributes">The attributes, if any.</param>
 230    /// <returns>An edge id.</returns>
 231    /// <exception cref="ArgumentException"></exception>
 232    public EdgeId AddEdge(VertexId tail, VertexId head,
 233        IEnumerable<(double longitude, double latitude, float? e)>? shape = null,
 234        IEnumerable<(string key, string value)>? attributes = null)
 198235    {
 198236        var (tile, edgeTypeFunc) = this.GetTileForWrite(tail.TileId);
 198237        if (tile == null) throw new ArgumentException($"Cannot add edge with a vertex that doesn't exist.");
 238
 198239        var edgeTypeId = attributes != null ? (uint?)edgeTypeFunc(attributes) : null;
 198240        var edge1 = tile.AddEdge(tail, head, shape, attributes, null, edgeTypeId);
 393241        if (tail.TileId == head.TileId) return edge1;
 242
 243        // this edge crosses tiles, also add an extra edge to the other tile.
 3244        (tile, _) = this.GetTileForWrite(head.TileId);
 3245        tile.AddEdge(tail, head, shape, attributes, edge1, edgeTypeId);
 246
 3247        return edge1;
 198248    }
 249
 250    /// <summary>
 251    /// Deletes the given edge.
 252    /// </summary>
 253    /// <param name="edgeId">The edge id.</param>
 254    /// <returns>True if the edge was found and deleted, false otherwise.</returns>
 255    public bool DeleteEdge(EdgeId edgeId)
 8256    {
 8257        var edgeEnumerator = this.GetEdgeEnumerator();
 8258        if (!edgeEnumerator.MoveTo(edgeId)) return false;
 259
 8260        var vertex1 = edgeEnumerator.Tail;
 8261        var vertex2 = edgeEnumerator.Head;
 262
 8263        var (tile, _) = this.GetTileForWrite(vertex1.TileId);
 8264        if (tile == null) throw new ArgumentException($"Cannot add edge with a vertex that doesn't exist.");
 265
 8266        tile.DeleteEdge(edgeId);
 267
 14268        if (vertex1.TileId == vertex2.TileId) return true;
 269
 270        // vertex2 is in different tile, delete edge from both.
 2271        (tile, _) = this.GetTileForWrite(vertex2.TileId);
 2272        tile.DeleteEdge(edgeId);
 273
 2274        return true;
 8275    }
 276
 277    /// <summary>
 278    /// Adds turn costs.
 279    /// </summary>
 280    /// <param name="vertex">The vertex.</param>
 281    /// <param name="attributes">The attributes.</param>
 282    /// <param name="edges">The edges.</param>
 283    /// <param name="costs">The costs as a matrix.</param>
 284    /// <param name="prefix">A path prefix, if any.</param>
 285    /// <exception cref="ArgumentException"></exception>
 286    public void AddTurnCosts(VertexId vertex, IEnumerable<(string key, string value)> attributes,
 287        EdgeId[] edges, uint[,] costs, IEnumerable<EdgeId>? prefix = null)
 8288    {
 8289        prefix ??= ArraySegment<EdgeId>.Empty;
 290
 291        // get the tile (or create it).
 8292        var (tile, _) = this.GetTileForWrite(vertex.TileId);
 8293        if (tile == null)
 0294        {
 0295            throw new ArgumentException($"Cannot add turn costs to a vertex that doesn't exist.");
 296        }
 297
 298        // get the turn cost type id.
 8299        var turnCostFunc = _network.RouterDb.GetTurnCostTypeMap();
 8300        var turnCostTypeId = turnCostFunc.func(attributes);
 301
 302        // add the turn cost table using the type id.
 8303        tile.AddTurnCosts(vertex, turnCostTypeId, edges, costs, attributes, prefix);
 8304    }
 305
 306    /// <summary>
 307    /// Removes all data.
 308    /// </summary>
 309    public void Clear()
 0310    {
 0311        _tiles.Resize(0);
 0312        _modified.Resize(0);
 0313    }
 314
 315    internal RoutingNetwork ToRoutingNetwork()
 119316    {
 15597925317        foreach (var tile in _tiles)
 7798784318        {
 7798784319            if (tile.value is { HasDeletedEdges: true })
 10320            {
 321                // at this point edge ids change but it right when the mutator is disposed.
 322                // for a user perspective this is not that strange.
 10323                tile.value.RemoveDeletedEdges();
 10324            }
 7798784325        }
 326
 327        // create the new network.
 119328        var routingNetwork = new RoutingNetwork(_network.RouterDb, _tiles, _network.Zoom, _islandManager);
 329
 330        // check if listeners need to be cloned/copied over.
 357331        foreach (var listener in _network.UsageNotifier.RegisteredListeners)
 0332        {
 0333            var clonedListener = listener.CloneForNewNetwork(routingNetwork);
 0334            if (clonedListener == null) continue;
 335
 0336            routingNetwork.UsageNotifier.AddListener(clonedListener);
 0337        }
 338
 119339        return routingNetwork;
 119340    }
 341
 342    /// <inheritdoc/>
 343    public void Dispose()
 119344    {
 119345        _network.ClearMutator();
 346
 119347        (_network.RouterDb as IRouterDbMutable).Finish(this.ToRoutingNetwork());
 119348    }
 349}