#include <config.h>
#include <utils/iodevices/OutputDevice.h>
#include <utils/common/StringUtils.h>
#include "NBEdge.h"
#include "NBEdgeCont.h"
#include "NBPTPlatform.h"
#include "NBPTStop.h"
NBPTStop::NBPTStop(std::string ptStopId, Position position, std::string edgeId, std::string origEdgeId, double length,
std::string name, SVCPermissions svcPermissions, double parkingLength, const RGBColor color, double givenStartPos) :
myPTStopId(ptStopId),
myPosition(position),
myEdgeId(edgeId),
myOrigEdgeId(origEdgeId),
myPTStopLength(length),
myName(name),
myParkingLength(parkingLength),
myColor(color),
myPermissions(svcPermissions),
myStartPos(0),
myEndPos(0),
myBidiStop(std::weak_ptr<NBPTStop>()),
myIsLoose(origEdgeId == ""),
myIsPlatform(false),
myIsMultipleStopPositions(false),
myAreaID(-1),
myGivenStartPos(givenStartPos) {
}
std::string
NBPTStop::getID() const {
return myPTStopId;
}
const std::string
NBPTStop::getOrigEdgeId() const {
return myOrigEdgeId;
}
const std::string&
NBPTStop::getEdgeId() const {
return myEdgeId;
}
const std::string
NBPTStop::getName() const {
return myName;
}
const Position&
NBPTStop::getPosition() const {
return myPosition;
}
void
NBPTStop::mirrorX() {
myPosition.mul(1, -1);
}
void
NBPTStop::addLine(const std::string& line) {
const std::string l = StringUtils::escapeXML(line);
if (std::find(myLines.begin(), myLines.end(), l) == myLines.end()) {
myLines.push_back(l);
}
}
void
NBPTStop::write(OutputDevice& device) {
device.openTag(SUMO_TAG_BUS_STOP);
device.writeAttr(SUMO_ATTR_ID, myPTStopId);
if (!myName.empty()) {
device.writeAttr(SUMO_ATTR_NAME, StringUtils::escapeXML(myName));
}
device.writeAttr(SUMO_ATTR_LANE, myLaneId);
device.writeAttr(SUMO_ATTR_STARTPOS, myStartPos);
device.writeAttr(SUMO_ATTR_ENDPOS, myEndPos);
device.writeAttr(SUMO_ATTR_FRIENDLY_POS, "true");
if (myLines.size() > 0) {
device.writeAttr(SUMO_ATTR_LINES, toString(myLines));
}
if (myParkingLength > 0) {
device.writeAttr(SUMO_ATTR_PARKING_LENGTH, myParkingLength);
}
if (myColor.isValid()) {
device.writeAttr(SUMO_ATTR_COLOR, myColor);
}
if (!myAccesses.empty()) {
std::sort(myAccesses.begin(), myAccesses.end());
for (auto tuple : myAccesses) {
device.openTag(SUMO_TAG_ACCESS);
device.writeAttr(SUMO_ATTR_LANE, std::get<0>(tuple));
device.writeAttr(SUMO_ATTR_POSITION, std::get<1>(tuple));
device.writeAttr(SUMO_ATTR_LENGTH, std::get<2>(tuple));
device.writeAttr(SUMO_ATTR_FRIENDLY_POS, true);
device.closeTag();
}
}
writeParams(device);
device.closeTag();
}
void
NBPTStop::reshiftPosition(const double offsetX, const double offsetY) {
myPosition.add(offsetX, offsetY, 0);
for (NBPTPlatform& platformCand : myPlatformCands) {
platformCand.reshiftPosition(offsetX, offsetY);
}
}
SVCPermissions
NBPTStop::getPermissions() const {
return myPermissions;
}
void
NBPTStop::addPlatformCand(NBPTPlatform platform) {
myPlatformCands.push_back(platform);
}
const std::vector<NBPTPlatform>&
NBPTStop::getPlatformCands() {
return myPlatformCands;
}
bool
NBPTStop::getIsMultipleStopPositions() const {
return myIsMultipleStopPositions;
}
void
NBPTStop::setIsMultipleStopPositions(bool multipleStopPositions, long long int areaID) {
myIsMultipleStopPositions = multipleStopPositions;
myAreaID = areaID;
}
double
NBPTStop::getLength() const {
return myPTStopLength;
}
bool
NBPTStop::setEdgeId(std::string edgeId, const NBEdgeCont& ec) {
myEdgeId = edgeId;
return findLaneAndComputeBusStopExtent(ec);
}
void
NBPTStop::registerAdditionalEdge(std::string wayId, std::string edgeId) {
myAdditionalEdgeCandidates[wayId] = edgeId;
}
bool
NBPTStop::findLaneAndComputeBusStopExtent(const NBEdgeCont& ec) {
NBEdge* edge = ec.getByID(myEdgeId);
return findLaneAndComputeBusStopExtent(edge);
}
bool
NBPTStop::findLaneAndComputeBusStopExtent(const NBEdge* edge) {
if (edge != nullptr) {
myEdgeId = edge->getID();
int laneNr = -1;
for (const auto& it : edge->getLanes()) {
if ((it.permissions & getPermissions()) == getPermissions()) {
++laneNr;
break;
}
laneNr++;
}
if (laneNr != -1) {
myLaneId = edge->getLaneID(laneNr);
const PositionVector& shape = edge->getLaneShape(laneNr);
double offset = shape.nearest_offset_to_point2D(getPosition(), false);
const double edgeLength = edge->getFinalLength();
offset *= edgeLength / shape.length2D();
if (wasLoaded()) {
myStartPos = myGivenStartPos;
myEndPos = myStartPos + myPTStopLength;
} else {
myStartPos = MAX2(0.0, offset - myPTStopLength / 2.);
myEndPos = MIN2(myStartPos + myPTStopLength, edgeLength);
double missing = myPTStopLength - (myEndPos - myStartPos);
if (missing > 0) {
myStartPos = MAX2(0.0, myStartPos - missing);
}
}
return true;
}
}
return myEdgeId == "";
}
void
NBPTStop::clearAccess() {
myAccesses.clear();
}
void
NBPTStop::addAccess(std::string laneID, double offset, double length) {
const std::string newEdgeID = SUMOXMLDefinitions::getEdgeIDFromLane(laneID);
for (auto it = myAccesses.begin(); it != myAccesses.end();) {
if (SUMOXMLDefinitions::getEdgeIDFromLane(std::get<0>(*it)) == newEdgeID) {
it = myAccesses.erase(it);
} else {
it++;
}
}
myAccesses.push_back(std::make_tuple(laneID, offset, length));
}
bool
NBPTStop::replaceEdge(const std::string& edgeID, const EdgeVector& replacement) {
if (myEdgeId == edgeID) {
double bestDist = std::numeric_limits<double>::max();
NBEdge* bestEdge = nullptr;
for (NBEdge* cand : replacement) {
if (myPermissions == 0 || (cand->getPermissions() & myPermissions) != 0) {
const double dist = cand->getGeometry().distance2D(myPosition) + MAX2(0., myPTStopLength - cand->getLoadedLength());
if (dist < bestDist) {
bestDist = dist;
bestEdge = cand;
}
}
}
if (bestEdge != nullptr) {
if ((bestEdge->getPermissions() & SVC_PEDESTRIAN) != 0) {
clearAccess();
}
return findLaneAndComputeBusStopExtent(bestEdge);
}
return false;
}
return true;
}