#include <config.h>
#include <string>
#include <iostream>
#include <utils/common/StringUtils.h>
#include <utils/common/ToString.h>
#include <utils/common/MsgHandler.h>
#include <utils/geom/GeoConvHelper.h>
#include <utils/vehicle/SUMOVTypeParameter.h>
#include <utils/options/OptionsCont.h>
#include <utils/iodevices/OutputDevice.h>
#include "RORouteDef.h"
#include "RORoute.h"
#include "ROHelper.h"
#include "RONet.h"
#include "ROLane.h"
#include "ROVehicle.h"
#define INVALID_STOP_POS -1
std::map<ConstROEdgeVector, std::string> ROVehicle::mySavedRoutes;
ROVehicle::ROVehicle(const SUMOVehicleParameter& pars,
RORouteDef* route, const SUMOVTypeParameter* type,
const RONet* net, MsgHandler* errorHandler):
RORoutable(pars, type),
myRoute(route),
myJumpTime(-1) {
getParameter().stops.clear();
if (route != nullptr && route->getFirstRoute() != nullptr) {
for (StopParVector::const_iterator s = route->getFirstRoute()->getStops().begin(); s != route->getFirstRoute()->getStops().end(); ++s) {
addStop(*s, net, errorHandler);
}
}
for (StopParVector::const_iterator s = pars.stops.begin(); s != pars.stops.end(); ++s) {
addStop(*s, net, errorHandler);
}
}
void
ROVehicle::addStop(const SUMOVehicleParameter::Stop& stopPar, const RONet* net, MsgHandler* errorHandler) {
const ROEdge* stopEdge = net->getEdge(stopPar.edge);
assert(stopEdge != 0);
if (stopEdge->prohibits(this)) {
if (errorHandler != nullptr) {
errorHandler->inform("Stop edge '" + stopEdge->getID() + "' does not allow vehicle '" + getID() + "'.");
}
return;
}
StopParVector::iterator iter = getParameter().stops.begin();
if (stopPar.index == STOP_INDEX_END || stopPar.index >= static_cast<int>(getParameter().stops.size())) {
if (getParameter().stops.size() > 0) {
iter = getParameter().stops.end();
}
} else {
if (stopPar.index == STOP_INDEX_FIT) {
iter = getParameter().stops.end();
} else {
iter += stopPar.index;
}
}
getParameter().stops.insert(iter, stopPar);
if (stopPar.jump >= 0) {
if (stopEdge->isInternal()) {
if (errorHandler != nullptr) {
errorHandler->inform("Jumps are not supported from internal stop edge '" + stopEdge->getID() + "'.");
}
} else {
if (myJumpTime < 0) {
myJumpTime = 0;
}
myJumpTime += stopPar.jump;
}
}
}
ROVehicle::~ROVehicle() {}
const ROEdge*
ROVehicle:: getDepartEdge() const {
return myRoute->getFirstRoute()->getFirst();
}
void
ROVehicle::computeRoute(const RORouterProvider& provider,
const bool removeLoops, MsgHandler* errorHandler) {
SUMOAbstractRouter<ROEdge, ROVehicle>& router = provider.getVehicleRouter(getVClass());
std::string noRouteMsg = "The vehicle '" + getID() + "' has no valid route.";
RORouteDef* const routeDef = getRouteDefinition();
if (routeDef == nullptr) {
errorHandler->inform(noRouteMsg);
myRoutingSuccess = false;
return;
}
routeDef->validateAlternatives(this, errorHandler);
if (routeDef->getFirstRoute() == nullptr) {
myRoutingSuccess = false;
return;
}
RORoute* current = routeDef->buildCurrentRoute(router, getDepartureTime(), *this);
if (current == nullptr || current->size() == 0) {
delete current;
if (current == nullptr || !routeDef->discardSilent()) {
errorHandler->inform(noRouteMsg);
}
myRoutingSuccess = false;
return;
}
if (removeLoops) {
const ROEdge* requiredStart = (getParameter().departPosProcedure == DepartPosDefinition::GIVEN
|| getParameter().departLaneProcedure == DepartLaneDefinition::GIVEN ? current->getEdgeVector().front() : 0);
const ROEdge* requiredEnd = (getParameter().arrivalPosProcedure == ArrivalPosDefinition::GIVEN
|| getParameter().arrivalLaneProcedure == ArrivalLaneDefinition::GIVEN ? current->getEdgeVector().back() : 0);
ConstROEdgeVector mandatory;
for (auto m : getMandatoryEdges(requiredStart, requiredEnd)) {
mandatory.push_back(m.edge);
}
current->recheckForLoops(mandatory);
if (current->size() == 0) {
delete current;
errorHandler->inform(noRouteMsg + " (after removing loops)");
myRoutingSuccess = false;
return;
}
}
if (RONet::getInstance()->getMaxTraveltime() > 0) {
double costs = router.recomputeCosts(current->getEdgeVector(), this, getDepartureTime());
if (costs > RONet::getInstance()->getMaxTraveltime()) {
errorHandler->inform(noRouteMsg + " (traveltime " + time2string(TIME2STEPS(costs)) + " exceeds max-traveltime)");
delete current;
myRoutingSuccess = false;
return;
}
}
routeDef->addAlternative(router, this, current, getDepartureTime(), errorHandler);
myRoutingSuccess = true;
}
std::vector<ROVehicle::Mandatory>
ROVehicle::getMandatoryEdges(const ROEdge* requiredStart, const ROEdge* requiredEnd) const {
const RONet* net = RONet::getInstance();
std::vector<Mandatory> mandatory;
if (requiredStart) {
double departPos = 0;
if (getParameter().departPosProcedure == DepartPosDefinition::GIVEN) {
departPos = SUMOVehicleParameter::interpretEdgePos(getParameter().departPos, requiredStart->getLength(), SUMO_ATTR_DEPARTPOS, "vehicle '" + getID() + "'");
}
mandatory.push_back(Mandatory(requiredStart, departPos));
}
if (getParameter().via.size() != 0) {
for (const std::string& via : getParameter().via) {
mandatory.push_back(Mandatory(net->getEdge(via), INVALID_STOP_POS));
}
} else {
for (const auto& stop : getParameter().stops) {
const ROEdge* e = net->getEdge(stop.edge);
if (e->isInternal()) {
const ROEdge* before = e->getNormalBefore();
const ROEdge* after = e->getNormalAfter();
mandatory.push_back(Mandatory(before, INVALID_STOP_POS));
mandatory.push_back(Mandatory(after, INVALID_STOP_POS, stop.jump));
} else {
double endPos = SUMOVehicleParameter::interpretEdgePos(stop.endPos, e->getLength(), SUMO_ATTR_ENDPOS, "stop of vehicle '" + getID() + "' on edge '" + e->getID() + "'");
mandatory.push_back(Mandatory(e, endPos, stop.jump));
}
}
}
if (requiredEnd) {
double arrivalPos = INVALID_STOP_POS;
if (getParameter().arrivalPosProcedure == ArrivalPosDefinition::GIVEN) {
arrivalPos = SUMOVehicleParameter::interpretEdgePos(getParameter().arrivalPos, requiredEnd->getLength(), SUMO_ATTR_ARRIVALPOS, "vehicle '" + getID() + "'");
}
mandatory.push_back(Mandatory(requiredEnd, arrivalPos));
}
return mandatory;
}
void
ROVehicle::saveAsXML(OutputDevice& os, OutputDevice* const typeos, bool asAlternatives, OptionsCont& options, int cloneIndex) const {
if (typeos != nullptr && getType() != nullptr && !getType()->saved) {
getType()->write(*typeos);
getType()->saved = true;
}
if (getType() != nullptr && !getType()->saved) {
getType()->write(os);
getType()->saved = asAlternatives;
}
const bool writeTrip = options.exists("write-trips") && options.getBool("write-trips");
const bool writeGeoTrip = writeTrip && options.getBool("write-trips.geo");
const bool writeJunctions = writeTrip && options.getBool("write-trips.junctions");
const bool writeNamedRoute = !asAlternatives && options.getBool("named-routes");
const bool writeCosts = options.exists("write-costs") && options.getBool("write-costs");
const bool writeExit = options.exists("exit-times") && options.getBool("exit-times");
const bool writeLength = options.exists("route-length") && options.getBool("route-length");
const bool writeFlow = options.exists("keep-flows") && options.getBool("keep-flows") && isPartOfFlow();
std::string routeID;
if (writeNamedRoute) {
ConstROEdgeVector edges = myRoute->getUsedRoute()->getNormalEdges();
auto it = mySavedRoutes.find(edges);
if (it == mySavedRoutes.end()) {
routeID = "r" + toString(mySavedRoutes.size());
myRoute->getUsedRoute()->writeXMLDefinition(os, this, writeCosts, false, writeExit,
writeLength, routeID);
mySavedRoutes[edges] = routeID;
} else {
routeID = it->second;
}
}
const SumoXMLTag tag = writeFlow ? SUMO_TAG_FLOW : (writeTrip ? SUMO_TAG_TRIP : SUMO_TAG_VEHICLE);
if (cloneIndex == 0) {
getParameter().write(os, options, tag);
} else {
SUMOVehicleParameter p = getParameter();
p.id += options.getString("scale-suffix") + toString(cloneIndex);
p.write(os, options, tag);
}
if (writeTrip) {
const ConstROEdgeVector edges = myRoute->getFirstRoute()->getEdgeVector();
const ROEdge* from = nullptr;
const ROEdge* to = nullptr;
if (edges.size() > 0) {
if (edges.front()->isTazConnector()) {
if (edges.size() > 1) {
from = edges[1];
if (from->isTazConnector() && writeJunctions && edges.front()->getSuccessors().size() > 0) {
from = edges.front()->getSuccessors(getVClass()).front();
}
}
} else {
from = edges[0];
}
if (edges.back()->isTazConnector()) {
if (edges.size() > 1) {
to = edges[edges.size() - 2];
if (to->isTazConnector() && writeJunctions && edges.back()->getPredecessors().size() > 0) {
to = edges.back()->getPredecessors().front();
}
}
} else {
to = edges[edges.size() - 1];
}
}
if (from != nullptr) {
if (writeGeoTrip) {
Position fromPos = from->getLanes()[0]->getShape().positionAtOffset2D(0);
if (GeoConvHelper::getFinal().usingGeoProjection()) {
os.setPrecision(gPrecisionGeo);
GeoConvHelper::getFinal().cartesian2geo(fromPos);
os.writeAttr(SUMO_ATTR_FROMLONLAT, fromPos);
os.setPrecision(gPrecision);
} else {
os.writeAttr(SUMO_ATTR_FROMXY, fromPos);
}
} else if (writeJunctions) {
os.writeAttr(SUMO_ATTR_FROM_JUNCTION, from->getFromJunction()->getID());
} else {
os.writeAttr(SUMO_ATTR_FROM, from->getID());
}
}
if (to != nullptr) {
if (writeGeoTrip) {
Position toPos = to->getLanes()[0]->getShape().positionAtOffset2D(to->getLanes()[0]->getShape().length2D());
if (GeoConvHelper::getFinal().usingGeoProjection()) {
os.setPrecision(gPrecisionGeo);
GeoConvHelper::getFinal().cartesian2geo(toPos);
os.writeAttr(SUMO_ATTR_TOLONLAT, toPos);
os.setPrecision(gPrecision);
} else {
os.writeAttr(SUMO_ATTR_TOXY, toPos);
}
} else if (writeJunctions) {
os.writeAttr(SUMO_ATTR_TO_JUNCTION, to->getToJunction()->getID());
} else {
os.writeAttr(SUMO_ATTR_TO, to->getID());
}
}
if (getParameter().via.size() > 0) {
std::vector<std::string> viaOut;
SumoXMLAttr viaAttr = (writeGeoTrip
? (GeoConvHelper::getFinal().usingGeoProjection() ? SUMO_ATTR_VIALONLAT : SUMO_ATTR_VIAXY)
: (writeJunctions ? SUMO_ATTR_VIAJUNCTIONS : SUMO_ATTR_VIA));
for (const std::string& viaID : getParameter().via) {
const ROEdge* viaEdge = RONet::getInstance()->getEdge(viaID);
if (viaEdge->isTazConnector()) {
if (viaEdge->getPredecessors().size() == 0) {
continue;
}
viaEdge = viaEdge->getPredecessors().front();
}
assert(viaEdge != nullptr);
if (writeGeoTrip) {
Position viaPos = viaEdge->getLanes()[0]->getShape().positionAtOffset2D(viaEdge->getLanes()[0]->getShape().length2D() / 2);
if (GeoConvHelper::getFinal().usingGeoProjection()) {
GeoConvHelper::getFinal().cartesian2geo(viaPos);
viaOut.push_back(toString(viaPos, gPrecisionGeo));
} else {
viaOut.push_back(toString(viaPos, gPrecision));
}
} else if (writeJunctions) {
viaOut.push_back(viaEdge->getToJunction()->getID());
} else {
viaOut.push_back(viaEdge->getID());
}
}
os.writeAttr(viaAttr, viaOut);
}
} else if (writeNamedRoute) {
os.writeAttr(SUMO_ATTR_ROUTE, routeID);
} else {
myRoute->writeXMLDefinition(os, this, asAlternatives, writeExit, writeCosts, writeLength);
}
for (StopParVector::const_iterator stop = getParameter().stops.begin(); stop != getParameter().stops.end(); ++stop) {
stop->write(os);
}
getParameter().writeParams(os);
os.closeTag();
}