/// <summary> /// Adjusts this location by inserting intermediate LR-points if needed. /// </summary> /// public static void AdjustToValidDistance(this ReferencedLine line, Coder coder, List <int> points, int start = 0) { // get start/end vertex. var vertexIdx1 = points[start]; var vertexIdx2 = points[start + 1]; var count = vertexIdx2 - vertexIdx1 + 1; // calculate length to begin with. var coordinates = line.GetCoordinates(coder.Router.Db, vertexIdx1, count); var length = coordinates.Length(); if (length > 15000) { // too long. // find the best intermediate point. var intermediatePoints = new SortedDictionary <double, int>(); for (int idx = vertexIdx1 + 1; idx < vertexIdx1 + count - 2; idx++) { var score = 0.0; if (coder.IsVertexValid(line.Vertices[idx])) { // a valid vertex is obviously a better choice! score = score + 4096; } // the length is good when close to 15000 but not over. var lengthBefore = line.GetCoordinates(coder.Router.Db, vertexIdx1, idx - vertexIdx1 + 1).Length(); if (lengthBefore < 15000) { // not over! score = score + (1024 * (lengthBefore / 15000)); } var lengthAfter = line.GetCoordinates(coder.Router.Db, idx, count - idx).Length(); if (lengthAfter < 15000) { // not over! score = score + (1024 * (lengthAfter / 15000)); } // add to sorted dictionary. intermediatePoints[8192 - score] = idx; } // select the best point and insert it in between. var bestPoint = intermediatePoints.First().Value; points.Insert(start + 1, bestPoint); // test the two distances. line.AdjustToValidDistance(coder, points, start + 1); line.AdjustToValidDistance(coder, points, start); } }
/// <summary> /// Finds a valid vertex for the given vertex but does not search in the direction of the target neighbour. /// </summary> public static EdgePath <float> FindValidVertexFor(this Coder coder, uint vertex, long targetDirectedEdgeId, uint targetVertex, HashSet <uint> excludeSet, bool searchForward) { var profile = coder.Profile.Profile; // GIST: execute a dykstra search to find a vertex that is valid. // this will return a vertex that is on the shortest path: // foundVertex -> vertex -> targetNeighbour. var targetEdge = Itinero.Constants.NO_EDGE; if (targetDirectedEdgeId > 0) { targetEdge = (uint)(targetDirectedEdgeId - 1); } else { targetEdge = (uint)((-targetDirectedEdgeId) - 1); } // initialize settled set. var settled = new HashSet <long>(); settled.Add(targetVertex); // initialize heap. var heap = new BinaryHeap <EdgePath <float> >(10); heap.Push(new EdgePath <float>((uint)vertex), 0); // find the path to the closest valid vertex. EdgePath <float> pathTo = null; var edgeEnumerator = coder.Router.Db.Network.GetEdgeEnumerator(); while (heap.Count > 0) { // get next. var current = heap.Pop(); if (settled.Contains(current.Vertex)) { // don't consider vertices twice. continue; } settled.Add(current.Vertex); // limit search. if (settled.Count > coder.Profile.MaxSettles) { // not valid vertex found. return(null); } // check if valid. if (current.Vertex != vertex && coder.IsVertexValid(current.Vertex)) { // ok! vertex is valid. pathTo = current; } else { // continue search. // add unsettled neighbours. edgeEnumerator.MoveTo(current.Vertex); foreach (var edge in edgeEnumerator) { if (!excludeSet.Contains(edge.To) && !settled.Contains(edge.To) && !(edge.Id == targetEdge)) { // ok, new neighbour, and ok, not the edge and neighbour to ignore. var edgeProfile = coder.Router.Db.EdgeProfiles.Get(edge.Data.Profile); var factor = profile.Factor(edgeProfile); if (factor.Value > 0 && (factor.Direction == 0 || (searchForward && (factor.Direction == 1) != edge.DataInverted) || (!searchForward && (factor.Direction == 1) == edge.DataInverted))) { // ok, we can traverse this edge and no oneway or oneway reversed. var weight = current.Weight + factor.Value * edge.Data.Distance; var path = new EdgePath <float>(edge.To, weight, edge.IdDirected(), current); heap.Push(path, (float)path.Weight); } } } } } // ok, is there a path found. if (pathTo == null) { // oeps, probably something wrong with network-topology. // just take the default option. //throw new Exception( // string.Format("Could not find a valid vertex for invalid vertex [{0}].", vertex)); return(null); } // add the path to the given location. return(pathTo); }
/// <summary> /// Tries to adjust this location backwards to a valid point. /// </summary> /// <returns></returns> public static bool TryAdjustToValidPointBackwards(this ReferencedLine line, Coder coder, uint vertex1, uint vertex2, HashSet <uint> exclude) { var length = line.Length(coder.Router.Db); var positiveOffsetLength = (line.PositiveOffsetPercentage / 100) * length; exclude = new HashSet <uint>(exclude); foreach (var vertex in line.Vertices) { exclude.Add(vertex); } if (!coder.IsVertexValid(line.Vertices[0])) { // from is not valid, try to find a valid point. var pathToValid = coder.FindValidVertexFor(line.Vertices[0], line.Edges[0], line.Vertices[1], exclude, false); // build edges list. if (pathToValid != null) { // path found check if on shortest route. var shortestRoute = coder.FindShortestPath(line.Vertices[1], pathToValid.Vertex, false); while (shortestRoute != null && !shortestRoute.HasVertex(line.Vertices[0])) { // the vertex that should be on this shortest route, isn't anymore. // exclude the current target vertex, exclude.Add(pathToValid.Vertex); // calulate a new path-to-valid. pathToValid = coder.FindValidVertexFor(line.Vertices[0], line.Edges[0], line.Vertices[1], exclude, false); if (pathToValid == null) { // a new path was not found. break; } shortestRoute = coder.FindShortestPath(line.Vertices[1], pathToValid.Vertex, false); } if (pathToValid != null) { // no path found, just leave things as is. var pathToValidAsList = pathToValid.ToList(); var newVertices = new List <uint>(); var newEdges = new List <long>(); for (int idx = 0; idx < pathToValidAsList.Count; idx++) { // loop over edges. newVertices.Add(pathToValidAsList[idx].Vertex); if (idx > 0) { newEdges.Add(-pathToValidAsList[idx].Edge); // need the reverse edges. } } newEdges.Reverse(); newVertices.Reverse(); // create new location. var edgesArray = new long[newEdges.Count + line.Edges.Length]; newEdges.CopyTo(0, edgesArray, 0, newEdges.Count); line.Edges.CopyTo(0, edgesArray, newEdges.Count, line.Edges.Length); var vertexArray = new uint[newVertices.Count - 1 + line.Vertices.Length]; newVertices.CopyTo(0, vertexArray, 0, newVertices.Count - 1); line.Vertices.CopyTo(0, vertexArray, newVertices.Count - 1, line.Vertices.Length); line.Edges = edgesArray; line.Vertices = vertexArray; // adjust offset length. var newLength = (float)line.Length(coder.Router.Db); positiveOffsetLength = positiveOffsetLength + (newLength - length); length = newLength; } else { // no valid path was found. return(false); } } else { // no valid path was found. return(false); } } // update offset percentage. line.PositiveOffsetPercentage = (float)((positiveOffsetLength / length) * 100.0); return(true); }
/// <summary> /// Adjusts this location to use valid LR-points. /// </summary> public static void AdjustToValidPoints(this ReferencedLine line, Coder coder) { if (line.Vertices.Length <= 1) { throw new ArgumentException("Cannot adjust a line location with only one vertex."); } var vertex1Valid = coder.IsVertexValid(line.Vertices[0]); var vertex2Valid = coder.IsVertexValid(line.Vertices[line.Vertices.Length - 1]); if (vertex1Valid && vertex2Valid) { // already valid. return; } if (line.Vertices.Length > 2) { return; } // line was already adjusted. var vertex1 = line.Vertices[0]; var vertex2 = line.Vertices[1]; if (!coder.IsOnShortestPath(line.Vertices[0], line.Vertices[line.Vertices.Length - 1], vertex1, vertex2)) { // impossible to expand edge. return; } // make sure the original sequence is still there on the shortest path. ReferencedLine validCopy = null; var backwardExcludeSet = line.GetVerticesSet(); while (true) { // search backward. var workingCopy = line.Clone() as ReferencedLine; if (!workingCopy.TryAdjustToValidPointBackwards(coder, vertex1, vertex2, backwardExcludeSet)) { // no more options exist, impossible to expand edge, just keep the edge itself. return; } if (!vertex2Valid) { // search forward. var forwardExcludeSet = workingCopy.GetVerticesSet(); do { var forwardWorkingCopy = workingCopy.Clone() as ReferencedLine; if (!forwardWorkingCopy.TryAdjustToValidPointForwards(coder, vertex1, vertex2, forwardExcludeSet)) { // no more forward options for the current backward. break; } // check valid. if (coder.IsOnShortestPath(forwardWorkingCopy.Vertices[0], forwardWorkingCopy.Vertices[forwardWorkingCopy.Vertices.Length - 1], vertex1, vertex2)) { // current location is valid. validCopy = forwardWorkingCopy; break; } // not valid here, exclude current forward. forwardExcludeSet.Add(forwardWorkingCopy.Vertices[forwardWorkingCopy.Vertices.Length - 1]); } while (true); } else { // check valid. if (coder.IsOnShortestPath(workingCopy.Vertices[0], workingCopy.Vertices[workingCopy.Vertices.Length - 1], vertex1, vertex2)) { // current location is valid. validCopy = workingCopy; break; } } if (validCopy != null) { // current location is valid. break; } if (vertex1Valid) { // vertex1 was already valid, no reason to continue searching. return; } // exclude current backward and continue. backwardExcludeSet.Add(workingCopy.Vertices[0]); } // copy from working copy. line.Edges = validCopy.Edges; line.Vertices = validCopy.Vertices; line.NegativeOffsetPercentage = validCopy.NegativeOffsetPercentage; line.PositiveOffsetPercentage = validCopy.PositiveOffsetPercentage; }