[Test] public void TreeEqualsSameTreeCell() { object actual = new ListTree(string.Empty, new [] { new ListTree("a") }); Parse table = Parse.ParseFrom("<table><tr><td><ul><li>a</li></ul></td></tr></table>"); Assert.IsTrue(IsEqual(table.Parts.Parts, actual)); }
public Graph(string path) { List<long>[,] geoBlocks = null; datasource = path; // Buffer is 1024 fileblocks big FileStream file = new FileStream(datasource, FileMode.Open, FileAccess.Read, FileShare.Read, 8 * 1024 * 1024); // List of file-positions where blocks with ways and/or // relations start. List<long> wayBlocks = new List<long>(); // We will read the fileblocks in parallel List<long> blocks = new List<long>(); List<long> busNodes = new List<long>(); List<string> busNames = new List<string>(); Console.WriteLine("Finding blocks"); while (true) { long blockstart = file.Position; BlobHeader blobHead = readBlobHeader(file); if (blobHead == null) break; if (blobHead.Type == "OSMHeader") { HeaderBlock filehead = HeaderBlock.ParseFrom( readBlockData(file, blobHead.Datasize)); for (int i = 0; i < filehead.RequiredFeaturesCount; i++) { string s = filehead.GetRequiredFeatures(i); if (s != "DenseNodes" && s != "OsmSchema-V0.6") { throw new NotSupportedException(s); } } // The .000000001 is 'cause longs are stored fileBounds = new BBox(.000000001 * filehead.Bbox.Left, .000000001 * filehead.Bbox.Top, .000000001 * filehead.Bbox.Right, .000000001 * filehead.Bbox.Bottom); horizontalGeoBlocks = (int)(fileBounds.Width / geoBlockWidth) + 1; verticalGeoBlocks = (int)(fileBounds.Height / geoBlockHeight) + 1; //Console.WriteLine("geoblocks {0}x{1}", horizontalGeoBlocks, verticalGeoBlocks); geoBlocks = new List<long>[horizontalGeoBlocks + 1, verticalGeoBlocks + 1]; wayGeoBlocks = new List<long>[horizontalGeoBlocks + 1, verticalGeoBlocks + 1]; landGeoBlocks = new List<long>[horizontalGeoBlocks + 1, verticalGeoBlocks + 1]; buildingGeoBlocks = new List<long>[horizontalGeoBlocks + 1, verticalGeoBlocks + 1]; } else { file.Position += blobHead.Datasize; blocks.Add(blockstart); } } Console.WriteLine("Reading nodes"); Parallel.ForEach(blocks, blockstart => //foreach(long blockstart in blocks) { BlobHeader blobHead; byte[] blockData; lock (file) { file.Position = blockstart; blobHead = readBlobHeader(file); // This means End Of File if (blobHead == null || blobHead.Type == "OSMHeader") throw new Exception("Should never happen"); blockData = readBlockData(file, blobHead.Datasize); } if (blobHead.Type == "OSMData") { PrimitiveBlock pb = PrimitiveBlock.ParseFrom(blockData); for (int i = 0; i < pb.PrimitivegroupCount; i++) { PrimitiveGroup pg = pb.GetPrimitivegroup(i); if (pg.HasDense) { // Remember the start of every blob with nodes nodeBlockIndexes.Insert(pg.Dense.GetId(0), blockstart); long id = 0; double latitude = 0, longitude = 0; for (int j = 0; j < pg.Dense.IdCount; j++) { id += pg.Dense.GetId(j); latitude += .000000001 * (pb.LatOffset + pb.Granularity * pg.Dense.GetLat(j)); longitude += .000000001 * (pb.LonOffset + pb.Granularity * pg.Dense.GetLon(j)); if (fileBounds.Contains(longitude, latitude)) { int blockX = XBlock(longitude); int blockY = YBlock(latitude); List<long> list = geoBlocks[blockX, blockY]; if (list == null) geoBlocks[blockX, blockY] = list = new List<long>(); list.Add(id); } } } else { wayBlocks.Add(blockstart); } } } //else //Console.WriteLine("Unknown blocktype: " + blobHead.Type); }); Console.WriteLine("Reading ways"); Parallel.ForEach(blocks, blockstart => //foreach(long blockstart in wayBlocks) { BlobHeader blobHead; byte[] blockData; lock (file) { file.Position = blockstart; blobHead = readBlobHeader(file); // This means End Of File if (blobHead == null || blobHead.Type == "OSMHeader") throw new Exception("Should never happen"); blockData = readBlockData(file, blobHead.Datasize); } if (blobHead.Type == "OSMData") { PrimitiveBlock pb = PrimitiveBlock.ParseFrom(blockData); for (int i = 0; i < pb.PrimitivegroupCount; i++) { PrimitiveGroup pg = pb.GetPrimitivegroup(i); /* * Part one: read all the curves and add them */ // Insert curves in the curve tree for (int j = 0; j < pg.WaysCount; j++) { CurveType type = CurveType.UnTested; OSMPBF.Way w = pg.GetWays(j); // Nodes in this way List<long> nodes = new List<long>(); long id = 0; for (int k = 0; k < w.RefsCount; k++) { id += w.GetRefs(k); nodes.Add(id); } string name = ""; int maxSpeed = 0; bool makeCurve = true; bool curveTypeSpecified = true; bool carSpecified = false; bool bicycleSpecified = false; bool footSpecified = false; bool busSpecified = false; bool carAllowed = false; bool bicycleAllowed = false; bool footAllowed = false; bool busAllowed = false; for (int k = 0; k < w.KeysCount; k++) { string key = pb.Stringtable.GetS( (int)w.GetKeys(k)).ToStringUtf8(); string value = pb.Stringtable.GetS( (int)w.GetVals(k)).ToStringUtf8(); #region Process keys switch (key.ToLower()) { #region highway case "highway": switch (value) { case "bus_guideway": type = CurveType.Bus_guideway; break; case "construction": type = CurveType.Construction_street; break; case "cycleway": type = CurveType.Cycleway; break; case "footway": type = CurveType.Footway; break; case "Living_street": type = CurveType.Living_street; break; case "motorway": type = CurveType.Motorway; break; case "motorway_link": type = CurveType.Motorway_link; break; case "pedestrian": type = CurveType.Pedestrian; break; case "primary": type = CurveType.Primary; break; case "primary_link": type = CurveType.Primary_link; break; case "proposed": type = CurveType.Proposed; break; case "raceway": type = CurveType.Raceway; break; case "residential": type = CurveType.Residential_street; break; case "road": type = CurveType.Road; break; case "secondary": type = CurveType.Secondary; break; case "secondary_link": type = CurveType.Secondary_link; break; case "service": type = CurveType.Service; break; case "steps": type = CurveType.Steps; break; case "tertiary": type = CurveType.Tertiary; break; case "tertiary_link": type = CurveType.Tertiary_link; break; case "track": type = CurveType.Track; break; case "trunk": type = CurveType.Trunk; break; case "trunk_link": type = CurveType.Trunk_link; break; case "unclassified": type = CurveType.Unclassified; break; case "path": type = CurveType.Path; break; default: //Console.WriteLine("TODO: highway=" + value); break; } break; #endregion #region landuse case "landuse": switch (value) { case "allotments": type = CurveType.Allotments; break; case "basin": type = CurveType.Basin; break; case "construction": type = CurveType.Construction_land; break; case "grass": type = CurveType.Grass; break; case "farm": type = CurveType.Farm; break; case "forest": type = CurveType.Forest; break; case "military": type = CurveType.Military; break; case "orchard": type = CurveType.Orchard; break; case "pond": type = CurveType.Salt_pond; break; case "recreation_centre": type = CurveType.Recreation_ground; break; default: //Console.WriteLine("TODO: landuse=" + value); break; } break; #endregion case "building": type = CurveType.Building; break; case "natural": if (value == "water") type = CurveType.Water; break; case "name": case "addr:housename": case "alt_name": case "loc_name": case "name_alt": case "name:left": case "name:right": if(name == "") name = value; else name = name + "/" + value; break; case "maxspeed": int.TryParse(value, out maxSpeed); break; case "bicycle": case "bicycle:backward": case "cyclestreet": case "cycleway": case "cycleway:lane": case "cycleway:left": case "cycleway:left:surface": case "cycleway:left:width": case "cycleway:right": case "cycleway:right:surface": case "cycleway:right:width": case "cycleway:surface": case "cycleway:width": bicycleSpecified = true; bicycleAllowed = value != "no"; break; case "bicycle:oneway": case "oneway:bicycle": case "cycleway:oneway": case "oneway:cycleway:": bicycleSpecified = true; bicycleAllowed = true; break; case "vehicle": bicycleSpecified = true; carSpecified = true; if(value == "no") { bicycleAllowed = false; carAllowed = false; } else { bicycleAllowed = true; carAllowed = true; } break; case "car": case "motorcar": case "motor_vehicle": carSpecified = true; carAllowed = value != "no"; break; case "foot": case "footway": footSpecified = true; footAllowed = value != "no"; break; case "public_transport": type = CurveType.PublicTransportPlatform; break; case "bus": if(value != "no") type = CurveType.PublicServiceVehicles; break; case "waterway": case "water": // TODO? draw these things? // Or just the lake/basin/pond? if (value == "ditch" || value == "drain" || value == "weir" || value == "stream" || value == "yes" || value == "river" || value == "culvert" || value == "drain; culvert" || value == "Ditch" || value == "Tank_ditch" || value == "dept_line" || value == "lock" || value == "canal" ) { type = CurveType.Waterway; } if ( value == "lake" || value == "basin" || value == "pond" || value == "riverbank" ) { type = CurveType.Water; } break; case "psv": if (value == "yes") type = CurveType.PublicServiceVehicles; break; case "amenity": if (value == "parking") { type = CurveType.Parking; Coordinate center = FindCentroid(nodes); extras.Add(new Location(new Node(center.Longitude, center.Latitude, 0), LocationType.Parking)); } break; case "power": if (value == "generator") type = CurveType.Power; break; default: if (key.StartsWith("building")) { type = CurveType.Building; } break; } #endregion } // Try to make sense of tags #region Process transport tags if (type.IsStreet()) { // If type props don't match specified props if(!curveTypeSpecified || (bicycleSpecified && (bicycleAllowed != type.BicyclesAllowed())) || (carSpecified && (carAllowed != type.CarsAllowed())) || (footSpecified && (footAllowed != type.FootAllowed()))) { // What is specified exactly? footAllowed = footSpecified ? footAllowed : type.FootAllowed(); bicycleAllowed = bicycleSpecified ? bicycleAllowed : type.BicyclesAllowed(); carAllowed = carSpecified ? carAllowed : type.CarsAllowed(); // Tedious matching of stuff if(carAllowed) { if(footAllowed) { if (bicycleAllowed) type = CurveType.CarBicycleFoot; } else { if(bicycleAllowed) type = CurveType.CarBicycleNoFoot; else type = CurveType.Motorway; } } else { // What is specified exactly? footAllowed = footSpecified ? footAllowed : type.FootAllowed(); bicycleAllowed = bicycleSpecified ? bicycleAllowed : type.BicyclesAllowed(); carAllowed = carSpecified ? carAllowed : type.CarsAllowed(); if(type == CurveType.PublicServiceVehicles) { if(footAllowed) { if(bicycleAllowed) type = CurveType.BusFootBicycle; else type = CurveType.BusFoot; } else type = CurveType.BusBicycle; } else { if(footAllowed && bicycleAllowed) type = CurveType.Path; if(footAllowed && !bicycleAllowed) type = CurveType.Footway; if(!footAllowed && bicycleAllowed) type = CurveType.NoCarBicycleNoFoot; if(!footAllowed && !bicycleAllowed) type = CurveType.NoneAllowed; } } } } #endregion if (makeCurve) { Curve c = new Curve(nodes.ToArray(), name); c.Name = name; c.Type = type; if (type.IsStreet()) { foreach (long n in nodes) { ways.Insert(n, c); } if (maxSpeed > 0) { c.MaxSpeed = maxSpeed; } } else { if (type.IsBuilding()) { foreach (long n in nodes) { buildings.Insert(n, c); } } else { foreach (long n in nodes) { lands.Insert(n, c); } } } } } /* * Part two: adding bus routes and the likes */ ListTree<long> endIdStartId = new ListTree<long>(); //Parallel.For(0, pg.RelationsCount, j => for(int j = 0; j < pg.RelationsCount; j++) { Relation rel = pg.GetRelations(j); bool publictransport = false; string name = ""; for (int k = 0; k < rel.KeysCount; k++) { string key = pb.Stringtable.GetS((int)rel.GetKeys(k)).ToStringUtf8(); string value = pb.Stringtable.GetS((int)rel.GetVals(k)).ToStringUtf8(); if (key == "route" && (value == "bus" || value == "trolleybus" || value == "share_taxi" || value == "tram")) publictransport = true; if (key == "ref") name = value; } if (publictransport) { long id = 0; //List<long> nodes = new List<long>(); for (int k = 0; k < rel.MemidsCount; k++) { id += rel.GetMemids(k); string role = pb.Stringtable.GetS((int)rel.GetRolesSid(k)).ToStringUtf8(); string type = rel.GetTypes(k).ToString(); //Console.WriteLine(type + " " + id + " is " + role); if (type == "NODE" && role.StartsWith("stop")) { busNodes.Add(id); busNames.Add(name); } } for (int l = 0; l < busNodes.Count - 1; l++) { Edge e = new Edge(busNodes[l], busNodes[l + 1]); e.Type = CurveType.AbstractBusRoute; if (!endIdStartId.Get(e.End).Contains(e.Start)) { abstractBusWays.Add(e); endIdStartId.Insert(e.End, e.Start); } } Edge e2 = new Edge(busNodes[busNodes.Count - 1], 0); e2.Type = CurveType.AbstractBusRoute; abstractBusWays.Add(e2); endIdStartId.Insert(busNodes[busNodes.Count - 1], 0); } } } } }); file.Close(); Console.WriteLine("Sorting nodes"); Parallel.For(0, horizontalGeoBlocks, (x) => { for(int y = 0; y <= verticalGeoBlocks; y++) { if(geoBlocks[x, y] != null) { List<long> wayList = new List<long>(); List<long> landList = new List<long>(); List<long> buildingList = new List<long>(); foreach(long id in geoBlocks[x, y]) { if(ways.Get(id).Count != 0) wayList.Add(id); if(lands.Get(id).Count != 0) landList.Add(id); if(buildings.Get(id).Count != 0) buildingList.Add(id); } wayGeoBlocks[x, y] = wayList; landGeoBlocks[x, y] = landList; buildingGeoBlocks[x, y] = buildingList; } } }); Console.WriteLine("Routing busStations"); // Add busstations for (int i = 0; i < busNodes.Count; i++) { if (busStations.Get(busNodes[i]) == null) { Node n = GetNode(busNodes[i]); if (n.Longitude != 0 && n.Latitude != 0) { busStations.Insert(busNodes[i], n); extras.Add(new Location(n, LocationType.BusStation)); } } } busThread = new Thread(new ThreadStart(() => { LoadBusses(); })); busThread.Start(); }
/// <summary> /// Dijkstra in graph gr, from source to destination, using vehicle v. /// </summary> /// <param name="source"> the startpoint </param> /// <param name="destination"> the destination </param> /// <param name="v"> vehicle that is used </param> /// <returns></returns> public Route Dijkstra(long from, long to, Vehicle[] vehicles, RouteMode mode, bool useBus) { Route result = null; if (from == 0 || to == 0 || graph == null) return result; Node source = graph.GetNode(from); Node destination = graph.GetNode(to); //all nodes that are completely solved SortedList<long, Node> solved = new SortedList<long, Node>(); //nodes that are encountered but not completely solved SortedList<double, Node> unsolved = new SortedList<double, Node>(); RBTree<Node> prevs = new RBTree<Node>(); RBTree<double> times = new RBTree<double>(); RBTree<double> distances = new RBTree<double>(); RBTree<Vehicle> vehicleUse = new RBTree<Vehicle>(); ListTree<Vehicle> forbiddenVehicles = new ListTree<Vehicle>(); List<Edge> abstractBusses = graph.GetAbstractBusEdges(); Node current = source; bool found = false; //if there's no new current node it means the algorithm should stop while (current != null) { //if we encounter the destination it means we found the shortest route so we break if (current == destination) { found = true; break; } List<Edge> edges = new List<Edge>(graph.GetEdgesFromNode(current.ID)); foreach (Edge busEdge in abstractBusses) { if (busEdge.End == current.ID || busEdge.Start == current.ID) edges.Add(busEdge); } Node prev = prevs.Get(current.ID); if (prev != null) { foreach (Vehicle vehicle in forbiddenVehicles.Get(prev.ID)) { forbiddenVehicles.Insert(current.ID, vehicle); } } foreach (Edge e in edges) { if (IsAllowed(e, vehicles, useBus)) { Node start = graph.GetNode(e.Start); Node end = graph.GetNode(e.End); double distance = double.PositiveInfinity; double time = double.PositiveInfinity; Vehicle v = vehicles[0]; if (e.Type != CurveType.AbstractBusRoute) { if (e.Type != CurveType.Bus) { double speed = 0; foreach (Vehicle vehicle in vehicles) { if (!forbiddenVehicles.Get(current.ID).Contains(vehicle)) { double vSpeed = GetSpeed(vehicle, e); if (vSpeed > speed && IsAllowed(e, vehicle, useBus)) { speed = vSpeed; v = vehicle; } } } distance = NodeCalcExtensions.Distance(start, end); time = distance / speed; } else if (e.Route != null) { // Take busroute if better if (mode == RouteMode.Fastest && time > e.Route.Time || mode == RouteMode.Shortest && distance > e.Route.Length) { distance = e.Route.Length; time = e.Route.Time; v = Vehicle.Foot; foreach (Vehicle vehicle in vehicles) if (vehicle != Vehicle.Foot) forbiddenVehicles.Insert(current.ID, vehicle); } } } else { Node n1 = null, n2 = null; if (start.Longitude != 0 || start.Latitude != 0) n1 = graph.GetNodeByPos(start.Longitude, start.Latitude, Vehicle.Bus); if (end.Longitude != 0 || end.Latitude != 0) n2 = graph.GetNodeByPos(end.Longitude, end.Latitude, Vehicle.Bus); if (n1 != default(Node) && n2 != default(Node)) { Curve curve = new Curve(new long[] { start.ID, end.ID }, e.Name); Route r = this.Dijkstra(n1.ID, n2.ID, new Vehicle[] { Vehicle.Bus }, RouteMode.Fastest, false); r = new Route(new Node[] { start }, Vehicle.Bus) + r + new Route(new Node[] { end }, Vehicle.Bus); curve.Type = CurveType.Bus; curve.Route = r; // We calculate with 60 seconds of switch over to the bus. r.Time += 60; graph.AddWay(start.ID, curve); graph.AddWay(end.ID, curve); e.Route = r; e.Type = CurveType.Bus; // Take busroute if better if (mode == RouteMode.Fastest && time > e.Route.Time || mode == RouteMode.Shortest && distance > e.Route.Length) { distance = e.Route.Length; time = e.Route.Time; v = Vehicle.Foot; foreach (Vehicle vehicle in vehicles) if (vehicle != Vehicle.Foot) forbiddenVehicles.Insert(current.ID, vehicle); } } graph.RemoveAbstractBus(e); abstractBusses.Remove(e); } time += times.Get(current.ID); double trueDist = distances.Get(current.ID) + distance; if (!solved.ContainsValue(end) && current != end) { if (end.Latitude != 0 && end.Longitude != 0) { if (times.Get(end.ID) == 0 || distances.Get(end.ID) == 0) { times.Insert(end.ID, double.PositiveInfinity); distances.Insert(end.ID, double.PositiveInfinity); vehicleUse.Insert(end.ID, v); } if ((mode == RouteMode.Fastest && times.Get(end.ID) > time) || (mode == RouteMode.Shortest && distances.Get(end.ID) > trueDist)) { times.GetNode(end.ID).Content = time; distances.GetNode(end.ID).Content = trueDist; vehicleUse.GetNode(end.ID).Content = v; if (prevs.GetNode(end.ID).Content == null) prevs.Insert(end.ID, current); else prevs.GetNode(end.ID).Content = current; if (!unsolved.ContainsValue(end)) { if (mode == RouteMode.Fastest) { // Very bad solution but I couldn't think of a simple better one. while (unsolved.ContainsKey(times.Get(end.ID))) { times.GetNode(end.ID).Content += 0.0000000001; } unsolved.Add(times.Get(end.ID), end); } else if (mode == RouteMode.Shortest) { // Very bad solution but I couldn't think of a simple better one. while (unsolved.ContainsKey(distances.Get(end.ID))) { distances.GetNode(end.ID).Content += 0.0000000001; } unsolved.Add(distances.Get(end.ID), end); } if (prevs.GetNode(current.ID).Content != null && vehicleUse.Get(prevs.GetNode(current.ID).Content.ID) == Vehicle.Car) { if (v == Vehicle.Foot) { forbiddenVehicles.Insert(end.ID, Vehicle.Car); } } } } } } else if (!solved.ContainsValue(start) && current != start) { if (start.Latitude != 0 && start.Longitude != 0) { if (times.Get(start.ID) == 0 || distances.Get(start.ID) == 0) { times.Insert(start.ID, double.PositiveInfinity); distances.Insert(start.ID, double.PositiveInfinity); vehicleUse.Insert(start.ID, v); } if ((mode == RouteMode.Fastest && times.Get(start.ID) > time) || (mode == RouteMode.Shortest && distances.Get(start.ID) > trueDist)) { times.GetNode(start.ID).Content = time; distances.GetNode(start.ID).Content = trueDist; vehicleUse.GetNode(start.ID).Content = v; if (prevs.GetNode(start.ID).Content == null) prevs.Insert(start.ID, current); else prevs.GetNode(start.ID).Content = current; if (!unsolved.ContainsValue(start)) { if (mode == RouteMode.Fastest) { // Very bad solution but I couldn't think of a simple better one. while (unsolved.ContainsKey(times.Get(start.ID))) { times.GetNode(start.ID).Content += 0.0000000001; } unsolved.Add(times.Get(start.ID), start); } else if (mode == RouteMode.Shortest) { // Very bad solution but I couldn't think of a simple better one. while (unsolved.ContainsKey(distances.Get(start.ID))) { distances.GetNode(start.ID).Content += 0.0000000001; } unsolved.Add(distances.Get(start.ID), start); } } if (prevs.GetNode(current.ID).Content != null && vehicleUse.Get(prevs.GetNode(current.ID).Content.ID) == Vehicle.Car) { if (v == Vehicle.Foot) { forbiddenVehicles.Insert(start.ID, Vehicle.Car); } } } } } } } //dit zou niet voor moeten komen maar toch gebeurt het... if (!solved.ContainsKey(current.ID)) solved.Add(current.ID, current); if (unsolved.Count > 0) { current = unsolved.Values[0]; unsolved.RemoveAt(0); } else { current = null; } } if (found) { List<Node> nodes = new List<Node>(); List<long> extras = graph.GetExtras(); Node n = destination; List<long> busStartStop = new List<long>(); do { bool foundRoute = false; if (extras.Contains(n.ID)) { // Change straigt buslines in for the actual route. foreach (Edge e in graph.GetEdgesFromNode(n.ID)) { if (prevs.Get(n.ID) != null && e.Route != null) { if (n.ID == e.Start && prevs.Get(n.ID).ID == e.End) { Node[] busNodes = e.Route.Points; if (busNodes[0].ID == e.Start) Array.Reverse(busNodes); busStartStop.Add(busNodes[busNodes.Length - 1].ID); busStartStop.Add(busNodes[0].ID); nodes.InsertRange(0, busNodes); n = prevs.Get(n.ID); foundRoute = true; break; } else if (n.ID == e.End && prevs.Get(n.ID).ID == e.Start) { Node[] busNodes = e.Route.Points; if (busNodes[0].ID == e.End) Array.Reverse(busNodes); busStartStop.Add(busNodes[busNodes.Length - 1].ID); busStartStop.Add(busNodes[0].ID); nodes.InsertRange(0, busNodes); n = prevs.Get(n.ID); foundRoute = true; break; } } } } if (!foundRoute) { nodes.Insert(0, n); n = prevs.Get(n.ID); } } while (n != null); result = new Route(nodes.ToArray(), vehicles[0]); result.Time = times.Get(destination.ID); result.Length = distances.Get(destination.ID); // Set bus as vehicle if (busStartStop.Count > 0) { int i = busStartStop.Count - 1; Node[] routePoints = result.Points; for (int j = 0; j < routePoints.Length; j++) { if (routePoints[j].ID == busStartStop[i]) { if (i % 2 == 1) { result.SetVehicle(Vehicle.Bus, j); i--; } else { result.SetVehicle(Vehicle.Foot, j); i--; } } if (i < 0) break; } } Vehicle cur = result.GetVehicle(0), prev; for (int i = 2; i < result.NumOfNodes; i++) { if (result.GetVehicle(i) != Vehicle.Bus) { prev = vehicleUse.Get(result[i].ID); if (prev != cur && i > 1) result.SetVehicle(prev, i - 1); cur = prev; } } } else { result = new Route(new Node[] { source }, vehicles[0]); result.Time = double.PositiveInfinity; result.Length = double.PositiveInfinity; } return result; }