/// <summary> /// Removes all islands that are not roots and updates all edges. /// </summary> /// <param name="islandLabels">The current labels.</param> internal long Reduce(IslandLabels islandLabels) { // remove vertices that aren't originals. var neighbours = new HashSet <uint>(); var edgeEnumerator = _graph.GetEdgeEnumerator(); var edgeEnumerator2 = _graph.GetEdgeEnumerator(); var nonNullLabels = 0; for (uint label = 0; label < _graph.VertexCount; label++) { var minimal = islandLabels[label]; if (!edgeEnumerator.MoveTo(label)) { _graph.RemoveEdges(label); continue; } neighbours.Clear(); while (edgeEnumerator.MoveNext()) { var n = edgeEnumerator.Neighbour; n = islandLabels[n]; if (n == IslandLabels.NoAccess || n == IslandLabels.NotSet || n == minimal) { continue; } // remove dead-ends. if (!edgeEnumerator2.MoveTo(n)) { // this edge has no targets. continue; } neighbours.Add(n); } _graph.RemoveEdges(label); if (neighbours.Count == 0) { continue; } nonNullLabels++; foreach (var n in neighbours) { _graph.RemoveEdge(minimal, n); _graph.AddEdge(minimal, n); } } _graph.Compress(); return(nonNullLabels); }
private IslandLabels _islandLabels; // island id's per edge id. /// <summary> /// Runs the island detection. /// </summary> protected override void DoRun(CancellationToken cancellationToken) { _islandLabels = new IslandLabels(); // do a run over each vertex and connect islands with bidirectional edges where possible. uint currentVertex = 0; var edgeEnumerator1 = _network.GetEdgeEnumerator(); var edgeEnumerator2 = _network.GetEdgeEnumerator(); var bestLabels = new Dictionary <uint, uint>(); while (currentVertex < _network.VertexCount) { if (!edgeEnumerator1.MoveTo(currentVertex)) { currentVertex++; continue; } // update restrictions. _restrictions?.Update(currentVertex); // log all connections. bestLabels.Clear(); var incomingOneWayEdge = -1L; uint incomingVertex = 0; var edges = 0; while (edgeEnumerator1.MoveNext()) { var edge1Factor = _profile(edgeEnumerator1.Data.Profile); if (edge1Factor.Value == 0) { // no access, don't evaluate neighbours. _islandLabels[edgeEnumerator1.Id] = IslandLabels.NoAccess; continue; } edges++; if (edge1Factor.Direction != 0) { if (incomingOneWayEdge != Constants.NO_EDGE) { // there was no bidirectional or second edge detected. if ((edgeEnumerator1.DataInverted && edge1Factor.Direction == 1) || (!edgeEnumerator1.DataInverted && edge1Factor.Direction == 2)) { if (incomingOneWayEdge < 0) { // keep edge. incomingOneWayEdge = edgeEnumerator1.Id; incomingVertex = edgeEnumerator1.To; } else { // oeps, a second incoming edge, don't keep. incomingOneWayEdge = Constants.NO_EDGE; } } } // only consider bidirectional edges in this first step. continue; } incomingOneWayEdge = Constants.NO_EDGE; // a bidirectional edge, not a candidate for one-way label propagation. // get label or provisionally set label to own id. if (!bestLabels.TryGetValue(edgeEnumerator1.Id, out var edge1Label)) { // label wasn't set yet locally, check globally. edge1Label = _islandLabels[edgeEnumerator1.Id]; if (edge1Label == IslandLabels.NotSet) { // provisionally set label to own id. edge1Label = edgeEnumerator1.Id; } bestLabels[edgeEnumerator1.Id] = edge1Label; } var turn = new Turn(new OriginalEdge(edgeEnumerator1.To, currentVertex), Constants.NO_VERTEX); // evaluate neighbours. edgeEnumerator2.MoveTo(currentVertex); while (edgeEnumerator2.MoveNext()) { if (edgeEnumerator1.Id == edgeEnumerator2.Id) { // don't evaluate u-turns. // TODO: what about loops? continue; } var edge2Factor = _profile(edgeEnumerator2.Data.Profile); if (edge2Factor.Value == 0) { // should be marked as no access in parent loop. continue; } if (edge2Factor.Direction != 0) { // only consider bidirectional edges in this first step. continue; } // check restrictions if needed. if (_restrictions != null) { turn.Vertex3 = edgeEnumerator2.To; if (turn.IsRestrictedBy(_restrictions)) { // turn is restricted. continue; } } // get label or provisionally set label to own id. if (!bestLabels.TryGetValue(edgeEnumerator2.Id, out var edge2Label)) { // label wasn't set locally, check globally. edge2Label = _islandLabels[edgeEnumerator2.Id]; if (edge2Label == IslandLabels.NotSet) { // provisionally set label to own id. edge2Label = edgeEnumerator2.Id; } bestLabels[edgeEnumerator2.Id] = edge2Label; } // bidirectional edge, choose best label. if (edge1Label < edge2Label) { bestLabels[edgeEnumerator2.Id] = edge1Label; } else { bestLabels[edgeEnumerator1.Id] = edge2Label; } } } if (incomingOneWayEdge != Constants.NO_EDGE && incomingOneWayEdge >= 0 && edges == 2) { // link sequences of oneways together. var edge1Id = (uint)incomingOneWayEdge; // we can also update neighbours if there is only one incoming edge. // get label or provisionally set label to own id. if (!bestLabels.TryGetValue(edge1Id, out var edge1Label)) { // label wasn't set yet locally, check globally. edge1Label = _islandLabels[edge1Id]; if (edge1Label == IslandLabels.NotSet) { // provisionally set label to own id. edge1Label = edge1Id; } bestLabels[edge1Id] = edge1Label; } var turn = new Turn(new OriginalEdge(incomingVertex, currentVertex), Constants.NO_VERTEX); // evaluate neighbours. edgeEnumerator2.MoveTo(currentVertex); while (edgeEnumerator2.MoveNext()) { if (edge1Id == edgeEnumerator2.Id) { // don't evaluate u-turns. // TODO: what about loops? continue; } var edge2Factor = _profile(edgeEnumerator2.Data.Profile); if (edge2Factor.Value == 0) { // should be marked as no access in parent loop. continue; } if ((edgeEnumerator2.DataInverted && edge2Factor.Direction == 1) || (!edgeEnumerator2.DataInverted && edge2Factor.Direction == 2)) { // REMARK: no need to check for bidirectionals, already done above. // only consider outgoing oneway edges. continue; } // check restrictions if needed. if (_restrictions != null) { turn.Vertex3 = edgeEnumerator2.To; if (turn.IsRestrictedBy(_restrictions)) { // turn is restricted. continue; } } // get label or provisionally set label to own id. if (!bestLabels.TryGetValue(edgeEnumerator2.Id, out var edge2Label)) { // label wasn't set locally, check globally. edge2Label = _islandLabels[edgeEnumerator2.Id]; if (edge2Label == IslandLabels.NotSet) { // provisionally set label to own id. edge2Label = edgeEnumerator2.Id; } bestLabels[edgeEnumerator2.Id] = edge2Label; } // choose best label. if (edge1Label < edge2Label) { bestLabels[edgeEnumerator2.Id] = edge1Label; } else { bestLabels[edge1Id] = edge2Label; } } } // update labels based on best labels. var labelsToUpdate = new HashSet <uint>(); foreach (var pair in bestLabels) { var edgeId = pair.Key; // the edge key. var label = pair.Value; var existing = _islandLabels[edgeId]; if (existing != IslandLabels.NotSet && existing != label && edgeId != existing) { // also make sure to update the existing label. _islandLabels[existing] = label; labelsToUpdate.Add(existing); } _islandLabels[edgeId] = label; labelsToUpdate.Add(edgeId); } foreach (var label in labelsToUpdate) { _islandLabels.UpdateLowest(label); } currentVertex++; } // update all labels to lowest possible. for (uint e = 0; e < _islandLabels.Count; e++) { _islandLabels.UpdateLowest(e); } // NOW ALL LABELLED EDGES ARE AT THEIR BEST. // do the one-way labels. var islandLabelGraph = new IslandLabelGraph(); currentVertex = 0; while (currentVertex < _network.VertexCount) { if (!edgeEnumerator1.MoveTo(currentVertex)) { currentVertex++; continue; } // update restrictions. _restrictions?.Update(currentVertex); // log all connections. while (edgeEnumerator1.MoveNext()) { var edge1Factor = _profile(edgeEnumerator1.Data.Profile); if (edge1Factor.Value == 0) { // no access, don't evaluate neighbours. _islandLabels[edgeEnumerator1.Id] = IslandLabels.NoAccess; continue; } if ((edgeEnumerator1.DataInverted && edge1Factor.Direction == 2) || !edgeEnumerator1.DataInverted && edge1Factor.Direction == 1) { // wrong direction, this edge is oneway away from current vertex. continue; } var turn = new Turn(new OriginalEdge(edgeEnumerator1.To, currentVertex), Constants.NO_VERTEX); // get label or set to own id. var edge1Label = _islandLabels[edgeEnumerator1.Id]; if (edge1Label == IslandLabels.NotSet) { edge1Label = edgeEnumerator1.Id; _islandLabels[edge1Label] = edge1Label; } // evaluate neighbours. edgeEnumerator2.MoveTo(currentVertex); while (edgeEnumerator2.MoveNext()) { if (edgeEnumerator1.Id == edgeEnumerator2.Id) { // don't evaluate u-turns. // TODO: what about loops? continue; } var edge2Factor = _profile(edgeEnumerator2.Data.Profile); if (edge2Factor.Value == 0) { // should be marked as no access in parent loop. continue; } if ((edgeEnumerator2.DataInverted && edge2Factor.Direction == 1) || !edgeEnumerator2.DataInverted && edge2Factor.Direction == 2) { // wrong direction, this edge is oneway away from current vertex. continue; } // check restrictions if needed. if (_restrictions != null) { turn.Vertex3 = edgeEnumerator2.To; if (turn.IsRestrictedBy(_restrictions)) { // turn is restricted. continue; } } // get label or set to own id. var edge2Label = _islandLabels[edgeEnumerator2.Id]; if (edge2Label == IslandLabels.NotSet) { edge2Label = edgeEnumerator2.Id; _islandLabels[edge2Label] = edge2Label; } if (edge1Label != edge2Label) { islandLabelGraph.Connect(edge1Label, edge2Label); } } } currentVertex++; } Itinero.Logging.Logger.Log($"{nameof(EdgeBasedIslandDetector)}.{nameof(Run)}", TraceEventType.Verbose, "Built directional graph."); // calculate all loops with increasing max settle settings until they are unlimited and all loops are removed. uint maxSettles = 1; Itinero.Logging.Logger.Log($"{nameof(EdgeBasedIslandDetector)}.{nameof(Run)}", TraceEventType.Verbose, $"Label graph has {islandLabelGraph.LabelCount} labels."); while (true) { Itinero.Logging.Logger.Log($"{nameof(EdgeBasedIslandDetector)}.{nameof(Run)}", TraceEventType.Verbose, $"Running loop detection with {maxSettles}."); var lastRun = maxSettles > islandLabelGraph.LabelCount; // find loops. islandLabelGraph.FindLoops(maxSettles, _islandLabels, (oldLabel, newLabel) => { _islandLabels[oldLabel] = newLabel; }); // update all labels to lowest possible. for (uint e = 0; e < _islandLabels.Count; e++) { _islandLabels.UpdateLowest(e); } if (lastRun) { break; } // reduce graph. var labelCount = islandLabelGraph.Reduce(_islandLabels); Itinero.Logging.Logger.Log($"{nameof(EdgeBasedIslandDetector)}.{nameof(Run)}", TraceEventType.Verbose, $"Label graph now has {labelCount} non-empty labels."); maxSettles = (uint)_network.EdgeCount; } this.HasSucceeded = true; }
/// <summary> /// Finds loops and merges them together. /// </summary> /// <param name="maxSettles">The maximum labels to settle.</param> /// <param name="updateLabel">A callback to update label.</param> internal void FindLoops(uint maxSettles, IslandLabels islandLabels, Action <uint, uint> updateLabel) { // TODO: it's probably better to call reduce here when too much has changed. var pathTree = new PathTree(); var enumerator = _graph.GetEdgeEnumerator(); var settled = new HashSet <uint>(); var queue = new Queue <uint>(); var loop = new HashSet <uint>(); // keeps all with a path back to label, initially only label. uint label = 0; while (label < _graph.VertexCount) { if (!enumerator.MoveTo(label)) { label++; continue; } if (islandLabels[label] != label) { label++; continue; } queue.Clear(); pathTree.Clear(); settled.Clear(); loop.Add(label); queue.Enqueue(pathTree.Add(label, uint.MaxValue)); while (queue.Count > 0 && settled.Count < maxSettles) { var pointer = queue.Dequeue(); pathTree.Get(pointer, out var current, out var previous); if (settled.Contains(current)) { continue; } settled.Add(current); if (!enumerator.MoveTo(current)) { continue; } while (enumerator.MoveNext()) { var n = enumerator.Neighbour; n = islandLabels[n]; if (loop.Contains(n)) { // yay, a loop! loop.Add(current); while (previous != uint.MaxValue) { pathTree.Get(previous, out current, out previous); loop.Add(current); } } if (settled.Contains(n)) { continue; } queue.Enqueue(pathTree.Add(n, pointer)); } } if (loop.Count > 0) { this.Merge(loop, updateLabel); } loop.Clear(); // move to the next label. label++; } }