| | | 1 | | using System; |
| | | 2 | | using System.Collections.Generic; |
| | | 3 | | using System.Linq; |
| | | 4 | | using Itinero.Geo.Elevation; |
| | | 5 | | using Itinero.IO.Osm.Restrictions.Barriers; |
| | | 6 | | using Itinero.IO.Osm.Restrictions.Turns; |
| | | 7 | | using Itinero.IO.Osm.Tiles; |
| | | 8 | | using Itinero.Network; |
| | | 9 | | using Itinero.Network.Mutation; |
| | | 10 | | using Itinero.Network.Tiles.Standalone.Global; |
| | | 11 | | using OsmSharp; |
| | | 12 | | using OsmSharp.Streams; |
| | | 13 | | |
| | | 14 | | namespace Itinero.IO.Osm; |
| | | 15 | | |
| | | 16 | | /// <inheritdoc /> |
| | | 17 | | public class RouterDbStreamTarget : OsmStreamTarget |
| | | 18 | | { |
| | 41 | 19 | | private readonly Dictionary<long, VertexId> _vertices = new(); |
| | | 20 | | private readonly RoutingNetworkMutator _mutableRouterDb; |
| | | 21 | | private readonly IElevationHandler? _elevationHandler; |
| | 41 | 22 | | private readonly OsmTurnRestrictionParser _restrictionParser = new(); |
| | 41 | 23 | | private readonly Dictionary<long, Way?> _restrictionMembers = new(); |
| | 41 | 24 | | private readonly OsmBarrierParser _barrierParser = new(); |
| | 41 | 25 | | private readonly Dictionary<long, List<Way>> _barrierNodes = new(); |
| | 41 | 26 | | private readonly Dictionary<long, Node> _barrierNodeObjects = new(); |
| | 41 | 27 | | private readonly Dictionary<long, (double longitude, double latitude)> _nodeLocations = new(); |
| | 41 | 28 | | private readonly HashSet<long> _usedNodes = new(); |
| | 41 | 29 | | private readonly Dictionary<GlobalEdgeId, EdgeId> _globalEdgeIds = new(); |
| | | 30 | | |
| | | 31 | | /// <inheritdoc /> |
| | 41 | 32 | | public RouterDbStreamTarget(RoutingNetworkMutator mutableRouterDb, |
| | 41 | 33 | | IElevationHandler? elevationHandler = null) |
| | 41 | 34 | | { |
| | 41 | 35 | | _mutableRouterDb = mutableRouterDb; |
| | 41 | 36 | | _elevationHandler = elevationHandler; |
| | 41 | 37 | | } |
| | | 38 | | |
| | 41 | 39 | | private bool _firstPass = true; |
| | | 40 | | |
| | | 41 | | public override void Initialize() |
| | 82 | 42 | | { |
| | 82 | 43 | | _firstPass = true; |
| | 82 | 44 | | } |
| | | 45 | | |
| | | 46 | | public override bool OnBeforePull() |
| | 41 | 47 | | { |
| | | 48 | | // execute the first pass (skip nodes, process ways and relations). |
| | 41 | 49 | | this.DoPull(true, false, false); |
| | | 50 | | |
| | | 51 | | // move to second pass. |
| | 41 | 52 | | _firstPass = false; |
| | 41 | 53 | | this.Source.Reset(); |
| | 41 | 54 | | this.DoPull(); |
| | | 55 | | |
| | | 56 | | // add barriers as turn costs after all edges exist. |
| | 4879 | 57 | | foreach (var (nodeId, ways) in _barrierNodes) |
| | 2378 | 58 | | { |
| | 2378 | 59 | | if (!_barrierNodeObjects.TryGetValue(nodeId, out var node)) continue; |
| | 2378 | 60 | | if (!_barrierParser.TryParse(node, ways, out var barrier)) continue; |
| | | 61 | | |
| | 2378 | 62 | | this.ResolveAndAddTurnCosts(barrier.ToGlobalNetworkRestrictions()); |
| | 2378 | 63 | | } |
| | | 64 | | |
| | 41 | 65 | | return false; |
| | 41 | 66 | | } |
| | | 67 | | |
| | | 68 | | /// <inheritdoc /> |
| | | 69 | | public override void AddNode(Node node) |
| | 535049 | 70 | | { |
| | 535049 | 71 | | if (!node.Id.HasValue) return; |
| | 535049 | 72 | | if (!node.Longitude.HasValue || !node.Latitude.HasValue) return; |
| | | 73 | | |
| | | 74 | | // first pass skips nodes (DoPull(true, false, false)), so this is always the second pass. |
| | | 75 | | |
| | | 76 | | // keep node locations. |
| | 535049 | 77 | | _nodeLocations[node.Id.Value] = (node.Longitude.Value, node.Latitude.Value); |
| | | 78 | | |
| | | 79 | | // detect barriers — nodes come before ways in the stream, so when AddWay |
| | | 80 | | // runs it can check _barrierNodes to track which ways pass through barriers. |
| | 535049 | 81 | | if (_barrierParser.IsBarrier(node)) |
| | 2378 | 82 | | { |
| | 2378 | 83 | | _vertices[node.Id.Value] = VertexId.Empty; |
| | 2378 | 84 | | _barrierNodes[node.Id.Value] = []; |
| | 2378 | 85 | | _barrierNodeObjects[node.Id.Value] = node; |
| | 2378 | 86 | | } |
| | 535049 | 87 | | } |
| | | 88 | | |
| | | 89 | | /// <inheritdoc /> |
| | | 90 | | public override void AddWay(Way way) |
| | 56868 | 91 | | { |
| | 56868 | 92 | | if (way.Nodes == null || way.Nodes.Length == 0 || !way.Id.HasValue) return; |
| | | 93 | | |
| | 56868 | 94 | | if (_firstPass) |
| | 28434 | 95 | | { |
| | | 96 | | // FIRST PASS: keep track of nodes that are used as routing nodes. |
| | 28434 | 97 | | _vertices[way.Nodes[0]] = VertexId.Empty; |
| | 360206 | 98 | | for (var i = 0; i < way.Nodes.Length; i++) |
| | 151669 | 99 | | { |
| | 151669 | 100 | | var node = way.Nodes[i]; |
| | | 101 | | |
| | 151669 | 102 | | if (_usedNodes.Contains(node)) |
| | 41674 | 103 | | { |
| | 41674 | 104 | | _vertices[node] = VertexId.Empty; |
| | 41674 | 105 | | continue; |
| | | 106 | | } |
| | | 107 | | |
| | 109995 | 108 | | _usedNodes.Add(node); |
| | 109995 | 109 | | } |
| | | 110 | | |
| | 28434 | 111 | | _vertices[way.Nodes[^1]] = VertexId.Empty; |
| | 28434 | 112 | | return; |
| | | 113 | | } |
| | | 114 | | |
| | | 115 | | // SECOND PASS: track barrier ways, add edges, register GlobalEdgeIds. |
| | | 116 | | |
| | | 117 | | // track ways for barrier nodes and mark for edge registration. |
| | 360206 | 118 | | for (var i = 0; i < way.Nodes.Length; i++) |
| | 151669 | 119 | | { |
| | 151669 | 120 | | if (_barrierNodes.TryGetValue(way.Nodes[i], out var barrierWays)) |
| | 3557 | 121 | | { |
| | 3557 | 122 | | barrierWays.Add(way); |
| | 3557 | 123 | | _restrictionMembers.TryAdd(way.Id!.Value, null); |
| | | 124 | | // barrier nodes must be vertices for turn costs, |
| | | 125 | | // but don't overwrite if already created by a previous way. |
| | 3557 | 126 | | _vertices.TryAdd(way.Nodes[i], VertexId.Empty); |
| | 3557 | 127 | | } |
| | 151669 | 128 | | } |
| | | 129 | | |
| | | 130 | | // if way is a member of restriction, queue for later. |
| | 28434 | 131 | | var saveEdge = _restrictionMembers.ContainsKey(way.Id.Value); |
| | 31277 | 132 | | if (saveEdge) _restrictionMembers[way.Id.Value] = way; |
| | | 133 | | |
| | | 134 | | // process the way into edges. |
| | 28434 | 135 | | var vertex1 = VertexId.Empty; |
| | 28434 | 136 | | var vertex1Idx = -1; |
| | 28434 | 137 | | var shape = new List<(double longitude, double latitude, float? e)>(); |
| | 345802 | 138 | | for (var n = 0; n < way.Nodes.Length; n++) |
| | 145341 | 139 | | { |
| | 145341 | 140 | | var node = way.Nodes[n]; |
| | 145341 | 141 | | if (!_nodeLocations.TryGetValue(node, out var location)) |
| | 874 | 142 | | { |
| | | 143 | | // an incomplete way, node not in source. |
| | 874 | 144 | | break; |
| | | 145 | | } |
| | | 146 | | |
| | 144467 | 147 | | if (!_vertices.TryGetValue(node, out var vertex2)) |
| | 67180 | 148 | | { |
| | | 149 | | // node is shape. |
| | 67180 | 150 | | var coordinate = location.AddElevation( |
| | 67180 | 151 | | elevationHandler: _elevationHandler); |
| | 67180 | 152 | | shape.Add(coordinate); |
| | 67180 | 153 | | continue; |
| | | 154 | | } |
| | | 155 | | |
| | 77287 | 156 | | if (vertex2.IsEmpty()) |
| | 36435 | 157 | | { |
| | | 158 | | // node is core and not present yet. |
| | 36435 | 159 | | var coordinate = location.AddElevation( |
| | 36435 | 160 | | elevationHandler: _elevationHandler); |
| | 36435 | 161 | | vertex2 = _mutableRouterDb.AddVertex(coordinate); |
| | 36435 | 162 | | _vertices[node] = vertex2; |
| | 36435 | 163 | | } |
| | | 164 | | |
| | 77287 | 165 | | if (vertex1.IsEmpty()) |
| | 27950 | 166 | | { |
| | 27950 | 167 | | vertex1 = vertex2; |
| | 27950 | 168 | | vertex1Idx = n; |
| | 27950 | 169 | | continue; |
| | | 170 | | } |
| | | 171 | | |
| | | 172 | | // add edge. |
| | 500962 | 173 | | var filteredTags = way.Tags?.Select(x => (x.Key, x.Value)); |
| | 49337 | 174 | | var edgeId = _mutableRouterDb.AddEdge(vertex1, vertex2, |
| | 49337 | 175 | | shape, |
| | 49337 | 176 | | filteredTags); |
| | | 177 | | |
| | | 178 | | // register GlobalEdgeId → EdgeId for restriction resolution. |
| | 49337 | 179 | | if (saveEdge) |
| | 5965 | 180 | | { |
| | 5965 | 181 | | var globalEdgeId = way.CreateGlobalEdgeId(vertex1Idx, n); |
| | 5965 | 182 | | _globalEdgeIds[globalEdgeId] = edgeId; |
| | 5965 | 183 | | } |
| | | 184 | | |
| | | 185 | | // move to next part. |
| | 49337 | 186 | | vertex1 = vertex2; |
| | 49337 | 187 | | vertex1Idx = n; |
| | 49337 | 188 | | shape.Clear(); |
| | 49337 | 189 | | } |
| | 56868 | 190 | | } |
| | | 191 | | |
| | | 192 | | /// <inheritdoc /> |
| | | 193 | | public override void AddRelation(Relation relation) |
| | 5136 | 194 | | { |
| | 5136 | 195 | | if (relation.Members == null || relation.Members.Length == 0) return; |
| | 5136 | 196 | | if (_firstPass) |
| | 2568 | 197 | | { |
| | 5062 | 198 | | if (!_restrictionParser.IsRestriction(relation, out _)) return; |
| | | 199 | | |
| | | 200 | | // log member ways. |
| | 554 | 201 | | foreach (var relationMember in relation.Members) |
| | 166 | 202 | | { |
| | 226 | 203 | | if (relationMember.Type != OsmGeoType.Way) continue; |
| | | 204 | | |
| | 106 | 205 | | _restrictionMembers[relationMember.Id] = null; |
| | 106 | 206 | | } |
| | 74 | 207 | | return; |
| | | 208 | | } |
| | | 209 | | |
| | | 210 | | // try to parse restriction. |
| | 2568 | 211 | | var result = _restrictionParser.TryParse(relation, |
| | 106 | 212 | | k => !_restrictionMembers.TryGetValue(k, out var osmGeo) ? null : osmGeo, |
| | 2568 | 213 | | out var restriction); |
| | | 214 | | |
| | 2582 | 215 | | if (result.IsError) return; |
| | 5048 | 216 | | if (!result.Value) return; |
| | 60 | 217 | | if (restriction == null) |
| | 0 | 218 | | throw new Exception("restriction parsing was successful but restriction is null"); |
| | | 219 | | |
| | 60 | 220 | | this.ResolveAndAddTurnCosts(restriction.ToGlobalNetworkRestrictions()); |
| | 5136 | 221 | | } |
| | | 222 | | |
| | | 223 | | private void ResolveAndAddTurnCosts(IEnumerable<GlobalRestriction> globalRestrictions) |
| | 2438 | 224 | | { |
| | 2438 | 225 | | var enumerator = _mutableRouterDb.GetEdgeEnumerator(); |
| | 16818 | 226 | | foreach (var globalRestriction in globalRestrictions) |
| | 4752 | 227 | | { |
| | 4752 | 228 | | if (!globalRestriction.TryBuildNetworkRestriction(GetEdge, out var networkRestriction)) |
| | 3720 | 229 | | continue; |
| | | 230 | | |
| | 1032 | 231 | | if (networkRestriction!.Count < 2) continue; |
| | | 232 | | |
| | | 233 | | // get last edge and turn cost vertex. |
| | 1032 | 234 | | var last = networkRestriction[^1]; |
| | 1032 | 235 | | if (!enumerator.MoveTo(last.edge, last.forward)) continue; |
| | 1032 | 236 | | var turnCostVertex = enumerator.Tail; |
| | | 237 | | |
| | 1032 | 238 | | var secondToLast = networkRestriction[^2]; |
| | 1032 | 239 | | if (networkRestriction.IsProhibitory) |
| | 1002 | 240 | | { |
| | 1002 | 241 | | var costs = new uint[,] { { 0, 1 }, { 0, 0 } }; |
| | 1002 | 242 | | _mutableRouterDb.AddTurnCosts(turnCostVertex, networkRestriction.Attributes, |
| | 1002 | 243 | | new[] { secondToLast.edge, last.edge }, costs, |
| | 1002 | 244 | | networkRestriction.Take(networkRestriction.Count - 2).Select(x => x.edge)); |
| | 1002 | 245 | | } |
| | | 246 | | else |
| | 30 | 247 | | { |
| | 30 | 248 | | if (!enumerator.MoveTo(secondToLast.edge, secondToLast.forward)) continue; |
| | 30 | 249 | | var to = enumerator.Head; |
| | 30 | 250 | | enumerator.MoveTo(to); |
| | | 251 | | |
| | 107 | 252 | | while (enumerator.MoveNext()) |
| | 77 | 253 | | { |
| | 77 | 254 | | if (enumerator.EdgeId == secondToLast.edge || |
| | 137 | 255 | | enumerator.EdgeId == last.edge) continue; |
| | | 256 | | |
| | 17 | 257 | | var costs = new uint[,] { { 0, 1 }, { 0, 0 } }; |
| | 17 | 258 | | _mutableRouterDb.AddTurnCosts(turnCostVertex, networkRestriction.Attributes, |
| | 17 | 259 | | new[] { secondToLast.edge, enumerator.EdgeId }, costs, |
| | 17 | 260 | | networkRestriction.Take(networkRestriction.Count - 2).Select(x => x.edge)); |
| | 17 | 261 | | } |
| | 30 | 262 | | } |
| | 1032 | 263 | | } |
| | | 264 | | |
| | 2438 | 265 | | return; |
| | | 266 | | |
| | | 267 | | (EdgeId edge, bool forward)? GetEdge(GlobalEdgeId geid) |
| | 7196 | 268 | | { |
| | 7196 | 269 | | if (_globalEdgeIds.TryGetValue(geid, out var edgeId)) |
| | 1766 | 270 | | return (edgeId, true); |
| | 5430 | 271 | | if (_globalEdgeIds.TryGetValue(geid.GetInverted(), out edgeId)) |
| | 1710 | 272 | | return (edgeId, false); |
| | 3720 | 273 | | return null; |
| | 7196 | 274 | | } |
| | 2438 | 275 | | } |
| | | 276 | | } |