#pragma once
#include <config.h>
#include <cassert>
#include <string>
#include <functional>
#include <vector>
#include <set>
#include <limits>
#include <algorithm>
#include <iterator>
#include <utils/common/ToString.h>
#include <utils/common/MsgHandler.h>
#include <utils/common/StdDefs.h>
#include "EffortCalculator.h"
#include "SUMOAbstractRouter.h"
#define DijkstraRouter_DEBUG_QUERY_VISITED_OUT std::cerr
template<class E, class V>
class DijkstraRouter : public SUMOAbstractRouter<E, V> {
public:
class EdgeInfoByEffortComparator {
public:
bool operator()(const typename SUMOAbstractRouter<E, V>::EdgeInfo* nod1, const typename SUMOAbstractRouter<E, V>::EdgeInfo* nod2) const {
if (nod1->effort == nod2->effort) {
return nod1->edge->getNumericalID() > nod2->edge->getNumericalID();
}
return nod1->effort > nod2->effort;
}
};
DijkstraRouter(const std::vector<E*>& edges, bool unbuildIsWarning, typename SUMOAbstractRouter<E, V>::Operation effortOperation,
typename SUMOAbstractRouter<E, V>::Operation ttOperation = nullptr, bool silent = false, EffortCalculator* calc = nullptr,
const bool havePermissions = false, const bool haveRestrictions = false) :
SUMOAbstractRouter<E, V>("DijkstraRouter", unbuildIsWarning, effortOperation, ttOperation, havePermissions, haveRestrictions),
mySilent(silent), myExternalEffort(calc) {
for (typename std::vector<E*>::const_iterator i = edges.begin(); i != edges.end(); ++i) {
this->myEdgeInfos.push_back(typename SUMOAbstractRouter<E, V>::EdgeInfo(*i));
}
}
virtual ~DijkstraRouter() { }
virtual SUMOAbstractRouter<E, V>* clone() {
auto clone = new DijkstraRouter<E, V>(this->myEdgeInfos, this->myErrorMsgHandler == MsgHandler::getWarningInstance(),
this->myOperation, this->myTTOperation, mySilent, myExternalEffort,
this->myHavePermissions, this->myHaveRestrictions);
clone->setAutoBulkMode(this->myAutoBulkMode);
return clone;
}
bool compute(const E* from, const E* to, const V* const vehicle,
SUMOTime msTime, std::vector<const E*>& into, bool silent = false) {
assert(from != nullptr && (vehicle == nullptr || to != nullptr));
if (this->myEdgeInfos[from->getNumericalID()].prohibited || this->isProhibited(from, vehicle)) {
if (!silent) {
this->myErrorMsgHandler->inform("Vehicle '" + Named::getIDSecure(vehicle) + "' is not allowed on source edge '" + from->getID() + "'.");
}
return false;
}
if (to != nullptr && (this->myEdgeInfos[to->getNumericalID()].prohibited || this->isProhibited(to, vehicle))) {
if (!silent) {
this->myErrorMsgHandler->inform("Vehicle '" + Named::getIDSecure(vehicle) + "' is not allowed on destination edge '" + to->getID() + "'.");
}
return false;
}
double length = 0.;
this->startQuery();
#ifdef DijkstraRouter_DEBUG_QUERY
std::cout << "DEBUG: starting search for '" << Named::getIDSecure(vehicle) << "' from '" << Named::getIDSecure(from) << "' to '" << Named::getIDSecure(to) << "' time: " << STEPS2TIME(msTime) << "\n";
#endif
const SUMOVehicleClass vClass = vehicle == nullptr ? SVC_IGNORING : vehicle->getVClass();
const bool ignoreTransient = vehicle == nullptr ? false : vehicle->ignoreTransientPermissions();
std::tuple<const E*, const V*, SUMOTime> query = std::make_tuple(from, vehicle, msTime);
if ((this->myBulkMode || (this->myAutoBulkMode && query == myLastQuery)) && !this->myAmClean) {
#ifdef DijkstraRouter_DEBUG_BULKMODE
if (query != myLastQuery) {
std::cout << " invalid bulk mode. myLastQuery="
<< std::get<0>(myLastQuery)->getID() << ","
<< std::get<1>(myLastQuery)->getID() << ","
<< time2string(std::get<2>(myLastQuery))
<< " query="
<< std::get<0>(query)->getID() << ","
<< std::get<1>(query)->getID() << ","
<< time2string(std::get<2>(query))
<< "\n";
}
#endif
const auto& toInfo = this->myEdgeInfos[to->getNumericalID()];
if (toInfo.visited) {
this->buildPathFrom(&toInfo, into);
this->endQuery(1);
return true;
}
} else {
this->init(from->getNumericalID(), msTime);
if (myExternalEffort != nullptr) {
myExternalEffort->setInitialState(from->getNumericalID());
}
this->myAmClean = false;
}
myLastQuery = query;
int num_visited = 0;
#ifdef DijkstraRouter_DEBUG_QUERY_VISITED
DijkstraRouter_DEBUG_QUERY_VISITED_OUT << "<edgeData>\n <interval begin=\"" << time2string(msTime) << "\" end=\"" << toString(msTime + 1000) << "\" id=\"" << Named::getIDSecure(vehicle) << "\">\n";
#endif
while (!this->myFrontierList.empty()) {
num_visited += 1;
auto* const minimumInfo = this->myFrontierList.front();
const E* const minEdge = minimumInfo->edge;
#ifdef DijkstraRouter_DEBUG_QUERY
std::cout << "DEBUG: hit=" << minEdge->getID()
<< " TT=" << minimumInfo->effort
<< " EF=" << this->getEffort(minEdge, vehicle, minimumInfo->leaveTime)
<< " Leave: " << minimumInfo->leaveTime << " Q: ";
#ifdef DijkstraRouter_DEBUG_QUERY_FRONTIER
for (const auto& edgeInfo : this->myFrontierList) {
std::cout << "\n " << edgeInfo->effort << ", " << edgeInfo->edge->getID();
}
#endif
std::cout << "\n";
#endif
#ifdef DijkstraRouter_DEBUG_QUERY_VISITED
DijkstraRouter_DEBUG_QUERY_VISITED_OUT << " <edge id=\"" << minEdge->getID() << "\" index=\"" << num_visited << "\" cost=\"" << minimumInfo->effort << "\" time=\"" << minimumInfo->leaveTime << "\"/>\n";
#endif
if (minEdge == to) {
if (myExternalEffort != nullptr) {
myExternalEffort->update(minEdge->getNumericalID(), minimumInfo->prev->edge->getNumericalID(), minEdge->getLength());
}
this->buildPathFrom(minimumInfo, into);
this->endQuery(num_visited);
#ifdef DijkstraRouter_DEBUG_QUERY_PERF
const double cost = this->recomputeCosts(into, vehicle, msTime);
std::cout << "visited " + toString(num_visited) + " edges (final path length=" + toString(into.size()) + " cost=" << cost << " edges=" + toString(into) + ")\n";
#endif
#ifdef DijkstraRouter_DEBUG_QUERY_VISITED
DijkstraRouter_DEBUG_QUERY_VISITED_OUT << " </interval>\n</edgeData>\n";
#endif
return true;
}
std::pop_heap(this->myFrontierList.begin(), this->myFrontierList.end(), myComparator);
this->myFrontierList.pop_back();
this->myFound.push_back(minimumInfo);
minimumInfo->visited = true;
const double effortDelta = this->getEffort(minEdge, vehicle, minimumInfo->leaveTime);
const double leaveTime = minimumInfo->leaveTime + this->getTravelTime(minEdge, vehicle, minimumInfo->leaveTime, effortDelta);
if (myExternalEffort != nullptr) {
myExternalEffort->update(minEdge->getNumericalID(), minimumInfo->prev->edge->getNumericalID(), minEdge->getLength());
}
for (const std::pair<const E*, const E*>& follower : minEdge->getViaSuccessors(vClass, ignoreTransient)) {
auto& followerInfo = this->myEdgeInfos[follower.first->getNumericalID()];
if (followerInfo.prohibited || this->isProhibited(follower.first, vehicle)) {
continue;
}
double effort = minimumInfo->effort + effortDelta;
double time = leaveTime;
this->updateViaEdgeCost(follower.second, vehicle, time, effort, length);
assert(effort >= minimumInfo->effort);
assert(time >= minimumInfo->leaveTime);
const double oldEffort = followerInfo.effort;
if (!followerInfo.visited && effort < oldEffort) {
followerInfo.effort = effort;
followerInfo.leaveTime = time;
followerInfo.prev = minimumInfo;
if (oldEffort == std::numeric_limits<double>::max()) {
this->myFrontierList.push_back(&followerInfo);
std::push_heap(this->myFrontierList.begin(), this->myFrontierList.end(), myComparator);
} else {
std::push_heap(this->myFrontierList.begin(),
std::find(this->myFrontierList.begin(), this->myFrontierList.end(), &followerInfo) + 1,
myComparator);
}
}
}
}
this->endQuery(num_visited);
#ifdef DijkstraRouter_DEBUG_QUERY_PERF
std::cout << "visited " + toString(num_visited) + " edges (unsuccessful path length: " + toString(into.size()) + ")\n";
#endif
if (to != nullptr && !mySilent && !silent) {
this->myErrorMsgHandler->informf(TL("No connection between edge '%' and edge '%' found."), from->getID(), to->getID());
}
#ifdef DijkstraRouter_DEBUG_QUERY_VISITED
DijkstraRouter_DEBUG_QUERY_VISITED_OUT << " </interval>\n</edgeData>\n";
#endif
return false;
}
private:
DijkstraRouter(const std::vector<typename SUMOAbstractRouter<E, V>::EdgeInfo>& edgeInfos, bool unbuildIsWarning,
typename SUMOAbstractRouter<E, V>::Operation effortOperation, typename SUMOAbstractRouter<E, V>::Operation ttOperation, bool silent, EffortCalculator* calc,
const bool havePermissions, const bool haveRestrictions) :
SUMOAbstractRouter<E, V>("DijkstraRouter", unbuildIsWarning, effortOperation, ttOperation, havePermissions, haveRestrictions),
mySilent(silent),
myExternalEffort(calc) {
for (const auto& edgeInfo : edgeInfos) {
this->myEdgeInfos.push_back(typename SUMOAbstractRouter<E, V>::EdgeInfo(edgeInfo.edge));
}
}
private:
bool mySilent;
std::tuple<const E*, const V*, SUMOTime> myLastQuery;
EffortCalculator* const myExternalEffort;
EdgeInfoByEffortComparator myComparator;
};