initial commit

This commit is contained in:
Patrick Brosi 2018-06-09 17:14:08 +02:00
commit efcd3e1892
106 changed files with 27000 additions and 0 deletions

View file

@ -0,0 +1,20 @@
file(GLOB_RECURSE pfaedle_SRC *.cpp)
set(pfaedle_main PfaedleMain.cpp)
list(REMOVE_ITEM pfaedle_SRC ${pfaedle_main})
include_directories(
${PFAEDLE_INCLUDE_DIR}
)
configure_file (
"_config.h.in"
"_config.h"
)
add_executable(pfaedle ${pfaedle_main})
add_library(pfaedle_dep ${pfaedle_SRC})
target_include_directories(pfaedle_dep PUBLIC ${PROJECT_SOURCE_DIR}/src/cppgtfs/src)
target_link_libraries(pfaedle pfaedle_dep util xml configparser ad_cppgtfs ${Boost_LIBRARIES} -lpthread)

188
src/pfaedle/PfaedleMain.cpp Normal file
View file

@ -0,0 +1,188 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include <signal.h>
#include <stdio.h>
#include <fstream>
#include <map>
#include <string>
#include <vector>
#include "ad/cppgtfs/Parser.h"
#include "ad/cppgtfs/Writer.h"
#include "ad/cppgtfs/gtfs/Feed.h"
#include "pfaedle/config/ConfigReader.h"
#include "pfaedle/config/MotConfig.h"
#include "pfaedle/config/MotConfigReader.h"
#include "pfaedle/eval/Collector.h"
#include "pfaedle/netgraph/Graph.h"
#include "pfaedle/osm/OsmIdSet.h"
#include "pfaedle/router/ShapeBuilder.h"
#include "pfaedle/trgraph/Graph.h"
#include "util/geo/output/GeoGraphJsonOutput.h"
#include "util/geo/output/GeoJsonOutput.h"
#include "util/json/JsonWriter.h"
#include "util/log/Log.h"
using std::string;
using pfaedle::router::MOTs;
using pfaedle::osm::BBoxIdx;
using pfaedle::osm::OsmBuilder;
using pfaedle::config::MotConfig;
using pfaedle::config::Config;
using pfaedle::router::ShapeBuilder;
using pfaedle::config::MotConfigReader;
using pfaedle::config::ConfigReader;
using pfaedle::eval::Collector;
std::string getMotStr(const MOTs& mots);
MOTs getContMots(const MotConfig& motCfg, const MOTs& mots);
// _____________________________________________________________________________
int main(int argc, char** argv) {
// disable output buffering for standard output
setbuf(stdout, NULL);
// initialize randomness
srand(time(NULL) + rand()); // NOLINT
Config cfg;
MotConfigReader motCfgReader;
ConfigReader cr;
cr.read(&cfg, argc, argv);
ad::cppgtfs::gtfs::Feed gtfs;
motCfgReader.parse(cfg.configPaths);
if (cfg.feedPaths.size() == 1) {
LOG(INFO) << "Reading " << cfg.feedPaths[0] << " ...";
ad::cppgtfs::Parser p;
p.parse(&gtfs, cfg.feedPaths[0]);
LOG(INFO) << "Done.";
} else if (cfg.feedPaths.size() > 1) {
LOG(ERROR) << "Maximal one input feed allowed.";
exit(1);
}
LOG(DEBUG) << "Read " << motCfgReader.getConfigs().size()
<< " unique MOT configs.";
MOTs cmdCfgMots = cfg.mots;
ad::cppgtfs::gtfs::Trip* singleTrip = 0;
if (cfg.shapeTripId.size()) {
singleTrip = gtfs.getTrips().get(cfg.shapeTripId);
if (!singleTrip) {
LOG(ERROR) << "Trip #" << cfg.shapeTripId << " not found.";
exit(1);
}
}
if (cfg.writeOsm.size()) {
LOG(INFO) << "Writing filtered XML to " << cfg.writeOsm << " ...";
BBoxIdx box(2500);
if (cfg.feedPaths.size()) {
box = ShapeBuilder::getPaddedGtfsBox(&gtfs, 2500, cmdCfgMots,
cfg.shapeTripId);
}
OsmBuilder osmBuilder;
std::vector<pfaedle::osm::OsmReadOpts> opts;
for (const auto& o : motCfgReader.getConfigs()) {
if (std::find_first_of(o.mots.begin(), o.mots.end(), cmdCfgMots.begin(),
cmdCfgMots.end()) != o.mots.end()) {
opts.push_back(o.osmBuildOpts);
}
}
osmBuilder.filterWrite(cfg.osmPath, cfg.writeOsm, opts, box);
exit(0);
}
std::vector<double> dfBins;
auto dfBinStrings = util::split(std::string(cfg.evalDfBins), ',');
for (auto st : dfBinStrings) dfBins.push_back(atof(st.c_str()));
Collector ecoll(cfg.evalPath, dfBins);
for (const auto& motCfg : motCfgReader.getConfigs()) {
auto usedMots = getContMots(motCfg, cmdCfgMots);
if (!usedMots.size()) continue;
std::string motStr = getMotStr(usedMots);
LOG(INFO) << "Calculating shapes for mots " << motStr;
ShapeBuilder shapeBuilder(&gtfs, cmdCfgMots, motCfg, &ecoll, cfg);
if (cfg.writeGraph) {
LOG(INFO) << "Outputting graph.json...";
util::geo::output::GeoGraphJsonOutput out;
std::ofstream fstr(cfg.dbgOutputPath + "/graph.json");
out.print(*shapeBuilder.getGraph(), fstr);
fstr.close();
}
if (singleTrip) {
LOG(INFO) << "Outputting path.json...";
std::ofstream pstr(cfg.dbgOutputPath + "/path.json");
util::geo::output::GeoJsonOutput o(pstr);
auto l = shapeBuilder.shapeL(singleTrip);
if (singleTrip->getShape()) {
auto orig = Collector::getWebMercLine(singleTrip->getShape(), -1, -1);
o.print(orig, {{"ver", "old"}});
}
o.print(l, {{"ver", "new"}});
o.flush();
pstr.close();
exit(0);
}
pfaedle::netgraph::Graph ng;
shapeBuilder.shape(&ng);
if (cfg.buildTransitGraph) {
LOG(INFO) << "Outputting trgraph.json...";
util::geo::output::GeoGraphJsonOutput out;
std::ofstream fstr(cfg.dbgOutputPath + "/trgraph.json");
out.print(ng, fstr);
fstr.close();
}
}
if (cfg.evaluate) ecoll.printStats(&std::cout);
if (cfg.feedPaths.size()) {
LOG(INFO) << "Writing output GTFS...";
ad::cppgtfs::Writer w;
w.write(&gtfs, cfg.outputPath);
}
return (0);
}
// _____________________________________________________________________________
std::string getMotStr(const MOTs& mots) {
bool first = false;
std::string motStr;
for (const auto& mot : mots) {
if (first) motStr += ", ";
motStr += "<" + Route::getTypeString(mot) + ">";
first = true;
}
return motStr;
}
// _____________________________________________________________________________
MOTs getContMots(const MotConfig& motCfg, const MOTs& mots) {
MOTs ret;
for (const auto& mot : mots) {
if (motCfg.mots.count(mot)) {
ret.insert(mot);
}
}
return ret;
}

10
src/pfaedle/_config.h.in Normal file
View file

@ -0,0 +1,10 @@
// Copyright 2017
// Author: Patrick Brosi
#ifndef SRC_PFAEDLE_CONFIG_H_
#define SRC_PFAEDLE_CONFIG_H_
// version number from cmake version module
#define VERSION_FULL "@VERSION_GIT_FULL@"
#endif // SRC_PFAEDLE_CONFIG_H_N

View file

@ -0,0 +1,201 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include <float.h>
#include <getopt.h>
#include <exception>
#include <iostream>
#include <string>
#include "pfaedle/_config.h"
#include "pfaedle/config/ConfigReader.h"
#include "util/String.h"
#include "util/log/Log.h"
using pfaedle::config::ConfigReader;
using std::string;
using std::exception;
using std::vector;
// _____________________________________________________________________________
void ConfigReader::help() {
std::cout
<< std::setfill(' ') << std::left
<< "\033[1mpfaedle GTFS map matcher \033[22m\n"
<< VERSION_FULL << " (built " << __DATE__ << " " << __TIME__ << ")\n\n"
<< "(C) 2018 University of Freiburg\n"
<< "Author: Patrick Brosi <brosi@informatik.uni-freiburg.de>\n\n"
<< "Usage: "
<< " -x <OSM FILE> -c <CFG FILE> <GTFS FEED>\n\n"
<< "Allowed options:\n\n"
<< "General:\n"
<< std::setw(35) << " -v [ --version ]"
<< "print version\n"
<< std::setw(35) << " -h [ --help ]"
<< "show this help message\n"
<< "\nInput:\n"
<< std::setw(35) << " -c [ --config ] arg"
<< "pfaedle config file\n"
<< std::setw(35) << " -i [ --input ] arg"
<< "gtfs feed(s), may also be given as positional parameter (see usage)\n"
<< std::setw(35) << " -x [ --osm-file ] arg"
<< "OSM xml input file\n"
<< std::setw(35) << " -m [ --mots ] arg (=all)"
<< "MOTs to calculate shapes for, comma separated, either as string "
"{all,\n"
<< std::setw(35) << " "
<< "tram | streetcar, subway | metro, rail | train, bus, ferry | boat | "
"\n"
<< std::setw(35) << " "
<< "ship, cableclar, gondola, funicular} or as GTFS mot codes\n"
<< "\nOutput:\n"
<< std::setw(35) << " -o [ --output ] arg (=gtfs-out)"
<< "GTFS output path\n"
<< std::setw(35) << " -X [ --osm-out ] arg"
<< "if specified, a filtered OSM file will be written to <arg>\n"
<< "\nDebug Output:\n"
<< std::setw(35) << " -d [ --dbg-path ] arg (=geo)"
<< "output path for debug files\n"
<< std::setw(35) << " --write-trgraph"
<< "write transit graph as GeoJSON to <dbg-path>/trgraph.json\n"
<< std::setw(35) << " --write-graph"
<< "write routing graph as GeoJSON to <dbg-path>/graph.json\n"
<< std::setw(35) << " --write-cgraph"
<< "write combination graph as GeoJSON to <dbg-path>/combraph.json\n"
<< std::setw(35) << " --method arg (=global)"
<< "matching method to use, either 'global' (based on HMM), 'greedy' or "
"'greedy2'\n"
<< std::setw(35) << " --eval"
<< "evaluate existing shapes against matched shapes and print results\n"
<< std::setw(35) << " --eval-path arg (=.)"
<< "path for eval file output\n"
<< std::setw(35) << " --eval-df-bins arg (= )"
<< "bins to use for d_f histogram, comma separated (e.g. 10,20,30,40)\n"
<< "\nMisc:\n"
<< std::setw(35) << " -T [ --trip-id ] arg"
<< "Do routing only for trip <arg>, write result to\n"
<< std::setw(35) << " "
<< "<dbg-path>/path.json\n"
<< std::setw(35) << " --grid-size arg (=2000)"
<< "Grid cell size\n";
}
// _____________________________________________________________________________
void ConfigReader::read(Config* cfg, int argc, char** argv) {
std::string motStr = "all";
bool printOpts = false;
struct option ops[] = {{"output", required_argument, 0, 'o'},
{"input", required_argument, 0, 'i'},
{"config", required_argument, 0, 'c'},
{"osm-file", required_argument, 0, 'x'},
{"drop-shapes", required_argument, 0, 'D'},
{"mots", required_argument, NULL, 'm'},
{"grid-size", required_argument, 0, 'g'},
{"osm-out", required_argument, 0, 'X'},
{"trip-id", required_argument, 0, 'T'},
{"write-graph", no_argument, 0, 1},
{"write-cgraph", no_argument, 0, 2},
{"write-trgraph", no_argument, 0, 4},
{"method", required_argument, 0, 5},
{"eval", no_argument, 0, 3},
{"eval-path", required_argument, 0, 6},
{"eval-df-bins", required_argument, 0, 7},
{"dbg-path", required_argument, 0, 'd'},
{"version", no_argument, 0, 'v'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}};
char c;
while ((c = getopt_long(argc, argv, ":o:hvi:c:x:Dm:g:X:T:d:p", ops, 0)) !=
-1) {
switch (c) {
case 1:
cfg->writeGraph = true;
break;
case 2:
cfg->writeCombGraph = true;
break;
case 3:
cfg->evaluate = true;
break;
case 4:
cfg->buildTransitGraph = true;
break;
case 5:
cfg->solveMethod = optarg;
break;
case 6:
cfg->evalPath = optarg;
break;
case 7:
cfg->evalDfBins = optarg;
break;
case 'o':
cfg->outputPath = optarg;
break;
case 'i':
cfg->feedPaths.push_back(optarg);
break;
case 'c':
cfg->configPaths.push_back(optarg);
break;
case 'x':
cfg->osmPath = optarg;
break;
case 'D':
cfg->dropShapes = true;
break;
case 'm':
motStr = optarg;
break;
case 'g':
cfg->gridSize = atof(optarg);
break;
case 'X':
cfg->writeOsm = optarg;
break;
case 'T':
cfg->shapeTripId = optarg;
break;
case 'd':
cfg->dbgOutputPath = optarg;
break;
case 'v':
std::cout << VERSION_FULL << " (built " << __DATE__ << " " << __TIME__
<< ")\n\n";
exit(0);
case 'p':
printOpts = true;
break;
case 'h':
help();
exit(0);
case ':':
std::cerr << argv[optind - 1];
std::cerr << " requires an argument" << std::endl;
exit(1);
case '?':
std::cerr << argv[optind - 1];
std::cerr << " option unknown" << std::endl;
exit(1);
break;
default:
std::cerr << "Error while parsing arguments" << std::endl;
exit(1);
break;
}
}
for (int i = optind; i < argc; i++) cfg->feedPaths.push_back(argv[i]);
auto v = util::split(motStr, ',');
for (const auto& motStr : v) {
const auto& mots = Route::getTypesFromString(util::trim(motStr));
cfg->mots.insert(mots.begin(), mots.end());
}
if (printOpts)
std::cout << "\nConfigured options:\n\n" << cfg->toString() << std::endl;
}

View file

@ -0,0 +1,21 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_CONFIG_CONFIGREADER_H_
#define PFAEDLE_CONFIG_CONFIGREADER_H_
#include <vector>
#include "pfaedle/config/PfaedleConfig.h"
namespace pfaedle {
namespace config {
class ConfigReader {
public:
static void read(Config* targetConfig, int argc, char** argv);
static void help();
};
}
}
#endif // PFAEDLE_CONFIG_CONFIGREADER_H_

View file

@ -0,0 +1,28 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_CONFIG_MOTCONFIG_H_
#define PFAEDLE_CONFIG_MOTCONFIG_H_
#include "pfaedle/osm/OsmBuilder.h"
#include "pfaedle/router/Router.h"
namespace pfaedle {
namespace config {
struct MotConfig {
router::MOTs mots;
osm::OsmReadOpts osmBuildOpts;
router::RoutingOpts routingOpts;
};
inline bool operator==(const MotConfig& a, const MotConfig& b) {
return a.osmBuildOpts == b.osmBuildOpts && a.routingOpts == b.routingOpts;
}
} // namespace config
} // namespace pfaedle
#endif // PFAEDLE_CONFIG_MOTCONFIG_H_

View file

