37 {}, {}, {}, {}, {}, {}, {}, {}),
39 myDefaultVehicleType(
true),
40 myDefaultVehicleTypeModified(
false) {
44 initRailVisualizationParameters();
50 {}, {}, {}, {}, {}, {}, {}, {}),
52 myDefaultVehicleType(
true),
53 myDefaultVehicleTypeModified(
false) {
55 vehicleClass = defaultVClass;
58 initRailVisualizationParameters();
64 {}, {}, {}, {}, {}, {}, {}, {}),
66 myDefaultVehicleType(
false),
67 myDefaultVehicleTypeModified(
false) {
69 initRailVisualizationParameters();
75 {}, {}, {}, {}, {}, {}, {}, {}),
77 myDefaultVehicleType(
false),
78 myDefaultVehicleTypeModified(
false) {
82 initRailVisualizationParameters();
169 return Boundary(-0.1, -0.1, 0.1, 0.1);
503 return defaultValues.
length;
509 return defaultValues.
minGap;
515 return defaultValues.
width;
521 return defaultValues.
height;
699 return canParse<double>(value) && (parse<double>(value) >= 0) && (parse<double>(value) <= 1);
736 return canParse<double>(value);
742 return canParse<double>(value) && (parse<double>(value) >= 0);
744 return canParse<double>(value) && (parse<double>(value) >= -1);
746 return canParse<double>(value) && (parse<double>(value) >= -1);
748 return canParse<double>(value) && (parse<double>(value) >= -1);
750 return canParse<double>(value) && (parse<double>(value) >= 0);
752 return canParse<double>(value) && (parse<double>(value) >= 0);
754 return canParse<double>(value) && (parse<double>(value) >= 0);
756 return canParse<double>(value) && (parse<double>(value) >= 0) && (parse<double>(value) <= 1);
758 return canParse<double>(value) && (parse<double>(value) >= 0);
760 return canParse<double>(value) && (parse<double>(value) >= 0);
769 return canParse<double>(value);
776 return canParse<double>(value) && (parse<double>(value) >= 0);
782 return canParse<double>(value) && (parse<double>(value) > 0);
788 return canParse<double>(value) && (parse<double>(value) > 0);
790 return canParse<double>(value) && (parse<double>(value) >= 0);
792 return canParse<double>(value) && (parse<double>(value) >= 0);
799 return canParse<RGBColor>(value);
806 if (value == eClass) {
812 if (value ==
"all") {
818 return canParse<double>(value);
820 return canParse<double>(value);
828 return canParse<int>(value);
830 return canParse<int>(value);
832 return canParse<double>(value);
834 return canParse<double>(value);
838 return canParse<double>(value);
840 return canParse<double>(value);
842 return canParse<double>(value) && (parse<double>(value) >= 0);
844 return canParse<double>(value) && (parse<double>(value) >= 0);
848 return canParse<double>(value) && (parse<double>(value) >= -1);
850 return canParse<double>(value) && (parse<double>(value) >= -1);
852 return canParse<double>(value) && (parse<double>(value) >= 0);
854 return canParse<bool>(value);
859 return canParse<bool>(value);
928 const std::map<std::string, std::string>&
1223 std::string parametersStr;
1226 parametersStr += parameter.first +
"=" + parameter.second +
"|";
1229 if (!parametersStr.empty()) {
1230 parametersStr.pop_back();
1248 if (vTypeDistributionID.empty()) {
1274 if (vTypeDistribution) {
1277 }
else if (vTypeDistributionParent && (vTypeDistributionParent->getChildDemandElements().size() == 1)) {
1306 WRITE_DEBUG(
"Opening FXMessageBox 'add vTypeDistribution'");
1308 const FXuint answer = FXMessageBox::question(
myNet->
getViewNet()->getApp(), MBOX_YES_NO,
1312 WRITE_DEBUG(
"Closed FXMessageBox 'add vTypeDistribution' with 'yes'");
1317 WRITE_DEBUG(
"Closed FXMessageBox 'add vTypeDistribution' with 'No'");
1320 WRITE_DEBUG(
"Closed FXMessageBox 'add vTypeDistribution' with 'ESC'");
1330 WRITE_DEBUG(
"Opening FXMessageBox 'remove vTypeDistribution'");
1332 const FXuint answer = FXMessageBox::question(
myNet->
getViewNet()->getApp(), MBOX_YES_NO,
1336 WRITE_DEBUG(
"Closed FXMessageBox 'remove vTypeDistribution' with 'yes'");
1341 WRITE_DEBUG(
"Closed FXMessageBox 'remove vTypeDistribution' with 'No'");
1344 WRITE_DEBUG(
"Closed FXMessageBox 'remove vTypeDistribution' with 'ESC'");
1408 if (value.empty()) {
1428 if (value.empty()) {
1468 if (value.empty()) {
1481 if (value.empty() || (canParse<double>(value) && (parse<double>(value) == 0))) {
1492 if (!value.empty() && (value !=
toString(defaultValues.
length))) {
1493 length = parse<double>(value);
1504 if (!value.empty() && (value !=
toString(defaultValues.
minGap))) {
1505 minGap = parse<double>(value);
1541 color = parse<RGBColor>(value);
1588 if (!value.empty() && (value !=
toString(defaultValues.
width))) {
1589 width = parse<double>(value);
1600 if (!value.empty() && (value !=
toString(defaultValues.
height))) {
1601 height = parse<double>(value);
1756 if (!value.empty() && (value != defaultValues.
osgFile)) {
1816 if (parse<bool>(value)) {
std::string time2string(SUMOTime t)
convert SUMOTime to string
SUMOTime string2time(const std::string &r)
convert string to SUMOTime
const int VTYPEPARS_MAXSPEED_SET
const int VTYPEPARS_PERSON_CAPACITY
const int VTYPEPARS_HEIGHT_SET
const int VTYPEPARS_VEHICLECLASS_SET
const int VTYPEPARS_COLOR_SET
const int VTYPEPARS_SPEEDFACTOR_SET
const int VTYPEPARS_BOARDING_DURATION
const int VTYPEPARS_IMPATIENCE_SET
const int VTYPEPARS_LATALIGNMENT_SET
const int VTYPEPARS_LENGTH_SET
const int VTYPEPARS_CARRIAGE_LENGTH_SET
const int VTYPEPARS_OSGFILE_SET
const int VTYPEPARS_LOCOMOTIVE_LENGTH_SET
const int VTYPEPARS_LANE_CHANGE_MODEL_SET
const int VTYPEPARS_MINGAP_SET
const int VTYPEPARS_IMGFILE_SET
const int VTYPEPARS_SHAPE_SET
const int VTYPEPARS_ACTIONSTEPLENGTH_SET
const int VTYPEPARS_LOADING_DURATION
@ GIVEN
The alignment as offset is given.
const int VTYPEPARS_CONTAINER_CAPACITY
const int VTYPEPARS_CARRIAGE_GAP_SET
const int VTYPEPARS_MAXSPEED_LAT_SET
const int VTYPEPARS_EMISSIONCLASS_SET
const int VTYPEPARS_PROBABILITY_SET
const int VTYPEPARS_CAR_FOLLOW_MODEL
const int VTYPEPARS_MINGAP_LAT_SET
const int VTYPEPARS_WIDTH_SET
bool canParseVehicleShape(const std::string &shape)
Checks whether the given string contains only known vehicle shape.
SUMOVehicleClass getVehicleClassID(const std::string &name)
Returns the class id of the abstract class given by its name.
SUMOVehicleShape getVehicleShapeID(const std::string &name)
Returns the class id of the shape class given by its name.
bool canParseVehicleClasses(const std::string &classes)
Checks whether the given string contains only known vehicle classes.
std::string getVehicleShapeName(SUMOVehicleShape id)
Returns the class name of the shape class given by its id.
StringBijection< SUMOVehicleShape > SumoVehicleShapeStrings(sumoVehicleShapeStringInitializer, SUMOVehicleShape::UNKNOWN, false)
SUMOVehicleClass
Definition of vehicle classes to differ between different lane usage and authority types.
const std::string DEFAULT_PEDTYPE_ID
const std::string DEFAULT_VTYPE_ID
const std::string DEFAULT_BIKETYPE_ID
@ SUMO_TAG_VTYPE
description of a vehicle/person/container type
@ SUMO_TAG_VTYPE_DISTRIBUTION
distribution of a vehicle type
SumoXMLAttr
Numbers representing SUMO-XML - attributes.
@ SUMO_ATTR_CF_EIDM_T_ACC_MAX
@ SUMO_ATTR_CF_EIDM_EPSILON_ACC
@ SUMO_ATTR_EMISSIONCLASS
@ SUMO_ATTR_JM_IGNORE_FOE_SPEED
@ SUMO_ATTR_JM_IGNORE_KEEPCLEAR_TIME
@ SUMO_ATTR_CF_EIDM_T_LOOK_AHEAD
@ SUMO_ATTR_CF_WIEDEMANN_SECURITY
@ SUMO_ATTR_LCA_ASSERTIVE
@ SUMO_ATTR_CF_EIDM_USEVEHDYNAMICS
@ SUMO_ATTR_CF_IDMM_ADAPT_TIME
@ SUMO_ATTR_LANE_CHANGE_MODEL
@ SUMO_ATTR_CF_KERNER_PHI
@ SUMO_ATTR_LCA_TURN_ALIGNMENT_DISTANCE
@ SUMO_ATTR_CF_EIDM_C_COOLNESS
@ SUMO_ATTR_CF_EIDM_SIG_ERROR
@ SUMO_ATTR_LCA_LOOKAHEADLEFT
@ SUMO_ATTR_APPARENTDECEL
@ SUMO_ATTR_LCA_SPEEDGAIN_PARAM
@ SUMO_ATTR_ACTIONSTEPLENGTH
@ GNE_ATTR_SELECTED
element is selected
@ SUMO_ATTR_LCA_IMPATIENCE
@ GNE_ATTR_VTYPE_DISTRIBUTION
vehicle type distribution
@ GNE_ATTR_PARAMETERS
parameters "key1=value1|key2=value2|...|keyN=valueN"
@ SUMO_ATTR_JM_DRIVE_AFTER_RED_TIME
@ SUMO_ATTR_LOADING_DURATION
@ SUMO_ATTR_CF_EIDM_MAX_VEH_PREVIEW
@ SUMO_ATTR_LCA_MAXSPEEDLATFACTOR
@ GNE_ATTR_DEFAULT_VTYPE
Flag to check if VType is a default VType.
@ SUMO_ATTR_CF_EIDM_T_REACTION
@ SUMO_ATTR_CF_EIDM_T_PERSISTENCE_ESTIMATE
@ SUMO_ATTR_CF_PWAGNER2009_TAULAST
@ SUMO_ATTR_CF_EIDM_SIG_GAP
@ SUMO_ATTR_CAR_FOLLOW_MODEL
@ SUMO_ATTR_CF_EIDM_JERK_MAX
@ SUMO_ATTR_LCA_MAXSPEEDLATSTANDING
@ SUMO_ATTR_JM_DRIVE_AFTER_YELLOW_TIME
@ SUMO_ATTR_LCA_KEEPRIGHT_PARAM
@ SUMO_ATTR_JM_IGNORE_FOE_PROB
@ SUMO_ATTR_CONTAINER_CAPACITY
@ SUMO_ATTR_LCA_COOPERATIVE_PARAM
@ SUMO_ATTR_LCA_OPPOSITE_PARAM
@ SUMO_ATTR_EMERGENCYDECEL
@ SUMO_ATTR_LCA_SUBLANE_PARAM
@ SUMO_ATTR_JM_CROSSING_GAP
@ SUMO_ATTR_CARRIAGE_LENGTH
@ SUMO_ATTR_CF_IDM_STEPPING
@ SUMO_ATTR_CF_IDMM_ADAPT_FACTOR
@ SUMO_ATTR_COLLISION_MINGAP_FACTOR
@ SUMO_ATTR_BOARDING_DURATION
@ SUMO_ATTR_CF_EIDM_M_FLATNESS
@ SUMO_ATTR_JM_SIGMA_MINOR
@ SUMO_ATTR_CF_EIDM_M_BEGIN
@ SUMO_ATTR_CF_EIDM_T_PERSISTENCE_DRIVE
@ SUMO_ATTR_CF_EIDM_SIG_LEADER
@ SUMO_ATTR_COLOR
A color information.
@ SUMO_ATTR_CF_PWAGNER2009_APPROB
@ GNE_ATTR_DEFAULT_VTYPE_MODIFIED
Flag to check if a default VType was modified.
@ SUMO_ATTR_LCA_OVERTAKE_RIGHT
@ SUMO_ATTR_LCA_ACCEL_LAT
@ SUMO_ATTR_LCA_STRATEGIC_PARAM
@ SUMO_ATTR_PERSON_CAPACITY
@ SUMO_ATTR_LCA_EXPERIMENTAL1
@ SUMO_ATTR_LOCOMOTIVE_LENGTH
@ SUMO_ATTR_JM_DRIVE_RED_SPEED
@ SUMO_ATTR_LCA_TIME_TO_IMPATIENCE
@ SUMO_ATTR_JM_TIMEGAP_MINOR
@ SUMO_ATTR_CF_WIEDEMANN_ESTIMATION
@ SUMO_ATTR_LCA_SPEEDGAINRIGHT
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
A class that stores a 2D geometrical boundary.
void parse(const std::string &description, const bool hardFail)
Overwrite by parsable distribution description.
static bool isValidDescription(const std::string &description)
validate input description
bool isAttributeCarrierSelected() const
check if attribute carrier is selected
friend class GNEChange_Attribute
declare friend class
const std::string & getTagStr() const
get tag assigned to this object in string format
const GNETagProperties & getTagProperty() const
get tagProperty associated with this Attribute Carrier
void unselectAttributeCarrier(const bool changeFlag=true)
unselect attribute carrier using GUIGlobalSelection
GNENet * myNet
pointer to net
void selectAttributeCarrier(const bool changeFlag=true)
select attribute carrier using GUIGlobalSelection
const GNETagProperties & myTagProperty
reference to tagProperty associated with this attribute carrier
bool requireUpdateGeometry() const
return true if atribute requires a update geometry in setAttribute(...)
the function-object for an editing operation (abstract base)
void forceChange()
force change
An Element which don't belongs to GNENet but has influency in the simulation.
virtual std::string getAttribute(SumoXMLAttr key) const =0
void setVTypeDistributionParent(const std::string &value)
set VTypeDistribution parent
virtual void setAttribute(SumoXMLAttr key, const std::string &value, GNEUndoList *undoList)=0
method for setting the attribute and letting the object perform demand element changes
Problem
enum class for demandElement problems
const std::string & getID() const
get ID
const std::vector< GNEDemandElement * > & getChildDemandElements() const
return child demand elements
const std::vector< GNEDemandElement * > & getParentDemandElements() const
get parent demand elements
This lane is powered by an underlying GNEEdge and basically knows how to draw itself.
GNEDemandElement * retrieveDemandElement(SumoXMLTag type, const std::string &id, bool hardFail=true) const
Returns the named demand element.
A NBNetBuilder extended by visualisation and editing capabilities.
GNENetHelper::AttributeCarriers * getAttributeCarriers() const
get all attribute carriers used in this net
GNEViewNet * getViewNet() const
get view net
const GNEAttributeProperties & getAttributeProperties(SumoXMLAttr attr) const
get attribute (throw error if doesn't exist)
GUIIcon getGUIIcon() const
get GUI icon associated to this Tag
const std::string & getDefaultValue(SumoXMLAttr attr) const
return the default value of the attribute of an element
bool hasAttribute(SumoXMLAttr attr) const
check if current TagProperties owns the attribute "attr"
void end()
End undo command sub-group. If the sub-group is still empty, it will be deleted; otherwise,...
void begin(GUIIcon icon, const std::string &description)
Begin undo command sub-group with current supermode. This begins a new group of commands that are tre...
void add(GNEChange *command, bool doit=false, bool merge=true)
Add new command, executing it if desired. The new command will be merged with the previous command if...
void changeAttribute(GNEChange_Attribute *change)
special method for change attributes, avoid empty changes, always execute
Problem isDemandElementValid() const
check if current demand element is valid to be writed into XML
double getExaggeration(const GUIVisualizationSettings &s) const
return exaggeration asociated with this GLObject
std::string getHierarchyName() const
get Hierarchy Name (Used in AC Hierarchy)
void toogleAttribute(SumoXMLAttr key, const bool value, const int previousParameters)
method for enable or disable the attribute and nothing else (used in GNEChange_EnableAttribute)
void updateGeometry()
update pre-computed geometry information
std::string getParentName() const
Returns the name of the parent object.
bool askRemoveVTypeDistribution(const std::string &vTypeDistribution) const
ask if remove vTypeDistribution
bool isValid(SumoXMLAttr key, const std::string &value)
method for checking if the key and their conrrespond attribute are valids
bool askAddVTypeDistribution(const std::string &vTypeDistribution) const
ask if add vTypeDistribution
GNEMoveOperation * getMoveOperation()
get move operation
Position getPositionInView() const
Returns position of additional in view.
void drawPartialGL(const GUIVisualizationSettings &s, const GNELane *lane, const GNEPathManager::Segment *segment, const double offsetFront) const
Draws partial object.
bool isAttributeEnabled(SumoXMLAttr key) const
void setAttribute(SumoXMLAttr key, const std::string &value, GNEUndoList *undoList)
method for setting the attribute and letting the object perform demand element changes
static void overwriteVType(GNEDemandElement *vType, const SUMOVTypeParameter newVTypeParameter, GNEUndoList *undoList)
overwrite all values of GNEVType with a SUMOVTypeParameter
void updateDefaultVClassAttributes(const VClassDefaultValues &defaultValues)
function called after set new VClass
void commitMoveShape(const GNEMoveResult &moveResult, GNEUndoList *undoList)
commit move shape
GNELane * getFirstPathLane() const
get first path lane
GNELane * getLastPathLane() const
get last path lane
void splitEdgeGeometry(const double splitPosition, const GNENetworkElement *originalElement, const GNENetworkElement *newElement, GNEUndoList *undoList)
split geometry
void writeDemandElement(OutputDevice &device) const
writte demand element element into a xml file
bool myDefaultVehicleType
flag to check if this GNEVType is a default vehicle Type (For Vehicles, Pedestrians....
const RGBColor & getColor() const
get color
std::string getDemandElementProblem() const
return a string with the current demand element problem
const std::map< std::string, std::string > & getACParametersMap() const
get parameters map
void fixDemandElementProblem()
fix demand element problem
void setMoveShape(const GNEMoveResult &moveResult)
set move shape
GNEVType(GNENet *net)
default constructor
Position getAttributePosition(SumoXMLAttr key) const
double getAttributeDouble(SumoXMLAttr key) const
void drawGL(const GUIVisualizationSettings &s) const
Draws the object.
std::string getAttribute(SumoXMLAttr key) const
inherited from GNEAttributeCarrier
Boundary getCenteringBoundary() const
Returns the boundary to which the view shall be centered in order to show the object.
void computePathElement()
compute pathElement
std::string getPopUpID() const
get PopPup ID (Used in AC Hierarchy)
SUMOVehicleClass getVClass() const
void enableAttribute(SumoXMLAttr key, GNEUndoList *undoList)
void editVTypeDistribution(const std::string &vTypeDistributionID, GNEUndoList *undoList)
edit vType distribution
void disableAttribute(SumoXMLAttr key, GNEUndoList *undoList)
bool myDefaultVehicleTypeModified
flag to check if this default GNEVType was modified
virtual const std::string & getMicrosimID() const
Returns the id of the object as known to microsim.
virtual void setMicrosimID(const std::string &newID)
Changes the microsimID of the object.
Stores the information about how to visualize structures.
Static storage of an output device and its base (abstract) implementation.
static bool areParametersValid(const std::string &value, bool report=false, const std::string kvsep="=", const std::string sep="|")
check if given string can be parsed to a parameters map "key1=value1|key2=value2|....
void unsetParameter(const std::string &key)
Removes a parameter.
void setParametersStr(const std::string ¶msString, const std::string kvsep="=", const std::string sep="|")
set the inner key/value map in string format "key1=value1|key2=value2|...|keyN=valueN"
virtual const std::string getParameter(const std::string &key, const std::string defaultValue="") const
Returns the value for a given key.
virtual void setParameter(const std::string &key, const std::string &value)
Sets a parameter.
std::string getParametersStr(const std::string kvsep="=", const std::string sep="|") const
Returns the inner key/value map in string format "key1=value1|key2=value2|...|keyN=valueN".
const std::map< std::string, std::string > & getParametersMap() const
Returns the inner key/value map.
bool knowsParameter(const std::string &key) const
Returns whether the parameter is known.
static std::string getName(const SUMOEmissionClass c)
Checks whether the string describes a known vehicle class.
static const std::vector< std::string > & getAllClassesStr()
Get all SUMOEmissionClass in string format.
static SUMOEmissionClass getClassByName(const std::string &eClass, const SUMOVehicleClass vc=SVC_IGNORING)
Checks whether the string describes a known vehicle class.
A point in 2D or 3D with translation and scaling methods.
Structure representing possible vehicle parameter.
static bool isValidLatAlignment(const std::string &val)
double width
This class' width.
SubParams cfParameter
Car-following parameter.
void write(OutputDevice &dev) const
Writes the vtype.
double defaultProbability
The probability when being added to a distribution without an explicit probability.
SUMOTime actionStepLength
The vehicle type's default actionStepLength [ms], i.e. the interval between two control actions....
double height
This class' height.
SUMOEmissionClass emissionClass
The emission class of this vehicle.
double latAlignmentOffset
(optional) The vehicle's desired lateral alignment as offset in m from center line
std::string getJMParamString(const SumoXMLAttr attr, const std::string defaultValue) const
Returns the named value from the map, or the default if it is not contained there.
double carriageLength
the length of train carriages and locomotive
double length
The physical vehicle length.
double maxSpeedLat
The vehicle type's maximum lateral speed [m/s].
bool wasSet(int what) const
Returns whether the given parameter was set.
double minGap
This class' free space in front of the vehicle itself.
std::string imgFile
Image file for this class.
SUMOVehicleShape shape
This class' shape.
int personCapacity
The person capacity of the vehicle.
Distribution_Parameterized speedFactor
The factor by which the maximum speed may deviate from the allowed max speed on the street.
std::string osgFile
3D model file for this class
double maxSpeed
The vehicle type's maximum speed [m/s].
int parametersSet
Information for the router which parameter were set.
int containerCapacity
The container capacity of the vehicle.
SUMOTime boardingDuration
The time a person needs to board the vehicle.
double getCFParam(const SumoXMLAttr attr, const double defaultValue) const
Returns the named value from the map, or the default if it is not contained there.
double minGapLat
The vehicle type's minimum lateral gap [m].
SUMOVehicleClass vehicleClass
The vehicle's class.
SUMOTime loadingDuration
The time a container needs to get loaded on the vehicle.
std::string getCFParamString(const SumoXMLAttr attr, const std::string defaultValue) const
Returns the named value from the map, or the default if it is not contained there.
SumoXMLTag cfModel
The enum-representation of the car-following model to use.
std::string getLCParamString(const SumoXMLAttr attr, const std::string &defaultValue) const
Returns the named value from the map, or the default if it is not contained there.
SubParams lcParameter
Lane-changing parameter.
LatAlignmentDefinition latAlignmentProcedure
Information on how the vehicle shall choose the lateral alignment.
SubParams jmParameter
Junction-model parameter.
double impatience
The vehicle's impatience (willingness to obstruct others)
LaneChangeModel lcModel
The lane-change model to use.
static bool parseLatAlignment(const std::string &val, double &lao, LatAlignmentDefinition &lad)
Parses and validates a given latAlignment value.
static StringBijection< SumoXMLTag > CarFollowModels
car following models
static bool isValidVehicleID(const std::string &value)
whether the given string is a valid id for a vehicle or flow
static bool isValidFilename(const std::string &value)
whether the given string is a valid attribute for a filename (for example, a name)
static StringBijection< TrainType > TrainTypes
train types
static StringBijection< LaneChangeModel > LaneChangeModels
lane change models
bool hasString(const std::string &str) const
const std::string & getString(const T key) const
T get(const std::string &str) const
struct for default values that depend of VClass
double height
This class' height.
int personCapacity
The person capacity of the vehicle.
double carriageLength
the length of train carriages
std::string osgFile
3D model file for this class
SUMOEmissionClass emissionClass
The emission class of this vehicle.
double minGap
This class' free space in front of the vehicle itself.
int containerCapacity
The container capacity of the vehicle.
double maxSpeed
The vehicle type's maximum speed [m/s].
Distribution_Parameterized speedFactor
The factor by which the maximum speed may deviate from the allowed max speed on the street.
double width
This class' width.
double length
The physical vehicle length.
SUMOVehicleShape shape
This class' shape.
double locomotiveLength
the length of train locomotive