/// <summary> /// Contracts the given vertex. /// </summary> /// <param name="vertex"></param> public void Contract(uint vertex) { if (this.IsContracted(vertex)) { throw new Exception("Is already contracted!"); } // get all information from the source. var edges = _target.GetEdges(vertex).ToList(); // report the before contraction event. this.OnBeforeContraction(vertex, edges); // build the list of edges to replace. var allNeigbours = new List <Edge <CHEdgeData> >(edges.Count); var tos = new List <uint>(edges.Count); var tosSet = new HashSet <uint>(); foreach (var edge in edges) { // use this edge for contraction. allNeigbours.Add(edge); tos.Add(edge.Neighbour); tosSet.Add(edge.Neighbour); // remove the edge in downwards direction and on the edge with the same data. _target.RemoveEdge(edge.Neighbour, vertex); } //// build the list of pairs and make sure duplicates don't count. //var allNeighbourPairs = new Dictionary<Tuple<uint, uint>, Tuple<float, float>>(); //for(int x = 1; x < allNeigbours.Count; x++) //{ // var xEdge = allNeigbours[x]; // var xEdgeForwardWeight = xEdge.EdgeData.CanMoveBackward ? xEdge.EdgeData.Weight : float.MaxValue; // var xEdgeBackwardWeight = xEdge.EdgeData.CanMoveForward ? xEdge.EdgeData.Weight : float.MaxValue; // for(int y = 0; y < x; y++) // { // var yEdge = allNeigbours[x]; // //var yEdgeForwardWeight = yEdge.EdgeData.CanMoveBackward ? yEdge.EdgeData.Weight : float.MaxValue; // //var yEdgeBackwardWeight = yEdge.EdgeData.CanMoveForward ? yEdge.EdgeData.Weight : float.MaxValue; // float forwardWeight = float.MaxValue; // float backwardWeight = float.MaxValue; // if(xEdge.Neighbour < yEdge.Neighbour) // { // if (xEdge.EdgeData.CanMoveBackward && yEdge.EdgeData.CanMoveForward) // { // forwardWeight = xEdgeBackwardWeight + yEdge.EdgeData.Weight; // } // if (xEdge.EdgeData.CanMoveForward && yEdge.EdgeData.CanMoveBackward) // { // backwardWeight = xEdgeForwardWeight + yEdge.EdgeData.Weight; // } // } // else if(xEdge.Neighbour > yEdge.Neighbour) // { // if (xEdge.EdgeData.CanMoveBackward && yEdge.EdgeData.CanMoveForward) // { // backwardWeight = xEdgeBackwardWeight + yEdge.EdgeData.Weight; // } // if (xEdge.EdgeData.CanMoveForward && yEdge.EdgeData.CanMoveBackward) // { // forwardWeight = xEdgeForwardWeight + yEdge.EdgeData.Weight; // } // } // Tuple<float, float> existingWeights; // Tuple<uint, uint> neighbourPair = new Tuple<uint,uint>(xEdge.Neighbour, yEdge.Neighbour); // if(!allNeighbourPairs.TryGetValue(neighbourPair, out existingWeights)) // { // if (existingWeights.Item1 < forwardWeight) // { // forwardWeight = existingWeights.Item1; // } // if (existingWeights.Item2 < backwardWeight) // { // backwardWeight = existingWeights.Item2; // } // } // allNeighbourPairs[neighbourPair] = new Tuple<float, float>(forwardWeight, backwardWeight); // } //} var toRequeue = new HashSet <uint>(); var forwardEdges = new CHEdgeData?[2]; var backwardEdges = new CHEdgeData?[2]; var existingEdgesToRemove = new HashSet <CHEdgeData>(); // loop over each combination of edges just once. var forwardWitnesses = new bool[allNeigbours.Count]; var backwardWitnesses = new bool[allNeigbours.Count]; var weights = new List <float>(allNeigbours.Count); var edgesToY = new Dictionary <uint, Tuple <CHEdgeData?, CHEdgeData?, CHEdgeData?, float, float> >(allNeigbours.Count); for (int x = 1; x < allNeigbours.Count; x++) { // loop over all elements first. var xEdge = allNeigbours[x]; // get edges. edgesToY.Clear(); var rawEdgesToY = _target.GetEdges(xEdge.Neighbour); while (rawEdgesToY.MoveNext()) { var rawEdgeNeighbour = rawEdgesToY.Neighbour; if (tosSet.Contains(rawEdgeNeighbour)) { var rawEdgeData = rawEdgesToY.EdgeData; var rawEdgeForwardWeight = rawEdgeData.CanMoveForward ? rawEdgeData.Weight : float.MaxValue; var rawEdgeBackwardWeight = rawEdgeData.CanMoveBackward ? rawEdgeData.Weight : float.MaxValue; Tuple <CHEdgeData?, CHEdgeData?, CHEdgeData?, float, float> edgeTuple; if (!edgesToY.TryGetValue(rawEdgeNeighbour, out edgeTuple)) { edgeTuple = new Tuple <CHEdgeData?, CHEdgeData?, CHEdgeData?, float, float>(rawEdgeData, null, null, rawEdgeForwardWeight, rawEdgeBackwardWeight); edgesToY.Add(rawEdgeNeighbour, edgeTuple); } else if (!edgeTuple.Item2.HasValue) { edgesToY[rawEdgeNeighbour] = new Tuple <CHEdgeData?, CHEdgeData?, CHEdgeData?, float, float>( edgeTuple.Item1, rawEdgeData, null, rawEdgeForwardWeight < edgeTuple.Item4 ? rawEdgeForwardWeight : edgeTuple.Item4, rawEdgeBackwardWeight < edgeTuple.Item5 ? rawEdgeBackwardWeight : edgeTuple.Item5); } else { edgesToY[rawEdgeNeighbour] = new Tuple <CHEdgeData?, CHEdgeData?, CHEdgeData?, float, float>( edgeTuple.Item1, edgeTuple.Item2, rawEdgeData, rawEdgeForwardWeight < edgeTuple.Item4 ? rawEdgeForwardWeight : edgeTuple.Item4, rawEdgeBackwardWeight < edgeTuple.Item5 ? rawEdgeBackwardWeight : edgeTuple.Item5); } } } // calculate max weight. weights.Clear(); var forwardUnknown = false; var backwardUnknown = false; for (int y = 0; y < x; y++) { // update maxWeight. var yEdge = allNeigbours[y]; if (xEdge.Neighbour != yEdge.Neighbour) { // reset witnesses. var forwardWeight = (float)xEdge.EdgeData.Weight + (float)yEdge.EdgeData.Weight; forwardWitnesses[y] = !xEdge.EdgeData.CanMoveBackward || !yEdge.EdgeData.CanMoveForward; backwardWitnesses[y] = !xEdge.EdgeData.CanMoveForward || !yEdge.EdgeData.CanMoveBackward; weights.Add(forwardWeight); Tuple <CHEdgeData?, CHEdgeData?, CHEdgeData?, float, float> edgeTuple; if (edgesToY.TryGetValue(yEdge.Neighbour, out edgeTuple)) { if (!forwardWitnesses[y]) { // check 1-hop witnesses. if (edgeTuple.Item4 <= forwardWeight) { forwardWitnesses[y] = true; } } if (!backwardWitnesses[y]) { // check 1-hop witnesses. if (edgeTuple.Item5 <= forwardWeight) { backwardWitnesses[y] = true; } } } forwardUnknown = !forwardWitnesses[y] || forwardUnknown; backwardUnknown = !backwardWitnesses[y] || backwardUnknown; } else { // already set this to true, not use calculating it's witness. forwardWitnesses[y] = true; backwardWitnesses[y] = true; weights.Add(0); } } // calculate witnesses. if (forwardUnknown || backwardUnknown) { _contractionWitnessCalculator.Exists(_target, xEdge.Neighbour, tos, weights, int.MaxValue, ref forwardWitnesses, ref backwardWitnesses); } for (int y = 0; y < x; y++) { // loop over all elements. var yEdge = allNeigbours[y]; // add the combinations of these edges. if (xEdge.Neighbour != yEdge.Neighbour) { // there is a connection from x to y and there is no witness path. // create x-to-y data and edge. var canMoveForward = !forwardWitnesses[y] && (xEdge.EdgeData.CanMoveBackward && yEdge.EdgeData.CanMoveForward); var canMoveBackward = !backwardWitnesses[y] && (xEdge.EdgeData.CanMoveForward && yEdge.EdgeData.CanMoveBackward); if (canMoveForward || canMoveBackward) { // add the edge if there is usefull info or if there needs to be a neighbour relationship. // calculate the total weights. var weight = (float)xEdge.EdgeData.Weight + (float)yEdge.EdgeData.Weight; // there are a few options now: // 1) No edges yet between xEdge.Neighbour and yEdge.Neighbour. // 1) There is no other contracted edge: just add as a duplicate. // 2) There is at least on other contracted edge: optimize information because there can only be 4 case between two vertices: // - One bidirectional edge. // - Two directed edges with different weights. // - One forward edge. // - One backward edge. // => all available information needs to be combined. // check existing data. var existingForwardWeight = float.MaxValue; var existingBackwardWeight = float.MaxValue; uint existingForwardContracted = 0; uint existingBackwardContracted = 0; var existingCanMoveForward = false; var existingCanMoveBackward = false; var existingEdges = _target.GetEdges(xEdge.Neighbour, yEdge.Neighbour); existingEdgesToRemove.Clear(); while (existingEdges.MoveNext()) { var existingEdgeData = existingEdges.EdgeData; if (existingEdgeData.IsContracted) { // this edge is contracted, collect it's information. existingEdgesToRemove.Add(existingEdgeData); if (existingEdgeData.CanMoveForward) { // can move forward, so at least one edge that can move forward. existingCanMoveForward = true; if (existingForwardWeight > existingEdgeData.Weight) { // update forward weight. existingForwardWeight = existingEdgeData.Weight; existingForwardContracted = existingEdgeData.ContractedId; } } if (existingEdgeData.CanMoveBackward) { // can move backward, so at least one edge that can move backward. existingCanMoveBackward = true; if (existingBackwardWeight > existingEdgeData.Weight) { // update backward weight. existingBackwardWeight = existingEdgeData.Weight; existingBackwardContracted = existingEdgeData.ContractedId; } } } } if (existingCanMoveForward || existingCanMoveBackward) { // there is already another contraced edge. uint forwardContractedId = vertex; float forwardWeight = weight; // merge with existing data. if (existingCanMoveForward && ((weight > existingForwardWeight) || !canMoveForward)) { // choose the smallest weight. canMoveForward = true; forwardContractedId = existingForwardContracted; forwardWeight = existingForwardWeight; } uint backwardContractedId = vertex; float backwardWeight = weight; // merge with existing data. if (existingCanMoveBackward && ((weight > existingBackwardWeight) || !canMoveBackward)) { // choose the smallest weight. canMoveBackward = true; backwardContractedId = existingBackwardContracted; backwardWeight = existingBackwardWeight; } // add one of the 4 above case. forwardEdges[0] = null; forwardEdges[1] = null; backwardEdges[0] = null; backwardEdges[1] = null; if (canMoveForward && canMoveBackward && forwardWeight == backwardWeight && forwardContractedId == backwardContractedId) { // just add one edge. forwardEdges[0] = new CHEdgeData(forwardContractedId, true, true, forwardWeight); //_target.AddEdge(xEdge.Neighbour, yEdge.Neighbour, new CHEdgeData(forwardContractedId, true, true, forwardWeight)); backwardEdges[0] = new CHEdgeData(backwardContractedId, true, true, backwardWeight); //_target.AddEdge(yEdge.Neighbour, xEdge.Neighbour, new CHEdgeData(backwardContractedId, true, true, backwardWeight)); } else if (canMoveBackward && canMoveForward) { // add two different edges. forwardEdges[0] = new CHEdgeData(forwardContractedId, true, false, forwardWeight); //_target.AddEdge(xEdge.Neighbour, yEdge.Neighbour, new CHEdgeData(forwardContractedId, true, false, forwardWeight)); backwardEdges[0] = new CHEdgeData(forwardContractedId, false, true, forwardWeight); //_target.AddEdge(yEdge.Neighbour, xEdge.Neighbour, new CHEdgeData(forwardContractedId, false, true, forwardWeight)); forwardEdges[1] = new CHEdgeData(backwardContractedId, false, true, backwardWeight); //_target.AddEdge(xEdge.Neighbour, yEdge.Neighbour, new CHEdgeData(backwardContractedId, false, true, backwardWeight)); backwardEdges[1] = new CHEdgeData(backwardContractedId, true, false, backwardWeight); //_target.AddEdge(yEdge.Neighbour, xEdge.Neighbour, new CHEdgeData(backwardContractedId, true, false, backwardWeight)); } else if (canMoveForward) { // only add one forward edge. forwardEdges[0] = new CHEdgeData(forwardContractedId, true, false, forwardWeight); //_target.AddEdge(xEdge.Neighbour, yEdge.Neighbour, new CHEdgeData(forwardContractedId, true, false, forwardWeight)); backwardEdges[0] = new CHEdgeData(forwardContractedId, false, true, forwardWeight); //_target.AddEdge(yEdge.Neighbour, xEdge.Neighbour, new CHEdgeData(forwardContractedId, false, true, forwardWeight)); } else if (canMoveBackward) { // only add one backward edge. forwardEdges[0] = new CHEdgeData(backwardContractedId, false, true, backwardWeight); //_target.AddEdge(xEdge.Neighbour, yEdge.Neighbour, new CHEdgeData(backwardContractedId, false, true, backwardWeight)); backwardEdges[0] = new CHEdgeData(backwardContractedId, true, false, backwardWeight); //_target.AddEdge(yEdge.Neighbour, xEdge.Neighbour, new CHEdgeData(backwardContractedId, true, false, backwardWeight)); } // remove all existing stuff. foreach (var existingEdgeToRemove in existingEdgesToRemove) { if (forwardEdges[0].Equals(existingEdgeToRemove)) { // this forward edge is to be kept. forwardEdges[0] = null; // it's already there. } else if (forwardEdges[1] != null && !forwardEdges[1].Equals(existingEdgeToRemove)) { // this forward edge is to be kept. forwardEdges[1] = null; // it's already there. } else { // yup, just remove it now. _target.RemoveEdge(xEdge.Neighbour, yEdge.Neighbour, existingEdgeToRemove); } var existingEdgeToRemoveBackward = (CHEdgeData)existingEdgeToRemove.Reverse(); if (backwardEdges[0].Equals(existingEdgeToRemoveBackward)) { // this backward edge is to be kept. backwardEdges[0] = null; // it's already there. } else if (backwardEdges[1] != null && !backwardEdges[1].Equals(existingEdgeToRemoveBackward)) { // this backward edge is to be kept. backwardEdges[1] = null; // it's already there. } else { // yup, just remove it now. _target.RemoveEdge(yEdge.Neighbour, xEdge.Neighbour, existingEdgeToRemoveBackward); } } // add remaining edges. if (forwardEdges[0].HasValue) { _target.AddEdge(xEdge.Neighbour, yEdge.Neighbour, forwardEdges[0].Value); } if (forwardEdges[1].HasValue) { _target.AddEdge(xEdge.Neighbour, yEdge.Neighbour, forwardEdges[1].Value); } if (backwardEdges[0].HasValue) { _target.AddEdge(yEdge.Neighbour, xEdge.Neighbour, backwardEdges[0].Value); } if (backwardEdges[1].HasValue) { _target.AddEdge(yEdge.Neighbour, xEdge.Neighbour, backwardEdges[1].Value); } toRequeue.Add(xEdge.Neighbour); toRequeue.Add(yEdge.Neighbour); } else { // there is no edge, just add the data. // add contracted edges like normal. _target.AddEdge(xEdge.Neighbour, yEdge.Neighbour, new CHEdgeData(vertex, canMoveForward, canMoveBackward, weight)); _target.AddEdge(yEdge.Neighbour, xEdge.Neighbour, new CHEdgeData(vertex, canMoveBackward, canMoveForward, weight)); toRequeue.Add(xEdge.Neighbour); toRequeue.Add(yEdge.Neighbour); } } } } } // mark the vertex as contracted. this.MarkContracted(vertex); // notify a contracted neighbour. _calculator.NotifyContracted(vertex); // report the after contraction event. this.OnAfterContraction(vertex, allNeigbours); //// update priority of direct neighbours. //foreach (var neighbour in toRequeue) //{ // this.ReQueue(neighbour); //} }
/// <summary> /// Starts pre-processing all nodes. /// </summary> public void Start() { // build the empty coordinate list. var emptyCoordinateList = new GeoCoordinateSimple[0]; var verticesList = new HashSet <uint>(); // initialize status variables. uint nextToProcess = 0; uint nextPosition = 0; // search edge until a real node. double latestProgress = 0; while (nextToProcess < _graph.VertexCount) { // keep looping until all vertices have been processed. // select a new vertext to select. var vertexToProcess = nextToProcess; var edges = _graph.GetEdges(vertexToProcess).ToList(); if (edges.Count == 2) { // find one of the neighbours that is usefull. vertexToProcess = edges[0].Neighbour; edges = _graph.GetEdges(vertexToProcess).ToList(); verticesList.Clear(); verticesList.Add(vertexToProcess); while (edges.Count == 2) { // keep looping until there is a vertex that is usefull. vertexToProcess = edges[0].Neighbour; if (verticesList.Contains(vertexToProcess)) { // take the other vertex. vertexToProcess = edges[1].Neighbour; if (verticesList.Contains(vertexToProcess)) { // an island was detected with only vertices having two neighbours. // TODO: find a way to handle this! edges = new List <Edge <Edge> >(0); break; } } verticesList.Add(vertexToProcess); edges = _graph.GetEdges(vertexToProcess).ToList(); } } if (edges.Count > 0) { // ok, the vertex was not already processed. nextPosition++; var oldEdges = new List <Edge <Edge> >(edges); var ignoreList = new HashSet <uint>(); foreach (var oldEdge in oldEdges) { if (ignoreList.Contains(oldEdge.Neighbour)) { // ignore this edge: already removed in a previous iteration. break; } // don't re-process edges that already have coordinates. ICoordinateCollection oldEdgeValueCoordinates; _graph.GetEdgeShape(vertexToProcess, oldEdge.Neighbour, out oldEdgeValueCoordinates); if (oldEdgeValueCoordinates != null) { // this edge has already been processed. break; } // STEP1: Build list of vertices that are only for form. // set current/previous. var distance = oldEdge.EdgeData.Distance; var current = oldEdge.Neighbour; var previous = vertexToProcess; // build list of vertices. var vertices = new List <uint>(); vertices.Add(previous); vertices.Add(current); // get next edges list. var nextEdges = _graph.GetEdges(current).ToList(); while (nextEdges.Count == 2) { // ok the current vertex can be removed. var nextEdge = nextEdges[0]; if (nextEdge.Neighbour == previous) { // it's the other edge! nextEdge = nextEdges[1]; } // compare edges. if (nextEdge.EdgeData.Forward != oldEdge.EdgeData.Forward || nextEdge.EdgeData.Tags != oldEdge.EdgeData.Tags) { // oeps, edges are different! break; } // check for intermediates. ICoordinateCollection nextEdgeValueCoordinates; _graph.GetEdgeShape(current, nextEdge.Neighbour, out nextEdgeValueCoordinates); if (nextEdgeValueCoordinates != null) { // oeps, there are intermediates already, this can occur when two osm-ways are drawn on top of eachother. break; } // add distance. distance = distance + nextEdge.EdgeData.Distance; // set current/previous. previous = current; current = nextEdge.Neighbour; vertices.Add(current); // get next edges. nextEdges = _graph.GetEdges(current).ToList(); } // check if the edge contains intermediate points. if (vertices.Count == 2) { // no intermediate points: add the empty coordinate list. var oldEdgeValue = oldEdge.EdgeData; // keep edges that already have intermediates. ICoordinateCollection edgeToKeepValueCoordinates = null; var edgesToKeep = new List <Tuple <uint, Edge, ICoordinateCollection> >(); foreach (var edgeToKeep in _graph.GetEdges(vertexToProcess).ToList()) { edgeToKeepValueCoordinates = null; if (edgeToKeep.Neighbour == oldEdge.Neighbour && _graph.GetEdgeShape(vertexToProcess, edgeToKeep.Neighbour, out edgeToKeepValueCoordinates)) { edgesToKeep.Add(new Tuple <uint, Edge, ICoordinateCollection>( edgeToKeep.Neighbour, edgeToKeep.EdgeData, edgeToKeepValueCoordinates)); } } // delete olds arcs. _graph.RemoveEdge(vertexToProcess, oldEdge.Neighbour); // add new arc. if (oldEdgeValue.Forward) { _graph.AddEdge(vertexToProcess, oldEdge.Neighbour, oldEdgeValue, null); } else { _graph.AddEdge(vertexToProcess, oldEdge.Neighbour, (Edge)oldEdgeValue.Reverse(), null); } // add edges to keep. foreach (var edgeToKeep in edgesToKeep) { _graph.AddEdge(vertexToProcess, edgeToKeep.Item1, edgeToKeep.Item2, edgeToKeep.Item3); } } else { // intermediate points: build array. // STEP2: Build array of coordinates. var coordinates = new GeoCoordinateSimple[vertices.Count - 2]; float latitude, longitude; for (int idx = 1; idx < vertices.Count - 1; idx++) { _graph.GetVertex(vertices[idx], out latitude, out longitude); coordinates[idx - 1] = new GeoCoordinateSimple() { Latitude = latitude, Longitude = longitude }; } // STEP3: Remove all unneeded edges. _graph.RemoveEdge(vertices[0], vertices[1]); // remove first edge. for (int idx = 1; idx < vertices.Count - 1; idx++) { // delete all intermidiate arcs. _graph.RemoveEdges(vertices[idx]); } _graph.RemoveEdge(vertices[vertices.Count - 1], vertices[vertices.Count - 2]); // remove last edge. if (vertices[0] == vertices[vertices.Count - 1]) { // also remove outgoing edge. ignoreList.Add(vertices[vertices.Count - 2]); // make sure this arc is ignored in next iteration. } // STEP4: Add new edge. if (oldEdge.EdgeData.Forward) { _graph.AddEdge(vertices[0], vertices[vertices.Count - 1], new Edge() { Forward = oldEdge.EdgeData.Forward, Tags = oldEdge.EdgeData.Tags, Distance = distance }, new CoordinateArrayCollection <GeoCoordinateSimple>(coordinates)); } else { var reverse = new GeoCoordinateSimple[coordinates.Length]; coordinates.CopyToReverse(reverse, 0); _graph.AddEdge(vertices[vertices.Count - 1], vertices[0], new Edge() { Forward = !oldEdge.EdgeData.Forward, Tags = oldEdge.EdgeData.Tags, Distance = distance }, new CoordinateArrayCollection <GeoCoordinateSimple>(reverse)); } } } } // move to the next position. nextToProcess++; // report progress. float progress = (float)System.Math.Round((((double)nextToProcess / (double)_graph.VertexCount) * 100)); if (progress != latestProgress) { OsmSharp.Logging.Log.TraceEvent("Preprocessor", TraceEventType.Information, "Removing edges... {0}%", progress); latestProgress = progress; } } // compress the graph. this.CompressGraph(); }