/// <summary> /// Builds a location referenced point for the last vertex. /// </summary> /// <returns></returns> public LocationReferencePoint BuildLocationReferencePointLast(ReferencedLine referencedLocation, int before) { FormOfWay fow; FunctionalRoadClass frc; var end = referencedLocation.Vertices.Length - 1; // get all coordinates along the sequence starting at 'before' and ending at 'end'. var coordinates = referencedLocation.GetCoordinates(this.Graph, before, end - before + 1); // create location reference point. var locationReferencedPoint = new LocationReferencePoint(); locationReferencedPoint.Coordinate = this.GetVertexLocation(referencedLocation.Vertices[end]); var tags = this.GetTags(referencedLocation.Edges[end - 1].Tags); if (!this.TryMatching(tags, out frc, out fow)) { throw new ReferencedEncodingException(referencedLocation, "Could not find frc and/or fow for the given tags."); } locationReferencedPoint.FormOfWay = fow; locationReferencedPoint.FuntionalRoadClass = frc; locationReferencedPoint.Bearing = (int)BearingEncoder.EncodeBearing(coordinates, true).Value; return(locationReferencedPoint); }
/// <summary> /// Gets a positive offset routerpoint. /// </summary> public static RouterPoint GetOffsetRouterPoint(this ReferencedLine referencedLine, RouterDb routerDb, float positiveOffsetPercentage) { var length = referencedLine.Length(routerDb); var lengthOffset = (positiveOffsetPercentage / 100.0f) * length; var totalLength = 0f; for (var e = 0; e < referencedLine.Edges.Length; e++) { var directedEdgeId = referencedLine.Edges[e]; var edge = routerDb.Network.GetEdge(directedEdgeId); var shape = routerDb.Network.GetShape(edge); if (directedEdgeId < 0) { shape.Reverse(); } var shapeLength = shape.Length(); if (lengthOffset < shapeLength + totalLength) { // offset is in this edge. var relativeOffset = (lengthOffset - totalLength) / shapeLength; var offset = (ushort)(ushort.MaxValue * relativeOffset); if (directedEdgeId < 0) { offset = (ushort)(ushort.MaxValue - offset); } var routerPoint = new RouterPoint(0, 0, edge.Id, offset); var location = routerPoint.LocationOnNetwork(routerDb); return(new RouterPoint(location.Latitude, location.Longitude, edge.Id, offset)); } totalLength += shapeLength; } return(routerDb.CreateRouterPointForEdge(referencedLine.Edges[referencedLine.Edges.Length - 1], false)); }
/// <summary> /// Builds a location referenced point for the last vertex. /// </summary> /// <returns></returns> public static Model.LocationReferencePoint BuildLocationReferencePointLast(this ReferencedLine referencedLocation, Coder coder, int before) { Model.FormOfWay fow; Model.FunctionalRoadClass frc; var end = referencedLocation.Vertices.Length - 1; // get all coordinates along the sequence starting at 'before' and ending at 'end'. var coordinates = referencedLocation.GetCoordinates(coder.Router.Db, before, end - before + 1); // create location reference point. var locationReferencedPoint = new Model.LocationReferencePoint(); locationReferencedPoint.Coordinate = coder.Router.Db.Network.GetVertex(referencedLocation.Vertices[end]).ToCoordinate(); var edgeProfile = coder.Router.Db.EdgeProfiles.Get(coder.Router.Db.Network.GetEdge(referencedLocation.Edges[end - 1]).Data.Profile); if (!coder.Profile.Extract(edgeProfile, out frc, out fow)) { throw new ReferencedEncodingException(referencedLocation, "Could not find frc and/or fow for the given tags."); } locationReferencedPoint.FormOfWay = fow; locationReferencedPoint.FuntionalRoadClass = frc; locationReferencedPoint.Bearing = (int)BearingEncoder.EncodeBearing(coordinates, true); return(locationReferencedPoint); }
/// <summary> /// Converts this line location to a line string. /// </summary> public static LineString ToLineString(this ReferencedLine line, RouterDb routerDb) { // build coordinates list. var coordinates = new List <Coordinate>(); for (int idx = 0; idx < line.Edges.Length; idx++) { var shape = routerDb.Network.GetShape( routerDb.Network.GetEdge(line.Edges[idx])); if (idx > 0) { coordinates.RemoveAt(coordinates.Count - 1); } if (line.Edges[idx] > 0) { coordinates.AddRange(shape.ToCoordinates()); } else { for (var i = shape.Count - 1; i >= 0; i--) { coordinates.Add(shape[i].ToGeoAPICoordinate()); } } } return(new LineString(coordinates.ToArray())); }
/// <summary> /// Builds a path from the given referenced line start and the first referenced point and ending at the last. /// </summary> public static EdgePath <float> BuildPathFromLine(this ReferencedLine referencedLine, RouterDb routerDb, out float postiveOffsetInMeters, out float negativeOffsetInMeters) { RouterPoint source; RouterPoint target; return(referencedLine.BuildPathFromLine(routerDb, out source, out postiveOffsetInMeters, out target, out negativeOffsetInMeters)); }
/// <summary> /// Projects the given coordinates on the referenced line and returns the edge. /// </summary> public static long ProjectOn(this ReferencedLine line, RouterDb routerDb, float latitude, float longitude, out float offsetInMeter) { long edge = Itinero.Constants.NO_EDGE; var bestDistance = float.MaxValue; offsetInMeter = float.MaxValue; for (var j = 0; j < line.Edges.Length; j++) { var shape = routerDb.Network.GetShape(routerDb.Network.GetEdge(line.Edges[j])); if (line.Edges[j] < 0) { shape.Reverse(); } float projectedLatitude; float projectedLongitude; int projectedShapeIndex; float distanceToProjected; float totalLength; float projectedOffsetInMeter; LinePointPosition position; if (!shape.ProjectOn(latitude, longitude, out projectedLatitude, out projectedLongitude, out projectedOffsetInMeter, out projectedShapeIndex, out distanceToProjected, out totalLength, out position)) { // try to find the closest point. distanceToProjected = float.MaxValue; totalLength = 0; for (var i = 0; i < shape.Count; i++) { var distance = Itinero.LocalGeo.Coordinate.DistanceEstimateInMeter(shape[i].Latitude, shape[i].Longitude, latitude, longitude); if (i > 0) { totalLength += Itinero.LocalGeo.Coordinate.DistanceEstimateInMeter(shape[i - 1].Latitude, shape[i - 1].Longitude, shape[i].Latitude, shape[i].Longitude); } if (distance < distanceToProjected) { projectedOffsetInMeter = totalLength; distanceToProjected = distance; projectedShapeIndex = i; position = LinePointPosition.On; projectedLatitude = shape[i].Latitude; projectedLongitude = shape[i].Longitude; } } } if (distanceToProjected < bestDistance) { edge = line.Edges[j]; offsetInMeter = projectedOffsetInMeter; bestDistance = distanceToProjected; } } return(edge); }
public void EncodeReferencedLineLocationNotShortestPath() { var e = 0.00001f; // setup a routing network to test against. var routerDb = new RouterDb(); routerDb.LoadTestNetwork( System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream( "OpenLR.Test.test_data.networks.network3.geojson")); routerDb.Sort(); routerDb.AddSupportedVehicle(Itinero.Osm.Vehicles.Vehicle.Car); // setup test location and data to verify this. var vertex2 = routerDb.Network.GetVertex(2); var vertex3 = routerDb.Network.GetVertex(3); var vertex4 = routerDb.Network.GetVertex(4); var vertex5 = routerDb.Network.GetVertex(5); var vertex6 = routerDb.Network.GetVertex(6); var vertex7 = routerDb.Network.GetVertex(7); var location = new ReferencedLine() { Edges = new long[] { 1, 3, 4, 5, 2 }, Vertices = new uint[] { 7, 4, 3, 2, 5, 6 }, StartLocation = routerDb.CreateRouterPointForVertex(7, routerDb.GetSupportedProfile("car")), EndLocation = routerDb.CreateRouterPointForVertex(6, routerDb.GetSupportedProfile("car")), NegativeOffsetPercentage = 0, PositiveOffsetPercentage = 0 }; var json = location.ToFeatures(routerDb).ToGeoJson(); var length = Itinero.LocalGeo.Coordinate.DistanceEstimateInMeter( new List <Itinero.LocalGeo.Coordinate>(new Itinero.LocalGeo.Coordinate[] { vertex7, vertex4, vertex3, vertex2, vertex5, vertex6 })); // encode and verify result. var encoded = ReferencedLineCodec.Encode(location, new Coder(routerDb, new OsmCoderProfile())); Assert.IsNotNull(encoded.First); Assert.AreEqual(vertex7.Latitude, encoded.First.Coordinate.Latitude, e); Assert.AreEqual(vertex7.Longitude, encoded.First.Coordinate.Longitude, e); Assert.IsTrue(encoded.Intermediate != null && encoded.Intermediate.Length == 1); Assert.AreEqual(vertex6.Latitude, encoded.Last.Coordinate.Latitude, e); Assert.AreEqual(vertex6.Longitude, encoded.Last.Coordinate.Longitude, e); Assert.AreEqual(0, encoded.NegativeOffsetPercentage); Assert.AreEqual(0, encoded.PositiveOffsetPercentage); Assert.AreEqual(FunctionalRoadClass.Frc4, encoded.First.FuntionalRoadClass); Assert.AreEqual(FunctionalRoadClass.Frc4, encoded.First.LowestFunctionalRoadClassToNext); Assert.AreEqual(length, encoded.First.DistanceToNext + encoded.Intermediate[0].DistanceToNext, 1); }
/// <summary> /// Converts the referenced line location to features. /// </summary> public static List <Coordinate> GetCoordinates(this ReferencedLine referencedLine, RouterDb routerDb, int start, int count) { var coordinates = new List <Coordinate>(); if (count <= 0) { return(coordinates); } for (var i = start; i < start + count - 1; i++) { List <Coordinate> shape = null; if (i == 0 && referencedLine.Vertices[0] == Itinero.Constants.NO_VERTEX) { // shape from startlocation -> vertex1. if (referencedLine.Edges.Length == 1) { // only 1 edge, shape from startLocation -> endLocation. shape = referencedLine.StartLocation.ShapePointsTo(routerDb, referencedLine.EndLocation); shape.Insert(0, referencedLine.StartLocation.LocationOnNetwork(routerDb)); shape.Add(referencedLine.EndLocation.LocationOnNetwork(routerDb)); } else { // just get shape to first vertex. shape = referencedLine.StartLocation.ShapePointsTo(routerDb, referencedLine.Vertices[1]); shape.Insert(0, referencedLine.StartLocation.LocationOnNetwork(routerDb)); shape.Add(routerDb.Network.GetVertex(referencedLine.Vertices[1])); } } else if (i == referencedLine.Edges.Length - 1 && referencedLine.Vertices[referencedLine.Vertices.Length - 1] == Itinero.Constants.NO_VERTEX) { // shape from second last vertex -> endlocation. shape = referencedLine.EndLocation.ShapePointsTo(routerDb, referencedLine.Vertices[referencedLine.Vertices.Length - 2]); shape.Reverse(); shape.Insert(0, routerDb.Network.GetVertex(referencedLine.Vertices[referencedLine.Vertices.Length - 2])); shape.Add(referencedLine.EndLocation.LocationOnNetwork(routerDb)); } else { // regular 2 vertices and edge. shape = routerDb.Network.GetShape(routerDb.Network.GetEdge(referencedLine.Edges[i])); if (referencedLine.Edges[i] < 0) { shape.Reverse(); } } if (shape != null) { if (coordinates.Count > 0) { coordinates.RemoveAt(coordinates.Count - 1); } for (var j = 0; j < shape.Count; j++) { coordinates.Add(shape[j]); } } } return(coordinates); }
/// <summary> /// Builds a path from the given referenced line start and the first referenced point and ending at the last. /// </summary> public static EdgePath <float> BuildPathFromLine(this ReferencedLine referencedLine, RouterDb routerDb, out RouterPoint source, out float postiveOffsetInMeters, out RouterPoint target, out float negativeOffsetInMeters) { source = referencedLine.GetPositiveOffsetRouterPoint(routerDb); target = referencedLine.GetNegativeOffsetRouterPoint(routerDb); negativeOffsetInMeters = 0; postiveOffsetInMeters = 0; var started = false; var path = new EdgePath <float>(); for (var e = 0; e < referencedLine.Edges.Length; e++) { var directedEdgeId = referencedLine.Edges[e]; var edge = routerDb.Network.GetEdge(directedEdgeId); var to = edge.To; if (directedEdgeId < 0) { to = edge.From; } if (!started) { if (edge.Id != source.EdgeId) { continue; } negativeOffsetInMeters = (source.Offset / (float)ushort.MaxValue) * edge.Data.Distance; if (directedEdgeId < 0) { negativeOffsetInMeters = edge.Data.Distance - negativeOffsetInMeters; } path = new EdgePath <float>(to, edge.Data.Distance - negativeOffsetInMeters, directedEdgeId, path); started = true; continue; } if (edge.Id == target.EdgeId) { postiveOffsetInMeters = (target.Offset / (float)ushort.MaxValue) * edge.Data.Distance; if (directedEdgeId > 0) { postiveOffsetInMeters = edge.Data.Distance - postiveOffsetInMeters; } path = new EdgePath <float>(Constants.NO_VERTEX, path.Weight + edge.Data.Distance - postiveOffsetInMeters, directedEdgeId, path); break; } else { path = new EdgePath <float>(to, path.Weight + edge.Data.Distance, directedEdgeId, path); } } return(path); }
/// <summary> /// Converts the referenced point along the line location to features. /// </summary> public static float Length(this ReferencedLine referencedLine, RouterDb routerDb) { var length = 0.0f; for (int idx = 0; idx < referencedLine.Edges.Length; idx++) { length = length + routerDb.Network.GetShape( routerDb.Network.GetEdge(referencedLine.Edges[idx])).Length(); } return(length); }
/// <summary> /// Builds a point along line location. /// </summary> /// <returns></returns> public static ReferencedPointAlongLine BuildPointAlongLine(this ReferencedEncoderBase encoder, GeoCoordinate location, Meter maxDistance, double boxSize = 0.01) { if (location == null) { throw new ArgumentNullException("location"); } // get the closest edge that can be traversed to the given location. var closest = encoder.Graph.GetClosestEdge(location, maxDistance, boxSize); if (closest == null) { // no location could be found. throw new BuildLocationFailedException("No network features found near the given location. Make sure the network covers the given location."); } var oneway = encoder.Vehicle.IsOneWay(encoder.Graph.TagsIndex.Get(closest.Item3.Tags)); if (oneway.HasValue && oneway.Value != closest.Item3.Forward) { // when the edge is not traversible in the direct that it's given in, reverse it. var reverseEdge = new LiveEdge(); reverseEdge.Tags = closest.Item3.Tags; reverseEdge.Forward = !closest.Item3.Forward; reverseEdge.Distance = closest.Item3.Distance; closest = new Tuple <long, long, LiveEdge>(closest.Item2, closest.Item1, reverseEdge); } // get locations of edge. var startLocation = encoder.GetVertexLocation(closest.Item1).ToGeoCoordinate(); var endLocation = encoder.GetVertexLocation(closest.Item2).ToGeoCoordinate(); // build a proper referenced line. var referencedLine = new ReferencedLine(encoder.Graph); referencedLine.Vertices = new long[] { closest.Item1, closest.Item2 }; referencedLine.Edges = new LiveEdge[] { closest.Item3 }; referencedLine.PositiveOffsetPercentage = 0; referencedLine.NegativeOffsetPercentage = 0; referencedLine.EdgeShapes = new GeoCoordinateSimple[][] { encoder.Graph.GetEdgeShape( referencedLine.Vertices[0], referencedLine.Vertices[1]) }; // build the point-along-line location. return(new ReferencedPointAlongLine() { Route = referencedLine, Latitude = location.Latitude, Longitude = location.Longitude, Orientation = Model.Orientation.NoOrientation }); }
public void TestReferencedLineGetCoordinates2() { var routerDb = new RouterDb(); routerDb.LoadTestNetwork( System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream( "OpenLR.Test.test_data.networks.network2.geojson")); // build a referenced line 0->1->2->5. var enumerator = routerDb.Network.GetEdgeEnumerator(); enumerator.MoveTo(0); enumerator.MoveNextUntil(x => x.To == 1); var edge01Directed = enumerator.IdDirected(); var edge01 = enumerator.Id; enumerator.MoveTo(1); enumerator.MoveNextUntil(x => x.To == 2); var edge12Directed = enumerator.IdDirected(); var edge12 = enumerator.Id; enumerator.MoveTo(2); enumerator.MoveNextUntil(x => x.To == 5); var edge25Directed = enumerator.IdDirected(); var edge25 = enumerator.Id; var vertex0 = routerDb.Network.GetVertex(0); var vertex1 = routerDb.Network.GetVertex(1); var vertex2 = routerDb.Network.GetVertex(2); var vertex5 = routerDb.Network.GetVertex(5); var line = new ReferencedLine() { Edges = new long[] { edge01Directed, edge12Directed, edge25Directed }, Vertices = new uint[] { 0, 1, 2, 5 }, StartLocation = routerDb.CreateRouterPointForVertex(0, Itinero.Osm.Vehicles.Vehicle.Car.Shortest()), EndLocation = routerDb.CreateRouterPointForVertex(5, Itinero.Osm.Vehicles.Vehicle.Car.Shortest()) }; var shape = line.GetCoordinates(routerDb); Assert.IsNotNull(shape); Assert.AreEqual(4, shape.Count); Assert.AreEqual(vertex0.Latitude, shape[0].Latitude); Assert.AreEqual(vertex0.Longitude, shape[0].Longitude); Assert.AreEqual(vertex1.Latitude, shape[1].Latitude); Assert.AreEqual(vertex1.Longitude, shape[1].Longitude); Assert.AreEqual(vertex2.Latitude, shape[2].Latitude); Assert.AreEqual(vertex2.Longitude, shape[2].Longitude); Assert.AreEqual(vertex5.Latitude, shape[3].Latitude); Assert.AreEqual(vertex5.Longitude, shape[3].Longitude); }
/// <summary> /// Validates if the location is connected. /// </summary> /// <returns></returns> public static void ValidateConnected(this ReferencedLine line, Coder coder) { var profile = coder.Profile; var edges = line.Edges; var vertices = line.Vertices; // 1: Is the path connected? // 2: Is the path traversable? for (int edgeIdx = 0; edgeIdx < edges.Length; edgeIdx++) { var from = vertices[edgeIdx]; var to = vertices[edgeIdx + 1]; // find edge. var found = false; RoutingEdge foundEdge = null; foreach (var edge in coder.Router.Db.Network.GetEdges(from)) { if (edge.To == to && edge.IdDirected() == edges[edgeIdx]) { // edge was found, is valid. found = true; foundEdge = edge; break; } } if (!found) { // edge is not found, path not connected. throw new ArgumentOutOfRangeException(string.Format("Edge {0} cannot be found between vertex {1} and {2}. The given path is not connected.", edges[edgeIdx].ToInvariantString(), from, to)); } // check whether the edge can traversed. var factor = profile.Profile.Factor(coder.Router.Db.EdgeProfiles.Get(foundEdge.Data.Profile)); if (factor.Value == 0) { // oeps, cannot be traversed. throw new ArgumentOutOfRangeException(string.Format("Edge at index {0} cannot be traversed by vehicle {1}.", edgeIdx, profile.Profile.Name)); } // check whether the edge can be traversed in the correct direction. var canMoveForward = (factor.Direction == 0) || (factor.Direction == 1 && !foundEdge.DataInverted) || (factor.Direction == 2 && foundEdge.DataInverted); if (!canMoveForward) { // path cannot be traversed in this direction. throw new ArgumentOutOfRangeException(string.Format("Edge at index {0} cannot be traversed by vehicle {1} in the direction given.", edgeIdx, profile.Profile.Name)); } } }
/// <summary> /// Gets all vertices in one hashset. /// </summary> /// <returns></returns> public static HashSet <uint> GetVerticesSet(this ReferencedLine line) { var set = new HashSet <uint>(); if (line.Vertices == null) { // empty set is ok. return(set); } for (var i = 0; i < line.Vertices.Length; i++) { set.Add(line.Vertices[i]); } return(set); }
/// <summary> /// Gets covered edges, once an edge is covered by more then the given tolerance it's returned. /// </summary> public static IEnumerable <long> GetCoveredEdges(this ReferencedLine line, RouterDb routerDb, float tolerancePercentage = 1f) { if (line.PositiveOffsetPercentage == 0 && line.NegativeOffsetPercentage == 0) { foreach (var e in line.Edges) { yield return(e); } } else { var lengths = new float[line.Edges.Length]; var totalLength = 0f; for (var i = 0; i < line.Edges.Length; i++) { lengths[i] = routerDb.Network.GetEdge(line.Edges[i]).Data.Distance; totalLength += lengths[i]; } var offset = 0f; for (var i = 0; i < line.Edges.Length; i++) { var endOffset = offset + lengths[i]; var startPercentage = (offset / totalLength) * 100; var endPercentage = (endOffset / totalLength) * 100; var startDiff = startPercentage - line.PositiveOffsetPercentage; if (System.Math.Abs(startDiff) < tolerancePercentage) { startDiff = 0; } var endDiff = (100 - endPercentage) - line.NegativeOffsetPercentage; if (System.Math.Abs(endDiff) < tolerancePercentage) { endDiff = 0; } if (startDiff >= 0 && endDiff >= 0) { yield return(line.Edges[i]); } offset = endOffset; } } }
/// <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); } }
/// <summary> /// Builds a location referenced point for the vertex at the given start-index. /// </summary> /// <returns></returns> public LocationReferencePoint BuildLocationReferencePoint(ReferencedLine referencedLocation, int start, int end) { FormOfWay fow; FunctionalRoadClass frc; // get all coordinates along the sequence starting at 'start' and ending at 'end'. var coordinates = referencedLocation.GetCoordinates(this.Graph, start, end - start + 1); // create location reference point. var locationReferencePoint = new LocationReferencePoint(); locationReferencePoint.Coordinate = this.GetVertexLocation(referencedLocation.Vertices[start]); var tags = this.GetTags(referencedLocation.Edges[start].Tags); if (!this.TryMatching(tags, out frc, out fow)) { throw new ReferencedEncodingException(referencedLocation, "Could not find frc and/or fow for the given tags."); } locationReferencePoint.FormOfWay = fow; locationReferencePoint.FuntionalRoadClass = frc; locationReferencePoint.Bearing = (int)BearingEncoder.EncodeBearing(coordinates).Value; locationReferencePoint.DistanceToNext = (int)coordinates.Length().Value; FunctionalRoadClass?lowest = null; for (var edge = start; edge < end; edge++) { tags = this.GetTags(referencedLocation.Edges[edge].Tags); if (!this.TryMatching(tags, out frc, out fow)) { throw new ReferencedEncodingException(referencedLocation, "Could not find frc and/or fow for the given tags."); } if (!lowest.HasValue || frc < lowest) { lowest = frc; } } locationReferencePoint.LowestFunctionalRoadClassToNext = lowest; return(locationReferencePoint); }
/// <summary> /// Builds a location referenced point for the vertex at the given start-index. /// </summary> /// <returns></returns> public static Model.LocationReferencePoint BuildLocationReferencePoint(this ReferencedLine referencedLocation, Coder coder, int start, int end) { Model.FormOfWay fow; Model.FunctionalRoadClass frc; // get all coordinates along the sequence starting at 'start' and ending at 'end'. var coordinates = referencedLocation.GetCoordinates(coder.Router.Db, start, end - start + 1); // create location reference point. var locationReferencePoint = new Model.LocationReferencePoint(); locationReferencePoint.Coordinate = coder.Router.Db.Network.GetVertex(referencedLocation.Vertices[start]).ToCoordinate(); var edgeProfile = coder.Router.Db.EdgeProfiles.Get(coder.Router.Db.Network.GetEdge(referencedLocation.Edges[start]).Data.Profile); if (!coder.Profile.Extract(edgeProfile, out frc, out fow)) { throw new ReferencedEncodingException(referencedLocation, "Could not find frc and/or fow for the given tags."); } locationReferencePoint.FormOfWay = fow; locationReferencePoint.FuntionalRoadClass = frc; locationReferencePoint.Bearing = (int)BearingEncoder.EncodeBearing(coordinates); locationReferencePoint.DistanceToNext = (int)coordinates.Length(); Model.FunctionalRoadClass?lowest = null; for (var edge = start; edge < end; edge++) { edgeProfile = coder.Router.Db.EdgeProfiles.Get(coder.Router.Db.Network.GetEdge(referencedLocation.Edges[edge]).Data.Profile); if (!coder.Profile.Extract(edgeProfile, out frc, out fow)) { throw new ReferencedEncodingException(referencedLocation, "Could not find frc and/or fow for the given tags."); } if (!lowest.HasValue || frc > lowest) { lowest = frc; } } locationReferencePoint.LowestFunctionalRoadClassToNext = lowest; return(locationReferencePoint); }
public void EncodeReferencedLineLocation() { var e = 0.00001f; // setup a routing network to test against. var routerDb = new RouterDb(); routerDb.LoadTestNetwork( System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream( "OpenLR.Test.test_data.networks.network3.geojson")); routerDb.Sort(); routerDb.AddSupportedVehicle(Itinero.Osm.Vehicles.Vehicle.Car); // setup test location and data to verify this. var start = routerDb.Network.GetVertex(7); var end = routerDb.Network.GetVertex(6); var location = new ReferencedLine() { Edges = new long[] { 1, -6, 2 }, Vertices = new uint[] { 7, 4, 5, 6 }, StartLocation = routerDb.CreateRouterPointForVertex(7, routerDb.GetSupportedProfile("car")), EndLocation = routerDb.CreateRouterPointForVertex(6, routerDb.GetSupportedProfile("car")), NegativeOffsetPercentage = 0, PositiveOffsetPercentage = 0 }; // encode and verify result. var encoded = ReferencedLineCodec.Encode(location, new Coder(routerDb, new OsmCoderProfile())); Assert.IsNotNull(encoded.First); Assert.AreEqual(start.Latitude, encoded.First.Coordinate.Latitude, e); Assert.AreEqual(start.Longitude, encoded.First.Coordinate.Longitude, e); Assert.IsTrue(encoded.Intermediate == null || encoded.Intermediate.Length == 0); Assert.AreEqual(end.Latitude, encoded.Last.Coordinate.Latitude, e); Assert.AreEqual(end.Longitude, encoded.Last.Coordinate.Longitude, e); Assert.AreEqual(0, encoded.NegativeOffsetPercentage); Assert.AreEqual(0, encoded.PositiveOffsetPercentage); Assert.AreEqual(FunctionalRoadClass.Frc4, encoded.First.FuntionalRoadClass); Assert.AreEqual(FunctionalRoadClass.Frc4, encoded.First.LowestFunctionalRoadClassToNext); Assert.AreEqual(Itinero.LocalGeo.Coordinate.DistanceEstimateInMeter(start, end), encoded.First.DistanceToNext, 1); }
/// <summary> /// Encodes a line location. /// </summary> /// <param name="lineLocation"></param> /// <returns></returns> public virtual string Encode(ReferencedLine lineLocation) { return(this.GetReferencedLineEncoder().Encode(lineLocation)); }
/// <summary> /// Converts the referenced line location to a list of sorted coordinates. /// </summary> /// <returns></returns> public static List <Coordinate> GetCoordinates(this ReferencedLine route, Coder coder, float offsetRatio, out int offsetEdgeIdx, out Coordinate offsetLocation, out float offsetLength, out float offsetEdgeLength, out float edgeLength) { if (route == null) { throw new ArgumentNullException("route"); } if (route.Edges == null || route.Edges.Length == 0) { throw new ArgumentOutOfRangeException("route", "Route has no edges."); } if (route.Vertices == null || route.Vertices.Length == 0) { throw new ArgumentOutOfRangeException("route", "Route has no vertices."); } if (route.Vertices.Length != route.Edges.Length + 1) { throw new ArgumentOutOfRangeException("route", "Route is invalid: there should be n vertices and n-1 edges."); } // calculate the total length first. var totalLength = route.GetCoordinates(coder.Router.Db).Length(); // calculate the lenght at the offst. offsetLength = (float)(totalLength * offsetRatio); offsetEdgeLength = -1; offsetEdgeIdx = -1; edgeLength = -1; // loop over all coordinates and collect offsetLocation and offsetEdgeIdx. float currentOffsetLength = 0; float currentEdgeLength = 0; var coordinates = new List <Coordinate>(); for (var i = 0; i < route.Edges.Length; i++) { List <Coordinate> shape = null; currentEdgeLength = 0; if (i == 0 && route.Vertices[0] == Itinero.Constants.NO_VERTEX) { // shape from startlocation -> vertex1. if (route.Edges.Length == 1) { // only 1 edge, shape from startLocation -> endLocation. shape = route.StartLocation.ShapePointsTo(coder.Router.Db, route.EndLocation); shape.Insert(0, route.StartLocation.LocationOnNetwork(coder.Router.Db)); shape.Add(route.EndLocation.LocationOnNetwork(coder.Router.Db)); } else { // just get shape to first vertex. shape = route.StartLocation.ShapePointsTo(coder.Router.Db, route.Vertices[1]); shape.Insert(0, route.StartLocation.LocationOnNetwork(coder.Router.Db)); shape.Add(coder.Router.Db.Network.GetVertex(route.Vertices[1])); } } else if (i == route.Edges.Length - 1 && route.Vertices[route.Vertices.Length - 1] == Itinero.Constants.NO_VERTEX) { // shape from second last vertex -> endlocation. shape = route.EndLocation.ShapePointsTo(coder.Router.Db, route.Vertices[route.Vertices.Length - 2]); shape.Reverse(); shape.Insert(0, coder.Router.Db.Network.GetVertex(route.Vertices[route.Vertices.Length - 2])); shape.Add(route.EndLocation.LocationOnNetwork(coder.Router.Db)); } else { // regular 2 vertices and edge. shape = coder.Router.Db.Network.GetShape(coder.Router.Db.Network.GetEdge(route.Edges[i])); if (route.Edges[i] < 0) { shape.Reverse(); } } if (shape != null) { currentEdgeLength = currentEdgeLength + shape.Length(); if (coordinates.Count > 0) { coordinates.RemoveAt(coordinates.Count - 1); } for (var j = 0; j < shape.Count; j++) { coordinates.Add(shape[j]); } } // add current edge length to current offset. if ((currentOffsetLength + currentEdgeLength) >= offsetLength && edgeLength < 0) { // it's this edge that has the valuable info. offsetEdgeIdx = i; offsetEdgeLength = offsetLength - currentOffsetLength; edgeLength = currentEdgeLength; } currentOffsetLength = currentOffsetLength + currentEdgeLength; } // choose the last edge. if (edgeLength < 0) { // it's this edge that has the valuable info. offsetEdgeIdx = route.Edges.Length - 1; offsetEdgeLength = offsetLength - currentOffsetLength; edgeLength = currentEdgeLength; } // calculate actual offset position. offsetLocation = coordinates.GetPositionLocation(offsetRatio); return(coordinates); }
/// <summary> /// Gets the negative offset location. /// </summary> public static Coordinate GetNegativeOffsetLocation(this ReferencedLine referencedLine, RouterDb routerDb) { var coordinates = referencedLine.GetCoordinates(routerDb); return(coordinates.GetPositionLocation(1f - (referencedLine.NegativeOffsetPercentage / 100.0f))); }
/// <summary> /// Encodes a referenced line location into an unreferenced location. /// </summary> /// <param name="lineLocation"></param> /// <returns></returns> public virtual LineLocation EncodeReferenced(ReferencedLine lineLocation) { return(this.GetReferencedLineEncoder().EncodeReferenced(lineLocation)); }
/// <summary> /// Converts the referenced line location to features. /// </summary> public static List <Coordinate> GetCoordinates(this ReferencedLine referencedLine, RouterDb routerDb) { return(referencedLine.GetCoordinates(routerDb, 0, referencedLine.Vertices.Length)); }
/// <summary> /// Gets the negative offset routerpoint. /// </summary> public static RouterPoint GetNegativeOffsetRouterPoint(this ReferencedLine referencedLine, RouterDb routerDb) { return(referencedLine.GetOffsetRouterPoint(routerDb, 100 - referencedLine.NegativeOffsetPercentage)); }
/// <summary> /// Validates the offsets. /// </summary> public static void ValidateOffsets(this ReferencedLine line) { }
/// <summary> /// Validates the location for encoding in binary format. /// </summary> public static void ValidateBinary(this ReferencedLine line) { }
/// <summary> /// Projects the given coordinates on the referenced line and returns the edge. /// </summary> public static long ProjectOn(this ReferencedLine line, RouterDb routerDb, float latitude, float longitude) { var offsetInMeter = float.MaxValue; return(line.ProjectOn(routerDb, latitude, longitude, out offsetInMeter)); }
/// <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; }
/// <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); }