#include <config.h>
#include <vector>
#include <string>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <utils/common/MsgHandler.h>
#include <utils/common/StringTokenizer.h>
#include <utils/common/StringUtils.h>
#include <utils/common/ToString.h>
#include <utils/common/UtilExceptions.h>
#include <utils/common/StdDefs.h>
#include <utils/geom/GeomHelper.h>
#include <utils/options/OptionsCont.h>
#include "NBEdgeCont.h"
#include "NBNode.h"
#include "NBNodeCont.h"
#include "NBContHelper.h"
#include "NBHelpers.h"
#include "NBTrafficLightDefinition.h"
#include "NBOwnTLDef.h"
#include "NBTypeCont.h"
#include "NBEdge.h"
#define DEBUGID ""
#define DEBUGCOND (getID() == DEBUGID)
#define DEBUGCOND2(obj) ((obj != 0 && (obj)->getID() == DEBUGID))
const double NBEdge::UNSPECIFIED_WIDTH = -1;
const double NBEdge::UNSPECIFIED_OFFSET = 0;
const double NBEdge::UNSPECIFIED_SPEED = -1;
const double NBEdge::UNSPECIFIED_FRICTION = 1.;
const double NBEdge::UNSPECIFIED_CONTPOS = -1;
const double NBEdge::UNSPECIFIED_VISIBILITY_DISTANCE = -1;
const double NBEdge::UNSPECIFIED_SIGNAL_OFFSET = -1;
const double NBEdge::UNSPECIFIED_LOADED_LENGTH = -1;
const double NBEdge::ANGLE_LOOKAHEAD = 10.0;
const int NBEdge::UNSPECIFIED_INTERNAL_LANE_INDEX = -1;
const bool NBEdge::UNSPECIFIED_CONNECTION_UNCONTROLLED = false;
double NBEdge::myDefaultConnectionLength = NBEdge::UNSPECIFIED_LOADED_LENGTH;
NBEdge NBEdge::DummyEdge;
ConstRouterEdgePairVector NBEdge::Connection::myViaSuccessors = ConstRouterEdgePairVector({ std::pair<NBRouterEdge*, NBRouterEdge*>(nullptr, nullptr) });
std::string
NBEdge::Connection::getInternalLaneID() const {
return id + "_" + toString(internalLaneIndex);
}
std::string
NBEdge::Connection::getInternalViaLaneID() const {
return viaID + "_" + toString(internalViaLaneIndex);
}
std::string
NBEdge::Connection::getDescription(const NBEdge* parent) const {
return (Named::getIDSecure(parent) + "_" + toString(fromLane) + "->" + Named::getIDSecure(toEdge) + "_" + toString(toLane)
+ (permissions == SVC_UNSPECIFIED ? "" : " (" + getVehicleClassNames(permissions) + ")"));
}
NBEdge::Connection::Connection(int fromLane_, NBEdge* toEdge_, int toLane_, const bool mayDefinitelyPass_) :
fromLane(fromLane_),
toEdge(toEdge_),
toLane(toLane_),
mayDefinitelyPass(mayDefinitelyPass_),
customLength(myDefaultConnectionLength),
id(toEdge_ == nullptr ? "" : toEdge->getFromNode()->getID()) {
}
NBEdge::Lane::Lane(NBEdge* e, const std::string& origID_) :
speed(e->getSpeed()),
friction(e->getFriction()),
permissions(SVCAll),
preferred(0),
changeLeft(SVCAll),
changeRight(SVCAll),
endOffset(e->getEndOffset()),
laneStopOffset(e->getEdgeStopOffset()),
width(e->getLaneWidth()),
accelRamp(false),
connectionsDone(false) {
if (origID_ != "") {
setParameter(SUMO_PARAM_ORIGID, origID_);
}
}
void
NBEdge::ToEdgeConnectionsAdder::execute(const int lane, const int virtEdge) {
assert((int)myTransitions.size() > virtEdge);
NBEdge* succEdge = myTransitions[virtEdge];
std::vector<int> lanes;
std::map<NBEdge*, std::vector<int> >::iterator i = myConnections.find(succEdge);
if (i != myConnections.end()) {
lanes = (*i).second;
}
std::vector<int>::iterator j = std::find(lanes.begin(), lanes.end(), lane);
if (j == lanes.end()) {
lanes.push_back(lane);
}
myConnections[succEdge] = lanes;
}
NBEdge::MainDirections::MainDirections(const EdgeVector& outgoing, NBEdge* parent, NBNode* to, const std::vector<int>& availableLanes) : myStraightest(-1) {
NBContHelper::edge_similar_direction_sorter sorter(parent);
const NBEdge* straight = nullptr;
for (const NBEdge* const out : outgoing) {
const SVCPermissions outPerms = out->getPermissions();
for (const int l : availableLanes) {
if ((parent->myLanes[l].permissions & outPerms) != 0) {
if (straight == nullptr || sorter(out, straight)) {
straight = out;
}
break;
}
}
}
if (straight == nullptr) {
return;
}
myStraightest = (int)std::distance(outgoing.begin(), std::find(outgoing.begin(), outgoing.end(), straight));
assert(outgoing.size() > 0);
const LinkDirection straightestDir = to->getDirection(parent, straight);
#ifdef DEBUG_CONNECTION_GUESSING
if (DEBUGCOND2(parent)) {
std::cout << " MainDirections edge=" << parent->getID() << " straightest=" << straight->getID() << " dir=" << toString(straightestDir) << "\n";
}
#endif
if (NBNode::isTrafficLight(to->getType()) &&
(straightestDir == LinkDirection::STRAIGHT || straightestDir == LinkDirection::PARTLEFT || straightestDir == LinkDirection::PARTRIGHT)) {
myDirs.push_back(MainDirections::Direction::FORWARD);
return;
}
if (outgoing[0]->getJunctionPriority(to) == 1) {
myDirs.push_back(MainDirections::Direction::RIGHTMOST);
}
if (outgoing.back()->getJunctionPriority(to) == 1) {
if (outgoing.back()->getPriority() > straight->getPriority() ||
outgoing.back()->getNumLanes() > straight->getNumLanes()) {
myDirs.push_back(MainDirections::Direction::LEFTMOST);
}
}
if (straight->getJunctionPriority(to) == 1 && to->getDirection(parent, straight) == LinkDirection::STRAIGHT) {
myDirs.push_back(MainDirections::Direction::FORWARD);
}
}
NBEdge::MainDirections::~MainDirections() {}
bool
NBEdge::MainDirections::empty() const {
return myDirs.empty();
}
bool
NBEdge::MainDirections::includes(Direction d) const {
return std::find(myDirs.begin(), myDirs.end(), d) != myDirs.end();
}
int
NBEdge::connections_relative_edgelane_sorter::operator()(const Connection& c1, const Connection& c2) const {
if (c1.toEdge != c2.toEdge) {
return NBContHelper::relative_outgoing_edge_sorter(myEdge)(c1.toEdge, c2.toEdge);
}
return c1.toLane < c2.toLane;
}
NBEdge::NBEdge(const std::string& id, NBNode* from, NBNode* to,
std::string type, double speed, double friction, int nolanes,
int priority, double laneWidth, double endOffset,
LaneSpreadFunction spread, const std::string& streetName) :
Named(StringUtils::convertUmlaute(id)),
myStep(EdgeBuildingStep::INIT),
myType(StringUtils::convertUmlaute(type)),
myFrom(from), myTo(to),
myStartAngle(0), myEndAngle(0), myTotalAngle(0),
myPriority(priority), mySpeed(speed), myFriction(friction),
myDistance(0),
myTurnDestination(nullptr),
myPossibleTurnDestination(nullptr),
myFromJunctionPriority(-1), myToJunctionPriority(-1),
myLaneSpreadFunction(spread), myEndOffset(endOffset),
myLaneWidth(laneWidth),
myLoadedLength(UNSPECIFIED_LOADED_LENGTH),
myAmInTLS(false), myAmMacroscopicConnector(false),
myStreetName(streetName),
mySignalPosition(Position::INVALID),
mySignalNode(nullptr),
myIsOffRamp(false),
myIsBidi(false),
myIndex(-1) {
init(nolanes, false, "");
}
NBEdge::NBEdge(const std::string& id, NBNode* from, NBNode* to,
std::string type, double speed, double friction, int nolanes,
int priority, double laneWidth, double endOffset,
PositionVector geom,
LaneSpreadFunction spread,
const std::string& streetName,
const std::string& origID,
bool tryIgnoreNodePositions) :
Named(StringUtils::convertUmlaute(id)),
myStep(EdgeBuildingStep::INIT),
myType(StringUtils::convertUmlaute(type)),
myFrom(from), myTo(to),
myStartAngle(0), myEndAngle(0), myTotalAngle(0),
myPriority(priority), mySpeed(speed), myFriction(friction),
myDistance(0),
myTurnDestination(nullptr),
myPossibleTurnDestination(nullptr),
myFromJunctionPriority(-1), myToJunctionPriority(-1),
myGeom(geom), myLaneSpreadFunction(spread), myEndOffset(endOffset),
myLaneWidth(laneWidth),
myLoadedLength(UNSPECIFIED_LOADED_LENGTH),
myAmInTLS(false), myAmMacroscopicConnector(false),
myStreetName(streetName),
mySignalPosition(Position::INVALID),
mySignalNode(nullptr),
myIsOffRamp(false),
myIsBidi(false),
myIndex(-1) {
init(nolanes, tryIgnoreNodePositions, origID);
}
NBEdge::NBEdge(const std::string& id, NBNode* from, NBNode* to, const NBEdge* tpl, const PositionVector& geom, int numLanes) :
Named(StringUtils::convertUmlaute(id)),
myStep(EdgeBuildingStep::INIT),
myType(tpl->getTypeID()),
myFrom(from), myTo(to),
myStartAngle(0), myEndAngle(0), myTotalAngle(0),
myPriority(tpl->getPriority()), mySpeed(tpl->getSpeed()),
myFriction(tpl->getFriction()),
myDistance(0),
myTurnDestination(nullptr),
myPossibleTurnDestination(nullptr),
myFromJunctionPriority(-1), myToJunctionPriority(-1),
myGeom(geom),
myLaneSpreadFunction(tpl->getLaneSpreadFunction()),
myEndOffset(tpl->getEndOffset()),
myEdgeStopOffset(tpl->getEdgeStopOffset()),
myLaneWidth(tpl->getLaneWidth()),
myLoadedLength(UNSPECIFIED_LOADED_LENGTH),
myAmInTLS(false),
myAmMacroscopicConnector(false),
myStreetName(tpl->getStreetName()),
mySignalPosition(to == tpl->myTo ? tpl->mySignalPosition : Position::INVALID),
mySignalNode(to == tpl->myTo ? tpl->mySignalNode : nullptr),
myIsOffRamp(false),
myIsBidi(tpl->myIsBidi),
myIndex(-1) {
init(numLanes > 0 ? numLanes : tpl->getNumLanes(), myGeom.size() > 0, "");
for (int i = 0; i < getNumLanes(); i++) {
const int tplIndex = MIN2(i, tpl->getNumLanes() - 1);
setSpeed(i, tpl->getLaneSpeed(tplIndex));
setFriction(i, tpl->getLaneFriction(tplIndex));
setPermissions(tpl->getPermissions(tplIndex), i);
setLaneWidth(i, tpl->myLanes[tplIndex].width);
setLaneType(i, tpl->myLanes[tplIndex].type);
myLanes[i].updateParameters(tpl->myLanes[tplIndex].getParametersMap());
if (to == tpl->myTo) {
setEndOffset(i, tpl->myLanes[tplIndex].endOffset);
setEdgeStopOffset(i, tpl->myLanes[tplIndex].laneStopOffset);
}
}
if (tpl->myLoadedLength > 0 && to == tpl->getFromNode() && from == tpl->getToNode() && geom == tpl->getGeometry().reverse()) {
myLoadedLength = tpl->myLoadedLength;
}
updateParameters(tpl->getParametersMap());
}
NBEdge::NBEdge() :
Named("DUMMY"),
myStep(EdgeBuildingStep::INIT),
myFrom(nullptr), myTo(nullptr),
myStartAngle(0), myEndAngle(0), myTotalAngle(0),
myPriority(0), mySpeed(0), myFriction(UNSPECIFIED_FRICTION),
myDistance(0),
myTurnDestination(nullptr),
myPossibleTurnDestination(nullptr),
myFromJunctionPriority(-1), myToJunctionPriority(-1),
myLaneSpreadFunction(LaneSpreadFunction::RIGHT),
myEndOffset(0),
myEdgeStopOffset(StopOffset()),
myLaneWidth(0),
myLoadedLength(UNSPECIFIED_LOADED_LENGTH),
myAmInTLS(false),
myAmMacroscopicConnector(false),
mySignalPosition(Position::INVALID),
mySignalNode(nullptr) {
}
void
NBEdge::reinit(NBNode* from, NBNode* to, const std::string& type,
double speed, double friction, int nolanes, int priority,
PositionVector geom, double laneWidth, double endOffset,
const std::string& streetName,
LaneSpreadFunction spread,
bool tryIgnoreNodePositions) {
if (myFrom != from) {
myFrom->removeEdge(this, false);
}
if (myTo != to) {
myTo->removeEdge(this, false);
}
myType = StringUtils::convertUmlaute(type);
myFrom = from;
myTo = to;
myPriority = priority;
myGeom = geom;
myLaneSpreadFunction = spread;
myLoadedLength = UNSPECIFIED_LOADED_LENGTH;
myStreetName = streetName;
const std::vector<Lane> oldLanes = myLanes;
init(nolanes, tryIgnoreNodePositions, oldLanes.empty() ? "" : oldLanes[0].getParameter(SUMO_PARAM_ORIGID));
for (int i = 0; i < (int)nolanes; ++i) {
PositionVector newShape = myLanes[i].shape;
myLanes[i] = oldLanes[MIN2(i, (int)oldLanes.size() - 1)];
myLanes[i].shape = newShape;
}
if (endOffset != UNSPECIFIED_OFFSET) {
setEndOffset(-1, endOffset);
}
if (laneWidth != UNSPECIFIED_WIDTH) {
setLaneWidth(-1, laneWidth);
}
if (speed != UNSPECIFIED_SPEED) {
setSpeed(-1, speed);
}
if (friction != UNSPECIFIED_FRICTION) {
setFriction(-1, friction);
}
}
void
NBEdge::reinitNodes(NBNode* from, NBNode* to) {
if (from == nullptr || to == nullptr) {
throw ProcessError(TLF("At least one of edge's '%' nodes is not known.", myID));
}
if (myFrom != from) {
myFrom->removeEdge(this, false);
}
if (myTo != to) {
myTo->removeEdge(this, false);
}
if (myFrom != from) {
myFrom = from;
myFrom->addOutgoingEdge(this);
}
if (myTo != to) {
myTo = to;
myTo->addIncomingEdge(this);
}
computeAngle();
}
void
NBEdge::init(int noLanes, bool tryIgnoreNodePositions, const std::string& origID) {
if (noLanes == 0) {
throw ProcessError(TLF("Edge '%' needs at least one lane.", myID));
}
if (myFrom == nullptr || myTo == nullptr) {
throw ProcessError(TLF("At least one of edge's '%' nodes is not known.", myID));
}
if (!SUMOXMLDefinitions::isValidNetID(myID)) {
throw ProcessError(TLF("Invalid edge id '%'.", myID));
}
if (myFrom->getID() < myTo->getID()) {
PositionVector reverse = myGeom.reverse();
reverse.removeDoublePoints(POSITION_EPS, true);
myGeom = reverse.reverse();
} else {
myGeom.removeDoublePoints(POSITION_EPS, true);
}
if (!tryIgnoreNodePositions || myGeom.size() < 2) {
if (myGeom.size() == 0) {
myGeom.push_back(myFrom->getPosition());
myGeom.push_back(myTo->getPosition());
} else {
myGeom.push_back_noDoublePos(myTo->getPosition());
myGeom.push_front_noDoublePos(myFrom->getPosition());
}
}
if (myGeom.size() < 2) {
myGeom.clear();
myGeom.push_back(myFrom->getPosition());
myGeom.push_back(myTo->getPosition());
}
if (myGeom.size() == 2 && myGeom[0] == myGeom[1]) {
WRITE_WARNINGF(TL("Edge's '%' from- and to-node are at the same position."), myID);
int patchIndex = myFrom->getID() < myTo->getID() ? 1 : 0;
myGeom[patchIndex].add(Position(POSITION_EPS, POSITION_EPS));
}
myFrom->addOutgoingEdge(this);
myTo->addIncomingEdge(this);
assert(myGeom.size() >= 2);
myLength = myGeom.length();
if ((int)myLanes.size() > noLanes) {
for (int lane = noLanes; lane < (int)myLanes.size(); ++lane) {
removeFromConnections(nullptr, lane, -1);
}
const EdgeVector& incoming = myFrom->getIncomingEdges();
for (EdgeVector::const_iterator i = incoming.begin(); i != incoming.end(); i++) {
for (int lane = noLanes; lane < (int)myLanes.size(); ++lane) {
(*i)->removeFromConnections(this, -1, lane);
}
}
}
myLanes.clear();
for (int i = 0; i < noLanes; i++) {
myLanes.push_back(Lane(this, origID));
}
computeLaneShapes();
computeAngle();
#ifdef DEBUG_CONNECTION_GUESSING
if (DEBUGCOND) {
std::cout << "init edge=" << getID() << "\n";
for (Connection& c : myConnections) {
std::cout << " conn " << c.getDescription(this) << "\n";
}
for (Connection& c : myConnectionsToDelete) {
std::cout << " connToDelete " << c.getDescription(this) << "\n";
}
}
#endif
}
NBEdge::~NBEdge() {}
void
NBEdge::reshiftPosition(double xoff, double yoff) {
myGeom.add(xoff, yoff, 0);
for (Lane& lane : myLanes) {
lane.customShape.add(xoff, yoff, 0);
}
computeLaneShapes();
for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
(*i).customShape.add(xoff, yoff, 0);
}
if (mySignalPosition != Position::INVALID) {
mySignalPosition.add(xoff, yoff);
}
myFromBorder.add(xoff, yoff, 0);
myToBorder.add(xoff, yoff, 0);
computeEdgeShape();
computeAngle();
}
void
NBEdge::roundGeometry() {
myGeom.round(gPrecision);
for (Lane& lane : myLanes) {
lane.customShape.round(gPrecision);
}
for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
(*i).customShape.round(gPrecision);
}
}
void
NBEdge::roundSpeed() {
mySpeed = roundDecimalToEven(mySpeed, gPrecision);
for (Lane& l : myLanes) {
l.speed = roundDecimalToEven(l.speed, gPrecision);
}
}
void
NBEdge::mirrorX() {
myGeom.mirrorX();
for (int i = 0; i < (int)myLanes.size(); i++) {
myLanes[i].shape.mirrorX();
myLanes[i].customShape.mirrorX();
}
for (Connection& c : myConnections) {
c.shape.mirrorX();
c.viaShape.mirrorX();
c.customShape.mirrorX();
}
if (mySignalPosition != Position::INVALID) {
mySignalPosition.sety(-mySignalPosition.y());
}
computeAngle();
}
const PositionVector
NBEdge::getInnerGeometry() const {
return myGeom.getSubpartByIndex(1, (int)myGeom.size() - 2);
}
bool
NBEdge::hasDefaultGeometry() const {
return myGeom.size() == 2 && hasDefaultGeometryEndpoints();
}
bool
NBEdge::hasDefaultGeometryEndpoints() const {
return myGeom.front().almostSame(myFrom->getPosition(), 0.01) &&
myGeom.back().almostSame(myTo->getPosition(), 0.01);
}
bool
NBEdge::hasDefaultGeometryEndpointAtNode(const NBNode* node) const {
if (node == myFrom) {
return myGeom.front() == node->getPosition();
} else {
assert(node == myTo);
return myGeom.back() == node->getPosition();
}
}
Position
NBEdge::getEndpointAtNode(const NBNode* node) const {
return node == myFrom ? myGeom.front() : myGeom.back();
}
void
NBEdge::resetEndpointAtNode(const NBNode* node) {
assert(myGeom.size() >= 2);
if (node == myFrom) {
myGeom[0] = myFrom->getPosition();
} else if (node == myTo) {
myGeom[-1] = myTo->getPosition();
} else {
assert(false);
}
}
void
NBEdge::setGeometry(const PositionVector& s, bool inner) {
Position begin = myGeom.front();
Position end = myGeom.back();
myGeom = s;
if (inner) {
myGeom.insert(myGeom.begin(), begin);
myGeom.push_back(end);
}
if (myGeom.size() == 2 && myGeom[0] == myGeom[1]) {
WRITE_WARNINGF(TL("Edge's '%' from- and to-node are at the same position."), myID);
int patchIndex = myFrom->getID() < myTo->getID() ? 1 : 0;
myGeom[patchIndex].add(Position(POSITION_EPS, POSITION_EPS));
}
computeLaneShapes();
computeAngle();
myLength = myGeom.length();
}
void
NBEdge::extendGeometryAtNode(const NBNode* node, double maxExtent) {
if (node == myFrom) {
myGeom.extrapolate(maxExtent, true);
double offset = myGeom.nearest_offset_to_point2D(node->getPosition());
if (offset != GeomHelper::INVALID_OFFSET) {
myGeom = myGeom.getSubpart2D(MIN2(offset, myGeom.length2D() - 2 * POSITION_EPS), myGeom.length2D());
}
} else {
assert(node == myTo);
myGeom.extrapolate(maxExtent, false, true);
double offset = myGeom.nearest_offset_to_point2D(node->getPosition());
if (offset != GeomHelper::INVALID_OFFSET) {
myGeom = myGeom.getSubpart2D(0, MAX2(offset, 2 * POSITION_EPS));
}
}
}
void
NBEdge::shortenGeometryAtNode(const NBNode* node, double reduction) {
reduction = MIN2(reduction, myGeom.length2D() - 2 * POSITION_EPS);
if (node == myFrom) {
myGeom = myGeom.getSubpart2D(reduction, myGeom.length2D());
} else {
myGeom = myGeom.getSubpart2D(0, myGeom.length2D() - reduction);
}
computeLaneShapes();
}
void
NBEdge::setNodeBorder(const NBNode* node, const Position& p, const Position& p2, bool rectangularCut) {
PositionVector border;
if (rectangularCut) {
const double extend = 100;
border = myGeom.getOrthogonal(p, extend, node == myTo);
} else {
border.push_back(p);
border.push_back(p2);
}
if (border.size() == 2) {
border.extrapolate2D(getTotalWidth());
if (node == myFrom) {
myFromBorder = border;
} else {
assert(node == myTo);
myToBorder = border;
}
}
#ifdef DEBUG_NODE_BORDER
gDebugFlag1 = DEBUGCOND;
if (DEBUGCOND) std::cout << "setNodeBorder edge=" << getID() << " node=" << node->getID()
<< " rect=" << rectangularCut
<< " p=" << p << " p2=" << p2
<< " border=" << border
<< " myGeom=" << myGeom
<< "\n";
#endif
}
const PositionVector&
NBEdge::getNodeBorder(const NBNode* node) const {
if (node == myFrom) {
return myFromBorder;
} else {
assert(node == myTo);
return myToBorder;
}
}
void
NBEdge::resetNodeBorder(const NBNode* node) {
if (node == myFrom) {
myFromBorder.clear();
} else {
assert(node == myTo);
myToBorder.clear();
}
}
bool
NBEdge::isBidiRail(bool ignoreSpread) const {
return (isRailway(getPermissions())
&& (ignoreSpread || myLaneSpreadFunction == LaneSpreadFunction::CENTER)
&& myPossibleTurnDestination != nullptr
&& myPossibleTurnDestination->myPossibleTurnDestination == this
&& (ignoreSpread || myPossibleTurnDestination->getLaneSpreadFunction() == LaneSpreadFunction::CENTER)
&& isRailway(myPossibleTurnDestination->getPermissions())
&& myPossibleTurnDestination->getGeometry().reverse() == getGeometry());
}
bool
NBEdge::isBidiEdge(bool checkPotential) const {
return myPossibleTurnDestination != nullptr
&& myPossibleTurnDestination->myPossibleTurnDestination == this
&& (myIsBidi || myPossibleTurnDestination->myIsBidi || checkPotential)
&& myPossibleTurnDestination->getToNode() == getFromNode()
&& myPossibleTurnDestination->getLaneSpreadFunction() == myLaneSpreadFunction
&& ((myLaneSpreadFunction == LaneSpreadFunction::CENTER
&& (myPossibleTurnDestination->getGeometry().reverse() == getGeometry()
|| (checkPotential && getGeometry().size() == 2 && myPossibleTurnDestination->getGeometry().size() == 2)))
|| (myLanes.back().shape.reverse().almostSame(myPossibleTurnDestination->myLanes.back().shape, POSITION_EPS))
);
}
bool
NBEdge::isRailDeadEnd() const {
if (!isRailway(getPermissions())) {
return false;
}
for (NBEdge* out : myTo->getOutgoingEdges()) {
if (isRailway(out->getPermissions()) &&
out != getTurnDestination(true)) {
return true;
}
}
return true;
}
PositionVector
NBEdge::cutAtIntersection(const PositionVector& old) const {
PositionVector shape = old;
shape = startShapeAt(shape, myFrom, myFromBorder);
#ifdef DEBUG_CUT_LANES
if (DEBUGCOND) {
std::cout << getID() << " cutFrom=" << shape << "\n";
}
#endif
if (shape.size() < 2) {
const double oldLength = old.length();
shape = old.getSubpart(oldLength - 2 * POSITION_EPS, oldLength);
#ifdef DEBUG_CUT_LANES
if (DEBUGCOND) {
std::cout << getID() << " cutFromFallback=" << shape << "\n";
}
#endif
}
shape = startShapeAt(shape.reverse(), myTo, myToBorder).reverse();
#ifdef DEBUG_CUT_LANES
if (DEBUGCOND) {
std::cout << getID() << " cutTo=" << shape << "\n";
}
#endif
if (shape.length() < POSITION_EPS) {
if (old.length() < 2 * POSITION_EPS) {
shape = old;
} else {
const double midpoint = old.length() / 2;
shape = old.getSubpart(midpoint - POSITION_EPS, midpoint + POSITION_EPS);
assert(shape.size() >= 2);
assert(shape.length() > 0);
#ifdef DEBUG_CUT_LANES
if (DEBUGCOND) {
std::cout << getID() << " fallBackShort=" << shape << "\n";
}
#endif
}
} else {
if (DEG2RAD(135) < fabs(GeomHelper::angleDiff(shape.beginEndAngle(), old.beginEndAngle()))) {
PositionVector tmp;
tmp.push_back(shape[0]);
tmp.push_back(shape[-1]);
shape = tmp;
if (tmp.length() < POSITION_EPS) {
if (old.length() < 2 * POSITION_EPS) {
shape = old;
} else {
const double midpoint = old.length() / 2;
shape = old.getSubpart(midpoint - POSITION_EPS, midpoint + POSITION_EPS);
assert(shape.size() >= 2);
assert(shape.length() > 0);
}
#ifdef DEBUG_CUT_LANES
if (DEBUGCOND) {
std::cout << getID() << " fallBackReversed=" << shape << "\n";
}
#endif
} else {
const double midpoint = shape.length() / 2;
shape = shape.getSubpart(midpoint - POSITION_EPS, midpoint + POSITION_EPS);
if (shape.length() < POSITION_EPS) {
assert(false);
}
shape = shape.reverse();
#ifdef DEBUG_CUT_LANES
if (DEBUGCOND) {
std::cout << getID() << " fallBackReversed2=" << shape << " mid=" << midpoint << "\n";
}
#endif
}
const double z = (shape[0].z() + shape[1].z()) / 2;
shape[0].setz(z);
shape[1].setz(z);
}
}
return shape;
}
void
NBEdge::computeEdgeShape(double smoothElevationThreshold) {
if (smoothElevationThreshold > 0 && myGeom.hasElevation()) {
PositionVector cut = cutAtIntersection(myGeom);
if (!myFrom->geometryLike()) {
cut[0].setz(myFrom->getPosition().z());
const double d = cut[0].distanceTo2D(cut[1]);
const double dZ = fabs(cut[0].z() - cut[1].z());
if (dZ / smoothElevationThreshold > d) {
cut = cut.smoothedZFront(MIN2(cut.length2D() / 2, dZ / smoothElevationThreshold));
}
}
if (!myTo->geometryLike()) {
cut[-1].setz(myTo->getPosition().z());
const double d = cut[-1].distanceTo2D(cut[-2]);
const double dZ = fabs(cut[-1].z() - cut[-2].z());
if (dZ / smoothElevationThreshold > d) {
cut = cut.reverse().smoothedZFront(MIN2(cut.length2D() / 2, dZ / smoothElevationThreshold)).reverse();
}
}
cut[0] = myGeom[0];
cut[-1] = myGeom[-1];
if (cut != myGeom) {
myGeom = cut;
computeLaneShapes();
}
}
for (int i = 0; i < (int)myLanes.size(); i++) {
myLanes[i].shape = cutAtIntersection(myLanes[i].shape);
}
double avgLength = 0;
for (int i = 0; i < (int)myLanes.size(); i++) {
avgLength += myLanes[i].shape.length();
}
myLength = avgLength / (double) myLanes.size();
computeAngle();
}
PositionVector
NBEdge::startShapeAt(const PositionVector& laneShape, const NBNode* startNode, PositionVector nodeShape) {
if (nodeShape.size() == 0) {
nodeShape = startNode->getShape();
nodeShape.closePolygon();
}
PositionVector lb = laneShape;
lb.extrapolate2D(100.0);
if (nodeShape.intersects(laneShape)) {
std::vector<double> pbv = laneShape.intersectsAtLengths2D(nodeShape);
assert(pbv.size() > 0);
double pb = MIN2(laneShape.length2D() - POSITION_EPS - NUMERICAL_EPS, VectorHelper<double>::maxValue(pbv));
if (pb < 0) {
return laneShape;
}
PositionVector ns = laneShape.getSubpart2D(pb, laneShape.length2D());
const double delta = ns[0].z() - laneShape[0].z();
if (fabs(delta) > 2 * POSITION_EPS && (!startNode->geometryLike() || pb < 1)) {
ns[0].setz(startNode->getPosition().z());
}
assert(ns.size() >= 2);
return ns;
} else if (nodeShape.intersects(lb)) {
std::vector<double> pbv = lb.intersectsAtLengths2D(nodeShape);
assert(pbv.size() > 0);
double pb = VectorHelper<double>::maxValue(pbv);
assert(pb >= 0);
PositionVector result = laneShape.getSubpartByIndex(1, (int)laneShape.size() - 1);
Position np = lb.positionAtOffset2D(pb);
const double delta = np.z() - laneShape[0].z();
if (fabs(delta) > 2 * POSITION_EPS && !startNode->geometryLike()) {
np.setz(startNode->getPosition().z());
}
result.push_front_noDoublePos(np);
return result;
} else {
return laneShape;
}
}
const PositionVector&
NBEdge::getLaneShape(int i) const {
return myLanes[i].shape;
}
void
NBEdge::setLaneSpreadFunction(LaneSpreadFunction spread) {
myLaneSpreadFunction = spread;
}
LaneSpreadFunction
NBEdge::getLaneSpreadFunction() const {
return myLaneSpreadFunction;
}
void
NBEdge::addGeometryPoint(int index, const Position& p) {
if (index >= 0) {
myGeom.insert(myGeom.begin() + index, p);
} else {
myGeom.insert(myGeom.end() + index, p);
}
}
void
NBEdge::reduceGeometry(const double minDist) {
if (myFrom->getID() < myTo->getID()) {
PositionVector reverse = myGeom.reverse();
reverse.removeDoublePoints(minDist, true, 0, 0, true);
myGeom = reverse.reverse();
for (Lane& lane : myLanes) {
reverse = lane.customShape.reverse();
reverse.removeDoublePoints(minDist, true, 0, 0, true);
lane.customShape = reverse.reverse();
}
} else {
myGeom.removeDoublePoints(minDist, true, 0, 0, true);
for (Lane& lane : myLanes) {
lane.customShape.removeDoublePoints(minDist, true, 0, 0, true);
}
}
}
void
NBEdge::checkGeometry(const double maxAngle, bool fixAngle, const double minRadius, bool fix, bool silent) {
if (myGeom.size() < 3) {
return;
}
std::vector<double> angles;
for (int i = 0; i < (int)myGeom.size() - 1; ++i) {
angles.push_back(myGeom.angleAt2D(i));
}
NBEdge* bidi = const_cast<NBEdge*>(getBidiEdge());
for (int i = 0; i < (int)angles.size() - 1; ++i) {
const double relAngle = fabs(GeomHelper::angleDiff(angles[i], angles[i + 1]));
if (maxAngle > 0 && relAngle > maxAngle) {
if (fixAngle) {
WRITE_MESSAGEF(TL("Removing sharp angle of % degrees at edge '%', segment %."),
toString(relAngle), getID(), i);
myGeom.erase(myGeom.begin() + i + 1);
if (bidi != nullptr) {
bidi->myGeom = myGeom.reverse();
}
checkGeometry(maxAngle, fixAngle, minRadius, fix, silent);
return;
} else if (!silent) {
WRITE_WARNINGF(TL("Found angle of % degrees at edge '%', segment %."), RAD2DEG(relAngle), getID(), i);
}
}
if (relAngle < DEG2RAD(1)) {
continue;
}
if (i == 0 || i == (int)angles.size() - 2) {
const bool start = i == 0;
const double dist = (start ? myGeom[0].distanceTo2D(myGeom[1]) : myGeom[-2].distanceTo2D(myGeom[-1]));
const double r = tan(0.5 * (M_PI - relAngle)) * dist;
if (minRadius > 0 && r < minRadius) {
if (fix) {
WRITE_MESSAGEF(TL("Removing sharp turn with radius % at the % of edge '%'."),
toString(r), start ? TL("start") : TL("end"), getID());
myGeom.erase(myGeom.begin() + (start ? 1 : i + 1));
if (bidi != nullptr) {
bidi->myGeom = myGeom.reverse();
}
checkGeometry(maxAngle, fixAngle, minRadius, fix, silent);
return;
} else if (!silent) {
WRITE_WARNINGF(TL("Found sharp turn with radius % at the % of edge '%'."),
toString(r), start ? TL("start") : TL("end"), getID());
}
}
}
}
}
bool
NBEdge::addEdge2EdgeConnection(NBEdge* dest, bool overrideRemoval, SVCPermissions permissions) {
if (myStep == EdgeBuildingStep::INIT_REJECT_CONNECTIONS) {
return true;
}
if (dest != nullptr && myTo != dest->myFrom) {
return false;
}
if (dest == nullptr) {
invalidateConnections();
myConnections.push_back(Connection(-1, dest, -1));
myStep = EdgeBuildingStep::LANES2LANES_USER;
} else if (find_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(dest)) == myConnections.end()) {
myConnections.push_back(Connection(-1, dest, -1));
myConnections.back().permissions = permissions;
}
if (overrideRemoval) {
for (std::vector<Connection>::iterator it = myConnectionsToDelete.begin(); it != myConnectionsToDelete.end();) {
if (it->toEdge == dest) {
it = myConnectionsToDelete.erase(it);
} else {
it++;
}
}
}
if (myStep < EdgeBuildingStep::EDGE2EDGES) {
myStep = EdgeBuildingStep::EDGE2EDGES;
}
return true;
}
bool
NBEdge::addLane2LaneConnection(int from, NBEdge* dest,
int toLane, Lane2LaneInfoType type,
bool mayUseSameDestination,
bool mayDefinitelyPass,
KeepClear keepClear,
double contPos,
double visibility,
double speed,
double friction,
double length,
const PositionVector& customShape,
bool uncontrolled,
SVCPermissions permissions,
bool indirectLeft,
const std::string& edgeType,
SVCPermissions changeLeft,
SVCPermissions changeRight,
bool postProcess) {
if (myStep == EdgeBuildingStep::INIT_REJECT_CONNECTIONS) {
return true;
}
if (myTo != dest->myFrom) {
return false;
}
if (!addEdge2EdgeConnection(dest)) {
return false;
}
return setConnection(from, dest, toLane, type, mayUseSameDestination, mayDefinitelyPass, keepClear, contPos, visibility, speed, friction, length,
customShape, uncontrolled, permissions, indirectLeft, edgeType, changeLeft, changeRight, postProcess);
}
bool
NBEdge::addLane2LaneConnections(int fromLane,
NBEdge* dest, int toLane,
int no, Lane2LaneInfoType type,
bool invalidatePrevious,
bool mayDefinitelyPass) {
if (invalidatePrevious) {
invalidateConnections(true);
}
bool ok = true;
for (int i = 0; i < no && ok; i++) {
ok &= addLane2LaneConnection(fromLane + i, dest, toLane + i, type, false, mayDefinitelyPass);
}
return ok;
}
bool
NBEdge::setConnection(int lane, NBEdge* destEdge,
int destLane, Lane2LaneInfoType type,
bool mayUseSameDestination,
bool mayDefinitelyPass,
KeepClear keepClear,
double contPos,
double visibility,
double speed,
double friction,
double length,
const PositionVector& customShape,
bool uncontrolled,
SVCPermissions permissions,
bool indirectLeft,
const std::string& edgeType,
SVCPermissions changeLeft,
SVCPermissions changeRight,
bool postProcess) {
if (myStep == EdgeBuildingStep::INIT_REJECT_CONNECTIONS) {
return false;
}
if (!mayUseSameDestination && hasConnectionTo(destEdge, destLane)) {
return false;
}
if (find_if(myConnections.begin(), myConnections.end(), connections_finder(lane, destEdge, destLane)) != myConnections.end()) {
return true;
}
if ((int)myLanes.size() <= lane || destEdge->getNumLanes() <= (int)destLane) {
WRITE_WARNINGF(TL("Could not set connection from '%' to '%'."), getLaneID(lane), destEdge->getLaneID(destLane));
return false;
}
for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
if ((*i).toEdge == destEdge && ((*i).fromLane == -1 || (*i).toLane == -1)) {
if (permissions == SVC_UNSPECIFIED) {
permissions = (*i).permissions;
}
i = myConnections.erase(i);
} else {
++i;
}
}
myConnections.push_back(Connection(lane, destEdge, destLane));
if (mayDefinitelyPass) {
myConnections.back().mayDefinitelyPass = true;
}
myConnections.back().keepClear = keepClear;
myConnections.back().contPos = contPos;
myConnections.back().visibility = visibility;
myConnections.back().permissions = permissions;
myConnections.back().indirectLeft = indirectLeft;
myConnections.back().edgeType = edgeType;
myConnections.back().changeLeft = changeLeft;
myConnections.back().changeRight = changeRight;
myConnections.back().speed = speed;
myConnections.back().friction = friction;
myConnections.back().customLength = length;
myConnections.back().customShape = customShape;
myConnections.back().uncontrolled = uncontrolled;
if (type == Lane2LaneInfoType::USER) {
myStep = EdgeBuildingStep::LANES2LANES_USER;
} else {
if (type == Lane2LaneInfoType::COMPUTED) {
myStep = EdgeBuildingStep::LANES2LANES_RECHECK;
} else {
if (myStep != EdgeBuildingStep::LANES2LANES_RECHECK) {
myStep = EdgeBuildingStep::LANES2LANES_DONE;
}
}
}
if (postProcess) {
for (std::vector<Connection>::iterator it = myConnectionsToDelete.begin(); it != myConnectionsToDelete.end();) {
if ((it->fromLane < 0 || it->fromLane == lane)
&& (it->toEdge == nullptr || it->toEdge == destEdge)
&& (it->toLane < 0 || it->toLane == destLane)) {
it = myConnectionsToDelete.erase(it);
} else {
it++;
}
}
}
return true;
}
std::vector<NBEdge::Connection>
NBEdge::getConnectionsFromLane(int lane, const NBEdge* to, int toLane) const {
std::vector<NBEdge::Connection> ret;
for (const Connection& c : myConnections) {
if ((lane < 0 || c.fromLane == lane)
&& (to == nullptr || to == c.toEdge)
&& (toLane < 0 || toLane == c.toLane)) {
ret.push_back(c);
}
}
return ret;
}
const NBEdge::Connection&
NBEdge::getConnection(int fromLane, const NBEdge* to, int toLane) const {
for (const Connection& c : myConnections) {
if (c.fromLane == fromLane && c.toEdge == to && c.toLane == toLane) {
return c;
}
}
throw ProcessError("Connection from " + getID() + "_" + toString(fromLane)
+ " to " + to->getID() + "_" + toString(toLane) + " not found");
}
NBEdge::Connection&
NBEdge::getConnectionRef(int fromLane, const NBEdge* to, int toLane) {
for (Connection& c : myConnections) {
if (c.fromLane == fromLane && c.toEdge == to && c.toLane == toLane) {
return c;
}
}
throw ProcessError("Connection from " + getID() + "_" + toString(fromLane)
+ " to " + to->getID() + "_" + toString(toLane) + " not found");
}
bool
NBEdge::hasConnectionTo(const NBEdge* destEdge, int destLane, int fromLane) const {
return destEdge != nullptr && find_if(myConnections.begin(), myConnections.end(), connections_toedgelane_finder(destEdge, destLane, fromLane)) != myConnections.end();
}
bool
NBEdge::isConnectedTo(const NBEdge* e, const bool ignoreTurnaround) const {
if (!ignoreTurnaround && (e == myTurnDestination)) {
return true;
}
return
find_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(e))
!=
myConnections.end();
}
const EdgeVector*
NBEdge::getConnectedSorted() {
EdgeVector outgoing;
if (myConnections.size() == 0) {
outgoing = myTo->getOutgoingEdges();
} else {
for (std::vector<Connection>::const_iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
if (find(outgoing.begin(), outgoing.end(), (*i).toEdge) == outgoing.end()) {
outgoing.push_back((*i).toEdge);
}
}
}
for (std::vector<Connection>::iterator it = myConnectionsToDelete.begin(); it != myConnectionsToDelete.end(); ++it) {
if (it->fromLane < 0 && it->toLane < 0) {
EdgeVector::iterator forbidden = std::find(outgoing.begin(), outgoing.end(), it->toEdge);
if (forbidden != outgoing.end()) {
outgoing.erase(forbidden);
}
}
}
int size = (int) outgoing.size();
EdgeVector* edges = new EdgeVector();
edges->reserve(size);
for (EdgeVector::const_iterator i = outgoing.begin(); i != outgoing.end(); i++) {
NBEdge* outedge = *i;
if (outedge != nullptr && outedge != myTurnDestination) {
edges->push_back(outedge);
}
}
std::sort(edges->begin(), edges->end(), NBContHelper::relative_outgoing_edge_sorter(this));
return edges;
}
EdgeVector
NBEdge::getConnectedEdges() const {
EdgeVector ret;
for (std::vector<Connection>::const_iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
if (find(ret.begin(), ret.end(), (*i).toEdge) == ret.end()) {
ret.push_back((*i).toEdge);
}
}
return ret;
}
EdgeVector
NBEdge::getIncomingEdges() const {
EdgeVector ret;
const EdgeVector& candidates = myFrom->getIncomingEdges();
for (EdgeVector::const_iterator i = candidates.begin(); i != candidates.end(); i++) {
if ((*i)->isConnectedTo(this)) {
ret.push_back(*i);
}
}
return ret;
}
std::vector<int>
NBEdge::getConnectionLanes(NBEdge* currentOutgoing, bool withBikes) const {
std::vector<int> ret;
if (currentOutgoing != myTurnDestination) {
for (const Connection& c : myConnections) {
if (c.toEdge == currentOutgoing && (withBikes || getPermissions(c.fromLane) != SVC_BICYCLE)) {
ret.push_back(c.fromLane);
}
}
}
return ret;
}
void
NBEdge::sortOutgoingConnectionsByAngle() {
sort(myConnections.begin(), myConnections.end(), connections_relative_edgelane_sorter(this));
}
void
NBEdge::sortOutgoingConnectionsByIndex() {
sort(myConnections.begin(), myConnections.end(), connections_sorter);
}
void
NBEdge::remapConnections(const EdgeVector& incoming) {
EdgeVector connected = getConnectedEdges();
for (EdgeVector::const_iterator i = incoming.begin(); i != incoming.end(); i++) {
NBEdge* inc = *i;
inc->myStep = EdgeBuildingStep::EDGE2EDGES;
for (EdgeVector::iterator j = connected.begin(); j != connected.end(); j++) {
inc->addEdge2EdgeConnection(*j);
}
inc->removeFromConnections(this);
}
}
void
NBEdge::removeFromConnections(NBEdge* toEdge, int fromLane, int toLane, bool tryLater, const bool adaptToLaneRemoval,
const bool keepPossibleTurns) {
const int fromLaneRemoved = adaptToLaneRemoval && fromLane >= 0 ? fromLane : -1;
const int toLaneRemoved = adaptToLaneRemoval && toLane >= 0 ? toLane : -1;
for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
Connection& c = *i;
if ((toEdge == nullptr || c.toEdge == toEdge)
&& (fromLane < 0 || c.fromLane == fromLane)
&& (toLane < 0 || c.toLane == toLane)) {
if (myTo->isTLControlled()) {
std::set<NBTrafficLightDefinition*> tldefs = myTo->getControllingTLS();
for (std::set<NBTrafficLightDefinition*>::iterator it = tldefs.begin(); it != tldefs.end(); it++) {
(*it)->removeConnection(NBConnection(this, c.fromLane, c.toEdge, c.toLane));
}
}
i = myConnections.erase(i);
tryLater = false;
} else {
if (fromLaneRemoved >= 0 && c.fromLane > fromLaneRemoved) {
if (myTo->isTLControlled()) {
std::set<NBTrafficLightDefinition*> tldefs = myTo->getControllingTLS();
for (std::set<NBTrafficLightDefinition*>::iterator it = tldefs.begin(); it != tldefs.end(); it++) {
for (NBConnectionVector::iterator tlcon = (*it)->getControlledLinks().begin(); tlcon != (*it)->getControlledLinks().end(); ++tlcon) {
NBConnection& tc = *tlcon;
if (tc.getTo() == c.toEdge && tc.getFromLane() == c.fromLane && tc.getToLane() == c.toLane) {
tc.shiftLaneIndex(this, -1);
}
}
}
}
c.fromLane--;
}
if (toLaneRemoved >= 0 && c.toLane > toLaneRemoved && (toEdge == nullptr || c.toEdge == toEdge)) {
c.toLane--;
}
++i;
}
}
if (myTurnDestination == toEdge && fromLane < 0) {
myTurnDestination = nullptr;
}
if (myPossibleTurnDestination == toEdge && fromLane < 0 && !keepPossibleTurns) {
myPossibleTurnDestination = nullptr;
}
if (tryLater) {
myConnectionsToDelete.push_back(Connection(fromLane, toEdge, toLane));
#ifdef DEBUG_CONNECTION_GUESSING
if (DEBUGCOND) {
std::cout << "removeFromConnections " << getID() << "_" << fromLane << "->" << toEdge->getID() << "_" << toLane << "\n";
for (Connection& c : myConnections) {
std::cout << " conn " << c.getDescription(this) << "\n";
}
for (Connection& c : myConnectionsToDelete) {
std::cout << " connToDelete " << c.getDescription(this) << "\n";
}
}
#endif
}
}
bool
NBEdge::removeFromConnections(const NBEdge::Connection& connectionToRemove) {
for (auto i = myConnections.begin(); i != myConnections.end(); i++) {
if ((i->toEdge == connectionToRemove.toEdge) && (i->fromLane == connectionToRemove.fromLane) && (i->toLane == connectionToRemove.toLane)) {
myConnections.erase(i);
return true;
}
}
return false;
}
void
NBEdge::invalidateConnections(bool reallowSetting) {
myTurnDestination = nullptr;
myConnections.clear();
if (reallowSetting) {
myStep = EdgeBuildingStep::INIT;
} else {
myStep = EdgeBuildingStep::INIT_REJECT_CONNECTIONS;
}
}
void
NBEdge::replaceInConnections(NBEdge* which, NBEdge* by, int laneOff) {
for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
if ((*i).toEdge == which) {
(*i).toEdge = by;
(*i).toLane += laneOff;
}
}
if (myTurnDestination == which) {
myTurnDestination = by;
}
}
void
NBEdge::replaceInConnections(NBEdge* which, const std::vector<NBEdge::Connection>& origConns) {
std::map<int, int> laneMap;
int minLane = -1;
int maxLane = -1;
bool wasConnected = false;
for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
if ((*i).toEdge != which) {
continue;
}
wasConnected = true;
if ((*i).fromLane != -1) {
int fromLane = (*i).fromLane;
laneMap[(*i).toLane] = fromLane;
if (minLane == -1 || minLane > fromLane) {
minLane = fromLane;
}
if (maxLane == -1 || maxLane < fromLane) {
maxLane = fromLane;
}
}
}
if (!wasConnected) {
return;
}
std::vector<NBEdge::Connection> conns = origConns;
EdgeVector origTargets = getSuccessors();
for (std::vector<NBEdge::Connection>::iterator i = conns.begin(); i != conns.end(); ++i) {
if ((*i).toEdge == which || (*i).toEdge == this
|| std::find(origTargets.begin(), origTargets.end(), (*i).toEdge) != origTargets.end()) {
#ifdef DEBUG_REPLACECONNECTION
if (DEBUGCOND) {
std::cout << " replaceInConnections edge=" << getID() << " which=" << which->getID()
<< " origTargets=" << toString(origTargets) << " newTarget=" << i->toEdge->getID() << " skipped\n";
}
#endif
continue;
}
if (which->getStep() == EdgeBuildingStep::EDGE2EDGES) {
replaceInConnections(which, (*i).toEdge, 0);
continue;
}
int fromLane = (*i).fromLane;
int toUse = -1;
if (laneMap.find(fromLane) == laneMap.end()) {
if (fromLane >= 0 && fromLane <= minLane) {
toUse = minLane;
for (auto& item : laneMap) {
if (item.first < fromLane) {
item.second = MIN2(item.second, minLane);
}
}
}
if (fromLane >= 0 && fromLane >= maxLane) {
toUse = maxLane;
for (auto& item : laneMap) {
if (item.first > fromLane) {
item.second = MAX2(item.second, maxLane);
}
}
}
} else {
toUse = laneMap[fromLane];
}
if (toUse == -1) {
toUse = 0;
}
#ifdef DEBUG_REPLACECONNECTION
if (DEBUGCOND) {
std::cout << " replaceInConnections edge=" << getID() << " which=" << which->getID() << " origTargets=" << toString(origTargets)
<< " origFrom=" << fromLane << " laneMap=" << joinToString(laneMap, ":", ",") << " minLane=" << minLane << " maxLane=" << maxLane
<< " newTarget=" << i->toEdge->getID() << " fromLane=" << toUse << " toLane=" << i->toLane << "\n";
}
#endif
setConnection(toUse, i->toEdge, i->toLane, Lane2LaneInfoType::COMPUTED, false, i->mayDefinitelyPass, i->keepClear,
i->contPos, i->visibility, i->speed, i->friction, i->customLength, i->customShape, i->uncontrolled);
}
removeFromConnections(which);
}
void
NBEdge::copyConnectionsFrom(NBEdge* src) {
myStep = src->myStep;
myConnections = src->myConnections;
}
bool
NBEdge::canMoveConnection(const Connection& con, int newFromLane) const {
const SVCPermissions common = (getPermissions(newFromLane) & con.toEdge->getPermissions(con.toLane));
return (common > 0 && common != SVC_PEDESTRIAN);
}
void
NBEdge::moveConnectionToLeft(int lane) {
#ifdef DEBUG_CONNECTION_CHECKING
std::cout << " moveConnectionToLeft " << getID() << " lane=" << lane << "\n";
#endif
int index = 0;
for (int i = 0; i < (int)myConnections.size(); ++i) {
if (myConnections[i].fromLane == (int)(lane) && canMoveConnection(myConnections[i], lane + 1)) {
index = i;
}
}
std::vector<Connection>::iterator i = myConnections.begin() + index;
Connection c = *i;
myConnections.erase(i);
setConnection(lane + 1, c.toEdge, c.toLane, Lane2LaneInfoType::VALIDATED, false);
}
void
NBEdge::moveConnectionToRight(int lane) {
#ifdef DEBUG_CONNECTION_CHECKING
std::cout << " moveConnectionToRight " << getID() << " lane=" << lane << "\n";
#endif
for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
if ((*i).fromLane == (int)lane && canMoveConnection(*i, lane - 1)) {
Connection c = *i;
i = myConnections.erase(i);
setConnection(lane - 1, c.toEdge, c.toLane, Lane2LaneInfoType::VALIDATED, false);
return;
}
}
}
double
NBEdge::buildInnerEdges(const NBNode& n, int noInternalNoSplits, int& linkIndex, int& splitIndex) {
const OptionsCont& oc = OptionsCont::getOptions();
const int numPoints = oc.getInt("junctions.internal-link-detail");
const bool joinTurns = oc.getBool("junctions.join-turns");
const double limitTurnSpeed = oc.getFloat("junctions.limit-turn-speed");
const double limitTurnSpeedMinAngle = DEG2RAD(oc.getFloat("junctions.limit-turn-speed.min-angle"));
const double limitTurnSpeedMinAngleRail = DEG2RAD(oc.getFloat("junctions.limit-turn-speed.min-angle.railway"));
const double limitTurnSpeedWarnStraight = oc.getFloat("junctions.limit-turn-speed.warn.straight");
const double limitTurnSpeedWarnTurn = oc.getFloat("junctions.limit-turn-speed.warn.turn");
const bool higherSpeed = oc.getBool("junctions.higher-speed");
const double interalJunctionVehicleWidth = oc.getFloat("internal-junctions.vehicle-width");
const double defaultContPos = oc.getFloat("default.connection.cont-pos");
const bool fromRail = isRailway(getPermissions());
std::string innerID = ":" + n.getID();
NBEdge* toEdge = nullptr;
int edgeIndex = linkIndex;
int internalLaneIndex = 0;
int numLanes = 0;
double lengthSum = 0;
int avoidedIntersectingLeftOriginLane = std::numeric_limits<int>::max();
bool averageLength = true;
double maxCross = 0.;
for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
Connection& con = *i;
con.haveVia = false;
if (con.toEdge == nullptr) {
continue;
}
LinkDirection dir = n.getDirection(this, con.toEdge);
const bool isRightTurn = (dir == LinkDirection::RIGHT || dir == LinkDirection::PARTRIGHT);
const bool isTurn = (isRightTurn || dir == LinkDirection::LEFT || dir == LinkDirection::PARTLEFT);
if (con.toEdge != toEdge) {
edgeIndex = linkIndex;
toEdge = con.toEdge;
internalLaneIndex = 0;
maxCross = MAX2(maxCross, assignInternalLaneLength(i, numLanes, lengthSum, averageLength));
numLanes = 0;
lengthSum = 0;
}
averageLength = !isTurn || joinTurns;
SVCPermissions conPermissions = getPermissions(con.fromLane) & con.toEdge->getPermissions(con.toLane);
const int conShapeFlag = (conPermissions & ~SVC_PEDESTRIAN) != 0 ? 0 : NBNode::SCURVE_IGNORE;
PositionVector shape = n.computeInternalLaneShape(this, con, numPoints, myTo, conShapeFlag);
std::vector<int> foeInternalLinks;
if (dir != LinkDirection::STRAIGHT && shape.length() < POSITION_EPS && !(isBidiRail() && getTurnDestination(true) == con.toEdge)) {
WRITE_WARNINGF(TL("Connection '%_%->%_%' is only %m short."), getID(), con.fromLane, con.toEdge->getID(), con.toLane, shape.length());
}
std::pair<double, std::vector<int> > crossingPositions(-1, std::vector<int>());
std::set<std::string> tmpFoeIncomingLanes;
if (dir != LinkDirection::STRAIGHT || con.contPos != UNSPECIFIED_CONTPOS) {
int index = 0;
std::vector<PositionVector> otherShapes;
const double width1 = MIN2(interalJunctionVehicleWidth / 2, getLaneWidth(con.fromLane) / 2);
const double width1OppositeLeft = 0;
for (const NBEdge* i2 : n.getIncomingEdges()) {
for (const Connection& k2 : i2->getConnections()) {
if (k2.toEdge == nullptr) {
continue;
}
double width2 = k2.toEdge->getLaneWidth(k2.toLane);
if (k2.toEdge->getPermissions(k2.toLane) != SVC_BICYCLE) {
width2 *= 0.5;
}
const bool foes = n.foes(this, con.toEdge, i2, k2.toEdge);
LinkDirection dir2 = n.getDirection(i2, k2.toEdge);
bool needsCont = !isRailway(conPermissions) && (n.needsCont(this, i2, con, k2) || (con.contPos != UNSPECIFIED_CONTPOS && !con.indirectLeft));
const bool avoidIntersectCandidate = !foes && bothLeftTurns(dir, i2, dir2);
bool oppositeLeftIntersect = avoidIntersectCandidate && haveIntersection(n, shape, i2, k2, numPoints, width1OppositeLeft, width2);
int shapeFlag = 0;
SVCPermissions warn = SVCAll & ~(SVC_PEDESTRIAN | SVC_BICYCLE | SVC_DELIVERY | SVC_RAIL_CLASSES);
if (con.customShape.size() == 0
&& k2.customShape.size() == 0
&& (oppositeLeftIntersect || (avoidedIntersectingLeftOriginLane < con.fromLane && avoidIntersectCandidate))
&& ((i2->getPermissions(k2.fromLane) & warn) != 0
&& (k2.toEdge->getPermissions(k2.toLane) & warn) != 0)) {
shapeFlag = NBNode::AVOID_INTERSECTING_LEFT_TURNS;
PositionVector origShape = shape;
shape = n.computeInternalLaneShape(this, con, numPoints, myTo, shapeFlag);
oppositeLeftIntersect = haveIntersection(n, shape, i2, k2, numPoints, width1OppositeLeft, width2, shapeFlag);
if (oppositeLeftIntersect
&& (conPermissions & (SVCAll & ~(SVC_BICYCLE | SVC_PEDESTRIAN))) == 0) {
shape = origShape;
} else {
if (avoidedIntersectingLeftOriginLane == std::numeric_limits<int>::max()
|| avoidedIntersectingLeftOriginLane < con.fromLane) {
for (const PositionVector& otherShape : otherShapes) {
const bool secondIntersection = con.indirectLeft && this == i2 && con.fromLane == k2.fromLane;
const double minDV = firstIntersection(shape, otherShape, width1OppositeLeft, width2,
"Could not compute intersection of conflicting internal lanes at node '" + myTo->getID() + "'", secondIntersection);
if (minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS) {
assert(minDV >= 0);
if (crossingPositions.first < 0 || crossingPositions.first > minDV) {
crossingPositions.first = minDV;
}
}
}
}
avoidedIntersectingLeftOriginLane = con.fromLane;
}
}
const bool bothPrio = getJunctionPriority(&n) > 0 && i2->getJunctionPriority(&n) > 0;
const bool isBicycleLeftTurn = k2.indirectLeft || (dir2 == LinkDirection::LEFT && (i2->getPermissions(k2.fromLane) & k2.toEdge->getPermissions(k2.toLane)) == SVC_BICYCLE);
if ((needsCont || (bothPrio && oppositeLeftIntersect && !isRailway(conPermissions))) && (!con.indirectLeft || dir2 == LinkDirection::STRAIGHT) && !isBicycleLeftTurn) {
crossingPositions.second.push_back(index);
const PositionVector otherShape = n.computeInternalLaneShape(i2, k2, numPoints, 0, shapeFlag);
otherShapes.push_back(otherShape);
const bool secondIntersection = con.indirectLeft && this == i2 && con.fromLane == k2.fromLane;
const double minDV = firstIntersection(shape, otherShape, width1, width2,
"Could not compute intersection of conflicting internal lanes at node '" + myTo->getID() + "'", secondIntersection);
if (minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS) {
assert(minDV >= 0);
if (crossingPositions.first < 0 || crossingPositions.first > minDV) {
crossingPositions.first = minDV;
}
}
}
const bool rightTurnConflict = NBNode::rightTurnConflict(
this, con.toEdge, con.fromLane, i2, k2.toEdge, k2.fromLane);
const bool indirectTurnConflit = con.indirectLeft && this == i2 && (dir2 == LinkDirection::STRAIGHT ||
(con.fromLane < k2.fromLane && (dir2 == LinkDirection::RIGHT || dir2 == LinkDirection::PARTRIGHT)));
const bool mergeConflict = myTo->mergeConflict(this, con, i2, k2, true);
const bool mergeResponse = myTo->mergeConflict(this, con, i2, k2, false);
const bool bidiConflict = myTo->bidiConflict(this, con, i2, k2, true);
if (foes || rightTurnConflict || oppositeLeftIntersect || mergeConflict || indirectTurnConflit || bidiConflict) {
foeInternalLinks.push_back(index);
}
if (oppositeLeftIntersect && getID() > i2->getID()
&& (getPermissions(con.fromLane) & warn) != 0
&& (con.toEdge->getPermissions(con.toLane) & warn) != 0
&& (i2->getPermissions(k2.fromLane) & warn) != 0
&& (k2.toEdge->getPermissions(k2.toLane) & warn) != 0
&& n.getType() != SumoXMLNodeType::NOJUNCTION
) {
WRITE_WARNINGF(TL("Intersecting left turns at junction '%' from lane '%' and lane '%' (increase junction radius to avoid this)."),
n.getID(), getLaneID(con.fromLane), i2->getLaneID(k2.fromLane));
}
const bool signalised = hasSignalisedConnectionTo(con.toEdge);
if ((n.forbids(i2, k2.toEdge, this, con.toEdge, signalised) || rightTurnConflict || indirectTurnConflit || mergeResponse)
&& (needsCont || dir == LinkDirection::TURN || (!signalised && this != i2 && !con.indirectLeft))) {
tmpFoeIncomingLanes.insert(i2->getID() + "_" + toString(k2.fromLane));
}
if (bothPrio && oppositeLeftIntersect && getID() < i2->getID()) {
tmpFoeIncomingLanes.insert(":" + toString(index));
}
index++;
}
}
if (dir == LinkDirection::TURN && crossingPositions.first < 0 && crossingPositions.second.size() != 0 && shape.length() > 2. * POSITION_EPS) {
crossingPositions.first = (double)(shape.length() + getEndOffset(con.fromLane)) / 2.;
}
std::vector<NBNode::Crossing*> crossings = n.getCrossings();
for (auto c : crossings) {
const NBNode::Crossing& crossing = *c;
for (EdgeVector::const_iterator it_e = crossing.edges.begin(); it_e != crossing.edges.end(); ++it_e) {
const NBEdge* edge = *it_e;
if ((this == edge || con.toEdge == edge) && !isRailway(conPermissions)) {
foeInternalLinks.push_back(index);
if (con.toEdge == edge &&
((isRightTurn && getJunctionPriority(&n) > 0) || (isTurn && con.tlID != ""))) {
PositionVector crossingShape = crossing.shape;
crossingShape.extrapolate(5.0);
const double minDV = firstIntersection(shape, crossingShape, 0, crossing.width / 2);
if (minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS) {
assert(minDV >= 0);
if (crossingPositions.first < 0 || crossingPositions.first > minDV) {
crossingPositions.first = minDV;
}
}
} else if (this == edge && crossing.priority && !myTo->isTLControlled()) {
crossingPositions.first = 0;
}
}
}
index++;
}
}
if (con.contPos == UNSPECIFIED_CONTPOS) {
con.contPos = defaultContPos;
}
if (con.contPos != UNSPECIFIED_CONTPOS) {
if (con.contPos <= 0 || con.contPos >= shape.length()) {
crossingPositions.first = -1;
} else {
crossingPositions.first = con.contPos;
}
}
if (con.speed == UNSPECIFIED_SPEED) {
if (higherSpeed) {
con.vmax = MAX2(myLanes[con.fromLane].speed, con.toEdge->getLanes()[con.toLane].speed);
} else {
con.vmax = (myLanes[con.fromLane].speed + con.toEdge->getLanes()[con.toLane].speed) / (double) 2.0;
}
if (limitTurnSpeed > 0) {
const double angleRaw = fabs(GeomHelper::angleDiff(
getLaneShape(con.fromLane).angleAt2D(-2),
con.toEdge->getLaneShape(con.toLane).angleAt2D(0)));
const double angle = MAX2(0.0, angleRaw - (fromRail ? limitTurnSpeedMinAngleRail : limitTurnSpeedMinAngle));
const double length = shape.length2D();
if (angle > 0 && length > 1) {
const double radius = length / angle + getLaneWidth(con.fromLane) / 4;
const double limit = sqrt(limitTurnSpeed * radius);
const double reduction = con.vmax - limit;
const bool atRoundabout = getJunctionPriority(myTo) == JunctionPriority::ROUNDABOUT || con.toEdge->getJunctionPriority(myFrom) == JunctionPriority::ROUNDABOUT;
const LinkDirection dir2 = atRoundabout ? LinkDirection::LEFT : dir;
if ((dir2 == LinkDirection::STRAIGHT && reduction > limitTurnSpeedWarnStraight)
|| (dir2 != LinkDirection::TURN && reduction > limitTurnSpeedWarnTurn)) {
std::string dirType = std::string(dir == LinkDirection::STRAIGHT ? "straight" : "turning");
if (atRoundabout) {
dirType = "roundabout";
}
WRITE_WARNINGF(TL("Speed of % connection '%' reduced by % due to turning radius of % (length=%, angle=%)."),
dirType, con.getDescription(this), reduction, radius, length, RAD2DEG(angleRaw));
}
con.vmax = MIN2(con.vmax, limit);
}
assert(con.vmax > 0);
} else if (fromRail && dir == LinkDirection::TURN) {
con.vmax = 0.01;
}
} else {
con.vmax = con.speed;
}
if (con.friction == UNSPECIFIED_FRICTION) {
con.friction = (myLanes[con.fromLane].friction + con.toEdge->getLanes()[con.toLane].friction) / 2.;
}
assert(shape.size() >= 2);
con.id = innerID + "_" + toString(edgeIndex);
const double shapeLength = shape.length();
double firstLength = shapeLength;
if (crossingPositions.first > 0 && crossingPositions.first < shapeLength) {
std::pair<PositionVector, PositionVector> split = shape.splitAt(crossingPositions.first);
con.shape = split.first;
con.foeIncomingLanes = std::vector<std::string>(tmpFoeIncomingLanes.begin(), tmpFoeIncomingLanes.end());
con.foeInternalLinks = foeInternalLinks;
if (i != myConnections.begin() && (i - 1)->toEdge == con.toEdge && (i - 1)->haveVia) {
--splitIndex;
con.internalViaLaneIndex = (i - 1)->internalViaLaneIndex + 1;
}
con.viaID = innerID + "_" + toString(splitIndex + noInternalNoSplits);
++splitIndex;
con.viaShape = split.second;
con.haveVia = true;
firstLength = con.shape.length();
} else {
con.shape = shape;
}
con.internalLaneIndex = internalLaneIndex;
++internalLaneIndex;
++linkIndex;
++numLanes;
if (con.customLength != UNSPECIFIED_LOADED_LENGTH) {
lengthSum += (shapeLength != 0 ? firstLength / shapeLength : 1) * con.customLength;
} else {
lengthSum += firstLength;
}
}
return MAX2(maxCross, assignInternalLaneLength(myConnections.end(), numLanes, lengthSum, averageLength));
}
double
NBEdge::assignInternalLaneLength(std::vector<Connection>::iterator i, int numLanes, double lengthSum, bool averageLength) {
double maxCross = 0.;
assert(i - myConnections.begin() >= numLanes);
for (int prevIndex = 1; prevIndex <= numLanes; prevIndex++) {
Connection& c = (*(i - prevIndex));
const double minLength = c.customLength != UNSPECIFIED_LOADED_LENGTH ? pow(10, -gPrecision) : POSITION_EPS;
c.length = MAX2(minLength, averageLength ? lengthSum / numLanes : c.shape.length());
if (c.haveVia) {
c.viaLength = MAX2(minLength, c.viaShape.length());
}
if (c.customLength != UNSPECIFIED_LOADED_LENGTH) {
if (c.haveVia) {
const double a = c.viaLength / (c.shape.length() + c.viaLength);
c.viaLength = MAX2(minLength, a * c.customLength);
}
if (!averageLength) {
c.length = MAX2(minLength, c.customLength - c.viaLength);
}
}
if (c.haveVia) {
maxCross = MAX2(maxCross, sqrt(2. * c.viaLength));
}
if (c.indirectLeft) {
maxCross = MAX2(maxCross, MAX2(c.length, c.viaLength) / MAX2(c.vmax, NBOwnTLDef::MIN_SPEED_CROSSING_TIME));
} else {
maxCross = MAX2(maxCross, (c.length + c.viaLength) / MAX2(c.vmax, NBOwnTLDef::MIN_SPEED_CROSSING_TIME));
}
}
return maxCross;
}
double
NBEdge::firstIntersection(const PositionVector& v1, const PositionVector& v2, double width1, double width2, const std::string& error, bool secondIntersection) {
double intersect = std::numeric_limits<double>::max();
if (v2.length() < POSITION_EPS) {
return intersect;
}
try {
PositionVector v1Right = v1;
v1Right.move2side(width1);
PositionVector v1Left = v1;
v1Left.move2side(-width1);
PositionVector v2Right = v2;
v2Right.move2side(width2);
PositionVector v2Left = v2;
v2Left.move2side(-width2);
bool skip = secondIntersection;
for (double cand : v1Left.intersectsAtLengths2D(v2Right)) {
if (skip) {
skip = false;
continue;
}
intersect = MIN2(intersect, cand);
}
skip = secondIntersection;
for (double cand : v1Left.intersectsAtLengths2D(v2Left)) {
if (skip) {
skip = false;
continue;
}
intersect = MIN2(intersect, cand);
}
skip = secondIntersection;
for (double cand : v1Right.intersectsAtLengths2D(v2Right)) {
if (skip) {
skip = false;
continue;
}
intersect = MIN2(intersect, cand);
}
skip = secondIntersection;
for (double cand : v1Right.intersectsAtLengths2D(v2Left)) {
if (skip) {
skip = false;
continue;
}
intersect = MIN2(intersect, cand);
}
} catch (InvalidArgument&) {
if (error != "") {
WRITE_WARNING(error);
}
}
return intersect;
}
bool
NBEdge::bothLeftTurns(LinkDirection dir, const NBEdge* otherFrom, LinkDirection dir2) const {
if (otherFrom == this) {
return false;
}
return (dir == LinkDirection::LEFT || dir == LinkDirection::PARTLEFT) && (dir2 == LinkDirection::LEFT || dir2 == LinkDirection::PARTLEFT);
}
bool
NBEdge::haveIntersection(const NBNode& n, const PositionVector& shape, const NBEdge* otherFrom, const NBEdge::Connection& otherCon, int numPoints,
double width1, double width2, int shapeFlag) const {
const PositionVector otherShape = n.computeInternalLaneShape(otherFrom, otherCon, numPoints, 0, shapeFlag);
const double minDV = firstIntersection(shape, otherShape, width1, width2);
return minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS;
}
int
NBEdge::getJunctionPriority(const NBNode* const node) const {
if (node == myFrom) {
return myFromJunctionPriority;
} else {
return myToJunctionPriority;
}
}
void
NBEdge::setJunctionPriority(const NBNode* const node, int prio) {
if (node == myFrom) {
myFromJunctionPriority = prio;
#ifdef DEBUG_JUNCTIONPRIO
setParameter("fromPrio", toString(prio));
#endif
} else {
myToJunctionPriority = prio;
#ifdef DEBUG_JUNCTIONPRIO
setParameter("toPrio", toString(prio));
#endif
}
}
double
NBEdge::getAngleAtNode(const NBNode* const atNode) const {
if (atNode == myFrom) {
return GeomHelper::legacyDegree(myGeom.angleAt2D(0));
}
assert(atNode == myTo);
return GeomHelper::legacyDegree(myGeom.angleAt2D(-2));
}
double
NBEdge::getAngleAtNodeNormalized(const NBNode* const atNode) const {
double res;
if (atNode == myFrom) {
res = GeomHelper::legacyDegree(myGeom.angleAt2D(0)) - 180;
} else {
assert(atNode == myTo);
res = GeomHelper::legacyDegree(myGeom.angleAt2D(-2));
}
if (res < 0) {
res += 360;
}
return res;
}
double
NBEdge::getAngleAtNodeToCenter(const NBNode* const atNode) const {
if (atNode == myFrom) {
double res = myStartAngle - 180;
if (res < 0) {
res += 360;
}
return res;
} else {
assert(atNode == myTo);
return myEndAngle;
}
}
void
NBEdge::setTurningDestination(NBEdge* e, bool onlyPossible) {
if (!onlyPossible) {
myTurnDestination = e;
}
myPossibleTurnDestination = e;
}
double
NBEdge::getLaneSpeed(int lane) const {
return myLanes[lane].speed;
}
double
NBEdge::getLaneFriction(int lane) const {
return myLanes[lane].friction;
}
void
NBEdge::resetLaneShapes() {
computeLaneShapes();
}
void
NBEdge::updateChangeRestrictions(SVCPermissions ignoring) {
for (Lane& lane : myLanes) {
if (lane.changeLeft != SVCAll) {
lane.changeLeft = ignoring;
}
if (lane.changeRight != SVCAll) {
lane.changeRight = ignoring;
}
}
for (Connection& con : myConnections) {
if (con.changeLeft != SVC_UNSPECIFIED && con.changeLeft != SVCAll) {
con.changeLeft = ignoring;
}
if (con.changeRight != SVC_UNSPECIFIED && con.changeRight != SVCAll) {
con.changeRight = ignoring;
}
}
}
void
NBEdge::computeLaneShapes() {
if (myFrom == myTo) {
return;
}
std::vector<double> offsets(myLanes.size(), 0.);
double offset = 0;
for (int i = (int)myLanes.size() - 2; i >= 0; --i) {
offset += (getLaneWidth(i) + getLaneWidth(i + 1)) / 2.;
offsets[i] = offset;
}
if (myLaneSpreadFunction == LaneSpreadFunction::CENTER) {
double width = 0;
for (int i = 0; i < (int)myLanes.size(); ++i) {
width += getLaneWidth(i);
}
offset = -width / 2. + getLaneWidth((int)myLanes.size() - 1) / 2.;
} else {
double laneWidth = myLanes.back().width != UNSPECIFIED_WIDTH ? myLanes.back().width : SUMO_const_laneWidth;
offset = laneWidth / 2.;
}
if (myLaneSpreadFunction == LaneSpreadFunction::ROADCENTER) {
for (NBEdge* e : myTo->getOutgoingEdges()) {
if (e->getToNode() == myFrom && getInnerGeometry().reverse() == e->getInnerGeometry()) {
offset += (e->getTotalWidth() - getTotalWidth()) / 2;
break;
}
}
}
for (int i = 0; i < (int)myLanes.size(); ++i) {
offsets[i] += offset;
}
for (int i = 0; i < (int)myLanes.size(); ++i) {
if (myLanes[i].customShape.size() != 0) {
myLanes[i].shape = myLanes[i].customShape;
continue;
}
try {
myLanes[i].shape = computeLaneShape(i, offsets[i]);
} catch (InvalidArgument& e) {
WRITE_WARNINGF(TL("In lane '%': lane shape could not be determined (%)."), getLaneID(i), e.what());
myLanes[i].shape = myGeom;
}
}
}
PositionVector
NBEdge::computeLaneShape(int lane, double offset) const {
PositionVector shape = myGeom;
try {
shape.move2side(offset);
} catch (InvalidArgument& e) {
WRITE_WARNINGF(TL("In lane '%': Could not build shape (%)."), getLaneID(lane), e.what());
}
return shape;
}
void
NBEdge::computeAngle() {
const bool hasFromShape = myFrom->getShape().size() > 0;
const bool hasToShape = myTo->getShape().size() > 0;
Position fromCenter = (hasFromShape ? myFrom->getShape().getCentroid() : myFrom->getPosition());
Position toCenter = (hasToShape ? myTo->getShape().getCentroid() : myTo->getPosition());
PositionVector shape = myGeom;
if ((hasFromShape || hasToShape) && getNumLanes() > 0) {
if (myLaneSpreadFunction == LaneSpreadFunction::RIGHT) {
shape = myLanes[getNumLanes() - 1].shape ;
} else {
shape = myLanes[getNumLanes() / 2].shape;
if (getNumLanes() % 2 == 0) {
shape.move2side(getLaneWidth(getNumLanes() / 2) * 0.5);
}
}
}
const bool suspiciousFromShape = hasFromShape && (myFrom->getShape().distance2D(shape[0]) > 2 * POSITION_EPS
|| myFrom->getShape().around(shape[-1])
|| !(myFrom->getShape().around(fromCenter)));
const bool suspiciousToShape = hasToShape && (myTo->getShape().distance2D(shape[-1]) > 2 * POSITION_EPS
|| myTo->getShape().around(shape[0])
|| !(myTo->getShape().around(toCenter)));
const double angleLookahead = MIN2(shape.length2D() / 2, ANGLE_LOOKAHEAD);
const Position referencePosStart = shape.positionAtOffset2D(angleLookahead);
const Position referencePosEnd = shape.positionAtOffset2D(shape.length2D() - angleLookahead);
myStartAngle = GeomHelper::legacyDegree(fromCenter.angleTo2D(referencePosStart), true);
const double myStartAngle2 = GeomHelper::legacyDegree(myFrom->getPosition().angleTo2D(referencePosStart), true);
const double myStartAngle3 = getAngleAtNode(myFrom);
myEndAngle = GeomHelper::legacyDegree(referencePosEnd.angleTo2D(toCenter), true);
const double myEndAngle2 = GeomHelper::legacyDegree(referencePosEnd.angleTo2D(myTo->getPosition()), true);
const double myEndAngle3 = getAngleAtNode(myTo);
#ifdef DEBUG_ANGLES
if (DEBUGCOND) {
if (suspiciousFromShape) {
std::cout << "suspiciousFromShape len=" << shape.length() << " startA=" << myStartAngle << " startA2=" << myStartAngle2 << " startA3=" << myStartAngle3
<< " rel=" << NBHelpers::normRelAngle(myStartAngle, myStartAngle2)
<< " fromCenter=" << fromCenter
<< " fromPos=" << myFrom->getPosition()
<< " refStart=" << referencePosStart
<< "\n";
}
if (suspiciousToShape) {
std::cout << "suspiciousToShape len=" << shape.length() << " endA=" << myEndAngle << " endA2=" << myEndAngle2 << " endA3=" << myEndAngle3
<< " rel=" << NBHelpers::normRelAngle(myEndAngle, myEndAngle2)
<< " toCenter=" << toCenter
<< " toPos=" << myTo->getPosition()
<< " refEnd=" << referencePosEnd
<< "\n";
}
}
#endif
if (suspiciousFromShape && shape.length() > 1) {
myStartAngle = myStartAngle2;
} else if (suspiciousToShape && fabs(NBHelpers::normRelAngle(myStartAngle, myStartAngle3)) > 90
&& (getPermissions() & ~SVC_PEDESTRIAN) != 0) {
myStartAngle = myStartAngle3;
if (myStartAngle < 0) {
myStartAngle += 360;
}
}
if (suspiciousToShape && shape.length() > 1) {
myEndAngle = myEndAngle2;
} else if (suspiciousToShape && fabs(NBHelpers::normRelAngle(myEndAngle, myEndAngle3)) > 90
&& (getPermissions() & ~SVC_PEDESTRIAN) != 0) {
myEndAngle = myEndAngle3;
if (myEndAngle < 0) {
myEndAngle += 360;
}
}
myTotalAngle = GeomHelper::legacyDegree(myFrom->getPosition().angleTo2D(myTo->getPosition()), true);
#ifdef DEBUG_ANGLES
if (DEBUGCOND) std::cout << "computeAngle edge=" << getID()
<< " fromCenter=" << fromCenter << " toCenter=" << toCenter
<< " refStart=" << referencePosStart << " refEnd=" << referencePosEnd << " shape=" << shape
<< " hasFromShape=" << hasFromShape
<< " hasToShape=" << hasToShape
<< " numLanes=" << getNumLanes()
<< " shapeLane=" << getNumLanes() / 2
<< " startA=" << myStartAngle << " endA=" << myEndAngle << " totA=" << myTotalAngle << "\n";
#endif
}
double
NBEdge::getShapeStartAngle() const {
const double angleLookahead = MIN2(myGeom.length2D() / 2, ANGLE_LOOKAHEAD);
const Position referencePosStart = myGeom.positionAtOffset2D(angleLookahead);
return GeomHelper::legacyDegree(myGeom.front().angleTo2D(referencePosStart), true);
}
double
NBEdge::getShapeEndAngle() const {
const double angleLookahead = MIN2(myGeom.length2D() / 2, ANGLE_LOOKAHEAD);
const Position referencePosEnd = myGeom.positionAtOffset2D(myGeom.length2D() - angleLookahead);
return GeomHelper::legacyDegree(referencePosEnd.angleTo2D(myGeom.back()), true);
}
bool
NBEdge::hasPermissions() const {
for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
if ((*i).permissions != SVCAll) {
return true;
}
}
return false;
}
bool
NBEdge::hasLaneSpecificPermissions() const {
std::vector<Lane>::const_iterator i = myLanes.begin();
SVCPermissions firstLanePermissions = i->permissions;
i++;
for (; i != myLanes.end(); ++i) {
if (i->permissions != firstLanePermissions) {
return true;
}
}
return false;
}
bool
NBEdge::hasLaneSpecificSpeed() const {
for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
if (i->speed != getSpeed()) {
return true;
}
}
return false;
}
bool
NBEdge::hasLaneSpecificFriction() const {
for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
if (i->friction != myLanes.begin()->friction) {
return true;
}
}
return false;
}
bool
NBEdge::hasLaneSpecificWidth() const {
for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
if (i->width != myLanes.begin()->width) {
return true;
}
}
return false;
}
bool
NBEdge::hasLaneSpecificType() const {
for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
if (i->type != myLanes.begin()->type) {
return true;
}
}
return false;
}
bool
NBEdge::hasLaneSpecificEndOffset() const {
for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
if (i->endOffset != myLanes.begin()->endOffset) {
return true;
}
}
return false;
}
bool
NBEdge::hasLaneSpecificStopOffsets() const {
for (const auto& lane : myLanes) {
if (lane.laneStopOffset.isDefined()) {
if (myEdgeStopOffset.isDefined() || (myEdgeStopOffset != lane.laneStopOffset)) {
return true;
}
}
}
return false;
}
bool
NBEdge::hasAccelLane() const {
for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
if (i->accelRamp) {
return true;
}
}
return false;
}
bool
NBEdge::hasCustomLaneShape() const {
for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
if (i->customShape.size() > 0) {
return true;
}
}
return false;
}
bool
NBEdge::hasLaneParams() const {
for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
if (i->getParametersMap().size() > 0) {
return true;
}
}
return false;
}
bool
NBEdge::prohibitsChanging() const {
for (const Lane& lane : myLanes) {
if (lane.changeLeft != SVCAll || lane.changeRight != SVCAll) {
return true;
}
}
return false;
}
bool
NBEdge::needsLaneSpecificOutput() const {
return (hasLaneSpecificPermissions()
|| hasLaneSpecificSpeed()
|| hasLaneSpecificWidth()
|| hasLaneSpecificType()
|| hasLaneSpecificEndOffset()
|| hasLaneSpecificStopOffsets()
|| hasAccelLane()
|| hasCustomLaneShape()
|| hasLaneParams()
|| prohibitsChanging()
|| (!myLanes.empty() && myLanes.back().oppositeID != ""));
}
bool
NBEdge::computeEdge2Edges(bool noLeftMovers) {
#ifdef DEBUG_CONNECTION_GUESSING
if (DEBUGCOND) {
std::cout << "computeEdge2Edges edge=" << getID() << " step=" << (int)myStep << " noLeftMovers=" << noLeftMovers << "\n";
for (Connection& c : myConnections) {
std::cout << " conn " << c.getDescription(this) << "\n";
}
for (Connection& c : myConnectionsToDelete) {
std::cout << " connToDelete " << c.getDescription(this) << "\n";
}
}
#endif
if (myStep >= EdgeBuildingStep::EDGE2EDGES) {
return true;
}
const bool fromRail = isRailway(getPermissions());
for (NBEdge* out : myTo->getOutgoingEdges()) {
if (noLeftMovers && myTo->isLeftMover(this, out)) {
continue;
}
if (fromRail && isRailway(out->getPermissions())) {
const double angle = fabs(NBHelpers::normRelAngle(getAngleAtNode(myTo), out->getAngleAtNode(myTo)));
if (angle > 150) {
continue;
} else if (angle > 90) {
const PositionVector& fromShape = myLanes.front().shape;
const PositionVector& toShape = out->getLanes().front().shape;
PositionVector shape = myTo->computeSmoothShape(fromShape, toShape, 5, getTurnDestination() == out, 5, 5);
const double radius = shape.length2D() / DEG2RAD(angle);
const double minRadius = (getPermissions() & SVC_TRAM) != 0 ? 20 : 80;
if (radius < minRadius) {
continue;
}
}
}
if (out == myTurnDestination) {
continue;
}
if ((getPermissions() & out->getPermissions() & ~SVC_PEDESTRIAN) == 0) {
continue;
}
myConnections.push_back(Connection(-1, out, -1));
}
myStep = EdgeBuildingStep::EDGE2EDGES;
return true;
}
bool
NBEdge::computeLanes2Edges() {
#ifdef DEBUG_CONNECTION_GUESSING
if (DEBUGCOND) {
std::cout << "computeLanes2Edges edge=" << getID() << " step=" << (int)myStep << "\n";
for (Connection& c : myConnections) {
std::cout << " conn " << c.getDescription(this) << "\n";
}
for (Connection& c : myConnectionsToDelete) {
std::cout << " connToDelete " << c.getDescription(this) << "\n";
}
}
#endif
if (myStep >= EdgeBuildingStep::LANES2EDGES) {
return true;
}
assert(myStep == EdgeBuildingStep::EDGE2EDGES);
const EdgeVector* edges = getConnectedSorted();
if (myConnections.size() != 0 && edges->size() == 0) {
myConnections.clear();
} else {
divideOnEdges(edges);
}
delete edges;
myStep = EdgeBuildingStep::LANES2EDGES;
return true;
}
std::vector<LinkDirection>
NBEdge::decodeTurnSigns(int turnSigns, int shift) {
std::vector<LinkDirection> result;
for (int i = 0; i < 8; i++) {
if ((turnSigns & (1 << (i + shift))) != 0) {
result.push_back((LinkDirection)(1 << i));
}
}
return result;
}
void
NBEdge::updateTurnPermissions(SVCPermissions& perm, LinkDirection dir, SVCPermissions spec, std::vector<LinkDirection> dirs) {
if (dirs.size() > 0) {
if (std::find(dirs.begin(), dirs.end(), dir) == dirs.end()) {
perm &= ~spec;
} else {
perm |= spec;
}
}
}
bool
NBEdge::applyTurnSigns() {
#ifdef DEBUG_TURNSIGNS
std::cout << "applyTurnSigns edge=" << getID() << "\n";
#endif
std::vector<const NBEdge*> targets;
std::map<const NBEdge*, std::vector<int> > toLaneMap;
for (const Connection& c : myConnections) {
if (myLanes[c.fromLane].turnSigns != 0) {
if (std::find(targets.begin(), targets.end(), c.toEdge) == targets.end()) {
targets.push_back(c.toEdge);
}
toLaneMap[c.toEdge].push_back(c.toLane);
}
}
for (auto& item : toLaneMap) {
std::sort(item.second.begin(), item.second.end());
}
std::map<LinkDirection, int> signCons;
int allDirs = 0;
for (const Lane& lane : myLanes) {
allDirs |= lane.turnSigns;
for (LinkDirection dir : decodeTurnSigns(lane.turnSigns)) {
signCons[dir]++;
}
}
allDirs |= allDirs >> TURN_SIGN_SHIFT_BUS;
allDirs |= allDirs >> TURN_SIGN_SHIFT_TAXI;
allDirs |= allDirs >> TURN_SIGN_SHIFT_BICYCLE;
if ((allDirs & (int)LinkDirection::NODIR) != 0) {
targets.push_back(nullptr);
}
SVCPermissions defaultPermissions = SVC_PASSENGER | SVC_DELIVERY;
std::vector<LinkDirection> signedDirs = decodeTurnSigns(allDirs);
std::map<LinkDirection, const NBEdge*> dirMap;
#ifdef DEBUG_TURNSIGNS
std::cout << " numDirs=" << signedDirs.size() << " numTargets=" << targets.size() << "\n";
#endif
if (signedDirs.size() > targets.size()) {
WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because there are % signed directions but only % targets"), getID(), signedDirs.size(), targets.size());
return false;
} else if (signedDirs.size() < targets.size()) {
std::vector<LinkDirection> sumoDirs;
for (const NBEdge* to : targets) {
sumoDirs.push_back(myTo->getDirection(this, to));
}
bool checkMore = true;
while (signedDirs.size() < targets.size() && checkMore) {
checkMore = false;
if (sumoDirs.back() != signedDirs.back()) {
targets.pop_back();
sumoDirs.pop_back();
checkMore = true;
}
}
checkMore = true;
while (signedDirs.size() < targets.size() && checkMore) {
checkMore = false;
if (sumoDirs.front() != signedDirs.front()) {
targets.erase(targets.begin());
sumoDirs.erase(sumoDirs.begin());
checkMore = true;
}
}
int i = 0;
while (signedDirs.size() < targets.size() && i < (int)targets.size()) {
if (targets[i] != nullptr && (targets[i]->getPermissions() & defaultPermissions) == 0) {
targets.erase(targets.begin() + i);
sumoDirs.erase(sumoDirs.begin() + i);
} else {
i++;
}
}
if (signedDirs.size() != targets.size()) {
WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because there are % signed directions and % targets (after target pruning)"), getID(), signedDirs.size(), targets.size());
return false;
}
}
for (int i = 0; i < (int)signedDirs.size(); i++) {
dirMap[signedDirs[i]] = targets[i];
}
for (auto item : signCons) {
const LinkDirection dir = item.first;
if (dir == LinkDirection::NODIR) {
continue;
}
const NBEdge* to = dirMap[dir];
int candidates = to->getNumLanesThatAllow(defaultPermissions, false);
if (candidates == 0) {
WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because the target edge '%' has no suitable lanes"), getID(), to->getID());
return false;
}
std::vector<int>& knownTargets = toLaneMap[to];
if ((int)knownTargets.size() < item.second) {
if (candidates < item.second) {
WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because there are % signed connections with directions '%' but target edge '%' has only % suitable lanes"),
getID(), item.second, toString(dir), to->getID(), candidates);
return false;
}
int i;
int iInc;
int iEnd;
if (dir > LinkDirection::STRAIGHT) {
i = to->getNumLanes() - 1;
iInc = -1;
iEnd = -1;
} else {
i = 0;
iInc = 1;
iEnd = to->getNumLanes();
}
while ((int)knownTargets.size() < item.second && i != iEnd) {
if ((to->getPermissions(i) & defaultPermissions) != 0) {
if (std::find(knownTargets.begin(), knownTargets.end(), i) == knownTargets.end()) {
knownTargets.push_back(i);
}
}
i += iInc;
}
if ((int)knownTargets.size() != item.second) {
WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because not enough target lanes could be determined for direction '%'"), getID(), toString(dir));
return false;
}
std::sort(knownTargets.begin(), knownTargets.end());
}
}
std::map<const NBEdge*, int> toLaneIndex;
for (int i = 0; i < getNumLanes(); i++) {
const int turnSigns = myLanes[i].turnSigns;
if (turnSigns != 0) {
for (auto it = myConnections.begin(); it != myConnections.end();) {
if (it->fromLane == i) {
it = myConnections.erase(it);
} else {
it++;
}
}
int allSigns = (turnSigns
| turnSigns >> TURN_SIGN_SHIFT_BUS
| turnSigns >> TURN_SIGN_SHIFT_TAXI
| turnSigns >> TURN_SIGN_SHIFT_BICYCLE);
std::vector<LinkDirection> all = decodeTurnSigns(turnSigns);
std::vector<LinkDirection> bus = decodeTurnSigns(turnSigns, TURN_SIGN_SHIFT_BUS);
std::vector<LinkDirection> taxi = decodeTurnSigns(turnSigns, TURN_SIGN_SHIFT_TAXI);
std::vector<LinkDirection> bike = decodeTurnSigns(turnSigns, TURN_SIGN_SHIFT_BICYCLE);
SVCPermissions fromP = getPermissions(i);
if ((fromP & SVC_PASSENGER) != 0) {
fromP = SVC_PASSENGER;
}
for (LinkDirection dir : decodeTurnSigns(allSigns)) {
SVCPermissions perm = 0;
updateTurnPermissions(perm, dir, SVCAll, all);
updateTurnPermissions(perm, dir, SVC_BUS, bus);
updateTurnPermissions(perm, dir, SVC_TAXI, taxi);
updateTurnPermissions(perm, dir, SVC_BICYCLE, bike);
if (perm == SVCAll) {
perm = SVC_UNSPECIFIED;
}
NBEdge* to = const_cast<NBEdge*>(dirMap[dir]);
if (to != nullptr) {
if (toLaneIndex.count(to) == 0) {
int toLane = toLaneMap[to][0];
while ((to->getPermissions(toLane) & fromP) == 0 && (toLane + 1 < to->getNumLanes())) {
toLane++;
}
#ifdef DEBUG_TURNSIGNS
std::cout << " target=" << to->getID() << " initial toLane=" << toLane << "\n";
#endif
toLaneIndex[to] = toLane;
}
#ifdef DEBUG_TURNSIGNS
#endif
setConnection(i, to, toLaneIndex[to], Lane2LaneInfoType::VALIDATED, true,
false, KEEPCLEAR_UNSPECIFIED, UNSPECIFIED_CONTPOS,
UNSPECIFIED_VISIBILITY_DISTANCE, UNSPECIFIED_SPEED, UNSPECIFIED_FRICTION,
myDefaultConnectionLength, PositionVector::EMPTY,
UNSPECIFIED_CONNECTION_UNCONTROLLED,
perm);
if (toLaneIndex[to] < to->getNumLanes() - 1
&& (to->getPermissions(toLaneIndex[to] + 1) & fromP) != 0) {
toLaneIndex[to]++;
} else if (toLaneIndex[to] < to->getNumLanes() - 2
&& (to->getPermissions(toLaneIndex[to] + 2) & fromP) != 0) {
toLaneIndex[to] += 2;
}
}
}
}
}
sortOutgoingConnectionsByAngle();
sortOutgoingConnectionsByIndex();
return true;
}
bool
NBEdge::recheckLanes() {
#ifdef DEBUG_CONNECTION_GUESSING
if (DEBUGCOND) {
std::cout << "recheckLanes (initial) edge=" << getID() << "\n";
for (Connection& c : myConnections) {
std::cout << " conn " << c.getDescription(this) << "\n";
}
for (Connection& c : myConnectionsToDelete) {
std::cout << " connToDelete " << c.getDescription(this) << "\n";
}
}
#endif
for (std::vector<Connection>::iterator it = myConnectionsToDelete.begin(); it != myConnectionsToDelete.end(); ++it) {
removeFromConnections(it->toEdge, it->fromLane, it->toLane, false, false, true);
}
std::vector<int> connNumbersPerLane(myLanes.size(), 0);
for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
if ((*i).toEdge == nullptr || (*i).fromLane < 0 || (*i).toLane < 0) {
i = myConnections.erase(i);
} else {
if ((*i).fromLane >= 0) {
++connNumbersPerLane[(*i).fromLane];
}
++i;
}
}
if (myStep != EdgeBuildingStep::LANES2LANES_DONE && myStep != EdgeBuildingStep::LANES2LANES_USER) {
#ifdef DEBUG_TURNSIGNS
if (myLanes.back().turnSigns != 0) {
std::cout << getID() << " hasTurnSigns\n";
if (myTurnSignTarget != myTo->getID()) {
std::cout << " tst=" << myTurnSignTarget << " to=" << myTo->getID() << "\n";
}
}
#endif
if (myLanes.back().turnSigns == 0 || myTurnSignTarget != myTo->getID() || !applyTurnSigns()) {
for (int i = 0; i < (int)myLanes.size(); i++) {
if (connNumbersPerLane[i] == 0 && !isForbidden(getPermissions(i) & ~SVC_PEDESTRIAN)) {
bool hasDeadEnd = true;
for (int i2 = i - 1; hasDeadEnd && i2 >= 0; i2--) {
if (getPermissions(i) != getPermissions(i2)) {
break;
}
if (connNumbersPerLane[i2] > 1) {
connNumbersPerLane[i2]--;
for (int i3 = i2; i3 != i; i3++) {
moveConnectionToLeft(i3);
sortOutgoingConnectionsByAngle();
sortOutgoingConnectionsByIndex();
}
hasDeadEnd = false;
}
}
if (hasDeadEnd) {
for (int i2 = i + 1; hasDeadEnd && i2 < getNumLanes(); i2++) {
if (getPermissions(i) != getPermissions(i2)) {
break;
}
if (connNumbersPerLane[i2] > 1) {
connNumbersPerLane[i2]--;
for (int i3 = i2; i3 != i; i3--) {
moveConnectionToRight(i3);
sortOutgoingConnectionsByAngle();
sortOutgoingConnectionsByIndex();
}
hasDeadEnd = false;
}
}
}
if (hasDeadEnd && myTo->getOutgoingEdges().size() > 1) {
int passengerLanes = 0;
int passengerTargetLanes = 0;
for (const Lane& lane : myLanes) {
if ((lane.permissions & SVC_PASSENGER) != 0) {
passengerLanes++;
}
}
for (const NBEdge* out : myTo->getOutgoingEdges()) {
if (!isTurningDirectionAt(out)) {
for (const Lane& lane : out->getLanes()) {
if ((lane.permissions & SVC_PASSENGER) != 0) {
passengerTargetLanes++;
}
}
}
}
if (passengerLanes > 0 && passengerLanes <= passengerTargetLanes) {
if (i > 0) {
std::vector<Connection> rightCons = getConnectionsFromLane(i - 1);
if (rightCons.size() > 0) {
const Connection& rc = rightCons.back();
NBEdge* to = rc.toEdge;
int toLane = rc.toLane + 1;
if (toLane < to->getNumLanes()
&& (getPermissions(i) & ~SVC_PEDESTRIAN & to->getPermissions(toLane)) != 0
&& !hasConnectionTo(to, toLane)) {
#ifdef DEBUG_CONNECTION_CHECKING
std::cout << " recheck1 setConnection " << getID() << "_" << i << "->" << to->getID() << "_" << toLane << "\n";
#endif
setConnection(i, to, toLane, Lane2LaneInfoType::COMPUTED);
hasDeadEnd = false;
sortOutgoingConnectionsByAngle();
sortOutgoingConnectionsByIndex();
}
if (hasDeadEnd) {
toLane = rc.toLane - 1;
if (toLane >= 0
&& (getPermissions(i) & ~SVC_PEDESTRIAN & to->getPermissions(rc.toLane)) != 0
&& (getPermissions(rc.fromLane) & ~SVC_PEDESTRIAN & to->getPermissions(toLane)) != 0
&& !hasConnectionTo(to, toLane)) {
getConnectionRef(rc.fromLane, to, rc.toLane).toLane = toLane;
#ifdef DEBUG_CONNECTION_CHECKING
std::cout << " recheck2 setConnection " << getID() << "_" << i << "->" << to->getID() << "_" << (toLane + 1) << "\n";
#endif
setConnection(i, to, toLane + 1, Lane2LaneInfoType::COMPUTED);
hasDeadEnd = false;
sortOutgoingConnectionsByAngle();
sortOutgoingConnectionsByIndex();
}
}
}
}
if (hasDeadEnd && i < getNumLanes() - 1) {
std::vector<Connection> leftCons = getConnectionsFromLane(i + 1);
if (leftCons.size() > 0) {
NBEdge* to = leftCons.front().toEdge;
int toLane = leftCons.front().toLane - 1;
if (toLane >= 0
&& (getPermissions(i) & ~SVC_PEDESTRIAN & to->getPermissions(toLane)) != 0
&& !hasConnectionTo(to, toLane)) {
#ifdef DEBUG_CONNECTION_CHECKING
std::cout << " recheck3 setConnection " << getID() << "_" << i << "->" << to->getID() << "_" << toLane << "\n";
#endif
setConnection(i, to, toLane, Lane2LaneInfoType::COMPUTED);
hasDeadEnd = false;
sortOutgoingConnectionsByAngle();
sortOutgoingConnectionsByIndex();
}
}
}
#ifdef ADDITIONAL_WARNINGS
if (hasDeadEnd) {
WRITE_WARNING("Found dead-end lane " + getLaneID(i));
}
#endif
}
}
}
}
removeInvalidConnections();
}
}
if (getPermissions() != SVC_PEDESTRIAN) {
if (myConnections.empty() && myTo->getOutgoingEdges().size() > 1 && (getPermissions() & ~SVC_PEDESTRIAN) != 0) {
WRITE_WARNINGF(TL("Edge '%' is not connected to outgoing edges at junction '%'."), getID(), myTo->getID());
}
const EdgeVector& incoming = myFrom->getIncomingEdges();
if (incoming.size() > 1) {
for (int i = 0; i < (int)myLanes.size(); i++) {
if (getPermissions(i) != 0 && getPermissions(i) != SVC_PEDESTRIAN) {
bool connected = false;
for (std::vector<NBEdge*>::const_iterator in = incoming.begin(); in != incoming.end(); ++in) {
if ((*in)->hasConnectionTo(this, i)) {
connected = true;
break;
}
}
if (!connected) {
WRITE_WARNINGF(TL("Lane '%' is not connected from any incoming edge at junction '%'."), getLaneID(i), myFrom->getID());
}
}
}
}
}
if (getNumLanes() > 1 && myConnections.size() > 0) {
for (int i = 0; i < (int)myLanes.size(); i++) {
Lane& lane = myLanes[i];
if ((connNumbersPerLane[i] == 0 || ((lane.accelRamp || (i > 0 && myLanes[i - 1].accelRamp && connNumbersPerLane[i - 1] > 0))
&& getSuccessors(SVC_PASSENGER).size() > 1))
&& getPermissions(i) != SVC_PEDESTRIAN && !isForbidden(getPermissions(i))) {
const bool forbiddenLeft = lane.changeLeft != SVCAll && lane.changeLeft != SVC_IGNORING && lane.changeLeft != SVC_UNSPECIFIED;
const bool forbiddenRight = lane.changeRight != SVCAll && lane.changeRight != SVC_IGNORING && lane.changeRight != SVC_UNSPECIFIED;
if (forbiddenLeft && (i == 0 || forbiddenRight)) {
lane.changeLeft = SVC_UNSPECIFIED;
WRITE_WARNINGF(TL("Ignoring changeLeft prohibition for '%' to avoid dead-end"), getLaneID(i));
} else if (forbiddenRight && (i == getNumLanes() - 1 || (i > 0 && myLanes[i - 1].accelRamp))) {
lane.changeRight = SVC_UNSPECIFIED;
WRITE_WARNINGF(TL("Ignoring changeRight prohibition for '%' to avoid dead-end"), getLaneID(i));
}
}
}
}
#ifdef ADDITIONAL_WARNINGS
for (const Connection& c : myConnections) {
SVCPermissions fromP = getPermissions(c.fromLane);
SVCPermissions toP = c.toEdge->getPermissions(c.toLane);
if ((fromP & SVC_PASSENGER) != 0
&& toP == SVC_BICYCLE) {
bool hasAlternative = false;
for (const Connection& c2 : myConnections) {
if (c.fromLane == c2.fromLane && c.toEdge == c2.toEdge
&& (c.toEdge->getPermissions(c2.toLane) & SVC_PASSENGER) != 0) {
hasAlternative = true;
}
}
if (!hasAlternative) {
WRITE_WARNING("Road lane ends on bikeLane for connection " + c.getDescription(this));
}
}
}
#endif
#ifdef DEBUG_CONNECTION_GUESSING
if (DEBUGCOND) {
std::cout << "recheckLanes (final) edge=" << getID() << "\n";
for (Connection& c : myConnections) {
std::cout << " conn " << c.getDescription(this) << "\n";
}
}
#endif
if (myStep != EdgeBuildingStep::LANES2LANES_USER) {
myStep = EdgeBuildingStep::LANES2LANES_DONE;
}
return true;
}
void NBEdge::recheckOpposite(const NBEdgeCont& ec, bool fixOppositeLengths) {
if (getNumLanes() == 0) {
return;
}
const int leftmostLane = getNumLanes() - 1;
for (int i = 0; i < leftmostLane; i++) {
const std::string& oppositeID = getLanes()[i].oppositeID;
NBEdge* oppEdge = ec.retrieve(oppositeID.substr(0, oppositeID.rfind("_")));
if (oppositeID != "" && oppositeID != "-") {
if (getLanes().back().oppositeID == "" && oppEdge != nullptr) {
getLaneStruct(leftmostLane).oppositeID = oppositeID;
WRITE_WARNINGF(TL("Moving opposite lane '%' from invalid lane '%' to lane index %."), oppositeID, getLaneID(i), leftmostLane);
} else {
WRITE_WARNINGF(TL("Removing opposite lane '%' for invalid lane '%'."), oppositeID, getLaneID(i));
}
getLaneStruct(i).oppositeID = "";
}
}
const std::string& oppositeID = getLanes().back().oppositeID;
if (oppositeID != "" && oppositeID != "-") {
NBEdge* oppEdge = ec.retrieve(oppositeID.substr(0, oppositeID.rfind("_")));
if (oppEdge == nullptr) {
WRITE_WARNINGF(TL("Removing unknown opposite lane '%' for edge '%'."), oppositeID, getID());
getLaneStruct(leftmostLane).oppositeID = "";
} else {
if (oppEdge->getFromNode() != getToNode() || oppEdge->getToNode() != getFromNode()) {
WRITE_ERRORF(TL("Opposite lane '%' does not reverse-connect the same nodes as edge '%'!"), oppositeID, getID());
getLaneStruct(getNumLanes() - 1).oppositeID = "";
} else {
if (oppEdge->getLaneID(oppEdge->getNumLanes() - 1) != oppositeID) {
const std::string oppEdgeLeftmost = oppEdge->getLaneID(oppEdge->getNumLanes() - 1);
WRITE_WARNINGF(TL("Adapting invalid opposite lane '%' for edge '%' to '%'."), oppositeID, getID(), oppEdgeLeftmost);
getLaneStruct(leftmostLane).oppositeID = oppEdgeLeftmost;
}
NBEdge::Lane& oppLane = oppEdge->getLaneStruct(oppEdge->getNumLanes() - 1);
const std::string leftmostID = getLaneID(leftmostLane);
if (oppLane.oppositeID == "") {
WRITE_WARNINGF(TL("Adapting missing opposite lane '%' for edge '%'."), leftmostID, oppEdge->getID());
oppLane.oppositeID = leftmostID;
} else if (oppLane.oppositeID != leftmostID && oppLane.oppositeID != "-") {
const std::string oppOpp = oppLane.oppositeID.substr(0, oppLane.oppositeID.rfind("_"));
NBEdge* oppOppEdge = ec.retrieve(oppOpp);
if (oppOppEdge == nullptr) {
WRITE_WARNINGF(TL("Adapting invalid opposite lane '%' for edge '%' to '%'."), oppLane.oppositeID, oppEdge->getID(), leftmostID);
oppLane.oppositeID = leftmostID;
} else {
if (oppEdge->getFromNode() != oppOppEdge->getToNode() || oppEdge->getToNode() != oppOppEdge->getFromNode()) {
WRITE_ERRORF(TL("Opposite edge '%' does not reverse-connect the same nodes as edge '%'!"), oppEdge->getID(), oppOppEdge->getID());
} else {
WRITE_WARNINGF(TL("Adapting inconsistent opposite lanes for edges '%', '%' and '%'."), getID(), oppEdge->getID(), oppOpp);
}
oppLane.oppositeID = leftmostID;
NBEdge::Lane& oppOppLane = oppOppEdge->getLaneStruct(oppOppEdge->getNumLanes() - 1);
if (oppOppLane.oppositeID == oppEdge->getLaneID(oppEdge->getNumLanes() - 1)) {
oppOppLane.oppositeID = "";
}
}
}
if (fabs(oppEdge->getLoadedLength() - getLoadedLength()) > NUMERICAL_EPS) {
if (fixOppositeLengths) {
const double avgLength = 0.5 * (getFinalLength() + oppEdge->getFinalLength());
WRITE_WARNINGF(TL("Averaging edge lengths for lane '%' (length %) and edge '%' (length %)."),
oppositeID, oppEdge->getLoadedLength(), getID(), getLoadedLength());
setLoadedLength(avgLength);
oppEdge->setLoadedLength(avgLength);
} else {
WRITE_ERROR("Opposite lane '" + oppositeID + "' (length " + toString(oppEdge->getLoadedLength()) +
") differs in length from edge '" + getID() + "' (length " +
toString(getLoadedLength()) + "). Set --opposites.guess.fix-lengths to fix this.");
getLaneStruct(getNumLanes() - 1).oppositeID = "";
}
}
}
}
}
const NBEdge* bidi = getBidiEdge();
if (bidi != nullptr && getNumLanes() == 1 && bidi->getNumLanes() == 1 && getID() < bidi->getID()) {
getLaneStruct(0).shape = bidi->getLaneStruct(0).shape.reverse();
}
const double startOffset = isBidiRail() ? getTurnDestination(true)->getEndOffset() : 0;
int i = 0;
for (const NBEdge::Lane& l : getLanes()) {
if (startOffset + l.endOffset > getLength()) {
WRITE_WARNINGF(TL("Invalid endOffset % at lane '%' with length % (startOffset %)."),
toString(l.endOffset), getLaneID(i), toString(l.shape.length()), toString(startOffset));
} else if (l.speed < 0.) {
WRITE_WARNINGF(TL("Negative allowed speed (%) on lane '%', use --speed.minimum to prevent this."), toString(l.speed), getLaneID(i));
} else if (l.speed == 0.) {
WRITE_WARNINGF(TL("Lane '%' has a maximum allowed speed of 0."), getLaneID(i));
}
i++;
}
}
void NBEdge::removeInvalidConnections() {
for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
Connection& c = *i;
const SVCPermissions common = getPermissions(c.fromLane) & c.toEdge->getPermissions(c.toLane);
if (common == SVC_PEDESTRIAN || getPermissions(c.fromLane) == SVC_PEDESTRIAN) {
#ifdef DEBUG_CONNECTION_CHECKING
std::cout << " remove pedCon " << c.getDescription(this) << "\n";
#endif
i = myConnections.erase(i);
} else if (common == 0) {
const int origToLane = c.toLane;
c.toLane = -1;
int toLane = origToLane;
while (toLane > 0
&& (getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) == 0
&& !hasConnectionTo(c.toEdge, toLane)
) {
toLane--;
}
if ((getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) != 0
&& !hasConnectionTo(c.toEdge, toLane)) {
c.toLane = toLane;
++i;
} else {
toLane = origToLane;
while (toLane < (int)c.toEdge->getNumLanes() - 1
&& (getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) == 0
&& !hasConnectionTo(c.toEdge, toLane)
) {
toLane++;
}
if ((getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) != 0
&& !hasConnectionTo(c.toEdge, toLane)) {
c.toLane = toLane;
++i;
} else {
#ifdef DEBUG_CONNECTION_CHECKING
std::cout << " remove " << c.getDescription(this) << " with no alternative target\n";
#endif
i = myConnections.erase(i);
}
}
} else if (isRailway(getPermissions(c.fromLane)) && isRailway(c.toEdge->getPermissions(c.toLane))
&& isTurningDirectionAt(c.toEdge)) {
#ifdef DEBUG_CONNECTION_CHECKING
std::cout << " remove " << c.getDescription(this) << " (rail turnaround)\n";
#endif
i = myConnections.erase(i);
} else {
++i;
}
}
}
void
NBEdge::divideOnEdges(const EdgeVector* outgoing) {
if (outgoing->size() == 0) {
myConnections.clear();
return;
}
#ifdef DEBUG_CONNECTION_GUESSING
if (DEBUGCOND) {
std::cout << " divideOnEdges " << getID() << " outgoing=" << toString(*outgoing) << "\n";
}
#endif
std::vector<int> availableLanes;
for (int i = 0; i < (int)myLanes.size(); ++i) {
if ((getPermissions(i) & SVC_PASSENGER) != 0) {
availableLanes.push_back(i);
}
}
if (availableLanes.size() > 0) {
divideSelectedLanesOnEdges(outgoing, availableLanes);
}
availableLanes.clear();
for (int i = 0; i < (int)myLanes.size(); ++i) {
const SVCPermissions perms = getPermissions(i);
if ((perms & ~(SVC_PEDESTRIAN | SVC_BICYCLE | SVC_BUS)) == 0 || (perms & SVC_PASSENGER) != 0 || isForbidden(perms)) {
continue;
}
availableLanes.push_back(i);
}
if (availableLanes.size() > 0) {
divideSelectedLanesOnEdges(outgoing, availableLanes);
}
availableLanes.clear();
for (int i = 0; i < (int)myLanes.size(); ++i) {
const SVCPermissions perms = getPermissions(i);
if ((perms & SVC_BUS) == 0 || (perms & ~(SVC_PEDESTRIAN | SVC_BICYCLE | SVC_BUS)) != 0 || (perms & SVC_PASSENGER) != 0) {
continue;
}
availableLanes.push_back(i);
}
if (availableLanes.size() > 0) {
divideSelectedLanesOnEdges(outgoing, availableLanes);
}
availableLanes.clear();
for (int i = 0; i < (int)myLanes.size(); ++i) {
const SVCPermissions perms = getPermissions(i);
if (perms != SVC_BICYCLE && perms != (SVC_BICYCLE | SVC_PEDESTRIAN)) {
continue;
}
availableLanes.push_back(i);
}
if (availableLanes.size() > 0) {
divideSelectedLanesOnEdges(outgoing, availableLanes);
}
bool explicitTurnaround = false;
SVCPermissions turnaroundPermissions = SVC_UNSPECIFIED;
for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
if ((*i).fromLane == -1) {
if ((*i).toEdge == myTurnDestination && myTurnDestination != nullptr) {
explicitTurnaround = true;
turnaroundPermissions = (*i).permissions;
}
if ((*i).permissions != SVC_UNSPECIFIED) {
for (Connection& c : myConnections) {
if (c.toLane == -1 && c.toEdge == (*i).toEdge) {
c.permissions = (*i).permissions;
}
}
}
i = myConnections.erase(i);
} else {
++i;
}
}
if (explicitTurnaround) {
myConnections.push_back(Connection((int)myLanes.size() - 1, myTurnDestination, myTurnDestination->getNumLanes() - 1));
myConnections.back().permissions = turnaroundPermissions;
}
sortOutgoingConnectionsByIndex();
}
void
NBEdge::divideSelectedLanesOnEdges(const EdgeVector* outgoing, const std::vector<int>& availableLanes) {
const std::vector<int>& priorities = prepareEdgePriorities(outgoing, availableLanes);
if (priorities.empty()) {
return;
}
#ifdef DEBUG_CONNECTION_GUESSING
if (DEBUGCOND) {
std::cout << "divideSelectedLanesOnEdges " << getID() << " out=" << toString(*outgoing) << " prios=" << toString(priorities) << " avail=" << toString(availableLanes) << "\n";
}
#endif
const int numOutgoing = (int)outgoing->size();
std::vector<int> resultingLanesFactor;
resultingLanesFactor.reserve(numOutgoing);
int minResulting = std::numeric_limits<int>::max();
for (int i = 0; i < numOutgoing; i++) {
const int res = priorities[i] * (int)availableLanes.size();
resultingLanesFactor.push_back(res);
if (minResulting > res && res > 0) {
minResulting = res;
}
}
int numVirtual = 0;
EdgeVector transition;
transition.reserve(numOutgoing);
for (int i = 0; i < numOutgoing; i++) {
assert(i < (int)resultingLanesFactor.size());
const int tmpNum = (resultingLanesFactor[i] + minResulting - 1) / minResulting;
numVirtual += tmpNum;
for (int j = 0; j < tmpNum; j++) {
transition.push_back((*outgoing)[i]);
}
}
#ifdef DEBUG_CONNECTION_GUESSING
if (DEBUGCOND) {
std::cout << " minResulting=" << minResulting << " numVirtual=" << numVirtual << " availLanes=" << toString(availableLanes) << " resLanes=" << toString(resultingLanesFactor) << " transition=" << toString(transition) << "\n";
}
#endif
ToEdgeConnectionsAdder adder(transition);
Bresenham::compute(&adder, static_cast<int>(availableLanes.size()), numVirtual);
const std::map<NBEdge*, std::vector<int> >& l2eConns = adder.getBuiltConnections();
for (NBEdge* const target : *outgoing) {
assert(l2eConns.find(target) != l2eConns.end());
for (const int j : l2eConns.find(target)->second) {
const int fromIndex = availableLanes[j];
if ((getPermissions(fromIndex) & target->getPermissions()) == 0) {
continue;
}
if ((getPermissions(fromIndex) & target->getPermissions()) == SVC_PEDESTRIAN) {
continue;
}
const int numConsToTarget = (int)count_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(target, true));
int targetLanes = target->getNumLanes();
if (target->getPermissions(0) == SVC_PEDESTRIAN) {
--targetLanes;
}
if (numConsToTarget >= targetLanes) {
continue;
}
if (myLanes[fromIndex].connectionsDone) {
#ifdef DEBUG_CONNECTION_GUESSING
if (DEBUGCOND) {
std::cout << " connectionsDone from " << getID() << "_" << fromIndex << ": ";
for (const Connection& c : getConnectionsFromLane(fromIndex)) {
std::cout << c.getDescription(this) << ", ";
}
std::cout << "\n";
}
#endif
continue;
}
myConnections.push_back(Connection(fromIndex, target, -1));
#ifdef DEBUG_CONNECTION_GUESSING
if (DEBUGCOND) {
std::cout << " request connection from " << getID() << "_" << fromIndex << " to " << target->getID() << "\n";
}
#endif
}
}
addStraightConnections(outgoing, availableLanes, priorities);
}
void
NBEdge::addStraightConnections(const EdgeVector* outgoing, const std::vector<int>& availableLanes, const std::vector<int>& priorities) {
const int numOutgoing = (int) outgoing->size();
NBEdge* target = nullptr;
NBEdge* rightOfTarget = nullptr;
NBEdge* leftOfTarget = nullptr;
int maxPrio = 0;
for (int i = 0; i < numOutgoing; i++) {
if (maxPrio < priorities[i]) {
const LinkDirection dir = myTo->getDirection(this, (*outgoing)[i]);
if (dir == LinkDirection::STRAIGHT) {
maxPrio = priorities[i];
target = (*outgoing)[i];
rightOfTarget = i == 0 ? outgoing->back() : (*outgoing)[i - 1];
leftOfTarget = i + 1 == numOutgoing ? outgoing->front() : (*outgoing)[i + 1];
}
}
}
if (target == nullptr) {
return;
}
int numConsToTarget = (int)count_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(target, true));
int targetLanes = (int)target->getNumLanes();
if (target->getPermissions(0) == SVC_PEDESTRIAN) {
--targetLanes;
}
const int numDesiredConsToTarget = MIN2(targetLanes, (int)availableLanes.size());
#ifdef DEBUG_CONNECTION_GUESSING
if (DEBUGCOND) {
std::cout << " checking extra lanes for target=" << target->getID() << " cons=" << numConsToTarget << " desired=" << numDesiredConsToTarget << "\n";
}
#endif
std::vector<int>::const_iterator it_avail = availableLanes.begin();
while (numConsToTarget < numDesiredConsToTarget && it_avail != availableLanes.end()) {
const int fromIndex = *it_avail;
if (
(count_if(myConnections.begin(), myConnections.end(), connections_finder(fromIndex, target, -1)) == 0)
&& ((getPermissions(fromIndex) & target->getPermissions()) != 0)
&& ((getPermissions(fromIndex) & target->getPermissions()) != SVC_PEDESTRIAN)
&& !myLanes[fromIndex].connectionsDone
) {
#ifdef DEBUG_CONNECTION_GUESSING
if (DEBUGCOND) {
std::cout << " candidate from " << getID() << "_" << fromIndex << " to " << target->getID() << "\n";
}
#endif
if (
((it_avail + 1) == availableLanes.end() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, rightOfTarget, false)) == 0)
&& (it_avail == availableLanes.begin() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, leftOfTarget, true)) == 0)) {
#ifdef DEBUG_CONNECTION_GUESSING
if (DEBUGCOND) {
std::cout << " request additional connection from " << getID() << "_" << fromIndex << " to " << target->getID() << "\n";
}
#endif
myConnections.push_back(Connection(fromIndex, target, -1));
numConsToTarget++;
} else {
#ifdef DEBUG_CONNECTION_GUESSING
if (DEBUGCOND) std::cout
<< " fail check1="
<< ((it_avail + 1) == availableLanes.end() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, rightOfTarget, false)) == 0)
<< " check2=" << (it_avail == availableLanes.begin() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, leftOfTarget, true)) == 0)
<< " rightOfTarget=" << rightOfTarget->getID()
<< " leftOfTarget=" << leftOfTarget->getID()
<< "\n";
#endif
}
}
++it_avail;
}
}
const std::vector<int>
NBEdge::prepareEdgePriorities(const EdgeVector* outgoing, const std::vector<int>& availableLanes) {
std::vector<int> priorities;
MainDirections mainDirections(*outgoing, this, myTo, availableLanes);
const int dist = mainDirections.getStraightest();
if (dist == -1) {
return priorities;
}
priorities.reserve(outgoing->size());
for (const NBEdge* const out : *outgoing) {
int prio = NBNode::isTrafficLight(myTo->getType()) ? 0 : out->getJunctionPriority(myTo);
assert((prio + 1) * 2 > 0);
prio = (prio + 1) * 2;
priorities.push_back(prio);
}
#ifdef DEBUG_CONNECTION_GUESSING
if (DEBUGCOND) std::cout << " prepareEdgePriorities " << getID()
<< " outgoing=" << toString(*outgoing)
<< " priorities1=" << toString(priorities)
<< " dist=" << dist
<< "\n";
#endif
if (dist != 0 && !mainDirections.includes(MainDirections::Direction::RIGHTMOST)) {
assert(priorities.size() > 0);
priorities[0] /= 2;
#ifdef DEBUG_CONNECTION_GUESSING
if (DEBUGCOND) {
std::cout << " priorities2=" << toString(priorities) << "\n";
}
#endif
}
if (mainDirections.empty()) {
assert(dist < (int)priorities.size());
priorities[dist] *= 2;
#ifdef DEBUG_CONNECTION_GUESSING
if (DEBUGCOND) {
std::cout << " priorities3=" << toString(priorities) << "\n";
}
#endif
}
if (NBNode::isTrafficLight(myTo->getType())) {
priorities[dist] += 1;
} else {
if (mainDirections.includes(MainDirections::Direction::RIGHTMOST) && mainDirections.includes(MainDirections::Direction::LEFTMOST)) {
priorities[0] /= 4;
priorities[(int)priorities.size() - 1] /= 2;
#ifdef DEBUG_CONNECTION_GUESSING
if (DEBUGCOND) {
std::cout << " priorities6=" << toString(priorities) << "\n";
}
#endif
} else if (mainDirections.includes(MainDirections::Direction::RIGHTMOST)
&& outgoing->size() > 2
&& availableLanes.size() == 2
&& (*outgoing)[dist]->getPriority() == (*outgoing)[0]->getPriority()) {
priorities[0] /= 4;
priorities.back() /= 2;
#ifdef DEBUG_CONNECTION_GUESSING
if (DEBUGCOND) {
std::cout << " priorities7=" << toString(priorities) << "\n";
}
#endif
}
}
if (mainDirections.includes(MainDirections::Direction::FORWARD)) {
if (myLanes.size() > 2) {
priorities[dist] *= 2;
#ifdef DEBUG_CONNECTION_GUESSING
if (DEBUGCOND) {
std::cout << " priorities4=" << toString(priorities) << "\n";
}
#endif
} else {
priorities[dist] *= 3;
#ifdef DEBUG_CONNECTION_GUESSING
if (DEBUGCOND) {
std::cout << " priorities5=" << toString(priorities) << "\n";
}
#endif
}
}
return priorities;
}
void
NBEdge::appendTurnaround(bool noTLSControlled, bool noFringe, bool onlyDeadends, bool onlyTurnlane, bool noGeometryLike, bool checkPermissions) {
if (myTurnDestination == nullptr || myTo->getType() == SumoXMLNodeType::RAIL_CROSSING) {
return;
}
if (noTLSControlled && myTo->isTLControlled()) {
return;
}
if (noFringe && myTo->getFringeType() == FringeType::OUTER) {
return;
}
bool isDeadEnd = true;
for (const Connection& c : myConnections) {
if ((c.toEdge->getPermissions(c.toLane)
& getPermissions(c.fromLane)
& SVC_PASSENGER) != 0
|| (c.toEdge->getPermissions() & getPermissions()) == getPermissions()) {
isDeadEnd = false;
break;
}
}
if (onlyDeadends && !isDeadEnd) {
return;
}
const int fromLane = getFirstAllowedLaneIndex(NBNode::BACKWARD);
if (onlyTurnlane) {
for (const Connection& c : getConnectionsFromLane(fromLane)) {
LinkDirection dir = myTo->getDirection(this, c.toEdge);
if (dir != LinkDirection::LEFT && dir != LinkDirection::PARTLEFT) {
return;
}
}
}
const int toLane = myTurnDestination->getFirstAllowedLaneIndex(NBNode::BACKWARD);
if (checkPermissions) {
if ((getPermissions(fromLane) & myTurnDestination->getPermissions(toLane)) == 0) {
return;
}
if ((getPermissions(fromLane) & myTurnDestination->getPermissions(toLane)) == SVC_PEDESTRIAN) {
return;
}
}
if (isRailway(getPermissions() & myTurnDestination->getPermissions())
&& fabs(NBHelpers::normRelAngle(getAngleAtNode(myTo), myTurnDestination->getAngleAtNode(myTo))) > 90) {
if (isBidiRail() && isRailDeadEnd()) {
setConnection(fromLane, myTurnDestination, toLane, Lane2LaneInfoType::VALIDATED, false, false, KEEPCLEAR_UNSPECIFIED, UNSPECIFIED_CONTPOS, UNSPECIFIED_VISIBILITY_DISTANCE, SUMO_const_haltingSpeed);
return;
} else {
return;
}
};
if (noGeometryLike && !isDeadEnd) {
if (myTo->geometryLike() || ((getPermissions() & SVC_PASSENGER) != 0
&& !onlyTurnlane
&& myTo->geometryLike(
NBEdge::filterByPermissions(myTo->getIncomingEdges(), ~(SVC_BICYCLE | SVC_PEDESTRIAN | SVC_DELIVERY)),
NBEdge::filterByPermissions(myTo->getOutgoingEdges(), ~(SVC_BICYCLE | SVC_PEDESTRIAN | SVC_DELIVERY))))) {
EdgeVector turnIncoming = myTurnDestination->getIncomingEdges();
if (turnIncoming.size() > 1) {
return;
}
}
}
setConnection(fromLane, myTurnDestination, toLane, Lane2LaneInfoType::VALIDATED);
}
bool
NBEdge::isTurningDirectionAt(const NBEdge* const edge) const {
if (edge == myTurnDestination) {
return true;
} else if (myTurnDestination != nullptr) {
return false;
}
return edge == myPossibleTurnDestination;
}
NBNode*
NBEdge::tryGetNodeAtPosition(double pos, double tolerance) const {
if (pos < tolerance) {
return myFrom;
}
if (pos > myLength - tolerance) {
return myTo;
}
return nullptr;
}
void
NBEdge::moveOutgoingConnectionsFrom(NBEdge* e, int laneOff) {
int lanes = e->getNumLanes();
for (int i = 0; i < lanes; i++) {
for (const NBEdge::Connection& el : e->getConnectionsFromLane(i)) {
assert(el.tlID == "");
addLane2LaneConnection(i + laneOff, el.toEdge, el.toLane, Lane2LaneInfoType::COMPUTED);
}
}
}
bool
NBEdge::lanesWereAssigned() const {
return myStep == EdgeBuildingStep::LANES2LANES_DONE || myStep == EdgeBuildingStep::LANES2LANES_USER;
}
double
NBEdge::getMaxLaneOffset() {
return SUMO_const_laneWidth * (double)myLanes.size();
}
bool
NBEdge::mayBeTLSControlled(int fromLane, NBEdge* toEdge, int toLane) const {
for (const Connection& c : myConnections) {
if (c.fromLane == fromLane && c.toEdge == toEdge && c.toLane == toLane && c.uncontrolled) {
return false;
}
}
return true;
}
bool
NBEdge::setControllingTLInformation(const NBConnection& c, const std::string& tlID) {
const int fromLane = c.getFromLane();
NBEdge* toEdge = c.getTo();
const int toLane = c.getToLane();
const int tlIndex = c.getTLIndex();
const int tlIndex2 = c.getTLIndex2();
if (!mayBeTLSControlled(fromLane, toEdge, toLane)) {
return false;
}
assert(fromLane < 0 || fromLane < (int) myLanes.size());
if (fromLane >= 0 && toLane >= 0) {
std::vector<Connection>::iterator i =
find_if(myConnections.begin(), myConnections.end(), connections_finder(fromLane, toEdge, toLane));
if (i != myConnections.end()) {
Connection& connection = *i;
connection.tlID = tlID;
connection.tlLinkIndex = tlIndex;
connection.tlLinkIndex2 = tlIndex2;
return true;
}
}
int no = 0;
bool hadError = false;
for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
if ((*i).toEdge != toEdge) {
continue;
}
if (fromLane >= 0 && fromLane != (*i).fromLane) {
continue;
}
if (toLane >= 0 && toLane != (*i).toLane) {
continue;
}
if ((*i).tlID == "") {
(*i).tlID = tlID;
(*i).tlLinkIndex = tlIndex;
(*i).tlLinkIndex2 = tlIndex2;
no++;
} else {
if ((*i).tlID != tlID && (*i).tlLinkIndex == tlIndex) {
WRITE_WARNINGF(TL("The lane '%' on edge '%' already had a traffic light signal."), i->fromLane, getID());
hadError = true;
}
}
}
if (hadError && no == 0) {
WRITE_WARNINGF(TL("Could not set any signal of the tlLogic '%' (unknown group)."), tlID);
}
return true;
}
void
NBEdge::clearControllingTLInformation() {
for (std::vector<Connection>::iterator it = myConnections.begin(); it != myConnections.end(); it++) {
it->tlID = "";
}
}
PositionVector
NBEdge::getCWBoundaryLine(const NBNode& n) const {
PositionVector ret;
int lane;
if (myFrom == (&n)) {
lane = getFirstAllowedLaneIndex(NBNode::FORWARD);
ret = myLanes[lane].shape;
} else {
lane = getFirstAllowedLaneIndex(NBNode::BACKWARD);
ret = myLanes[lane].shape.reverse();
}
ret.move2side(getLaneWidth(lane) / 2.);
return ret;
}
PositionVector
NBEdge::getCCWBoundaryLine(const NBNode& n) const {
PositionVector ret;
int lane;
if (myFrom == (&n)) {
lane = getFirstAllowedLaneIndex(NBNode::BACKWARD);
ret = myLanes[lane].shape;
} else {
lane = getFirstAllowedLaneIndex(NBNode::FORWARD);
ret = myLanes[lane].shape.reverse();
}
ret.move2side(-getLaneWidth(lane) / 2.);
return ret;
}
bool
NBEdge::expandableBy(NBEdge* possContinuation, std::string& reason) const {
if (myLanes.size() != possContinuation->myLanes.size()) {
reason = "laneNumber";
return false;
}
if (myFrom == possContinuation->myTo) {
reason = "loop";
return false;
}
if (isBidiRail() != possContinuation->isBidiRail()) {
reason = "bidi-rail";
return false;
}
switch (myStep) {
case EdgeBuildingStep::INIT_REJECT_CONNECTIONS:
break;
case EdgeBuildingStep::INIT:
break;
case EdgeBuildingStep::EDGE2EDGES: {
const EdgeVector& conn = getConnectedEdges();
if (find(conn.begin(), conn.end(), possContinuation) == conn.end()) {
reason = "disconnected";
return false;
}
}
break;
case EdgeBuildingStep::LANES2EDGES:
case EdgeBuildingStep::LANES2LANES_RECHECK:
case EdgeBuildingStep::LANES2LANES_DONE:
case EdgeBuildingStep::LANES2LANES_USER: {
if (find_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(possContinuation)) == myConnections.end()) {
reason = "disconnected";
return false;
}
std::vector<int> conns = getConnectionLanes(possContinuation);
const int offset = MAX2(0, getFirstNonPedestrianLaneIndex(NBNode::FORWARD, true));
if (conns.size() < myLanes.size() - offset) {
reason = "some lanes disconnected";
return false;
}
}
break;
default:
break;
}
const double minLength = OptionsCont::getOptions().getFloat("geometry.remove.min-length");
if (minLength > 0 && (possContinuation->getLoadedLength() < minLength || getLoadedLength() < minLength)) {
return true;
}
const double maxJunctionSize = OptionsCont::getOptions().getFloat("geometry.remove.max-junction-size");
if (maxJunctionSize >= 0) {
const double junctionSize = myGeom.back().distanceTo2D(possContinuation->myGeom.front());
if (junctionSize > maxJunctionSize + POSITION_EPS) {
reason = "junction size (" + toString(junctionSize) + ") > max-junction-size (" + toString(maxJunctionSize) + ")";
return false;
}
}
if (getPriority() != possContinuation->getPriority()) {
reason = "priority";
return false;
}
if (mySpeed != possContinuation->mySpeed) {
reason = "speed";
return false;
}
if (myLaneSpreadFunction != possContinuation->myLaneSpreadFunction) {
reason = "spreadType";
return false;
}
for (int i = 0; i < (int)myLanes.size(); i++) {
if (myLanes[i].speed != possContinuation->myLanes[i].speed) {
reason = "lane " + toString(i) + " speed";
return false;
} else if (myLanes[i].permissions != possContinuation->myLanes[i].permissions) {
reason = "lane " + toString(i) + " permissions";
return false;
} else if (myLanes[i].changeLeft != possContinuation->myLanes[i].changeLeft || myLanes[i].changeRight != possContinuation->myLanes[i].changeRight) {
reason = "lane " + toString(i) + " change restrictions";
return false;
} else if (myLanes[i].width != possContinuation->myLanes[i].width &&
fabs(myLanes[i].width - possContinuation->myLanes[i].width) > OptionsCont::getOptions().getFloat("geometry.remove.width-tolerance")) {
reason = "lane " + toString(i) + " width";
return false;
}
}
if (!OptionsCont::getOptions().isDefault("output.street-names") && myStreetName != possContinuation->getStreetName()
&& ((myStreetName != "" && possContinuation->getStreetName() != "")
|| (myStreetName != "" && myLength <= possContinuation->getLength())
|| (myStreetName == "" && myLength >= possContinuation->getLength()))) {
return false;
}
return true;
}
void
NBEdge::append(NBEdge* e) {
myGeom.append(e->myGeom);
for (int i = 0; i < (int)myLanes.size(); i++) {
myLanes[i].customShape.append(e->myLanes[i].customShape);
if (myLanes[i].hasParameter(SUMO_PARAM_ORIGID) || e->myLanes[i].hasParameter(SUMO_PARAM_ORIGID)
|| OptionsCont::getOptions().getBool("output.original-names")) {
const std::string origID = myLanes[i].getParameter(SUMO_PARAM_ORIGID, getID());
const std::string origID2 = e->myLanes[i].getParameter(SUMO_PARAM_ORIGID, e->getID());
if (origID != origID2) {
myLanes[i].setParameter(SUMO_PARAM_ORIGID, origID + " " + origID2);
}
}
myLanes[i].connectionsDone = e->myLanes[i].connectionsDone;
myLanes[i].turnSigns = e->myLanes[i].turnSigns;
}
if (e->getLength() > myLength) {
for (int i = 0; i < (int)myLanes.size(); i++) {
myLanes[i].width = e->myLanes[i].width;
}
if (myStreetName == "") {
myStreetName = e->myStreetName;
}
}
myLength += e->myLength;
if (myLoadedLength > 0 || e->myLoadedLength > 0) {
myLoadedLength = getFinalLength() + e->getFinalLength();
}
myStep = e->myStep;
myConnections = e->myConnections;
myTurnDestination = e->myTurnDestination;
myPossibleTurnDestination = e->myPossibleTurnDestination;
myConnectionsToDelete = e->myConnectionsToDelete;
updateRemovedNodes(e->getParameter(SUMO_PARAM_REMOVED_NODES));
myTo = e->myTo;
myTurnSignTarget = e->myTurnSignTarget;
myToBorder = e->myToBorder;
mergeParameters(e->getParametersMap());
if (e->mySignalPosition != Position::INVALID) {
mySignalPosition = e->mySignalPosition;
}
computeAngle();
}
void
NBEdge::updateRemovedNodes(const std::string& removed) {
std::string result = getParameter(SUMO_PARAM_REMOVED_NODES);
if (!result.empty() && !removed.empty()) {
result += " ";
}
result += removed;
if (!result.empty()) {
setParameter(SUMO_PARAM_REMOVED_NODES, result);
}
}
bool
NBEdge::hasSignalisedConnectionTo(const NBEdge* const e) const {
for (std::vector<Connection>::const_iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
if ((*i).toEdge == e && (*i).tlID != "") {
return true;
}
}
return false;
}
NBEdge*
NBEdge::getTurnDestination(bool possibleDestination) const {
if (myTurnDestination == nullptr && possibleDestination) {
return myPossibleTurnDestination;
}
return myTurnDestination;
}
std::string
NBEdge::getLaneID(int lane) const {
return myID + "_" + toString(lane);
}
bool
NBEdge::isNearEnough2BeJoined2(NBEdge* e, double threshold) const {
std::vector<double> distances = myGeom.distances(e->getGeometry());
assert(distances.size() > 0);
return VectorHelper<double>::maxValue(distances) < threshold;
}
void
NBEdge::addLane(int index, bool recomputeShape, bool recomputeConnections, bool shiftIndices) {
assert(index <= (int)myLanes.size());
myLanes.insert(myLanes.begin() + index, Lane(this, ""));
if (myLanes.size() > 1) {
int templateIndex = index > 0 ? index - 1 : index + 1;
myLanes[index].speed = myLanes[templateIndex].speed;
myLanes[index].friction = myLanes[templateIndex].friction;
myLanes[index].permissions = myLanes[templateIndex].permissions;
myLanes[index].preferred = myLanes[templateIndex].preferred;
myLanes[index].endOffset = myLanes[templateIndex].endOffset;
myLanes[index].width = myLanes[templateIndex].width;
myLanes[index].updateParameters(myLanes[templateIndex].getParametersMap());
}
const EdgeVector& incs = myFrom->getIncomingEdges();
if (recomputeShape) {
computeLaneShapes();
}
if (recomputeConnections) {
for (EdgeVector::const_iterator i = incs.begin(); i != incs.end(); ++i) {
(*i)->invalidateConnections(true);
}
invalidateConnections(true);
} else if (shiftIndices) {
for (Connection& c : myConnections) {
if (c.fromLane >= index) {
c.fromLane += 1;
}
}
for (NBEdge* inc : myFrom->getIncomingEdges()) {
for (Connection& c : inc->myConnections) {
if (c.toEdge == this && c.toLane >= index) {
c.toLane += 1;
}
}
}
myFrom->shiftTLConnectionLaneIndex(this, +1, index - 1);
myTo->shiftTLConnectionLaneIndex(this, +1, index - 1);
}
}
void
NBEdge::incLaneNo(int by) {
int newLaneNo = (int)myLanes.size() + by;
while ((int)myLanes.size() < newLaneNo) {
const bool recompute = ((int)myLanes.size() == newLaneNo - 1) && myStep < EdgeBuildingStep::LANES2LANES_USER;
addLane((int)myLanes.size(), recompute, recompute, false);
}
}
void
NBEdge::deleteLane(int index, bool recompute, bool shiftIndices) {
assert(index < (int)myLanes.size());
myLanes.erase(myLanes.begin() + index);
if (recompute) {
computeLaneShapes();
const EdgeVector& incs = myFrom->getIncomingEdges();
for (EdgeVector::const_iterator i = incs.begin(); i != incs.end(); ++i) {
(*i)->invalidateConnections(true);
}
invalidateConnections(true);
} else if (shiftIndices) {
removeFromConnections(nullptr, index, -1, false, true);
for (NBEdge* inc : myFrom->getIncomingEdges()) {
inc->removeFromConnections(this, -1, index, false, true);
}
}
}
void
NBEdge::decLaneNo(int by) {
int newLaneNo = (int) myLanes.size() - by;
assert(newLaneNo > 0);
while ((int)myLanes.size() > newLaneNo) {
const bool recompute = (int)myLanes.size() == newLaneNo + 1 && myStep < EdgeBuildingStep::LANES2LANES_USER;
deleteLane((int)myLanes.size() - 1, recompute, false);
}
}
void
NBEdge::markAsInLane2LaneState() {
assert(myTo->getOutgoingEdges().size() == 0);
myStep = EdgeBuildingStep::LANES2LANES_DONE;
}
void
NBEdge::allowVehicleClass(int lane, SUMOVehicleClass vclass) {
if (lane < 0) {
for (int i = 0; i < (int)myLanes.size(); i++) {
allowVehicleClass(i, vclass);
}
} else {
assert(lane < (int)myLanes.size());
myLanes[lane].permissions |= vclass;
}
}
void
NBEdge::disallowVehicleClass(int lane, SUMOVehicleClass vclass) {
if (lane < 0) {
for (int i = 0; i < (int)myLanes.size(); i++) {
disallowVehicleClass((int) i, vclass);
}
} else {
assert(lane < (int)myLanes.size());
myLanes[lane].permissions &= ~vclass;
}
}
void
NBEdge::preferVehicleClass(int lane, SVCPermissions vclasses) {
if (lane < 0) {
for (int i = 0; i < (int)myLanes.size(); i++) {
preferVehicleClass(i, vclasses);
}
} else {
assert(lane < (int)myLanes.size());
myLanes[lane].permissions |= vclasses;
myLanes[lane].preferred |= vclasses;
}
}
void
NBEdge::setLaneWidth(int lane, double width) {
if (lane < 0) {
myLaneWidth = width;
for (int i = 0; i < (int)myLanes.size(); i++) {
setLaneWidth(i, width);
}
return;
}
assert(lane < (int)myLanes.size());
myLanes[lane].width = width;
}
void
NBEdge::setLaneType(int lane, const std::string& type) {
if (lane < 0) {
for (int i = 0; i < (int)myLanes.size(); i++) {
setLaneType(i, type);
}
return;
}
assert(lane < (int)myLanes.size());
myLanes[lane].type = type;
}
double
NBEdge::getLaneWidth(int lane) const {
return myLanes[lane].width != UNSPECIFIED_WIDTH
? myLanes[lane].width
: getLaneWidth() != UNSPECIFIED_WIDTH ? getLaneWidth() : SUMO_const_laneWidth;
}
double
NBEdge::getInternalLaneWidth(
const NBNode& node,
const NBEdge::Connection& connection,
const NBEdge::Lane& successor,
bool isVia) const {
if (!isVia && node.isConstantWidthTransition() && getNumLanes() > connection.toEdge->getNumLanes()) {
return getLaneWidth(connection.fromLane);
}
return (isBikepath(getPermissions(connection.fromLane)) && (
getLaneWidth(connection.fromLane) < successor.width || successor.width == UNSPECIFIED_WIDTH)) ?
myLanes[connection.fromLane].width : successor.width;
}
double
NBEdge::getTotalWidth() const {
double result = 0;
for (int i = 0; i < (int)myLanes.size(); i++) {
result += getLaneWidth(i);
}
return result;
}
double
NBEdge::getEndOffset(int lane) const {
return myLanes[lane].endOffset != UNSPECIFIED_OFFSET ? myLanes[lane].endOffset : getEndOffset();
}
const StopOffset&
NBEdge::getEdgeStopOffset() const {
return myEdgeStopOffset;
}
const StopOffset&
NBEdge::getLaneStopOffset(int lane) const {
if (lane == -1) {
return myEdgeStopOffset;
} else {
return myLanes[lane].laneStopOffset;
}
}
void
NBEdge::setEndOffset(int lane, double offset) {
if (lane < 0) {
myEndOffset = offset;
for (int i = 0; i < (int)myLanes.size(); i++) {
setEndOffset(i, offset);
}
return;
}
assert(lane < (int)myLanes.size());
myLanes[lane].endOffset = offset;
}
bool
NBEdge::setEdgeStopOffset(int lane, const StopOffset& offset, bool overwrite) {
if (lane < 0) {
if (!overwrite && myEdgeStopOffset.isDefined()) {
return false;
}
if (offset.getOffset() < 0) {
WRITE_WARNINGF(TL("Ignoring invalid stopOffset for edge '%' (negative offset)."), getID());
return false;
} else {
myEdgeStopOffset = offset;
}
} else if (lane < (int)myLanes.size()) {
if (!myLanes[lane].laneStopOffset.isDefined() || overwrite) {
if (offset.getOffset() < 0) {
WRITE_WARNINGF(TL("Ignoring invalid stopOffset for lane '%' (negative offset)."), getLaneID(lane));
} else {
myLanes[lane].laneStopOffset = offset;
}
}
} else {
WRITE_WARNINGF(TL("Ignoring invalid stopOffset for lane '%' (invalid lane index)."), toString(lane));
}
return true;
}
void
NBEdge::setSpeed(int lane, double speed) {
if (lane < 0) {
mySpeed = speed;
for (int i = 0; i < (int)myLanes.size(); i++) {
setSpeed(i, speed);
}
return;
}
assert(lane < (int)myLanes.size());
myLanes[lane].speed = speed;
}
void
NBEdge::setFriction(int lane, double friction) {
if (lane < 0) {
myFriction = friction;
for (int i = 0; i < (int)myLanes.size(); i++) {
setFriction(i, friction);
}
return;
}
assert(lane < (int)myLanes.size());
myLanes[lane].friction = friction;
}
void
NBEdge::setAcceleration(int lane, bool accelRamp) {
assert(lane >= 0);
assert(lane < (int)myLanes.size());
myLanes[lane].accelRamp = accelRamp;
}
void
NBEdge::setLaneShape(int lane, const PositionVector& shape) {
assert(lane >= 0);
assert(lane < (int)myLanes.size());
myLanes[lane].customShape = shape;
}
void
NBEdge::setPermissions(SVCPermissions permissions, int lane) {
if (lane < 0) {
for (int i = 0; i < (int)myLanes.size(); i++) {
setPermissions(permissions, i);
}
} else {
assert(lane < (int)myLanes.size());
myLanes[lane].permissions = permissions;
}
}
void
NBEdge::setPreferredVehicleClass(SVCPermissions permissions, int lane) {
if (lane < 0) {
for (int i = 0; i < (int)myLanes.size(); i++) {
setPreferredVehicleClass(permissions, i);
}
} else {
assert(lane < (int)myLanes.size());
myLanes[lane].preferred = permissions;
}
}
void
NBEdge::setPermittedChanging(int lane, SVCPermissions changeLeft, SVCPermissions changeRight) {
assert(lane >= 0);
assert(lane < (int)myLanes.size());
myLanes[lane].changeLeft = changeLeft;
myLanes[lane].changeRight = changeRight;
}
SVCPermissions
NBEdge::getPermissions(int lane) const {
if (lane < 0) {
SVCPermissions result = 0;
for (int i = 0; i < (int)myLanes.size(); i++) {
result |= getPermissions(i);
}
return result;
} else {
assert(lane < (int)myLanes.size());
return myLanes[lane].permissions;
}
}
void
NBEdge::setLoadedLength(double val) {
myLoadedLength = val;
}
void
NBEdge::setAverageLengthWithOpposite(double val) {
myLength = val;
}
void
NBEdge::dismissVehicleClassInformation() {
for (std::vector<Lane>::iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
(*i).permissions = SVCAll;
(*i).preferred = 0;
}
}
bool
NBEdge::connections_sorter(const Connection& c1, const Connection& c2) {
if (c1.fromLane != c2.fromLane) {
return c1.fromLane < c2.fromLane;
}
if (c1.toEdge != c2.toEdge) {
return false;
}
return c1.toLane < c2.toLane;
}
double
NBEdge::getSignalOffset() const {
if (mySignalPosition == Position::INVALID) {
return UNSPECIFIED_SIGNAL_OFFSET;
} else {
Position laneEnd = myLaneSpreadFunction == LaneSpreadFunction::RIGHT ?
myLanes.back().shape.back() : myLanes[getNumLanes() / 2].shape.back();
return mySignalPosition.distanceTo2D(laneEnd);
}
}
int
NBEdge::getFirstNonPedestrianLaneIndex(int direction, bool exclusive) const {
assert(direction == NBNode::FORWARD || direction == NBNode::BACKWARD);
const int start = (direction == NBNode::FORWARD ? 0 : (int)myLanes.size() - 1);
const int end = (direction == NBNode::FORWARD ? (int)myLanes.size() : - 1);
for (int i = start; i != end; i += direction) {
if ((exclusive && myLanes[i].permissions != SVC_PEDESTRIAN && myLanes[i].permissions != 0)
|| ((myLanes[i].permissions & SVC_PEDESTRIAN) == 0 && myLanes[i].permissions != 0)) {
return i;
}
}
return -1;
}
int
NBEdge::getFirstNonPedestrianNonBicycleLaneIndex(int direction, bool exclusive) const {
assert(direction == NBNode::FORWARD || direction == NBNode::BACKWARD);
const int start = (direction == NBNode::FORWARD ? 0 : (int)myLanes.size() - 1);
const int end = (direction == NBNode::FORWARD ? (int)myLanes.size() : - 1);
for (int i = start; i != end; i += direction) {
SVCPermissions p = myLanes[i].permissions;
if ((exclusive && p != SVC_PEDESTRIAN && p != SVC_BICYCLE && p != (SVC_PEDESTRIAN | SVC_BICYCLE) && p != 0)
|| (p == SVCAll || ((p & (SVC_PEDESTRIAN | SVC_BICYCLE)) == 0 && p != 0))) {
return i;
}
}
return -1;
}
int
NBEdge::getSpecialLane(SVCPermissions permissions) const {
for (int i = 0; i < (int)myLanes.size(); i++) {
if (myLanes[i].permissions == permissions) {
return i;
}
}
return -1;
}
int
NBEdge::getFirstAllowedLaneIndex(int direction) const {
assert(direction == NBNode::FORWARD || direction == NBNode::BACKWARD);
const int start = (direction == NBNode::FORWARD ? 0 : (int)myLanes.size() - 1);
const int end = (direction == NBNode::FORWARD ? (int)myLanes.size() : - 1);
for (int i = start; i != end; i += direction) {
if (myLanes[i].permissions != 0) {
return i;
}
}
return end - direction;
}
std::set<SVCPermissions>
NBEdge::getPermissionVariants(int iStart, int iEnd) const {
std::set<SVCPermissions> result;
if (iStart < 0 || iStart >= getNumLanes() || iEnd > getNumLanes()) {
throw ProcessError("invalid indices iStart " + toString(iStart) + " iEnd " + toString(iEnd) + " for edge with " + toString(getNumLanes()) + " lanes.");
}
for (int i = iStart; i < iEnd; ++i) {
result.insert(getPermissions(i));
}
return result;
}
int
NBEdge::getNumLanesThatAllow(SVCPermissions permissions, bool allPermissions) const {
int result = 0;
for (const Lane& lane : myLanes) {
if ((allPermissions && (lane.permissions & permissions) == permissions)
|| (!allPermissions && (lane.permissions & permissions) != 0)) {
result++;
}
}
return result;
}
bool
NBEdge::allowsChangingLeft(int lane, SUMOVehicleClass vclass) const {
assert(lane >= 0 && lane < getNumLanes());
return myLanes[lane].changeLeft == SVC_UNSPECIFIED ? true : (myLanes[lane].changeLeft & vclass) == vclass;
}
bool
NBEdge::allowsChangingRight(int lane, SUMOVehicleClass vclass) const {
assert(lane >= 0 && lane < getNumLanes());
return myLanes[lane].changeRight == SVC_UNSPECIFIED ? true : (myLanes[lane].changeRight & vclass) == vclass;
}
double
NBEdge::getCrossingAngle(NBNode* node) {
double angle = getAngleAtNode(node) + (getFromNode() == node ? 180.0 : 0.0);
if (angle < 0) {
angle += 360.0;
}
if (angle >= 360) {
angle -= 360.0;
}
if (gDebugFlag1) {
std::cout << getID() << " angle=" << getAngleAtNode(node) << " convAngle=" << angle << "\n";
}
return angle;
}
NBEdge::Lane
NBEdge::getFirstNonPedestrianLane(int direction) const {
int index = getFirstNonPedestrianLaneIndex(direction);
if (index < 0) {
throw ProcessError(TLF("Edge % allows pedestrians on all lanes", getID()));
}
return myLanes[index];
}
std::string
NBEdge::getSidewalkID() {
for (int i = 0; i < (int)myLanes.size(); i++) {
if (myLanes[i].permissions == SVC_PEDESTRIAN) {
return getLaneID(i);
}
}
for (int i = 0; i < (int)myLanes.size(); i++) {
if ((myLanes[i].permissions & SVC_PEDESTRIAN) != 0) {
return getLaneID(i);
}
}
return getLaneID(0);
}
void
NBEdge::addSidewalk(double width) {
addRestrictedLane(width, SVC_PEDESTRIAN);
}
void
NBEdge::restoreSidewalk(std::vector<NBEdge::Lane> oldLanes, PositionVector oldGeometry, std::vector<NBEdge::Connection> oldConnections) {
restoreRestrictedLane(SVC_PEDESTRIAN, oldLanes, oldGeometry, oldConnections);
}
void
NBEdge::addBikeLane(double width) {
addRestrictedLane(width, SVC_BICYCLE);
}
void
NBEdge::restoreBikelane(std::vector<NBEdge::Lane> oldLanes, PositionVector oldGeometry, std::vector<NBEdge::Connection> oldConnections) {
restoreRestrictedLane(SVC_BICYCLE, oldLanes, oldGeometry, oldConnections);
}
bool
NBEdge::hasRestrictedLane(SUMOVehicleClass vclass) const {
for (const Lane& lane : myLanes) {
if (lane.permissions == vclass) {
return true;
}
}
return false;
}
void
NBEdge::addRestrictedLane(double width, SUMOVehicleClass vclass) {
if (hasRestrictedLane(vclass)) {
WRITE_WARNINGF(TL("Edge '%' already has a dedicated lane for %s. Not adding another one."), getID(), toString(vclass));
return;
}
if (myLaneSpreadFunction == LaneSpreadFunction::CENTER) {
myGeom.move2side(width / 2);
}
disallowVehicleClass(-1, vclass);
const int newIndex = (vclass != SVC_PEDESTRIAN && myLanes[0].permissions == SVC_PEDESTRIAN) ? 1 : 0;
if (newIndex == 0) {
disallowVehicleClass(-1, SVC_PEDESTRIAN);
}
myLanes.insert(myLanes.begin() + newIndex, Lane(this, myLanes[0].getParameter(SUMO_PARAM_ORIGID)));
myLanes[newIndex].permissions = vclass;
myLanes[newIndex].width = fabs(width);
for (std::vector<Connection>::iterator it = myConnections.begin(); it != myConnections.end(); ++it) {
Connection& c = *it;
if (c.fromLane >= newIndex) {
c.fromLane += 1;
}
}
const EdgeVector& incoming = myFrom->getIncomingEdges();
for (EdgeVector::const_iterator it = incoming.begin(); it != incoming.end(); ++it) {
(*it)->shiftToLanesToEdge(this, 1);
}
myFrom->shiftTLConnectionLaneIndex(this, 1);
myTo->shiftTLConnectionLaneIndex(this, 1);
computeLaneShapes();
}
void
NBEdge::restoreRestrictedLane(SUMOVehicleClass vclass, std::vector<NBEdge::Lane> oldLanes, PositionVector oldGeometry, std::vector<NBEdge::Connection> oldConnections) {
if (myLanes[0].permissions != vclass) {
WRITE_WARNINGF(TL("Edge '%' doesn't have a dedicated lane for %s. Cannot be restored."), getID(), toString(vclass));
return;
}
myGeom = oldGeometry;
myLanes = oldLanes;
myConnections = oldConnections;
const EdgeVector& incoming = myFrom->getIncomingEdges();
for (EdgeVector::const_iterator it = incoming.begin(); it != incoming.end(); ++it) {
(*it)->shiftToLanesToEdge(this, 0);
}
myFrom->shiftTLConnectionLaneIndex(this, 0);
myTo->shiftTLConnectionLaneIndex(this, 0);
computeLaneShapes();
}
void
NBEdge::shiftToLanesToEdge(NBEdge* to, int laneOff) {
for (std::vector<Connection>::iterator it = myConnections.begin(); it != myConnections.end(); ++it) {
if ((*it).toEdge == to && (*it).toLane >= 0) {
(*it).toLane += laneOff;
}
}
}
bool
NBEdge::shiftPositionAtNode(NBNode* node, NBEdge* other) {
if (myLaneSpreadFunction == LaneSpreadFunction::CENTER
&& !isRailway(getPermissions())
&& !isRailway(other->getPermissions())
&& getBidiEdge() == nullptr) {
const int i = (node == myTo ? -1 : 0);
const int i2 = (node == myTo ? 0 : -1);
const double dist = myGeom[i].distanceTo2D(node->getPosition());
const double neededOffset = getTotalWidth() / 2;
const double dist2 = MIN2(myGeom.distance2D(other->getGeometry()[i2]),
other->getGeometry().distance2D(myGeom[i]));
const double neededOffset2 = neededOffset + (other->getTotalWidth()) / 2;
if (dist < neededOffset && dist2 < neededOffset2) {
PositionVector tmp = myGeom;
try {
tmp.move2side(neededOffset - dist);
tmp[i].round(gPrecision);
myGeom[i] = tmp[i];
computeAngle();
return true;
} catch (InvalidArgument&) {
WRITE_WARNINGF(TL("Could not avoid overlapping shape at node '%' for edge '%'."), node->getID(), getID());
}
}
}
return false;
}
Position
NBEdge::geometryPositionAtOffset(double offset) const {
if (myLoadedLength > 0) {
return myGeom.positionAtOffset(offset * myLength / myLoadedLength);
} else {
return myGeom.positionAtOffset(offset);
}
}
double
NBEdge::getFinalLength() const {
double result = getLoadedLength();
if (OptionsCont::getOptions().getBool("no-internal-links") && !hasLoadedLength()) {
PositionVector geom = cutAtIntersection(myGeom);
geom.push_back_noDoublePos(getToNode()->getCenter());
geom.push_front_noDoublePos(getFromNode()->getCenter());
result = geom.length();
}
double avgEndOffset = 0;
for (const Lane& lane : myLanes) {
avgEndOffset += lane.endOffset;
}
if (isBidiRail()) {
avgEndOffset += myPossibleTurnDestination->getEndOffset();
}
avgEndOffset /= (double)myLanes.size();
return MAX2(result - avgEndOffset, POSITION_EPS);
}
void
NBEdge::setOrigID(const std::string origID, const bool append, const int laneIdx) {
if (laneIdx == -1) {
for (int i = 0; i < (int)myLanes.size(); i++) {
setOrigID(origID, append, i);
}
} else {
if (origID != "") {
if (append) {
std::vector<std::string> oldIDs = StringTokenizer(myLanes[laneIdx].getParameter(SUMO_PARAM_ORIGID)).getVector();
if (std::find(oldIDs.begin(), oldIDs.end(), origID) == oldIDs.end()) {
oldIDs.push_back(origID);
}
myLanes[laneIdx].setParameter(SUMO_PARAM_ORIGID, toString(oldIDs));
} else {
myLanes[laneIdx].setParameter(SUMO_PARAM_ORIGID, origID);
}
} else {
myLanes[laneIdx].unsetParameter(SUMO_PARAM_ORIGID);
}
}
}
const EdgeVector&
NBEdge::getSuccessors(SUMOVehicleClass vClass) const {
mySuccessors.clear();
for (const Connection& con : myConnections) {
if (con.fromLane >= 0 && con.toLane >= 0 && con.toEdge != nullptr &&
(vClass == SVC_IGNORING || (getPermissions(con.fromLane)
& con.toEdge->getPermissions(con.toLane) & vClass) != 0)
&& std::find(mySuccessors.begin(), mySuccessors.end(), con.toEdge) == mySuccessors.end()) {
mySuccessors.push_back(con.toEdge);
}
}
return mySuccessors;
}
const ConstRouterEdgePairVector&
NBEdge::getViaSuccessors(SUMOVehicleClass vClass, bool ) const {
myViaSuccessors.clear();
for (const Connection& con : myConnections) {
std::pair<const NBEdge*, const Connection*> pair(con.toEdge, nullptr);
if (vClass == SVC_PEDESTRIAN) {
myViaSuccessors.push_back(pair);
} else if ((con.fromLane >= 0) && (con.toLane >= 0) &&
(con.toEdge != nullptr) &&
((getPermissions(con.fromLane) & con.toEdge->getPermissions(con.toLane) & vClass) == vClass)) {
if (con.getLength() > 0) {
pair.second = &con;
}
myViaSuccessors.push_back(pair);
}
}
return myViaSuccessors;
}
void
NBEdge::debugPrintConnections(bool outgoing, bool incoming) const {
if (outgoing) {
for (const Connection& c : myConnections) {
std::cout << " " << getID() << "_" << c.fromLane << "->" << c.toEdge->getID() << "_" << c.toLane << "\n";
}
}
if (incoming) {
for (NBEdge* inc : myFrom->getIncomingEdges()) {
for (Connection& c : inc->myConnections) {
if (c.toEdge == this) {
std::cout << " " << inc->getID() << "_" << c.fromLane << "->" << c.toEdge->getID() << "_" << c.toLane << "\n";
}
}
}
}
}
int
NBEdge::getLaneIndexFromLaneID(const std::string laneID) {
return StringUtils::toInt(laneID.substr(laneID.rfind("_") + 1));
}
bool
NBEdge::joinLanes(SVCPermissions perms) {
bool haveJoined = false;
int i = 0;
while (i < getNumLanes() - 1) {
if ((getPermissions(i) == perms) && (getPermissions(i + 1) == perms)) {
const double newWidth = getLaneWidth(i) + getLaneWidth(i + 1);
const std::string newType = myLanes[i].type + "|" + myLanes[i + 1].type;
deleteLane(i, false, true);
setLaneWidth(i, newWidth);
setLaneType(i, newType);
haveJoined = true;
} else {
i++;
}
}
return haveJoined;
}
EdgeVector
NBEdge::filterByPermissions(const EdgeVector& edges, SVCPermissions permissions) {
EdgeVector result;
for (NBEdge* edge : edges) {
if ((edge->getPermissions() & permissions) != 0) {
result.push_back(edge);
}
}
return result;
}
NBEdge*
NBEdge::getStraightContinuation(SVCPermissions permissions) const {
EdgeVector cands = filterByPermissions(myTo->getOutgoingEdges(), permissions);
if (cands.size() == 0) {
return nullptr;
}
sort(cands.begin(), cands.end(), NBContHelper::edge_similar_direction_sorter(this));
NBEdge* best = cands.front();
if (isTurningDirectionAt(best)) {
return nullptr;
} else {
return best;
}
}
NBEdge*
NBEdge::getStraightPredecessor(SVCPermissions permissions) const {
EdgeVector cands = filterByPermissions(myFrom->getIncomingEdges(), permissions);
if (cands.size() == 0) {
return nullptr;
}
sort(cands.begin(), cands.end(), NBContHelper::edge_similar_direction_sorter(this, false));
NBEdge* best = cands.front();
if (best->isTurningDirectionAt(this)) {
return nullptr;
} else {
return best;
}
}
NBEdge*
NBEdge::guessOpposite(bool reguess) {
NBEdge* opposite = nullptr;
if (getNumLanes() > 0) {
NBEdge::Lane& lastLane = myLanes.back();
const double lastWidth = getLaneWidth(getNumLanes() - 1);
if (lastLane.oppositeID == "" || reguess) {
for (NBEdge* cand : getToNode()->getOutgoingEdges()) {
if (cand->getToNode() == getFromNode() && !cand->getLanes().empty()) {
const NBEdge::Lane& candLastLane = cand->getLanes().back();
if (candLastLane.oppositeID == "" || candLastLane.oppositeID == getLaneID(getNumLanes() - 1)) {
const double lastWidthCand = cand->getLaneWidth(cand->getNumLanes() - 1);
const double threshold = 1.42 * 0.5 * (lastWidth + lastWidthCand) + 0.5;
const double distance = VectorHelper<double>::maxValue(lastLane.shape.distances(cand->getLanes().back().shape));
if (distance < threshold) {
opposite = cand;
}
}
}
}
if (opposite != nullptr) {
lastLane.oppositeID = opposite->getLaneID(opposite->getNumLanes() - 1);
}
}
}
return opposite;
}
double
NBEdge::getDistancAt(double pos) const {
return fabs(myDistance + pos);
}