#include <utils/iodevices/OutputDevice.h>
#include <utility>
#include <utils/common/ToString.h>
#include <utils/common/StringUtils.h>
#include <utils/common/MsgHandler.h>
#include "NBEdge.h"
#include "NBEdgeCont.h"
#include "NBPTStop.h"
#include "NBPTStopCont.h"
#include "NBPTLine.h"
NBPTLine::NBPTLine(const std::string& id, const std::string& name, const std::string& type, const std::string& ref, int interval, const std::string& nightService,
SUMOVehicleClass vClass, RGBColor color) :
myName(name),
myType(type),
myPTLineId(id),
myRef(ref != "" ? ref : name),
myColor(color),
myInterval(interval),
myNightService(nightService),
myVClass(vClass),
myNumOfStops(0),
myMissingStopsBefore(0),
myMissingStopsAfter(0)
{ }
void
NBPTLine::addPTStop(std::shared_ptr<NBPTStop> pStop) {
if (!myPTStops.empty() && pStop->getName() != "" && myPTStops.back()->getName() == pStop->getName()) {
if (myPTStops.back()->isPlatform() && !pStop->isPlatform()) {
myPTStops.pop_back();
} else if (pStop->isPlatform()) {
return;
}
}
myPTStops.push_back(pStop);
}
const std::vector<std::shared_ptr<NBPTStop> >&
NBPTLine::getStops() {
return myPTStops;
}
void
NBPTLine::write(OutputDevice& device) {
device.openTag(SUMO_TAG_PT_LINE);
device.writeAttr(SUMO_ATTR_ID, myPTLineId);
if (!myName.empty()) {
device.writeAttr(SUMO_ATTR_NAME, StringUtils::escapeXML(myName));
}
device.writeAttr(SUMO_ATTR_LINE, StringUtils::escapeXML(myRef));
device.writeAttr(SUMO_ATTR_TYPE, myType);
device.writeAttr(SUMO_ATTR_VCLASS, toString(myVClass));
if (myInterval > 0) {
device.writeAttr(SUMO_ATTR_PERIOD, 60 * myInterval);
}
if (myNightService != "") {
device.writeAttr("nightService", myNightService);
}
if (myColor.isValid()) {
device.writeAttr(SUMO_ATTR_COLOR, myColor);
}
device.writeAttr("completeness", (double)myPTStops.size() / myNumOfStops);
if (myMissingStopsBefore != 0) {
device.writeAttr("missingBefore", myMissingStopsBefore);
}
if (myMissingStopsAfter != 0) {
device.writeAttr("missingAfter", myMissingStopsAfter);
}
if (!myRoute.empty()) {
device.openTag(SUMO_TAG_ROUTE);
device.writeAttr(SUMO_ATTR_EDGES, myRoute);
device.closeTag();
}
for (auto& myPTStop : myPTStops) {
device.openTag(SUMO_TAG_BUS_STOP);
device.writeAttr(SUMO_ATTR_ID, myPTStop->getID());
device.writeAttr(SUMO_ATTR_NAME, StringUtils::escapeXML(myPTStop->getName()));
device.closeTag();
}
device.closeTag();
}
void
NBPTLine::addWayNode(long long int way, long long int node) {
std::string wayStr = toString(way);
if (wayStr != myCurrentWay) {
myCurrentWay = wayStr;
myWays.push_back(wayStr);
}
myWayNodes[wayStr].push_back(node);
}
const std::vector<long long int>*
NBPTLine::getWayNodes(std::string wayId) {
if (myWayNodes.find(wayId) != myWayNodes.end()) {
return &myWayNodes[wayId];
}
return nullptr;
}
void
NBPTLine::setEdges(const std::vector<NBEdge*>& edges) {
myRoute = edges;
for (NBEdge* e : edges) {
SVCPermissions permissions = e->getPermissions();
if ((permissions & myVClass) != myVClass) {
SVCPermissions nVuln = ~(SVC_PEDESTRIAN | SVC_BICYCLE);
if (permissions != 0 && (permissions & nVuln) == 0) {
e->addRestrictedLane(SUMO_const_laneWidth, myVClass);
} else {
for (int i = 0; i < (int)e->getNumLanes(); i++) {
if ((e->getPermissions(i) & nVuln) != 0) {
e->allowVehicleClass(i, myVClass);
break;
}
}
}
}
}
}
void
NBPTLine::setNumOfStops(int numStops, int missingBefore, int missingAfter) {
myNumOfStops = numStops;
myMissingStopsBefore = missingBefore;
myMissingStopsAfter = missingAfter;
}
const std::vector<NBEdge*>&
NBPTLine::getRoute() const {
return myRoute;
}
std::vector<std::pair<NBEdge*, std::string> >
NBPTLine::getStopEdges(const NBEdgeCont& ec) const {
std::vector<std::pair<NBEdge*, std::string> > result;
for (std::shared_ptr<NBPTStop> stop : myPTStops) {
NBEdge* e = ec.retrieve(stop->getEdgeId());
if (e != nullptr) {
result.push_back({e, stop->getID()});
}
}
return result;
}
NBEdge*
NBPTLine::getRouteStart(const NBEdgeCont& ec) const {
std::vector<NBEdge*> validEdges;
for (NBEdge* e : myRoute) {
if (ec.retrieve(e->getID())) {
validEdges.push_back(e);
}
}
if (validEdges.size() == 0) {
return nullptr;
}
if (myPTStops.size() > 0) {
NBEdge* firstStopEdge = ec.retrieve(myPTStops.front()->getEdgeId());
if (firstStopEdge == nullptr) {
WRITE_WARNINGF(TL("Could not retrieve edge '%' for first stop of line '%'."), myPTStops.front()->getEdgeId(), myPTLineId);
return nullptr;
}
auto it = std::find(validEdges.begin(), validEdges.end(), firstStopEdge);
if (it == validEdges.end()) {
WRITE_WARNINGF(TL("First stop edge '%' is not part of the route of line '%'."), firstStopEdge->getID(), myPTLineId);
return nullptr;
}
}
return validEdges.front();
}
NBEdge*
NBPTLine::getRouteEnd(const NBEdgeCont& ec) const {
std::vector<NBEdge*> validEdges;
for (NBEdge* e : myRoute) {
if (ec.retrieve(e->getID())) {
validEdges.push_back(e);
}
}
if (validEdges.size() == 0) {
return nullptr;
}
if (myPTStops.size() > 0) {
NBEdge* lastStopEdge = ec.retrieve(myPTStops.back()->getEdgeId());
if (lastStopEdge == nullptr) {
WRITE_WARNINGF(TL("Could not retrieve edge '%' for last stop of line '%'."), myPTStops.back()->getEdgeId(), myPTLineId);
return nullptr;
}
auto it = std::find(validEdges.begin(), validEdges.end(), lastStopEdge);
if (it == validEdges.end()) {
WRITE_WARNINGF(TL("Last stop edge '%' is not part of the route of line '%'."), lastStopEdge->getID(), myPTLineId);
return nullptr;
}
}
return validEdges.back();
}
bool
NBPTLine::isConsistent(std::vector<NBEdge*> stops) const {
if (myRoute.empty() || stops.empty()) {
return true;
}
if (stops.size() > 1 && stops.front() == stops.back()) {
if (myRoute.front() == stops.front()) {
stops.pop_back();
} else if (myRoute.back() == stops.back()) {
stops.erase(stops.begin());
}
}
std::vector<NBEdge*>::const_iterator stopIt = stops.begin();
for (const NBEdge* const e : myRoute) {
while (stopIt != stops.end() && e == *stopIt) {
++stopIt;
}
if (stopIt == stops.end()) {
return true;
}
}
return false;
}
void
NBPTLine::replaceStop(std::shared_ptr<NBPTStop> oldStop, std::shared_ptr<NBPTStop> newStop) {
for (int i = 0; i < (int)myPTStops.size(); i++) {
if (myPTStops[i] == oldStop) {
myPTStops[i] = newStop;
}
}
}
void
NBPTLine::replaceEdge(const std::string& edgeID, const EdgeVector& replacement) {
EdgeVector oldRoute = myRoute;
myRoute.clear();
for (NBEdge* e : oldRoute) {
if (e->getID() == edgeID) {
for (NBEdge* e2 : replacement) {
if (myRoute.empty() || myRoute.back() != e2) {
myRoute.push_back(e2);
}
}
} else {
myRoute.push_back(e);
}
}
}
void
NBPTLine::deleteInvalidStops(const NBEdgeCont& ec, const NBPTStopCont& sc) {
for (auto it = myPTStops.begin(); it != myPTStops.end();) {
std::shared_ptr<NBPTStop> stop = *it;
if (sc.get(stop->getID()) == nullptr ||
ec.getByID(stop->getEdgeId()) == nullptr) {
WRITE_WARNINGF(TL("Removed invalid stop '%' from line '%'."), stop->getID(), getLineID());
it = myPTStops.erase(it);
} else {
it++;
}
}
}
void
NBPTLine::deleteDuplicateStops() {
long long int lastAreaID = -1;
std::string lastName = "";
for (auto it = myPTStops.begin(); it != myPTStops.end();) {
std::shared_ptr<NBPTStop> stop = *it;
if (lastAreaID != -1 && stop->getAreaID() == lastAreaID) {
WRITE_WARNINGF(TL("Removed duplicate stop '%' at area '%' from line '%'."), stop->getID(), toString(lastAreaID), getLineID());
it = myPTStops.erase(it);
} else if (lastName != "" && stop->getName() == lastName) {
WRITE_WARNINGF(TL("Removed duplicate stop '%' named '%' from line '%'."), stop->getID(), lastName, getLineID());
it = myPTStops.erase(it);
} else {
it++;
}
lastAreaID = stop->getAreaID();
lastName = stop->getName();
}
}
void
NBPTLine::removeInvalidEdges(const NBEdgeCont& ec) {
for (int i = 0; i < (int)myRoute.size();) {
const std::pair<NBEdge*, NBEdge*>* split = ec.getSplit(myRoute[i]);
if (split != nullptr) {
myRoute[i] = split->first;
myRoute.insert(myRoute.begin() + i + 1, split->second);
} else if (ec.retrieve(myRoute[i]->getID()) == nullptr) {
myRoute.erase(myRoute.begin() + i);
} else {
i++;
}
}
}