routing
routing copied to clipboard
C# Profiles/Vehicles cannot be used from routerDB after serialization.
/// <summary>
/// A Train <see cref="Profile"/>
/// </summary>
public class TrainProfile : Profile
{
private static Dictionary<String, int> SpeedProfiles =
new Dictionary<string, int>
{
{"railway",90 },
{"railway_link",90 },
{"trunk",70 },
{"trunk_link",70 },
{"primary",50 },
{"primary_link",50 },
{"secondary",45 },
{"secondary_link",45 },
{"tertiary",45 },
{"tertiary_link",45 },
{"unclassified",40 },
{"residential",40 },
{"service",20 },
{"services",20 },
{"road",20 },
{"yard",20 },
{"siding",5 },
{"ferry",5 },
{"movable",5 },
{"shuttle_train",10 },
{"default",10 }
};
private static Dictionary<String, bool> AccessValues =
new Dictionary<string, bool>
{
{"private",false},
{"yes",true },
{"no",false },
{"permissive",true },
{"destination",true },
{"customers",false },
{"designated",true },
{"public",true },
{"delivery",true},
{"use_sidepath",false }
};
private static string[] Vehicles = new[] { "vehicle", "motor_vehicle", "motorcar", "train" };
public TrainProfile() : base("train", ProfileMetric.TimeInSeconds,
Vehicles, null, new TrainVehicle())
{
_instructionGenerator = new FastInstructionGenerator(this);
}
//interprets access tags
private static bool? CanAccess(IAttributeCollection attributes)
{
bool? last_access = null;
string accessValue = null;
if (attributes.TryGetValue("access", out accessValue) &&
AccessValues.ContainsKey(accessValue))
{
var access = AccessValues[accessValue];
last_access = access;
}
foreach (var vtype in Vehicles)
{
string accessKey = null;
if (attributes.TryGetValue(vtype, out accessKey) && AccessValues.ContainsKey(accessKey))
{
var access = AccessValues[accessKey];
last_access = access;
}
}
return last_access;
}
private static short? IsOneway(IAttributeCollection attributes, Whitelist whitelist, String name)
{
String oneway = null;
if (attributes.TryGetValue(name, out oneway))
{
whitelist.Add(name);
if (!String.IsNullOrEmpty(oneway))
{
if (oneway == "yes" ||
oneway == "true" ||
oneway == "1")
return 1;
if (oneway == "-1")
return 2;
}
}
return null;
}
internal static FactorAndSpeed FactorAndSpeed(IAttributeCollection attributes,
Whitelist whitelist)
{
if (attributes == null || attributes.Count == 0)
{
return Itinero.Profiles.FactorAndSpeed.NoFactor;
}
string highway = null;
if (attributes.TryGetValue("highway", out highway))
whitelist.Add("highway");
var result = new FactorAndSpeed();
var speed = 0.0f;
short direction = 0;
var canstop = true;
//set highway to ferry when ferry.
string route = null;
if (attributes.TryGetValue("route", out route))
whitelist.Add("route");
if (route == "ferry")
{
highway = "ferry";
}
if (String.IsNullOrEmpty(highway))
return Itinero.Profiles.FactorAndSpeed.NoFactor;
//get default speed profiles
var highway_speed = SpeedProfiles.ContainsKey(highway) ? (int?)SpeedProfiles[highway] : null;
if (highway_speed != null)
{
speed = highway_speed.Value;
direction = 0;
canstop = true;
if (highway == "railway" ||
highway == "railway_link")
canstop = false;
}
else
return Itinero.Profiles.FactorAndSpeed.NoFactor;
if (CanAccess(attributes) == false)
return Itinero.Profiles.FactorAndSpeed.NoFactor;
//get maxspeed if any.
string maxSpeed = null;
if (attributes.TryGetValue("maxspeed", out maxSpeed))
{
whitelist.Add("maxspeed");
float lspeed;
if (float.TryParse(maxSpeed, out lspeed))
speed = lspeed * 0.75f;
}
//get maxweight and maxwidth constraints if any
var maxweight = 0.0f;
var maxwidth = 0.0f;
string maxWeightString = null;
if (attributes.TryGetValue("maxweight", out maxWeightString))
{
whitelist.Add("maxweight");
float.TryParse(maxWeightString, out maxweight);
}
string maxWidthString = null;
if (attributes.TryGetValue("maxwidth", out maxWidthString))
{
whitelist.Add("maxwidth");
float.TryParse(maxWidthString, out maxwidth);
}
if (maxwidth != 0 || maxweight != 0)
{
result.Constraints = new[]
{ maxweight, maxwidth};
}
//get directional information
String junction = null;
if (attributes.TryGetValue("junction", out junction))
{
whitelist.Add("junction");
if (junction == "roundabout")
direction = 1;
}
var ldirection = IsOneway(attributes, whitelist, "oneway");
if (ldirection != null)
direction = ldirection.Value;
if (speed == 0)
return Itinero.Profiles.FactorAndSpeed.NoFactor;
result.SpeedFactor = 1.0f / (speed / 3.6f); // 1/m/s
result.Value = result.SpeedFactor;
result.Direction = direction;
if (!canstop)
{
result.Direction += 3;
}
return result;
}
public override FactorAndSpeed FactorAndSpeed(IAttributeCollection attributes)
{
return TrainProfile.FactorAndSpeed(attributes, new Whitelist());
}
/// </summary>
/// <remarks>
/// Default implementation compares attributes one-by-one.
/// </remarks>
public override sealed bool Equals(IAttributeCollection attributes1, IAttributeCollection attributes2)
{
return attributes1.ContainsSame(attributes2);
}
private IUnimodalInstructionGenerator _instructionGenerator;
public override IUnimodalInstructionGenerator InstructionGenerator
{
get { return _instructionGenerator; }
}
}
/// <summary>
/// A Train <see cref="Itinero.Profiles.Vehicle"/>
/// </summary>
public class TrainVehicle : Itinero.Profiles.Vehicle
{
static FactorAndSpeed DefaultFactorAndSpeed = new FactorAndSpeed()
{
SpeedFactor = 1 / (50f / 3.6f),
Value = 1 / (50f / 3.6f),
Direction = 0
};
readonly string[] _vehicleTypes = new[] { "vehicle", "motor_vehicle", "motorcor", "train" };
public TrainVehicle()
{
Register(new Profile("shortest", ProfileMetric.DistanceInMeters, _vehicleTypes, null, this));
Register(new Profile(string.Empty, ProfileMetric.TimeInSeconds, _vehicleTypes, null, this));
ProfileWhiteList.Add("railway");
ProfileWhiteList.Add("maxspeed");
ProfileWhiteList.Add("route");
ProfileWhiteList.Add("barrier");
ProfileWhiteList.Add("service");
MetaWhiteList.Add("name");
MetaWhiteList.Add("ref");
MetaWhiteList.Add("bridge");
MetaWhiteList.Add("tunnel");
MetaWhiteList.Add("embankment");
MetaWhiteList.Add("cutting");
MetaWhiteList.Add("electrified");
MetaWhiteList.Add("owner");
MetaWhiteList.Add("operator");
MetaWhiteList.Add("usage");
MetaWhiteList.Add("from");
MetaWhiteList.Add("to");
MetaWhiteList.Add("via");
MetaWhiteList.Add("voltage");
MetaWhiteList.Add("wikipedia");
MetaWhiteList.Add("embedded_rails");
MetaWhiteList.Add("frequency");
MetaWhiteList.Add("railway:track_ref");
MetaWhiteList.Add("tracks");
MetaWhiteList.Add("spur");
MetaWhiteList.Add("spiding");
MetaWhiteList.Add("crossover");
}
/// <summary>
/// Gets the name of this vehicle.
/// </summary>
public override string Name => nameof(TrainVehicle);
/// <summary>
/// Gets the vehicle types.
/// </summary>
public override string[] VehicleTypes
{
get
{
return _vehicleTypes;
}
}
public override bool AddToWhiteList(IAttributeCollection attributes, Whitelist whitelist)
{
return TrainProfile.FactorAndSpeed(attributes, whitelist).SpeedFactor > 0;
}
public override FactorAndSpeed FactorAndSpeed(IAttributeCollection attributes, Whitelist whitelist)
{
return DefaultFactorAndSpeed;
}
}
/// <summary>
/// Class to generate instructions from the <see cref="Route"/>
/// </summary>
internal class FastInstructionGenerator :
IUnimodalInstructionGenerator
{
private Profile _profile;
internal FastInstructionGenerator(Profile profile)
{
_profile = profile;
}
public IList<Itinero.Navigation.Instructions.Instruction> Generate(Route route, ILanguageReference language)
{
if (route.IsMultimodal())
{
throw new ArgumentException("Cannot use a unimodal instruction generator on multimodal route.");
}
if (_profile.FullName.ToLowerInvariant() != route.Profile)
{
throw new ArgumentException(string.Format("Cannot generate instructions with a generator for profile {0} for a route with profile {1}.",
_profile.FullName, route.Profile));
}
var instructions = new List<Itinero.Navigation.Instructions.Instruction>();
foreach (var position in route)
{
if (position.IsFirst())
instructions.Add(GetStart(position, language));
else if (position.IsLast())
instructions.Add(GetStop(position, language));
else if (position.GetMetaAttribute("junction") ==
"roundabout")
{
var instruction = GetRoundabout(position, language);
if (instruction != null)
instructions.Add(instruction);
}
else
{
var instruction = GetTurn(position, language);
if (instruction != null)
instructions.Add(instruction);
}
}
return instructions;
}
private Itinero.Navigation.Instructions.Instruction GetStart(RoutePosition position, ILanguageReference language)
{
var instruction = new Itinero.Navigation.Instructions.Instruction
{
Shape = position.Shape,
Type = "start"
};
var direction = position.Direction();
instruction.Text = String.Format(language
["Start {0}."], language[direction.ToString()
.ToLower()]);
return instruction;
}
// gets the last instruction
private Itinero.Navigation.Instructions.Instruction GetStop(RoutePosition position, ILanguageReference language)
{
return new Itinero.Navigation.Instructions.Instruction
{
Text = language["Arrived at destination."],
Shape = position.Shape,
Type = "stop"
};
}
private IEnumerable<Route.Branch> GetTraversable(IEnumerable<Route.Branch> branches)
{
foreach (var branch in branches)
{
var factorAndSpeed = _profile.FactorAndSpeed(branch.Attributes);
if (factorAndSpeed.SpeedFactor != 0)
{
if (factorAndSpeed.Direction == 0 ||
factorAndSpeed.Direction == 3)
{
yield return branch;
}
else
{
if (branch.AttributesDirection)
{
if (factorAndSpeed.Direction == 1 ||
factorAndSpeed.Direction == 4)
{
yield return branch;
}
}
else
{
if (factorAndSpeed.Direction == 2 ||
factorAndSpeed.Direction == 5)
{
yield return branch;
}
}
}
}
}
}
//gets a roundabout instruction
private Itinero.Navigation.Instructions.Instruction GetRoundabout(RoutePosition position, ILanguageReference language)
{
position.Next();
if (string.IsNullOrEmpty(position.GetMetaAttribute("junction")))
{
var instruction = new Itinero.Navigation.Instructions.Instruction
{
Shape = position.Shape,
Type = "roundabout"
};
var exit = 1;
var count = 1;
var previous = position.Previous();
while (previous != null && previous.Value.GetMetaAttribute("junction") == "roundabout")
{
var branches = previous.Value.Branches();
if (branches != null)
{
branches = GetTraversable(branches);
if (branches.Any())
exit++;
}
count++;
previous = previous.Value.Previous();
}
instruction.Text = string.Format(language["Take the {0}th exit at the next roundabout."], exit);
if (exit == 1)
instruction.Text = string.Format(language["Take the first exit at the next roundabout."]);
else if (exit == 2)
instruction.Text = string.Format(language["Take the second exit at the next roundabout."]);
else if (exit == 3)
instruction.Text = string.Format(language["Take the third exit at the next roundabout."]);
instruction.Shape = position.Shape;
return instruction;
}
return null;
}
//gets a turn
private Itinero.Navigation.Instructions.Instruction GetTurn(RoutePosition position, ILanguageReference language)
{
var instruction = new Itinero.Navigation.Instructions.Instruction
{
Shape = position.Shape,
Type = "turn"
};
var relativeDirection = position.RelativeDirection().Direction;
var turnRelevant = false;
var branches = position.Branches();
if (branches != null)
{
var traversedBranches =
GetTraversable(branches).ToList();
if (relativeDirection == RelativeDirectionEnum.StraightOn &&
traversedBranches.Count >= 2)
turnRelevant = true;// straight on at cross road
if (relativeDirection != RelativeDirectionEnum.StraightOn &&
traversedBranches.Count > 0)
turnRelevant = true;// an actual normal turn
}
if (turnRelevant)
{
var next = position.Next();
string name = null;
if (next != null)
name = next.Value.GetMetaAttribute("name");
if (!String.IsNullOrEmpty(name))
{
instruction.Text = string.Format(language["Go {0} on {1}."],
language[relativeDirection.ToString()
.ToLower()], name);
instruction.Shape = position.Shape;
}
else
{
instruction.Text = String.Format(language["Go {0}."],
language[relativeDirection.ToString().ToLower()]);
instruction.Shape = position.Shape;
}
return instruction;
}
return null;
}
public IList<Itinero.Navigation.Instructions.Instruction> Generate(Route route, ILanguageReference languageReference, CancellationToken cancellationToken)
{
return Generate(route, languageReference);
}
}
Exception
'Cannot deserialize for type with name: Itinero.Test.Functional.Program+TrainVehicle. A custom deserializer was not found.
When
using (var stream = new FileInfo(@"planetrail.routerdb").OpenRead())
routerDb = RouterDb.Deserialize(stream);
However the odd thing is that when the following code is used:
using (var stream = new FileInfo(@"planet-rail.osm.pbf").OpenRead())
routerDb.LoadOsmData(stream, vehicle, trainVehicle);
That it works.
Did I do something wrong in the Profile / Vehicle code I created?
I am mainly trying to handle Turn Penalty and prevention of U turns.
The C# profiles cannot be serialized to a routerdb. You can register them before deserializing if I remember correctly:
https://github.com/itinero/routing/blob/develop/src/Itinero/Profiles/Vehicle.cs#L155
What is it exactly you cannot do from lua?
I was having a harder time debugging the LUA profiles to determine why certain routes were not working the way we would expect. E.g. there are what seem like U turns on certain tracks I initially thought because of the profile for Fastest but even when changing to Slowest I still see the same result.
I missed calling Register because it was marked obsolete but if you see in the constructor of the Vehicle it already calls Register. @ https://github.com/itinero/routing/blob/develop/src/Itinero/Profiles/Vehicle.cs#L113 with the TrainProfile as the argument?
I tried both and I get an exception when I use
var trainProfile = new TrainProfile(); var trainVehicle = new TrainVehicle(); Profile.Register(trainProfile);
I get:
'Routing profile is not supported.'
Further is there any documentation on the LUA which can help me determine what API there is e.g. what route can do and what API there is in Itinero e.g. parsing functions, what they return, and if possible any documentation on how turns decisions are made via the profile.
Thank you for your time!
I found the module in code @
https://github.com/itinero/routing/blob/develop/src/Itinero/Profiles/Lua/ItineroLib/ItineroModule.cs
It seems we have:
parseweight, parsewidth,parsespeed,log and format so that part of the question is answered.
Thank you.
I still would like to understand why the C# version of the vehicle cannot find a point while the LUA version can however it's not a big problem since the LUA profiles can be embedded in the DB and loaded from there anyway.
If you get around to it please provide an example of a simple train profile completely in C# which is able to be used from the routerDB after serialization.
I'll do my best to get an example to you. The only thing I can think of that could cause this is the preprocessing code not handling this properly and the routerdb ending up being different.
I would also have loved to be able to support C# based profiles and still embed them in the routerdb but I haven't figured out how to do that in a way that supports all platforms.
One of the choices I made in Itinero when I started it was to link profiles in with the preprocessing of the OSM data. I should have separated those two in two different configurations. Once I can release Itinero 2.0 that should be the case.
I think I understand what your saying, what I find unusual is that there is no way to register the C# profile to the existing routerDb even though it was built using that same vehicle / profile.
I also find it unusual that it works when loaded from OSM but not when loaded from the routerDb.
Thank you for your help!