| | | 1 | | using System; |
| | | 2 | | using System.Collections.Generic; |
| | | 3 | | using System.Diagnostics.CodeAnalysis; |
| | | 4 | | |
| | | 5 | | namespace Itinero.Network.Tiles.Standalone.Global; |
| | | 6 | | |
| | | 7 | | public static class GlobalRestrictionExtensions |
| | | 8 | | { |
| | | 9 | | /// <summary> |
| | | 10 | | /// Tries to build a network restriction from a global restriction. |
| | | 11 | | /// </summary> |
| | | 12 | | /// <remarks> |
| | | 13 | | /// Should always succeed if all edges are available but can fail if the data |
| | | 14 | | /// available is a tile and does not contain all edges yet. |
| | | 15 | | /// </remarks> |
| | | 16 | | /// <param name="globalNetworkRestriction">The global network restriction.</param> |
| | | 17 | | /// <param name="getEdge"> |
| | | 18 | | /// Resolves a chain edge. The bool argument is <c>true</c> for the first edge |
| | | 19 | | /// in the chain (anchor with the next edge is at the geid's <see cref="GlobalEdgeId.Head"/>), |
| | | 20 | | /// <c>false</c> for any subsequent edge (anchor with the previous edge is at |
| | | 21 | | /// the geid's <see cref="GlobalEdgeId.Tail"/>). The chain-connectivity invariant |
| | | 22 | | /// emitted by the OSM converters is <c>previous.Head == current.Tail</c> at |
| | | 23 | | /// the same OSM node, so the anchor end is unambiguous from chain position. |
| | | 24 | | /// </param> |
| | | 25 | | /// <param name="networkRestriction">The resulting network restriction, if any.</param> |
| | | 26 | | /// <returns>True if success, false otherwise.</returns> |
| | | 27 | | public static bool TryBuildNetworkRestriction(this GlobalRestriction globalNetworkRestriction, |
| | | 28 | | Func<GlobalEdgeId, bool, (EdgeId edge, bool forward)?> getEdge, |
| | | 29 | | [MaybeNullWhen(false)] out NetworkRestriction? networkRestriction) |
| | 4822 | 30 | | { |
| | 4822 | 31 | | networkRestriction = null; |
| | | 32 | | |
| | 4822 | 33 | | var edges = new List<(EdgeId edge, bool forward)>(); |
| | 28842 | 34 | | for (var i = 0; i < globalNetworkRestriction.Count; i++) |
| | 9629 | 35 | | { |
| | 9629 | 36 | | var globalId = globalNetworkRestriction[i]; |
| | 9629 | 37 | | var isFirst = i == 0; |
| | | 38 | | |
| | 9629 | 39 | | var e = getEdge(globalId, isFirst); |
| | 9659 | 40 | | if (e == null) return false; |
| | | 41 | | |
| | 9599 | 42 | | edges.Add(e.Value); |
| | 9599 | 43 | | } |
| | | 44 | | |
| | 4792 | 45 | | networkRestriction = new NetworkRestriction(edges, globalNetworkRestriction.IsProhibitory, |
| | 4792 | 46 | | globalNetworkRestriction.Attributes); |
| | 4792 | 47 | | return true; |
| | 4822 | 48 | | } |
| | | 49 | | |
| | | 50 | | /// <summary> |
| | | 51 | | /// Walks from the chain-anchor end of <paramref name="geid"/> toward the far end, |
| | | 52 | | /// returning the first sub-edge in the index whose endpoint matches the anchor. |
| | | 53 | | /// </summary> |
| | | 54 | | /// <remarks> |
| | | 55 | | /// Given the OSM converters' invariant that <c>previous.Head == current.Tail</c> |
| | | 56 | | /// at the shared via node, the anchor end is determined by chain position: |
| | | 57 | | /// the first edge anchors at <see cref="GlobalEdgeId.Head"/>, every subsequent |
| | | 58 | | /// edge anchors at <see cref="GlobalEdgeId.Tail"/>. The walk steps one sub-index |
| | | 59 | | /// at a time, trying both the canonical and inverted lookups; the returned |
| | | 60 | | /// <c>forward</c> reflects whether the matched stored edge runs in the chain's |
| | | 61 | | /// traversal direction (Tail → Head of <paramref name="geid"/>). |
| | | 62 | | /// </remarks> |
| | | 63 | | public static (EdgeId edge, bool forward)? WalkFromAnchor(GlobalEdgeId geid, bool isFirst, |
| | | 64 | | TryGetEdgeId tryGet) |
| | 4681 | 65 | | { |
| | 4681 | 66 | | var anchor = isFirst ? geid.Head : geid.Tail; |
| | 4681 | 67 | | var farEnd = isFirst ? geid.Tail : geid.Head; |
| | 4681 | 68 | | if (anchor == farEnd) return null; |
| | | 69 | | |
| | | 70 | | // chainGoesAnchorToFar: chain enters the edge at the anchor and exits at far. |
| | | 71 | | // True when anchor == geid.Tail (i.e. not the first edge). |
| | 4681 | 72 | | var chainGoesAnchorToFar = !isFirst; |
| | | 73 | | |
| | 4681 | 74 | | var step = farEnd > anchor ? 1 : -1; |
| | 4681 | 75 | | for (int otherIdx = anchor + step; |
| | 7851 | 76 | | step > 0 ? otherIdx <= farEnd : otherIdx >= farEnd; |
| | 3170 | 77 | | otherIdx += step) |
| | 7807 | 78 | | { |
| | 7807 | 79 | | var sub = GlobalEdgeId.Create(geid.EdgeId, (ushort)anchor, (ushort)otherIdx); |
| | 7807 | 80 | | if (tryGet(sub, out var eId)) |
| | 2311 | 81 | | { |
| | | 82 | | // canonical direction is anchor → otherIdx (toward far). Forward |
| | | 83 | | // matches when chain also goes anchor → far. |
| | 2311 | 84 | | return (eId, chainGoesAnchorToFar); |
| | | 85 | | } |
| | 5496 | 86 | | if (tryGet(sub.GetInverted(), out eId)) |
| | 2326 | 87 | | { |
| | | 88 | | // canonical direction is otherIdx → anchor (toward anchor). |
| | | 89 | | // Forward matches when chain goes far → anchor. |
| | 2326 | 90 | | return (eId, !chainGoesAnchorToFar); |
| | | 91 | | } |
| | 3170 | 92 | | } |
| | | 93 | | |
| | 44 | 94 | | return null; |
| | 4681 | 95 | | } |
| | | 96 | | |
| | | 97 | | /// <summary> |
| | | 98 | | /// Lookup callback for <see cref="WalkFromAnchor"/>: returns whether the |
| | | 99 | | /// given <see cref="GlobalEdgeId"/> is registered and what |
| | | 100 | | /// <see cref="EdgeId"/> it maps to. |
| | | 101 | | /// </summary> |
| | | 102 | | public delegate bool TryGetEdgeId(GlobalEdgeId geid, out EdgeId edgeId); |
| | | 103 | | } |