#pragma once
#include <config.h>
#include <string>
#include <vector>
#include <algorithm>
#include <assert.h>
#include <utils/common/MsgHandler.h>
#include <utils/common/Named.h>
#include <utils/common/SUMOTime.h>
#include <utils/common/ToString.h>
#include <utils/geom/Position.h>
#include <utils/vehicle/SUMOVehicleParameter.h>
#include "AccessEdge.h"
#include "CarEdge.h"
#include "IntermodalEdge.h"
#include "PedestrianEdge.h"
#include "PublicTransportEdge.h"
#include "StopEdge.h"
enum ModeChangeOptions {
PARKING_AREAS = 1,
PT_STOPS = 2,
ALL_JUNCTIONS = 2 << 2,
TAXI_DROPOFF_PARKING_AREAS = 2 << 3,
TAXI_DROPOFF_PT = 2 << 4,
TAXI_DROPOFF_ANYWHERE = 2 << 5,
TAXI_PICKUP_PARKING_AREAS = 2 << 6,
TAXI_PICKUP_PT = 2 << 7,
TAXI_PICKUP_ANYWHERE = 2 << 8
};
template<class E, class L, class N, class V>
class IntermodalNetwork {
private:
typedef IntermodalEdge<E, L, N, V> _IntermodalEdge;
typedef AccessEdge<E, L, N, V> _AccessEdge;
typedef PedestrianEdge<E, L, N, V> _PedestrianEdge;
typedef PublicTransportEdge<E, L, N, V> _PTEdge;
typedef std::pair<_IntermodalEdge*, _IntermodalEdge*> EdgePair;
public:
IntermodalNetwork(const std::vector<E*>& edges, const bool pedestrianOnly, const int carWalkTransfer = 0)
: myNumericalID(0), myCarWalkTransfer(carWalkTransfer) {
#ifdef IntermodalRouter_DEBUG_NETWORK
std::cout << "initIntermodalNetwork\n";
#endif
bool haveSeenWalkingArea = false;
for (const E* const edge : edges) {
if (edge->isTazConnector()) {
_AccessEdge* access = new _AccessEdge(myNumericalID++, edge->getID(), edge);
addEdge(access);
myDepartLookup[edge].push_back(access);
myArrivalLookup[edge].push_back(access);
} else {
const L* lane = getSidewalk<E, L>(edge);
if (lane != nullptr) {
if (edge->isWalkingArea()) {
addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, true));
myBidiLookup[edge] = std::make_pair(myEdges.back(), myEdges.back());
myDepartLookup[edge].push_back(myEdges.back());
myArrivalLookup[edge].push_back(myEdges.back());
haveSeenWalkingArea = true;
} else {
addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, true));
addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, false));
myBidiLookup[edge] = std::make_pair(myEdges[myNumericalID - 2], myEdges.back());
}
}
if (!edge->isWalkingArea()) {
_IntermodalEdge* const departConn = new _IntermodalEdge(edge->getID() + "_depart_connector", myNumericalID++, edge, "!connector");
_IntermodalEdge* const arrivalConn = new _IntermodalEdge(edge->getID() + "_arrival_connector", myNumericalID++, edge, "!connector");
addConnectors(departConn, arrivalConn, 0);
}
}
}
for (const E* const edge : edges) {
if (edge->isTazConnector() || edge->isInternal()) {
continue;
}
if (haveSeenWalkingArea) {
if (!pedestrianOnly && getSidewalk<E, L>(edge) == nullptr) {
const N* const node = edge->getToJunction();
if (myWalkingConnectorLookup.count(node) == 0) {
addEdge(new _IntermodalEdge(node->getID() + "_walking_connector", myNumericalID++, nullptr, "!connector"));
myWalkingConnectorLookup[node] = myEdges.back();
}
}
} else {
for (const N* const node : {
edge->getFromJunction(), edge->getToJunction()
}) {
if (myWalkingConnectorLookup.count(node) == 0) {
addEdge(new _IntermodalEdge(node->getID() + "_walking_connector", myNumericalID++, nullptr, "!connector"));
myWalkingConnectorLookup[node] = myEdges.back();
}
}
}
}
for (const E* const edge : edges) {
if (edge->isTazConnector()) {
_IntermodalEdge* const tazDepart = getDepartConnector(edge);
_IntermodalEdge* const tazArrive = getArrivalConnector(edge);
const E* other = edge->getOtherTazConnector();
_IntermodalEdge* const otherTazDepart = other != nullptr ? getDepartConnector(other) : tazDepart;
_IntermodalEdge* const otherTazArrive = other != nullptr ? getArrivalConnector(other) : tazArrive;
for (const E* out : edge->getSuccessors()) {
if (out->isNormal()) {
tazDepart->addSuccessor(getDepartConnector(out));
getArrivalConnector(out)->addSuccessor(otherTazArrive);
}
}
for (const E* in : edge->getPredecessors()) {
if (in->isNormal()) {
getArrivalConnector(in)->addSuccessor(tazArrive);
otherTazDepart->addSuccessor(getDepartConnector(in));
}
}
continue;
}
const L* const sidewalk = getSidewalk<E, L>(edge);
if (sidewalk == nullptr) {
continue;
}
const EdgePair& pair = getBothDirections(edge);
#ifdef IntermodalRouter_DEBUG_NETWORK
std::cout << " building connections from " << sidewalk->getID() << "\n";
#endif
if (haveSeenWalkingArea) {
const std::vector<std::pair<const L*, const E*> > outgoing = sidewalk->getOutgoingViaLanes();
bool hasWalkingArea = false;
for (const auto& target : outgoing) {
if (target.first->getEdge().isWalkingArea()) {
hasWalkingArea = true;
break;
}
}
for (const auto& target : outgoing) {
const E* const targetEdge = &(target.first->getEdge());
const bool used = (target.first == getSidewalk<E, L>(targetEdge)
&& (!hasWalkingArea || targetEdge->isWalkingArea()));
#ifdef IntermodalRouter_DEBUG_NETWORK
const L* potTarget = getSidewalk<E, L>(targetEdge);
std::cout << " lane=" << (potTarget == 0 ? "NULL" : potTarget->getID()) << (used ? "(used)" : "") << "\n";
#endif
if (used) {
const EdgePair& targetPair = getBothDirections(targetEdge);
pair.first->addSuccessor(targetPair.first);
targetPair.second->addSuccessor(pair.second);
#ifdef IntermodalRouter_DEBUG_NETWORK
std::cout << " " << pair.first->getID() << " -> " << targetPair.first->getID() << "\n";
std::cout << " " << targetPair.second->getID() << " -> " << pair.second->getID() << "\n";
#endif
}
}
}
_IntermodalEdge* const toNodeConn = myWalkingConnectorLookup[edge->getToJunction()];
if (toNodeConn != nullptr) {
const std::vector<std::pair<const L*, const E*> > outgoing = sidewalk->getOutgoingViaLanes();
double minViaLength = std::numeric_limits<double>::max();
const E* minVia = nullptr;
for (const auto& target : outgoing) {
if (target.second != nullptr && target.second->getLength() < minViaLength) {
minViaLength = target.second->getLength();
minVia = target.second;
}
}
EdgePair interVia = std::make_pair(nullptr, nullptr);
if (minVia != nullptr) {
const auto it = myBidiLookup.find(minVia);
if (it != myBidiLookup.end()) {
interVia = it->second;
}
}
if (!haveSeenWalkingArea) {
pair.first->addSuccessor(toNodeConn, interVia.first);
}
toNodeConn->addSuccessor(pair.second, interVia.second);
}
_IntermodalEdge* const fromNodeConn = myWalkingConnectorLookup[edge->getFromJunction()];
if (fromNodeConn != nullptr) {
if (!haveSeenWalkingArea) {
pair.second->addSuccessor(fromNodeConn);
}
fromNodeConn->addSuccessor(pair.first);
}
if (!edge->isWalkingArea()) {
_IntermodalEdge* startConnector = getDepartConnector(edge);
startConnector->addSuccessor(pair.first);
startConnector->addSuccessor(pair.second);
_IntermodalEdge* endConnector = getArrivalConnector(edge);
pair.first->addSuccessor(endConnector);
pair.second->addSuccessor(endConnector);
#ifdef IntermodalRouter_DEBUG_NETWORK
std::cout << " " << startConnector->getID() << " -> " << pair.first->getID() << "\n";
std::cout << " " << startConnector->getID() << " -> " << pair.second->getID() << "\n";
std::cout << " " << pair.first->getID() << " -> " << endConnector->getID() << "\n";
std::cout << " " << pair.second->getID() << " -> " << endConnector->getID() << "\n";
#endif
}
}
}
~IntermodalNetwork() {
for (typename std::vector<_IntermodalEdge*>::iterator it = myEdges.begin(); it != myEdges.end(); ++it) {
delete *it;
}
}
void addEdge(_IntermodalEdge* edge) {
while ((int)myEdges.size() <= edge->getNumericalID()) {
myEdges.push_back(0);
}
myEdges[edge->getNumericalID()] = edge;
}
void addConnectors(_IntermodalEdge* const depConn, _IntermodalEdge* const arrConn, const int index) {
addEdge(depConn);
addEdge(arrConn);
myDepartLookup[depConn->getEdge()].insert(myDepartLookup[depConn->getEdge()].begin() + index, depConn);
myArrivalLookup[arrConn->getEdge()].insert(myArrivalLookup[arrConn->getEdge()].begin() + index, arrConn);
}
const std::vector<_IntermodalEdge*>& getAllEdges() {
return myEdges;
}
const EdgePair& getBothDirections(const E* e) const {
typename std::map<const E*, EdgePair>::const_iterator it = myBidiLookup.find(e);
if (it == myBidiLookup.end()) {
assert(false);
throw ProcessError(TLF("Edge '%' not found in intermodal network.'", e->getID()));
}
return (*it).second;
}
const _IntermodalEdge* getDepartEdge(const E* e, const double pos) const {
typename std::map<const E*, std::vector<_IntermodalEdge*> >::const_iterator it = myDepartLookup.find(e);
if (it == myDepartLookup.end()) {
throw ProcessError(TLF("Depart edge '%' not found in intermodal network.", e->getID()));
}
if ((e->getPermissions() & SVC_PEDESTRIAN) == 0) {
double bestDist = std::numeric_limits<double>::max();
const _IntermodalEdge* best = nullptr;
for (const _IntermodalEdge* const split : it->second) {
if (pos >= split->getStartPos() - POSITION_EPS && pos <= split->getEndPos() + POSITION_EPS) {
const double dist = split->getEndPos() - split->getStartPos();
if (dist < bestDist) {
bestDist = dist;
best = split;
}
}
}
assert(best != nullptr);
return best;
} else {
const std::vector<_IntermodalEdge*>& splitList = it->second;
typename std::vector<_IntermodalEdge*>::const_iterator splitIt = splitList.begin();
double totalLength = 0.;
while (splitIt + 1 != splitList.end() && totalLength + (*splitIt)->getLength() < pos) {
totalLength += (*splitIt)->getLength();
++splitIt;
}
return *splitIt;
}
}
_IntermodalEdge* getDepartConnector(const E* e, const int splitIndex = 0) const {
typename std::map<const E*, std::vector<_IntermodalEdge*> >::const_iterator it = myDepartLookup.find(e);
if (it == myDepartLookup.end()) {
throw ProcessError(TLF("Depart edge '%' not found in intermodal network.", e->getID()));
}
if (splitIndex >= (int)it->second.size()) {
throw ProcessError("Split index " + toString(splitIndex) + " invalid for depart edge '" + e->getID() + "' .");
}
return it->second[splitIndex];
}
_IntermodalEdge* getArrivalEdge(const E* e, const double pos) const {
typename std::map<const E*, std::vector<_IntermodalEdge*> >::const_iterator it = myArrivalLookup.find(e);
if (it == myArrivalLookup.end()) {
throw ProcessError(TLF("Arrival edge '%' not found in intermodal network.", e->getID()));
}
const std::vector<_IntermodalEdge*>& splitList = it->second;
typename std::vector<_IntermodalEdge*>::const_iterator splitIt = splitList.begin();
double totalLength = 0.;
while (splitIt != splitList.end() && totalLength + (*splitIt)->getLength() < pos) {
totalLength += (*splitIt)->getLength();
++splitIt;
}
if (splitIt != splitList.end()) {
return *splitIt;
} else {
return splitList.back();
}
}
_IntermodalEdge* getArrivalConnector(const E* e, const int splitIndex = 0) const {
return myArrivalLookup.find(e)->second[splitIndex];
}
_IntermodalEdge* getWalkingConnector(const E* e) const {
typename std::map<const N*, _IntermodalEdge*>::const_iterator it = myWalkingConnectorLookup.find(e->getToJunction());
if (it == myWalkingConnectorLookup.end()) {
const L* const sidewalk = getSidewalk<E, L>(e);
if (e->isInternal() || sidewalk == 0) {
return 0;
}
for (const auto& target : sidewalk->getOutgoingViaLanes()) {
if (target.first->getEdge().isWalkingArea()) {
return getBothDirections(&target.first->getEdge()).first;
}
}
return 0;
}
return it->second;
}
void addCarEdges(const std::vector<E*>& edges, double taxiWait) {
for (const E* const edge : edges) {
if (edge->getFunction() == SumoXMLEdgeFunc::NORMAL || edge->getFunction() == SumoXMLEdgeFunc::INTERNAL) {
myCarLookup[edge] = new CarEdge<E, L, N, V>(myNumericalID++, edge);
addEdge(myCarLookup[edge]);
}
}
for (const auto& edgePair : myCarLookup) {
_IntermodalEdge* const carEdge = edgePair.second;
for (const auto& suc : edgePair.first->getViaSuccessors()) {
_IntermodalEdge* const sucCarEdge = getCarEdge(suc.first);
_IntermodalEdge* const sucViaEdge = getCarEdge(suc.second);
if (sucCarEdge != nullptr) {
carEdge->addSuccessor(sucCarEdge, sucViaEdge);
}
}
if (edgePair.first->getFunction() != SumoXMLEdgeFunc::NORMAL) {
continue;
}
if ((myCarWalkTransfer & ALL_JUNCTIONS) != 0) {
_IntermodalEdge* const walkCon = getWalkingConnector(edgePair.first);
if (walkCon != 0) {
carEdge->addSuccessor(walkCon);
} else {
for (const E* const out : edgePair.first->getToJunction()->getOutgoing()) {
if (!out->isInternal() && !out->isTazConnector() && getSidewalk<E, L>(out) != 0) {
carEdge->addSuccessor(getBothDirections(out).first);
}
}
for (const E* const in : edgePair.first->getToJunction()->getIncoming()) {
if (!in->isInternal() && !in->isTazConnector() && getSidewalk<E, L>(in) != 0) {
carEdge->addSuccessor(getBothDirections(in).second);
}
}
}
}
if ((myCarWalkTransfer & ALL_JUNCTIONS) == 0 && (myCarWalkTransfer & TAXI_DROPOFF_ANYWHERE) != 0) {
_IntermodalEdge* const walkCon = getWalkingConnector(edgePair.first);
if (walkCon != 0) {
addRestrictedCarExit(carEdge, walkCon, SVC_TAXI);
} else {
for (const E* const out : edgePair.first->getToJunction()->getOutgoing()) {
if (!out->isInternal() && !out->isTazConnector() && getSidewalk<E, L>(out) != 0) {
addRestrictedCarExit(carEdge, getBothDirections(out).first, SVC_TAXI);
}
}
for (const E* const in : edgePair.first->getToJunction()->getIncoming()) {
if (!in->isInternal() && !in->isTazConnector() && getSidewalk<E, L>(in) != 0) {
addRestrictedCarExit(carEdge, getBothDirections(in).second, SVC_TAXI);
}
}
}
}
_IntermodalEdge* departConn = getDepartConnector(edgePair.first);
_AccessEdge* access = new _AccessEdge(myNumericalID++, departConn, carEdge, 0, (SVCAll & ~SVC_TAXI));
addEdge(access);
departConn->addSuccessor(access);
access->addSuccessor(carEdge);
if ((myCarWalkTransfer & TAXI_PICKUP_PT) == 0) {
_AccessEdge* taxiAccess = new _AccessEdge(myNumericalID++, departConn, carEdge, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
addEdge(taxiAccess);
departConn->addSuccessor(taxiAccess);
taxiAccess->addSuccessor(carEdge);
}
if ((myCarWalkTransfer & TAXI_DROPOFF_PT) == 0) {
carEdge->addSuccessor(getArrivalConnector(edgePair.first));
} else {
addRestrictedCarExit(carEdge, getArrivalConnector(edgePair.first), (SVCAll & ~SVC_TAXI));
}
}
}
_IntermodalEdge* getCarEdge(const E* e) const {
if (e == nullptr) {
return nullptr;
}
auto it = myCarLookup.find(e);
if (it == myCarLookup.end()) {
return nullptr;
}
return it->second;
}
_IntermodalEdge* getStopEdge(const std::string& stopId) const {
auto it = myStopConnections.find(stopId);
if (it == myStopConnections.end()) {
return nullptr;
}
return it->second;
}
void addAccess(const std::string& stopId, const E* stopEdge, const double startPos, const double endPos, const double length, const SumoXMLTag category, bool isAccess, double taxiWait) {
assert(stopEdge != nullptr);
const bool transferCarWalk = ((category == SUMO_TAG_PARKING_AREA && (myCarWalkTransfer & PARKING_AREAS) != 0) ||
(category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & PT_STOPS) != 0));
const bool transferTaxiWalk = (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & TAXI_DROPOFF_PT) != 0);
const bool transferWalkTaxi = (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & TAXI_PICKUP_PT) != 0);
const double pos = (startPos + endPos) / 2.;
#ifdef IntermodalRouter_DEBUG_ACCESS
std::cout << "addAccess stopId=" << stopId << " stopEdge=" << stopEdge->getID() << " pos=" << pos << " length=" << length << " tag=" << toString(category)
<< " access=" << isAccess << " tWait=" << taxiWait << "\n";
#endif
if (myStopConnections.count(stopId) == 0) {
myStopConnections[stopId] = new StopEdge<E, L, N, V>(stopId, myNumericalID++, stopEdge, startPos, endPos);
addEdge(myStopConnections[stopId]);
}
_IntermodalEdge* const stopConn = myStopConnections[stopId];
const L* lane = getSidewalk<E, L>(stopEdge);
if (lane != nullptr) {
const std::pair<_IntermodalEdge*, _IntermodalEdge*>& pair = getBothDirections(stopEdge);
double relPos;
bool needSplit;
const int splitIndex = findSplitIndex(pair.first, pos, relPos, needSplit);
_IntermodalEdge* const fwdSplit = needSplit ? new PedestrianEdge<E, L, N, V>(myNumericalID++, stopEdge, lane, true, pos) : nullptr;
splitEdge(pair.first, splitIndex, fwdSplit, relPos, length, needSplit, stopConn);
_IntermodalEdge* const backSplit = needSplit ? new PedestrianEdge<E, L, N, V>(myNumericalID++, stopEdge, lane, false, pos) : nullptr;
splitEdge(pair.second, splitIndex, backSplit, relPos, length, needSplit, stopConn, false);
_IntermodalEdge* carSplit = nullptr;
if (myCarLookup.count(stopEdge) > 0) {
if (needSplit) {
carSplit = new CarEdge<E, L, N, V>(myNumericalID++, stopEdge, pos);
}
splitEdge(myCarLookup[stopEdge], splitIndex, carSplit, relPos, length, needSplit, stopConn, true, false, transferCarWalk);
}
if (needSplit) {
if (carSplit != nullptr && (transferCarWalk || transferTaxiWalk)) {
_IntermodalEdge* const beforeSplit = myAccessSplits[myCarLookup[stopEdge]][splitIndex];
for (_IntermodalEdge* conn : {
fwdSplit, backSplit
}) {
if (transferCarWalk) {
_AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, conn, length);
addEdge(access);
beforeSplit->addSuccessor(access);
access->addSuccessor(conn);
} else if (transferTaxiWalk) {
addRestrictedCarExit(beforeSplit, stopConn, SVC_TAXI);
}
}
}
if (carSplit != nullptr && transferWalkTaxi && !isAccess) {
_AccessEdge* access = new _AccessEdge(myNumericalID++, stopConn, carSplit, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
addEdge(access);
stopConn->addSuccessor(access);
access->addSuccessor(carSplit);
}
_IntermodalEdge* const prevDep = getDepartConnector(stopEdge, splitIndex);
const std::vector<_IntermodalEdge*>& backSplitList = myAccessSplits[pair.second];
_IntermodalEdge* const backBeforeSplit = backSplitList[backSplitList.size() - 2 - splitIndex];
_IntermodalEdge* const depConn = new _IntermodalEdge(stopEdge->getID() + "_depart_connector" + toString(pos), myNumericalID++, stopEdge, "!connector");
depConn->addSuccessor(fwdSplit);
depConn->addSuccessor(backBeforeSplit);
depConn->setLength(fwdSplit->getLength());
prevDep->removeSuccessor(backBeforeSplit);
prevDep->addSuccessor(backSplit);
prevDep->setLength(backSplit->getLength());
if (carSplit != nullptr) {
depConn->addSuccessor(carSplit);
}
_IntermodalEdge* const prevArr = getArrivalConnector(stopEdge, splitIndex);
_IntermodalEdge* const fwdBeforeSplit = myAccessSplits[pair.first][splitIndex];
_IntermodalEdge* const arrConn = new _IntermodalEdge(stopEdge->getID() + "_arrival_connector" + toString(pos), myNumericalID++, stopEdge, "!connector");
fwdSplit->addSuccessor(arrConn);
backBeforeSplit->addSuccessor(arrConn);
arrConn->setLength(fwdSplit->getLength());
fwdSplit->removeSuccessor(prevArr);
fwdBeforeSplit->addSuccessor(prevArr);
prevArr->setLength(backSplit->getLength());
if (carSplit != nullptr) {
if (carSplit->removeSuccessor(prevArr)) {
carSplit->addSuccessor(arrConn);
myAccessSplits[myCarLookup[stopEdge]][splitIndex]->addSuccessor(prevArr);
}
}
addConnectors(depConn, arrConn, splitIndex + 1);
}
} else {
std::vector<_IntermodalEdge*>& splitList = myDepartLookup[stopEdge];
assert(splitList.size() > 0);
typename std::vector<_IntermodalEdge*>::iterator splitIt = splitList.begin();
while (splitIt != splitList.end() && startPos > (*splitIt)->getEndPos()) {
++splitIt;
}
splitList.insert(splitIt, stopConn);
if (!isAccess && (transferWalkTaxi || transferCarWalk || transferTaxiWalk)) {
_IntermodalEdge* carEdge = myCarLookup[stopEdge];
double relPos;
bool needSplit;
const int splitIndex = findSplitIndex(carEdge, pos, relPos, needSplit);
if (needSplit) {
_IntermodalEdge* carSplit = new CarEdge<E, L, N, V>(myNumericalID++, stopEdge, pos);
splitEdge(carEdge, splitIndex, carSplit, relPos, length, needSplit, stopConn, true, false, false);
if (transferCarWalk || transferTaxiWalk) {
_IntermodalEdge* const beforeSplit = myAccessSplits[myCarLookup[stopEdge]][splitIndex];
if (transferCarWalk) {
_AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, stopConn, length);
addEdge(access);
beforeSplit->addSuccessor(access);
access->addSuccessor(stopConn);
} else if (transferTaxiWalk) {
addRestrictedCarExit(beforeSplit, stopConn, SVC_TAXI);
}
}
if (transferWalkTaxi) {
_AccessEdge* access = new _AccessEdge(myNumericalID++, stopConn, carSplit, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
addEdge(access);
stopConn->addSuccessor(access);
access->addSuccessor(carSplit);
}
}
}
}
}
void addSchedule(const SUMOVehicleParameter& pars, const StopParVector* addStops = nullptr) {
SUMOTime lastUntil = 0;
StopParVector validStops;
if (addStops != nullptr) {
for (const SUMOVehicleParameter::Stop& stop : *addStops) {
if (myStopConnections.count(stop.busstop) > 0) {
const SUMOTime newUntil = stop.until + pars.depart;
if (newUntil >= lastUntil) {
validStops.push_back(stop);
validStops.back().until = newUntil;
lastUntil = newUntil;
} else {
WRITE_WARNINGF(TL("Ignoring unordered stop at '%' until % for vehicle '%'."), stop.busstop, time2string(stop.until), pars.id);
}
}
}
}
for (const SUMOVehicleParameter::Stop& stop : pars.stops) {
if (myStopConnections.count(stop.busstop) > 0 && stop.until >= lastUntil) {
validStops.push_back(stop);
lastUntil = stop.until;
} else {
if (stop.busstop != "" && stop.until >= 0) {
WRITE_WARNINGF(TL("Ignoring stop at '%' until % for vehicle '%'."), stop.busstop, time2string(stop.until), pars.id);
}
}
}
if (validStops.size() < 2 && pars.line != "taxi") {
WRITE_WARNINGF(TL("Not using public transport line '%' for routing persons. It has less than two usable stops."), pars.line);
return;
}
typename std::vector<_PTEdge*>& lineEdges = myPTLines[pars.line];
if (lineEdges.empty()) {
_IntermodalEdge* lastStop = nullptr;
Position lastPos;
SUMOTime lastTime = 0;
for (const SUMOVehicleParameter::Stop& s : validStops) {
_IntermodalEdge* currStop = myStopConnections[s.busstop];
Position stopPos = E::getStopPosition(s);
if (lastStop != nullptr) {
_PTEdge* const newEdge = new _PTEdge(s.busstop, myNumericalID++, lastStop, currStop->getEdge(), pars.line, lastPos.distanceTo(stopPos));
addEdge(newEdge);
newEdge->addSchedule(pars.id, lastTime, pars.repetitionNumber, pars.repetitionOffset, s.until - lastTime);
lastStop->addSuccessor(newEdge);
newEdge->addSuccessor(currStop);
lineEdges.push_back(newEdge);
}
lastTime = s.until;
lastStop = currStop;
lastPos = stopPos;
}
if (pars.line != "taxi" && validStops.front().busstop == validStops.back().busstop) {
myLoopedLines.insert(pars.line);
}
} else {
if (validStops.size() != lineEdges.size() + 1) {
WRITE_WARNINGF("Number of stops for public transport line '%' does not match earlier definitions, ignoring schedule.", pars.line);
return;
}
if (lineEdges.front()->getEntryStop() != myStopConnections[validStops.front().busstop]) {
WRITE_WARNINGF("Different stop for '%' compared to earlier definitions, ignoring schedule.", pars.line);
return;
}
typename std::vector<_PTEdge*>::const_iterator lineEdge = lineEdges.begin();
typename StopParVector::const_iterator s = validStops.begin() + 1;
for (; s != validStops.end(); ++s, ++lineEdge) {
if ((*lineEdge)->getSuccessors(SVC_IGNORING)[0] != myStopConnections[s->busstop]) {
WRITE_WARNINGF("Different stop for '%' compared to earlier definitions, ignoring schedule.", pars.line);
return;
}
}
SUMOTime lastTime = validStops.front().until;
if (lineEdges.front()->hasSchedule(lastTime)) {
WRITE_WARNINGF("Duplicate schedule for '%' at time=%.", pars.line, time2string(lastTime));
}
for (lineEdge = lineEdges.begin(), s = validStops.begin() + 1; lineEdge != lineEdges.end(); ++lineEdge, ++s) {
(*lineEdge)->addSchedule(pars.id, lastTime, pars.repetitionNumber, pars.repetitionOffset, s->until - lastTime);
lastTime = s->until;
}
}
}
void addCarAccess(const E* edge, SUMOVehicleClass svc, double traveltime) {
assert(edge != nullptr);
assert(myCarLookup.count(edge) != 0);
assert(myBidiLookup.count(edge) != 0);
EdgePair pedestrianEdges = myBidiLookup[edge];
_IntermodalEdge* carEdge = myCarLookup[edge];
_AccessEdge* access = new _AccessEdge(myNumericalID++, pedestrianEdges.first, carEdge, 0, svc, SVC_IGNORING, traveltime);
addEdge(access);
pedestrianEdges.first->addSuccessor(access);
pedestrianEdges.second->addSuccessor(access);
access->addSuccessor(carEdge);
}
void addRestrictedCarExit(_IntermodalEdge* from, _IntermodalEdge* to, SVCPermissions vehicleRestriction) {
_AccessEdge* access = new _AccessEdge(myNumericalID++, from, to, 0, SVC_IGNORING, vehicleRestriction);
addEdge(access);
from->addSuccessor(access);
access->addSuccessor(to);
}
bool isLooped(const std::string lineID) const {
return myLoopedLines.count(lineID) != 0;
}
private:
int findSplitIndex(_IntermodalEdge* const toSplit, const double pos, double& relPos, bool& needSplit) const {
relPos = pos;
needSplit = true;
int splitIndex = 0;
const auto& splitList = myAccessSplits.find(toSplit);
if (splitList != myAccessSplits.end() && !splitList->second.empty()) {
for (const _IntermodalEdge* const split : splitList->second) {
if (relPos < split->getLength() + POSITION_EPS) {
break;
}
relPos -= split->getLength();
splitIndex++;
}
assert(splitIndex < (int)splitList->second.size());
if (splitIndex + 1 < (int)splitList->second.size() && fabs(relPos - splitList->second[splitIndex]->getLength()) < POSITION_EPS) {
needSplit = false;
}
}
return splitIndex;
}
void splitEdge(_IntermodalEdge* const toSplit, int splitIndex,
_IntermodalEdge* afterSplit, const double relPos, const double length, const bool needSplit,
_IntermodalEdge* const stopConn, const bool forward = true, const bool addExit = true, const bool addEntry = true) {
std::vector<_IntermodalEdge*>& splitList = myAccessSplits[toSplit];
if (splitList.empty()) {
splitList.push_back(toSplit);
}
if (!forward) {
splitIndex = (int)splitList.size() - 1 - splitIndex;
if (!needSplit) {
splitIndex--;
}
}
_IntermodalEdge* beforeSplit = splitList[splitIndex];
if (needSplit) {
addEdge(afterSplit);
beforeSplit->transferSuccessors(afterSplit);
beforeSplit->addSuccessor(afterSplit);
if (forward) {
afterSplit->setLength(MAX2(0.0, beforeSplit->getLength() - relPos));
beforeSplit->setLength(relPos);
} else {
afterSplit->setLength(relPos);
beforeSplit->setLength(MAX2(0.0, beforeSplit->getLength() - relPos));
const std::string newID = beforeSplit->getID();
beforeSplit->setID(afterSplit->getID());
afterSplit->setID(newID);
}
splitList.insert(splitList.begin() + splitIndex + 1, afterSplit);
} else {
afterSplit = splitList[splitIndex + 1];
}
if (addEntry) {
_AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, stopConn, length);
addEdge(access);
beforeSplit->addSuccessor(access);
access->addSuccessor(stopConn);
}
if (addExit) {
_AccessEdge* exit = new _AccessEdge(myNumericalID++, stopConn, afterSplit, length);
addEdge(exit);
stopConn->addSuccessor(exit);
exit->addSuccessor(afterSplit);
}
}
private:
std::vector<_IntermodalEdge*> myEdges;
std::map<const E*, EdgePair> myBidiLookup;
std::map<const E*, std::vector<_IntermodalEdge*> > myDepartLookup;
std::map<const E*, std::vector<_IntermodalEdge*> > myArrivalLookup;
std::map<const N*, _IntermodalEdge*> myWalkingConnectorLookup;
std::map<const E*, _IntermodalEdge*, ComparatorNumericalIdLess> myCarLookup;
std::map<std::string, std::vector<_PTEdge*> > myPTLines;
std::map<std::string, _IntermodalEdge*> myStopConnections;
std::map<_IntermodalEdge*, std::vector<_IntermodalEdge*> > myAccessSplits;
std::set<std::string > myLoopedLines;
int myNumericalID;
const int myCarWalkTransfer;
private:
IntermodalNetwork& operator=(const IntermodalNetwork& s);
};