/// <summary> /// Finds all candidate vertex/edge pairs for a given location reference point. /// </summary> public static Itinero.Algorithms.Collections.SortedSet <CandidatePathSegment> FindCandidatesFor(this Coder coder, LocationReferencePoint lrp, bool forward, float maxVertexDistance = 40) { var vertexEdgeCandidates = new Itinero.Algorithms.Collections.SortedSet <CandidatePathSegment>(new CandidateVertexEdgeComparer()); var vertexCandidates = coder.FindCandidateLocationsFor(lrp, maxVertexDistance); foreach (var vertexCandidate in vertexCandidates) { var edgeCandidates = coder.FindCandidatePathSegmentsFor(vertexCandidate, forward, lrp.FormOfWay.Value, lrp.FuntionalRoadClass.Value, lrp.Bearing.Value); foreach (var edgeCandidate in edgeCandidates) { vertexEdgeCandidates.Add(edgeCandidate); } } return(vertexEdgeCandidates); }
/// <summary> /// Decodes the given location. /// </summary> public static ReferencedPointAlongLine Decode(PointAlongLineLocation location, Coder coder) { CandidateRoute best = null; CombinedScore bestCombinedEdge = null; // get candidate vertices and edges. var candidates = new List <Itinero.Algorithms.Collections.SortedSet <CandidatePathSegment> >(); var lrps = new List <LocationReferencePoint>(); var fromBearingReference = location.First.Bearing; var toBearingReference = location.Last.Bearing; // loop over all lrps. lrps.Add(location.First); var firstCandidates = coder.FindCandidatesFor(location.First, true); candidates.Add(firstCandidates); var lastCandidates = coder.FindCandidatesFor(location.Last, false); candidates.Add(lastCandidates); // build a list of combined scores. var combinedScoresSet = new Itinero.Algorithms.Collections.SortedSet <CombinedScore>(new CombinedScoreComparer()); foreach (var previousCandidate in candidates[0]) { foreach (var currentCandidate in candidates[1]) { if (previousCandidate.Location.EdgeId != currentCandidate.Location.EdgeId || previousCandidate.Location.Offset != currentCandidate.Location.Offset) { // make sure vertices are different. combinedScoresSet.Add(new CombinedScore() { Source = previousCandidate, Target = currentCandidate }); } } } // find the best candidate route. var combinedScores = new List <CombinedScore>(combinedScoresSet); while (combinedScores.Count > 0) { // get the first pair. var combinedScore = combinedScores.First(); combinedScores.Remove(combinedScore); // find a route. var candidate = coder.FindCandidateRoute(combinedScore.Source, combinedScore.Target, lrps[0].LowestFunctionalRoadClassToNext.Value); // 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 = location.First.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 > Parameters.DONT_CARE_DISTANCE || distance > Parameters.DONT_CARE_DISTANCE) { // non-perfect score. // don't care about difference smaller than Parameters.DONT_CARE_DISTANCE, the binary encoding only handles segments of about 50m. var distanceDiff = System.Math.Max(System.Math.Abs(distance - expectedDistance) - Parameters.DONT_CARE_DISTANCE, 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) { // ok, candidate is good enough. // check candidate. if (best == null) { // there was no previous candidate. best = candidate; bestCombinedEdge = combinedScore; } else if (best.Score.Value < candidate.Score.Value) { // the new candidate is better. best = candidate; bestCombinedEdge = combinedScore; } else if (best.Score.Value > candidate.Score.Value) { // the current candidate is better. break; } } } } // check if a location was found or not. if (best == null || best.Route == null) { // no location could be found. throw new ReferencedDecodingException(location, "No valid location was found."); } // calculate total score. var totalScore = bestCombinedEdge.Score + best.Score; // calculate the percentage value. var offsetRatio = 0.0f; if (location.PositiveOffsetPercentage.HasValue) { // there is a percentage set. offsetRatio = location.PositiveOffsetPercentage.Value / 100.0f; } // calculate the actual location and take into account the shape. int offsetEdgeIdx; Itinero.LocalGeo.Coordinate offsetLocation; float offsetLength; float offsetEdgeLength; float edgeLength; var coordinates = best.Route.GetCoordinates(coder, offsetRatio, out offsetEdgeIdx, out offsetLocation, out offsetLength, out offsetEdgeLength, out edgeLength); var longitudeReference = offsetLocation.Longitude; var latitudeReference = offsetLocation.Latitude; // create the referenced location. var pointAlongLineLocation = new ReferencedPointAlongLine(); pointAlongLineLocation.Score = totalScore; pointAlongLineLocation.Route = best.Route; pointAlongLineLocation.Latitude = latitudeReference; pointAlongLineLocation.Longitude = longitudeReference; if (location.Orientation.HasValue) { pointAlongLineLocation.Orientation = location.Orientation.Value; } else { pointAlongLineLocation.Orientation = Orientation.NoOrientation; } // add the edge meta data. pointAlongLineLocation.EdgeMeta = new EdgeMeta() { Idx = offsetEdgeIdx, Length = edgeLength, Offset = offsetEdgeLength }; return(pointAlongLineLocation); }
/// <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); }