@ -0,0 +1,463 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include <set>
#include <string>
#include "pfaedle/config/MotConfigReader.h"
#include "util/Misc.h"
#include "util/String.h"
using pfaedle::config::MotConfigReader;
using pfaedle::config::MotConfig;
using pfaedle::osm::FilterRule;
using pfaedle::osm::KeyVal;
using configparser::ConfigFileParser;
using configparser::ParseExc;
using pfaedle::osm::DeepAttrRule;
using pfaedle::trgraph::ReplRules;
using ad::cppgtfs::gtfs::Route;
// _____________________________________________________________________________
MotConfigReader::MotConfigReader() {}
// _____________________________________________________________________________
void MotConfigReader::parse(const std::vector<std::string>& paths) {
for (const auto& s : paths) {
ConfigFileParser p;
p.parse(s);
for (const auto& sec : p.getSecs()) {
MotConfig curCfg;
std::string secStr = sec.first;
if (p.hasKey(secStr, "osm_filter_keep")) {
for (const auto& kvs : p.getStrArr(sec.first, "osm_filter_keep", ' ')) {
auto fRule = getFRule(kvs);
curCfg.osmBuildOpts.keepFilter[fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
for (uint8_t i = 0; i < 7; i++) {
std::string name =
std::string("osm_filter_lvl") + std::to_string(i + 1);
if (p.hasKey(secStr, name)) {
for (const auto& kvs : p.getStrArr(sec.first, name, ' ')) {
auto fRule = getFRule(kvs);
curCfg.osmBuildOpts.levelFilters[i][fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
}
if (p.hasKey(secStr, "osm_filter_drop")) {
for (const auto& kvs : p.getStrArr(sec.first, "osm_filter_drop", ' ')) {
auto fRule = getFRule(kvs);
curCfg.osmBuildOpts.dropFilter[fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "osm_max_snap_level")) {
curCfg.osmBuildOpts.maxSnapLevel =
p.getInt(sec.first, "osm_max_snap_level");
} else {
curCfg.osmBuildOpts.maxSnapLevel = 7;
}
if (p.hasKey(secStr, "osm_filter_nohup")) {
for (const auto& kvs :
p.getStrArr(sec.first, "osm_filter_nohup", ' ')) {
auto fRule = getFRule(kvs);
curCfg.osmBuildOpts.noHupFilter[fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "osm_filter_oneway")) {
for (const auto& kvs :
p.getStrArr(sec.first, "osm_filter_oneway", ' ')) {
auto fRule = getFRule(kvs);
curCfg.osmBuildOpts.oneWayFilter[fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "osm_filter_oneway_reverse")) {
for (const auto& kvs :
p.getStrArr(sec.first, "osm_filter_oneway_reverse", ' ')) {
auto fRule = getFRule(kvs);
curCfg.osmBuildOpts.oneWayFilterRev[fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "osm_filter_undirected")) {
for (const auto& kvs :
p.getStrArr(sec.first, "osm_filter_undirected", ' ')) {
auto fRule = getFRule(kvs);
curCfg.osmBuildOpts.twoWayFilter[fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "osm_filter_station")) {
for (const auto& kvs :
p.getStrArr(sec.first, "osm_filter_station", ' ')) {
auto fRule = getFRule(kvs);
curCfg.osmBuildOpts.stationFilter[fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "osm_filter_station_blocker")) {
for (const auto& kvs :
p.getStrArr(sec.first, "osm_filter_station_blocker", ' ')) {
auto fRule = getFRule(kvs);
curCfg.osmBuildOpts.stationBlockerFilter[fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "osm_node_positive_restriction")) {
for (const auto& kvs :
p.getStrArr(sec.first, "osm_node_positive_restriction", ' ')) {
auto fRule = getFRule(kvs);
curCfg.osmBuildOpts.restrPosRestr[fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "osm_node_negative_restriction")) {
for (const auto& kvs :
p.getStrArr(sec.first, "osm_node_negative_restriction", ' ')) {
auto fRule = getFRule(kvs);
curCfg.osmBuildOpts.restrNegRestr[fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "osm_filter_no_restriction")) {
for (const auto& kvs :
p.getStrArr(sec.first, "osm_filter_no_restriction", ' ')) {
auto fRule = getFRule(kvs);
curCfg.osmBuildOpts.noRestrFilter[fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "osm_station_name_attrs")) {
for (const std::string& r :
p.getStrArr(sec.first, "osm_station_name_attrs", ' ')) {
curCfg.osmBuildOpts.statAttrRules.nameRule.push_back(
getDeepAttrRule(r));
}
}
if (p.hasKey(secStr, "osm_track_number_tags")) {
for (const std::string& r :
p.getStrArr(sec.first, "osm_track_number_tags", ' ')) {
curCfg.osmBuildOpts.statAttrRules.platformRule.push_back(
getDeepAttrRule(r));
}
}
if (p.hasKey(secStr, "osm_edge_track_number_tags")) {
for (const std::string& r :
p.getStrArr(sec.first, "osm_edge_track_number_tags", ' ')) {
curCfg.osmBuildOpts.edgePlatformRules.push_back(getDeepAttrRule(r));
}
}
if (p.hasKey(secStr, "osm_station_group_attrs")) {
auto arr = p.getStrArr(secStr, "osm_station_group_attrs", ' ');
for (const auto& ruleStr : arr) {
auto deep = getDeepAttrRule(ruleStr);
// TODO(patrick): getKv is misused here as a a=b parser
auto attrD = getKv(deep.attr);
deep.attr = attrD.first;
double dist = atof(attrD.second.c_str());
curCfg.osmBuildOpts.statGroupNAttrRules.push_back({deep, dist});
}
}
if (p.hasKey(secStr, "osm_line_relation_tags")) {
auto arr = p.getStrArr(secStr, "osm_line_relation_tags", ' ');
for (const auto& ruleStr : arr) {
auto rule = getKv(ruleStr);
auto tags = util::split(rule.second, ',');
if (rule.first == "from_name")
curCfg.osmBuildOpts.relLinerules.fromNameRule = tags;
else if (rule.first == "to_name")
curCfg.osmBuildOpts.relLinerules.toNameRule = tags;
else if (rule.first == "line_name")
curCfg.osmBuildOpts.relLinerules.sNameRule = tags;
}
}
if (p.hasKey(secStr, "osm_max_snap_distance")) {
curCfg.osmBuildOpts.maxSnapDistances =
p.getDoubleArr(secStr, "osm_max_snap_distance", ',');
} else {
curCfg.osmBuildOpts.maxSnapDistances.push_back(50);
}
if (p.hasKey(secStr, "osm_max_snap_fallback_distance")) {
curCfg.osmBuildOpts.maxSnapFallbackHeurDistance =
p.getDouble(secStr, "osm_max_snap_fallback_distance");
} else {
curCfg.osmBuildOpts.maxSnapFallbackHeurDistance =
*std::max_element(curCfg.osmBuildOpts.maxSnapDistances.begin(),
curCfg.osmBuildOpts.maxSnapDistances.end()) *
2;
}
if (p.hasKey(secStr, "osm_max_group_search_distance")) {
curCfg.osmBuildOpts.maxGroupSearchDistance =
p.getDouble(secStr, "osm_max_group_search_distance");
} else {
curCfg.osmBuildOpts.maxGroupSearchDistance =
*std::max_element(curCfg.osmBuildOpts.maxSnapDistances.begin(),
curCfg.osmBuildOpts.maxSnapDistances.end()) *
4;
}
if (p.hasKey(secStr, "osm_max_osm_station_distance")) {
curCfg.osmBuildOpts.maxOsmStationDistance =
p.getDouble(secStr, "osm_max_osm_station_distance");
} else {
curCfg.osmBuildOpts.maxOsmStationDistance = 5;
}
if (p.hasKey(secStr, "osm_max_node_block_distance")) {
curCfg.osmBuildOpts.maxBlockDistance =
p.getDouble(secStr, "osm_max_node_block_distance");
} else {
curCfg.osmBuildOpts.maxBlockDistance =
*std::max_element(curCfg.osmBuildOpts.maxSnapDistances.begin(),
curCfg.osmBuildOpts.maxSnapDistances.end()) /
8;
}
for (uint8_t i = 0; i < 8; i++) {
std::string name =
std::string("routing_lvl") + std::to_string(i) + "_fac";
if (p.hasKey(secStr, name)) {
double v = p.getDouble(sec.first, name);
curCfg.routingOpts.levelPunish[i] = v;
} else {
curCfg.routingOpts.levelPunish[i] = 1;
}
}
if (p.hasKey(secStr, "routing_full_turn_punish")) {
curCfg.routingOpts.fullTurnPunishFac =
p.getDouble(secStr, "routing_full_turn_punish");
}
if (p.hasKey(secStr, "routing_full_turn_angle")) {
double ang = p.getDouble(secStr, "routing_full_turn_angle");
curCfg.routingOpts.fullTurnAngle = ang;
} else {
curCfg.routingOpts.fullTurnAngle = 5;
}
if (p.hasKey(secStr, "routing_snap_full_turn_angle")) {
double ang = p.getDouble(secStr, "routing_snap_full_turn_angle");
curCfg.osmBuildOpts.maxAngleSnapReach = ang;
} else {
curCfg.osmBuildOpts.maxAngleSnapReach =
curCfg.routingOpts.fullTurnAngle;
}
if (p.hasKey(secStr, "routing_pass_thru_station_punish")) {
curCfg.routingOpts.passThruStationsPunish =
p.getDouble(secStr, "routing_pass_thru_station_punish");
}
if (p.hasKey(secStr, "routing_one_way_meter_punish_fac")) {
curCfg.routingOpts.oneWayPunishFac =
p.getDouble(secStr, "routing_one_way_meter_punish_fac");
}
if (p.hasKey(secStr, "routing_one_way_edge_punish")) {
curCfg.routingOpts.oneWayEdgePunish =
p.getDouble(secStr, "routing_one_way_edge_punish");
}
if (p.hasKey(secStr, "routing_line_unmatched_punish_fac")) {
curCfg.routingOpts.lineUnmatchedPunishFact =
p.getDouble(secStr, "routing_line_unmatched_punish_fac");
}
if (p.hasKey(secStr, "routing_platform_unmatched_punish")) {
curCfg.routingOpts.platformUnmatchedPen =
p.getDouble(secStr, "routing_platform_unmatched_punish");
}
if (p.hasKey(secStr, "routing_non_osm_station_punish")) {
curCfg.routingOpts.nonOsmPen =
p.getDouble(secStr, "routing_non_osm_station_punish");
} else {
curCfg.routingOpts.nonOsmPen = 0;
}
if (p.hasKey(secStr, "routing_station_distance_punish_fac")) {
curCfg.routingOpts.stationDistPenFactor =
p.getDouble(secStr, "routing_station_distance_punish_fac");
} else {
curCfg.routingOpts.stationDistPenFactor = 1;
}
if (p.hasKey(secStr, "station_normalize_chain")) {
try {
auto arr = p.getStrArr(secStr, "station_normalize_chain", ';');
curCfg.osmBuildOpts.statNormzer =
trgraph::Normalizer(getNormRules(arr));
} catch (const std::exception& e) {
throw ParseExc(p.getVal(secStr, "station_normalize_chain").line,
p.getVal(secStr, "station_normalize_chain").pos,
"<valid regular expression>",
std::string("<regex error: ") + e.what() + ">", s);
}
}
if (p.hasKey(secStr, "track_normalize_chain")) {
try {
auto arr = p.getStrArr(secStr, "track_normalize_chain", ';');
curCfg.osmBuildOpts.trackNormzer =
trgraph::Normalizer(getNormRules(arr));
} catch (const std::exception& e) {
throw ParseExc(p.getVal(secStr, "track_normalize_chain").line,
p.getVal(secStr, "station_normalize_chain").pos,
"<valid regular expression>",
std::string("<regex error: ") + e.what() + ">", s);
}
}
if (p.hasKey(secStr, "line_normalize_chain")) {
try {
auto arr = p.getStrArr(secStr, "line_normalize_chain", ';');
curCfg.osmBuildOpts.lineNormzer =
trgraph::Normalizer(getNormRules(arr));
} catch (const std::exception& e) {
throw ParseExc(p.getVal(secStr, "station_normalize_chain").line,
p.getVal(secStr, "station_normalize_chain").pos,
"<valid regular expression>",
std::string("<regex error: ") + e.what() + ">", s);
}
}
bool found = false;
for (auto& cfg : _cfgs) {
if (cfg == curCfg) {
for (auto mot : Route::getTypesFromString(secStr)) {
cfg.mots.insert(mot);
}
found = true;
break;
}
}
if (!found) {
curCfg.mots = Route::getTypesFromString(secStr);
_cfgs.push_back(curCfg);
}
}
}
}
// _____________________________________________________________________________
ReplRules MotConfigReader::getNormRules(
const std::vector<std::string>& arr) const {
trgraph::ReplRules ret;
for (auto a : arr) {
size_t p = a.find(" -> ");
if (p == std::string::npos) continue;
trgraph::ReplRule r;
r.first = a.substr(0, p);
r.second = a.substr(p + 4, std::string::npos);
if (r.first.size() > 1 && r.first.front() == '\'' &&
r.first.back() == '\'') {
r.first = r.first.substr(1, r.first.size() - 2);
}
if (r.second.size() > 1 && r.second.front() == '\'' &&
r.second.back() == '\'') {
r.second = r.second.substr(1, r.second.size() - 2);
}
ret.push_back(r);
}
return ret;
}
// _____________________________________________________________________________
uint64_t MotConfigReader::getFlags(const std::set<string>& flags) const {
uint64_t ret = osm::USE;
for (const auto& flag : flags) {
if (flag == "rel_flat") {
ret |= osm::REL_NO_DOWN;
continue;
}
if (flag == "no_match_nds") {
ret |= osm::NO_NODES;
continue;
}
if (flag == "no_match_rels") {
ret |= osm::NO_RELATIONS;
continue;
}
if (flag == "no_match_ways") {
ret |= osm::NO_WAYS;
continue;
}
if (flag == "mult_val_match") {
ret |= osm::MULT_VAL_MATCH;
continue;
}
}
return ret;
}
// _____________________________________________________________________________
FilterRule MotConfigReader::getFRule(const std::string& r) const {
osm::FilterRule ret;
auto parts = util::split(util::trim(r), '|');
ret.kv = getKv(parts[0]);
ret.flags = std::set<std::string>(parts.begin() + 1, parts.end());
return ret;
}
// _____________________________________________________________________________
KeyVal MotConfigReader::getKv(const std::string& kv) const {
osm::KeyVal ret;
size_t p = kv.find('=', 0);
ret.first = kv.substr(0, p);
if (p != std::string::npos) {
ret.second = kv.substr(p + 1, std::string::npos);
}
return ret;
}
// _____________________________________________________________________________
const std::vector<MotConfig>& MotConfigReader::getConfigs() const {
return _cfgs;
}
// _____________________________________________________________________________
DeepAttrRule MotConfigReader::getDeepAttrRule(const std::string& rule) const {
if (rule[0] == '[' && rule.find(']') != std::string::npos) {
auto kv = getFRule(rule.substr(1, rule.find(']') - 1));
std::string attr = rule.substr(rule.find(']') + 1);
return osm::DeepAttrRule{attr, kv};
} else {
return osm::DeepAttrRule{rule, osm::FilterRule()};
}
}

View file

@ -0,0 +1,42 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_CONFIG_MOTCONFIGREADER_H_
#define PFAEDLE_CONFIG_MOTCONFIGREADER_H_
#include <unordered_map>
#include <vector>
#include <set>
#include <string>
#include "ad/cppgtfs/gtfs/Route.h"
#include "configparser/ConfigFileParser.h"
#include "pfaedle/config/MotConfig.h"
#include "pfaedle/osm/OsmBuilder.h"
namespace pfaedle {
namespace config {
using ad::cppgtfs::gtfs::Route;
class MotConfigReader {
public:
MotConfigReader();
void parse(const std::vector<std::string>& paths);
const std::vector<MotConfig>& getConfigs() const;
private:
std::vector<MotConfig> _cfgs;
osm::KeyVal getKv(const std::string& kv) const;
osm::FilterRule getFRule(const std::string& kv) const;
trgraph::ReplRules getNormRules(const std::vector<std::string>& arr) const;
osm::DeepAttrRule getDeepAttrRule(const std::string& rule) const;
uint64_t getFlags(const std::set<string>& flags) const;
};
} // namespace config
} // namespace pfaedle
#endif // PFAEDLE_CONFIG_MOTCONFIGREADER_H_

View file

@ -0,0 +1,89 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_CONFIG_PFAEDLECONFIG_H_
#define PFAEDLE_CONFIG_PFAEDLECONFIG_H_
#include <sstream>
#include <string>
#include <vector>
#include <set>
#include "ad/cppgtfs/gtfs/Route.h"
namespace pfaedle {
namespace config {
using ad::cppgtfs::gtfs::Route;
struct Config {
Config()
: dbgOutputPath("geo"),
solveMethod("global"),
evalPath("."),
dropShapes(false),
useHMM(false),
writeGraph(false),
writeCombGraph(false),
evaluate(false),
buildTransitGraph(false),
gridSize(2000) {}
std::string dbgOutputPath;
std::string solveMethod;
std::string evalPath;
std::string shapeTripId;
std::string outputPath;
std::string writeOsm;
std::string osmPath;
std::string evalDfBins;
std::vector<std::string> feedPaths;
std::vector<std::string> configPaths;
std::set<Route::TYPE> mots;
bool dropShapes;
bool useHMM;
bool writeGraph;
bool writeCombGraph;
bool evaluate;
bool buildTransitGraph;
double gridSize;
std::string toString() {
std::stringstream ss;
ss << "trip-id: " << shapeTripId << "\n"
<< "output-path: " << outputPath << "\n"
<< "write-osm-path: " << writeOsm << "\n"
<< "read-osm-path: " << osmPath << "\n"
<< "debug-output-path: " << dbgOutputPath << "\n"
<< "drop-shapes: " << dropShapes << "\n"
<< "use-hmm: " << useHMM << "\n"
<< "write-graph: " << writeGraph << "\n"
<< "write-cgraph: " << writeCombGraph << "\n"
<< "grid-size: " << gridSize << "\n"
<< "feed-paths: ";
for (const auto& p : feedPaths) {
ss << p << " ";
}
ss << "\nconfig-paths: ";
for (const auto& p : configPaths) {
ss << p << " ";
}
ss << "\nmots: ";
for (const auto& mot : mots) {
ss << mot << " ";
}
ss << "\n";
return ss.str();
}
};
} // namespace config
} // namespace pfaedle
#endif // PFAEDLE_CONFIG_PFAEDLECONFIG_H_

View file

@ -0,0 +1,417 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include <csignal>
#include <fstream>
#include <set>
#include <string>
#include <utility>
#include "ad/cppgtfs/gtfs/Feed.h"
#include "pfaedle/eval/Collector.h"
#include "pfaedle/eval/Result.h"
#include "util/geo/Geo.h"
#include "util/geo/PolyLine.h"
#include "util/geo/output/GeoJsonOutput.h"
#include "util/log/Log.h"
using util::geo::FLine;
using util::geo::PolyLine;
using util::geo::FPoint;
using ad::cppgtfs::gtfs::Trip;
using ad::cppgtfs::gtfs::Shape;
using pfaedle::eval::Collector;
using pfaedle::eval::Result;
using util::geo::output::GeoJsonOutput;
// _____________________________________________________________________________
double Collector::add(const Trip* t, const Shape* oldS, const Shape* newS,
const std::vector<double>& newTripDists) {
if (!oldS) {
_noOrigShp++;
return 0;
}
for (auto st : t->getStopTimes()) {
if (st.getShapeDistanceTravelled() < 0) {
// we cannot safely compare trips without shape dist travelled
// info
_noOrigShp++;
return 0;
}
}
double fd = 0;
size_t unmatchedSegments;
double unmatchedSegmentsLength;
std::vector<double> oldDists;
FLine oldL = getWebMercLine(
oldS, t->getStopTimes().begin()->getShapeDistanceTravelled(),
(--t->getStopTimes().end())->getShapeDistanceTravelled(), &oldDists);
std::vector<double> newDists;
FLine newL = getWebMercLine(newS, -1, -1, &newDists);
std::ofstream fstr(_evalOutPath + "/trip-" + t->getId() + ".json");
GeoJsonOutput gjout(fstr);
auto oldSegs = segmentize(t, oldL, oldDists, 0);
auto newSegs = segmentize(t, newL, newDists, &newTripDists);
// cut both result at the beginning and end to clear evaluation from
// loops at the end
PolyLine<float> oldStart = oldSegs[0];
PolyLine<float> newStart = newSegs[0];
auto oldStartNew =
oldStart.getSegment(oldStart.projectOn(newSegs[0][0]).totalPos, 1);
auto newStartNew =
newStart.getSegment(newStart.projectOn(oldSegs[0][0]).totalPos, 1);
if (fabs(oldStartNew.getLength() - oldStart.getLength()) /
oldStart.getLength() <
0.5 &&
fabs(newStartNew.getLength() - newStart.getLength()) /
newStart.getLength() <
0.5) {
oldSegs[0] = oldStartNew.getLine();
newSegs[0] = newStartNew.getLine();
}
PolyLine<float> oldEnd = oldSegs[oldSegs.size() - 1];
PolyLine<float> newEnd = newSegs[oldSegs.size() - 1];
auto oldEndNew =
oldEnd.getSegment(0, oldEnd.projectOn(newSegs.back().back()).totalPos);
auto newEndNew =
newEnd.getSegment(0, newEnd.projectOn(oldSegs.back().back()).totalPos);
if (fabs(oldEndNew.getLength() - oldEnd.getLength()) / oldEnd.getLength() <
0.5 &&
fabs(newEndNew.getLength() - newEnd.getLength()) / newEnd.getLength() <
0.5) {
oldSegs[oldSegs.size() - 1] = oldEndNew.getLine();
newSegs[newSegs.size() - 1] = newEndNew.getLine();
}
// check for suspicious (most likely erroneous) lines in the
// ground truth data which have a long straight-line segment
for (auto oldL : oldSegs) {
for (size_t i = 1; i < oldL.size(); i++) {
if (util::geo::webMercMeterDist(oldL[i - 1], oldL[i]) > 500) {
// return 0;
}
}
}
// new lines build from cleaned-up shapes
FLine oldLCut;
FLine newLCut;
for (auto oldL : oldSegs) {
gjout.print(oldL, {{"ver", "old"}});
oldLCut.insert(oldLCut.end(), oldL.begin(), oldL.end());
}
for (auto newL : newSegs) {
gjout.print(newL, {{"ver", "new"}});
newLCut.insert(newLCut.end(), newL.begin(), newL.end());
}
gjout.flush();
fstr.close();
double fac = cos(2 * atan(exp((oldSegs.front().front().get<1>() +
oldSegs.back().back().get<1>()) /
6378137.0)) -
1.5707965);
if (_dCache.count(oldS) && _dCache.find(oldS)->second.count(newS)) {
fd = _dCache[oldS][newS];
} else {
fd = util::geo::accFrechetDistC(oldLCut, newLCut, 5 / fac) * fac;
_dCache[oldS][newS] = fd;
}
if (_dACache.count(oldS) && _dACache.find(oldS)->second.count(newS)) {
unmatchedSegments = _dACache[oldS][newS].first;
unmatchedSegmentsLength = _dACache[oldS][newS].second;
} else {
auto dA = getDa(oldSegs, newSegs);
_dACache[oldS][newS] = dA;
unmatchedSegments = dA.first;
unmatchedSegmentsLength = dA.second;
}
double totL = 0;
for (auto l : oldSegs) totL += util::geo::len(l) * fac;
// filter out shapes with a lenght of under 5 meters - they are most likely
// artifacts
if (totL < 5) {
_noOrigShp++;
return 0;
}
_fdSum += fd / totL;
_unmatchedSegSum += unmatchedSegments;
_unmatchedSegLengthSum += unmatchedSegmentsLength;
_results.insert(Result(t, fd / totL));
_resultsAN.insert(Result(t, static_cast<double>(unmatchedSegments) /
static_cast<double>(oldSegs.size())));
_resultsAL.insert(Result(t, unmatchedSegmentsLength / totL));
LOG(DEBUG) << "This result (" << t->getId()
<< "): A_N/N = " << unmatchedSegments << "/" << oldSegs.size()
<< " = "
<< static_cast<double>(unmatchedSegments) /
static_cast<double>(oldSegs.size())
<< " A_L/L = " << unmatchedSegmentsLength << "/" << totL << " = "
<< unmatchedSegmentsLength / totL << " d_f = " << fd;
return fd;
}
// _____________________________________________________________________________
std::vector<FLine> Collector::segmentize(
const Trip* t, const FLine& shape, const std::vector<double>& dists,
const std::vector<double>* newTripDists) {
std::vector<FLine> ret;
if (t->getStopTimes().size() < 2) return ret;
util::geo::PolyLine<float> pl(shape);
std::vector<std::pair<FPoint, double> > cuts;
size_t i = 0;
for (auto st : t->getStopTimes()) {
if (newTripDists) {
cuts.push_back(std::pair<FPoint, double>(
util::geo::latLngToWebMerc<float>(st.getStop()->getLat(),
st.getStop()->getLng()),
(*newTripDists)[i]));
} else {
cuts.push_back(std::pair<FPoint, double>(
util::geo::latLngToWebMerc<float>(st.getStop()->getLat(),
st.getStop()->getLng()),
st.getShapeDistanceTravelled()));
}
i++;
}
// get first half of geometry, and search for start point there!
size_t before = std::upper_bound(dists.begin(), dists.end(), cuts[1].second) -
dists.begin();
util::geo::PolyLine<float> l(
FLine(shape.begin(), shape.begin() + before + 1));
auto lastLp = l.projectOn(cuts.front().first);
for (size_t i = 1; i < cuts.size(); i++) {
size_t before = shape.size();
if (i < cuts.size() - 1 && cuts[i + 1].second > -0.5) {
before =
std::upper_bound(dists.begin(), dists.end(), cuts[i + 1].second) -
dists.begin();
}
util::geo::PolyLine<float> beforePl(
FLine(shape.begin(), shape.begin() + before));
auto curLp = beforePl.projectOnAfter(cuts[i].first, lastLp.lastIndex);
ret.push_back(pl.getSegment(lastLp, curLp).getLine());
lastLp = curLp;
}
// std::raise(SIGABRT);
return ret;
}
// _____________________________________________________________________________
FLine Collector::getWebMercLine(const Shape* s, double from, double t) {
return getWebMercLine(s, from, t, 0);
}
// _____________________________________________________________________________
FLine Collector::getWebMercLine(const Shape* s, double from, double to,
std::vector<double>* dists) {
FLine ret;
auto i = s->getPoints().begin();
for (; i != s->getPoints().end(); i++) {
auto p = *i;
if ((from < 0 || (p.travelDist - from) > -0.01)) {
if (to >= 0 && (p.travelDist - to) > 0.01) break;
FPoint mercP = util::geo::latLngToWebMerc<float>(p.lat, p.lng);
ret.push_back(mercP);
if (dists) dists->push_back(p.travelDist);
}
}
return ret;
}
// _____________________________________________________________________________
const std::set<Result>& Collector::getResults() const { return _results; }
// _____________________________________________________________________________
double Collector::getAvgDist() const { return _fdSum / _results.size(); }
// _____________________________________________________________________________
void Collector::printHisto(std::ostream* os, const std::set<Result>& result,
const std::vector<double>& bins) const {
size_t W = 60;
auto it = result.begin();
std::vector<std::pair<double, size_t> > res;
std::vector<const Trip*> examples;
size_t maxC = 0;
for (size_t i = 0; i < bins.size(); i++) {
size_t c = 0;
const Trip* trip = 0;
while (it != result.end() && it->getDist() <= (bins[i] + 0.001)) {
if (!trip) trip = it->getTrip();
c++;
it++;
}
if (c > maxC) maxC = c;
examples.push_back(trip);
res.push_back(std::pair<double, size_t>(bins[i], c));
}
size_t j = 0;
for (auto r : res) {
std::string range = util::toString(r.first);
(*os) << " < " << std::setfill(' ') << std::setw(10) << range << ": ";
size_t i = 0;
for (; i < W * (static_cast<double>(r.second) / static_cast<double>(maxC));
i++) {
(*os) << "|";
}
if (r.second)
(*os) << " (" << r.second << ", e.g. #" << examples[j]->getId() << ")";
(*os) << std::endl;
j++;
}
}
// _____________________________________________________________________________
std::vector<double> Collector::getBins(double mind, double maxd, size_t steps) {
double bin = (maxd - mind) / steps;
double curE = mind + bin;
std::vector<double> ret;
while (curE <= maxd) {
ret.push_back(curE);
curE += bin;
}
return ret;
}
// _____________________________________________________________________________
void Collector::printCsv(std::ostream* os, const std::set<Result>& result,
const std::vector<double>& bins) const {
auto it = result.begin();
std::vector<std::pair<double, size_t> > res;
for (size_t i = 0; i < bins.size(); i++) {
size_t c = 0;
const Trip* trip = 0;
while (it != result.end() && it->getDist() <= (bins[i] + 0.001)) {
if (!trip) trip = it->getTrip();
c++;
it++;
}
res.push_back(std::pair<double, size_t>(bins[i], c));
}
(*os) << "range, count\n";
for (auto r : res) {
(*os) << r.first << "," << r.second << "\n";
}
}
// _____________________________________________________________________________
void Collector::printStats(std::ostream* os) const {
size_t buckets = 10;
(*os) << "\n ===== Evalution results =====\n\n";
(*os) << std::setfill(' ') << std::setw(30)
<< " # of trips new shapes were matched for: " << _results.size()
<< "\n";
(*os) << std::setw(30) << " # of trips without input shapes: " << _noOrigShp
<< "\n";
if (_results.size()) {
(*os) << std::setw(30) << " highest distance to input shapes: "
<< (--_results.end())->getDist() << " (on trip #"
<< (--_results.end())->getTrip()->getId() << ")\n";
(*os) << std::setw(30) << " lowest distance to input shapes: "
<< (_results.begin())->getDist() << " (on trip #"
<< (_results.begin())->getTrip()->getId() << ")\n";
(*os) << std::setw(30) << " avg total frechet distance: " << getAvgDist()
<< "\n";
std::vector<double> dfBins = getBins(
(_results.begin())->getDist(), (--_results.end())->getDist(), buckets);
if (_dfBins.size()) dfBins = _dfBins;
(*os) << "\n -- Histogram of d_f for this run -- " << std::endl;
printHisto(os, _results, dfBins);
std::ofstream fstr1(_evalOutPath + "/eval-frechet.csv");
printCsv(&fstr1, _results, dfBins);
(*os) << "\n\n\n -- Histogram of A_N/N for this run -- " << std::endl;
printHisto(os, _resultsAN,
getBins((_resultsAN.begin())->getDist(),
(--_resultsAN.end())->getDist(), buckets));
std::ofstream fstr2(_evalOutPath + "/eval-AN.csv");
printCsv(&fstr2, _resultsAN, getBins(0, 1, 20));
(*os) << "\n\n\n -- Histogram of A_L/L for this run -- " << std::endl;
printHisto(os, _resultsAL,
getBins((_resultsAL.begin())->getDist(),
(--_resultsAL.end())->getDist(), buckets));
std::ofstream fstr3(_evalOutPath + "/eval-AL.csv");
printCsv(&fstr3, _resultsAL, getBins(0, 1, 20));
}
(*os) << "\n ===== End of evaluation results =====\n";
(*os) << std::endl;
}
// _____________________________________________________________________________
std::pair<size_t, double> Collector::getDa(const std::vector<FLine>& a,
const std::vector<FLine>& b) {
assert(a.size() == b.size());
std::pair<size_t, double> ret{0, 0};
// euclidean distance on web mercator is in meters on equator,
// and proportional to cos(lat) in both y directions
double fac =
cos(2 * atan(exp((a.front().front().get<1>() + a.back().back().get<1>()) /
6378137.0)) -
1.5707965);
for (size_t i = 0; i < a.size(); i++) {
double fd = util::geo::frechetDist(a[i], b[i], 3 / fac) * fac;
if (fd >= 20) {
ret.first++;
ret.second += util::geo::len(a[i]) * fac;
}
}
return ret;
}

View file

@ -0,0 +1,96 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_EVAL_COLLECTOR_H_
#define PFAEDLE_EVAL_COLLECTOR_H_
#include <map>
#include <ostream>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "ad/cppgtfs/gtfs/Feed.h"
#include "pfaedle/eval/Result.h"
#include "util/geo/Geo.h"
using ad::cppgtfs::gtfs::Trip;
using ad::cppgtfs::gtfs::Shape;
using util::geo::FLine;
namespace pfaedle {
namespace eval {
/*
* Collects routing results for evaluation
*/
class Collector {
public:
Collector(const std::string& evalOutPath, const std::vector<double>& dfBins)
: _noOrigShp(0),
_noMatchShp(0),
_fdSum(0),
_unmatchedSegSum(0),
_unmatchedSegLengthSum(0),
_evalOutPath(evalOutPath),
_dfBins(dfBins) {}
// Add a shape found by our tool newS for a trip t with newly calculated
// station dist values with the old shape oldS
double add(const Trip* t, const Shape* oldS, const Shape* newS,
const std::vector<double>& newDists);
// Return the set of all Result objects
const std::set<Result>& getResults() const;
// Print general stats to os
void printStats(std::ostream* os) const;
// Print histogramgs for the results to os
void printHisto(std::ostream* os, const std::set<Result>& result,
const std::vector<double>& bins) const;
// Print a CSV for the results to os
void printCsv(std::ostream* os, const std::set<Result>& result,
const std::vector<double>& bins) const;
// Return the averaged average frechet distance
double getAvgDist() const;
static FLine getWebMercLine(const Shape* s, double from, double to);
static FLine getWebMercLine(const Shape* s, double from, double to,
std::vector<double>* dists);
private:
std::set<Result> _results;
std::set<Result> _resultsAN;
std::set<Result> _resultsAL;
std::map<const Shape*, std::map<const Shape*, double> > _dCache;
std::map<const Shape*, std::map<const Shape*, std::pair<size_t, double> > >
_dACache;
size_t _noOrigShp;
size_t _noMatchShp;
double _fdSum;
size_t _unmatchedSegSum;
double _unmatchedSegLengthSum;
std::string _evalOutPath;
std::vector<double> _dfBins;
static std::pair<size_t, double> getDa(const std::vector<FLine>& a,
const std::vector<FLine>& b);
static std::vector<FLine> segmentize(const Trip* t, const FLine& shape,
const std::vector<double>& dists,
const std::vector<double>* newTripDists);
static std::vector<double> getBins(double mind, double maxd, size_t steps);
};
} // namespace eval
} // namespace pfaedle
#endif // PFAEDLE_EVAL_COLLECTOR_H_

39
src/pfaedle/eval/Result.h Normal file
View file

@ -0,0 +1,39 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_EVAL_RESULT_H_
#define PFAEDLE_EVAL_RESULT_H_
#include "ad/cppgtfs/gtfs/Feed.h"
using ad::cppgtfs::gtfs::Trip;
using ad::cppgtfs::gtfs::Shape;
namespace pfaedle {
namespace eval {
/*
* A single evaluation result.
*/
class Result {
public:
Result(const Trip* t, double dist) : _t(t), _dist(dist) {}
double getDist() const { return _dist; }
const Trip* getTrip() const { return _t; }
private:
const Trip* _t;
double _dist;
};
inline bool operator<(const Result& lhs, const Result& rhs) {
return lhs.getDist() < rhs.getDist() ||
(lhs.getDist() == rhs.getDist() && lhs.getTrip() < rhs.getTrip());
}
} // namespace eval
} // namespace pfaedle
#endif // PFAEDLE_EVAL_RESULT_H_

View file

@ -0,0 +1,41 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_NETGRAPH_EDGEPL_H_
#define PFAEDLE_NETGRAPH_EDGEPL_H_
#include <set>
#include <string>
#include <map>
#include "ad/cppgtfs/gtfs/Feed.h"
#include "util/geo/GeoGraph.h"
using util::geograph::GeoEdgePL;
using ad::cppgtfs::gtfs::Trip;
namespace pfaedle {
namespace netgraph {
/*
* A payload class for edges on a network graph - that is a graph
* that exactly represents a physical public transit network
*/
class EdgePL : public GeoEdgePL<float> {
public:
EdgePL() {}
EdgePL(const util::geo::FLine& l, const std::set<const Trip*>& trips)
: _l(l), _trips(trips) {}
const util::geo::FLine* getGeom() const { return &_l; }
void getAttrs(std::map<std::string, std::string>* obj) const {
(*obj)["numtrips"] = std::to_string(_trips.size());
}
private:
util::geo::FLine _l;
std::set<const Trip*> _trips;
};
} // namespace netgraph
} // namespace pfaedle
#endif // PFAEDLE_NETGRAPH_EDGEPL_H_

View file

@ -0,0 +1,31 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_NETGRAPH_GRAPH_H_
#define PFAEDLE_NETGRAPH_GRAPH_H_
#include "pfaedle/netgraph/NodePL.h"
#include "pfaedle/netgraph/EdgePL.h"
#include "util/graph/UndirGraph.h"
using util::geo::Point;
using util::geo::Line;
using util::geo::FPoint;
using util::geo::FLine;
namespace pfaedle {
namespace netgraph {
/*
* A payload class for edges on a network graph - that is a graph
* that exactly represents a physical public transit network
*/
typedef util::graph::Edge<NodePL, EdgePL> Edge;
typedef util::graph::Node<NodePL, EdgePL> Node;
typedef util::graph::UndirGraph<NodePL, EdgePL> Graph;
} // namespace netgraph
} // namespace pfaedle
#endif // PFAEDLE_NETGRAPH_GRAPH_H_

View file

@ -0,0 +1,38 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_NETGRAPH_NODEPL_H_
#define PFAEDLE_NETGRAPH_NODEPL_H_
#include <map>
#include <string>
#include "ad/cppgtfs/gtfs/Feed.h"
#include "util/geo/GeoGraph.h"
using util::geograph::GeoNodePL;
namespace pfaedle {
namespace netgraph {
/*
* A payload class for edges on a network graph - that is a graph
* that exactly represents a physical public transit network
*/
class NodePL : public GeoNodePL<float> {
public:
NodePL() {}
NodePL(const util::geo::FPoint& geom) { _geom = geom; } // NOLINT
const util::geo::FPoint* getGeom() const { return &_geom; }
void getAttrs(std::map<std::string, std::string>* attrs) const {
UNUSED(attrs);
}
private:
util::geo::FPoint _geom;
};
} // namespace netgraph
} // namespace pfaedle
#endif // PFAEDLE_NETGRAPH_NODEPL_H_

View file

@ -0,0 +1,80 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include "pfaedle/osm/BBoxIdx.h"
using pfaedle::osm::BBoxIdx;
// _____________________________________________________________________________
BBoxIdx::BBoxIdx(float padding) : _padding(padding), _size(0) {}
// _____________________________________________________________________________
void BBoxIdx::add(Box<float> box) {
// division by 83.000m is only correct here around a latitude deg of 25,
// but should be a good heuristic. 1 deg is around 63km at latitude deg of 44,
// and 110 at deg=0, since we usually dont do map matching in the arctic,
// its okay to use 83km here.
box = util::geo::pad(box, _padding / 83000);
addToTree(box, &_root, 0);
_size++;
}
// _____________________________________________________________________________
size_t BBoxIdx::size() const { return _size; }
// _____________________________________________________________________________
bool BBoxIdx::contains(const Point<float>& p) const {
return treeHas(p, _root);
}
// _____________________________________________________________________________
util::geo::Box<float> BBoxIdx::getFullWebMercBox() const {
return util::geo::FBox(
util::geo::latLngToWebMerc<float>(_root.box.min_corner().get<1>(),
_root.box.min_corner().get<0>()),
util::geo::latLngToWebMerc<float>(_root.box.max_corner().get<1>(),
_root.box.max_corner().get<0>()));
}
// _____________________________________________________________________________
bool BBoxIdx::treeHas(const Point<float>& p, const BBoxIdxNd& nd) const {
if (!nd.childs.size()) return util::geo::contains(p, nd.box);
for (const auto& child : nd.childs) {
if (util::geo::contains(p, child.box)) return treeHas(p, child);
}
return false;
}
// _____________________________________________________________________________
void BBoxIdx::addToTree(const Box<float>& box, BBoxIdxNd* nd, size_t lvl) {
double bestCommonArea = 0;
ssize_t bestChild = -1;
// 1. update the bbox of this node
nd->box = util::geo::extendBox(box, nd->box);
if (lvl == MAX_LVL) return;
// 2. find best candidate
for (size_t i = 0; i < nd->childs.size(); i++) {
double cur = util::geo::commonArea(box, nd->childs[i].box);
if (cur > MIN_COM_AREA && cur > bestCommonArea) {
bestChild = i;
bestCommonArea = cur;
}
}
if (bestChild < 0) {
// 3. add a new node with the inserted bbox
nd->childs.push_back(BBoxIdxNd(box));
addToTree(box, &nd->childs.back(), lvl + 1);
} else {
// 3. add to best node
addToTree(box, &nd->childs[bestChild], lvl + 1);
}
// TODO(patrick): some tree balancing by mergin overlapping bboxes in
// non-leafs
}

58
src/pfaedle/osm/BBoxIdx.h Normal file
View file

@ -0,0 +1,58 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_OSM_BBOXIDX_H_
#define PFAEDLE_OSM_BBOXIDX_H_
#include <vector>
#include "util/geo/Geo.h"
namespace pfaedle {
namespace osm {
using util::geo::Box;
using util::geo::Point;
struct BBoxIdxNd {
BBoxIdxNd() : box(util::geo::minbox<float>()) {}
explicit BBoxIdxNd(const Box<float>& box) : box(box) {}
Box<float> box;
std::vector<BBoxIdxNd> childs;
};
/*
* Poor man's R-tree
*/
class BBoxIdx {
public:
explicit BBoxIdx(float padding);
// Add a bounding box to this index
void add(Box<float> box);
// Check if a point is contained in this index
bool contains(const Point<float>& box) const;
// Return the full total bounding box of this index
util::geo::Box<float> getFullWebMercBox() const;
// Return the size of this index
size_t size() const;
private:
double _padding;
size_t _size;
BBoxIdxNd _root;
void addToTree(const Box<float>& box, BBoxIdxNd* nd, size_t lvl);
bool treeHas(const Point<float>& p, const BBoxIdxNd& nd) const;
static const size_t MAX_LVL = 5;
static constexpr double MIN_COM_AREA = 0.0;
};
} // namespace osm
} // namespace pfaedle
#endif // PFAEDLE_OSM_BBOXIDX_H_

70
src/pfaedle/osm/Osm.h Normal file
View file

@ -0,0 +1,70 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_OSM_OSM_H_
#define PFAEDLE_OSM_OSM_H_
#include <set>
#include <string>
#include <utility>
#include <unordered_map>
#include <vector>
namespace pfaedle {
namespace osm {
typedef uint64_t osmid;
typedef std::unordered_map<std::string, std::string> AttrMap;
typedef std::pair<std::string, std::string> Attr;
typedef std::vector<osmid> OsmIdList;
struct OsmRel {
OsmRel() : id(0) {}
osmid id;
AttrMap attrs;
std::vector<osmid> nodes;
std::vector<osmid> ways;
std::vector<std::string> nodeRoles;
std::vector<std::string> wayRoles;
uint64_t keepFlags;
uint64_t dropFlags;
};
struct OsmWay {
OsmWay() : id(0) {}
osmid id;
AttrMap attrs;
std::vector<osmid> nodes;
uint64_t keepFlags;
uint64_t dropFlags;
};
struct OsmNode {
OsmNode() : id(0) {}
osmid id;
double lat;
double lng;
AttrMap attrs;
uint64_t keepFlags;
uint64_t dropFlags;
};
struct Restriction {
osmid eFrom, eTo;
};
typedef std::unordered_map<osmid, std::vector<Restriction>> RestrMap;
struct Restrictions {
RestrMap pos;
RestrMap neg;
};
} // namespace osm
} // namespace pfaedle
#endif // PFAEDLE_OSM_OSM_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,252 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_OSM_OSMBUILDER_H_
#define PFAEDLE_OSM_OSMBUILDER_H_
#include <queue>
#include <set>
#include <map>
#include <string>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include "ad/cppgtfs/gtfs/Feed.h"
#include "pfaedle/osm/BBoxIdx.h"
#include "pfaedle/osm/OsmFilter.h"
#include "pfaedle/osm/OsmIdSet.h"
#include "pfaedle/osm/OsmReadOpts.h"
#include "pfaedle/osm/Restrictor.h"
#include "pfaedle/router/Router.h"
#include "pfaedle/trgraph/Graph.h"
#include "pfaedle/trgraph/Normalizer.h"
#include "pfaedle/trgraph/StatInfo.h"
#include "util/Nullable.h"
#include "util/xml/XmlWriter.h"
#include "xml/File.h"
namespace pfaedle {
namespace osm {
using pfaedle::trgraph::EdgeGrid;
using pfaedle::trgraph::NodeGrid;
using pfaedle::trgraph::Normalizer;
using pfaedle::trgraph::Graph;
using pfaedle::trgraph::Node;
using pfaedle::trgraph::NodePL;
using pfaedle::trgraph::Edge;
using pfaedle::trgraph::EdgePL;
using pfaedle::trgraph::TransitEdgeLine;
using pfaedle::trgraph::StatInfo;
using pfaedle::trgraph::StatGroup;
using pfaedle::trgraph::Component;
using pfaedle::router::NodeSet;
using ad::cppgtfs::gtfs::Stop;
using util::Nullable;
struct NodeCand {
double dist;
Node* node;
const Edge* fromEdge;
int fullTurns;
};
struct SearchFunc {
virtual bool operator()(const Node* n, const StatInfo* si) const = 0;
};
struct EqSearch : public SearchFunc {
double minSimi = 0.9;
bool operator()(const Node* cand, const StatInfo* si) const {
return cand->pl().getSI() && cand->pl().getSI()->simi(si) > minSimi;
}
};
struct BlockSearch : public SearchFunc {
bool operator()(const Node* n, const StatInfo* si) const {
if (n->pl().getSI() && n->pl().getSI()->simi(si) < 0.5) return true;
return n->pl().isBlocker();
}
};
inline bool operator<(const NodeCand& a, const NodeCand& b) {
return a.fullTurns > b.fullTurns || a.dist > b.dist;
}
typedef std::priority_queue<NodeCand> NodeCandPQ;
/*
* Builds a physical transit network graph from OSM data
*/
class OsmBuilder {
public:
OsmBuilder();
// Read the OSM file at path, and write a graph to g. Only elements
// inside the bounding box will be read
void read(const std::string& path, const OsmReadOpts& opts, Graph* g,
const BBoxIdx& box, size_t gridSize, router::FeedStops* fs,
Restrictor* res);
// Based on the list of options, read an OSM file from in and output an
// OSM file to out which contains exactly the entities that are needed
// from the file at in
void filterWrite(const std::string& in, const std::string& out,
const std::vector<OsmReadOpts>& opts, const BBoxIdx& box);
private:
xml::ParserState readBBoxNds(xml::File* xml, OsmIdSet* nodes,
OsmIdSet* noHupNodes, const OsmFilter& filter,
const BBoxIdx& bbox) const;
void readRels(xml::File* f, RelLst* rels, RelMap* nodeRels, RelMap* wayRels,
const OsmFilter& filter, const AttrKeySet& keepAttrs,
Restrictions* rests) const;
void readRestr(const OsmRel& rel, Restrictions* rests,
const OsmFilter& filter) const;
void readNodes(xml::File* f, Graph* g, const RelLst& rels,
const RelMap& nodeRels, const OsmFilter& filter,
const OsmIdSet& bBoxNodes, NIdMap* nodes,
NIdMultMap* multNodes, NodeSet* orphanStations,
const AttrKeySet& keepAttrs, const FlatRels& flatRels,
const OsmReadOpts& opts) const;
void readWriteNds(xml::File* i, util::xml::XmlWriter* o,
const RelMap& nodeRels, const OsmFilter& filter,
const OsmIdSet& bBoxNodes, NIdMap* nodes,
const AttrKeySet& keepAttrs, const FlatRels& f) const;
void readWriteWays(xml::File* i, util::xml::XmlWriter* o, OsmIdList* ways,
const AttrKeySet& keepAttrs) const;
void readWriteRels(xml::File* i, util::xml::XmlWriter* o, OsmIdList* ways,
NIdMap* nodes, const OsmFilter& filter,
const AttrKeySet& keepAttrs);
void readEdges(xml::File* xml, Graph* g, const RelLst& rels,
const RelMap& wayRels, const OsmFilter& filter,
const OsmIdSet& bBoxNodes, NIdMap* nodes,
NIdMultMap* multNodes, const OsmIdSet& noHupNodes,
const AttrKeySet& keepAttrs, const Restrictions& rest,
Restrictor* restor, const FlatRels& flatRels,
EdgTracks* etracks, const OsmReadOpts& opts);
void readEdges(xml::File* xml, const RelMap& wayRels, const OsmFilter& filter,
const OsmIdSet& bBoxNodes, const AttrKeySet& keepAttrs,
OsmIdList* ret, NIdMap* nodes, const FlatRels& flatRels);
OsmWay nextWay(xml::File* xml, const RelMap& wayRels, const OsmFilter& filter,
const OsmIdSet& bBoxNodes, const AttrKeySet& keepAttrs,
const FlatRels& flatRels) const;
bool keepWay(const OsmWay& w, const RelMap& wayRels, const OsmFilter& filter,
const OsmIdSet& bBoxNodes, const FlatRels& fl) const;
OsmWay nextWayWithId(xml::File* xml, osmid wid,
const AttrKeySet& keepAttrs) const;
OsmNode nextNode(xml::File* xml, NIdMap* nodes, NIdMultMap* multNodes,
const RelMap& nodeRels, const OsmFilter& filter,
const OsmIdSet& bBoxNodes, const AttrKeySet& keepAttrs,
const FlatRels& flatRels) const;
bool keepNode(const OsmNode& n, const NIdMap& nodes,
const NIdMultMap& multNodes, const RelMap& nodeRels,
const OsmIdSet& bBoxNodes, const OsmFilter& filter,
const FlatRels& fl) const;
OsmRel nextRel(xml::File* xml, const OsmFilter& filter,
const AttrKeySet& keepAttrs) const;
Nullable<StatInfo> getStatInfo(Node* node, osmid nid, const FPoint& pos,
const AttrMap& m, StAttrGroups* groups,
const RelMap& nodeRels, const RelLst& rels,
const OsmReadOpts& ops) const;
void writeGeoms(Graph* g) const;
void deleteOrphNds(Graph* g) const;
void deleteOrphEdgs(Graph* g) const;
double dist(const Node* a, const Node* b) const;
double webMercDist(const Node* a, const Node* b) const;
double webMercDistFactor(const FPoint& a) const;
NodeGrid buildNodeIdx(Graph* g, size_t size,
const util::geo::Box<float>& webMercBox,
bool which) const;
EdgeGrid buildEdgeIdx(Graph* g, size_t size,
const util::geo::Box<float>& webMercBox) const;
void fixGaps(Graph* g, NodeGrid* ng) const;
void collapseEdges(Graph* g) const;
void writeODirEdgs(Graph* g, Restrictor* restor) const;
void writeSelfEdgs(Graph* g) const;
void writeEdgeTracks(const EdgTracks& tracks) const;
void simplifyGeoms(Graph* g) const;
uint32_t writeComps(Graph* g) const;
bool edgesSim(const Edge* a, const Edge* b) const;
const EdgePL& mergeEdgePL(Edge* a, Edge* b) const;
void getEdgCands(const FPoint& s, EdgeCandPQ* ret, EdgeGrid* eg,
double d) const;
std::set<Node*> getMatchingNds(const NodePL& s, NodeGrid* ng, double d) const;
Node* getMatchingNd(const NodePL& s, NodeGrid* ng, double d) const;
NodeSet snapStation(Graph* g, NodePL* s, EdgeGrid* eg, NodeGrid* sng,
const OsmReadOpts& opts, Restrictor* restor, bool surHeur,
double maxD) const;
// Checks if from the edge e, a station similar to si can be reach with less
// than maxD distance and less or equal to "maxFullTurns" full turns. If
// such a station exists, it is returned. If not, 0 is returned.
Node* eqStatReach(const Edge* e, const StatInfo* si, const FPoint& p,
double maxD, int maxFullTurns, double maxAng) const;
Node* depthSearch(const Edge* e, const StatInfo* si,
const util::geo::FPoint& p, double maxD, int maxFullTurns,
double minAngle, const SearchFunc& sfunc) const;
bool isBlocked(const Edge* e, const StatInfo* si, const FPoint& p,
double maxD, int maxFullTurns, double minAngle) const;
StatGroup* groupStats(const NodeSet& s) const;
std::vector<TransitEdgeLine*> getLines(const std::vector<size_t>& edgeRels,
const RelLst& rels,
const OsmReadOpts& ops);
NodePL plFromGtfs(const Stop* s, const OsmReadOpts& ops) const;
void getKeptAttrKeys(const OsmReadOpts& opts, AttrKeySet sets[3]) const;
void skipUntil(xml::File* xml, const std::string& s) const;
void processRestr(osmid nid, osmid wid, const Restrictions& rawRests, Edge* e,
Node* n, Restrictor* restor) const;
std::string getAttrByFirstMatch(const DeepAttrLst& rule, osmid id,
const AttrMap& attrs, const RelMap& entRels,
const RelLst& rels,
const Normalizer& norm) const;
std::vector<std::string> getAttrMatchRanked(const DeepAttrLst& rule, osmid id,
const AttrMap& attrs,
const RelMap& entRels,
const RelLst& rels,
const Normalizer& norm) const;
std::string getAttr(const DeepAttrRule& s, osmid id, const AttrMap& attrs,
const RelMap& entRels, const RelLst& rels) const;
bool relKeep(osmid id, const RelMap& rels, const FlatRels& fl) const;
std::map<TransitEdgeLine, TransitEdgeLine*> _lines;
std::map<size_t, TransitEdgeLine*> _relLines;
};
} // namespace osm
} // namespace pfaedle
#endif // PFAEDLE_OSM_OSMBUILDER_H_

View file

@ -0,0 +1,260 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include <iostream>
#include <sstream>
#include <string>
#include "pfaedle/osm/OsmFilter.h"
using pfaedle::osm::OsmFilter;
// _____________________________________________________________________________
OsmFilter::OsmFilter(const MultAttrMap& keep, const MultAttrMap& drop)
: _keep(keep), _drop(drop) {}
// _____________________________________________________________________________
OsmFilter::OsmFilter(const OsmReadOpts& o)
: _keep(o.keepFilter),
_drop(o.dropFilter),
_nohup(o.noHupFilter),
_oneway(o.oneWayFilter),
_onewayrev(o.oneWayFilterRev),
_twoway(o.twoWayFilter),
_station(o.stationFilter),
_blocker(o.stationBlockerFilter),
_posRestr(o.restrPosRestr),
_negRestr(o.restrNegRestr),
_noRestr(o.noRestrFilter),
_levels(o.levelFilters) {}
// _____________________________________________________________________________
uint64_t OsmFilter::keep(const AttrMap& attrs, Type t) const {
return contained(attrs, _keep, t);
}
// _____________________________________________________________________________
uint64_t OsmFilter::drop(const AttrMap& attrs, Type t) const {
return contained(attrs, _drop, t);
}
// _____________________________________________________________________________
uint64_t OsmFilter::nohup(const char* key, const char* v) const {
const auto& dkv = _nohup.find(key);
if (dkv != _nohup.end()) {
for (const auto& val : dkv->second) {
if (valMatches(v, val.first)) return true;
}
}
return false;
}
// _____________________________________________________________________________
uint64_t OsmFilter::oneway(const AttrMap& attrs) const {
if (contained(attrs, _twoway, WAY)) return false;
return contained(attrs, _oneway, WAY);
}
// _____________________________________________________________________________
uint64_t OsmFilter::onewayrev(const AttrMap& attrs) const {
if (contained(attrs, _twoway, WAY)) return false;
return contained(attrs, _onewayrev, WAY);
}
// _____________________________________________________________________________
uint64_t OsmFilter::station(const AttrMap& attrs) const {
return contained(attrs, _station, NODE);
}
// _____________________________________________________________________________
uint64_t OsmFilter::blocker(const AttrMap& attrs) const {
return contained(attrs, _blocker, NODE);
}
// _____________________________________________________________________________
uint64_t OsmFilter::contained(const AttrMap& attrs, const MultAttrMap& map,
Type t) {
for (const auto& kv : attrs) {
const auto& dkv = map.find(kv.first);
if (dkv != map.end()) {
for (const auto& val : dkv->second) {
bool multValMatch = val.second & osm::MULT_VAL_MATCH;
if (val.second & t) continue;
if (valMatches(kv.second, val.first, multValMatch)) return val.second;
}
}
}
return 0;
}
// _____________________________________________________________________________
uint64_t OsmFilter::contained(const AttrMap& attrs, const Attr& attr) {
for (const auto& kv : attrs) {
if (kv.first == attr.first) return valMatches(kv.second, attr.second);
}
return 0;
}
// _____________________________________________________________________________
uint8_t OsmFilter::level(const AttrMap& attrs) const {
// the best matching level is always returned
for (int16_t i = 0; i < 7; i++) {
for (const auto& kv : attrs) {
const auto& lkv = (_levels + i)->find(kv.first);
if (lkv != (_levels + i)->end()) {
for (const auto& val : lkv->second) {
if (valMatches(kv.second, val.first)) return i + 1;
}
}
}
}
return 0;
}
// _____________________________________________________________________________
bool OsmFilter::valMatches(const std::string& a, const std::string& b) {
return valMatches(a, b, false);
}
// _____________________________________________________________________________
bool OsmFilter::valMatches(const std::string& a, const std::string& b, bool m) {
if (b == "*") return true;
if (m) {
// search for occurances in semicolon separated list
if (a.find(std::string(";") + b) != std::string::npos) return true;
if (a.find(b + ";") != std::string::npos) return true;
if (a.find(std::string("; ") + b) != std::string::npos) return true;
if (a.find(b + " ;") != std::string::npos) return true;
}
return a == b;
}
// _____________________________________________________________________________
std::vector<std::string> OsmFilter::getAttrKeys() const {
std::vector<std::string> ret;
for (const auto& kv : _keep) {
ret.push_back(kv.first);
}
for (const auto& kv : _drop) {
ret.push_back(kv.first);
}
for (const auto& kv : _nohup) {
ret.push_back(kv.first);
}
for (const auto& kv : _oneway) {
ret.push_back(kv.first);
}
for (const auto& kv : _twoway) {
ret.push_back(kv.first);
}
for (const auto& kv : _station) {
ret.push_back(kv.first);
}
for (const auto& kv : _blocker) {
ret.push_back(kv.first);
}
for (const auto& kv : _posRestr) {
ret.push_back(kv.first);
}
for (const auto& kv : _negRestr) {
ret.push_back(kv.first);
}
for (const auto& kv : _noRestr) {
ret.push_back(kv.first);
}
for (uint8_t i = 0; i < 7; i++) {
for (const auto& kv : *(_levels + i)) {
ret.push_back(kv.first);
}
}
return ret;
}
// _____________________________________________________________________________
OsmFilter OsmFilter::merge(const OsmFilter& other) const {
MultAttrMap keep;
MultAttrMap drop;
for (const auto& kv : _keep) {
keep[kv.first].insert(kv.second.begin(), kv.second.end());
}
for (const auto& kv : other._keep) {
keep[kv.first].insert(kv.second.begin(), kv.second.end());
}
// TODO(patrick): multi-level combination for filters. otherwise
// filter drop filters meant as a refinement for keep filters
// interfere with other keeps
// const auto* d = &_drop;
// for (size_t i = 0; i < 2; i++) {
// for (const auto& kv : *d) {
// if (keep.find(kv.first) != keep.end()) {
// for (const auto& val : kv.second) {
// if (keep[kv.first].find(val.first) == keep[kv.first].end()) {
// drop[kv.first].insert(val);
// }
// }
// } else {
// drop[kv.first].insert(kv.second.begin(), kv.second.end());
// }
// }
// d = &other._drop;
// }
return OsmFilter(keep, drop);
}
// _____________________________________________________________________________
std::string OsmFilter::toString() const {
std::stringstream ss;
ss << "[KEEP]\n\n";
for (const auto& kv : _keep) {
ss << " " << kv.first << "=";
bool first = false;
for (const auto& v : kv.second) {
if (first) ss << ",";
first = true;
ss << v.first;
}
ss << "\n";
}
ss << "\n[DROP]\n\n";
for (const auto& kv : _drop) {
ss << " " << kv.first << "=";
bool first = false;
for (const auto& v : kv.second) {
if (first) ss << ",";
first = true;
ss << v.first;
}
ss << "\n";
}
return ss.str();
}
// _____________________________________________________________________________
uint64_t OsmFilter::negRestr(const AttrMap& attrs) const {
if (contained(attrs, _noRestr, ALL)) return false;
return (contained(attrs, _negRestr, ALL));
}
// _____________________________________________________________________________
uint64_t OsmFilter::posRestr(const AttrMap& attrs) const {
if (contained(attrs, _noRestr, ALL)) return false;
return (contained(attrs, _posRestr, ALL));
}

View file

@ -0,0 +1,51 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_OSM_OSMFILTER_H_
#define PFAEDLE_OSM_OSMFILTER_H_
#include <string>
#include <vector>
#include "pfaedle/osm/Osm.h"
#include "pfaedle/osm/OsmReadOpts.h"
namespace pfaedle {
namespace osm {
class OsmFilter {
public:
enum Type : uint64_t { NODE = 16, WAY = 8, REL = 4, ALL = 0 };
OsmFilter() {}
OsmFilter(const MultAttrMap& keep, const MultAttrMap& drop);
explicit OsmFilter(const OsmReadOpts& o);
uint64_t keep(const AttrMap& attrs, Type t) const;
uint64_t drop(const AttrMap& attrs, Type t) const;
uint64_t nohup(const char* key, const char* val) const;
uint8_t level(const AttrMap& attrs) const;
uint64_t oneway(const AttrMap& attrs) const;
uint64_t onewayrev(const AttrMap& attrs) const;
uint64_t station(const AttrMap& attrs) const;
uint64_t blocker(const AttrMap& attrs) const;
uint64_t negRestr(const AttrMap& attrs) const;
uint64_t posRestr(const AttrMap& attrs) const;
std::vector<std::string> getAttrKeys() const;
OsmFilter merge(const OsmFilter& other) const;
std::string toString() const;
static bool valMatches(const std::string& a, const std::string& b, bool m);
static bool valMatches(const std::string& a, const std::string& b);
static uint64_t contained(const AttrMap& attrs, const MultAttrMap& map,
Type t);
static uint64_t contained(const AttrMap& attrs, const Attr& map);
private:
MultAttrMap _keep, _drop, _nohup, _oneway, _onewayrev, _twoway, _station,
_blocker, _posRestr, _negRestr, _noRestr;
const MultAttrMap* _levels;
};
} // namespace osm
} // namespace pfaedle
#endif // PFAEDLE_OSM_OSMFILTER_H_

View file

@ -0,0 +1,302 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include <stdio.h>
#include <unistd.h>
#include <string>
#include <algorithm>
#include <cassert>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <exception>
#include <iostream>
#include <sstream>
#include "pfaedle/osm/OsmIdSet.h"
using pfaedle::osm::OsmIdSet;
size_t OsmIdSet::LOOKUPS = 0;
size_t OsmIdSet::FLOOKUPS = 0;
// _____________________________________________________________________________
OsmIdSet::OsmIdSet()
: _closed(false),
_sorted(true),
_last(0),
_smallest(-1),
_biggest(0),
_obufpos(0),
_curBlock(-1),
_fsize(0) {
_bitset = new std::bitset<BLOOMF_BITS>();
_file = openTmpFile();
_buffer = new unsigned char[BUFFER_S];
_outBuffer = new unsigned char[OBUFFER_S];
}
// _____________________________________________________________________________
OsmIdSet::~OsmIdSet() {
delete _bitset;
delete[] _buffer;
if (!_closed) delete[] _outBuffer;
}
// _____________________________________________________________________________
void OsmIdSet::add(osmid id) {
if (_closed) throw std::exception();
diskAdd(id);
// _set.insert(id);
if (_last > id) _sorted = false;
_last = id;
if (id < _smallest) _smallest = id;
if (id > _biggest) _biggest = id;
for (int i = 0; i < 10; i++) (*_bitset)[hash(id, i)] = 1;
}
// _____________________________________________________________________________
void OsmIdSet::diskAdd(osmid id) {
memcpy(_outBuffer + _obufpos, &id, 8);
_obufpos += 8;
if (_obufpos % BUFFER_S == 0) {
// this is the last value in this block
_blockEnds.push_back(id);
}
if (_obufpos >= OBUFFER_S) {
ssize_t w = cwrite(_file, _outBuffer, OBUFFER_S);
_fsize += w;
_obufpos = 0;
}
}
// _____________________________________________________________________________
size_t OsmIdSet::getBlock(osmid id) const {
auto it = std::upper_bound(_blockEnds.begin(), _blockEnds.end(), id);
return (it - _blockEnds.begin());
}
// _____________________________________________________________________________
bool OsmIdSet::diskHas(osmid id) const {
assert(_sorted);
if (std::find(_blockEnds.begin(), _blockEnds.end(), id) != _blockEnds.end()) {
return true;
}
size_t block = getBlock(id);
if (block != _curBlock) {
lseek(_file, block * BUFFER_S, SEEK_SET);
ssize_t n = cread(_file, _buffer, BUFFER_S);
_curBlockSize = n;
FLOOKUPS++;
_curBlock = block;
}
if (_curBlockSize <= 7) return false;
if (*(reinterpret_cast<uint64_t*>(_buffer)) > id) return false;
ssize_t l = 0;
ssize_t r = _curBlockSize - 8;
while (l <= r) {
unsigned char* p = _buffer + (l + ((r - l) / 16) * 8);
osmid cur = *(reinterpret_cast<uint64_t*>(p));
if (cur == id) return true;
if (cur < id)
l = (p - _buffer) + 8;
else
r = (p - _buffer) - 8;
}
return false;
}
// _____________________________________________________________________________
bool OsmIdSet::has(osmid id) const {
LOOKUPS++;
if (!_closed) close();
if (id < _smallest || id > _biggest) {
return false;
}
for (int i = 0; i < 10; i++) {
if ((*_bitset)[hash(id, i)] == 0) return false;
}
bool has = diskHas(id);
// assert(has == (bool)_set.count(id));
return has;
}
// _____________________________________________________________________________
void OsmIdSet::close() const {
ssize_t w = cwrite(_file, _outBuffer, _obufpos);
_fsize += w;
_blockEnds.push_back(_biggest);
delete[] _outBuffer;
_closed = true;
// if order was not sorted, sort now
if (!_sorted) sort();
}
// _____________________________________________________________________________
void OsmIdSet::sort() const {
// sort file via an external merge sort
_blockEnds.clear();
size_t parts = _fsize / SORT_BUFFER_S + 1;
size_t partsBufSize = ((SORT_BUFFER_S / 8) / parts + 1) * 8;
unsigned char* buf = new unsigned char[SORT_BUFFER_S];
unsigned char** partbufs = new unsigned char*[parts];
size_t* partpos = new size_t[parts];
size_t* partsize = new size_t[parts];
// sort the 'parts' number of file parts independently
for (size_t i = 0; i < parts; i++) {
partbufs[i] = new unsigned char[partsBufSize];
partpos[i] = 0;
partsize[i] = 0;
lseek(_file, SORT_BUFFER_S * i, SEEK_SET);
ssize_t n = read(_file, buf, SORT_BUFFER_S);
if (n < 0) continue;
qsort(buf, n / 8, 8, qsortCmp);
lseek(_file, SORT_BUFFER_S * i, SEEK_SET);
cwrite(_file, buf, n);
memcpy(partbufs[i], buf, std::min<size_t>(n, partsBufSize));
partsize[i] = n;
}
// now the individial parts are sorted
int newFile = openTmpFile();
for (size_t i = 0; i < _fsize; i += 8) {
uint64_t smallest = UINT64_MAX;
ssize_t smallestP = -1;
// look for smallest element (not optimal, but running time is not
// really critical here)
for (size_t j = 0; j < parts; j++) {
if (partpos[j] == partsize[j]) continue; // bucket already empty
if (*reinterpret_cast<uint64_t*>(
&partbufs[j][partpos[j] % partsBufSize]) <= smallest) {
smallestP = j;
smallest = *reinterpret_cast<uint64_t*>(
&partbufs[j][partpos[j] % partsBufSize]);
}
}
assert(smallestP > -1);
memcpy(buf + (i % SORT_BUFFER_S), &smallest, 8);
if ((i + 8) % BUFFER_S == 0) _blockEnds.push_back(smallest);
if ((i % SORT_BUFFER_S) == SORT_BUFFER_S - 8 || i == _fsize - 8) {
// write to output file
cwrite(newFile, buf, i % SORT_BUFFER_S + 8);
}
partpos[smallestP] += 8;
if (partpos[smallestP] % partsBufSize == 0) {
lseek(_file, SORT_BUFFER_S * smallestP + partpos[smallestP], SEEK_SET);
cread(_file, partbufs[smallestP], partsBufSize);
}
}
// cleanup
delete[] buf;
for (size_t j = 0; j < parts; j++) delete[] partbufs[j];
delete[] partbufs;
delete[] partpos;
delete[] partsize;
_file = newFile;
_sorted = true;
}
// _____________________________________________________________________________
size_t OsmIdSet::cwrite(int f, const void* buf, size_t n) const {
ssize_t w = write(f, buf, n);
if (w < 0) {
throw std::runtime_error("OSMIDSET: could not write to tmp file.\n");
}
return w;
}
// _____________________________________________________________________________
size_t OsmIdSet::cread(int f, void* buf, size_t n) const {
ssize_t w = read(f, buf, n);
if (w < 0) {
throw std::runtime_error("OSMIDSET: could not read from tmp file.\n");
}
return w;
}
// _____________________________________________________________________________
uint32_t OsmIdSet::knuth(uint32_t in) const {
const uint32_t prime = 2654435769;
return (in * prime) >> 2;
}
// _____________________________________________________________________________
uint32_t OsmIdSet::jenkins(uint32_t in) const {
in = (in + 0x7ed55d16) + (in << 12);
in = (in ^ 0xc761c23c) ^ (in >> 19);
in = (in + 0x165667b1) + (in << 5);
in = (in + 0xd3a2646c) ^ (in << 9);
in = (in + 0xfd7046c5) + (in << 3);
in = (in ^ 0xb55a4f09) ^ (in >> 16);
return in >> 2;
}
// _____________________________________________________________________________
uint32_t OsmIdSet::hash(uint32_t in, int i) const {
return (knuth(in) + jenkins(in) * i) % BLOOMF_BITS;
}
// _____________________________________________________________________________
int OsmIdSet::openTmpFile() const {
const std::string& fname = getFName();
int file = open(fname.c_str(), O_RDWR | O_CREAT, 0666);
// immediately unlink
unlink(fname.c_str());
if (file < 0) {
std::cerr << "Could not open temporary file " << fname << std::endl;
exit(1);
}
posix_fadvise(file, 0, 0, POSIX_FADV_SEQUENTIAL);
return file;
}
// _____________________________________________________________________________
std::string OsmIdSet::getFName() const {
std::string f = ".pfaedle-tmp";
while (access(f.c_str(), F_OK) != -1) {
std::stringstream ss;
ss << ".pfaedle-tmp-";
ss << std::rand();
f = ss.str().c_str();
}
return f;
}

View file

@ -0,0 +1,91 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_OSM_OSMIDSET_H_
#define PFAEDLE_OSM_OSMIDSET_H_
#include <fcntl.h>
#include <unistd.h>
#include <bitset>
#include <set>
#include <string>
#include <vector>
#include "pfaedle/osm/Osm.h"
namespace pfaedle {
namespace osm {
// buffer sizes _must_ be multiples of 8
static const size_t BUFFER_S = 8 * 64 * 1024;
static const size_t SORT_BUFFER_S = 8 * 64 * 1024;
static const size_t OBUFFER_S = 8 * 1024 * 1024;
#define BLOOMF_BITS 400000000
/*
* A disk-based set for OSM ids. Read-access for checking the presence is
* reduced by a bloom filter
*/
class OsmIdSet {
public:
OsmIdSet();
~OsmIdSet();
// Add an OSM id
void add(osmid id);
// Check if an OSM id is contained
bool has(osmid id) const;
// Count the number of lookups and file lookups for debugging
static size_t LOOKUPS;
static size_t FLOOKUPS;
private:
std::set<osmid> _set;
mutable bool _closed;
mutable int _file;
unsigned char* _buffer;
unsigned char* _outBuffer;
mutable bool _sorted;
osmid _last;
osmid _smallest;
osmid _biggest;
size_t _obufpos;
mutable size_t _curBlock;
mutable ssize_t _curBlockSize;
// bloom filter
std::bitset<BLOOMF_BITS>* _bitset;
mutable std::vector<osmid> _blockEnds;
mutable size_t _fsize;
uint32_t knuth(uint32_t in) const;
uint32_t jenkins(uint32_t in) const;
uint32_t hash(uint32_t in, int i) const;
void diskAdd(osmid id);
void close() const;
void sort() const;
bool diskHas(osmid id) const;
std::string getFName() const;
size_t getBlock(osmid id) const;
int openTmpFile() const;
size_t cwrite(int f, const void* buf, size_t n) const;
size_t cread(int f, void* buf, size_t n) const;
static int qsortCmp(const void* a, const void* b) {
if (*static_cast<const uint64_t*>(a) < *static_cast<const uint64_t*>(b))
return -1;
if (*static_cast<const uint64_t*>(a) > *static_cast<const uint64_t*>(b))
return 1;
return 0;
}
};
} // namespace osm
} // namespace pfaedle
#endif // PFAEDLE_OSM_OSMIDSET_H_

View file

@ -0,0 +1,197 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_OSM_OSMREADOPTS_H_
#define PFAEDLE_OSM_OSMREADOPTS_H_
#include <queue>
#include <unordered_set>
#include <string>
#include <unordered_map>
#include <map>
#include <utility>
#include <vector>
#include <set>
#include "pfaedle/osm/Osm.h"
#include "pfaedle/trgraph/Graph.h"
#include "pfaedle/trgraph/Normalizer.h"
namespace pfaedle {
namespace osm {
typedef std::unordered_set<std::string> AttrKeySet;
typedef std::unordered_map<osmid, trgraph::Node*> NIdMap;
typedef std::unordered_map<osmid, std::set<trgraph::Node*>> NIdMultMap;
typedef std::pair<double, trgraph::Edge*> EdgeCand;
typedef std::priority_queue<EdgeCand> EdgeCandPQ;
typedef std::unordered_map<osmid, std::vector<size_t>> RelMap;
typedef std::vector<AttrMap> RelVec;
typedef std::vector<std::string> AttrLst;
typedef std::pair<std::string, uint64_t> AttrFlagPair;
typedef std::unordered_map<std::string, std::map<std::string, uint64_t>>
MultAttrMap;
typedef std::pair<std::string, std::string> KeyVal;
typedef std::set<size_t> FlatRels;
typedef std::unordered_map<const trgraph::Edge*, std::string> EdgTracks;
struct RelLst {
RelVec rels;
FlatRels flat;
};
enum FilterFlags : uint64_t {
USE = 1, // dummy value
REL_NO_DOWN = 2,
NO_RELATIONS = 4,
NO_WAYS = 8,
NO_NODES = 16,
MULT_VAL_MATCH = 32
};
struct FilterRule {
FilterRule() : kv(KeyVal("", "")) {}
KeyVal kv;
std::set<std::string> flags;
};
inline bool operator==(const FilterRule& a, const FilterRule& b) {
return a.kv == b.kv && a.flags == b.flags;
}
struct DeepAttrRule {
std::string attr;
FilterRule relRule;
};
inline bool operator==(const DeepAttrRule& a, const DeepAttrRule& b) {
return a.attr == b.attr && a.relRule == b.relRule;
}
typedef std::vector<DeepAttrRule> DeepAttrLst;
struct RelLineRules {
AttrLst sNameRule;
AttrLst fromNameRule;
AttrLst toNameRule;
};
inline bool operator==(const RelLineRules& a, const RelLineRules& b) {
return a.sNameRule == b.sNameRule && a.fromNameRule == b.fromNameRule &&
a.toNameRule == b.toNameRule;
}
struct StationAttrRules {
DeepAttrLst nameRule;
DeepAttrLst platformRule;
};
inline bool operator==(const StationAttrRules& a, const StationAttrRules& b) {
return a.nameRule == b.nameRule && a.platformRule == b.platformRule;
}
struct StatGroupNAttrRule {
DeepAttrRule attr;
double maxDist;
};
inline bool operator==(const StatGroupNAttrRule& a,
const StatGroupNAttrRule& b) {
return a.attr == b.attr && a.maxDist == b.maxDist;
}
typedef std::unordered_map<
std::string,
std::unordered_map<std::string, std::vector<trgraph::StatGroup*>>>
StAttrGroups;
struct OsmReadOpts {
OsmReadOpts() {}
MultAttrMap noHupFilter;
MultAttrMap keepFilter;
MultAttrMap levelFilters[7];
MultAttrMap dropFilter;
MultAttrMap oneWayFilter;
MultAttrMap oneWayFilterRev;
MultAttrMap twoWayFilter;
MultAttrMap stationFilter;
MultAttrMap stationBlockerFilter;
std::vector<StatGroupNAttrRule> statGroupNAttrRules;
trgraph::Normalizer statNormzer;
trgraph::Normalizer lineNormzer;
trgraph::Normalizer trackNormzer;
RelLineRules relLinerules;
StationAttrRules statAttrRules;
DeepAttrLst edgePlatformRules;
uint8_t maxSnapLevel;
double maxAngleSnapReach;
std::vector<double> maxSnapDistances;
double maxSnapFallbackHeurDistance;
double maxGroupSearchDistance;
double maxBlockDistance;
double maxOsmStationDistance;
// TODO(patrick): this is not implemented yet
double levelSnapPunishFac[7] = {0, 0, 0, 0, 0, 0, 0};
// restriction system
MultAttrMap restrPosRestr;
MultAttrMap restrNegRestr;
MultAttrMap noRestrFilter;
};
inline bool operator==(const OsmReadOpts& a, const OsmReadOpts& b) {
if (a.maxSnapDistances.size() != b.maxSnapDistances.size()) return false;
for (size_t i = 0; i < a.maxSnapDistances.size(); i++) {
if (fabs(a.maxSnapDistances[i] - b.maxSnapDistances[i]) >= 0.1)
return false;
}
return a.noHupFilter == b.noHupFilter && a.keepFilter == b.keepFilter &&
a.levelFilters[0] == b.levelFilters[0] &&
a.levelFilters[1] == b.levelFilters[1] &&
a.levelFilters[2] == b.levelFilters[2] &&
a.levelFilters[3] == b.levelFilters[3] &&
a.levelFilters[4] == b.levelFilters[4] &&
a.levelFilters[5] == b.levelFilters[5] &&
a.levelFilters[6] == b.levelFilters[6] &&
a.dropFilter == b.dropFilter && a.oneWayFilter == b.oneWayFilter &&
a.oneWayFilterRev == b.oneWayFilterRev &&
a.twoWayFilter == b.twoWayFilter &&
a.stationFilter == b.stationFilter &&
a.stationBlockerFilter == b.stationBlockerFilter &&
a.statGroupNAttrRules == b.statGroupNAttrRules &&
a.statNormzer == b.statNormzer && a.lineNormzer == b.lineNormzer &&
a.trackNormzer == b.trackNormzer && a.relLinerules == b.relLinerules &&
a.statAttrRules == b.statAttrRules &&
a.maxSnapLevel == b.maxSnapLevel &&
fabs(a.maxAngleSnapReach - b.maxAngleSnapReach) < 0.1 &&
fabs(a.maxOsmStationDistance - b.maxOsmStationDistance) < 0.1 &&
fabs(a.maxSnapFallbackHeurDistance - b.maxSnapFallbackHeurDistance) <
0.1 &&
fabs(a.maxGroupSearchDistance - b.maxGroupSearchDistance) < 0.1 &&
fabs(a.maxBlockDistance - b.maxBlockDistance) < 0.1 &&
fabs(a.levelSnapPunishFac[0] - b.levelSnapPunishFac[0]) < 0.1 &&
fabs(a.levelSnapPunishFac[1] - b.levelSnapPunishFac[1]) < 0.1 &&
fabs(a.levelSnapPunishFac[2] - b.levelSnapPunishFac[2]) < 0.1 &&
fabs(a.levelSnapPunishFac[3] - b.levelSnapPunishFac[3]) < 0.1 &&
fabs(a.levelSnapPunishFac[4] - b.levelSnapPunishFac[4]) < 0.1 &&
fabs(a.levelSnapPunishFac[5] - b.levelSnapPunishFac[5]) < 0.1 &&
fabs(a.levelSnapPunishFac[6] - b.levelSnapPunishFac[6]) < 0.1 &&
a.restrPosRestr == b.restrPosRestr &&
a.restrNegRestr == b.restrNegRestr &&
a.noRestrFilter == b.noRestrFilter;
}
} // namespace osm
} // namespace pfaedle
#endif // PFAEDLE_OSM_OSMREADOPTS_H_

View file

@ -0,0 +1,155 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include "pfaedle/osm/Restrictor.h"
#include "util/log/Log.h"
using pfaedle::osm::Restrictor;
// _____________________________________________________________________________
void Restrictor::relax(osmid wid, const trgraph::Node* n,
const trgraph::Edge* e) {
// the wid is not unique here, because the OSM ways are split into
// multiple edges. They are only unique as a pair with a "from-via" point.
_rlx[NodeOsmIdP(n, wid)] = e;
auto i = _posDangling.find(NodeOsmIdP(n, wid));
if (i != _posDangling.end()) {
for (const auto& path : i->second) {
_pos[path.first][path.second].second = e;
assert(path.first->hasEdge(e));
}
}
auto j = _negDangling.find(NodeOsmIdP(n, wid));
if (j != _negDangling.end()) {
for (const auto& path : j->second) {
_neg[path.first][path.second].second = e;
assert(path.first->hasEdge(e));
}
}
}
// _____________________________________________________________________________
void Restrictor::add(const trgraph::Edge* from, osmid to,
const trgraph::Node* via, bool pos) {
const trgraph::Edge* toE = 0;
if (_rlx.count(NodeOsmIdP(via, to)))
toE = _rlx.find(NodeOsmIdP(via, to))->second;
if (pos) {
_pos[via].push_back(RulePair(from, toE));
if (!toE)
_posDangling[NodeOsmIdP(via, to)].push_back(
DanglPath(via, _pos[via].size() - 1));
} else {
_neg[via].push_back(RulePair(from, toE));
if (!toE)
_negDangling[NodeOsmIdP(via, to)].push_back(
DanglPath(via, _neg[via].size() - 1));
}
}
// _____________________________________________________________________________
bool Restrictor::may(const trgraph::Edge* from, const trgraph::Edge* to,
const trgraph::Node* via) const {
auto posI = _pos.find(via);
auto negI = _neg.find(via);
if (posI != _pos.end()) {
for (const auto& r : posI->second) {
if (r.first == from && r.second && r.second != to)
return false;
else if (r.first == from && r.second == to)
return true;
}
}
if (negI != _neg.end()) {
for (const auto& r : negI->second) {
if (r.first == from && r.second == to) return false;
}
}
return true;
}
// _____________________________________________________________________________
void Restrictor::replaceEdge(const trgraph::Edge* old,
const trgraph::Edge* newA,
const trgraph::Edge* newB) {
const trgraph::Edge* newFrom;
const trgraph::Edge* newTo;
if (old->getFrom() == newA->getFrom() || old->getFrom() == newA->getTo()) {
newFrom = newA;
newTo = newB;
} else {
newFrom = newB;
newTo = newA;
}
replaceEdge(old, old->getFrom(), newFrom);
replaceEdge(old, old->getTo(), newTo);
}
// _____________________________________________________________________________
void Restrictor::duplicateEdge(const trgraph::Edge* old,
const trgraph::Edge* newE) {
duplicateEdge(old, old->getFrom(), newE);
duplicateEdge(old, old->getTo(), newE);
}
// _____________________________________________________________________________
void Restrictor::duplicateEdge(const trgraph::Edge* old,
const trgraph::Node* via,
const trgraph::Edge* newE) {
auto posI = _pos.find(via);
auto negI = _neg.find(via);
assert(old->getFrom() == newE->getTo() && old->getTo() == newE->getFrom());
if (posI != _pos.end()) {
for (auto& r : posI->second) {
if (r.first == old) {
if (r.first->getTo() != via) {
r.first = newE;
}
}
if (r.second == old) {
if (r.second->getFrom() != via) {
r.second = newE;
}
}
}
}
if (negI != _neg.end()) {
for (auto& r : negI->second) {
if (r.first == old) {
if (r.first->getTo() != via) {
r.first = newE;
}
}
if (r.second == old) {
if (r.second->getFrom() != via) {
r.second = newE;
}
}
}
}
}
// _____________________________________________________________________________
void Restrictor::replaceEdge(const trgraph::Edge* old, const trgraph::Node* via,
const trgraph::Edge* newE) {
auto posI = _pos.find(via);
auto negI = _neg.find(via);
if (posI != _pos.end()) {
for (auto& r : posI->second) {
if (r.first == old) r.first = newE;
if (r.second == old) r.second = newE;
}
}
if (negI != _neg.end()) {
for (auto& r : negI->second) {
if (r.first == old) r.first = newE;
if (r.second == old) r.second = newE;
}
}
}

View file

@ -0,0 +1,60 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_OSM_RESTRICTOR_H_
#define PFAEDLE_OSM_RESTRICTOR_H_
#include <unordered_map>
#include <map>
#include <vector>
#include <utility>
#include "pfaedle/osm/Osm.h"
#include "pfaedle/trgraph/Graph.h"
namespace pfaedle {
namespace osm {
typedef std::pair<const trgraph::Edge*, const trgraph::Edge*> RulePair;
typedef std::vector<RulePair> RuleVec;
typedef std::pair<const trgraph::Node*, size_t> DanglPath;
// very seldom, there are more than a handful of rules for a node. Use a
// vector here, should have lesser overhead and be faster for such small
// numbers
typedef std::unordered_map<const trgraph::Node*, RuleVec> Rules;
typedef std::pair<const trgraph::Node*, osmid> NodeOsmIdP;
/*
* Stores restrictions between edges
*/
class Restrictor {
public:
Restrictor() {}
void relax(osmid wid, const trgraph::Node* n, const trgraph::Edge* e);
void add(const trgraph::Edge* from, osmid to, const trgraph::Node* via,
bool pos);
bool may(const trgraph::Edge* from, const trgraph::Edge* to,
const trgraph::Node* via) const;
void replaceEdge(const trgraph::Edge* old, const trgraph::Edge* newA,
const trgraph::Edge* newB);
void duplicateEdge(const trgraph::Edge* old, const trgraph::Node* via,
const trgraph::Edge* newE);
void duplicateEdge(const trgraph::Edge* old, const trgraph::Edge* newE);
private:
Rules _pos;
Rules _neg;
std::map<NodeOsmIdP, const trgraph::Edge*> _rlx;
std::map<NodeOsmIdP, std::vector<DanglPath>> _posDangling;
std::map<NodeOsmIdP, std::vector<DanglPath>> _negDangling;
void replaceEdge(const trgraph::Edge* old, const trgraph::Node* via,
const trgraph::Edge* newE);
};
} // namespace osm
} // namespace pfaedle
#endif // PFAEDLE_OSM_RESTRICTOR_H_

75
src/pfaedle/router/Comp.h Normal file
View file

@ -0,0 +1,75 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_ROUTER_COMP_H_
#define PFAEDLE_ROUTER_COMP_H_
#include <iostream>
#include <algorithm>
#include <string>
#include "util/String.h"
namespace pfaedle {
namespace router {
using util::editDist;
// _____________________________________________________________________________
inline double statSimi(const std::string& a, const std::string& b) {
if (a == b) return 1;
if (a.empty() || b.empty()) return 0;
if (a.size() > b.size() + 1) {
// check if a begins with b
if (a.compare(0, b.size() + 1, b + " ") == 0) {
return 1;
}
// check if a ends with b
if (a.compare(a.size() - (b.size() + 1), b.size() + 1, " " + b) == 0) {
return 1;
}
}
if (b.size() > a.size() + 1) {
// check if b begins with a
if (b.compare(0, a.size() + 1, a + " ") == 0) {
return 1;
}
// check if b ends with a
if (b.compare(b.size() - (a.size() + 1), a.size() + 1, " " + a) == 0) {
return 1;
}
}
if (static_cast<double>(editDist(a, b)) /
(std::max(static_cast<double>(a.size()),
static_cast<double>(b.size()))) <
0.05)
return 1;
return 0;
}
// _____________________________________________________________________________
inline double lineSimi(const std::string& a, const std::string& b) {
if (a == b) return 1;
if (a.empty() || b.empty()) return 0;
// if one of the lines is completely contained in the other, return 1
if (a.find(b) != std::string::npos) {
return 1;
} else if (b.find(a) != std::string::npos) {
return 1;
}
return 0;
}
} // namespace router
} // namespace pfaedle
#endif // PFAEDLE_ROUTER_COMP_H_

View file

@ -0,0 +1,99 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include "pfaedle/router/EdgePL.h"
#include "pfaedle/router/Router.h"
#include "util/String.h"
using pfaedle::router::EdgePL;
using pfaedle::router::EdgeCost;
using pfaedle::router::EdgeList;
using pfaedle::trgraph::Node;
// _____________________________________________________________________________
EdgeList* EdgePL::getEdges() { return &_edges; }
// _____________________________________________________________________________
const EdgeList& EdgePL::getEdges() const { return _edges; }
// _____________________________________________________________________________
const FPoint& EdgePL::frontHop() const {
if (!_edges.size()) return *_end->pl().getGeom();
return _edges.back()->pl().frontHop();
}
// _____________________________________________________________________________
const FPoint& EdgePL::backHop() const {
if (!_edges.size()) return *_start->pl().getGeom();
return _edges.front()->pl().backHop();
}
// _____________________________________________________________________________
const Node* EdgePL::backNode() const { return _end; }
// _____________________________________________________________________________
const Node* EdgePL::frontNode() const { return _start; }
// _____________________________________________________________________________
const util::geo::FLine* EdgePL::getGeom() const {
if (!_edges.size()) return 0;
if (!_geom.size()) {
const trgraph::Node* l = _start;
for (auto i = _edges.rbegin(); i != _edges.rend(); i++) {
const auto e = *i;
if ((e->getFrom() == l) ^ e->pl().isRev()) {
_geom.insert(_geom.end(), e->pl().getGeom()->begin(),
e->pl().getGeom()->end());
} else {
_geom.insert(_geom.end(), e->pl().getGeom()->rbegin(),
e->pl().getGeom()->rend());
}
l = e->getOtherNd(l);
}
}
return &_geom;
}
// _____________________________________________________________________________
void EdgePL::setStartNode(const trgraph::Node* s) { _start = s; }
// _____________________________________________________________________________
void EdgePL::setEndNode(const trgraph::Node* e) { _end = e; }
// _____________________________________________________________________________
void EdgePL::setStartEdge(const trgraph::Edge* s) { _startE = s; }
// _____________________________________________________________________________
void EdgePL::setEndEdge(const trgraph::Edge* e) { _endE = e; }
// _____________________________________________________________________________
const EdgeCost& EdgePL::getCost() const { return _cost; }
// _____________________________________________________________________________
void EdgePL::setCost(const router::EdgeCost& c) { _cost = c; }
// _____________________________________________________________________________
void EdgePL::getAttrs(std::map<std::string, std::string>* obj) const {
(*obj)["cost"] = std::to_string(_cost.getValue());
(*obj)["from_edge"] = util::toString(_startE);
(*obj)["to_edge"] = util::toString(_endE);
(*obj)["cost_m_lvl1"] = std::to_string(_cost.meterDistLvl1);
(*obj)["cost_m_lvl0"] = std::to_string(_cost.meterDist);
(*obj)["cost_m_lvl1"] = std::to_string(_cost.meterDistLvl1);
(*obj)["cost_m_lvl2"] = std::to_string(_cost.meterDistLvl2);
(*obj)["cost_m_lvl3"] = std::to_string(_cost.meterDistLvl3);
(*obj)["cost_m_lvl4"] = std::to_string(_cost.meterDistLvl4);
(*obj)["cost_m_lvl5"] = std::to_string(_cost.meterDistLvl5);
(*obj)["cost_m_lvl6"] = std::to_string(_cost.meterDistLvl6);
(*obj)["cost_m_lvl7"] = std::to_string(_cost.meterDistLvl7);
(*obj)["cost_fullturn"] = std::to_string(_cost.fullTurns);
(*obj)["cost_st_passthru"] = std::to_string(_cost.passThruStations);
(*obj)["cost_m_oneway"] = std::to_string(_cost.oneWayMeters);
(*obj)["cost_m_lineunmatch"] = std::to_string(_cost.lineUnmatchedMeters);
(*obj)["cost_reach_node_pen"] = std::to_string(_cost.reachPen);
(*obj)["cost_oneway_event"] = std::to_string(_cost.oneWayEdges);
(*obj)["dummy"] = _edges.size() ? "no" : "yes";
}

View file

@ -0,0 +1,49 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_ROUTER_EDGEPL_H_
#define PFAEDLE_ROUTER_EDGEPL_H_
#include <map>
#include <string>
#include "pfaedle/router/Misc.h"
#include "util/geo/GeoGraph.h"
using util::geograph::GeoEdgePL;
namespace pfaedle {
namespace router {
class EdgePL : public GeoEdgePL<float> {
public:
EdgePL() : _cost(), _start(0), _end(0), _startE(0), _endE(0) {}
const util::geo::FLine* getGeom() const;
void getAttrs(std::map<std::string, std::string>* attrs) const;
router::EdgeList* getEdges();
const router::EdgeList& getEdges() const;
void setStartNode(const trgraph::Node* s);
void setEndNode(const trgraph::Node* s);
void setStartEdge(const trgraph::Edge* s);
void setEndEdge(const trgraph::Edge* s);
const router::EdgeCost& getCost() const;
void setCost(const router::EdgeCost& c);
const FPoint& frontHop() const;
const FPoint& backHop() const;
const trgraph::Node* frontNode() const;
const trgraph::Node* backNode() const;
private:
router::EdgeCost _cost;
// the edges are in this field in REVERSED ORDER!
router::EdgeList _edges;
const trgraph::Node* _start;
const trgraph::Node* _end;
const trgraph::Edge* _startE;
const trgraph::Edge* _endE;
mutable util::geo::FLine _geom;
};
} // namespace router
} // namespace pfaedle
#endif // PFAEDLE_ROUTER_EDGEPL_H_

View file

@ -0,0 +1,26 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_ROUTER_GRAPH_H_
#define PFAEDLE_ROUTER_GRAPH_H_
#include "pfaedle/trgraph/Graph.h"
#include "pfaedle/router/EdgePL.h"
#include "pfaedle/router/NodePL.h"
#include "util/graph/DirGraph.h"
using util::geo::Point;
using util::geo::Line;
namespace pfaedle {
namespace router {
typedef util::graph::Edge<router::NodePL, router::EdgePL> Edge;
typedef util::graph::Node<router::NodePL, router::EdgePL> Node;
typedef util::graph::DirGraph<router::NodePL, router::EdgePL> Graph;
} // namespace router
} // namespace pfaedle
#endif // PFAEDLE_ROUTER_GRAPH_H_

197
src/pfaedle/router/Misc.h Normal file
View file

@ -0,0 +1,197 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_ROUTER_MISC_H_
#define PFAEDLE_ROUTER_MISC_H_
#include <set>
#include <string>
#include <vector>
#include <unordered_map>
#include "ad/cppgtfs/gtfs/Feed.h"
#include "ad/cppgtfs/gtfs/Route.h"
#include "pfaedle/trgraph/Graph.h"
using ad::cppgtfs::gtfs::Route;
using ad::cppgtfs::gtfs::Stop;
namespace pfaedle {
namespace router {
struct NodeCand {
trgraph::Node* nd;
double pen;
};
struct RoutingOpts {
RoutingOpts()
: fullTurnPunishFac(2000),
fullTurnAngle(45),
passThruStationsPunish(100),
oneWayPunishFac(1),
oneWayEdgePunish(0),
lineUnmatchedPunishFact(0.5),
platformUnmatchedPen(0),
stationDistPenFactor(0) {}
double fullTurnPunishFac;
double fullTurnAngle;
double passThruStationsPunish;
double oneWayPunishFac;
double oneWayEdgePunish;
double lineUnmatchedPunishFact;
double platformUnmatchedPen;
double stationDistPenFactor;
double nonOsmPen;
double levelPunish[8];
};
inline bool operator==(const RoutingOpts& a, const RoutingOpts& b) {
return fabs(a.fullTurnPunishFac - b.fullTurnPunishFac) < 0.01 &&
fabs(a.fullTurnAngle - b.fullTurnAngle) < 0.01 &&
fabs(a.passThruStationsPunish - b.passThruStationsPunish) < 0.01 &&
fabs(a.oneWayPunishFac - b.oneWayPunishFac) < 0.01 &&
fabs(a.oneWayEdgePunish - b.oneWayEdgePunish) < 0.01 &&
fabs(a.lineUnmatchedPunishFact - b.lineUnmatchedPunishFact) < 0.01 &&
fabs(a.platformUnmatchedPen - b.platformUnmatchedPen) < 0.01 &&
fabs(a.stationDistPenFactor - b.stationDistPenFactor) < 0.01 &&
fabs(a.nonOsmPen - b.nonOsmPen) < 0.01 &&
fabs(a.levelPunish[0] - b.levelPunish[0]) < 0.01 &&
fabs(a.levelPunish[1] - b.levelPunish[1]) < 0.01 &&
fabs(a.levelPunish[2] - b.levelPunish[2]) < 0.01 &&
fabs(a.levelPunish[3] - b.levelPunish[3]) < 0.01 &&
fabs(a.levelPunish[4] - b.levelPunish[4]) < 0.01 &&
fabs(a.levelPunish[5] - b.levelPunish[5]) < 0.01 &&
fabs(a.levelPunish[6] - b.levelPunish[6]) < 0.01 &&
fabs(a.levelPunish[7] - b.levelPunish[7]) < 0.01;
}
struct EdgeCost {
EdgeCost()
: meterDist(0),
meterDistLvl1(0),
meterDistLvl2(0),
meterDistLvl3(0),
meterDistLvl4(0),
meterDistLvl5(0),
meterDistLvl6(0),
meterDistLvl7(0),
fullTurns(0),
passThruStations(0),
oneWayMeters(0),
oneWayEdges(0),
lineUnmatchedMeters(0),
reachPen(0),
o(0) {}
EdgeCost(double mDist, double mDistLvl1, double mDistLvl2, double mDistLvl3,
double mDistLvl4, double mDistLvl5, double mDistLvl6,
double mDistLvl7, uint32_t fullTurns, int32_t passThru,
double oneWayMeters, size_t oneWayEdges, double lineUnmatchedMeters,
double reachPen, const RoutingOpts* o)
: meterDist(mDist),
meterDistLvl1(mDistLvl1),
meterDistLvl2(mDistLvl2),
meterDistLvl3(mDistLvl3),
meterDistLvl4(mDistLvl4),
meterDistLvl5(mDistLvl5),
meterDistLvl6(mDistLvl6),
meterDistLvl7(mDistLvl7),
fullTurns(fullTurns),
passThruStations(passThru),
oneWayMeters(oneWayMeters),
oneWayEdges(oneWayEdges),
lineUnmatchedMeters(lineUnmatchedMeters),
reachPen(reachPen),
o(o) {}
double meterDist;
double meterDistLvl1;
double meterDistLvl2;
double meterDistLvl3;
double meterDistLvl4;
double meterDistLvl5;
double meterDistLvl6;
double meterDistLvl7;
uint32_t fullTurns;
int32_t passThruStations;
double oneWayMeters;
size_t oneWayEdges;
double lineUnmatchedMeters;
double reachPen;
const RoutingOpts* o;
double getValue() const {
if (!o) return meterDist + reachPen;
return meterDist * o->levelPunish[0] + meterDistLvl1 * o->levelPunish[1] +
meterDistLvl2 * o->levelPunish[2] +
meterDistLvl3 * o->levelPunish[3] +
meterDistLvl4 * o->levelPunish[4] +
meterDistLvl5 * o->levelPunish[5] +
meterDistLvl6 * o->levelPunish[6] +
meterDistLvl7 * o->levelPunish[7] +
oneWayMeters * o->oneWayPunishFac +
oneWayEdges * o->oneWayEdgePunish +
lineUnmatchedMeters * o->lineUnmatchedPunishFact +
fullTurns * o->fullTurnPunishFac +
passThruStations * o->passThruStationsPunish + reachPen;
}
double getTotalMeters() const {
return meterDist + meterDistLvl1 + meterDistLvl2 + meterDistLvl3 +
meterDistLvl4 + meterDistLvl5 + meterDistLvl6 + meterDistLvl7;
}
};
inline EdgeCost operator+(const EdgeCost& a, const EdgeCost& b) {
return EdgeCost(
a.meterDist + b.meterDist, a.meterDistLvl1 + b.meterDistLvl1,
a.meterDistLvl2 + b.meterDistLvl2, a.meterDistLvl3 + b.meterDistLvl3,
a.meterDistLvl4 + b.meterDistLvl4, a.meterDistLvl5 + b.meterDistLvl5,
a.meterDistLvl6 + b.meterDistLvl6, a.meterDistLvl7 + b.meterDistLvl7,
a.fullTurns + b.fullTurns, a.passThruStations + b.passThruStations,
a.oneWayMeters + b.oneWayMeters, a.oneWayEdges + b.oneWayEdges,
a.lineUnmatchedMeters + b.lineUnmatchedMeters, a.reachPen + b.reachPen,
a.o ? a.o : b.o);
}
inline bool operator<=(const EdgeCost& a, const EdgeCost& b) {
return a.getValue() <= b.getValue();
}
inline bool operator==(const EdgeCost& a, const EdgeCost& b) {
return a.getValue() == b.getValue();
}
inline bool operator>(const EdgeCost& a, const EdgeCost& b) {
return a.getValue() > b.getValue();
}
inline int angSmaller(const FPoint& f, const FPoint& m, const FPoint& t,
double ang) {
if (util::geo::innerProd(m, f, t) < ang) return 1;
return 0;
}
typedef std::set<trgraph::Node*> NodeSet;
typedef std::set<trgraph::Edge*> EdgeSet;
typedef std::unordered_map<const Stop*, trgraph::Node*> FeedStops;
typedef std::vector<NodeCand> NodeCandGroup;
typedef std::vector<NodeCandGroup> NodeCandRoute;
typedef std::vector<trgraph::Edge*> EdgeList;
typedef std::vector<trgraph::Node*> NodeList;
struct EdgeListHop {
EdgeList edges;
const trgraph::Node* start;
const trgraph::Node* end;
};
typedef std::vector<EdgeListHop> EdgeListHops;
typedef std::set<Route::TYPE> MOTs;
} // namespace router
} // namespace pfaedle
#endif // PFAEDLE_ROUTER_MISC_H_

View file

@ -0,0 +1,36 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_ROUTER_NODEPL_H_
#define PFAEDLE_ROUTER_NODEPL_H_
#include <map>
#include <string>
#include "pfaedle/trgraph/Graph.h"
#include "util/geo/GeoGraph.h"
using util::geograph::GeoNodePL;
namespace pfaedle {
namespace router {
class NodePL : public GeoNodePL<float> {
public:
NodePL() : _n(0) {}
NodePL(const pfaedle::trgraph::Node* n) : _n(n) {} // NOLINT
const util::geo::FPoint* getGeom() const {
return !_n ? 0 : _n->pl().getGeom();
}
void getAttrs(std::map<std::string, std::string>* attrs) const {
if (_n) _n->pl().getAttrs(attrs);
}
private:
const pfaedle::trgraph::Node* _n;
};
} // namespace router
} // namespace pfaedle
#endif // PFAEDLE_ROUTER_NODEPL_H_

View file

@ -0,0 +1,615 @@
// 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(); }

194
src/pfaedle/router/Router.h Normal file
View file

@ -0,0 +1,194 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_ROUTER_ROUTER_H_
#define PFAEDLE_ROUTER_ROUTER_H_
#include <mutex>
#include <map>
#include <unordered_map>
#include <utility>
#include <vector>
#include <set>
#include <limits>
#include <string>
#include "pfaedle/osm/Restrictor.h"
#include "pfaedle/router/Graph.h"
#include "pfaedle/router/Misc.h"
#include "pfaedle/router/RoutingAttrs.h"
#include "pfaedle/trgraph/Graph.h"
#include "util/graph/Dijkstra.h"
#include "util/graph/EDijkstra.h"
using util::graph::EDijkstra;
using util::graph::Dijkstra;
namespace pfaedle {
namespace router {
typedef std::unordered_map<const trgraph::Edge*, router::Node*> CombNodeMap;
typedef std::pair<size_t, size_t> HId;
typedef std::map<
RoutingAttrs,
std::unordered_map<const trgraph::Edge*,
std::unordered_map<const trgraph::Edge*,
std::pair<EdgeCost, EdgeList> > > >
Cache;
struct HopBand {
double minD;
double maxD;
const trgraph::Edge* nearest;
double maxInGrpDist;
};
struct CostFunc
: public EDijkstra::CostFunc<trgraph::NodePL, trgraph::EdgePL, EdgeCost> {
CostFunc(const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
const osm::Restrictor& res, const trgraph::StatGroup* tgGrp,
double max)
: _rAttrs(rAttrs),
_rOpts(rOpts),
_res(res),
_max(max),
_tgGrp(tgGrp),
_inf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _max, 0) {}
const RoutingAttrs& _rAttrs;
const RoutingOpts& _rOpts;
const osm::Restrictor& _res;
double _max;
const trgraph::StatGroup* _tgGrp;
EdgeCost _inf;
EdgeCost operator()(const trgraph::Edge* from, const trgraph::Node* n,
const trgraph::Edge* to) const;
EdgeCost inf() const { return _inf; }
double transitLineCmp(const trgraph::EdgePL& e) const;
};
struct NCostFunc
: public Dijkstra::CostFunc<trgraph::NodePL, trgraph::EdgePL, EdgeCost> {
NCostFunc(const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
const osm::Restrictor& res, const trgraph::StatGroup* tgGrp)
: _rAttrs(rAttrs),
_rOpts(rOpts),
_res(res),
_tgGrp(tgGrp),
_inf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
std::numeric_limits<double>::infinity(), 0) {}
const RoutingAttrs& _rAttrs;
const RoutingOpts& _rOpts;
const osm::Restrictor& _res;
const trgraph::StatGroup* _tgGrp;
EdgeCost _inf;
EdgeCost operator()(const trgraph::Node* from, const trgraph::Edge* e,
const trgraph::Node* to) const;
EdgeCost inf() const { return _inf; }
double transitLineCmp(const trgraph::EdgePL& e) const;
};
struct DistHeur
: public EDijkstra::HeurFunc<trgraph::NodePL, trgraph::EdgePL, EdgeCost> {
DistHeur(uint8_t minLvl, const RoutingOpts& rOpts,
const std::set<trgraph::Edge*>& tos);
const RoutingOpts& _rOpts;
uint8_t _lvl;
FPoint _center;
double _maxCentD;
EdgeCost operator()(const trgraph::Edge* a,
const std::set<trgraph::Edge*>& b) const;
};
struct NDistHeur
: public Dijkstra::HeurFunc<trgraph::NodePL, trgraph::EdgePL, EdgeCost> {
NDistHeur(const RoutingOpts& rOpts, const std::set<trgraph::Node*>& tos);
const RoutingOpts& _rOpts;
FPoint _center;
double _maxCentD;
EdgeCost operator()(const trgraph::Node* a,
const std::set<trgraph::Node*>& b) const;
};
struct CombCostFunc
: public EDijkstra::CostFunc<router::NodePL, router::EdgePL, double> {
explicit CombCostFunc(const RoutingOpts& rOpts) : _rOpts(rOpts) {}
const RoutingOpts& _rOpts;
double operator()(const router::Edge* from, const router::Node* n,
const router::Edge* to) const;
double inf() const { return std::numeric_limits<double>::infinity(); }
};
/*
* Finds the most likely route of schedule-based vehicle between stops in a
* physical transportation network
*/
class Router {
public:
// Init this router with caches for numThreads threads
Router(const trgraph::Graph& g, size_t numThreads);
~Router();
// Find the most likely path through the graph for a node candidate route.
EdgeListHops route(const NodeCandRoute& route, const RoutingAttrs& rAttrs,
const RoutingOpts& rOpts, const osm::Restrictor& rest,
router::Graph* cgraph) const;
// Find the most likely path through cgraph for a node candidate route, but
// based on a greedy node to node approach
EdgeListHops routeGreedy(const NodeCandRoute& route,
const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
const osm::Restrictor& rest) const;
// Find the most likely path through cgraph for a node candidate route, but
// based on a greedy node to node set approach
EdgeListHops routeGreedy2(const NodeCandRoute& route,
const RoutingAttrs& rAttrs,
const RoutingOpts& rOpts,
const osm::Restrictor& rest) const;
// Return the number of thread caches this router was initialized with
size_t getCacheNumber() const;
private:
const trgraph::Graph& _g;
mutable std::vector<Cache*> _cache;
HopBand getHopBand(const NodeCandGroup& a, const NodeCandGroup& b,
const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
const osm::Restrictor& rest) const;
void hops(trgraph::Edge* from, const std::set<trgraph::Edge*>& froms,
const std::set<trgraph::Edge*> to, 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*> getCachedHops(
trgraph::Edge* from, const std::set<trgraph::Edge*>& to,
const std::unordered_map<trgraph::Edge*, EdgeList*>& edgesRet,
std::unordered_map<trgraph::Edge*, EdgeCost>* rCosts,
const RoutingAttrs& rAttrs) const;
void cache(trgraph::Edge* from, trgraph::Edge* to, const EdgeCost& c,
EdgeList* edges, const RoutingAttrs& rAttrs) const;
void nestedCache(const EdgeList* el, const std::set<trgraph::Edge*>& froms,
const CostFunc& cost, const RoutingAttrs& rAttrs) const;
bool compConned(const NodeCandGroup& a, const NodeCandGroup& b) const;
};
} // namespace router
} // namespace pfaedle
#endif // PFAEDLE_ROUTER_ROUTER_H_

View file

@ -0,0 +1,64 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_ROUTER_ROUTINGATTRS_H_
#define PFAEDLE_ROUTER_ROUTINGATTRS_H_
#include <map>
#include <string>
#include "pfaedle/trgraph/EdgePL.h"
using pfaedle::trgraph::TransitEdgeLine;
namespace pfaedle {
namespace router {
struct RoutingAttrs {
RoutingAttrs() : fromString(""), toString(""), shortName(""), _simiCache() {}
std::string fromString;
std::string toString;
std::string shortName;
mutable std::map<const TransitEdgeLine*, double> _simiCache;
double simi(const TransitEdgeLine* line) const {
auto i = _simiCache.find(line);
if (i != _simiCache.end()) return i->second;
double cur = 1;
if (router::lineSimi(line->shortName, shortName) > 0.5) cur -= 0.33;
if (line->toStr.empty() || router::statSimi(line->toStr, toString) > 0.5)
cur -= 0.33;
if (line->fromStr.empty() ||
router::statSimi(line->fromStr, fromString) > 0.5)
cur -= 0.33;
_simiCache[line] = cur;
return cur;
}
};
inline bool operator==(const RoutingAttrs& a, const RoutingAttrs& b) {
return a.shortName == b.shortName && a.toString == b.toString &&
a.fromString == b.fromString;
}
inline bool operator!=(const RoutingAttrs& a, const RoutingAttrs& b) {
return !(a == b);
}
inline bool operator<(const RoutingAttrs& a, const RoutingAttrs& b) {
return a.fromString < b.fromString ||
(a.fromString == b.fromString && a.toString < b.toString) ||
(a.fromString == b.fromString && a.toString == b.toString &&
a.shortName < b.shortName);
}
} // namespace router
} // namespace pfaedle
#endif // PFAEDLE_ROUTER_ROUTINGATTRS_H_

View file

@ -0,0 +1,627 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include <omp.h>
#include <map>
#include <mutex>
#include <unordered_map>
#include <utility>
#include "ad/cppgtfs/gtfs/Feed.h"
#include "pfaedle/eval/Collector.h"
#include "pfaedle/osm/OsmBuilder.h"
#include "pfaedle/router/ShapeBuilder.h"
#include "pfaedle/trgraph/StatGroup.h"
#include "util/geo/output/GeoGraphJsonOutput.h"
#include "util/geo/output/GeoJsonOutput.h"
#include "util/graph/EDijkstra.h"
#include "util/log/Log.h"
using util::geo::FPoint;
using util::geo::extendBox;
using util::geo::Box;
using util::geo::minbox;
using util::geo::FLine;
using util::geo::webMercMeterDist;
using util::geo::webMercToLatLng;
using util::geo::latLngToWebMerc;
using util::geo::output::GeoGraphJsonOutput;
using pfaedle::router::ShapeBuilder;
using pfaedle::router::FeedStops;
using pfaedle::router::NodeCandGroup;
using pfaedle::router::NodeCandRoute;
using pfaedle::router::RoutingAttrs;
using pfaedle::router::EdgeListHops;
using pfaedle::router::Clusters;
using pfaedle::osm::BBoxIdx;
using ad::cppgtfs::gtfs::Stop;
using ad::cppgtfs::gtfs::Trip;
using ad::cppgtfs::gtfs::Feed;
using ad::cppgtfs::gtfs::StopTime;
using ad::cppgtfs::gtfs::ShapePoint;
// _____________________________________________________________________________
ShapeBuilder::ShapeBuilder(Feed* feed, MOTs mots,
const config::MotConfig& motCfg,
eval::Collector* ecoll, const config::Config& cfg)
: _feed(feed),
_mots(mots),
_motCfg(motCfg),
_ecoll(ecoll),
_cfg(cfg),
_crouter(_g, omp_get_num_procs()),
_curShpCnt(0) {
_numThreads = _crouter.getCacheNumber();
writeMotStops();
// TODO(patrick): maybe do this on demand to avoid graph filtering / reading
// for input where no routing is necessary (already shape'd)
buildGraph();
}
// _____________________________________________________________________________
void ShapeBuilder::writeMotStops() {
for (auto t : _feed->getTrips()) {
if (!_cfg.shapeTripId.empty() && t.second->getId() != _cfg.shapeTripId)
continue;
if (_mots.count(t.second->getRoute()->getType()) &&
_motCfg.mots.count(t.second->getRoute()->getType())) {
for (auto st : t.second->getStopTimes()) {
_stops[st.getStop()] = 0;
}
}
}
}
// _____________________________________________________________________________
FeedStops* ShapeBuilder::getFeedStops() { return &_stops; }
// _____________________________________________________________________________
const NodeCandGroup& ShapeBuilder::getNodeCands(const Stop* s) const {
if (_stops.find(s) == _stops.end() || _stops.at(s) == 0) {
return _emptyNCG;
}
return _stops.at(s)->pl().getSI()->getGroup()->getNodeCands(s);
}
// _____________________________________________________________________________
FLine ShapeBuilder::shapeL(const router::NodeCandRoute& ncr,
const router::RoutingAttrs& rAttrs) {
const router::EdgeListHops& res = route(ncr, rAttrs);
FLine l;
for (const auto& hop : res) {
const trgraph::Node* last = hop.start;
if (hop.edges.size() == 0) {
l.push_back(*hop.start->pl().getGeom());
l.push_back(*hop.end->pl().getGeom());
}
for (auto i = hop.edges.rbegin(); i != hop.edges.rend(); i++) {
const auto* e = *i;
if ((e->getFrom() == last) ^ e->pl().isRev()) {
l.insert(l.end(), e->pl().getGeom()->begin(), e->pl().getGeom()->end());
} else {
l.insert(l.end(), e->pl().getGeom()->rbegin(),
e->pl().getGeom()->rend());
}
last = e->getOtherNd(last);
}
}
return l;
}
// _____________________________________________________________________________
FLine ShapeBuilder::shapeL(Trip* trip) {
return shapeL(getNCR(trip), getRAttrs(trip));
}
// _____________________________________________________________________________
EdgeListHops ShapeBuilder::route(const router::NodeCandRoute& ncr,
const router::RoutingAttrs& rAttrs) const {
router::Graph g;
if (_cfg.solveMethod == "global") {
const router::EdgeListHops& ret =
_crouter.route(ncr, rAttrs, _motCfg.routingOpts, _restr, &g);
// write combination graph
if (!_cfg.shapeTripId.empty() && _cfg.writeCombGraph) {
LOG(INFO) << "Outputting combgraph.json...";
std::ofstream pstr(_cfg.dbgOutputPath + "/combgraph.json");
GeoGraphJsonOutput o;
o.print(g, pstr);
}
return ret;
} else if (_cfg.solveMethod == "greedy") {
return _crouter.routeGreedy(ncr, rAttrs, _motCfg.routingOpts, _restr);
} else if (_cfg.solveMethod == "greedy2") {
return _crouter.routeGreedy2(ncr, rAttrs, _motCfg.routingOpts, _restr);
} else {
LOG(ERROR) << "Unknown solution method " << _cfg.solveMethod;
exit(1);
}
return EdgeListHops();
}
// _____________________________________________________________________________
pfaedle::router::Shape ShapeBuilder::shape(Trip* trip) const {
LOG(VDEBUG) << "Map-matching shape for trip #" << trip->getId() << " of mot "
<< trip->getRoute()->getType() << "(sn=" << trip->getShortname()
<< ", rsn=" << trip->getRoute()->getShortName()
<< ", rln=" << trip->getRoute()->getLongName() << ")";
Shape ret;
ret.hops = route(getNCR(trip), getRAttrs(trip));
ret.avgHopDist = avgHopDist(trip);
LOG(VDEBUG) << "Finished map-matching for #" << trip->getId();
return ret;
}
// _____________________________________________________________________________
pfaedle::router::Shape ShapeBuilder::shape(Trip* trip) {
LOG(VDEBUG) << "Map-matching shape for trip #" << trip->getId() << " of mot "
<< trip->getRoute()->getType() << "(sn=" << trip->getShortname()
<< ", rsn=" << trip->getRoute()->getShortName()
<< ", rln=" << trip->getRoute()->getLongName() << ")";
Shape ret;
ret.hops = route(getNCR(trip), getRAttrs(trip));
ret.avgHopDist = avgHopDist(trip);
LOG(VDEBUG) << "Finished map-matching for #" << trip->getId();
return ret;
}
// _____________________________________________________________________________
void ShapeBuilder::shape(pfaedle::netgraph::Graph* ng) {
TrGraphEdgs gtfsGraph;
LOG(INFO) << "Clustering trips...";
Clusters clusters = clusterTrips(_feed, _mots);
LOG(INFO) << "Clustered trips into " << clusters.size() << " clusters.";
std::map<ad::cppgtfs::gtfs::Shape*, size_t> shpUsage;
for (auto t : _feed->getTrips()) {
if (t.second->getShape()) shpUsage[t.second->getShape()]++;
}
// to avoid unfair load balance on threads
std::random_shuffle(clusters.begin(), clusters.end());
size_t iters = EDijkstra::ITERS;
size_t totiters = EDijkstra::ITERS;
size_t oiters = EDijkstra::ITERS;
size_t j = 0;
auto t1 = TIME();
auto t2 = TIME();
double totAvgDist = 0;
size_t totNumTrips = 0;
#pragma omp parallel for num_threads(_numThreads)
for (size_t i = 0; i < clusters.size(); i++) {
j++;
if (j % 10 == 0) {
#pragma omp critical
{
LOG(INFO) << "@ " << j << " / " << clusters.size() << " ("
<< (static_cast<int>((j * 1.0) / clusters.size() * 100))
<< "%, " << (EDijkstra::ITERS - oiters) << " iters, tput "
<< (static_cast<double>(EDijkstra::ITERS - oiters)) /
TOOK(t1, TIME())
<< " iters/ms"
<< ", " << (10.0 / (TOOK(t1, TIME()) / 1000))
<< " trips/sec)";
oiters = EDijkstra::ITERS;
t1 = TIME();
}
}
// explicitly call const version of shape here for thread safety
const Shape& cshp =
const_cast<const ShapeBuilder&>(*this).shape(clusters[i][0]);
totAvgDist += cshp.avgHopDist;
if (_cfg.buildTransitGraph) {
#pragma omp critical
{ writeTransitGraph(cshp, &gtfsGraph, clusters[i]); }
}
std::vector<double> distances;
ad::cppgtfs::gtfs::Shape* shp =
getGtfsShape(cshp, clusters[i][0], &distances);
LOG(DEBUG) << "Took " << EDijkstra::ITERS - iters << " iterations.";
iters = EDijkstra::ITERS;
totNumTrips += clusters[i].size();
for (auto t : clusters[i]) {
if (_cfg.evaluate) {
_ecoll->add(t, t->getShape(), shp, distances);
}
if (t->getShape() && shpUsage[t->getShape()] > 0) {
shpUsage[t->getShape()]--;
if (shpUsage[t->getShape()] == 0) {
_feed->getShapes().remove(t->getShape()->getId());
delete t->getShape();
}
}
setShape(t, shp, distances);
}
}
LOG(INFO) << "Done.";
LOG(INFO) << "Matched " << totNumTrips << " trips in " << clusters.size()
<< " clusters.";
LOG(INFO) << "Took " << (EDijkstra::ITERS - totiters)
<< " iterations in total.";
LOG(INFO) << "Took " << TOOK(t2, TIME()) << " ms in total.";
LOG(INFO) << "Total avg. tput "
<< (static_cast<double>(EDijkstra::ITERS - totiters)) /
TOOK(t2, TIME())
<< " iters/sec";
LOG(INFO) << "Total avg. trip tput "
<< (clusters.size() / (TOOK(t2, TIME()) / 1000)) << " trips/sec";
LOG(INFO) << "Avg hop distance was "
<< (totAvgDist / static_cast<double>(clusters.size())) << " meters";
if (_cfg.buildTransitGraph) {
LOG(INFO) << "Building transit network graph...";
buildTrGraph(&gtfsGraph, ng);
}
}
// _____________________________________________________________________________
void ShapeBuilder::setShape(Trip* t, ad::cppgtfs::gtfs::Shape* s,
const std::vector<double>& distances) {
assert(distances.size() == t->getStopTimes().size());
// set distances
size_t i = 0;
for (const StopTime& st : t->getStopTimes()) {
const_cast<StopTime&>(st).setShapeDistanceTravelled(distances[i]);
i++;
}
t->setShape(s);
std::lock_guard<std::mutex> guard(_shpMutex);
_feed->getShapes().add(s);
}
// _____________________________________________________________________________
ad::cppgtfs::gtfs::Shape* ShapeBuilder::getGtfsShape(
const Shape& shp, Trip* t, std::vector<double>* hopDists) {
ad::cppgtfs::gtfs::Shape* ret =
new ad::cppgtfs::gtfs::Shape(getFreeShapeId(t));
assert(shp.hops.size() == t->getStopTimes().size() - 1);
size_t seq = 0;
double dist = -1;
double lastDist = -1;
hopDists->push_back(0);
FPoint last(0, 0);
for (const auto& hop : shp.hops) {
const trgraph::Node* l = hop.start;
if (hop.edges.size() == 0) {
FPoint ll = webMercToLatLng<float>(hop.start->pl().getGeom()->get<0>(),
hop.start->pl().getGeom()->get<1>());
if (dist > -0.5)
dist += webMercMeterDist(last, *hop.start->pl().getGeom());
else
dist = 0;
last = *hop.start->pl().getGeom();
if (dist - lastDist > 0.01) {
ret->addPoint(ShapePoint(ll.get<1>(), ll.get<0>(), dist, seq));
seq++;
lastDist = dist;
}
dist += webMercMeterDist(last, *hop.end->pl().getGeom());
last = *hop.end->pl().getGeom();
if (dist - lastDist > 0.01) {
ll = webMercToLatLng<float>(hop.end->pl().getGeom()->get<0>(),
hop.end->pl().getGeom()->get<1>());
ret->addPoint(ShapePoint(ll.get<1>(), ll.get<0>(), dist, seq));
seq++;
lastDist = dist;
}
}
for (auto i = hop.edges.rbegin(); i != hop.edges.rend(); i++) {
const auto* e = *i;
if ((e->getFrom() == l) ^ e->pl().isRev()) {
for (size_t i = 0; i < e->pl().getGeom()->size(); i++) {
const FPoint& cur = (*e->pl().getGeom())[i];
if (dist > -0.5)
dist += webMercMeterDist(last, cur);
else
dist = 0;
last = cur;
if (dist - lastDist > 0.01) {
FPoint ll = webMercToLatLng<float>(cur.get<0>(), cur.get<1>());
ret->addPoint(ShapePoint(ll.get<1>(), ll.get<0>(), dist, seq));
seq++;
lastDist = dist;
}
}
} else {
for (int64_t i = e->pl().getGeom()->size() - 1; i >= 0; i--) {
const FPoint& cur = (*e->pl().getGeom())[i];
if (dist > -0.5)
dist += webMercMeterDist(last, cur);
else
dist = 0;
last = cur;
if (dist - lastDist > 0.01) {
FPoint ll = webMercToLatLng<float>(cur.get<0>(), cur.get<1>());
ret->addPoint(ShapePoint(ll.get<1>(), ll.get<0>(), dist, seq));
seq++;
lastDist = dist;
}
}
}
l = e->getOtherNd(l);
}
hopDists->push_back(lastDist);
}
return ret;
}
// _____________________________________________________________________________
std::string ShapeBuilder::getFreeShapeId(Trip* trip) {
std::string ret;
std::lock_guard<std::mutex> guard(_shpMutex);
while (!ret.size() || _feed->getShapes().get(ret)) {
_curShpCnt++;
ret = "shp_";
ret += std::to_string(trip->getRoute()->getType());
ret += "_" + std::to_string(_curShpCnt);
}
return ret;
}
// _____________________________________________________________________________
const RoutingAttrs& ShapeBuilder::getRAttrs(const Trip* trip) {
auto i = _rAttrs.find(trip);
if (i == _rAttrs.end()) {
router::RoutingAttrs ret;
const auto& lnormzer = _motCfg.osmBuildOpts.lineNormzer;
ret.shortName = lnormzer(trip->getRoute()->getShortName());
if (ret.shortName.empty()) ret.shortName = lnormzer(trip->getShortname());
if (ret.shortName.empty())
ret.shortName = lnormzer(trip->getRoute()->getLongName());
ret.fromString = _motCfg.osmBuildOpts.statNormzer(
trip->getStopTimes().begin()->getStop()->getName());
ret.toString = _motCfg.osmBuildOpts.statNormzer(
(--trip->getStopTimes().end())->getStop()->getName());
return _rAttrs
.insert(std::pair<const Trip*, router::RoutingAttrs>(trip, ret))
.first->second;
} else {
return i->second;
}
}
// _____________________________________________________________________________
const RoutingAttrs& ShapeBuilder::getRAttrs(const Trip* trip) const {
return _rAttrs.find(trip)->second;
}
// _____________________________________________________________________________
BBoxIdx ShapeBuilder::getPaddedGtfsBox(const Feed* feed, double pad,
const MOTs& mots,
const std::string& tid) {
osm::BBoxIdx box(pad);
for (const auto& t : feed->getTrips()) {
if (!tid.empty() && t.second->getId() != tid) continue;
if (mots.count(t.second->getRoute()->getType())) {
Box<float> cur = minbox<float>();
for (const auto& st : t.second->getStopTimes()) {
cur = extendBox(
Point<float>(st.getStop()->getLng(), st.getStop()->getLat()), cur);
}
box.add(cur);
}
}
return box;
}
// _____________________________________________________________________________
void ShapeBuilder::buildGraph() {
LOG(INFO) << "Reading " << _cfg.osmPath << " ... ";
osm::OsmBuilder osmBuilder;
osm::BBoxIdx box = getPaddedGtfsBox(_feed, 2500, _mots, _cfg.shapeTripId);
osmBuilder.read(_cfg.osmPath, _motCfg.osmBuildOpts, &_g, box, _cfg.gridSize,
getFeedStops(), &_restr);
for (auto& feedStop : *getFeedStops()) {
if (feedStop.second) {
feedStop.second->pl().getSI()->getGroup()->writePens(
_motCfg.osmBuildOpts.trackNormzer,
_motCfg.routingOpts.platformUnmatchedPen,
_motCfg.routingOpts.stationDistPenFactor,
_motCfg.routingOpts.nonOsmPen);
}
}
LOG(INFO) << "Done.";
}
// _____________________________________________________________________________
NodeCandRoute ShapeBuilder::getNCR(Trip* trip) const {
router::NodeCandRoute ncr(trip->getStopTimes().size());
size_t i = 0;
for (const auto& st : trip->getStopTimes()) {
ncr[i] = getNodeCands(st.getStop());
i++;
}
return ncr;
}
// _____________________________________________________________________________
double ShapeBuilder::avgHopDist(Trip* trip) const {
size_t i = 0;
double sum = 0;
const Stop* prev = 0;
for (const auto& st : trip->getStopTimes()) {
if (!prev) {
prev = st.getStop();
continue;
}
auto a = util::geo::latLngToWebMerc<float>(prev->getLat(), prev->getLng());
auto b = util::geo::latLngToWebMerc<float>(st.getStop()->getLat(),
st.getStop()->getLng());
sum += util::geo::webMercMeterDist(a, b);
prev = st.getStop();
i++;
}
return sum / static_cast<double>(i);
}
// _____________________________________________________________________________
Clusters ShapeBuilder::clusterTrips(Feed* f, MOTs mots) {
// building an index [start station, end station] -> [cluster]
std::map<StopPair, std::vector<size_t>> clusterIdx;
size_t j = 0;
Clusters ret;
for (const auto& trip : f->getTrips()) {
// if (trip.second->getId() != "L5Cvl_T01") continue;
if (trip.second->getShape() && !_cfg.dropShapes) continue;
if (trip.second->getStopTimes().size() < 2) continue;
if (!mots.count(trip.second->getRoute()->getType()) ||
!_motCfg.mots.count(trip.second->getRoute()->getType()))
continue;
bool found = false;
auto spair = StopPair(trip.second->getStopTimes().begin()->getStop(),
trip.second->getStopTimes().rbegin()->getStop());
const auto& c = clusterIdx[spair];
for (size_t i = 0; i < c.size(); i++) {
j++;
if (routingEqual(ret[c[i]][0], trip.second)) {
ret[c[i]].push_back(trip.second);
found = true;
break;
}
}
if (!found) {
ret.push_back({trip.second});
// explicit call to write render attrs to cache
getRAttrs(trip.second);
clusterIdx[spair].push_back(ret.size() - 1);
}
}
return ret;
}
// _____________________________________________________________________________
bool ShapeBuilder::routingEqual(const Stop* a, const Stop* b) {
if (a == b) return true; // trivial
auto namea = _motCfg.osmBuildOpts.statNormzer(a->getName());
auto nameb = _motCfg.osmBuildOpts.statNormzer(b->getName());
if (namea != nameb) return false;
auto tracka = _motCfg.osmBuildOpts.trackNormzer(a->getPlatformCode());
auto trackb = _motCfg.osmBuildOpts.trackNormzer(b->getPlatformCode());
if (tracka != trackb) return false;
FPoint ap = util::geo::latLngToWebMerc<float>(a->getLat(), a->getLng());
FPoint bp = util::geo::latLngToWebMerc<float>(b->getLat(), b->getLng());
double d = util::geo::webMercMeterDist(ap, bp);
if (d > 1) return false;
return true;
}
// _____________________________________________________________________________
bool ShapeBuilder::routingEqual(Trip* a, Trip* b) {
if (a->getStopTimes().size() != b->getStopTimes().size()) return false;
if (getRAttrs(a) != getRAttrs(b)) return false;
auto stb = b->getStopTimes().begin();
for (const auto& sta : a->getStopTimes()) {
if (!routingEqual(sta.getStop(), stb->getStop())) {
return false;
}
stb++;
}
return true;
}
// _____________________________________________________________________________
const pfaedle::trgraph::Graph* ShapeBuilder::getGraph() const { return &_g; }
// _____________________________________________________________________________
void ShapeBuilder::writeTransitGraph(const Shape& shp, TrGraphEdgs* edgs,
const Cluster& cluster) const {
for (auto hop : shp.hops) {
for (const auto* e : hop.edges) {
if (e->pl().isRev()) e = _g.getEdg(e->getTo(), e->getFrom());
(*edgs)[e].insert(cluster.begin(), cluster.end());
}
}
}
// _____________________________________________________________________________
void ShapeBuilder::buildTrGraph(TrGraphEdgs* edgs,
pfaedle::netgraph::Graph* ng) const {
std::unordered_map<trgraph::Node*, pfaedle::netgraph::Node*> nodes;
for (auto ep : *edgs) {
auto e = ep.first;
pfaedle::netgraph::Node* from = 0;
pfaedle::netgraph::Node* to = 0;
if (nodes.count(e->getFrom())) from = nodes[e->getFrom()];
if (nodes.count(e->getTo())) to = nodes[e->getTo()];
if (!from) {
from = ng->addNd(*e->getFrom()->pl().getGeom());
nodes[e->getFrom()] = from;
}
if (!to) {
to = ng->addNd(*e->getTo()->pl().getGeom());
nodes[e->getTo()] = to;
}
ng->addEdg(from, to,
pfaedle::netgraph::EdgePL(*e->pl().getGeom(), ep.second));
}
}

View file

@ -0,0 +1,120 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_ROUTER_SHAPEBUILDER_H_
#define PFAEDLE_ROUTER_SHAPEBUILDER_H_
#include <mutex>
#include <set>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include "ad/cppgtfs/gtfs/Feed.h"
#include "pfaedle/config/MotConfig.h"
#include "pfaedle/config/PfaedleConfig.h"
#include "pfaedle/eval/Collector.h"
#include "pfaedle/netgraph/Graph.h"
#include "pfaedle/osm/Restrictor.h"
#include "pfaedle/router/Misc.h"
#include "pfaedle/router/Router.h"
#include "pfaedle/trgraph/Graph.h"
namespace pfaedle {
namespace router {
using ad::cppgtfs::gtfs::Stop;
using ad::cppgtfs::gtfs::Trip;
using ad::cppgtfs::gtfs::Feed;
struct Shape {
router::EdgeListHops hops;
double avgHopDist;
};
typedef std::vector<Trip*> Cluster;
typedef std::vector<Cluster> Clusters;
typedef std::pair<const Stop*, const Stop*> StopPair;
typedef std::unordered_map<const Trip*, router::RoutingAttrs> TripRAttrs;
typedef std::unordered_map<const trgraph::Edge*, std::set<const Trip*>>
TrGraphEdgs;
/*
* Layer class for the router. Provides an interface for direct usage with
* GTFS data
*/
class ShapeBuilder {
public:
ShapeBuilder(Feed* feed, MOTs mots, const config::MotConfig& motCfg,
eval::Collector* ecoll, const config::Config& cfg);
void shape(pfaedle::netgraph::Graph* ng);
router::FeedStops* getFeedStops();
const NodeCandGroup& getNodeCands(const Stop* s) const;
util::geo::FLine shapeL(const router::NodeCandRoute& ncr,
const router::RoutingAttrs& rAttrs);
util::geo::FLine shapeL(Trip* trip);
pfaedle::router::Shape shape(Trip* trip) const;
pfaedle::router::Shape shape(Trip* trip);
const trgraph::Graph* getGraph() const;
static osm::BBoxIdx getPaddedGtfsBox(const Feed* feed, double pad,
const MOTs& mots,
const std::string& tid);
private:
Feed* _feed;
MOTs _mots;
config::MotConfig _motCfg;
eval::Collector* _ecoll;
config::Config _cfg;
trgraph::Graph _g;
router::Router _crouter;
router::FeedStops _stops;
NodeCandGroup _emptyNCG;
size_t _curShpCnt, _numThreads;
std::mutex _shpMutex;
TripRAttrs _rAttrs;
osm::Restrictor _restr;
void writeMotStops();
void buildGraph();
Clusters clusterTrips(Feed* f, MOTs mots);
void writeTransitGraph(const Shape& shp, TrGraphEdgs* edgs,
const Cluster& cluster) const;
void buildTrGraph(TrGraphEdgs* edgs, pfaedle::netgraph::Graph* ng) const;
std::string getFreeShapeId(Trip* t);
ad::cppgtfs::gtfs::Shape* getGtfsShape(const Shape& shp, Trip* t,
std::vector<double>* hopDists);
void setShape(Trip* t, ad::cppgtfs::gtfs::Shape* s,
const std::vector<double>& dists);
router::NodeCandRoute getNCR(Trip* trip) const;
double avgHopDist(Trip* trip) const;
const router::RoutingAttrs& getRAttrs(const Trip* trip) const;
const router::RoutingAttrs& getRAttrs(const Trip* trip);
bool routingEqual(Trip* a, Trip* b);
bool routingEqual(const Stop* a, const Stop* b);
router::EdgeListHops route(const router::NodeCandRoute& ncr,
const router::RoutingAttrs& rAttrs) const;
};
} // namespace router
} // namespace pfaedle
#endif // PFAEDLE_ROUTER_SHAPEBUILDER_H_

View file

@ -0,0 +1,174 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include <map>
#include <string>
#include <vector>
#include "pfaedle/trgraph/EdgePL.h"
using pfaedle::trgraph::EdgePL;
using pfaedle::trgraph::TransitEdgeLine;
std::map<util::geo::FLine*, size_t> EdgePL::_flines;
std::map<const TransitEdgeLine*, size_t> EdgePL::_tlines;
// _____________________________________________________________________________
EdgePL::EdgePL()
: _length(0), _oneWay(0), _hasRestr(false), _rev(false), _lvl(0) {
_l = new util::geo::FLine();
_flines[_l] = 1;
}
// _____________________________________________________________________________
EdgePL::EdgePL(const EdgePL& pl) : EdgePL(pl, false) {}
// _____________________________________________________________________________
EdgePL::EdgePL(const EdgePL& pl, bool geoflat)
: _length(pl._length),
_oneWay(pl._oneWay),
_hasRestr(pl._hasRestr),
_rev(pl._rev),
_lvl(pl._lvl) {
if (geoflat) {
_l = pl._l;
} else {
_l = new util::geo::FLine(*pl._l);
}
_flines[_l]++;
for (auto l : _lines) addLine(l);
}
// _____________________________________________________________________________
EdgePL::~EdgePL() {
if (_l) {
_flines[_l]--;
if (_flines[_l] == 0) delete _l;
}
for (auto l : _lines) unRefTLine(l);
}
// _____________________________________________________________________________
void EdgePL::unRefTLine(const TransitEdgeLine* l) {
if (l) {
_tlines[l]--;
if (_tlines[l] == 0) {
delete l;
_tlines.erase(_tlines.find(l));
}
}
}
// _____________________________________________________________________________
EdgePL EdgePL::revCopy() const {
EdgePL ret(*this);
ret.setRev();
if (ret.oneWay() == 1)
ret.setOneWay(2);
else if (ret.oneWay() == 2)
ret.setOneWay(1);
return ret;
}
// _____________________________________________________________________________
void EdgePL::setLength(double d) { _length = d; }
// _____________________________________________________________________________
double EdgePL::getLength() const { return _length; }
// _____________________________________________________________________________
void EdgePL::addLine(const TransitEdgeLine* l) {
if (_lines.insert(l).second) {
if (_tlines.count(l))
_tlines[l]++;
else
_tlines[l] = 1;
}
}
// _____________________________________________________________________________
void EdgePL::addLines(const std::vector<TransitEdgeLine*>& l) {
for (auto line : l) addLine(line);
}
// _____________________________________________________________________________
const std::set<const TransitEdgeLine*>& EdgePL::getLines() const {
return _lines;
}
// _____________________________________________________________________________
void EdgePL::addPoint(const util::geo::FPoint& p) { _l->push_back(p); }
// _____________________________________________________________________________
const util::geo::FLine* EdgePL::getGeom() const { return _l; }
// _____________________________________________________________________________
util::geo::FLine* EdgePL::getGeom() { return _l; }
// _____________________________________________________________________________
void EdgePL::getAttrs(std::map<std::string, std::string>* obj) const {
(*obj)["m_length"] = std::to_string(_length);
(*obj)["oneway"] = std::to_string(static_cast<int>(_oneWay));
(*obj)["level"] = std::to_string(_lvl);
(*obj)["restriction"] = isRestricted() ? "yes" : "no";
std::stringstream ss;
bool first = false;
for (auto* l : _lines) {
if (first) ss << ",";
ss << l->shortName;
if (l->fromStr.size() || l->toStr.size()) {
ss << "(" << l->fromStr;
ss << "->" << l->toStr << ")";
}
first = true;
}
(*obj)["lines"] = ss.str();
}
// _____________________________________________________________________________
void EdgePL::setRestricted() { _hasRestr = true; }
// _____________________________________________________________________________
bool EdgePL::isRestricted() const { return _hasRestr; }
// _____________________________________________________________________________
uint8_t EdgePL::oneWay() const { return _oneWay; }
// _____________________________________________________________________________
void EdgePL::setOneWay(uint8_t dir) { _oneWay = dir; }
// _____________________________________________________________________________
void EdgePL::setOneWay() { _oneWay = 1; }
// _____________________________________________________________________________
void EdgePL::setLvl(uint8_t lvl) { _lvl = lvl; }
// _____________________________________________________________________________
uint8_t EdgePL::lvl() const { return _lvl; }
// _____________________________________________________________________________
void EdgePL::setRev() { _rev = true; }
// _____________________________________________________________________________
bool EdgePL::isRev() const { return _rev; }
// _____________________________________________________________________________
const util::geo::FPoint& EdgePL::backHop() const {
if (isRev()) {
return *(++(getGeom()->cbegin()));
}
return *(++(getGeom()->crbegin()));
}
// _____________________________________________________________________________
const util::geo::FPoint& EdgePL::frontHop() const {
if (isRev()) {
return *(++(getGeom()->crbegin()));
}
return *(++(getGeom()->cbegin()));
}

View file

@ -0,0 +1,133 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_TRGRAPH_EDGEPL_H_
#define PFAEDLE_TRGRAPH_EDGEPL_H_
#include <map>
#include <set>
#include <string>
#include <vector>
#include "pfaedle/router/Comp.h"
#include "util/geo/GeoGraph.h"
using util::geograph::GeoEdgePL;
namespace pfaedle {
namespace trgraph {
/*
* A line occuring on an edge
*/
struct TransitEdgeLine {
std::string fromStr;
std::string toStr;
std::string shortName;
};
inline bool operator==(const TransitEdgeLine& a, const TransitEdgeLine& b) {
return a.fromStr == b.fromStr && a.toStr == b.toStr &&
a.shortName == b.shortName;
}
inline bool operator<(const TransitEdgeLine& a, const TransitEdgeLine& b) {
return a.fromStr < b.fromStr ||
(a.fromStr == b.fromStr && a.toStr < b.toStr) ||
(a.fromStr == b.fromStr && a.toStr == b.toStr &&
a.shortName < b.shortName);
}
/*
* An edge payload class for the transit graph.
*/
class EdgePL : public GeoEdgePL<float> {
public:
EdgePL();
~EdgePL();
EdgePL(const EdgePL& pl);
EdgePL(const EdgePL& pl, bool geoFlat);
// Return the geometry of this edge.
const util::geo::FLine* getGeom() const;
util::geo::FLine* getGeom();
// Extends this edge payload's geometry by Point p
void addPoint(const util::geo::FPoint& p);
// Fill obj with k/v pairs describing the parameters of this payload.
void getAttrs(std::map<std::string, std::string>* obj) const;
// Return the length in meters stored for this edge payload
double getLength() const;
// Set the length in meters for this edge payload
void setLength(double d);
// Set this edge as a one way node, either in the default direction of
// the edge (no arg), or the direction specified in dir
void setOneWay();
void setOneWay(uint8_t dir);
// Mark this payload' edge as having some restrictions
void setRestricted();
// Mark this payload' edge as being secondary to an inversed partner
void setRev();
// True if this edge is secondary to an inversed partner
bool isRev() const;
// True if this edge is restricted
bool isRestricted() const;
// Set the level of this edge.
void setLvl(uint8_t lvl);
// Return the level of this edge.
uint8_t lvl() const;
// Return the one-way code stored for this edge.
uint8_t oneWay() const;
// Add a TransitedgeLine to this payload's edge
void addLine(const TransitEdgeLine* l);
// Add multiple TransitedgeLine objects to this payload's edge
void addLines(const std::vector<TransitEdgeLine*>& l);
// Return the TransitEdgeLines stored for this payload
const std::set<const TransitEdgeLine*>& getLines() const;
// Returns the last hop of the payload - this is the (n-2)th point in
// the payload geometry of length n > 1
const util::geo::FPoint& backHop() const;
// Returns the first hop of the payload - this is the 2nd point in
// the payload geometry of length n > 1
const util::geo::FPoint& frontHop() const;
// Obtain an exact copy of this edge, but in reverse.
EdgePL revCopy() const;
private:
float _length;
uint8_t _oneWay : 2;
bool _hasRestr : 1;
bool _rev : 1;
uint8_t _lvl : 3;
util::geo::FLine* _l;
std::set<const TransitEdgeLine*> _lines;
static void unRefTLine(const TransitEdgeLine* l);
static std::map<util::geo::FLine*, size_t> _flines;
static std::map<const TransitEdgeLine*, size_t> _tlines;
};
} // namespace trgraph
} // namespace pfaedle
#endif // PFAEDLE_TRGRAPH_EDGEPL_H_

View file

@ -0,0 +1,35 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_TRGRAPH_GRAPH_H_
#define PFAEDLE_TRGRAPH_GRAPH_H_
#include "pfaedle/trgraph/NodePL.h"
#include "pfaedle/trgraph/EdgePL.h"
#include "util/graph/UndirGraph.h"
#include "util/graph/DirGraph.h"
#include "util/geo/Grid.h"
using util::geo::Grid;
using util::geo::Point;
using util::geo::Line;
using util::geo::FPoint;
using util::geo::FLine;
namespace pfaedle {
namespace trgraph {
/*
* A graph for physical transit networks
*/
typedef util::graph::Edge<NodePL, EdgePL> Edge;
typedef util::graph::Node<NodePL, EdgePL> Node;
typedef util::graph::DirGraph<NodePL, EdgePL> Graph;
typedef Grid<Node*, Point, float> NodeGrid;
typedef Grid<Edge*, Line, float> EdgeGrid;
} // namespace trgraph
} // namespace pfaedle
#endif // PFAEDLE_TRGRAPH_GRAPH_H_

View file

@ -0,0 +1,124 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include <string>
#include <unordered_map>
#include "pfaedle/trgraph/NodePL.h"
#include "pfaedle/trgraph/StatGroup.h"
#include "pfaedle/trgraph/StatInfo.h"
#include "util/String.h"
using pfaedle::trgraph::StatInfo;
using pfaedle::trgraph::NodePL;
using pfaedle::trgraph::Component;
// we use the adress of this dummy station info as a special value
// of this node, meaning "is a station block". Re-using the _si field here
// saves some memory
StatInfo NodePL::_blockerSI = StatInfo();
std::unordered_map<const Component*, size_t> NodePL::_comps;
// _____________________________________________________________________________
NodePL::NodePL() : _geom(0, 0), _si(0), _component(0), _vis(0) {}
// _____________________________________________________________________________
NodePL::NodePL(const NodePL& pl)
: _geom(pl._geom), _si(0), _component(pl._component), _vis(pl._vis) {
if (pl._si) setSI(*(pl._si));
}
// _____________________________________________________________________________
NodePL::NodePL(const util::geo::FPoint& geom)
: _geom(geom), _si(0), _component(0), _vis(0) {}
// _____________________________________________________________________________
NodePL::NodePL(const util::geo::FPoint& geom, const StatInfo& si)
: _geom(geom), _si(0), _component(0), _vis(0) {
setSI(si);
}
// _____________________________________________________________________________
NodePL::~NodePL() {
if (getSI()) delete _si;
if (_component) {
_comps[_component]--;
if (_comps[_component] == 0) {
delete _component;
_comps.erase(_comps.find(_component));
}
}
}
// _____________________________________________________________________________
void NodePL::setVisited() const { _vis = true; }
// _____________________________________________________________________________
void NodePL::setNoStat() { _si = 0; }
// _____________________________________________________________________________
const Component* NodePL::getComp() const { return _component; }
// _____________________________________________________________________________
void NodePL::setComp(const Component* c) {
if (_component == c) return;
_component = c;
// NOT thread safe!
if (!_comps.count(c))
_comps[c] = 1;
else
_comps[c]++;
}
// _____________________________________________________________________________
const util::geo::FPoint* NodePL::getGeom() const { return &_geom; }
// _____________________________________________________________________________
void NodePL::setGeom(const util::geo::FPoint& geom) { _geom = geom; }
// _____________________________________________________________________________
void NodePL::getAttrs(std::map<std::string, std::string>* obj) const {
(*obj)["component"] = std::to_string(reinterpret_cast<size_t>(_component));
(*obj)["dijkstra_vis"] = _vis ? "yes" : "no";
if (getSI()) {
(*obj)["station_info_ptr"] = util::toString(_si);
(*obj)["station_name"] = _si->getName();
(*obj)["station_alt_names"] = util::implode(_si->getAltNames(), ",");
(*obj)["from_osm"] = _si->isFromOsm() ? "yes" : "no";
(*obj)["station_platform"] = _si->getTrack();
(*obj)["station_group"] =
std::to_string(reinterpret_cast<size_t>(_si->getGroup()));
std::stringstream gtfsIds;
if (_si->getGroup()) {
for (auto* s : _si->getGroup()->getStops()) {
gtfsIds << s->getId() << " (" << s->getName() << "),";
}
}
(*obj)["station_group_stops"] = gtfsIds.str();
}
}
// _____________________________________________________________________________
void NodePL::setSI(const StatInfo& si) { _si = new StatInfo(si); }
// _____________________________________________________________________________
const StatInfo* NodePL::getSI() const {
if (isBlocker()) return 0;
return _si;
}
// _____________________________________________________________________________
StatInfo* NodePL::getSI() {
if (isBlocker()) return 0;
return _si;
}
// _____________________________________________________________________________
void NodePL::setBlocker() { _si = &_blockerSI; }
// _____________________________________________________________________________
bool NodePL::isBlocker() const { return _si == &_blockerSI; }

View file

@ -0,0 +1,83 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_TRGRAPH_NODEPL_H_
#define PFAEDLE_TRGRAPH_NODEPL_H_
#include <map>
#include <string>
#include <unordered_map>
#include "ad/cppgtfs/gtfs/Feed.h"
#include "pfaedle/trgraph/StatInfo.h"
#include "util/geo/GeoGraph.h"
using util::geograph::GeoNodePL;
namespace pfaedle {
namespace trgraph {
struct Component {
uint8_t minEdgeLvl : 3;
};
/*
* A node payload class for the transit graph.
*/
class NodePL : public GeoNodePL<float> {
public:
NodePL();
NodePL(const NodePL& pl); // NOLINT
NodePL(const util::geo::FPoint& geom); // NOLINT
NodePL(const util::geo::FPoint& geom, const StatInfo& si);
~NodePL();
// Return the geometry of this node.
const util::geo::FPoint* getGeom() const;
void setGeom(const util::geo::FPoint& geom);
// Fill obj with k/v pairs describing the parameters of this payload.
void getAttrs(std::map<std::string, std::string>* attrs) const;
// Set the station info for this node
void setSI(const StatInfo& si);
// Return the station info for this node
const StatInfo* getSI() const;
StatInfo* getSI();
// Delete the station info for this node
void setNoStat();
// Get the component of this node
const Component* getComp() const;
// Set the component of this node
void setComp(const Component* c);
// Make this node a blocker
void setBlocker();
// Check if this node is a blocker
bool isBlocker() const;
// Mark this node as visited (usefull for counting search space in Dijkstra)
void setVisited() const;
private:
std::string _b;
// 32bit floats are enough here
util::geo::FPoint _geom;
StatInfo* _si;
const Component* _component;
static StatInfo _blockerSI;
mutable bool _vis;
static std::unordered_map<const Component*, size_t> _comps;
};
} // namespace trgraph
} // namespace pfaedle
#endif // PFAEDLE_TRGRAPH_NODEPL_H_

View file

@ -0,0 +1,61 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include <algorithm>
#include <iostream>
#include <regex>
#include <utility>
#include <string>
#include <vector>
#include "pfaedle/trgraph/Normalizer.h"
using pfaedle::trgraph::Normalizer;
// _____________________________________________________________________________
Normalizer::Normalizer(const ReplRules& rules) : _rulesOrig(rules) {
buildRules(rules);
}
// _____________________________________________________________________________
std::string Normalizer::operator()(std::string sn) const {
auto i = _cache.find(sn);
if (i != _cache.end()) return i->second;
std::string ret = sn;
for (auto rule : _rules) {
std::string tmp;
std::regex_replace(std::back_inserter(tmp), ret.begin(), ret.end(),
rule.first, rule.second,
std::regex_constants::format_sed);
std::swap(ret, tmp);
}
std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower);
_cache[sn] = ret;
return ret;
}
// _____________________________________________________________________________
bool Normalizer::operator==(const Normalizer& b) const {
return _rulesOrig == b._rulesOrig;
}
// _____________________________________________________________________________
void Normalizer::buildRules(const ReplRules& rules) {
for (auto rule : rules) {
try {
_rules.push_back(ReplRuleComp(
std::regex(rule.first, std::regex::ECMAScript | std::regex::icase |
std::regex::optimize),
rule.second));
} catch (const std::regex_error& e) {
std::stringstream ss;
ss << "'" << rule.first << "'"
<< ": " << e.what();
throw std::runtime_error(ss.str());
}
}
}

View file

@ -0,0 +1,45 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_TRGRAPH_NORMALIZER_H_
#define PFAEDLE_TRGRAPH_NORMALIZER_H_
#include <regex>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
namespace pfaedle {
namespace trgraph {
typedef std::pair<std::string, std::string> ReplRule;
typedef std::vector<ReplRule> ReplRules;
typedef std::pair<std::regex, std::string> ReplRuleComp;
typedef std::vector<ReplRuleComp> ReplRulesComp;
/*
* A class for normalizing station names
*/
class Normalizer {
public:
Normalizer() {}
explicit Normalizer(const ReplRules& rules);
// Normalize sn based on the rules of this normalizer
std::string operator()(std::string sn) const;
bool operator==(const Normalizer& b) const;
private:
ReplRulesComp _rules;
ReplRules _rulesOrig;
mutable std::unordered_map<std::string, std::string> _cache;
void buildRules(const ReplRules& rules);
};
} // namespace trgraph
} // namespace pfaedle
#endif // PFAEDLE_TRGRAPH_NORMALIZER_H_

View file

@ -0,0 +1,95 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include <set>
#include "pfaedle/trgraph/StatGroup.h"
#include "util/geo/Geo.h"
using pfaedle::trgraph::StatGroup;
using pfaedle::trgraph::Node;
using pfaedle::router::NodeCandGroup;
using ad::cppgtfs::gtfs::Stop;
// _____________________________________________________________________________
StatGroup::StatGroup() {}
// _____________________________________________________________________________
void StatGroup::addStop(const Stop* s) { _stops.insert(s); }
// _____________________________________________________________________________
void StatGroup::addNode(trgraph::Node* n) { _nodes.insert(n); }
// _____________________________________________________________________________
void StatGroup::merge(StatGroup* other) {
if (other == this) return;
std::set<Node*> nds = other->getNodes();
std::set<const Stop*> stops = other->getStops();
for (auto on : nds) {
on->pl().getSI()->setGroup(this);
addNode(on);
}
for (auto* os : stops) {
addStop(os);
}
}
// _____________________________________________________________________________
const NodeCandGroup& StatGroup::getNodeCands(const Stop* s) const {
return _stopNodePens.at(s);
}
// _____________________________________________________________________________
const std::set<Node*>& StatGroup::getNodes() const {
return _nodes;
}
// _____________________________________________________________________________
void StatGroup::remNode(trgraph::Node* n) {
auto it = _nodes.find(n);
if (it != _nodes.end()) _nodes.erase(it);
}
// _____________________________________________________________________________
std::set<Node*>& StatGroup::getNodes() { return _nodes; }
// _____________________________________________________________________________
const std::set<const Stop*>& StatGroup::getStops() const { return _stops; }
// _____________________________________________________________________________
double StatGroup::getPen(const Stop* s, trgraph::Node* n,
const trgraph::Normalizer& platformNorm,
double trackPen, double distPenFac,
double nonOsmPen) const {
FPoint p = util::geo::latLngToWebMerc<float>(s->getLat(), s->getLng());
double distPen = util::geo::webMercMeterDist(p, *n->pl().getGeom());
distPen *= distPenFac;
std::string platform = platformNorm(s->getPlatformCode());
if (!platform.empty() && !n->pl().getSI()->getTrack().empty() &&
n->pl().getSI()->getTrack() == platform) {
trackPen = 0;
}
if (n->pl().getSI()->isFromOsm()) nonOsmPen = 0;
return distPen + trackPen + nonOsmPen;
}
// _____________________________________________________________________________
void StatGroup::writePens(const trgraph::Normalizer& platformNorm,
double trackPen, double distPenFac,
double nonOsmPen) {
if (_stopNodePens.size()) return; // already written
for (auto* s : _stops) {
for (auto* n : _nodes) {
_stopNodePens[s].push_back(router::NodeCand{
n, getPen(s, n, platformNorm, trackPen, distPenFac, nonOsmPen)});
}
}
}

View file

@ -0,0 +1,72 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_TRGRAPH_STATGROUP_H_
#define PFAEDLE_TRGRAPH_STATGROUP_H_
#include <string>
#include <unordered_map>
#include <set>
#include "ad/cppgtfs/gtfs/Feed.h"
#include "pfaedle/router/Router.h"
#include "pfaedle/trgraph/Graph.h"
#include "pfaedle/trgraph/Normalizer.h"
namespace pfaedle {
namespace trgraph {
using ad::cppgtfs::gtfs::Stop;
/*
* A group of stations that belong together semantically (for example, multiple
* stop points of a larger bus station)
*/
class StatGroup {
public:
StatGroup();
StatGroup(const StatGroup& a) = delete;
// Add a stop s to this station group
void addStop(const Stop* s);
// Add a node n to this station group
void addNode(trgraph::Node* n);
// Return all nodes contained in this group
const std::set<trgraph::Node*>& getNodes() const;
std::set<trgraph::Node*>& getNodes();
// Return all stops contained in this group
const std::set<const Stop*>& getStops() const;
// Remove a node from this group
void remNode(trgraph::Node* n);
// All nodes in other will be in this group, their SI's updated, and the
// "other" group deleted.
void merge(StatGroup* other);
// Return node candidates for stop s from this group
const router::NodeCandGroup& getNodeCands(const Stop* s) const;
// Write the penalties for all stops contained in this group so far.
void writePens(const trgraph::Normalizer& platformNorm, double trackPen,
double distPenFac, double nonOsmPen);
private:
std::set<trgraph::Node*> _nodes;
std::set<const Stop*> _stops;
// for each stop in this group, a penalty for each of the nodes here, based on
// its distance and optionally the track number
std::unordered_map<const Stop*, router::NodeCandGroup> _stopNodePens;
double getPen(const Stop* s, trgraph::Node* n,
const trgraph::Normalizer& norm, double trackPen,
double distPenFac, double nonOsmPen) const;
};
} // namespace trgraph
} // namespace pfaedle
#endif // PFAEDLE_TRGRAPH_STATGROUP_H_

View file

@ -0,0 +1,106 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include "pfaedle/router/Comp.h"
#include "pfaedle/trgraph/StatGroup.h"
#include "pfaedle/trgraph/StatInfo.h"
using pfaedle::trgraph::StatInfo;
using pfaedle::trgraph::StatGroup;
std::unordered_map<const StatGroup*, size_t> StatInfo::_groups;
// _____________________________________________________________________________
StatInfo::StatInfo() : _name(""), _track(""), _fromOsm(false), _group(0) {}
// _____________________________________________________________________________
StatInfo::StatInfo(const StatInfo& si)
: _name(si._name),
_altNames(si._altNames),
_track(si._track),
_fromOsm(si._fromOsm),
_group(0) {
setGroup(si._group);
}
// _____________________________________________________________________________
StatInfo::StatInfo(const std::string& name, const std::string& track,
bool fromOsm)
: _name(name), _track(track), _fromOsm(fromOsm), _group(0) {}
// _____________________________________________________________________________
StatInfo::~StatInfo() { unRefGroup(_group); }
// _____________________________________________________________________________
void StatInfo::unRefGroup(StatGroup* g) {
if (g) {
_groups[g]--;
if (_groups[g] == 0) {
// std::cout << "Deleting " << g << std::endl;
delete g;
_groups.erase(_groups.find(g));
}
}
}
// _____________________________________________________________________________
void StatInfo::setGroup(StatGroup* g) {
if (_group == g) return;
unRefGroup(_group);
_group = g;
// NOT thread safe!
if (!_groups.count(g))
_groups[g] = 1;
else
_groups[g]++;
}
// _____________________________________________________________________________
StatGroup* StatInfo::getGroup() const { return _group; }
// _____________________________________________________________________________
const std::string& StatInfo::getName() const { return _name; }
// _____________________________________________________________________________
const std::string& StatInfo::getTrack() const { return _track; }
// _____________________________________________________________________________
bool StatInfo::isFromOsm() const { return _fromOsm; }
// _____________________________________________________________________________
void StatInfo::setIsFromOsm(bool is) { _fromOsm = is; }
// _____________________________________________________________________________
double StatInfo::simi(const StatInfo* other) const {
if (!other) return 0;
if (router::statSimi(_name, other->getName()) > 0.5) return 1;
for (const auto& a : _altNames) {
if (router::statSimi(a, other->getName()) > 0.5) return 1;
for (const auto& b : other->getAltNames()) {
if (router::statSimi(a, b) > 0.5) return 1;
}
}
for (const auto& b : other->getAltNames()) {
if (router::statSimi(_name, b) > 0.5) return 1;
}
return 0;
}
// _____________________________________________________________________________
const std::vector<std::string>& StatInfo::getAltNames() const {
return _altNames;
}
// _____________________________________________________________________________
void StatInfo::addAltName(const std::string& name) {
_altNames.push_back(name);
}
// _____________________________________________________________________________
void StatInfo::setTrack(const std::string& tr) { _track = tr; }

View file

@ -0,0 +1,71 @@
// Copyright 2017, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_TRGRAPH_STATINFO_H_
#define PFAEDLE_TRGRAPH_STATINFO_H_
#include <string>
#include <vector>
#include <unordered_map>
namespace pfaedle {
namespace trgraph {
// forward declaration
class StatGroup;
/*
* Meta information (name, alternative names, track, group...) of a single stop
*/
class StatInfo {
public:
StatInfo();
StatInfo(const StatInfo& si);
StatInfo(const std::string& name, const std::string& track, bool _fromOsm);
~StatInfo();
// Return this stops names.
const std::string& getName() const;
// Return this stops track or empty string, if none.
const std::string& getTrack() const;
// Add an alternative name for this station.
void addAltName(const std::string& name);
// Return all alternative names for this station.
const std::vector<std::string>& getAltNames() const;
// Set the track of this stop.
void setTrack(const std::string& tr);
// Return the similarity between this stop and other
double simi(const StatInfo* other) const;
// Set this stations group.
void setGroup(StatGroup* g);
// Return this stations group.
StatGroup* getGroup() const;
// True if this stop was from osm
bool isFromOsm() const;
// Set this stop as coming from osm
void setIsFromOsm(bool is);
private:
std::string _name;
std::vector<std::string> _altNames;
std::string _track;
bool _fromOsm;
StatGroup* _group;
static std::unordered_map<const StatGroup*, size_t> _groups;
static void unRefGroup(StatGroup* g);
};
} // namespace trgraph
} // namespace pfaedle
#endif // PFAEDLE_TRGRAPH_STATINFO_H_