#include <config.h>
#include <string>
#include <utils/common/MsgHandler.h>
#include <utils/common/RGBColor.h>
#include <utils/geom/PositionVector.h>
#include <utils/geom/Boundary.h>
#include <utils/gui/div/GLHelper.h>
#include <utils/common/ToString.h>
#include <microsim/MSNet.h>
#include <microsim/MSLane.h>
#include <microsim/MSEdge.h>
#include <microsim/transportables/MSTransportable.h>
#include <microsim/transportables/MSStageDriving.h>
#include "GUINet.h"
#include "GUIEdge.h"
#include <utils/gui/globjects/GUIGLObjectPopupMenu.h>
#include <utils/gui/windows/GUIAppEnum.h>
#include <gui/GUIGlobals.h>
#include <utils/gui/div/GUIParameterTableWindow.h>
#include <gui/GUIApplicationWindow.h>
#include <microsim/logging/FunctionBinding.h>
#include <utils/gui/div/GUIGlobalSelection.h>
#include <foreign/fontstash/fontstash.h>
#include <utils/geom/GeomHelper.h>
#include <guisim/GUIBusStop.h>
#include <utils/common/MsgHandler.h>
#include <utils/gui/globjects/GLIncludes.h>
GUIBusStop::GUIBusStop(const std::string& id, SumoXMLTag element, const std::vector<std::string>& lines, MSLane& lane,
double frompos, double topos, const std::string name, int personCapacity,
double parkingLength, const RGBColor& color, double angle) :
MSStoppingPlace(id, element, lines, lane, frompos, topos, name, personCapacity, parkingLength, color, angle),
GUIGlObject_AbstractAdd(GLO_BUS_STOP, id, GUIIconSubSys::getIcon(GUIIcon::BUSSTOP)),
myEmptyColor(RGBColor::INVISIBLE) {
myWidth = MAX2(1.0, ceil((double)myTransportableCapacity / getTransportablesAbreast()) * myTransportableDepth);
initShape(myFGShape, myFGShapeRotations, myFGShapeLengths, myFGSignPos, myFGSignRot);
if (myLane.getShape(true).size() > 0) {
initShape(myFGShape2, myFGShapeRotations2, myFGShapeLengths2, myFGSignPos2, myFGSignRot2, true);
}
}
GUIBusStop::~GUIBusStop() {}
void
GUIBusStop::initShape(PositionVector& fgShape,
std::vector<double>& fgShapeRotations, std::vector<double>& fgShapeLengths,
Position& fgSignPos, double& fgSignRot,
bool secondaryShape) {
const double offsetSign = MSGlobals::gLefthand ? -1 : 1;
const double lgf = myLane.getLengthGeometryFactor(secondaryShape);
fgShape = myLane.getShape(secondaryShape);
fgShape = fgShape.getSubpart(lgf * myBegPos, lgf * myEndPos);
fgShape.move2side(((myLane.getWidth() + myWidth) * 0.5 - 0.2) * offsetSign);
fgShapeRotations.reserve(fgShape.size() - 1);
fgShapeLengths.reserve(fgShape.size() - 1);
int e = (int) fgShape.size() - 1;
for (int i = 0; i < e; ++i) {
const Position& f = fgShape[i];
const Position& s = fgShape[i + 1];
fgShapeLengths.push_back(f.distanceTo(s));
fgShapeRotations.push_back((double) atan2((s.x() - f.x()), (f.y() - s.y())) * (double) 180.0 / (double) M_PI);
}
PositionVector tmp = fgShape;
tmp.move2side(myWidth / 2 * offsetSign);
fgSignPos = tmp.getLineCenter();
fgSignRot = 0;
if (tmp.length() != 0) {
fgSignRot = fgShape.rotationDegreeAtOffset(double((fgShape.length() / 2.)));
const double rotSign = MSGlobals::gLefthand ? -1 : 1;
fgSignRot -= 90 * rotSign;
}
}
void
GUIBusStop::finishedLoading() {
MSStoppingPlace::finishedLoading();
if (hasParameter("emptyColor")) {
try {
myEmptyColor = RGBColor::parseColor(getParameter("emptyColor"));
} catch (ProcessError& e) {
WRITE_WARNINGF("Could not parse color '%' (%)", getParameter("emptyColor"), e.what());
}
}
}
bool
GUIBusStop::addAccess(MSLane* const lane, const double startPos, const double endPos, double length, const MSStoppingPlace::AccessExit exit) {
const bool added = MSStoppingPlace::addAccess(lane, startPos, endPos, length, exit);
if (added) {
myAccessCoords.push_back(lane->geometryPositionAtOffset((startPos + endPos) / 2.));
}
return added;
}
GUIGLObjectPopupMenu*
GUIBusStop::getPopUpMenu(GUIMainWindow& app,
GUISUMOAbstractView& parent) {
GUIGLObjectPopupMenu* ret = new GUIGLObjectPopupMenu(app, parent, this);
buildPopupHeader(ret, app);
buildCenterPopupEntry(ret);
buildNameCopyPopupEntry(ret);
buildSelectionPopupEntry(ret);
buildShowParamsPopupEntry(ret);
buildPositionCopyEntry(ret, app);
return ret;
}
GUIParameterTableWindow*
GUIBusStop::getParameterWindow(GUIMainWindow& app,
GUISUMOAbstractView&) {
GUIParameterTableWindow* ret =
new GUIParameterTableWindow(app, *this);
ret->mkItem(TL("name"), false, getMyName());
ret->mkItem(TL("begin position [m]"), false, myBegPos);
ret->mkItem(TL("end position [m]"), false, myEndPos);
ret->mkItem(TL("lines"), false, joinToString(myLines, " "));
ret->mkItem(TL("parking length [m]"), false, (myEndPos - myBegPos) / myParkingFactor);
const std::string transportable = (myElement == SUMO_TAG_CONTAINER_STOP ? "container" : "person");
ret->mkItem((transportable + " capacity [#]").c_str(), false, myTransportableCapacity);
ret->mkItem((transportable + " number [#]").c_str(), true, new FunctionBinding<GUIBusStop, int>(this, &MSStoppingPlace::getTransportableNumber));
ret->mkItem(TL("stopped vehicles [#]"), true, new FunctionBinding<GUIBusStop, int>(this, &MSStoppingPlace::getStoppedVehicleNumber));
ret->mkItem(TL("last free pos [m]"), true, new FunctionBinding<GUIBusStop, double>(this, &GUIBusStop::getCroppedLastFreePos));
std::map<std::string, int> stats;
for (const MSTransportable* t : getTransportables()) {
MSStageDriving* s = dynamic_cast<MSStageDriving*>(t->getCurrentStage());
if (s != nullptr) {
if (s->getIntendedVehicleID() != "") {
stats[s->getIntendedVehicleID()] += 1;
} else {
stats[joinToString(s->getLines(), " ")] += 1;
}
}
}
if (stats.size() > 0) {
ret->mkItem(TL("waiting for:"), false, "[#]");
for (auto item : stats) {
ret->mkItem(item.first.c_str(), false, toString(item.second));
}
}
ret->closeBuilding();
return ret;
}
void
GUIBusStop::drawGL(const GUIVisualizationSettings& s) const {
RGBColor color, colorSign;
if (myElement == SUMO_TAG_CONTAINER_STOP) {
color = s.colorSettings.containerStopColor;
colorSign = s.colorSettings.containerStopColorSign;
} else if (myElement == SUMO_TAG_TRAIN_STOP) {
color = s.colorSettings.trainStopColor;
colorSign = s.colorSettings.trainStopColorSign;
} else {
color = s.colorSettings.busStopColor;
colorSign = s.colorSettings.busStopColorSign;
}
if (getColor() != RGBColor::INVISIBLE) {
color = getColor();
}
if (myEmptyColor != RGBColor::INVISIBLE && myEndPositions.empty() && myWaitingTransportables.empty()) {
color = myEmptyColor;
}
const bool s2 = s.secondaryShape;
const Position& signPos = s2 ? myFGSignPos2 : myFGSignPos;
if (color.alpha() != 0) {
GLHelper::pushName(getGlID());
GLHelper::pushMatrix();
glTranslated(0, 0, getType());
GLHelper::setColor(color);
const double exaggeration = getExaggeration(s);
if (s2) {
GLHelper::drawBoxLines(myFGShape2, myFGShapeRotations2, myFGShapeLengths2, myWidth * 0.5 * MIN2(1.0, exaggeration), 0, 0);
} else {
GLHelper::drawBoxLines(myFGShape, myFGShapeRotations, myFGShapeLengths, myWidth * 0.5 * MIN2(1.0, exaggeration), 0, 0);
}
const double signRot = s2 ? myFGSignRot2 : myFGSignRot;
if (s.drawDetail(10, exaggeration)) {
GLHelper::pushMatrix();
const double rotSign = MSGlobals::gLefthand ? 1 : -1;
const double lineAngle = s.getTextAngle(signRot);
RGBColor lineColor = color.changedBrightness(-51);
const double textOffset = s.flippedTextAngle(rotSign * signRot) ? -1 : 1;
const double textOffset2 = s.flippedTextAngle(rotSign * signRot) ? -1 : 0.3;
for (int i = 0; i < (int)myLines.size(); ++i) {
GLHelper::pushMatrix();
glTranslated(signPos.x(), signPos.y(), 0);
glRotated(-lineAngle, 0, 0, 1);
GLHelper::drawText(myLines[i].c_str(), Position(1.2, i * textOffset + textOffset2), .1, 1.f, lineColor, 0, FONS_ALIGN_LEFT);
GLHelper::popMatrix();
}
GLHelper::setColor(color);
const Position accessOrigin = getCenterPos();
for (std::vector<Position>::const_iterator i = myAccessCoords.begin(); i != myAccessCoords.end(); ++i) {
GLHelper::drawBoxLine(*i, RAD2DEG(accessOrigin.angleTo2D(*i)) - 90, accessOrigin.distanceTo2D(*i), .05);
}
glTranslated(signPos.x(), signPos.y(), 0);
int noPoints = 9;
if (s.scale * exaggeration > 25) {
noPoints = MIN2((int)(9.0 + (s.scale * exaggeration) / 10.0), 36);
}
glScaled(exaggeration, exaggeration, 1);
GLHelper::drawFilledCircle((double) 1.1, noPoints);
glTranslated(0, 0, .1);
GLHelper::setColor(colorSign);
GLHelper::drawFilledCircle((double) 0.9, noPoints);
if (myElement == SUMO_TAG_CONTAINER_STOP) {
GLHelper::drawText("C", Position(), .1, 1.6, color, signRot);
} else if (myElement == SUMO_TAG_TRAIN_STOP) {
GLHelper::drawText("T", Position(), .1, 1.6, color, signRot);
} else {
GLHelper::drawText("H", Position(), .1, 1.6, color, signRot);
}
GLHelper::popMatrix();
}
if (s.addFullName.show(this) && getMyName() != "") {
GLHelper::drawTextSettings(s.addFullName, getMyName(), signPos, s.scale, s.getTextAngle(signRot), GLO_MAX - getType());
}
GLHelper::popMatrix();
GLHelper::popName();
}
drawName(signPos, s.scale, s.addName, s.angle);
}
double
GUIBusStop::getExaggeration(const GUIVisualizationSettings& s) const {
return s.addSize.getExaggeration(s, this);
}
Boundary
GUIBusStop::getCenteringBoundary() const {
const PositionVector& shape = GUIGlobals::gSecondaryShape ? myFGShape2 : myFGShape;
Boundary b = shape.getBoxBoundary();
b.grow(myWidth);
for (const Position& p : myAccessCoords) {
b.add(p);
}
return b;
}
const std::string
GUIBusStop::getOptionalName() const {
return myName;
}
double
GUIBusStop::getCroppedLastFreePos() const {
return MAX2(0., getLastFreePos());
}