/// <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);
            }
        }
Exemple #2
0
        /// <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;
        }