/// <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> /// Returns true if the sequence vertex1->vertex2 occurs on the shortest path between from and to. /// </summary> /// <returns></returns> public static bool IsOnShortestPath(this Coder coder, uint from, uint to, uint vertex1, uint vertex2) { var path = coder.FindShortestPath(from, to, true).ToListAsVertices(); for (var i = 1; i < path.Count; i++) { if (path[i - 1] == vertex1 && path[i] == vertex2) { return(true); } } return(false); }
/// <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 the shortest path along all the given coordinates as a referenced line. /// </summary> public static ReferencedLine BuildLine(this Coder coder, Itinero.LocalGeo.Coordinate[] coordinates) { var edgesTotal = new List <long>(); var verticesTotal = new List <uint>(); float pathDistance = 0; var sourceOffset = 0f; var targetOffset = 0f; var totalDistance = 0f; for (int w = 1; w < coordinates.Length; w++) { var coordinate1 = coordinates[w - 1]; var coordinate2 = coordinates[w]; // calculate raw path. var weightHandler = coder.Router.GetDefaultWeightHandler(coder.Profile.Profile); var source = coder.Router.Resolve(coder.Profile.Profile, coordinate1, 100); var target = coder.Router.Resolve(coder.Profile.Profile, coordinate2, 100); var path = coder.Router.TryCalculateRaw(coder.Profile.Profile, weightHandler, source, target, coder.Profile.RoutingSettings); if (path.IsError) { throw new InvalidOperationException("No route found."); } // build route. var route = coder.Router.BuildRoute(coder.Profile.Profile, weightHandler, source, target, path.Value).Value; //pathDistance += path.Value.Weight; pathDistance += route.TotalDistance; // build referenced line by building vertices and edge list. 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); } } } if (vertices[0] == Constants.NO_VERTEX) { var edge = coder.Router.Db.Network.GetEdge(edges[0]); if (edge.From == vertices[1]) { vertices[0] = edge.To; } else if (edge.To == vertices[1]) { vertices[0] = edge.From; } else { throw new Exception("First edge does not match first vertex."); } } if (vertices[vertices.Count - 1] == Constants.NO_VERTEX) { var edge = coder.Router.Db.Network.GetEdge(edges[edges.Count - 1]); if (edge.From == vertices[vertices.Count - 2]) { vertices[vertices.Count - 1] = edge.To; } else if (edge.To == vertices[vertices.Count - 2]) { vertices[vertices.Count - 1] = edge.From; } else { throw new Exception("Last edge does not match last vertex."); } } if (edgesTotal.Count > 0) { edges.RemoveAt(0); } edgesTotal.AddRange(edges); if (verticesTotal.Count > 0) { vertices.RemoveAt(0); vertices.RemoveAt(0); } verticesTotal.AddRange(vertices); } var edge_ = coder.Router.Db.Network.GetEdge(edgesTotal[0]); if (edge_.From == verticesTotal[1]) { sourceOffset = Itinero.LocalGeo.Coordinate.DistanceEstimateInMeter(coordinates[0], coder.Router.Db.Network.GetVertex(edge_.To)); } else if (edge_.To == verticesTotal[1]) { sourceOffset = Itinero.LocalGeo.Coordinate.DistanceEstimateInMeter(coordinates[0], coder.Router.Db.Network.GetVertex(edge_.From)); } else { throw new Exception("First edge does not match first vertex."); } edge_ = coder.Router.Db.Network.GetEdge(edgesTotal[edgesTotal.Count - 1]); if (edge_.From == verticesTotal[verticesTotal.Count - 2]) { targetOffset = Itinero.LocalGeo.Coordinate.DistanceEstimateInMeter(coordinates[coordinates.Length - 1], coder.Router.Db.Network.GetVertex(edge_.To)); } else if (edge_.To == verticesTotal[verticesTotal.Count - 2]) { targetOffset = Itinero.LocalGeo.Coordinate.DistanceEstimateInMeter(coordinates[coordinates.Length - 1], coder.Router.Db.Network.GetVertex(edge_.From)); } else { throw new Exception("Last edge does not match last vertex."); } totalDistance = pathDistance + sourceOffset + targetOffset; return(new ReferencedLine() { Edges = edgesTotal.ToArray(), Vertices = verticesTotal.ToArray(), NegativeOffsetPercentage = 100.0f * (targetOffset / totalDistance), PositiveOffsetPercentage = 100.0f * (sourceOffset / totalDistance), StartLocation = coder.Router.Db.CreateRouterPointForEdgeAndVertex(edgesTotal[0], verticesTotal[0]), EndLocation = coder.Router.Db.CreateRouterPointForEdgeAndVertex(edgesTotal[edgesTotal.Count - 1], verticesTotal[verticesTotal.Count - 1]) }); }
/// <summary> /// Returns true if the given vertex is a valid candidate to use as a location reference point. /// </summary> public static bool IsVertexValid(this Coder coder, uint vertex) { var profile = coder.Profile; var edges = coder.Router.Db.Network.GetEdges(vertex); // go over each arc and count the traversible arcs. var traversCount = 0; foreach (var edge in edges) { var factor = profile.Profile.Factor(coder.Router.Db.EdgeProfiles.Get(edge.Data.Profile)); if (factor.Value != 0) { traversCount++; } } if (traversCount != 3) { // no special cases, only 1=valid, 2=invalid or 4 and up=valid. if (traversCount == 2) { // only two traversable edges, no options here! return(false); } return(true); } else { // special cases possible here, we need more info here. var incoming = new List <Tuple <long, IAttributeCollection, uint> >(); var outgoing = new List <Tuple <long, IAttributeCollection, uint> >(); var bidirectional = new List <Tuple <long, IAttributeCollection, uint> >(); foreach (var edge in edges) { var edgeProfile = coder.Router.Db.EdgeProfiles.Get(edge.Data.Profile); var factor = profile.Profile.Factor(edgeProfile); if (factor.Value != 0) { if (factor.Direction == 0) { // bidirectional, can be used as incoming. bidirectional.Add(new Tuple <long, IAttributeCollection, uint>(edge.From, edgeProfile, edge.Id)); } else if ((factor.Direction == 2 && !edge.DataInverted) || (factor.Direction == 1 && edge.DataInverted)) { // oneway is forward but arc is backward, arc is incoming. // oneway is backward and arc is forward, arc is incoming. incoming.Add(new Tuple <long, IAttributeCollection, uint>(edge.From, edgeProfile, edge.Id)); } else if ((factor.Direction == 2 && edge.DataInverted) || (factor.Direction == 1 && !edge.DataInverted)) { // oneway is forward and arc is forward, arc is outgoing. // oneway is backward and arc is backward, arc is outgoing. outgoing.Add(new Tuple <long, IAttributeCollection, uint>(edge.From, edgeProfile, edge.Id)); } } } if (bidirectional.Count == 1 && incoming.Count == 1 && outgoing.Count == 1) { // all special cases are found here. // get incoming's frc and fow. FormOfWay incomingFow, outgoingFow, bidirectionalFow; FunctionalRoadClass incomingFrc, outgoingFrc, bidirectionalFrc; if (profile.Extract(incoming[0].Item2, out incomingFrc, out incomingFow)) { if (incomingFow == FormOfWay.Roundabout) { // is this a roundabout, always valid. return(true); } if (profile.Extract(outgoing[0].Item2, out outgoingFrc, out outgoingFow)) { if (outgoingFow == FormOfWay.Roundabout) { // is this a roundabout, always valid. return(true); } if (incomingFrc != outgoingFrc) { // is there a difference in frc. return(true); } if (profile.Extract(bidirectional[0].Item2, out bidirectionalFrc, out bidirectionalFow)) { if (incomingFrc != bidirectionalFrc) { // is there a difference in frc. return(true); } } } // at this stage we have: // - two oneways, in opposite direction // - one bidirectional // - all same frc. // the only thing left to check is if the oneway edges go in the same general direction or not. // compare bearings but only if distance is large enough. var incomingShape = coder.Router.Db.Network.GetShape(coder.Router.Db.Network.GetEdge(incoming[0].Item3)); var outgoingShape = coder.Router.Db.Network.GetShape(coder.Router.Db.Network.GetEdge(outgoing[0].Item3)); if (incomingShape.Length() < 25 && outgoingShape.Length() < 25) { // edges are too short to compare bearing in a way meaningful for determining this. // assume not valid. return(false); } var incomingBearing = BearingEncoder.EncodeBearing(incomingShape); var outgoingBearing = BearingEncoder.EncodeBearing(outgoingShape); if (OpenLR.Extensions.AngleSmallestDifference(incomingBearing, outgoingBearing) > 30) { // edges are clearly not going in the same direction. return(true); } } return(false); } return(true); } }
/// <summary> /// Builds the shortest path between the two coordinates as a referenced line. /// </summary> public static ReferencedLine BuildLine(this Coder coder, Itinero.LocalGeo.Coordinate coordinate1, Itinero.LocalGeo.Coordinate coordinate2) { Route route; return(coder.BuildLine(coordinate1, coordinate2, out route)); }
/// <summary> /// Encodes a set of coordinates as a point along line. /// </summary> public static string EncodeAsPointAlongLine(this Coder coder, float latitude, float longitude, out RouterPoint resolvedPoint) { return(coder.Encode(coder.BuildPointAlongLine(latitude, longitude, out resolvedPoint))); }
/// <summary> /// Builds a point along line location. /// </summary> public static ReferencedPointAlongLine BuildPointAlongLine(this Coder coder, float latitude, float longitude, out RouterPoint resolvedPoint) { var routerPoint = coder.Router.TryResolve(coder.Profile.Profile, latitude, longitude); if (routerPoint.IsError) { throw new Exception("Could not build point along line: Could not find an edge close to the given location."); } resolvedPoint = routerPoint.Value; var locationOnNetwork = resolvedPoint.LocationOnNetwork(coder.Router.Db); // get edge info. var edge = coder.Router.Db.Network.GetEdge(routerPoint.Value.EdgeId); // check direction. var forward = true; var factor = coder.Profile.Profile.Factor(coder.Router.Db.EdgeProfiles.Get(edge.Data.Profile)); if (factor.Direction == 2) { forward = false; } // build the location with one edge. ReferencedPointAlongLine referencedPointAlongLine = null; if (forward) { referencedPointAlongLine = new ReferencedPointAlongLine() { Route = new ReferencedLine() { Edges = new long[] { edge.IdDirected() }, Vertices = new uint[] { edge.From, edge.To }, StartLocation = coder.Router.Db.CreateRouterPointForEdgeAndVertex(edge.IdDirected(), edge.From), EndLocation = coder.Router.Db.CreateRouterPointForEdgeAndVertex(edge.IdDirected(), edge.To) }, Latitude = locationOnNetwork.Latitude, Longitude = locationOnNetwork.Longitude, Orientation = Orientation.NoOrientation }; } else { referencedPointAlongLine = new ReferencedPointAlongLine() { Route = new ReferencedLine() { Edges = new long[] { -edge.IdDirected() }, Vertices = new uint[] { edge.To, edge.From }, StartLocation = coder.Router.Db.CreateRouterPointForEdgeAndVertex(edge.IdDirected(), edge.To), EndLocation = coder.Router.Db.CreateRouterPointForEdgeAndVertex(edge.IdDirected(), edge.From) }, Latitude = locationOnNetwork.Latitude, Longitude = locationOnNetwork.Longitude, Orientation = Orientation.NoOrientation }; } // expand to valid location. referencedPointAlongLine.Route.AdjustToValidPoints(coder); referencedPointAlongLine.Route.StartLocation = coder.Router.Db.CreateRouterPointForEdgeAndVertex( referencedPointAlongLine.Route.Edges[0], referencedPointAlongLine.Route.Vertices[0]); referencedPointAlongLine.Route.EndLocation = coder.Router.Db.CreateRouterPointForEdgeAndVertex( referencedPointAlongLine.Route.Edges[referencedPointAlongLine.Route.Edges.Length - 1], referencedPointAlongLine.Route.Vertices[referencedPointAlongLine.Route.Vertices.Length - 1]); return(referencedPointAlongLine); }
/// <summary> /// Builds a point along line location. /// </summary> public static ReferencedPointAlongLine BuildPointAlongLine(this Coder coder, float latitude, float longitude) { RouterPoint resolvedPoint; return(coder.BuildPointAlongLine(latitude, longitude, out resolvedPoint)); }
/// <summary> /// Builds a point along line location. /// </summary> public static ReferencedPointAlongLine BuildPointAlongLine(this Coder coder, Itinero.LocalGeo.Coordinate coordinate, out RouterPoint resolvedPoint) { return(coder.BuildPointAlongLine(coordinate.Latitude, coordinate.Longitude, out resolvedPoint)); }
/// <summary> /// Finds a valid vertex for the given vertex but does not search in the direction of the target neighbour. /// </summary> public static EdgePath <float> FindValidVertexFor(this Coder coder, uint vertex, long targetDirectedEdgeId, uint targetVertex, HashSet <uint> excludeSet, bool searchForward) { var profile = coder.Profile.Profile; // GIST: execute a dykstra search to find a vertex that is valid. // this will return a vertex that is on the shortest path: // foundVertex -> vertex -> targetNeighbour. var targetEdge = Itinero.Constants.NO_EDGE; if (targetDirectedEdgeId > 0) { targetEdge = (uint)(targetDirectedEdgeId - 1); } else { targetEdge = (uint)((-targetDirectedEdgeId) - 1); } // initialize settled set. var settled = new HashSet <long>(); settled.Add(targetVertex); // initialize heap. var heap = new BinaryHeap <EdgePath <float> >(10); heap.Push(new EdgePath <float>((uint)vertex), 0); // find the path to the closest valid vertex. EdgePath <float> pathTo = null; var edgeEnumerator = coder.Router.Db.Network.GetEdgeEnumerator(); while (heap.Count > 0) { // get next. var current = heap.Pop(); if (settled.Contains(current.Vertex)) { // don't consider vertices twice. continue; } settled.Add(current.Vertex); // limit search. if (settled.Count > coder.Profile.MaxSettles) { // not valid vertex found. return(null); } // check if valid. if (current.Vertex != vertex && coder.IsVertexValid(current.Vertex)) { // ok! vertex is valid. pathTo = current; } else { // continue search. // add unsettled neighbours. edgeEnumerator.MoveTo(current.Vertex); foreach (var edge in edgeEnumerator) { if (!excludeSet.Contains(edge.To) && !settled.Contains(edge.To) && !(edge.Id == targetEdge)) { // ok, new neighbour, and ok, not the edge and neighbour to ignore. var edgeProfile = coder.Router.Db.EdgeProfiles.Get(edge.Data.Profile); var factor = profile.Factor(edgeProfile); if (factor.Value > 0 && (factor.Direction == 0 || (searchForward && (factor.Direction == 1) != edge.DataInverted) || (!searchForward && (factor.Direction == 1) == edge.DataInverted))) { // ok, we can traverse this edge and no oneway or oneway reversed. var weight = current.Weight + factor.Value * edge.Data.Distance; var path = new EdgePath <float>(edge.To, weight, edge.IdDirected(), current); heap.Push(path, (float)path.Weight); } } } } } // ok, is there a path found. if (pathTo == null) { // oeps, probably something wrong with network-topology. // just take the default option. //throw new Exception( // string.Format("Could not find a valid vertex for invalid vertex [{0}].", vertex)); return(null); } // add the path to the given location. return(pathTo); }
/// <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> /// 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); }
/// <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); }
/// <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> /// 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; }