/// <summary> /// Encodes a point along line location. /// </summary> public static byte[] Encode(PointAlongLineLocation location) { var data = new byte[17]; var header = new Header(); header.Version = 3; header.HasAttributes = true; header.ArF0 = false; header.IsPoint = true; header.ArF1 = false; HeaderConvertor.Encode(data, 0, header); CoordinateConverter.Encode(location.First.Coordinate, data, 1); FunctionalRoadClassConvertor.Encode(location.First.FuntionalRoadClass.Value, data, 7, 2); FormOfWayConvertor.Encode(location.First.FormOfWay.Value, data, 7, 5); FunctionalRoadClassConvertor.Encode(location.First.LowestFunctionalRoadClassToNext.Value, data, 8, 0); BearingConvertor.Encode(BearingConvertor.EncodeAngleToBearing(location.First.Bearing.Value), data, 8, 3); data[9] = DistanceToNextConvertor.Encode(location.First.DistanceToNext); CoordinateConverter.EncodeRelative(location.First.Coordinate, location.Last.Coordinate, data, 10); FunctionalRoadClassConvertor.Encode(location.Last.FuntionalRoadClass.Value, data, 14, 2); FormOfWayConvertor.Encode(location.Last.FormOfWay.Value, data, 14, 5); BearingConvertor.Encode(BearingConvertor.EncodeAngleToBearing(location.Last.Bearing.Value), data, 15, 3); OrientationConverter.Encode(location.Orientation.Value, data, 7, 0); SideOfRoadConverter.Encode(location.SideOfRoad.Value, data, 14, 0); if (location.PositiveOffsetPercentage.HasValue) { // positive offset percentage is present. OffsetConvertor.Encode(location.PositiveOffsetPercentage.Value, data, 16); } return(data); }
/// <summary> /// Decodes the given data into a location reference. /// </summary> public static PointAlongLineLocation Decode(byte[] data) { var pointAlongLine = new PointAlongLineLocation(); // decode first location reference point. var first = new LocationReferencePoint(); first.Coordinate = CoordinateConverter.Decode(data, 1); first.FuntionalRoadClass = FunctionalRoadClassConvertor.Decode(data, 7, 2); first.FormOfWay = FormOfWayConvertor.Decode(data, 7, 5); first.LowestFunctionalRoadClassToNext = FunctionalRoadClassConvertor.Decode(data, 8, 0); first.Bearing = BearingConvertor.DecodeAngleFromBearing(BearingConvertor.Decode(data, 8, 3)); first.DistanceToNext = DistanceToNextConvertor.Decode(data[9]); // decode second location reference point. var last = new LocationReferencePoint(); last.Coordinate = CoordinateConverter.DecodeRelative(first.Coordinate, data, 10); last.FuntionalRoadClass = FunctionalRoadClassConvertor.Decode(data, 14, 2); last.FormOfWay = FormOfWayConvertor.Decode(data, 14, 5); last.Bearing = BearingConvertor.DecodeAngleFromBearing(BearingConvertor.Decode(data, 15, 3)); pointAlongLine.First = first; pointAlongLine.Orientation = OrientationConverter.Decode(data, 7, 0); pointAlongLine.SideOfRoad = SideOfRoadConverter.Decode(data, 14, 0); pointAlongLine.PositiveOffsetPercentage = OffsetConvertor.Decode(data, 16); pointAlongLine.Last = last; return(pointAlongLine); }
public void EncodeBase64Test() { double delta = 0.0001; // create a location. var location = new PointAlongLineLocation(); location.First = new LocationReferencePoint(); location.First.Coordinate = new Coordinate() { Latitude = 49.60597, Longitude = 6.12829 }; location.First.FuntionalRoadClass = FunctionalRoadClass.Frc2; location.First.FormOfWay = FormOfWay.MultipleCarriageWay; location.First.LowestFunctionalRoadClassToNext = FunctionalRoadClass.Frc2; location.First.Bearing = 17; location.First.DistanceToNext = (int)(58.6 * 2) + 1; location.Last = new LocationReferencePoint(); location.Last.Coordinate = new Coordinate() { Latitude = 49.60521, Longitude = 6.12779 }; location.Last.FuntionalRoadClass = FunctionalRoadClass.Frc2; location.Last.FormOfWay = FormOfWay.MultipleCarriageWay; location.Last.Bearing = 3; location.Last.DistanceToNext = 0; location.Orientation = Orientation.NoOrientation; location.SideOfRoad = SideOfRoad.Left; location.PositiveOffsetPercentage = 30.19f; // encode. var encoder = new PointAlongLineEncoder(); var stringData = encoder.Encode(location); // decode again (decoding was tested above). var decoder = new PointAlongLineDecoder(); var decodedLocation = decoder.Decode(stringData); Assert.IsNotNull(decodedLocation); Assert.IsInstanceOf <PointAlongLineLocation>(decodedLocation); var pointAlongLineLocation = (decodedLocation as PointAlongLineLocation); // check first reference. Assert.IsNotNull(pointAlongLineLocation.First); Assert.AreEqual(location.First.Coordinate.Longitude, pointAlongLineLocation.First.Coordinate.Longitude, delta); // 6.12829° Assert.AreEqual(location.First.Coordinate.Latitude, pointAlongLineLocation.First.Coordinate.Latitude, delta); // 49.60597° Assert.AreEqual(location.First.FuntionalRoadClass, pointAlongLineLocation.First.FuntionalRoadClass); Assert.AreEqual(location.First.FormOfWay, pointAlongLineLocation.First.FormOfWay); Assert.AreEqual(location.First.LowestFunctionalRoadClassToNext, pointAlongLineLocation.First.LowestFunctionalRoadClassToNext); Assert.AreEqual(location.First.Bearing.Value, pointAlongLineLocation.First.Bearing.Value, 11.25); // binary encode loses accuracy for bearing. Assert.AreEqual(location.First.DistanceToNext, pointAlongLineLocation.First.DistanceToNext); // check second reference. Assert.IsNotNull(pointAlongLineLocation.Last); Assert.AreEqual(location.Last.Coordinate.Longitude, pointAlongLineLocation.Last.Coordinate.Longitude, delta); // 6.12779° Assert.AreEqual(location.Last.Coordinate.Latitude, pointAlongLineLocation.Last.Coordinate.Latitude, delta); // 49.60521° Assert.AreEqual(location.Last.FuntionalRoadClass, pointAlongLineLocation.Last.FuntionalRoadClass); Assert.AreEqual(location.Last.FormOfWay, pointAlongLineLocation.Last.FormOfWay); Assert.AreEqual(location.Last.Bearing.Value, pointAlongLineLocation.Last.Bearing.Value, 11.25); // binary encode loses accuracy for bearing. Assert.AreEqual(location.Last.DistanceToNext, pointAlongLineLocation.Last.DistanceToNext); // check other properties. Assert.AreEqual(location.Orientation, pointAlongLineLocation.Orientation); Assert.AreEqual(location.SideOfRoad, pointAlongLineLocation.SideOfRoad); Assert.AreEqual(location.PositiveOffsetPercentage.Value, pointAlongLineLocation.PositiveOffsetPercentage.Value, 0.5f); // binary encode loses accuracy. // compare again with reference encoded string. var referenceStringData = "KwRbnyNGfhJBAv/P/7WSAEw="; var referenceDecodedLocation = decoder.Decode(referenceStringData); var referenceBinary = System.Convert.FromBase64String(referenceStringData); var encodedBinary = System.Convert.FromBase64String(stringData); // check first reference. Assert.IsNotNull(pointAlongLineLocation.First); Assert.AreEqual(referenceDecodedLocation.First.Coordinate.Longitude, pointAlongLineLocation.First.Coordinate.Longitude, delta); // 6.12829° Assert.AreEqual(referenceDecodedLocation.First.Coordinate.Latitude, pointAlongLineLocation.First.Coordinate.Latitude, delta); // 49.60597° Assert.AreEqual(referenceDecodedLocation.First.FuntionalRoadClass, pointAlongLineLocation.First.FuntionalRoadClass); Assert.AreEqual(referenceDecodedLocation.First.FormOfWay, pointAlongLineLocation.First.FormOfWay); Assert.AreEqual(referenceDecodedLocation.First.LowestFunctionalRoadClassToNext, pointAlongLineLocation.First.LowestFunctionalRoadClassToNext); Assert.AreEqual(referenceDecodedLocation.First.Bearing.Value, pointAlongLineLocation.First.Bearing.Value, 11.25); // binary encode loses accuracy for bearing. // check second reference. Assert.IsNotNull(pointAlongLineLocation.Last); Assert.AreEqual(referenceDecodedLocation.Last.Coordinate.Longitude, pointAlongLineLocation.Last.Coordinate.Longitude, delta); // 6.12779° Assert.AreEqual(referenceDecodedLocation.Last.Coordinate.Latitude, pointAlongLineLocation.Last.Coordinate.Latitude, delta); // 49.60521° Assert.AreEqual(referenceDecodedLocation.Last.FuntionalRoadClass, pointAlongLineLocation.Last.FuntionalRoadClass); Assert.AreEqual(referenceDecodedLocation.Last.FormOfWay, pointAlongLineLocation.Last.FormOfWay); Assert.AreEqual(referenceDecodedLocation.Last.Bearing.Value, pointAlongLineLocation.Last.Bearing.Value, 11.25); // binary encode loses accuracy for bearing. // check other properties. Assert.AreEqual(referenceDecodedLocation.Orientation, pointAlongLineLocation.Orientation); Assert.AreEqual(referenceDecodedLocation.SideOfRoad, pointAlongLineLocation.SideOfRoad); Assert.AreEqual(referenceDecodedLocation.PositiveOffsetPercentage.Value, pointAlongLineLocation.PositiveOffsetPercentage.Value, 0.5f); // binary encode loses accuracy. }
public void DecodeReferencedPointAlongLineLocation() { double delta = 0.0001; // build the location to decode. var location = new PointAlongLineLocation(); location.First = new LocationReferencePoint(); location.First.Coordinate = new Coordinate() { Latitude = 49.60597, Longitude = 6.12829 }; location.First.DistanceToNext = 92; location.First.FuntionalRoadClass = FunctionalRoadClass.Frc2; location.First.FormOfWay = FormOfWay.MultipleCarriageWay; location.First.LowestFunctionalRoadClassToNext = FunctionalRoadClass.Frc2; location.First.Bearing = 203; location.Last = new LocationReferencePoint(); location.Last.Coordinate = new Coordinate() { Latitude = 49.60521, Longitude = 6.12779 }; location.Last.DistanceToNext = 10; location.Last.FuntionalRoadClass = FunctionalRoadClass.Frc2; location.Last.FormOfWay = FormOfWay.MultipleCarriageWay; location.Last.Bearing = 23; location.PositiveOffsetPercentage = (float)((28.0 / 92.0) * 100.0); location.Orientation = Orientation.FirstToSecond; location.SideOfRoad = SideOfRoad.Left; // build a graph to decode onto. var tags = new TagsTableCollectionIndex(); var graphDataSource = new DynamicGraphRouterDataSource <LiveEdge>(tags); var vertex1 = graphDataSource.AddVertex(49.60597f, 6.12829f); var vertex2 = graphDataSource.AddVertex(49.60521f, 6.12779f); graphDataSource.AddEdge(vertex1, vertex2, new LiveEdge() { Distance = 10, Forward = true, Tags = tags.Add(new TagsCollection(Tag.Create("highway", "tertiary"))) }, null); graphDataSource.AddEdge(vertex2, vertex1, new LiveEdge() { Distance = 10, Forward = true, Tags = tags.Add(new TagsCollection(Tag.Create("highway", "tertiary"))) }, null); // decode the location var graph = new BasicRouterDataSource <LiveEdge>(graphDataSource); var decoder = new PointAlongLineDecoder(); var router = new BasicRouter(); var mainDecoder = new ReferencedOsmDecoder(graph, new BinaryDecoder()); var referencedDecoder = new ReferencedPointAlongLineDecoder(mainDecoder, decoder); var referencedLocation = referencedDecoder.Decode(location); // confirm result. Assert.IsNotNull(referencedLocation); Assert.IsNotNull(referencedLocation.Route); Assert.IsNotNull(referencedLocation.Route.Edges); Assert.AreEqual(vertex1, referencedLocation.Route.Vertices[0]); Assert.AreEqual(vertex2, referencedLocation.Route.Vertices[1]); var longitudeReference = (location.Last.Coordinate.Longitude - location.First.Coordinate.Longitude) * (location.PositiveOffsetPercentage.Value / 100.0) + location.First.Coordinate.Longitude; var latitudeReference = (location.Last.Coordinate.Latitude - location.First.Coordinate.Latitude) * (location.PositiveOffsetPercentage.Value / 100.0) + location.First.Coordinate.Latitude; Assert.AreEqual(longitudeReference, referencedLocation.Longitude, delta); Assert.AreEqual(latitudeReference, referencedLocation.Latitude, delta); Assert.AreEqual(Orientation.FirstToSecond, referencedLocation.Orientation); }
/// <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> /// Encodes the given location. /// </summary> public static PointAlongLineLocation Encode(ReferencedPointAlongLine referencedLocation, Coder coder) { try { // Step – 1: Check validity of the location and offsets to be encoded. // validate connected and traversal. referencedLocation.Route.ValidateConnected(coder); // validate offsets. referencedLocation.Route.ValidateOffsets(); // validate for binary. referencedLocation.Route.ValidateBinary(); // Step – 2 Adjust start and end node of the location to represent valid map nodes. referencedLocation.Route.AdjustToValidPoints(coder); // keep a list of LR-point. var points = new List <int>(new int[] { 0, referencedLocation.Route.Vertices.Length - 1 }); // Step – 3 Determine coverage of the location by a shortest-path. // Step – 4 Check whether the calculated shortest-path covers the location completely. // Go to step 5 if the location is not covered completely, go to step 7 if the location is covered. // Step – 5 Determine the position of a new intermediate location reference point so that the part of the // location between the start of the shortest-path calculation and the new intermediate is covered // completely by a shortest-path. // Step – 6 Go to step 3 and restart shortest path calculation between the new intermediate location reference // point and the end of the location. // Step – 7 Concatenate the calculated shortest-paths for a complete coverage of the location and form an // ordered list of location reference points (from the start to the end of the location). // Step – 8 Check validity of the location reference path. If the location reference path is invalid then go // to step 9, if the location reference path is valid then go to step 10. // Step – 9 Add a sufficient number of additional intermediate location reference points if the distance // between two location reference points exceeds the maximum distance. Remove the start/end LR-point // if the positive/negative offset value exceeds the length of the corresponding path. // WARNING: the OpenLR-spec says that there cannot be intermediate points on an PointAlongLineLocation. // this means that if the route found here is > 15.000m it cannot be encoding. // assumed is that the OpenLR-spec assumes that this will never happen (?) // referencedLocation.Route.AdjustToValidDistances(this.MainEncoder, points); // Step – 10 Create physical representation of the location reference. var coordinates = referencedLocation.Route.GetCoordinates(coder.Router.Db); var lengthInMeter = coordinates.Length(); var location = new PointAlongLineLocation(); location.First = referencedLocation.Route.BuildLocationReferencePoint( coder, 0, referencedLocation.Route.Vertices.Length - 1); location.Last = referencedLocation.Route.BuildLocationReferencePointLast( coder, 0); // calculate orientation and side of road. float projectedLatitude; float projectedLongitude; float projectedDistanceFromFirst; int projectedShapeIndex; float distanceToProjected; float totalLength; LinePointPosition position; if (!coordinates.ProjectOn(referencedLocation.Latitude, referencedLocation.Longitude, out projectedLatitude, out projectedLongitude, out projectedDistanceFromFirst, out projectedShapeIndex, out distanceToProjected, out totalLength, out position)) { // the projection on the edge failed. // try to find the closest point. distanceToProjected = float.MaxValue; totalLength = 0; for (var i = 0; i < coordinates.Count; i++) { var distance = Itinero.LocalGeo.Coordinate.DistanceEstimateInMeter(coordinates[i].Latitude, coordinates[i].Longitude, referencedLocation.Latitude, referencedLocation.Longitude); if (i > 0) { totalLength += Itinero.LocalGeo.Coordinate.DistanceEstimateInMeter(coordinates[i - 1].Latitude, coordinates[i - 1].Longitude, coordinates[i].Latitude, coordinates[i].Longitude); } if (distance < distanceToProjected) { projectedDistanceFromFirst = totalLength; distanceToProjected = distance; projectedShapeIndex = i; position = LinePointPosition.On; projectedLatitude = coordinates[i].Latitude; projectedLongitude = coordinates[i].Longitude; } } } if (distanceToProjected > MaxDistanceFromProjected) { throw new ReferencedEncodingException(referencedLocation, string.Format("The point in the ReferencedPointAlongLine is too far from the referenced edge: {0}m with a max allowed of {1}m.", distanceToProjected, MaxDistanceFromProjected)); } location.Orientation = referencedLocation.Orientation; switch (position) { case LinePointPosition.Left: location.SideOfRoad = SideOfRoad.Left; break; case LinePointPosition.On: location.SideOfRoad = SideOfRoad.OnOrAbove; break; case LinePointPosition.Right: location.SideOfRoad = SideOfRoad.Right; break; } // calculate offset. location.PositiveOffsetPercentage = (float)(projectedDistanceFromFirst / lengthInMeter) * 100.0f; if (location.PositiveOffsetPercentage >= 100) { // should be in the range of [0-100[. // encoding should always work even if not 100% accurate in this case. location.PositiveOffsetPercentage = 99; } return(location); } catch (ReferencedEncodingException) { // rethrow referenced encoding exception. throw; } catch (Exception ex) { // unhandled exception! throw new ReferencedEncodingException(referencedLocation, string.Format("Unhandled exception during ReferencedPointAlongLineEncoder: {0}", ex.ToString()), ex); } }