/// <summary> /// Calculates a route between the two given vertices. /// </summary> /// <returns></returns> public override CandidateRoute FindCandidateRoute(CandidateVertexEdge from, CandidateVertexEdge to, FunctionalRoadClass minimum, bool ignoreFromEdge = false, bool ignoreToEdge = false) { var edgeInterpreter = new ShapefileEdgeInterpreter(); var interpreter = new ShapefileRoutingInterpreter(); var path = this.GetRouter().Calculate(this.Graph, interpreter, this.Vehicle, from, to, minimum, ignoreFromEdge, ignoreToEdge); // if no route is found, score is 0. if (path == null) { return(new CandidateRoute() { Route = null, Score = Score.New(Score.CANDIDATE_ROUTE, "Candidate route quality.", 0, 1) }); } var edges = new List <LiveEdge>(); var vertices = new List <long>(); vertices.Add(path.VertexId); while (path.From != null) { // add to vertices list. vertices.Add(path.From.VertexId); // get edge between current and from. var fromVertex = path.From.VertexId; var toVertex = path.VertexId; var edgeDistance = double.MaxValue; var arcs = this.Graph.GetEdges(fromVertex); LiveEdge?edge = null; foreach (var arc in arcs) { if (arc.Key == toVertex && arc.Value.Distance < edgeDistance) { // there is a candidate arc. edgeDistance = arc.Value.Distance; edge = arc.Value; } } if (!edge.HasValue) { // this should be impossible. throw new Exception("No edge found between two consequtive vertices on a route."); } edges.Add(edge.Value); // move to next segment. path = path.From; } // reverse lists. edges.Reverse(); vertices.Reverse(); // fill shapes. var edgeShapes = new GeoCoordinateSimple[edges.Count][]; for (int i = 0; i < edges.Count; i++) { edgeShapes[i] = this.Graph.GetEdgeShape(vertices[i], vertices[i + 1]); } return(new CandidateRoute() { Route = new ReferencedLine(this.Graph) { Edges = edges.ToArray(), EdgeShapes = edgeShapes, Vertices = vertices.ToArray() }, Score = Score.New(Score.CANDIDATE_ROUTE, "Candidate route quality.", 1, 1) }); }
/// <summary> /// Calculates a route between the two given vertices. /// </summary> /// <returns></returns> public abstract CandidateRoute FindCandidateRoute(CandidateVertexEdge from, CandidateVertexEdge to, FunctionalRoadClass minimum, bool ignoreFromEdge = false, bool ignoreToEdge = false);
/// <summary> /// Calculates a path between the two candidates using the information in the candidates. /// </summary> public PathSegment <long> Calculate(BasicRouterDataSource <LiveEdge> graph, IRoutingInterpreter interpreter, Vehicle vehicle, CandidateVertexEdge from, CandidateVertexEdge to, FunctionalRoadClass minimum, bool ignoreFromEdge, bool ignoreToEdge) { return(this.Calculate(graph, interpreter, vehicle, from, to, minimum, MaxSettles, ignoreFromEdge, ignoreToEdge)); }
/// <summary> /// Calculates a path between the two candidates using the information in the candidates. /// </summary> /// <returns></returns> public PathSegment <long> Calculate(BasicRouterDataSource <LiveEdge> graph, IRoutingInterpreter interpreter, Vehicle vehicle, CandidateVertexEdge from, CandidateVertexEdge to, FunctionalRoadClass minimum, uint maxSettles, bool ignoreFromEdge, bool ignoreToEdge) { // first check for the simple stuff. if (from.Vertex == to.Vertex) { // route consists of one vertex. return(new PathSegment <long>(from.Vertex)); } // check paths. var fromPath = new PathSegment <long>(from.TargetVertex, vehicle.Weight(graph.TagsIndex.Get(from.Edge.Tags), from.Edge.Distance), new PathSegment <long>(from.Vertex)); if (!ignoreFromEdge || !ignoreToEdge) { // do not check paths when one of the edges need to be ignored. if (from.Vertex == to.TargetVertex && to.Vertex == from.TargetVertex) { // edges are the same, return(fromPath); } } if (ignoreFromEdge) { // ignore from edge, just use the from-vertex. fromPath = new PathSegment <long>(from.Vertex); } // initialize the heap/visit list. var heap = new BinaryHeap <PathSegment <long> >(maxSettles / 4); var visited = new HashSet <long>(); visited.Add(from.Vertex); // set target. var target = to.Vertex; var targetWeight = double.MaxValue; if (!ignoreToEdge) { // also add the target to the visit list and actually search for the target candidate edge ending. target = to.TargetVertex; visited.Add(to.Vertex); } // create a path segment from the from-candidate. heap.Push(fromPath, (float)fromPath.Weight); // keep searching for the target. while (true) { // get the next vertex. var current = heap.Pop(); if (current == null) { // there is nothing more in the queue, target will not be found. break; } if (visited.Contains(current.VertexId)) { // move to the next neighbour. continue; } visited.Add(current.VertexId); // check for the target. if (current.VertexId == target) { // target was found. if (ignoreToEdge) { return(current); } return(new PathSegment <long>(to.Vertex, current.Weight, current)); } // check if the maximum settled vertex count has been reached. if (visited.Count >= maxSettles) { // stop search, target will not be found. break; } // add the neighbours to queue. var neighbours = graph.GetEdges(current.VertexId); if (neighbours != null) { // neighbours exist. foreach (var neighbour in neighbours) { // check if the neighbour was settled before. if (visited.Contains(neighbour.Key)) { // move to the next neighbour. continue; } // get tags and check traversability and oneway. var tags = graph.TagsIndex.Get(neighbour.Value.Tags); if (vehicle.CanTraverse(tags)) { // yay! can traverse. var oneway = vehicle.IsOneWay(tags); if (oneway == null || oneway.Value == neighbour.Value.Forward) { // create path to neighbour and queue it. var weight = vehicle.Weight(graph.TagsIndex.Get(neighbour.Value.Tags), neighbour.Value.Distance); var path = new PathSegment <long>(neighbour.Key, current.Weight + weight, current); if (path.Weight < targetWeight) { // the weight of the neighbour is smaller than the first neighbour found. heap.Push(path, (float)path.Weight); // save distance. if (path.VertexId == target) { // the target is already found, no use of queuing neigbours that have a higher weight. if (targetWeight > path.Weight) { // set the weight. targetWeight = path.Weight; } } } } } } } } return(null); }
/// <summary> /// Decodes an OpenLR-encoded unreferenced raw OpenLR location into a referenced Location. /// </summary> /// <param name="location"></param> /// <returns></returns> public override ReferencedLine Decode(LineLocation location) { // get candidate vertices and edges. var candidates = new List <SortedSet <CandidateVertexEdge> >(); var lrps = new List <LocationReferencePoint>(); // loop over all lrps. lrps.Add(location.First); candidates.Add(this.MainDecoder.FindCandidatesFor(location.First, true)); if (location.Intermediate != null) { // there are intermediates. for (int idx = 0; idx < location.Intermediate.Length; idx++) { lrps.Add(location.Intermediate[idx]); candidates.Add(this.MainDecoder.FindCandidatesFor(location.Intermediate[idx], true)); } } lrps.Add(location.Last); candidates.Add(this.MainDecoder.FindCandidatesFor(location.Last, false)); // find a route between each pair of sequential points. // start with the last two points and move backwards. var target = lrps[lrps.Count - 1]; var targetCandidates = candidates[candidates.Count - 1]; var lineLocationSegments = new List <ReferencedLine>(); for (int idx = lrps.Count - 2; idx >= 0; idx--) { var source = lrps[idx]; var sourceCandidates = candidates[idx]; // build a list of combined scores. var combinedScoresSet = new SortedSet <CombinedScore>(new CombinedScoreComparer()); foreach (var targetCandidate in targetCandidates) { foreach (var currentCandidate in sourceCandidates) { combinedScoresSet.Add(new CombinedScore() { Source = currentCandidate, Target = targetCandidate }); } } var combinedScores = new List <CombinedScore>(combinedScoresSet); // find the best candidate route. CandidateRoute best = null; CandidateVertexEdge bestSource = null; while (combinedScores.Count > 0) { // get the first pair. var combinedScore = combinedScores[0]; combinedScores.RemoveAt(0); // find a route. var candidate = this.MainDecoder.FindCandidateRoute(combinedScore.Source, combinedScore.Target, source.LowestFunctionalRoadClassToNext.Value, false, idx < lrps.Count - 2); // bring score of from/to also into the mix. candidate.Score = candidate.Score + combinedScore.Score; // verify bearing by adding it to the score. if (candidate != null && candidate.Route != null) { // calculate bearing and compare with reference bearing. // calculate distance and compare with distancetonext. var distance = candidate.Route.GetCoordinates(this.MainDecoder.Graph).Length().Value; var expectedDistance = source.DistanceToNext; // default a perfect score, only compare large distances. Score deviation = Score.New(Score.DISTANCE_COMPARISON, "Compares expected location distance with decoded location distance (1=perfect, 0=difference bigger than total distance)", 1, 1); if (expectedDistance > 200 || distance > 200) { // non-perfect score. // don't care about difference smaller than 200m, the binary encoding only handles segments of about 50m. var distanceDiff = System.Math.Max(System.Math.Abs(distance - expectedDistance) - 200, 0); deviation = Score.New(Score.DISTANCE_COMPARISON, "Compares expected location distance with decoded location distance (1=prefect, 0=difference bigger than total distance)", 1 - System.Math.Min(System.Math.Max(distanceDiff / expectedDistance, 0), 1), 1); } // add deviation-score. candidate.Score = candidate.Score * deviation; if ((candidate.Score.Value / candidate.Score.Reference) > this.MainDecoder.ScoreThreshold) { // check candidate. if (best == null) { // there was no previous candidate or candidate has no route. best = candidate; bestSource = combinedScore.Source; } else if (best.Score.Value < candidate.Score.Value) { // the new candidate is better. best = candidate; bestSource = combinedScore.Source; } else if (best.Score.Value > candidate.Score.Value) { // the current candidate is better. break; } if (best.Score.Value == 1) { // stop search on a perfect scrore! break; } } } } // append the current best. if (best == null || best.Route == null) { // no location reference found between two points. return(null); } // keep the segment. lineLocationSegments.Insert(0, best.Route); // assign new next. target = source; targetCandidates = new SortedSet <CandidateVertexEdge>(); targetCandidates.Add(bestSource); // only the best source can be re-used for the next segment. } // build the line location from the segments. var lineLocation = lineLocationSegments[0]; for (var i = 1; i < lineLocationSegments.Count; i++) { lineLocation.Add(lineLocationSegments[i]); } lineLocation.PositiveOffsetPercentage = location.PositiveOffsetPercentage.Value; lineLocation.NegativeOffsetPercentage = location.NegativeOffsetPercentage.Value; return(lineLocation); }