#include <config.h>
#include <utils/common/MsgHandler.h>
#include <utils/common/ToString.h>
#include <algorithm>
#include <cassert>
#include <iostream>
#include <utils/vehicle/SUMOVTypeParameter.h>
#include <utils/emissions/PollutantsInterface.h>
#include <utils/emissions/HelpersHarmonoise.h>
#include "ROLane.h"
#include "RONet.h"
#include "ROVehicle.h"
#include "ROEdge.h"
bool ROEdge::myInterpolate = false;
bool ROEdge::myHaveTTWarned = false;
bool ROEdge::myHaveEWarned = false;
ROEdgeVector ROEdge::myEdges;
double ROEdge::myPriorityFactor(0);
double ROEdge::myMinEdgePriority(std::numeric_limits<double>::max());
double ROEdge::myEdgePriorityRange(0);
ROEdge::ROEdge(const std::string& id, RONode* from, RONode* to, int index, const int priority, const std::string& type) :
Named(id),
myFromJunction(from),
myToJunction(to),
myIndex(index),
myPriority(priority),
myType(type),
mySpeed(-1),
myLength(0),
myAmSink(false),
myAmSource(false),
myUsingTTTimeLine(false),
myUsingETimeLine(false),
myRestrictions(nullptr),
myCombinedPermissions(0),
myOtherTazConnector(nullptr),
myTimePenalty(0) {
while ((int)myEdges.size() <= index) {
myEdges.push_back(0);
}
myEdges[index] = this;
if (from == nullptr && to == nullptr) {
myCombinedPermissions = SVCAll;
} else {
myBoundary.add(from->getPosition());
myBoundary.add(to->getPosition());
}
}
ROEdge::ROEdge(const std::string& id, const RONode* from, const RONode* to, SVCPermissions p) :
Named(id),
myFromJunction(const_cast<RONode*>(from)),
myToJunction(const_cast<RONode*>(to)),
myIndex(-1),
myPriority(0),
myType(""),
mySpeed(std::numeric_limits<double>::max()),
myLength(0),
myAmSink(false),
myAmSource(false),
myUsingTTTimeLine(false),
myUsingETimeLine(false),
myCombinedPermissions(p),
myOtherTazConnector(nullptr),
myTimePenalty(0)
{ }
ROEdge::~ROEdge() {
for (ROLane* const lane : myLanes) {
delete lane;
}
delete myReversedRoutingEdge;
delete myFlippedRoutingEdge;
delete myRailwayRoutingEdge;
}
void
ROEdge::addLane(ROLane* lane) {
const double speed = lane->getSpeed();
if (speed > mySpeed) {
mySpeed = speed;
myLength = lane->getLength();
}
mySpeed = speed > mySpeed ? speed : mySpeed;
myLanes.push_back(lane);
myCombinedPermissions |= lane->getPermissions();
}
void
ROEdge::addSuccessor(ROEdge* s, ROEdge* via, std::string) {
if (isInternal()) {
myFollowingEdges.clear();
myFollowingViaEdges.clear();
}
if (find(myFollowingEdges.begin(), myFollowingEdges.end(), s) == myFollowingEdges.end()) {
myFollowingEdges.push_back(s);
myFollowingViaEdges.push_back(std::make_pair(s, via));
if (isTazConnector() && s->getFromJunction() != nullptr) {
myBoundary.add(s->getFromJunction()->getPosition());
}
if (!isInternal()) {
s->myApproachingEdges.push_back(this);
if (s->isTazConnector() && getToJunction() != nullptr) {
s->myBoundary.add(getToJunction()->getPosition());
}
}
if (via != nullptr) {
if (via->myApproachingEdges.size() == 0) {
via->myApproachingEdges.push_back(this);
}
}
}
}
void
ROEdge::addEffort(double value, double timeBegin, double timeEnd) {
myEfforts.add(timeBegin, timeEnd, value);
myUsingETimeLine = true;
}
void
ROEdge::addTravelTime(double value, double timeBegin, double timeEnd) {
myTravelTimes.add(timeBegin, timeEnd, value);
myUsingTTTimeLine = true;
}
double
ROEdge::getEffort(const ROVehicle* const veh, double time) const {
double ret = 0;
if (!getStoredEffort(time, ret)) {
return myLength / MIN2(veh->getMaxSpeed(), getVClassMaxSpeed(veh->getVClass())) + myTimePenalty;
}
return ret;
}
double
ROEdge::getDistanceTo(const ROEdge* other, const bool doBoundaryEstimate) const {
assert(this != other);
if (doBoundaryEstimate) {
return myBoundary.distanceTo2D(other->myBoundary);
}
if (isTazConnector()) {
if (other->isTazConnector()) {
return myBoundary.distanceTo2D(other->myBoundary);
}
return myBoundary.distanceTo2D(other->getFromJunction()->getPosition());
}
if (other->isTazConnector()) {
return other->myBoundary.distanceTo2D(getToJunction()->getPosition());
}
return getLanes()[0]->getShape()[-1].distanceTo2D(other->getLanes()[0]->getShape()[0]);
}
bool
ROEdge::hasLoadedTravelTime(double time) const {
return myUsingTTTimeLine && myTravelTimes.describesTime(time);
}
double
ROEdge::getTravelTime(const ROVehicle* const veh, double time) const {
if (myUsingTTTimeLine) {
if (myTravelTimes.describesTime(time)) {
double lineTT = myTravelTimes.getValue(time);
if (myInterpolate) {
const double inTT = lineTT;
const double split = (double)(myTravelTimes.getSplitTime(time, time + inTT) - time);
if (split >= 0) {
lineTT = myTravelTimes.getValue(time + inTT) * ((double)1. - split / inTT) + split;
}
}
return MAX2(getMinimumTravelTime(veh), lineTT);
} else {
if (!myHaveTTWarned) {
WRITE_WARNINGF(TL("No interval matches passed time=% in edge '%'.\n Using edge's length / max speed."), time, myID);
myHaveTTWarned = true;
}
}
}
const double speed = veh != nullptr ? MIN2(veh->getMaxSpeed(), veh->getType()->speedFactor.getParameter(0) * getVClassMaxSpeed(veh->getVClass())) : mySpeed;
return myLength / speed + myTimePenalty;
}
double
ROEdge::getNoiseEffort(const ROEdge* const edge, const ROVehicle* const veh, double time) {
double ret = 0;
if (!edge->getStoredEffort(time, ret)) {
const double v = MIN2(veh->getMaxSpeed(), edge->getVClassMaxSpeed(veh->getVClass()));
ret = HelpersHarmonoise::computeNoise(veh->getType()->emissionClass, v, 0);
}
return ret;
}
bool
ROEdge::getStoredEffort(double time, double& ret) const {
if (myUsingETimeLine) {
if (!myEfforts.describesTime(time)) {
if (!myHaveEWarned) {
WRITE_WARNINGF(TL("No interval matches passed time=% in edge '%'.\n Using edge's length / edge's speed."), time, myID);
myHaveEWarned = true;
}
return false;
}
if (myInterpolate) {
const double inTT = myTravelTimes.getValue(time);
const double ratio = (myEfforts.getSplitTime(time, time + inTT) - time) / inTT;
if (ratio >= 0.) {
ret = ratio * myEfforts.getValue(time) + (1. - ratio) * myEfforts.getValue(time + inTT);
return true;
}
}
ret = myEfforts.getValue(time);
return true;
}
return false;
}
int
ROEdge::getNumSuccessors() const {
if (myAmSink) {
return 0;
}
return (int) myFollowingEdges.size();
}
int
ROEdge::getNumPredecessors() const {
if (myAmSource) {
return 0;
}
return (int) myApproachingEdges.size();
}
const ROEdge*
ROEdge::getNormalBefore() const {
const ROEdge* result = this;
while (result->isInternal()) {
assert(myApproachingEdges.size() == 1);
result = result->myApproachingEdges.front();
}
return result;
}
const ROEdge*
ROEdge::getNormalAfter() const {
const ROEdge* result = this;
while (result->isInternal()) {
assert(myFollowingEdges.size() == 1);
result = result->myFollowingEdges.front();
}
return result;
}
void
ROEdge::buildTimeLines(const std::string& measure, const bool boundariesOverride) {
if (myUsingETimeLine) {
double value = myLength / mySpeed;
const SUMOEmissionClass c = PollutantsInterface::getClassByName("HBEFA4/default");
if (measure == "CO") {
value = PollutantsInterface::compute(c, PollutantsInterface::CO, mySpeed, 0, 0, nullptr) * value;
}
if (measure == "CO2") {
value = PollutantsInterface::compute(c, PollutantsInterface::CO2, mySpeed, 0, 0, nullptr) * value;
}
if (measure == "HC") {
value = PollutantsInterface::compute(c, PollutantsInterface::HC, mySpeed, 0, 0, nullptr) * value;
}
if (measure == "PMx") {
value = PollutantsInterface::compute(c, PollutantsInterface::PM_X, mySpeed, 0, 0, nullptr) * value;
}
if (measure == "NOx") {
value = PollutantsInterface::compute(c, PollutantsInterface::NO_X, mySpeed, 0, 0, nullptr) * value;
}
if (measure == "fuel") {
value = PollutantsInterface::compute(c, PollutantsInterface::FUEL, mySpeed, 0, 0, nullptr) * value;
}
if (measure == "electricity") {
value = PollutantsInterface::compute(c, PollutantsInterface::ELEC, mySpeed, 0, 0, nullptr) * value;
}
myEfforts.fillGaps(value, boundariesOverride);
}
if (myUsingTTTimeLine) {
myTravelTimes.fillGaps(myLength / mySpeed + myTimePenalty, boundariesOverride);
}
}
void
ROEdge::cacheParamRestrictions(const std::vector<std::string>& restrictionKeys) {
for (const std::string& key : restrictionKeys) {
const std::string value = getParameter(key, "1e40");
myParamRestrictions.push_back(StringUtils::toDouble(value));
}
}
double
ROEdge::getLengthGeometryFactor() const {
return myLanes.empty() ? 1. : myLanes[0]->getShape().length() / myLanes[0]->getLength();
}
bool
ROEdge::allFollowersProhibit(const ROVehicle* const vehicle) const {
for (ROEdgeVector::const_iterator i = myFollowingEdges.begin(); i != myFollowingEdges.end(); ++i) {
if (!(*i)->prohibits(vehicle)) {
return false;
}
}
return true;
}
const ROEdgeVector&
ROEdge::getAllEdges() {
return myEdges;
}
const Position
ROEdge::getStopPosition(const SUMOVehicleParameter::Stop& stop) {
const double middle = (stop.endPos + stop.startPos) / 2.;
const ROEdge* const edge = RONet::getInstance()->getEdge(SUMOXMLDefinitions::getEdgeIDFromLane(stop.lane));
return (edge->getFromJunction()->getPosition() + edge->getToJunction()->getPosition()) * (middle / edge->getLength());
}
const ROEdgeVector&
ROEdge::getSuccessors(SUMOVehicleClass vClass) const {
if (vClass == SVC_IGNORING || !RONet::getInstance()->hasPermissions() || isTazConnector()) {
return myFollowingEdges;
}
#ifdef HAVE_FOX
FXMutexLock locker(myLock);
#endif
std::map<SUMOVehicleClass, ROEdgeVector>::const_iterator i = myClassesSuccessorMap.find(vClass);
if (i != myClassesSuccessorMap.end()) {
return i->second;
}
std::set<ROEdge*> followers;
for (const ROLane* const lane : myLanes) {
if ((lane->getPermissions() & vClass) != 0) {
for (const auto& next : lane->getOutgoingViaLanes()) {
if ((next.first->getPermissions() & vClass) != 0 && (next.second == nullptr || (next.second->getPermissions() & vClass) != 0)) {
followers.insert(&next.first->getEdge());
}
}
}
}
for (ROEdgeVector::const_iterator it = myFollowingEdges.begin(); it != myFollowingEdges.end(); ++it) {
if ((*it)->isTazConnector()) {
followers.insert(*it);
}
}
myClassesSuccessorMap[vClass].insert(myClassesSuccessorMap[vClass].begin(),
followers.begin(), followers.end());
return myClassesSuccessorMap[vClass];
}
const ROConstEdgePairVector&
ROEdge::getViaSuccessors(SUMOVehicleClass vClass, bool ) const {
if (vClass == SVC_IGNORING || !RONet::getInstance()->hasPermissions() || isTazConnector()) {
return myFollowingViaEdges;
}
#ifdef HAVE_FOX
FXMutexLock locker(myLock);
#endif
std::map<SUMOVehicleClass, ROConstEdgePairVector>::const_iterator i = myClassesViaSuccessorMap.find(vClass);
if (i != myClassesViaSuccessorMap.end()) {
return i->second;
}
std::set<std::pair<const ROEdge*, const ROEdge*> > followers;
for (const ROLane* const lane : myLanes) {
if ((lane->getPermissions() & vClass) != 0) {
for (const auto& next : lane->getOutgoingViaLanes()) {
if ((next.first->getPermissions() & vClass) != 0 && (next.second == nullptr || (next.second->getPermissions() & vClass) != 0)) {
followers.insert(std::make_pair(&next.first->getEdge(), next.second));
}
}
}
}
for (const ROEdge* e : myFollowingEdges) {
if (e->isTazConnector()) {
followers.insert(std::make_pair(e, e));
}
}
myClassesViaSuccessorMap[vClass].insert(myClassesViaSuccessorMap[vClass].begin(),
followers.begin(), followers.end());
return myClassesViaSuccessorMap[vClass];
}
bool
ROEdge::isConnectedTo(const ROEdge& e, const SUMOVehicleClass vClass, bool ignoreTransientPermissions) const {
UNUSED_PARAMETER(ignoreTransientPermissions);
const ROEdgeVector& followers = getSuccessors(vClass);
return std::find(followers.begin(), followers.end(), &e) != followers.end();
}
bool
ROEdge::initPriorityFactor(double priorityFactor) {
myPriorityFactor = priorityFactor;
double maxEdgePriority = -std::numeric_limits<double>::max();
for (ROEdge* edge : myEdges) {
maxEdgePriority = MAX2(maxEdgePriority, (double)edge->getPriority());
myMinEdgePriority = MIN2(myMinEdgePriority, (double)edge->getPriority());
}
myEdgePriorityRange = maxEdgePriority - myMinEdgePriority;
if (myEdgePriorityRange == 0) {
WRITE_WARNING(TL("Option weights.priority-factor does not take effect because all edges have the same priority."));
myPriorityFactor = 0;
return false;
}
return true;
}