| | | 1 | | using System; |
| | | 2 | | using System.Collections.Generic; |
| | | 3 | | using Itinero.Geo; |
| | | 4 | | using Itinero.Network.Enumerators.Edges; |
| | | 5 | | using Itinero.Network.Tiles; |
| | | 6 | | using Itinero.Network.Tiles.Standalone.Global; |
| | | 7 | | // ReSharper disable PossibleMultipleEnumeration |
| | | 8 | | |
| | | 9 | | namespace Itinero.Network.Writer; |
| | | 10 | | |
| | | 11 | | /// <summary> |
| | | 12 | | /// A writer to write to a network. This writer will never change existing data, only add new data. |
| | | 13 | | /// |
| | | 14 | | /// This writer can: |
| | | 15 | | /// - add new vertices |
| | | 16 | | /// - add new edges. |
| | | 17 | | /// |
| | | 18 | | /// This writer cannot mutate existing data, only add new. |
| | | 19 | | /// </summary> |
| | | 20 | | public class RoutingNetworkWriter : IDisposable |
| | | 21 | | { |
| | | 22 | | private readonly IRoutingNetworkWritable _network; |
| | | 23 | | |
| | 41 | 24 | | internal RoutingNetworkWriter(IRoutingNetworkWritable network) |
| | 41 | 25 | | { |
| | 41 | 26 | | _network = network; |
| | 41 | 27 | | } |
| | | 28 | | |
| | | 29 | | /// <summary> |
| | | 30 | | /// Gets an edge enumerator. |
| | | 31 | | /// </summary> |
| | | 32 | | /// <returns>The enumerator.</returns> |
| | | 33 | | internal RoutingNetworkEdgeEnumerator GetEdgeEnumerator() |
| | 0 | 34 | | { |
| | 0 | 35 | | return _network.GetEdgeEnumerator(); |
| | 0 | 36 | | } |
| | | 37 | | |
| | | 38 | | /// <summary> |
| | | 39 | | /// Adds a new vertex. |
| | | 40 | | /// </summary> |
| | | 41 | | /// <param name="longitude">The longitude.</param> |
| | | 42 | | /// <param name="latitude">The latitude.</param> |
| | | 43 | | /// <param name="elevation">The elevation.</param> |
| | | 44 | | /// <returns>The vertex id.</returns> |
| | | 45 | | public VertexId AddVertex(double longitude, double latitude, float? elevation = null) |
| | 85 | 46 | | { |
| | | 47 | | // get the local tile id. |
| | 85 | 48 | | var (x, y) = TileStatic.WorldToTile(longitude, latitude, _network.Zoom); |
| | 85 | 49 | | var localTileId = TileStatic.ToLocalId(x, y, _network.Zoom); |
| | | 50 | | |
| | | 51 | | // get the tile (or create it). |
| | 85 | 52 | | var (tile, _) = _network.GetTileForWrite(localTileId); |
| | | 53 | | |
| | 85 | 54 | | return tile.AddVertex(longitude, latitude, elevation); |
| | 85 | 55 | | } |
| | | 56 | | |
| | | 57 | | /// <summary> |
| | | 58 | | /// Computes the edge length in centimeters from vertex locations and shape. |
| | | 59 | | /// </summary> |
| | | 60 | | public uint ComputeEdgeLength(VertexId tail, VertexId head, |
| | | 61 | | IEnumerable<(double longitude, double latitude, float? e)>? shape = null) |
| | 43 | 62 | | { |
| | 43 | 63 | | if (!_network.TryGetVertex(tail, out var lon1, out var lat1, out var e1)) |
| | 0 | 64 | | { |
| | 0 | 65 | | throw new ArgumentOutOfRangeException(nameof(tail), $"Vertex {tail} not found."); |
| | | 66 | | } |
| | | 67 | | |
| | 43 | 68 | | if (!_network.TryGetVertex(head, out var lon2, out var lat2, out var e2)) |
| | 0 | 69 | | { |
| | 0 | 70 | | throw new ArgumentOutOfRangeException(nameof(head), $"Vertex {head} not found."); |
| | | 71 | | } |
| | | 72 | | |
| | 43 | 73 | | return (uint)((lon1, lat1, e1).DistanceEstimateInMeterShape( |
| | 43 | 74 | | (lon2, lat2, e2), shape) * 100); |
| | 43 | 75 | | } |
| | | 76 | | |
| | | 77 | | /// <summary> |
| | | 78 | | /// Adds a new edge. |
| | | 79 | | /// </summary> |
| | | 80 | | /// <param name="tail">The tail vertex.</param> |
| | | 81 | | /// <param name="head">The head vertex.</param> |
| | | 82 | | /// <param name="shape">The shape, if any.</param> |
| | | 83 | | /// <param name="attributes">The attributes, if any.</param> |
| | | 84 | | /// <param name="edgeTypeId">The edge type id, if any.</param> |
| | | 85 | | /// <param name="length">The length in centimeters. Use <see cref="ComputeEdgeLength"/> if not known.</param> |
| | | 86 | | /// <param name="globalEdgeId">The global edge id, if any.</param> |
| | | 87 | | /// <returns></returns> |
| | | 88 | | /// <exception cref="ArgumentException"></exception> |
| | | 89 | | /// <exception cref="ArgumentOutOfRangeException"></exception> |
| | | 90 | | public EdgeId AddEdge(VertexId tail, VertexId head, |
| | | 91 | | IEnumerable<(double longitude, double latitude, float? e)>? shape, |
| | | 92 | | IEnumerable<(string key, string value)>? attributes, uint? edgeTypeId, |
| | | 93 | | uint length, GlobalEdgeId? globalEdgeId = null) |
| | 43 | 94 | | { |
| | | 95 | | // get the tile (or create it). |
| | 43 | 96 | | var (tile, edgeTypeMap) = _network.GetTileForWrite(tail.TileId); |
| | 43 | 97 | | if (tile == null) throw new ArgumentException($"Cannot add edge with a vertex that doesn't exist."); |
| | | 98 | | |
| | | 99 | | // get the edge type id. |
| | 43 | 100 | | edgeTypeId ??= attributes != null ? edgeTypeMap(attributes) : null; |
| | | 101 | | |
| | 43 | 102 | | var edge1 = tile.AddEdge(tail, head, shape, attributes, null, edgeTypeId, length, globalEdgeId); |
| | 43 | 103 | | if (tail.TileId == head.TileId) |
| | 40 | 104 | | { |
| | 40 | 105 | | return edge1; |
| | | 106 | | } |
| | | 107 | | |
| | | 108 | | // this edge crosses tiles, also add an extra edge to the other tile. |
| | 3 | 109 | | (tile, _) = _network.GetTileForWrite(head.TileId); |
| | 3 | 110 | | tile.AddEdge(tail, head, shape, attributes, edge1, edgeTypeId, length, globalEdgeId); |
| | | 111 | | |
| | 3 | 112 | | return edge1; |
| | 43 | 113 | | } |
| | | 114 | | |
| | | 115 | | public void AddTurnCosts(VertexId vertex, IEnumerable<(string key, string value)> attributes, |
| | | 116 | | EdgeId[] edges, uint[,] costs, IEnumerable<EdgeId>? prefix, uint? turnCostType = null) |
| | 0 | 117 | | { |
| | 0 | 118 | | prefix ??= ArraySegment<EdgeId>.Empty; |
| | | 119 | | |
| | | 120 | | // get the tile (or create it). |
| | 0 | 121 | | var (tile, _) = _network.GetTileForWrite(vertex.TileId); |
| | 0 | 122 | | if (tile == null) |
| | 0 | 123 | | { |
| | 0 | 124 | | throw new ArgumentException($"Cannot add turn costs to a vertex that doesn't exist."); |
| | | 125 | | } |
| | | 126 | | |
| | | 127 | | // get the turn cost type id. |
| | 0 | 128 | | var turnCostMap = _network.RouterDb.GetTurnCostTypeMap(); |
| | 0 | 129 | | turnCostType ??= turnCostMap.func(attributes); |
| | | 130 | | |
| | | 131 | | // add the turn cost table using the type id. |
| | 0 | 132 | | tile.AddTurnCosts(vertex, turnCostType.Value, edges, costs, attributes, prefix); |
| | | 133 | | |
| | | 134 | | // for cross-tile edges, the order was set on this tile's copy. |
| | | 135 | | // sync the order to the other tile's copy so routing from either side sees it. |
| | 0 | 136 | | var enumerator = new NetworkTileEnumerator(); |
| | 0 | 137 | | enumerator.MoveTo(tile); |
| | 0 | 138 | | if (enumerator.MoveTo(vertex)) |
| | 0 | 139 | | { |
| | 0 | 140 | | while (enumerator.MoveNext()) |
| | 0 | 141 | | { |
| | | 142 | | // only cross-tile edges need syncing. |
| | 0 | 143 | | if (enumerator.Tail.TileId == enumerator.Head.TileId) continue; |
| | | 144 | | |
| | | 145 | | // Head is always the other vertex (Tail = turn cost vertex we enumerated from). |
| | 0 | 146 | | var (otherTile, _) = _network.GetTileForWrite(enumerator.Head.TileId); |
| | 0 | 147 | | if (otherTile == null) continue; |
| | | 148 | | |
| | | 149 | | // find the same edge in the other tile by iterating from the other vertex. |
| | 0 | 150 | | var otherEnumerator = new NetworkTileEnumerator(); |
| | 0 | 151 | | otherEnumerator.MoveTo(otherTile); |
| | 0 | 152 | | if (!otherEnumerator.MoveTo(enumerator.Head)) continue; |
| | | 153 | | |
| | 0 | 154 | | while (otherEnumerator.MoveNext()) |
| | 0 | 155 | | { |
| | 0 | 156 | | if (otherEnumerator.EdgeId != enumerator.EdgeId) continue; |
| | | 157 | | |
| | | 158 | | // found the same edge — copy the order bytes. |
| | | 159 | | // SetTailHeadOrder takes STORED tail/head orders (for vertex1/vertex2 as encoded). |
| | | 160 | | // enumerator.TailOrder = order at turn cost vertex, HeadOrder = order at other vertex. |
| | | 161 | | // Map these to stored positions based on otherEnumerator.Forward: |
| | | 162 | | // Forward=true: vertex1=otherVertex → stored tail=HeadOrder, stored head=TailOrder |
| | | 163 | | // Forward=false: vertex1=turnCostVertex → stored tail=TailOrder, stored head=HeadOrder |
| | 0 | 164 | | otherTile.SetTailHeadOrder(otherEnumerator.EdgePointer, |
| | 0 | 165 | | otherEnumerator.Forward ? enumerator.HeadOrder : enumerator.TailOrder, |
| | 0 | 166 | | otherEnumerator.Forward ? enumerator.TailOrder : enumerator.HeadOrder); |
| | 0 | 167 | | break; |
| | | 168 | | } |
| | 0 | 169 | | } |
| | 0 | 170 | | } |
| | 0 | 171 | | } |
| | | 172 | | |
| | | 173 | | internal void AddTile(NetworkTile tile) |
| | 0 | 174 | | { |
| | 0 | 175 | | _network.SetTile(tile); |
| | 0 | 176 | | } |
| | | 177 | | |
| | | 178 | | internal bool HasTile(uint localTileId) |
| | 0 | 179 | | { |
| | 0 | 180 | | return _network.HasTile(localTileId); |
| | 0 | 181 | | } |
| | | 182 | | |
| | | 183 | | /// <inheritdoc/> |
| | | 184 | | public void Dispose() |
| | 41 | 185 | | { |
| | 41 | 186 | | _network.ClearWriter(); |
| | 41 | 187 | | } |
| | | 188 | | } |