| | 1 | | using System; |
| | 2 | | using System.Collections.Generic; |
| | 3 | | using Itinero.Network.DataStructures; |
| | 4 | | using Itinero.Network.Enumerators.Edges; |
| | 5 | | using Itinero.Network.Search.Islands; |
| | 6 | | using Itinero.Network.Tiles; |
| | 7 | | // ReSharper disable PossibleMultipleEnumeration |
| | 8 | |
|
| | 9 | | namespace 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> |
| | 20 | | public 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 | |
|
| 122 | 27 | | internal RoutingNetworkMutator(IRoutingNetworkMutable network) |
| 122 | 28 | | { |
| 122 | 29 | | _network = network; |
| | 30 | |
|
| 122 | 31 | | _tiles = _network.Tiles.Clone(); |
| 122 | 32 | | _modified = new SparseArray<bool>(_tiles.Length); |
| | 33 | |
|
| 122 | 34 | | _islandManager = network.IslandManager.Clone(); |
| 122 | 35 | | } |
| | 36 | |
|
| | 37 | | NetworkTile? IEdgeEnumerable.GetTileForRead(uint localTileId) |
| 17 | 38 | | { |
| 17 | 39 | | return this.GetTileForRead(localTileId); |
| 17 | 40 | | } |
| | 41 | |
|
| | 42 | | internal bool HasTile(uint localTileId) |
| 0 | 43 | | { |
| 0 | 44 | | return _tiles.Length > localTileId && |
| 0 | 45 | | _modified[localTileId] == true; |
| 0 | 46 | | } |
| | 47 | |
|
| | 48 | | internal void SetTile(NetworkTile tile) |
| 0 | 49 | | { |
| 0 | 50 | | _tiles[tile.TileId] = tile; |
| 0 | 51 | | _modified[tile.TileId] = true; |
| 0 | 52 | | } |
| | 53 | |
|
| | 54 | | private NetworkTile? GetTileForRead(uint localTileId) |
| 19 | 55 | | { |
| 19 | 56 | | if (_tiles.Length <= localTileId) return null; |
| | 57 | |
|
| | 58 | |
|
| | 59 | | // get tile, if any. |
| 19 | 60 | | var tile = _tiles[localTileId]; |
| 19 | 61 | | if (tile == null) return null; |
| | 62 | |
|
| | 63 | | // check edge type map. |
| 19 | 64 | | var edgeTypeMap = _network.RouterDb.GetEdgeTypeMap(); |
| 38 | 65 | | 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 |
| 0 | 69 | | tile = tile.CloneForEdgeTypeMap(edgeTypeMap); |
| 0 | 70 | | _tiles[localTileId] = tile; |
| 0 | 71 | | return tile; |
| 19 | 72 | | } |
| | 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) |
| 0 | 81 | | { |
| 0 | 82 | | var (tile, _) = this.GetTileForWrite(edgeId.TileId); |
| | 83 | |
|
| 0 | 84 | | var edge = new NetworkTileEnumerator(); |
| 0 | 85 | | edge.MoveTo(tile); |
| 0 | 86 | | edge.MoveTo(edgeId, forward); |
| 0 | 87 | | return edge; |
| 0 | 88 | | } |
| | 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) |
| 522 | 100 | | { |
| 522 | 101 | | var edgeTypeMap = _network.RouterDb.GetEdgeTypeMap(); |
| | 102 | |
|
| | 103 | | // ensure minimum size. |
| 522 | 104 | | _tiles.EnsureMinimumSize(localTileId); |
| 522 | 105 | | _modified.EnsureMinimumSize(localTileId); |
| | 106 | |
|
| | 107 | | // check if there is already a modified version. |
| 522 | 108 | | var tile = _tiles[localTileId]; |
| 522 | 109 | | if (tile != null) |
| 409 | 110 | | { |
| | 111 | | // if edge types map doesn't match, clone and mark as modified. |
| 409 | 112 | | if (tile.EdgeTypeMapId != edgeTypeMap.id) |
| 0 | 113 | | { |
| 0 | 114 | | tile = tile.CloneForEdgeTypeMap(edgeTypeMap); |
| 0 | 115 | | _modified[localTileId] = true; |
| 0 | 116 | | _tiles[localTileId] = tile; |
| 0 | 117 | | return (tile, edgeTypeMap.func); |
| | 118 | | } |
| | 119 | |
|
| | 120 | | // if already modified, just return. |
| 409 | 121 | | var modified = _modified[localTileId]; |
| 808 | 122 | | if (modified) return (tile, edgeTypeMap.func); |
| | 123 | |
|
| | 124 | | // make sure all tiles being written to are cloned. |
| 10 | 125 | | tile = tile.Clone(); |
| 10 | 126 | | _modified[localTileId] = true; |
| 10 | 127 | | _tiles[localTileId] = tile; |
| 10 | 128 | | return (tile, edgeTypeMap.func); |
| | 129 | | } |
| | 130 | |
|
| | 131 | | // there is no tile, create a new one. |
| 113 | 132 | | tile = new NetworkTile(_network.Zoom, localTileId, edgeTypeMap.id); |
| | 133 | |
|
| | 134 | | // store in the local tiles. |
| 113 | 135 | | _tiles[localTileId] = tile; |
| 113 | 136 | | _modified[localTileId] = true; |
| 113 | 137 | | return (tile, edgeTypeMap.func); |
| 522 | 138 | | } |
| | 139 | |
|
| | 140 | | /// <summary> |
| | 141 | | /// Gets the edge enumerator. |
| | 142 | | /// </summary> |
| | 143 | | /// <returns></returns> |
| | 144 | | public RoutingNetworkMutatorEdgeEnumerator GetEdgeEnumerator() |
| 23 | 145 | | { |
| 23 | 146 | | return new(this); |
| 23 | 147 | | } |
| | 148 | |
|
| | 149 | | internal IEnumerable<uint> GetTiles() |
| 8 | 150 | | { |
| 524312 | 151 | | foreach (var (i, value) in _tiles) |
| 262144 | 152 | | { |
| 262144 | 153 | | if (value == null) |
| 262140 | 154 | | { |
| 262140 | 155 | | continue; |
| | 156 | | } |
| | 157 | |
|
| 4 | 158 | | yield return (uint)i; |
| 4 | 159 | | } |
| 8 | 160 | | } |
| | 161 | |
|
| | 162 | | internal NetworkTile? GetTile(uint localTileId) |
| 2 | 163 | | { |
| 2 | 164 | | return this.GetTileForRead(localTileId); |
| 2 | 165 | | } |
| | 166 | |
|
| | 167 | | /// <summary> |
| | 168 | | /// The zoom level. |
| | 169 | | /// </summary> |
| 4 | 170 | | public int Zoom => _network.Zoom; |
| | 171 | |
|
| | 172 | | /// <summary> |
| | 173 | | /// The max island size. |
| | 174 | | /// </summary> |
| 4 | 175 | | 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) |
| 303 | 185 | | { |
| | 186 | | // get the local tile id. |
| 303 | 187 | | var (x, y) = TileStatic.WorldToTile(longitude, latitude, _network.Zoom); |
| 303 | 188 | | var localTileId = TileStatic.ToLocalId(x, y, _network.Zoom); |
| | 189 | |
|
| | 190 | | // ensure minimum size. |
| 303 | 191 | | _tiles.EnsureMinimumSize(localTileId); |
| | 192 | |
|
| | 193 | | // get the tile (or create it). |
| 303 | 194 | | var (tile, _) = this.GetTileForWrite(localTileId); |
| | 195 | |
|
| | 196 | | // add the vertex. |
| 303 | 197 | | return tile.AddVertex(longitude, latitude, elevation); |
| 303 | 198 | | } |
| | 199 | |
|
| | 200 | | internal bool TryGetVertex(VertexId vertex, out double longitude, out double latitude, out float? e) |
| 0 | 201 | | { |
| 0 | 202 | | var localTileId = vertex.TileId; |
| | 203 | |
|
| | 204 | | // get tile. |
| 0 | 205 | | if (_tiles.Length <= localTileId) |
| 0 | 206 | | { |
| 0 | 207 | | longitude = default; |
| 0 | 208 | | latitude = default; |
| 0 | 209 | | e = null; |
| 0 | 210 | | return false; |
| | 211 | | } |
| | 212 | |
|
| 0 | 213 | | var tile = this.GetTileForRead(localTileId); |
| 0 | 214 | | if (tile != null) return tile.TryGetVertex(vertex, out longitude, out latitude, out e); |
| | 215 | |
|
| | 216 | | // no tile, no vertex. |
| 0 | 217 | | longitude = default; |
| 0 | 218 | | latitude = default; |
| 0 | 219 | | e = null; |
| 0 | 220 | | return false; |
| 0 | 221 | | } |
| | 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) |
| 198 | 235 | | { |
| 198 | 236 | | var (tile, edgeTypeFunc) = this.GetTileForWrite(tail.TileId); |
| 198 | 237 | | if (tile == null) throw new ArgumentException($"Cannot add edge with a vertex that doesn't exist."); |
| | 238 | |
|
| 198 | 239 | | var edgeTypeId = attributes != null ? (uint?)edgeTypeFunc(attributes) : null; |
| 198 | 240 | | var edge1 = tile.AddEdge(tail, head, shape, attributes, null, edgeTypeId); |
| 393 | 241 | | if (tail.TileId == head.TileId) return edge1; |
| | 242 | |
|
| | 243 | | // this edge crosses tiles, also add an extra edge to the other tile. |
| 3 | 244 | | (tile, _) = this.GetTileForWrite(head.TileId); |
| 3 | 245 | | tile.AddEdge(tail, head, shape, attributes, edge1, edgeTypeId); |
| | 246 | |
|
| 3 | 247 | | return edge1; |
| 198 | 248 | | } |
| | 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) |
| 8 | 256 | | { |
| 8 | 257 | | var edgeEnumerator = this.GetEdgeEnumerator(); |
| 8 | 258 | | if (!edgeEnumerator.MoveTo(edgeId)) return false; |
| | 259 | |
|
| 8 | 260 | | var vertex1 = edgeEnumerator.Tail; |
| 8 | 261 | | var vertex2 = edgeEnumerator.Head; |
| | 262 | |
|
| 8 | 263 | | var (tile, _) = this.GetTileForWrite(vertex1.TileId); |
| 8 | 264 | | if (tile == null) throw new ArgumentException($"Cannot add edge with a vertex that doesn't exist."); |
| | 265 | |
|
| 8 | 266 | | tile.DeleteEdge(edgeId); |
| | 267 | |
|
| 14 | 268 | | if (vertex1.TileId == vertex2.TileId) return true; |
| | 269 | |
|
| | 270 | | // vertex2 is in different tile, delete edge from both. |
| 2 | 271 | | (tile, _) = this.GetTileForWrite(vertex2.TileId); |
| 2 | 272 | | tile.DeleteEdge(edgeId); |
| | 273 | |
|
| 2 | 274 | | return true; |
| 8 | 275 | | } |
| | 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) |
| 8 | 288 | | { |
| 8 | 289 | | prefix ??= ArraySegment<EdgeId>.Empty; |
| | 290 | |
|
| | 291 | | // get the tile (or create it). |
| 8 | 292 | | var (tile, _) = this.GetTileForWrite(vertex.TileId); |
| 8 | 293 | | if (tile == null) |
| 0 | 294 | | { |
| 0 | 295 | | throw new ArgumentException($"Cannot add turn costs to a vertex that doesn't exist."); |
| | 296 | | } |
| | 297 | |
|
| | 298 | | // get the turn cost type id. |
| 8 | 299 | | var turnCostFunc = _network.RouterDb.GetTurnCostTypeMap(); |
| 8 | 300 | | var turnCostTypeId = turnCostFunc.func(attributes); |
| | 301 | |
|
| | 302 | | // add the turn cost table using the type id. |
| 8 | 303 | | tile.AddTurnCosts(vertex, turnCostTypeId, edges, costs, attributes, prefix); |
| 8 | 304 | | } |
| | 305 | |
|
| | 306 | | /// <summary> |
| | 307 | | /// Removes all data. |
| | 308 | | /// </summary> |
| | 309 | | public void Clear() |
| 0 | 310 | | { |
| 0 | 311 | | _tiles.Resize(0); |
| 0 | 312 | | _modified.Resize(0); |
| 0 | 313 | | } |
| | 314 | |
|
| | 315 | | internal RoutingNetwork ToRoutingNetwork() |
| 119 | 316 | | { |
| 15597925 | 317 | | foreach (var tile in _tiles) |
| 7798784 | 318 | | { |
| 7798784 | 319 | | if (tile.value is { HasDeletedEdges: true }) |
| 10 | 320 | | { |
| | 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. |
| 10 | 323 | | tile.value.RemoveDeletedEdges(); |
| 10 | 324 | | } |
| 7798784 | 325 | | } |
| | 326 | |
|
| | 327 | | // create the new network. |
| 119 | 328 | | var routingNetwork = new RoutingNetwork(_network.RouterDb, _tiles, _network.Zoom, _islandManager); |
| | 329 | |
|
| | 330 | | // check if listeners need to be cloned/copied over. |
| 357 | 331 | | foreach (var listener in _network.UsageNotifier.RegisteredListeners) |
| 0 | 332 | | { |
| 0 | 333 | | var clonedListener = listener.CloneForNewNetwork(routingNetwork); |
| 0 | 334 | | if (clonedListener == null) continue; |
| | 335 | |
|
| 0 | 336 | | routingNetwork.UsageNotifier.AddListener(clonedListener); |
| 0 | 337 | | } |
| | 338 | |
|
| 119 | 339 | | return routingNetwork; |
| 119 | 340 | | } |
| | 341 | |
|
| | 342 | | /// <inheritdoc/> |
| | 343 | | public void Dispose() |
| 119 | 344 | | { |
| 119 | 345 | | _network.ClearMutator(); |
| | 346 | |
|
| 119 | 347 | | (_network.RouterDb as IRouterDbMutable).Finish(this.ToRoutingNetwork()); |
| 119 | 348 | | } |
| | 349 | | } |