< Summary

Class:Itinero.Network.Mutation.RoutingNetworkMutator
Assembly:Itinero
File(s):/home/runner/work/routing2/routing2/src/Itinero/Network/Mutation/RoutingNetworkMutator.cs
Covered lines:117
Uncovered lines:45
Coverable lines:162
Total lines:370
Line coverage:72.2% (117 of 162)
Covered branches:36
Total branches:52
Branch coverage:69.2% (36 of 52)
Tag:251_23667616543

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(...)50%440%
AddEdge(...)70%1087.5%
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.Geo;
 4using Itinero.Network.DataStructures;
 5using Itinero.Network.Enumerators.Edges;
 6using Itinero.Network.Search.Islands;
 7using Itinero.Network.Tiles;
 8// ReSharper disable PossibleMultipleEnumeration
 9
 10namespace Itinero.Network.Mutation;
 11
 12/// <summary>
 13/// A routing network mutator. This mutator can be used to change anything except deleting vertices.
 14///
 15/// This mutator can be used to:
 16/// - add new vertices
 17/// - add new edges.
 18/// - delete edges.
 19///
 20/// </summary>
 21public class RoutingNetworkMutator : IDisposable, IEdgeEnumerable
 22{
 23    private readonly SparseArray<bool> _modified;
 24    private readonly SparseArray<NetworkTile?> _tiles;
 25    private readonly IRoutingNetworkMutable _network;
 26    private readonly RoutingNetworkIslandManager _islandManager;
 27
 17228    internal RoutingNetworkMutator(IRoutingNetworkMutable network)
 17229    {
 17230        _network = network;
 31
 17232        _tiles = _network.Tiles.Clone();
 17233        _modified = new SparseArray<bool>(_tiles.Length);
 34
 17235        _islandManager = network.IslandManager.Clone();
 17236    }
 37
 38    NetworkTile? IEdgeEnumerable.GetTileForRead(uint localTileId)
 56539    {
 56540        return this.GetTileForRead(localTileId);
 56541    }
 42
 43    internal bool HasTile(uint localTileId)
 044    {
 045        return _tiles.Length > localTileId &&
 046               _modified[localTileId] == true;
 047    }
 48
 49    internal void SetTile(NetworkTile tile)
 050    {
 051        _tiles[tile.TileId] = tile;
 052        _modified[tile.TileId] = true;
 053    }
 54
 55    private NetworkTile? GetTileForRead(uint localTileId)
 9966756    {
 9966757        if (_tiles.Length <= localTileId) return null;
 58
 59
 60        // get tile, if any.
 9966761        var tile = _tiles[localTileId];
 9966762        if (tile == null) return null;
 63
 64        // check edge type map.
 9966765        var edgeTypeMap = _network.RouterDb.GetEdgeTypeMap();
 19933466        if (tile.EdgeTypeMapId == edgeTypeMap.id) return tile;
 67
 68        // tile.EdgeTypeMapId indicates the version of the used edgeTypeMap
 69        // If the id is different, the loaded tile needs updating; e.g. because a cost function has been changed
 070        tile = tile.CloneForEdgeTypeMap(edgeTypeMap);
 071        _tiles[localTileId] = tile;
 072        return tile;
 9966773    }
 74
 75    /// <summary>
 76    /// Gets the edge associated with the given id.
 77    /// </summary>
 78    /// <param name="edgeId">The edge id.</param>
 79    /// <param name="forward">The forward flag.</param>
 80    /// <returns>The edge details.</returns>
 81    public INetworkTileEdge GetEdge(EdgeId edgeId, bool forward)
 082    {
 083        var (tile, _) = this.GetTileForWrite(edgeId.TileId);
 84
 085        var edge = new NetworkTileEnumerator();
 086        edge.MoveTo(tile);
 087        edge.MoveTo(edgeId, forward);
 088        return edge;
 089    }
 90
 91    /// <summary>
 92    /// Gets a tile for writing.
 93    /// </summary>
 94    /// <param name="localTileId">The local tile id.</param>
 95    /// <returns></returns>
 96    /// <remarks>
 97    /// This makes sure tiles are first cloned before being written to.
 98    /// </remarks>
 99    private (NetworkTile tile, Func<IEnumerable<(string key, string value)>, uint> func) GetTileForWrite(
 100        uint localTileId)
 89622101    {
 89622102        var edgeTypeMap = _network.RouterDb.GetEdgeTypeMap();
 103
 104        // ensure minimum size.
 89622105        _tiles.EnsureMinimumSize(localTileId);
 89622106        _modified.EnsureMinimumSize(localTileId);
 107
 108        // check if there is already a modified version.
 89622109        var tile = _tiles[localTileId];
 89622110        if (tile != null)
 89227111        {
 112            // if edge types map doesn't match, clone and mark as modified.
 89227113            if (tile.EdgeTypeMapId != edgeTypeMap.id)
 0114            {
 0115                tile = tile.CloneForEdgeTypeMap(edgeTypeMap);
 0116                _modified[localTileId] = true;
 0117                _tiles[localTileId] = tile;
 0118                return (tile, edgeTypeMap.func);
 119            }
 120
 121            // if already modified, just return.
 89227122            var modified = _modified[localTileId];
 178444123            if (modified) return (tile, edgeTypeMap.func);
 124
 125            // make sure all tiles being written to are cloned.
 10126            tile = tile.Clone();
 10127            _modified[localTileId] = true;
 10128            _tiles[localTileId] = tile;
 10129            return (tile, edgeTypeMap.func);
 130        }
 131
 132        // there is no tile, create a new one.
 395133        tile = new NetworkTile(_network.Zoom, localTileId, edgeTypeMap.id);
 134
 135        // store in the local tiles.
 395136        _tiles[localTileId] = tile;
 395137        _modified[localTileId] = true;
 395138        return (tile, edgeTypeMap.func);
 89622139    }
 140
 141    /// <summary>
 142    /// Gets the edge enumerator.
 143    /// </summary>
 144    /// <returns></returns>
 145    public RoutingNetworkMutatorEdgeEnumerator GetEdgeEnumerator()
 2455146    {
 2455147        return new(this);
 2455148    }
 149
 150    internal IEnumerable<uint> GetTiles()
 8151    {
 524312152        foreach (var (i, value) in _tiles)
 262144153        {
 262144154            if (value == null)
 262140155            {
 262140156                continue;
 157            }
 158
 4159            yield return (uint)i;
 4160        }
 8161    }
 162
 163    internal NetworkTile? GetTile(uint localTileId)
 2164    {
 2165        return this.GetTileForRead(localTileId);
 2166    }
 167
 168    /// <summary>
 169    /// The zoom level.
 170    /// </summary>
 4171    public int Zoom => _network.Zoom;
 172
 173    /// <summary>
 174    /// The max island size.
 175    /// </summary>
 4176    internal RoutingNetworkIslandManager IslandManager => _network.IslandManager;
 177
 178    /// <summary>
 179    /// Adds a new vertex.
 180    /// </summary>
 181    /// <param name="longitude">The longitude.</param>
 182    /// <param name="latitude">The latitude.</param>
 183    /// <param name="elevation">The elevation.</param>
 184    /// <returns>The vertex id.</returns>
 185    public VertexId AddVertex(double longitude, double latitude, float? elevation = null)
 36766186    {
 187        // get the local tile id.
 36766188        var (x, y) = TileStatic.WorldToTile(longitude, latitude, _network.Zoom);
 36766189        var localTileId = TileStatic.ToLocalId(x, y, _network.Zoom);
 190
 191        // ensure minimum size.
 36766192        _tiles.EnsureMinimumSize(localTileId);
 193
 194        // get the tile (or create it).
 36766195        var (tile, _) = this.GetTileForWrite(localTileId);
 196
 197        // add the vertex.
 36766198        return tile.AddVertex(longitude, latitude, elevation);
 36766199    }
 200
 201    internal bool TryGetVertex(VertexId vertex, out double longitude, out double latitude, out float? e)
 99100202    {
 99100203        var localTileId = vertex.TileId;
 204
 205        // get tile.
 99100206        if (_tiles.Length <= localTileId)
 0207        {
 0208            longitude = default;
 0209            latitude = default;
 0210            e = null;
 0211            return false;
 212        }
 213
 99100214        var tile = this.GetTileForRead(localTileId);
 198200215        if (tile != null) return tile.TryGetVertex(vertex, out longitude, out latitude, out e);
 216
 217        // no tile, no vertex.
 0218        longitude = default;
 0219        latitude = default;
 0220        e = null;
 0221        return false;
 99100222    }
 223
 224    /// <summary>
 225    /// Adds a new edge.
 226    /// </summary>
 227    /// <param name="tail">The tail vertex.</param>
 228    /// <param name="head">The head vertex.</param>
 229    /// <param name="shape">The shape points, if any.</param>
 230    /// <param name="attributes">The attributes, if any.</param>
 231    /// <returns>An edge id.</returns>
 232    /// <exception cref="ArgumentException"></exception>
 233    public EdgeId AddEdge(VertexId tail, VertexId head,
 234        IEnumerable<(double longitude, double latitude, float? e)>? shape = null,
 235        IEnumerable<(string key, string value)>? attributes = null)
 49550236    {
 49550237        var (tile, edgeTypeFunc) = this.GetTileForWrite(tail.TileId);
 49550238        if (tile == null) throw new ArgumentException($"Cannot add edge with a vertex that doesn't exist.");
 239
 49550240        var edgeTypeId = attributes != null ? (uint?)edgeTypeFunc(attributes) : null;
 241
 242        // compute edge length in centimeters.
 49550243        if (!this.TryGetVertex(tail, out var lon1, out var lat1, out var e1))
 0244            throw new ArgumentException($"Vertex {tail} not found.", nameof(tail));
 49550245        if (!this.TryGetVertex(head, out var lon2, out var lat2, out var e2))
 0246            throw new ArgumentException($"Vertex {head} not found.", nameof(head));
 49550247        var length = (uint)((lon1, lat1, e1).DistanceEstimateInMeterShape(
 49550248            (lon2, lat2, e2), shape) * 100);
 249
 49550250        var edge1 = tile.AddEdge(tail, head, shape, attributes, null, edgeTypeId, length);
 96829251        if (tail.TileId == head.TileId) return edge1;
 252
 253        // this edge crosses tiles, also add an extra edge to the other tile.
 2271254        (tile, _) = this.GetTileForWrite(head.TileId);
 2271255        tile.AddEdge(tail, head, shape, attributes, edge1, edgeTypeId, length);
 256
 2271257        return edge1;
 49550258    }
 259
 260    /// <summary>
 261    /// Deletes the given edge.
 262    /// </summary>
 263    /// <param name="edgeId">The edge id.</param>
 264    /// <returns>True if the edge was found and deleted, false otherwise.</returns>
 265    public bool DeleteEdge(EdgeId edgeId)
 8266    {
 8267        var edgeEnumerator = this.GetEdgeEnumerator();
 8268        if (!edgeEnumerator.MoveTo(edgeId)) return false;
 269
 8270        var vertex1 = edgeEnumerator.Tail;
 8271        var vertex2 = edgeEnumerator.Head;
 272
 8273        var (tile, _) = this.GetTileForWrite(vertex1.TileId);
 8274        if (tile == null) throw new ArgumentException($"Cannot add edge with a vertex that doesn't exist.");
 275
 8276        tile.DeleteEdge(edgeId);
 277
 14278        if (vertex1.TileId == vertex2.TileId) return true;
 279
 280        // vertex2 is in different tile, delete edge from both.
 2281        (tile, _) = this.GetTileForWrite(vertex2.TileId);
 2282        tile.DeleteEdge(edgeId);
 283
 2284        return true;
 8285    }
 286
 287    /// <summary>
 288    /// Adds turn costs.
 289    ///
 290    /// Example, a simple restricted turn is added as:
 291    /// - vertex: The vertex the turn occurs at.
 292    /// - attributes: describes the type of costs, vehicle profiles should interpret this turn as binary.
 293    /// - edges: [FromEdgeId, ToEdgeId]
 294    /// - costs: [[0, 1], [0,0]] -> this adds only a cost for the turn (FromEdgeId -> ToEdgeId).
 295    ///
 296    /// Things to consider:
 297    /// - When multiple costs are added on top of each other they are added.
 298    /// - A single binary cost is enough to restrict the turn.
 299    /// - The actual cost is interpreted by the vehicle profile, the vehicle profile is free to interpret the data being
 300    /// </summary>
 301    /// <param name="vertex">The vertex.</param>
 302    /// <param name="attributes">The attributes.</param>
 303    /// <param name="edges">The edges, the edge should match the ordering in the costs matrix.</param>
 304    /// <param name="costs">The costs as a matrix, [fromEdge, toEdge].</param>
 305    /// <param name="prefix">A path prefix, if any.</param>
 306    /// <exception cref="ArgumentException"></exception>
 307    public void AddTurnCosts(VertexId vertex, IEnumerable<(string key, string value)> attributes,
 308        EdgeId[] edges, uint[,] costs, IEnumerable<EdgeId>? prefix = null)
 1025309    {
 1025310        prefix ??= ArraySegment<EdgeId>.Empty;
 311
 312        // get the tile (or create it).
 1025313        var (tile, _) = this.GetTileForWrite(vertex.TileId);
 1025314        if (tile == null)
 0315        {
 0316            throw new ArgumentException($"Cannot add turn costs to a vertex that doesn't exist.");
 317        }
 318
 319        // get the turn cost type id.
 1025320        var turnCostFunc = _network.RouterDb.GetTurnCostTypeMap();
 1025321        var turnCostTypeId = turnCostFunc.func(attributes);
 322
 323        // add the turn cost table using the type id.
 1025324        tile.AddTurnCosts(vertex, turnCostTypeId, edges, costs, attributes, prefix);
 1025325    }
 326
 327    /// <summary>
 328    /// Removes all data.
 329    /// </summary>
 330    public void Clear()
 0331    {
 0332        _tiles.Resize(0);
 0333        _modified.Resize(0);
 0334    }
 335
 336    internal RoutingNetwork ToRoutingNetwork()
 169337    {
 22807035338        foreach (var tile in _tiles)
 11403264339        {
 11403264340            if (tile.value is { HasDeletedEdges: true })
 10341            {
 342                // at this point edge ids change but it right when the mutator is disposed.
 343                // for a user perspective this is not that strange.
 10344                tile.value.RemoveDeletedEdges();
 10345            }
 11403264346        }
 347
 348        // create the new network.
 169349        var routingNetwork = new RoutingNetwork(_network.RouterDb, _tiles, _network.Zoom, _islandManager);
 350
 351        // check if listeners need to be cloned/copied over.
 507352        foreach (var listener in _network.UsageNotifier.RegisteredListeners)
 0353        {
 0354            var clonedListener = listener.CloneForNewNetwork(routingNetwork);
 0355            if (clonedListener == null) continue;
 356
 0357            routingNetwork.UsageNotifier.AddListener(clonedListener);
 0358        }
 359
 169360        return routingNetwork;
 169361    }
 362
 363    /// <inheritdoc/>
 364    public void Dispose()
 169365    {
 169366        _network.ClearMutator();
 367
 169368        (_network.RouterDb as IRouterDbMutable).Finish(this.ToRoutingNetwork());
 169369    }
 370}