generate-shapes/src/pfaedle/router/Router.cpp
2018-06-09 17:14:08 +02:00

615 lines
21 KiB
C++

// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include <omp.h>
#include <algorithm>
#include <fstream>
#include <limits>
#include <map>
#include <set>
#include <unordered_map>
#include <utility>
#include <vector>
#include "pfaedle/router/Comp.h"
#include "pfaedle/router/Router.h"
#include "pfaedle/router/RoutingAttrs.h"
#include "util/geo/output/GeoGraphJsonOutput.h"
#include "util/graph/Dijkstra.h"
#include "util/graph/EDijkstra.h"
#include "util/log/Log.h"
using pfaedle::router::Router;
using pfaedle::router::EdgeCost;
using pfaedle::router::CostFunc;
using pfaedle::router::DistHeur;
using pfaedle::router::NCostFunc;
using pfaedle::router::NDistHeur;
using pfaedle::router::CombCostFunc;
using pfaedle::router::EdgeListHop;
using pfaedle::router::EdgeListHops;
using pfaedle::router::RoutingOpts;
using pfaedle::router::RoutingAttrs;
using pfaedle::router::HopBand;
using pfaedle::router::NodeCandRoute;
using util::graph::EDijkstra;
using util::graph::Dijkstra;
// _____________________________________________________________________________
EdgeCost NCostFunc::operator()(const trgraph::Node* from,
const trgraph::Edge* e,
const trgraph::Node* to) const {
UNUSED(to);
if (!from) return EdgeCost();
int oneway = e->pl().oneWay() == 2;
int32_t stationSkip = 0;
double transitLinePen = 0; // transitLineCmp(e->pl());
return EdgeCost(e->pl().lvl() == 0 ? e->pl().getLength() : 0,
e->pl().lvl() == 1 ? e->pl().getLength() : 0,
e->pl().lvl() == 2 ? e->pl().getLength() : 0,
e->pl().lvl() == 3 ? e->pl().getLength() : 0,
e->pl().lvl() == 4 ? e->pl().getLength() : 0,
e->pl().lvl() == 5 ? e->pl().getLength() : 0,
e->pl().lvl() == 6 ? e->pl().getLength() : 0,
e->pl().lvl() == 7 ? e->pl().getLength() : 0, 0, stationSkip,
e->pl().getLength() * oneway, oneway,
e->pl().getLength() * transitLinePen, 0, &_rOpts);
}
// _____________________________________________________________________________
EdgeCost CostFunc::operator()(const trgraph::Edge* from, const trgraph::Node* n,
const trgraph::Edge* to) const {
if (!from) return EdgeCost();
uint32_t fullTurns = 0;
int oneway = from->pl().oneWay() == 2;
int32_t stationSkip = 0;
if (n) {
if (from->getFrom() == to->getTo() && from->getTo() == to->getFrom()) {
// trivial full turn
fullTurns = 1;
} else if (n->getDeg() > 2) {
// otherwise, only intersection angles will be punished
fullTurns = router::angSmaller(from->pl().backHop(), *n->pl().getGeom(),
to->pl().frontHop(), _rOpts.fullTurnAngle);
}
if (from->pl().isRestricted() && !_res.may(from, to, n)) oneway = 1;
// for debugging
n->pl().setVisited();
if (_tgGrp && n->pl().getSI() && n->pl().getSI()->getGroup() != _tgGrp)
stationSkip = 1;
}
double transitLinePen = transitLineCmp(from->pl());
return EdgeCost(from->pl().lvl() == 0 ? from->pl().getLength() : 0,
from->pl().lvl() == 1 ? from->pl().getLength() : 0,
from->pl().lvl() == 2 ? from->pl().getLength() : 0,
from->pl().lvl() == 3 ? from->pl().getLength() : 0,
from->pl().lvl() == 4 ? from->pl().getLength() : 0,
from->pl().lvl() == 5 ? from->pl().getLength() : 0,
from->pl().lvl() == 6 ? from->pl().getLength() : 0,
from->pl().lvl() == 7 ? from->pl().getLength() : 0, fullTurns,
stationSkip, from->pl().getLength() * oneway, oneway,
from->pl().getLength() * transitLinePen, 0, &_rOpts);
}
// _____________________________________________________________________________
double CostFunc::transitLineCmp(const trgraph::EdgePL& e) const {
double best = 1;
for (const auto* l : e.getLines()) {
double cur = _rAttrs.simi(l);
if (cur < 0.0001) return cur;
if (cur < best) best = cur;
}
return best;
}
// _____________________________________________________________________________
NDistHeur::NDistHeur(const RoutingOpts& rOpts,
const std::set<trgraph::Node*>& tos)
: _rOpts(rOpts), _maxCentD(0) {
size_t c = 0;
double x = 0, y = 0;
for (auto to : tos) {
x += to->pl().getGeom()->get<0>();
y += to->pl().getGeom()->get<1>();
c++;
}
x /= c;
y /= c;
_center = FPoint(x, y);
for (auto to : tos) {
double cur = static_cast<double>(static_cast<double>(
util::geo::webMercMeterDist(*to->pl().getGeom(), _center)));
if (cur > _maxCentD) _maxCentD = cur;
}
}
// _____________________________________________________________________________
DistHeur::DistHeur(uint8_t minLvl, const RoutingOpts& rOpts,
const std::set<trgraph::Edge*>& tos)
: _rOpts(rOpts), _lvl(minLvl), _maxCentD(0) {
size_t c = 0;
double x = 0, y = 0;
for (auto to : tos) {
x += to->getFrom()->pl().getGeom()->get<0>();
y += to->getFrom()->pl().getGeom()->get<1>();
c++;
}
x /= c;
y /= c;
_center = FPoint(x, y);
for (auto to : tos) {
double cur = static_cast<double>(static_cast<double>(
util::geo::webMercMeterDist(*to->getFrom()->pl().getGeom(), _center) *
_rOpts.levelPunish[_lvl]));
if (cur > _maxCentD) _maxCentD = cur;
}
}
// _____________________________________________________________________________
EdgeCost DistHeur::operator()(const trgraph::Edge* a,
const std::set<trgraph::Edge*>& b) const {
double cur = static_cast<double>(static_cast<double>(
util::geo::webMercMeterDist(*a->getTo()->pl().getGeom(), _center) *
_rOpts.levelPunish[_lvl]));
UNUSED(b);
return EdgeCost(cur - _maxCentD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}
// _____________________________________________________________________________
EdgeCost NDistHeur::operator()(const trgraph::Node* a,
const std::set<trgraph::Node*>& b) const {
double cur = static_cast<double>(static_cast<double>(
util::geo::webMercMeterDist(*a->pl().getGeom(), _center)));
UNUSED(b);
return EdgeCost(cur - _maxCentD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}
// _____________________________________________________________________________
double CombCostFunc::operator()(const router::Edge* from, const router::Node* n,
const router::Edge* to) const {
UNUSED(n);
UNUSED(from);
return to->pl().getCost().getValue();
}
// _____________________________________________________________________________
Router::Router(const trgraph::Graph& g, size_t numThreads)
: _g(g), _cache(numThreads) {
for (size_t i = 0; i < numThreads; i++) {
_cache[i] = new Cache();
}
}
// _____________________________________________________________________________
Router::~Router() {
for (size_t i = 0; i < _cache.size(); i++) {
delete _cache[i];
}
}
// _____________________________________________________________________________
bool Router::compConned(const NodeCandGroup& a, const NodeCandGroup& b) const {
for (auto n1 : a) {
for (auto n2 : b) {
if (n1.nd->pl().getComp() == n2.nd->pl().getComp()) return true;
}
}
return false;
}
// _____________________________________________________________________________
HopBand Router::getHopBand(const NodeCandGroup& a, const NodeCandGroup& b,
const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
const osm::Restrictor& rest) const {
double pend = 0;
for (size_t i = 0; i < a.size(); i++) {
for (size_t j = 0; j < b.size(); j++) {
double d = util::geo::webMercMeterDist(*a[i].nd->pl().getGeom(),
*b[j].nd->pl().getGeom());
if (d > pend) pend = d;
}
}
LOG(VDEBUG) << "Pending max hop distance is " << pend << " meters";
const trgraph::StatGroup* tgGrpTo = 0;
if (b.begin()->nd->pl().getSI())
tgGrpTo = b.begin()->nd->pl().getSI()->getGroup();
CostFunc costF(rAttrs, rOpts, rest, tgGrpTo, pend * 50);
std::set<trgraph::Edge *> from, to;
// TODO(patrick): test if the two sets share a common connected component
for (auto n : a)
from.insert(n.nd->getAdjListOut().begin(), n.nd->getAdjListOut().end());
for (auto n : b)
to.insert(n.nd->getAdjListOut().begin(), n.nd->getAdjListOut().end());
LOG(VDEBUG) << "Doing pilot run between " << from.size() << "->" << to.size()
<< " candidates";
EdgeList el;
EdgeCost ret = costF.inf();
DistHeur distH(0, rOpts, to);
if (compConned(a, b))
ret = EDijkstra::shortestPath(from, to, costF, distH, &el);
if (el.size() < 2 && costF.inf() <= ret) {
LOG(VDEBUG) << "Pilot run: no connection between candidate groups,"
<< " setting max distance to 1";
return HopBand{0, 1, 0, 0};
}
// cache the found path, will save a few dijkstra iterations
nestedCache(&el, from, costF, rAttrs);
auto na = el.back()->getFrom();
auto nb = el.front()->getFrom();
double maxStrD = 0;
for (auto e : to) {
double d = static_cast<double>(static_cast<double>(
util::geo::webMercMeterDist(*el.front()->getFrom()->pl().getGeom(),
*e->getTo()->pl().getGeom())));
if (d > maxStrD) maxStrD = d;
}
// TODO(patrick): derive the punish level here automatically
double maxD = std::max(ret.getValue(), pend * rOpts.levelPunish[2]) * 3;
double minD = ret.getValue();
LOG(VDEBUG) << "Pilot run: min distance between two groups is "
<< ret.getValue() << " (between nodes " << na << " and " << nb
<< "), using a max routing distance of " << maxD << ". The max"
<< " straight line distance from the pilot target to any other "
"target node was"
<< " " << maxStrD << ".";
return HopBand{minD, maxD, el.front(), maxStrD};
}
// _____________________________________________________________________________
EdgeListHops Router::routeGreedy(const NodeCandRoute& route,
const RoutingAttrs& rAttrs,
const RoutingOpts& rOpts,
const osm::Restrictor& rest) const {
if (route.size() < 2) return EdgeListHops();
EdgeListHops ret(route.size() - 1);
for (size_t i = 0; i < route.size() - 1; i++) {
const trgraph::StatGroup* tgGrp = 0;
std::set<trgraph::Node *> from, to;
for (auto c : route[i]) from.insert(c.nd);
for (auto c : route[i + 1]) to.insert(c.nd);
if (route[i + 1].begin()->nd->pl().getSI())
tgGrp = route[i + 1].begin()->nd->pl().getSI()->getGroup();
NCostFunc cost(rAttrs, rOpts, rest, tgGrp);
NDistHeur dist(rOpts, to);
NodeList nodesRet;
EdgeListHop hop;
Dijkstra::shortestPath(from, to, cost, dist, &hop.edges, &nodesRet);
if (nodesRet.size() > 1) {
// careful: nodesRet is reversed!
hop.start = nodesRet.back();
hop.end = nodesRet.front();
} else {
// just take the first candidate if no route could be found
hop.start = *from.begin();
hop.end = *to.begin();
}
ret[i] = hop;
}
return ret;
}
// _____________________________________________________________________________
EdgeListHops Router::routeGreedy2(const NodeCandRoute& route,
const RoutingAttrs& rAttrs,
const RoutingOpts& rOpts,
const osm::Restrictor& rest) const {
if (route.size() < 2) return EdgeListHops();
EdgeListHops ret(route.size() - 1);
for (size_t i = 0; i < route.size() - 1; i++) {
const trgraph::StatGroup* tgGrp = 0;
std::set<trgraph::Node *> from, to;
if (i == 0)
for (auto c : route[i]) from.insert(c.nd);
else
from.insert(const_cast<trgraph::Node*>(ret[i - 1].end));
for (auto c : route[i + 1]) to.insert(c.nd);
if (route[i + 1].begin()->nd->pl().getSI())
tgGrp = route[i + 1].begin()->nd->pl().getSI()->getGroup();
NCostFunc cost(rAttrs, rOpts, rest, tgGrp);
NDistHeur dist(rOpts, to);
NodeList nodesRet;
EdgeListHop hop;
Dijkstra::shortestPath(from, to, cost, dist, &hop.edges, &nodesRet);
if (nodesRet.size() > 1) {
// careful: nodesRet is reversed!
hop.start = nodesRet.back();
hop.end = nodesRet.front();
} else {
// just take the first candidate if no route could be found
hop.start = *from.begin();
hop.end = *to.begin();
}
ret[i] = hop;
}
return ret;
}
// _____________________________________________________________________________
EdgeListHops Router::route(const NodeCandRoute& route,
const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
const osm::Restrictor& rest,
router::Graph* cgraph) const {
if (route.size() < 2) return EdgeListHops();
EdgeListHops ret(route.size() - 1);
CombCostFunc ccost(rOpts);
router::Node* source = cgraph->addNd();
router::Node* sink = cgraph->addNd();
CombNodeMap nodes;
CombNodeMap nextNodes;
for (size_t i = 0; i < route[0].size(); i++) {
for (const auto* e : route[0][i].nd->getAdjListOut()) {
// we can be sure that each edge is exactly assigned to only one
// node because the transitgraph is directed
nodes[e] = cgraph->addNd(route[0][i].nd);
cgraph->addEdg(source, nodes[e])
->pl()
.setCost(EdgeCost(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
route[0][i].pen, 0));
}
}
size_t iters = EDijkstra::ITERS;
double itPerSecTot = 0;
size_t n = 0;
for (size_t i = 0; i < route.size() - 1; i++) {
nextNodes.clear();
HopBand hopBand = getHopBand(route[i], route[i + 1], rAttrs, rOpts, rest);
const trgraph::StatGroup* tgGrp = 0;
if (route[i + 1].begin()->nd->pl().getSI())
tgGrp = route[i + 1].begin()->nd->pl().getSI()->getGroup();
std::set<trgraph::Edge*> froms;
for (const auto& fr : route[i]) {
froms.insert(fr.nd->getAdjListOut().begin(),
fr.nd->getAdjListOut().end());
}
for (auto eFr : froms) {
router::Node* cNodeFr = nodes.find(eFr)->second;
EdgeSet tos;
std::map<trgraph::Edge*, router::Edge*> edges;
std::map<trgraph::Edge*, double> pens;
std::unordered_map<trgraph::Edge*, EdgeList*> edgeLists;
std::unordered_map<trgraph::Edge*, EdgeCost> costs;
assert(route[i + 1].size());
for (const auto& to : route[i + 1]) {
assert(to.nd->getAdjListOut().size());
for (auto eTo : to.nd->getAdjListOut()) {
tos.insert(eTo);
if (!nextNodes.count(eTo)) nextNodes[eTo] = cgraph->addNd(to.nd);
if (i == route.size() - 2) cgraph->addEdg(nextNodes[eTo], sink);
auto* ce = cgraph->addEdg(cNodeFr, nextNodes[eTo]);
edges[eTo] = ce;
pens[eTo] = to.pen;
edgeLists[eTo] = ce->pl().getEdges();
ce->pl().setStartNode(eFr->getFrom());
// for debugging
ce->pl().setStartEdge(eFr);
ce->pl().setEndNode(to.nd);
// for debugging
ce->pl().setEndEdge(eTo);
}
}
size_t iters = EDijkstra::ITERS;
auto t1 = TIME();
assert(tos.size());
assert(froms.size());
hops(eFr, froms, tos, tgGrp, edgeLists, &costs, rAttrs, rOpts, rest,
hopBand);
double itPerSec =
(static_cast<double>(EDijkstra::ITERS - iters)) / TOOK(t1, TIME());
n++;
itPerSecTot += itPerSec;
LOG(VDEBUG) << "from " << eFr << ": 1-" << tos.size() << " ("
<< route[i + 1].size() << " nodes) hop took "
<< EDijkstra::ITERS - iters << " iterations, "
<< TOOK(t1, TIME()) << "ms (tput: " << itPerSec << " its/ms)";
for (auto& kv : edges) {
kv.second->pl().setCost(
EdgeCost(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, pens[kv.first], 0) +
costs[kv.first]);
if (kv.second->pl().getEdges()->size()) {
if (kv.second->pl().getEdges() &&
kv.second->pl().getEdges()->size()) {
// the reach edge is included, but we dont want it in the geometry
kv.second->pl().getEdges()->erase(
kv.second->pl().getEdges()->begin());
}
}
}
}
std::swap(nodes, nextNodes);
}
LOG(VDEBUG) << "Hops took " << EDijkstra::ITERS - iters << " iterations,"
<< " average tput was " << (itPerSecTot / n) << " its/ms";
iters = EDijkstra::ITERS;
std::vector<router::Edge*> res;
EDijkstra::shortestPath(source, sink, ccost, &res);
size_t j = 0;
LOG(VDEBUG) << "Optim graph solve took " << EDijkstra::ITERS - iters
<< " iterations.";
for (auto i = res.rbegin(); i != res.rend(); i++) {
const auto e = *i;
if (e->getFrom() != source && e->getTo() != sink) {
assert(e->pl().frontNode());
assert(e->pl().backNode());
ret[j] = EdgeListHop{std::move(*e->pl().getEdges()), e->pl().frontNode(),
e->pl().backNode()};
j++;
}
}
assert(ret.size() == j);
return ret;
}
// _____________________________________________________________________________
void Router::hops(trgraph::Edge* from, const std::set<trgraph::Edge*>& froms,
const std::set<trgraph::Edge*> tos,
const trgraph::StatGroup* tgGrp,
const std::unordered_map<trgraph::Edge*, EdgeList*>& edgesRet,
std::unordered_map<trgraph::Edge*, EdgeCost>* rCosts,
const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
const osm::Restrictor& rest, HopBand hopB) const {
std::set<trgraph::Edge*> rem;
CostFunc cost(rAttrs, rOpts, rest, tgGrp, hopB.maxD);
const auto& cached = getCachedHops(from, tos, edgesRet, rCosts, rAttrs);
for (auto e : cached) {
// shortcut: if the nodes lie in two different connected components,
// the distance between them is trivially infinite
if (e == from || e->getFrom() == from->getFrom() ||
from->getFrom()->pl().getComp() != e->getTo()->pl().getComp() ||
e->pl().oneWay() == 2 || from->pl().oneWay() == 2) {
(*rCosts)[e] = cost.inf();
} else {
rem.insert(e);
}
}
LOG(VDEBUG) << "From cache: " << tos.size() - rem.size()
<< ", have to cal: " << rem.size();
if (rem.size()) {
DistHeur dist(from->getFrom()->pl().getComp()->minEdgeLvl, rOpts, rem);
const auto& ret = EDijkstra::shortestPath(from, rem, cost, dist, edgesRet);
for (const auto& kv : ret) {
nestedCache(edgesRet.at(kv.first), froms, cost, rAttrs);
(*rCosts)[kv.first] = kv.second;
}
}
}
// _____________________________________________________________________________
void Router::nestedCache(const EdgeList* el,
const std::set<trgraph::Edge*>& froms,
const CostFunc& cost,
const RoutingAttrs& rAttrs) const {
if (el->size() == 0) return;
// iterate over result edges backwards
EdgeList curEdges;
EdgeCost curCost;
size_t j = 0;
for (auto i = el->begin(); i < el->end(); i++) {
if (curEdges.size()) {
curCost = curCost + cost(*i, (*i)->getTo(), curEdges.back());
}
curEdges.push_back(*i);
if (froms.count(*i)) {
EdgeCost startC = cost(0, 0, *i) + curCost;
cache(*i, el->front(), startC, &curEdges, rAttrs);
j++;
}
}
}
// _____________________________________________________________________________
std::set<pfaedle::trgraph::Edge*> Router::getCachedHops(
trgraph::Edge* from, const std::set<trgraph::Edge*>& tos,
const std::unordered_map<trgraph::Edge*, EdgeList*>& edgesRet,
std::unordered_map<trgraph::Edge*, EdgeCost>* rCosts,
const RoutingAttrs& rAttrs) const {
std::set<trgraph::Edge*> ret;
for (auto to : tos) {
if ((*_cache[omp_get_thread_num()])[rAttrs][from].count(to)) {
const auto& cv = (*_cache[omp_get_thread_num()])[rAttrs][from][to];
(*rCosts)[to] = cv.first;
*edgesRet.at(to) = cv.second;
} else {
ret.insert(to);
}
}
return ret;
}
// _____________________________________________________________________________
void Router::cache(trgraph::Edge* from, trgraph::Edge* to, const EdgeCost& c,
EdgeList* edges, const RoutingAttrs& rAttrs) const {
if (from == to) return;
(*_cache[omp_get_thread_num()])[rAttrs][from][to] =
std::pair<EdgeCost, EdgeList>(c, *edges);
}
// _____________________________________________________________________________
size_t Router::getCacheNumber() const { return _cache.size(); }