/// <summary> /// Converts this candidate to a feature collection. /// </summary> public static FeatureCollection ToFeatures(this CandidatePathSegment candidate, RouterDb routerDb) { var features = new FeatureCollection(); features.Add(candidate.Location.ToFeature(routerDb)); return(features); }
/// <summary> /// Calculates a route between the two given vertices. /// </summary> public static CandidateRoute FindCandidateRoute(this Coder coder, CandidatePathSegment from, CandidatePathSegment to, FunctionalRoadClass minimum, bool invertTargetEdge = true) { var weightHandler = coder.Profile.Profile.DefaultWeightHandler(coder.Router); var directedEdgeFrom = from.Path.Edge; var directedEdgeTo = -to.Path.Edge; if (!invertTargetEdge) { directedEdgeTo = -directedEdgeTo; } // TODO: probably the bug is one-edge routes. var path = coder.Router.TryCalculateRaw(coder.Profile.Profile, weightHandler, directedEdgeFrom, directedEdgeTo, coder.Profile.RoutingSettings); if (Itinero.LocalGeo.Coordinate.DistanceEstimateInMeter(from.Location.Location(), to.Location.Location()) > coder.Profile.MaxSearch / 2) { try { coder.Profile.RoutingSettings.SetMaxSearch(coder.Profile.Profile.FullName, coder.Profile.MaxSearch * 4); path = coder.Router.TryCalculateRaw(coder.Profile.Profile, weightHandler, directedEdgeFrom, directedEdgeTo, coder.Profile.RoutingSettings); } catch { throw; } finally { coder.Profile.RoutingSettings.SetMaxSearch(coder.Profile.Profile.FullName, coder.Profile.MaxSearch); } } // if no route is found, score is 0. if (path.IsError) { return(new CandidateRoute() { Route = null, Score = Score.New(Score.CANDIDATE_ROUTE, "Candidate route quality.", 0, 1) }); } var pathAsList = path.Value.ToList(); var edges = new List <long>(); var vertices = new List <uint>(); for (var i = 0; i < pathAsList.Count; i++) { vertices.Add(pathAsList[i].Vertex); if (i > 0) { if (pathAsList[i].Edge != Itinero.Constants.NO_EDGE && pathAsList[i].Edge != -Itinero.Constants.NO_EDGE) { edges.Add(pathAsList[i].Edge); } else { var edgeEnumerator = coder.Router.Db.Network.GeometricGraph.Graph.GetEdgeEnumerator(); float best; var edge = edgeEnumerator.FindBestEdge(weightHandler, vertices[vertices.Count - 2], vertices[vertices.Count - 1], out best); edges.Add(edge); } } } var startLocation = from.Location; if (!from.Location.IsVertex()) { // first vertex is virtual. vertices[0] = Itinero.Constants.NO_VERTEX; } else { // make sure routerpoint has same edge. startLocation = coder.Router.Db.CreateRouterPointForEdgeAndVertex(edges[0], vertices[0]); } var endLocation = to.Location; if (!to.Location.IsVertex()) { // last vertex is virtual. vertices[vertices.Count - 1] = Itinero.Constants.NO_VERTEX; } else { // make sure routerpoint has the same edge. endLocation = coder.Router.Db.CreateRouterPointForEdgeAndVertex(edges[edges.Count - 1], vertices[vertices.Count - 1]); } return(new CandidateRoute() { Route = new ReferencedLine() { Edges = edges.ToArray(), Vertices = vertices.ToArray(), StartLocation = startLocation, EndLocation = endLocation }, Score = Score.New(Score.CANDIDATE_ROUTE, "Candidate route quality.", 1, 1) }); }
/// <summary> /// Decodes the given location. /// </summary> public static ReferencedLine Decode(LineLocation location, Coder coder) { // get candidate vertices and edges. var candidates = new List <Itinero.Algorithms.Collections.SortedSet <CandidatePathSegment> >(); var lrps = new List <LocationReferencePoint>(); // loop over all lrps. lrps.Add(location.First); candidates.Add(coder.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(coder.FindCandidatesFor(location.Intermediate[idx], true)); } } lrps.Add(location.Last); candidates.Add(coder.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 Itinero.Algorithms.Collections.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; CandidatePathSegment bestSource = null; var targetIsLast = idx == lrps.Count - 2; while (combinedScores.Count > 0) { // get the first pair. var combinedScore = combinedScores[0]; combinedScores.RemoveAt(0); // find a route. var candidate = coder.FindCandidateRoute(combinedScore.Source, combinedScore.Target, source.LowestFunctionalRoadClassToNext.Value, targetIsLast); // 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(coder.Router.Db).Length(); var expectedDistance = source.DistanceToNext; // default a perfect score, only compare large distances. var 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) > coder.Profile.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); } if (!targetIsLast) { // strip last edge and vertex, these will overlap with the previous. best.Route.Vertices = best.Route.Vertices.Range(0, best.Route.Vertices.Length - 1); best.Route.Edges = best.Route.Edges.Range(0, best.Route.Edges.Length - 1); } // keep the segment. lineLocationSegments.Insert(0, best.Route); // assign new next. target = source; targetCandidates = new Itinero.Algorithms.Collections.SortedSet <CandidatePathSegment>(); 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 == null) ? 0 : location.PositiveOffsetPercentage.Value; lineLocation.NegativeOffsetPercentage = (location.NegativeOffsetPercentage == null) ? 0 : location.NegativeOffsetPercentage.Value; return(lineLocation); }