#include <config.h>
#include <cmath>
#include <vector>
#include <string>
#include <bitset>
#include <utils/common/MsgHandler.h>
#include <utils/common/StringUtils.h>
#include <utils/vehicle/SUMOVehicleParameter.h>
#include <utils/emissions/PollutantsInterface.h>
#include <utils/geom/GeomHelper.h>
#include <utils/gui/globjects/GLIncludes.h>
#include <utils/gui/windows/GUISUMOAbstractView.h>
#include <utils/gui/windows/GUIAppEnum.h>
#include <utils/gui/images/GUITexturesHelper.h>
#include <utils/gui/div/GUIParameterTableWindow.h>
#include <utils/gui/div/GUIGlobalSelection.h>
#include <utils/gui/div/GLHelper.h>
#include <utils/gui/div/GLObjectValuePassConnector.h>
#include <utils/gui/div/GUIGlobalSelection.h>
#include <utils/gui/div/GUIBaseVehicleHelper.h>
#include <microsim/MSGlobals.h>
#include <microsim/MSVehicle.h>
#include <microsim/MSJunction.h>
#include <microsim/MSLane.h>
#include <microsim/MSLink.h>
#include <microsim/MSStop.h>
#include <microsim/MSParkingArea.h>
#include <microsim/MSTrainHelper.h>
#include <microsim/logging/CastingFunctionBinding.h>
#include <microsim/logging/FunctionBinding.h>
#include <microsim/lcmodels/MSAbstractLaneChangeModel.h>
#include <microsim/devices/MSDevice_Vehroutes.h>
#include <microsim/devices/MSDevice_Routing.h>
#include <microsim/devices/MSRoutingEngine.h>
#include <microsim/devices/MSDevice_Transportable.h>
#include <microsim/devices/MSDevice_BTreceiver.h>
#include <microsim/devices/MSDevice_ElecHybrid.h>
#include <microsim/devices/MSDevice_Battery.h>
#include <microsim/traffic_lights/MSDriveWay.h>
#include <gui/GUIApplicationWindow.h>
#include <gui/GUIGlobals.h>
#include "GUIVehicle.h"
#include "GUIPerson.h"
#include "GUIContainer.h"
#include "GUINet.h"
#include "GUIEdge.h"
#include "GUILane.h"
#define SPEEDMODE_DEFAULT 31
#define LANECHANGEMODE_DEFAULT 1621
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4355)
#endif
GUIVehicle::GUIVehicle(SUMOVehicleParameter* pars, ConstMSRoutePtr route,
MSVehicleType* type, const double speedFactor) :
MSVehicle(pars, route, type, speedFactor),
GUIBaseVehicle((MSBaseVehicle&) * this) {
}
#ifdef _MSC_VER
#pragma warning(pop)
#endif
GUIVehicle::~GUIVehicle() {
gSelected.deselect(GLO_VEHICLE, getGlID());
}
GUIParameterTableWindow*
GUIVehicle::getParameterWindow(GUIMainWindow& app,
GUISUMOAbstractView&) {
const bool isElecHybrid = getDevice(typeid(MSDevice_ElecHybrid)) != nullptr ? true : false;
const bool hasBattery = getDevice(typeid(MSDevice_Battery)) != nullptr;
GUIParameterTableWindow* ret = new GUIParameterTableWindow(app, *this);
ret->mkItem(TL("lane [id]"), true, new FunctionBindingString<GUIVehicle>(this, &GUIVehicle::getLaneID));
if (MSGlobals::gSublane) {
ret->mkItem(TL("shadow lane [id]"), true, new FunctionBindingString<GUIVehicle>(this, &GUIVehicle::getShadowLaneID));
}
if (MSGlobals::gLateralResolution > 0) {
ret->mkItem(TL("target lane [id]"), true, new FunctionBindingString<GUIVehicle>(this, &GUIVehicle::getTargetLaneID));
}
if (isSelected()) {
ret->mkItem(TL("back lanes [id,..]"), true, new FunctionBindingString<GUIVehicle>(this, &GUIVehicle::getBackLaneIDs));
}
ret->mkItem(TL("position [m]"), true,
new FunctionBinding<GUIVehicle, double>(this, &MSVehicle::getPositionOnLane));
ret->mkItem(TL("lateral offset [m]"), true,
new FunctionBinding<GUIVehicle, double>(this, &GUIVehicle::getLateralPositionOnLane));
ret->mkItem(TL("speed [m/s]"), true,
new FunctionBinding<GUIVehicle, double>(this, &MSVehicle::getSpeed));
ret->mkItem(TL("lateral speed [m/s]"), true,
new FunctionBinding<MSAbstractLaneChangeModel, double>(&getLaneChangeModel(), &MSAbstractLaneChangeModel::getSpeedLat));
ret->mkItem(TL("acceleration [m/s^2]"), true,
new FunctionBinding<GUIVehicle, double>(this, &MSVehicle::getAcceleration));
ret->mkItem(TL("angle [degree]"), true,
new FunctionBinding<GUIVehicle, double>(this, &GUIBaseVehicle::getNaviDegree));
ret->mkItem(TL("slope [degree]"), true,
new FunctionBinding<GUIVehicle, double>(this, &MSVehicle::getSlope));
ret->mkItem(TL("speed factor"), true,
new FunctionBinding<GUIVehicle, double>(this, &MSVehicle::getChosenSpeedFactor));
ret->mkItem(TL("time gap on lane [s]"), true,
new FunctionBinding<GUIVehicle, double>(this, &MSVehicle::getTimeGapOnLane));
ret->mkItem(TL("waiting time [s]"), true,
new FunctionBinding<GUIVehicle, double>(this, &MSVehicle::getWaitingSeconds));
ret->mkItem(TLF("waiting time (accumulated, % s) [s]", time2string(MSGlobals::gWaitingTimeMemory)).c_str(), true,
new FunctionBinding<GUIVehicle, double>(this, &MSVehicle::getAccumulatedWaitingSeconds));
ret->mkItem(TL("time since startup [s]"), true,
new FunctionBinding<GUIVehicle, double>(this, &MSVehicle::getTimeSinceStartupSeconds));
ret->mkItem(TL("time loss [s]"), true,
new FunctionBinding<GUIVehicle, double>(this, &MSVehicle::getTimeLossSeconds));
ret->mkItem(TL("impatience"), true,
new FunctionBinding<GUIVehicle, double>(this, &MSVehicle::getImpatience));
ret->mkItem(TL("last lane change [s]"), true,
new FunctionBinding<GUIVehicle, double>(this, &GUIVehicle::getLastLaneChangeOffset));
ret->mkItem(TL("desired depart [s]"), false, time2string(getParameter().depart));
ret->mkItem(TL("depart delay [s]"), false, time2string(getDepartDelay()));
ret->mkItem(TL("odometer [m]"), true,
new FunctionBinding<GUIVehicle, double>(this, &MSBaseVehicle::getOdometer));
if (getParameter().repetitionNumber < std::numeric_limits<int>::max()) {
ret->mkItem(TL("remaining [#]"), false, (int) getParameter().repetitionNumber - getParameter().repetitionsDone);
}
if (getParameter().repetitionOffset > 0) {
ret->mkItem(TL("insertion period [s]"), false, time2string(getParameter().repetitionOffset));
}
if (getParameter().repetitionProbability > 0) {
ret->mkItem(TL("insertion probability"), false, getParameter().repetitionProbability);
}
if (getParameter().poissonRate > 0) {
ret->mkItem(TL("poisson rate"), false, getParameter().poissonRate);
}
ret->mkItem(TL("stop info"), true, new FunctionBindingString<GUIVehicle>(this, &GUIVehicle::getStopInfo));
ret->mkItem(TL("line"), false, myParameter->line);
ret->mkItem(TL("CO2 [mg/s]"), true,
new FunctionBinding<GUIVehicle, double>(this, &MSVehicle::getEmissions<PollutantsInterface::CO2>));
ret->mkItem(TL("CO [mg/s]"), true,
new FunctionBinding<GUIVehicle, double>(this, &MSVehicle::getEmissions<PollutantsInterface::CO>));
ret->mkItem(TL("HC [mg/s]"), true,
new FunctionBinding<GUIVehicle, double>(this, &MSVehicle::getEmissions<PollutantsInterface::HC>));
ret->mkItem(TL("NOx [mg/s]"), true,
new FunctionBinding<GUIVehicle, double>(this, &MSVehicle::getEmissions<PollutantsInterface::NO_X>));
ret->mkItem(TL("PMx [mg/s]"), true,
new FunctionBinding<GUIVehicle, double>(this, &MSVehicle::getEmissions<PollutantsInterface::PM_X>));
ret->mkItem(TL("fuel [mg/s]"), true,
new FunctionBinding<GUIVehicle, double>(this, &MSVehicle::getEmissions<PollutantsInterface::FUEL>));
ret->mkItem(TL("electricity [Wh/s]"), true,
new FunctionBinding<GUIVehicle, double>(this, &MSVehicle::getEmissions<PollutantsInterface::ELEC>));
ret->mkItem(TL("noise (Harmonoise) [dB]"), true,
new FunctionBinding<GUIVehicle, double>(this, &MSVehicle::getHarmonoise_NoiseEmissions));
ret->mkItem(TL("devices"), false, getDeviceDescription());
ret->mkItem(TL("persons"), true,
new FunctionBinding<GUIVehicle, int>(this, &MSVehicle::getPersonNumber));
ret->mkItem(TL("containers"), true,
new FunctionBinding<GUIVehicle, int>(this, &MSVehicle::getContainerNumber));
ret->mkItem(TL("lcState right"), true, new FunctionBindingString<GUIVehicle>(this, &GUIVehicle::getLCStateRight));
ret->mkItem(TL("lcState left"), true, new FunctionBindingString<GUIVehicle>(this, &GUIVehicle::getLCStateLeft));
ret->mkItem(TL("parking badges"), false, joinToString(getParkingBadges(), " "));
if (MSGlobals::gLateralResolution > 0) {
ret->mkItem(TL("lcState center"), true, new FunctionBindingString<GUIVehicle>(this, &GUIVehicle::getLCStateCenter));
ret->mkItem(TL("right side on edge [m]"), true, new FunctionBinding<GUIVehicle, double>(this, &GUIVehicle::getRightSideOnEdge2));
ret->mkItem(TL("left side on edge [m]"), true, new FunctionBinding<GUIVehicle, double>(this, &GUIVehicle::getLeftSideOnEdge));
ret->mkItem(TL("rightmost edge sublane [#]"), true, new FunctionBinding<GUIVehicle, int>(this, &GUIVehicle::getRightSublaneOnEdge));
ret->mkItem(TL("leftmost edge sublane [#]"), true, new FunctionBinding<GUIVehicle, int>(this, &GUIVehicle::getLeftSublaneOnEdge));
ret->mkItem(TL("lane change maneuver distance [m]"), true, new FunctionBinding<GUIVehicle, double>(this, &GUIVehicle::getManeuverDist));
}
if (isRailway(getVClass())) {
ret->mkItem(TL("driveways"), true, new FunctionBindingString<GUIVehicle>(this, &GUIVehicle::getDriveWays));
}
if (hasBattery || isElecHybrid) {
ret->mkItem(TL("present state of charge [Wh]"), true,
new FunctionBinding<GUIVehicle, double>(this, &MSVehicle::getStateOfCharge));
}
if (hasBattery) {
ret->mkItem(TL("relative state of charge (SoC) [-]"), true,
new FunctionBinding<GUIVehicle, double>(this, &MSVehicle::getRelativeStateOfCharge));
ret->mkItem(TL("current timestep charge [Wh]"), true,
new FunctionBinding<GUIVehicle, double>(this, &MSVehicle::getChargedEnergy));
ret->mkItem(TL("maximum charge rate [W]"), true,
new FunctionBinding<GUIVehicle, double>(this, &MSVehicle::getMaxChargeRate));
}
if (isElecHybrid) {
ret->mkItem(TL("present electric current [A]"), true,
new FunctionBinding<GUIVehicle, double>(this, &MSVehicle::getElecHybridCurrent));
}
if (hasInfluencer()) {
if (getInfluencer().getSpeedMode() != SPEEDMODE_DEFAULT) {
ret->mkItem(TL("speed mode"), true, new FunctionBindingString<GUIVehicle>(this, &GUIVehicle::getSpeedMode));
}
if (getInfluencer().getLaneChangeMode() != LANECHANGEMODE_DEFAULT) {
ret->mkItem(TL("lane change mode"), true, new FunctionBindingString<GUIVehicle>(this, &GUIVehicle::getLaneChangeMode));
}
}
ret->closeBuilding(&getParameter());
return ret;
}
GUIParameterTableWindow*
GUIVehicle::getTypeParameterWindow(GUIMainWindow& app, GUISUMOAbstractView&) {
GUIParameterTableWindow* ret = new GUIParameterTableWindow(app, *this, "vType:" + myType->getID());
ret->mkItem(TL("type"), false, myType->getID());
ret->mkItem(TL("length [m]"), false, myType->getLength());
ret->mkItem(TL("width [m]"), false, myType->getWidth());
ret->mkItem(TL("height [m]"), false, myType->getHeight());
ret->mkItem(TL("minGap [m]"), false, myType->getMinGap());
ret->mkItem(TL("vehicle class"), false, SumoVehicleClassStrings.getString(myType->getVehicleClass()));
ret->mkItem(TL("emission class"), false, PollutantsInterface::getName(myType->getEmissionClass()));
ret->mkItem(TL("mass [kg]"), false, myType->getMass());
ret->mkItem(TL("car-following model"), false, SUMOXMLDefinitions::CarFollowModels.getString((SumoXMLTag)getCarFollowModel().getModelID()));
ret->mkItem(TL("lane-change model"), false, SUMOXMLDefinitions::LaneChangeModels.getString(getLaneChangeModel().getModelID()));
ret->mkItem(TL("guiShape"), false, getVehicleShapeName(myType->getGuiShape()));
ret->mkItem(TL("maximum speed [m/s]"), false, getVehicleType().getMaxSpeed());
ret->mkItem(TL("desired maximum speed [m/s]"), false, getVehicleType().getDesiredMaxSpeed());
ret->mkItem(TL("maximum acceleration [m/s^2]"), false, getCarFollowModel().getMaxAccel());
ret->mkItem(TL("maximum deceleration [m/s^2]"), false, getCarFollowModel().getMaxDecel());
ret->mkItem(TL("emergency deceleration [m/s^2]"), false, getCarFollowModel().getEmergencyDecel());
ret->mkItem(TL("apparent deceleration [m/s^2]"), false, getCarFollowModel().getApparentDecel());
ret->mkItem(TL("imperfection (sigma)"), false, getCarFollowModel().getImperfection());
ret->mkItem(TL("desired headway (tau) [s]"), false, getCarFollowModel().getHeadwayTime());
ret->mkItem(TL("speedfactor"), false, myType->getParameter().speedFactor.toStr(gPrecision));
ret->mkItem(TL("startupDelay [s]"), false, STEPS2TIME(getCarFollowModel().getStartupDelay()));
if (myType->getParameter().wasSet(VTYPEPARS_ACTIONSTEPLENGTH_SET)) {
ret->mkItem(TL("action step length [s]"), false, myType->getActionStepLengthSecs());
}
ret->mkItem(TL("person capacity"), false, myType->getPersonCapacity());
ret->mkItem(TL("boarding time [s]"), false, STEPS2TIME(myType->getLoadingDuration(true)));
ret->mkItem(TL("container capacity"), false, myType->getContainerCapacity());
ret->mkItem(TL("loading time [s]"), false, STEPS2TIME(myType->getLoadingDuration(false)));
if (MSGlobals::gLateralResolution > 0) {
ret->mkItem(TL("minGapLat [m]"), false, myType->getMinGapLat());
ret->mkItem(TL("maxSpeedLat [m/s]"), false, myType->getMaxSpeedLat());
ret->mkItem(TL("latAlignment"), true, new FunctionBindingString<GUIVehicle>(this, &GUIVehicle::getDynamicAlignment));
} else if (MSGlobals::gLaneChangeDuration > 0) {
ret->mkItem(TL("maxSpeedLat [m/s]"), false, myType->getMaxSpeedLat());
}
for (auto item : myType->getParameter().lcParameter) {
ret->mkItem(toString(item.first).c_str(), false, toString(item.second));
}
for (auto item : myType->getParameter().jmParameter) {
ret->mkItem(toString(item.first).c_str(), false, toString(item.second));
}
if (MSGlobals::gModelParkingManoeuver) {
ret->mkItem(TL("manoeuver Angle vs Times"), false, myType->getParameter().getManoeuverAngleTimesS());
}
ret->closeBuilding(&(myType->getParameter()));
return ret;
}
std::string
GUIVehicle::getDynamicAlignment() const {
std::string align = myType->getPreferredLateralAlignment() == LatAlignmentDefinition::GIVEN
? toString(myType->getPreferredLateralAlignmentOffset())
: toString(myType->getPreferredLateralAlignment());
std::string align2 = toString(getLaneChangeModel().getDesiredAlignment());
if (align2 != align) {
align = align2 + " (default: " + align + ")";
}
return align;
}
void
GUIVehicle::drawAction_drawLinkItems(const GUIVisualizationSettings& s) const {
glTranslated(0, 0, getType() + .2);
for (DriveItemVector::const_iterator i = myLFLinkLanes.begin(); i != myLFLinkLanes.end(); ++i) {
if ((*i).myLink == nullptr) {
continue;
}
MSLink* link = (*i).myLink;
MSLane* via = link->getViaLaneOrLane();
if (via != nullptr) {
Position p = via->getShape()[0];
if ((*i).mySetRequest) {
glColor3d(0, .8, 0);
} else {
glColor3d(.8, 0, 0);
}
const SUMOTime leaveTime = (*i).myLink->getLeaveTime(
(*i).myArrivalTime, (*i).myArrivalSpeed, (*i).getLeaveSpeed(), getVehicleType().getLength());
drawLinkItem(p, (*i).myArrivalTime, leaveTime, s.vehicleName.size / s.scale);
MSLink::ApproachingVehicleInformation avi = (*i).myLink->getApproaching(this);
assert(avi.arrivalTime == (*i).myArrivalTime && avi.leavingTime == leaveTime);
UNUSED_PARAMETER(avi);
}
}
glTranslated(0, 0, getType() - .2);
}
void
GUIVehicle::drawAction_drawCarriageClass(const GUIVisualizationSettings& s, double scaledLength, bool asImage) const {
RGBColor current = GLHelper::getColor();
RGBColor darker = current.changedBrightness(-51);
const double exaggeration = (s.vehicleSize.getExaggeration(s, this)
* s.vehicleScaler.getScheme().getColor(getScaleValue(s, s.vehicleScaler.getActive())));
if (exaggeration == 0) {
return;
}
MSTrainHelper trainHelper(this, scaledLength, isReversed() && s.drawReversed, s.secondaryShape, exaggeration, s.vehicleQuality);
const int numCarriages = trainHelper.getNumCarriages();
const int firstPassengerCarriage = trainHelper.getFirstPassengerCarriage();
const int noPersonsBackCarriages = (getVehicleType().getGuiShape() == SUMOVehicleShape::TRUCK_SEMITRAILER || getVehicleType().getGuiShape() == SUMOVehicleShape::TRUCK_1TRAILER) && numCarriages > 1 ? 1 : 0;
const int firstContainerCarriage = numCarriages == 1 || getVehicleType().getGuiShape() == SUMOVehicleShape::TRUCK_1TRAILER ? 0 : 1;
const int seatsPerCarriage = (int)ceil(getVType().getPersonCapacity() / (numCarriages - firstPassengerCarriage - noPersonsBackCarriages));
const int containersPerCarriage = (int)ceil(getVType().getContainerCapacity() / (numCarriages - firstContainerCarriage));
int requiredSeats = getNumPassengers();
int requiredPositions = getNumContainers();
if (requiredSeats > 0) {
mySeatPositions.clear();
}
if (requiredPositions > 0) {
myContainerPositions.clear();
}
GLHelper::popMatrix();
const double xCornerCut = 0.3 * exaggeration;
const double yCornerCut = MIN2(0.4 * trainHelper.getUpscaleLength(), trainHelper.getUpscaleLength() * scaledLength / 4);
Position front, back;
double angle = 0.0;
double curCLength = trainHelper.getFirstCarriageLength();
const std::vector<MSTrainHelper::Carriage*>& carriages = trainHelper.getCarriages();
for (int i = 0; i < numCarriages; ++i) {
front = carriages[i]->front;
back = carriages[i]->back;
if (front == back) {
continue;
}
const double drawnCarriageLength = front.distanceTo2D(back);
angle = atan2((front.x() - back.x()), (back.y() - front.y())) * (double) 180.0 / (double) M_PI;
const bool reversed = trainHelper.isReversed();
if (reversed) {
if (i <= numCarriages - firstPassengerCarriage) {
computeSeats(back, front, SUMO_const_waitingPersonWidth, seatsPerCarriage, exaggeration, requiredSeats, mySeatPositions);
}
if (i <= numCarriages - firstContainerCarriage) {
computeSeats(front, back, SUMO_const_waitingContainerWidth, containersPerCarriage, exaggeration, requiredPositions, myContainerPositions);
}
} else {
if (i >= firstPassengerCarriage) {
computeSeats(front, back, SUMO_const_waitingPersonWidth, seatsPerCarriage, exaggeration, requiredSeats, mySeatPositions);
}
if (i >= firstContainerCarriage) {
computeSeats(front, back, SUMO_const_waitingContainerWidth, containersPerCarriage, exaggeration, requiredPositions, myContainerPositions);
}
}
curCLength = (i == trainHelper.getFirstCarriageNo() ? trainHelper.getFirstCarriageLength() : trainHelper.getCarriageLength());
GLHelper::pushMatrix();
if (s.trueZ) {
glTranslated(front.x(), front.y(), front.z() + 1);
} else {
glTranslated(front.x(), front.y(), getType());
}
glRotated(angle, 0, 0, 1);
double halfWidth = trainHelper.getHalfWidth();
std::string imgFile = getVType().getImgFile();
if (asImage && i != trainHelper.getFirstCarriageNo()) {
const size_t nImages = getVType().getParameter().carriageImages.size();
if (nImages > 0) {
const int carIndex = trainHelper.isReversed() ? numCarriages - i : i;
imgFile = getVType().getParameter().carriageImages[MIN2((int)nImages - 1, carIndex - 1)];
}
}
if (!asImage || !GUIBaseVehicleHelper::drawAction_drawVehicleAsImage(s, imgFile, this, getVType().getWidth() * exaggeration, curCLength)) {
switch (getVType().getGuiShape()) {
case SUMOVehicleShape::TRUCK_SEMITRAILER:
case SUMOVehicleShape::TRUCK_1TRAILER:
if (i == trainHelper.getFirstCarriageNo()) {
GLHelper::pushMatrix();
if (getVType().getGuiShape() == SUMOVehicleShape::TRUCK_SEMITRAILER) {
glScaled(1, curCLength / 2.5, 1);
}
GUIBaseVehicleHelper::drawAction_drawVehicleAsPoly(s, getVType().getGuiShape(), getVType().getWidth() * exaggeration, curCLength, 0, false, reversed);
GLHelper::popMatrix();
} else {
GLHelper::setColor(current);
GLHelper::drawBoxLine(Position(0, 0), 180, curCLength, halfWidth);
}
break;
default: {
if (i == trainHelper.getFirstCarriageNo()) {
GLHelper::setColor(darker);
} else {
GLHelper::setColor(current);
}
glBegin(GL_TRIANGLE_FAN);
glVertex2d(-halfWidth + xCornerCut, 0);
glVertex2d(-halfWidth, yCornerCut);
glVertex2d(-halfWidth, drawnCarriageLength - yCornerCut);
glVertex2d(-halfWidth + xCornerCut, drawnCarriageLength);
glVertex2d(halfWidth - xCornerCut, drawnCarriageLength);
glVertex2d(halfWidth, drawnCarriageLength - yCornerCut);
glVertex2d(halfWidth, yCornerCut);
glVertex2d(halfWidth - xCornerCut, 0);
glEnd();
if (i == trainHelper.getFirstCarriageNo()) {
glTranslated(0, 0, 0.1);
glColor3d(0, 0, 0);
glBegin(GL_TRIANGLE_FAN);
if (reversed) {
glVertex2d(-halfWidth + xCornerCut, yCornerCut);
glVertex2d(-halfWidth + 2 * xCornerCut, 3 * yCornerCut);
glVertex2d(halfWidth - 2 * xCornerCut, 3 * yCornerCut);
glVertex2d(halfWidth - xCornerCut, yCornerCut);
} else {
glVertex2d(-halfWidth + 2 * xCornerCut, yCornerCut);
glVertex2d(-halfWidth + xCornerCut, 3 * yCornerCut);
glVertex2d(halfWidth - xCornerCut, 3 * yCornerCut);
glVertex2d(halfWidth - 2 * xCornerCut, yCornerCut);
}
glEnd();
glTranslated(0, 0, -0.1);
}
}
}
}
GLHelper::popMatrix();
}
if (getVType().getGuiShape() == SUMOVehicleShape::RAIL_CAR) {
GLHelper::pushMatrix();
if (s.trueZ) {
glTranslated(front.x(), front.y(), front.z() + 1);
} else {
glTranslated(front.x(), front.y(), getType());
}
glRotated(angle, 0, 0, 1);
drawAction_drawVehicleBlinker(curCLength);
drawAction_drawVehicleBrakeLight(curCLength);
GLHelper::popMatrix();
}
GLHelper::pushMatrix();
front = getPosition();
glTranslated(front.x(), front.y(), getType());
const double degAngle = RAD2DEG(getAngle() + M_PI / 2.);
glRotated(degAngle, 0, 0, 1);
glScaled(exaggeration, trainHelper.getUpscaleLength(), 1);
if (mySeatPositions.size() == 0) {
mySeatPositions.push_back(Seat(back, DEG2RAD(angle)));
}
if (myContainerPositions.size() == 0) {
myContainerPositions.push_back(Seat(back, DEG2RAD(angle)));
}
}
#define BLINKER_POS_FRONT .5
#define BLINKER_POS_BACK .5
inline void
drawAction_drawBlinker(double dir, double length) {
glColor3d(1.f, .8f, 0);
GLHelper::pushMatrix();
glTranslated(dir, BLINKER_POS_FRONT, -0.1);
GLHelper::drawFilledCircle(.5, 6);
GLHelper::popMatrix();
GLHelper::pushMatrix();
glTranslated(dir, length - BLINKER_POS_BACK, -0.1);
GLHelper::drawFilledCircle(.5, 6);
GLHelper::popMatrix();
}
void
GUIVehicle::drawAction_drawVehicleBlinker(double length) const {
if (!signalSet(MSVehicle::VEH_SIGNAL_BLINKER_RIGHT | MSVehicle::VEH_SIGNAL_BLINKER_LEFT | MSVehicle::VEH_SIGNAL_BLINKER_EMERGENCY)) {
return;
}
const double offset = MAX2(.5 * getVehicleType().getWidth(), .4);
if (signalSet(MSVehicle::VEH_SIGNAL_BLINKER_RIGHT)) {
drawAction_drawBlinker(-offset, length);
}
if (signalSet(MSVehicle::VEH_SIGNAL_BLINKER_LEFT)) {
drawAction_drawBlinker(offset, length);
}
if (signalSet(MSVehicle::VEH_SIGNAL_BLINKER_EMERGENCY)) {
drawAction_drawBlinker(-offset, length);
drawAction_drawBlinker(offset, length);
}
}
inline void
GUIVehicle::drawAction_drawVehicleBrakeLight(double length, bool onlyOne) const {
if (!signalSet(MSVehicle::VEH_SIGNAL_BRAKELIGHT)) {
return;
}
glColor3f(1.f, .2f, 0);
GLHelper::pushMatrix();
if (onlyOne) {
glTranslated(0, length, -0.1);
GLHelper::drawFilledCircle(.5, 6);
} else {
glTranslated(-getVehicleType().getWidth() * 0.5, length, -0.1);
GLHelper::drawFilledCircle(.5, 6);
GLHelper::popMatrix();
GLHelper::pushMatrix();
glTranslated(getVehicleType().getWidth() * 0.5, length, -0.1);
GLHelper::drawFilledCircle(.5, 6);
}
GLHelper::popMatrix();
}
inline void
GUIVehicle::drawAction_drawVehicleBlueLight() const {
if (signalSet(MSVehicle::VEH_SIGNAL_EMERGENCY_BLUE)) {
GLHelper::pushMatrix();
glTranslated(0, 2.5, .5);
glColor3f(0, 0, 1);
GLHelper::drawFilledCircle(.5, 6);
GLHelper::popMatrix();
}
}
double
GUIVehicle::getColorValue(const GUIVisualizationSettings& s, int activeScheme) const {
switch (activeScheme) {
case 8:
if (isStopped()) {
return isParking() ? -2 : -1;
}
return getSpeed();
case 9:
if (isActionStep(SIMSTEP)) {
return 1.;
} else if (isActive()) {
return 2.;
} else {
return 0.;
}
case 10:
return getWaitingSeconds();
case 11:
return getAccumulatedWaitingSeconds();
case 12:
return getLastLaneChangeOffset();
case 13:
return getLane()->getVehicleMaxSpeed(this);
case 14:
return getEmissions<PollutantsInterface::CO2>();
case 15:
return getEmissions<PollutantsInterface::CO>();
case 16:
return getEmissions<PollutantsInterface::PM_X>();
case 17:
return getEmissions<PollutantsInterface::NO_X>();
case 18:
return getEmissions<PollutantsInterface::HC>();
case 19:
return getEmissions<PollutantsInterface::FUEL>();
case 20:
return getHarmonoise_NoiseEmissions();
case 21:
return getNumberReroutes();
case 22:
return gSelected.isSelected(GLO_VEHICLE, getGlID());
case 23:
return getLaneChangeModel().isOpposite() ? -100 : getBestLaneOffset();
case 24:
return getAcceleration();
case 25:
return getTimeGapOnLane();
case 26:
return STEPS2TIME(getDepartDelay());
case 27:
return getEmissions<PollutantsInterface::ELEC>();
case 28:
return getRelativeStateOfCharge();
case 29:
return getChargedEnergy();
case 30:
return getTimeLossSeconds();
case 31:
return getStopDelay();
case 32:
return getStopArrivalDelay();
case 33:
return getLaneChangeModel().getSpeedLat();
case 34:
std::string error;
std::string val = getPrefixedParameter(s.vehicleParam, error);
try {
if (val == "") {
return GUIVisualizationSettings::MISSING_DATA;
} else {
return StringUtils::toDouble(val);
}
} catch (NumberFormatException&) {
try {
return StringUtils::toBool(val);
} catch (BoolFormatException&) {
WRITE_WARNINGF(TL("Vehicle parameter '%' key '%' is not a number for vehicle '%'."),
myParameter->getParameter(s.vehicleParam, "0"), s.vehicleParam, getID());
return GUIVisualizationSettings::MISSING_DATA;
}
}
}
return 0;
}
void
GUIVehicle::drawBestLanes() const {
myLock.lock();
std::vector<std::vector<MSVehicle::LaneQ> > bestLanes = myBestLanes;
myLock.unlock();
for (std::vector<std::vector<MSVehicle::LaneQ> >::iterator j = bestLanes.begin(); j != bestLanes.end(); ++j) {
std::vector<MSVehicle::LaneQ>& lanes = *j;
double gmax = -1;
double rmax = -1;
for (std::vector<MSVehicle::LaneQ>::const_iterator i = lanes.begin(); i != lanes.end(); ++i) {
gmax = MAX2((*i).length, gmax);
rmax = MAX2((*i).occupation, rmax);
}
for (std::vector<MSVehicle::LaneQ>::const_iterator i = lanes.begin(); i != lanes.end(); ++i) {
const PositionVector& shape = (*i).lane->getShape();
double g = (*i).length / gmax;
double r = (*i).occupation / rmax;
glColor3d(r, g, 0);
double width = 0.5 / (1 + abs((*i).bestLaneOffset));
GLHelper::drawBoxLines(shape, width);
PositionVector s1 = shape;
s1.move2side((double) .1);
glColor3d(r, 0, 0);
GLHelper::drawLine(s1);
s1.move2side((double) - .2);
glColor3d(0, g, 0);
GLHelper::drawLine(s1);
glColor3d(r, g, 0);
}
}
}
void
GUIVehicle::drawRouteHelper(const GUIVisualizationSettings& s, ConstMSRoutePtr r, bool future, bool noLoop, const RGBColor& col) const {
const double exaggeration = s.vehicleSize.getExaggeration(s, this) * (s.gaming ? 0.5 : 1);
MSRouteIterator start = future ? myCurrEdge : r->begin();
MSRouteIterator i = start;
const std::vector<MSLane*>& bestLaneConts = getBestLanesContinuation();
int bestLaneIndex = (r == myRoute ? 0 : (int)bestLaneConts.size());
std::map<const MSLane*, int> repeatLane;
const double textSize = s.vehicleName.size / s.scale;
const GUILane* prevLane = nullptr;
int reversalIndex = 0;
const int indexDigits = (int)toString(r->size()).size();
if (!isOnRoad() && !isParking()) {
const std::string offRoadLabel = hasInfluencer() && getInfluencer()->isRemoteAffected(SIMSTEP) ? "offRoad" : "teleporting";
GLHelper::drawTextSettings(s.vehicleValue, offRoadLabel, getPosition(), s.scale, s.angle, 1.0);
} else if (myLane->isInternal()) {
bestLaneIndex++;
}
const bool s2 = s.secondaryShape;
for (; i != r->end(); ++i) {
const GUILane* lane;
if (bestLaneIndex < (int)bestLaneConts.size() && bestLaneConts[bestLaneIndex] != 0 && (*i) == &(bestLaneConts[bestLaneIndex]->getEdge())) {
lane = static_cast<GUILane*>(bestLaneConts[bestLaneIndex]);
++bestLaneIndex;
} else {
const std::vector<MSLane*>* allowed = (*i)->allowedLanes(getVClass());
if (allowed != nullptr && allowed->size() != 0) {
lane = static_cast<GUILane*>((*allowed)[0]);
} else {
lane = static_cast<GUILane*>((*i)->getLanes()[0]);
}
}
GLHelper::setColor(col);
GLHelper::drawBoxLines(lane->getShape(s2), lane->getShapeRotations(s2), lane->getShapeLengths(s2), exaggeration);
if (prevLane != nullptr && lane->getBidiLane() == prevLane) {
std::string label = "reverse:" + toString(reversalIndex++);
if (s.showRouteIndex) {
label += "@r" + toString((int)(i - myCurrEdge));
}
Position pos = lane->geometryPositionAtOffset(lane->getLength() / 2) - Position(0, textSize * repeatLane[lane]);
GLHelper::drawTextSettings(s.vehicleValue, label, pos, s.scale, s.angle, 1.0);
}
if (s.showRouteIndex) {
std::string label = toString((int)(i - myCurrEdge));
const double laneAngle = lane->getShape(s2).angleAt2D(0);
Position pos = lane->getShape(s2).front() - Position(0, textSize * repeatLane[lane]) + Position(
(laneAngle >= -0.25 * M_PI && laneAngle < 0.75 * M_PI ? 1 : -1) * 0.4 * indexDigits * textSize, 0);
GLHelper::drawTextSettings(s.vehicleName, label, pos, s.scale, s.angle, 1.0);
}
repeatLane[lane]++;
prevLane = lane;
if (noLoop && i != start && (*i) == (*start)) {
break;
}
}
drawStopLabels(s, noLoop, col);
drawParkingInfo(s);
drawChargingInfo(s);
}
double
GUIVehicle::getLastLaneChangeOffset() const {
return STEPS2TIME(getLaneChangeModel().getLastLaneChangeOffset());
}
std::string
GUIVehicle::getStopInfo() const {
std::string result = "";
if (isParking()) {
result += "parking";
} else if (isStopped()) {
result += "stopped";
} else if (hasStops()) {
return "next: " + myStops.front().getDescription();
} else {
return "";
}
if (myStops.front().pars.triggered) {
result += ", triggered";
}
if (myStops.front().pars.containerTriggered) {
result += ", containerTriggered";
}
if (myStops.front().pars.collision) {
result += ", collision";
}
if (myStops.front().pars.arrival != -1) {
result += ", arrival=" + time2string(myStops.front().pars.arrival);
}
if (myStops.front().pars.started != -1) {
result += ", started=" + time2string(myStops.front().pars.started);
}
if (myStops.front().pars.until != -1) {
result += ", until=" + time2string(myStops.front().pars.until);
}
if (myStops.front().pars.extension != -1) {
result += ", extension=" + time2string(myStops.front().pars.extension);
}
if (!myStops.front().pars.permitted.empty()) {
result += ", permitted=" + toString(myStops.front().pars.permitted);
}
if (myStops.front().pars.actType != "") {
result += ", actType=" + myStops.front().pars.actType;
}
result += ", duration=" + time2string(myStops.front().duration);
return StringUtils::wrapText(result, 60);
}
void
GUIVehicle::selectBlockingFoes() const {
double dist = myLane->getLength() - getPositionOnLane();
#ifdef DEBUG_FOES
gDebugFlag1 = true;
std::cout << SIMTIME << " selectBlockingFoes veh=" << getID() << " dist=" << dist << " numLinks=" << myLFLinkLanes.size() << "\n";
#endif
for (DriveItemVector::const_iterator i = myLFLinkLanes.begin(); i != myLFLinkLanes.end(); ++i) {
const DriveProcessItem& dpi = *i;
if (dpi.myLink == nullptr) {
continue;
}
MSLink::BlockingFoes blockingFoes;
std::vector<const MSPerson*> blockingPersons;
#ifdef DEBUG_FOES
std::cout << " foeLink=" << dpi.myLink->getViaLaneOrLane()->getID() << "\n";
const bool isOpen =
#endif
dpi.myLink->opened(dpi.myArrivalTime, dpi.myArrivalSpeed, dpi.getLeaveSpeed(), getVehicleType().getLength(),
getImpatience(), getCarFollowModel().getMaxDecel(), getWaitingTime(), getLateralPositionOnLane(), &blockingFoes, false, this, dpi.myDistance);
#ifdef DEBUG_FOES
if (!isOpen) {
std::cout << " closed due to:\n";
for (const auto& item : blockingFoes) {
std::cout << " " << item->getID() << "\n";
}
}
#endif
if (getLaneChangeModel().getShadowLane() != nullptr) {
MSLink* parallelLink = dpi.myLink->getParallelLink(getLaneChangeModel().getShadowDirection());
if (parallelLink != nullptr) {
const double shadowLatPos = getLateralPositionOnLane() - getLaneChangeModel().getShadowDirection() * 0.5 * (
myLane->getWidth() + getLaneChangeModel().getShadowLane()->getWidth());
#ifdef DEBUG_FOES
const bool isShadowOpen =
#endif
parallelLink->opened(dpi.myArrivalTime, dpi.myArrivalSpeed, dpi.getLeaveSpeed(),
getVehicleType().getLength(), getImpatience(),
getCarFollowModel().getMaxDecel(),
getWaitingTime(), shadowLatPos, &blockingFoes, false, this, dpi.myDistance);
#ifdef DEBUG_FOES
if (!isShadowOpen) {
std::cout << " foes at shadow link=" << parallelLink->getViaLaneOrLane()->getID() << ":\n";
for (const auto& item : blockingFoes) {
std::cout << " " << item->getID() << "\n";
}
}
#endif
}
}
for (const auto& item : blockingFoes) {
if (item->isVehicle()) {
gSelected.select(static_cast<const GUIVehicle*>(item)->getGlID());
} else {
gSelected.select(static_cast<const GUIPerson*>(item)->getGlID());
}
}
const MSLink::LinkLeaders linkLeaders = (dpi.myLink)->getLeaderInfo(this, dist, &blockingPersons);
#ifdef DEBUG_FOES
gDebugFlag1 = false;
#endif
for (MSLink::LinkLeaders::const_iterator it = linkLeaders.begin(); it != linkLeaders.end(); ++it) {
const GUIVehicle* leader = dynamic_cast<const GUIVehicle*>(it->vehAndGap.first);
if (leader != nullptr) {
if (isLeader(dpi.myLink, leader, it->vehAndGap.second) || it->inTheWay()) {
gSelected.select(leader->getGlID());
#ifdef DEBUG_FOES
std::cout << " linkLeader=" << leader->getID() << "\n";
#endif
}
} else {
for (std::vector<const MSPerson*>::iterator it_p = blockingPersons.begin(); it_p != blockingPersons.end(); ++it_p) {
const GUIPerson* foe = dynamic_cast<const GUIPerson*>(*it_p);
if (foe != nullptr) {
gSelected.select(foe->getGlID());
}
}
}
}
dist += dpi.myLink->getViaLaneOrLane()->getLength();
}
}
void
GUIVehicle::drawOutsideNetwork(bool add) {
GUIMainWindow* mw = GUIMainWindow::getInstance();
GUISUMOAbstractView* view = mw->getActiveView();
if (view != nullptr) {
if (add) {
if ((myAdditionalVisualizations[view] & VO_DRAW_OUTSIDE_NETWORK) == 0) {
myAdditionalVisualizations[view] |= VO_DRAW_OUTSIDE_NETWORK;
view->addAdditionalGLVisualisation(this);
}
} else {
view->removeAdditionalGLVisualisation(this);
myAdditionalVisualizations[view] &= ~VO_DRAW_OUTSIDE_NETWORK;
}
}
}
bool
GUIVehicle::isSelected() const {
return gSelected.isSelected(GLO_VEHICLE, getGlID());
}
int
GUIVehicle::getRightSublaneOnEdge() const {
const double rightSide = getRightSideOnEdge();
const std::vector<double>& sublaneSides = myLane->getEdge().getSubLaneSides();
for (int i = 0; i < (int)sublaneSides.size(); ++i) {
if (sublaneSides[i] > rightSide) {
return MAX2(i - 1, 0);
}
}
return (int)sublaneSides.size() - 1;
}
int
GUIVehicle::getLeftSublaneOnEdge() const {
const double leftSide = getLeftSideOnEdge();
const std::vector<double>& sublaneSides = myLane->getEdge().getSubLaneSides();
for (int i = (int)sublaneSides.size() - 1; i >= 0; --i) {
if (sublaneSides[i] < leftSide) {
return i;
}
}
return -1;
}
std::string
GUIVehicle::getLCStateRight() const {
return toString((LaneChangeAction)getLaneChangeModel().getSavedState(-1).second);
}
std::string
GUIVehicle::getLCStateLeft() const {
return toString((LaneChangeAction)getLaneChangeModel().getSavedState(1).second);
}
std::string
GUIVehicle::getLCStateCenter() const {
return toString((LaneChangeAction)getLaneChangeModel().getSavedState(0).second);
}
std::string
GUIVehicle::getLaneID() const {
return Named::getIDSecure(myLane, "n/a");
}
std::string
GUIVehicle::getBackLaneIDs() const {
return toString(myFurtherLanes);
}
std::string
GUIVehicle::getShadowLaneID() const {
return Named::getIDSecure(getLaneChangeModel().getShadowLane(), "");
}
std::string
GUIVehicle::getTargetLaneID() const {
return Named::getIDSecure(getLaneChangeModel().getTargetLane(), "");
}
std::string
GUIVehicle::getDriveWays() const {
std::vector<std::string> result;
for (auto item : myMoveReminders) {
const MSDriveWay* dw = dynamic_cast<const MSDriveWay*>(item.first);
if (dw) {
result.push_back(dw->getID());
}
}
return StringUtils::wrapText(joinToStringSorting(result, " "), 60);
}
double
GUIVehicle::getManeuverDist() const {
return getLaneChangeModel().getManeuverDist();
}
std::string
GUIVehicle::getSpeedMode() const {
return std::bitset<7>(getInfluencer()->getSpeedMode()).to_string();
}
std::string
GUIVehicle::getLaneChangeMode() const {
return std::bitset<12>(getInfluencer()->getLaneChangeMode()).to_string();
}
void
GUIVehicle::rerouteDRTStop(MSStoppingPlace* busStop) {
SUMOTime intermediateDuration = TIME2STEPS(20);
SUMOTime finalDuration = SUMOTime_MAX;
if (myParameter->stops.size() >= 2) {
intermediateDuration = myParameter->stops.front().duration;
finalDuration = myParameter->stops.back().duration;
}
std::string line = "";
int destinations = 0;
bool add = true;
for (auto it = myStops.begin(); it != myStops.end(); it++) {
if (!it->reached && destinations < 2 && it->busstop != nullptr) {
line += it->busstop->getID();
destinations++;
}
if (it->busstop == busStop) {
it->duration = finalDuration;
myStops.erase(++it, myStops.end());
add = false;
break;
} else {
it->duration = MIN2(it->duration, intermediateDuration);
}
}
if (destinations < 2) {
line += busStop->getID();
}
if (add) {
SUMOVehicleParameter::Stop stopPar;
stopPar.busstop = busStop->getID();
stopPar.lane = busStop->getLane().getID();
stopPar.startPos = busStop->getBeginLanePosition();
stopPar.endPos = busStop->getEndLanePosition();
stopPar.duration = finalDuration;
stopPar.until = -1;
stopPar.triggered = false;
stopPar.containerTriggered = false;
stopPar.parking = ParkingType::ONROAD;
stopPar.index = STOP_INDEX_FIT;
stopPar.parametersSet = STOP_START_SET | STOP_END_SET;
ConstMSEdgeVector edges = myRoute->getEdges();
edges.erase(edges.begin(), edges.begin() + getRoutePosition());
edges.push_back(&busStop->getLane().getEdge());
replaceRouteEdges(edges, -1, 0, "DRT.tmp", false, false, false);
std::string errorMsg;
addStop(stopPar, errorMsg);
}
const bool hasReroutingDevice = getDevice(typeid(MSDevice_Routing)) != nullptr;
SUMOAbstractRouter<MSEdge, SUMOVehicle>& router = hasReroutingDevice
? MSRoutingEngine::getRouterTT(getRNGIndex(), getVClass())
: MSNet::getInstance()->getRouterTT(getRNGIndex());
reroute(MSNet::getInstance()->getCurrentTimeStep(), "DRT", router);
myParameter->line = line;
assert(haveValidStopEdges());
}
Position
GUIVehicle::getVisualPosition(bool s2, const double offset) const {
if (s2) {
if (myLane == nullptr) {
return Position::INVALID;
}
if (isParking()) {
if (myStops.begin()->parkingarea != nullptr) {
return myStops.begin()->parkingarea->getVehiclePosition(*this);
} else {
PositionVector shp = myLane->getEdge().getLanes()[0]->getShape(s2);
shp.move2side(SUMO_const_laneWidth * (MSGlobals::gLefthand ? -1 : 1));
return shp.positionAtOffset((getPositionOnLane() + offset) * myLane->getLengthGeometryFactor(s2));
}
}
const PositionVector& shape = myLane->getShape(s2);
const double posLat = (MSGlobals::gLefthand ? 1 : -1) * getLateralPositionOnLane();
return shape.positionAtOffset((getPositionOnLane() + offset) * myLane->getLengthGeometryFactor(s2), posLat);
} else {
return getPosition(offset);
}
}
double
GUIVehicle::getVisualAngle(bool s2) const {
if (s2) {
const PositionVector& shape = myLane->getShape(s2);
if (isParking()) {
if (myStops.begin()->parkingarea != nullptr) {
return myStops.begin()->parkingarea->getVehicleAngle(*this);
} else {
return shape.rotationAtOffset(getPositionOnLane() * myLane->getLengthGeometryFactor(s2));
}
}
const double lefthandSign = (MSGlobals::gLefthand ? -1 : 1);
Position p1 = getVisualPosition(s2);
Position p2 = getVisualPosition(s2, MAX2(0.0, -myType->getLength()));
double result = (p1 != p2 ? p2.angleTo2D(p1) :
shape.rotationAtOffset(getPositionOnLane() * myLane->getLengthGeometryFactor(s2)));
if (myLaneChangeModel->isChangingLanes()) {
result += lefthandSign * DEG2RAD(myLaneChangeModel->getAngleOffset());
}
return result;
} else {
return getAngle();
}
}